それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

nft によるMSQUERADE(iptables MASQUERADEからの移行)

nft によるMSQUERADE(iptables MASQUERADEからの移行)

iptablesでマスカレード(またはSNAT)は古くから行われている枯れた手法。これをnft(nftables)に書き換える場合のメモ

次のような、マスカレード(NAT)をiptablesで追記しているとする。

iptables -I FORWARD -o ${interface} -j ACCEPT
iptables -t nat -A POSTROUTING -o ${interface} -j MASQUERADE

たとえば、このように。ルーティングと組み合わせて使ってる。

DEST_NET=10.1.1.0/24
interface=vpn0
router=10.1.1.1.1

ip route add ${DEST_NET} via ${router} dev ${interface}
##
iptables -I FORWARD -o ${interface} -j ACCEPT
iptables -t nat -A POSTROUTING -o ${interface} -j MASQUERADE

これをnft(nftables)に書き換えてみると。

nft コマンドでは次のようになる。 テーブル追加とチェインの追加、そして、ルールの追加である。

interface=vpn0
router=10.1.1.1.1

TABLE=my_vpn_nat
nft add table ip ${TABLE};
# 冗長じゃない?
nft add chain ${TABLE} prerouting { type nat hook postrouting priority srcnat \; }
nft add chain ${TABLE} postrouting type nat hook postrouting priority srcnat accept;
nft add rule  ${TABLE} postrouting oifname ${interface} masquerade 
nft add rule  ${TABLE} postrouting oifname ${interface} masquerade 

テーブルの追加

nft add table ip ${TABLE};

一般形式は次のようになっていて。

nft add table <?family> <name>

<?family> にはip, ip6, inet ,arp, bridge が使える。省略時は ip である。ipはv4 アドレスを意味する。inet はinet = ip + ip6であり、v6/v4に無関係(双方)を指す。

テーブルを削除する場合 add を delete にかえるだけである。(2023-08-29 現在 del ではエラーになる)

nft delete table <?family> <name>

サンプル

nft add table my_sample
nft list tables # チェック
nft delete table my_sample
nft list tables # チェック

チェインの追加

チェインは、テーブル内部に作られる。チェインの中にはパケットのルールが入る。ルールをグループにして一括りにする。

nft add chain <?family> <table> <chain_name> { \
  type <type> hook <hook> priority <priority> \; \
  policy <policy> \;
}

hook はチェインが実行されるタイミング。type は何をするか。

{ ... } で括られた部分が、チェインに入るruleである。

ip/ip6/inetにおいては、

  • hook={prerouting, input, forward, output, postrouting} から一つ.
  • type={filter,nat,route} から一つを選ぶ。
  • policy ={accept,drop} から一つを選ぶ。
  • priority は、他のチェイン・ルールとの優先度±1 を書く。基本は0を書いておく。

ベースチェインの候補は、テーブル名やチェイン名と混同するような類似キーワードが多く初見殺しである。

以下をよく覚えておく。

type <type_name> hook <hook_name> priority <num|name>

例えばよくあるサンプルでは次のようになっている

nft add chain vpn_nat postrouting { type nat hook postrouting priority srcnat; policy accept; }   

この場合、ベースチェインの名前=postrouting となり、hook = postrouting であり、type=nat であり、priority=srcnatである。postroutingnatが、名前にもキーワードにも登場し、ややこしくなる。これが初見殺しである。

ベースチェインとはiptablesでも使っていた、mangle や nat のような特定パケットに一致する機能である。

ベースチェインのタイプ未指定にしたら、単なる関数になる。独立したチェインとして登録され、他チェインから呼び出されるために存在できる。

## ベースチェイン(タイプあり)の例
nft add chain ip MyMangle prerouting { type filter hook prerouting priority mangle \;}
## 単純チェイン(ベースタイプなし)の例
nft add chain ip MyMangle my_chain

詳しくは、公式wikiを参考にする。

ベースチェインは「パケットが条件一致したら呼び出される手順」、関数は「他チェインのルールから使われる手順」という違いがある。

マスカレード対象のフォワードをAcceptする。

iptable の場合は次のように先頭に書けば良い。

iptables -I FORWARD -o $TARGET_IF -i br0 -j ACCEPT

nftables の場合は次のように、対象のチェインに追記すると良い

nft insert rule inet fw4 forward iifname br0 oifname $TARGET_IF accept;

nftablesの場合は、同じチェイン内でaccept したら打ち止めになるが、ほかテーブルを参照している場合、他テーブルでDROPされる可能性もあるので注意する。デフォルトがDROPなどであれば、add は使わずに、insert でチェイン先頭にacceptを追記するようにする。