nftables
linux のiptablesはそのままのコマンドで動作するのですが。
iptablesはnftablesのサブセットのような扱いになっています。
iptablesと比較しながら、超基本的な書式を学ぶことにした。
- nftables
- nftables で masquerade する。
- nftablesの特徴
- 実際にやってみる。
- router01で転送を有効にする
- 最初にiptables(legacy nft)を試す
- nftables で masquerade する。
- SNATする。
- iptables-translate
- ACTIONの決め方 ACCEPT/DENY/REJECT
- ルールをすべて表示して、個別に削除する
- 参考資料
nftables で masquerade する。
iptables では一行でかけたものが3行に分かれました。
nft add table nat nft add chain nat postrouting { type nat hook postrouting priority 100 \; } nft add rule nat postrouting oifname "eth1" masquerade
え?三行?ですか。とおもったので、nftablesにチャレンジすることにした。
ただし、ゼロからの初期状態から追加するには、prerouting でnftにパケットを食わせる必要があります。(合計4行になります。)
nft add chain nat prerouting { type nat hook prerouting priority -100 \; }
最初にテーブル追加
最初にテーブルを追加します。
nft add table my_nat
iptablesのときは nat というテーブルは用意がありましたが、nftablesでは空っぽからスタートするので、自分で作ります。名前は自由に決められるが自由すぎて迷う。名前空間とか用意してくれればいいのに。
テーブルにフィルタチェーンを追加
次に、作ったテーブルにチェインを追加します。
nft add chain my_nat postrouting { type nat hook postrouting priority 100 \; }
masquerade する
チェインに入ったものをマスカレードするように書きます。
nft add rule my_nat postrouting oifname "eth1" masquerade
nftablesの特徴
テーブルにチェインを作って、rule書く
table -> chain -> rule
実際にやってみる。
クライアントがRouterを経由してマスカレードで出ていく構成を考えます。
home-lan - router - lxdbr - client
次のような構成をLXDで作ります。
router としてのマシン作る
lxc launch ubuntu: router01
2本目のNICを挿し込む
lan 側のnic をmacvlanとして、router01 に挿し込む
lxc config device add router01 mytap0 nic nictype=macvlan parent=eth0 lxc exec router01 dhclient -v eth1
クライアントを作成する
lxc launch ubuntu: client01
2台分をアップデートすると時間がもったいないので、apt-cacherを使う。
echo 'Acquire::HTTP::Proxy "http://apt-cacher.lan:3142";' |sudo tee /etc/apt/apt.conf.d/01proxy apt update apt upgrade -y
nftables を確認する
# iptables --version iptables v1.8.7 (nf_tables)
2つとも、iptablesはnftables提供のものを使っていることを確認。
client01 の通信を router 経由にする
変更前の確認
root@client01:~# ip route default via 10.17.238.1 dev eth0 proto dhcp src 10.17.238.67 metric 100 10.17.238.1 dev eth0 proto dhcp scope link src 10.17.238.67 metric 100
変更
ROUTER01_IP=10.17.238.10 CLIENT01_IP=10.17.238.202 ip route del default ip route add default via $ROUTER01_IP dev eth0 src $CLIENT01_IP metric 100
変更後の確認
root@client01:~# ip route show default via 10.17.238.127 dev eth0 src 10.17.238.67 metric 100 10.17.238.0/24 dev eth0 proto kernel scope link src 10.17.238.67 metric 100 10.17.238.1 dev eth0 proto dhcp scope link src 10.17.238.67 metric 100
router01で転送を有効にする
パケット転送を有効にする。
sysctl -a | grep -F all.forward sysctl -w net.ipv4.conf.all.forwarding=1
準備は完了です。
以上で準備は完了です。
最初にiptables(legacy nft)を試す
iptablesはnft です。
# iptables --version iptables v1.8.7 (nf_tables)
nft でもiptablesコマンドはそのまま使えるので、よく知られたiptablesのコマンドでマスカレードを作ってみます。
iptables ( legacy) router01 でNAT/MASQUERADEする
LAN_DEV=eth0 LAN_NET=10.17.238.0/24 WAN_DEV=eth1 WAN_NET=0.0.0.0/0 iptables -t nat -A POSTROUTING -s $LAN_NET -d $WAN_NET -o $WAN_DEV -j MASQUERADE
iptables (legacy)として互換レイヤーで扱う。とiptablesと同じ書き方で同じ動作がするようになってるので、最初に互換レイヤーで試す。
疎通確認する
router01 でtcpdumo を見ながら、client01 からping を送信してみます。 ping が応答してきます。
root@client01:~# ping 1.1.1.1 PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data. 64 bytes from 1.1.1.1: icmp_seq=1 ttl=55 time=8.67 ms 64 bytes from 1.1.1.1: icmp_seq=2 ttl=55 time=8.51 ms 64 bytes from 1.1.1.1: icmp_seq=3 ttl=55 time=8.70 ms
NATで正しく通信できることがわかります。
iptablesを消す
疎通が確認できたので、iptablesのテーブルをflush して何もない状態に戻します。
iptables -t nat -F iptables -F
iptables をリセットして 初期状態に戻した。
nftables で masquerade する。
今度は、nft コマンド(nftables)を使ってマスカレードを試みる。
## テーブル追加 nft add table nat ## テーブルにフィルタチェーンを追加 nft add chain nat postrouting { type nat hook postrouting priority 100 \; }
masquerade する
nft add rule nat postrouting oifname "eth1" masquerade
iptablesとnftablesは同じだけど、iptablesは初期テーブルを持ってるが、nftablesはすべてを自分で書く必要がある。
初期テーブルnatくらい持っておけよとおもうが、natテーブルが大量になったり依存が増えて管理が大変になるだろうことを思うと、ルールセットごとに個別に指定するためには名前で分けるほうが、依存関係をなくせてスッキリするんだろうな。でも、単純なルールで使うカジュアル用途だと不便な気もする。
疎通確認
root@client01:~# ping 1.1.1.1 PING 1.1.1.1 (1.1.1.1) 56(84) bytes of data. 64 bytes from 1.1.1.1: icmp_seq=1 ttl=55 time=8.72 ms 64 bytes from 1.1.1.1: icmp_seq=3 ttl=55 time=9.42 ms 64 bytes from 1.1.1.1: icmp_seq=4 ttl=55 time=9.03 ms
iptablesの状態を確認
nftが有効な状態でiptablesの状態を確認すると・・・
root@router01:~# iptables -L -t nat free(): double free detected in tcache 2 Aborted (core dumped)
コアダンプになった・・・ nat
いう名前だとcore dump になった。なぜだ。
別の名前にすると動く、どうやらnatというテーブル名を使うべきではないようだ。
nft テーブルを確認
全ルールを見てみる。
nft list ruleset | less
全テーブルを見る。
nft list tables
テーブルを指定して中身を見る
nft list table nat
テーブル名を指定するとき、自分で決めた名前を使う。
nft テーブルを削除
作ったテーブルを削除してもとに戻します。
nft delete table nat
テーブルに名前をつける。
先程のnat作成は、テーブル名がnat
なので、具体的な名前をつける。
nft add table ip my_nat nft add chain ip my_nat postrouting { type nat hook postrouting priority 100 \; } nft add rule ip my_nat postrouting oifname "eth1" masquerade
ip
はアドレスファミリipv4
を示す。省略可能
削除のときも名前を使う。
nft delete table ip my_nat
名前をnat 以外にすると、コアダンプもなくなる。
root@router01:~# iptables -L -t nat Chain PREROUTING (policy ACCEPT) (略 Chain POSTROUTING (policy ACCEPT) target prot opt source destination root@router01:~# iptables -L -t nat Chain PREROUTING (policy ACCEPT) (略
nftables で作った my_nat テーブルは、iptablesから見えない。期待通りの動作ですね。
root@router01:~# iptables -L -t my_nat iptables v1.8.7 (nf_tables): table 'my_nat' does not exist Perhaps iptables or your kernel needs to be upgraded.
natという名前以外を使うとコアダンプしなくなったので、名前には注意したほうがいいかもしれない。
名前にドット( . ) が使えるようなので、簡易的な名前空間っぽいものが作れるかもしれない。大文字小文字は区別しない(はず)なのでいい感じに記号を使う必要がある。
SNATする。
SNAT も iptables で旧来スタイルは、次のようになる。
## 外向きのIPでSNATする ROUTER01_IP=192.168.2.183 iptables -t nat -A POSTROUTING -o eth1 -j SNAT --to-source $ROUTER01_IP ## 削除する iptables -t nat -D POSTROUTING -o eth1 -j SNAT --to-source $ROUTER01_IP
同じことを nftables で書くには、次のようになる。
ROUTER01_IP=192.168.2.183 nft add table ip my_snat nft add chain ip my_snat postrouting { type nat hook postrouting priority 100 \; } nft add rule ip my_snat postrouting oifname "eth1" snat to $ROUTER01_IP
削除する。
ip
は ipv4の意味で、省略可能。
nft delete table ip my_snat
iptables-translate
iptables-translate をつかうと、iptablesの書式をnftablesに変換できる。
最初に書いた iptables の書き方をtranslate してみる。
LAN_DEV=eth0 LAN_NET=10.17.238.0/24 WAN_DEV=eth1 WAN_NET=192.168.2.0/24 iptables-translate -t nat -A POSTROUTING -s $LAN_NET -d $WAN_NET -o $WAN_DEV -j MASQUERADE
nft add rule ip nat POSTROUTING oifname "eth1" ip saddr 10.17.238.0/24 counter masquerade
変換できるが、そのままコピペでは動かない。上記をよく見ると、iptablesの-d $WAN
が欠損している。tableはnat
であるが、ruleは大文字POSTROUTING
である。chainが無いので動かない。nftablesに必要な、table->rule->chain の包含関係が無い。上記のpostrouting を動かすためには、prerouting でpostrouting にjumpしなくちゃいけない。そのためiptablesルールをnftablesでゼロから書くと3行必要なのだが、translateでは1行になっている。なので、このままで動かない。
このように、nftを少しでも知らないと変換結果を判断できない。またテーブル名称がnatになってしまうのでnft の良さを活かせないと思う。 「iptablesのあれはnftablesではどう書くんだろう。」ってときにサンプルが出てくる。translateの結果は参考程度にとどめたほうがい無難なのではないか。
同じことをnftablesで書くと次のようになる。
WAN_NET=eth1 nft add table nat nft add chain nat postrouting { type nat hook postrouting priority 100\; } nft add rule nat postrouting ip saddr $LAN_NET ip daddr $WAN_NET oifname eth1 masquerade ## 削除 nft delete table ip nat
nat
は自分で決める名前、oif
は oifname
の略、
iptables-translate は、「ルール」を変換するのであって、ルールの入れ物である、チェインやテーブルは自分で作る必要がある。
-s
-d
のようにSRC・DSTのアドレスを指定する場合
次のように、SRC・DSTのアドレスを指定する場合はよくある。
iptables -t nat -A POSTROUTING -s 1.1.1.1 -d 192.168.2.1 -o eth1 -j MASQUERADE
これをtranslate すると次のようになる。
$ iptables-translate -t nat -A POSTROUTING -s 1.1.1.1 -d 192.168.2.1 -o eth1 -j MASQUERADE
translate 結果
nft add rule ip nat POSTROUTING oifname "eth1" ip saddr 1.1.1.1 ip daddr 192.168.2.1 counter masquerade
先程までのテーブルとルールの話を踏まえると、translateの結果をそのまま使えない。POSTROUTING
が大文字なのも慣習的すぎて理解しにくい。counter
も初見ですよね。
nftables で記述するとtanslate の結果を次のように書き換えれば、同じように動くはずである。
WAN_DEV=eth1 LAN_IP=10.17.238.0/24 WAN_IP=1.1.1.1 nft add table my_nat nft add chain my_nat postrouting { type nat hook postrouting priority 100\; } nft add rule my_nat postrouting oif $WAN_DEV ip saddr $LAN_IP ip daddr $WAN_IP masquerade
oifname
は oif
と略称にできる。oifname
は output interface name
の略だと思われる。
ip
は IPv4
のことでip
は省略可能。ただし、ip daddr
とip saddr
につくip
省略不可である。
このように、translate した結果は、そのままコピペしてもえらーになり、参考程度にするのがいいかと思っ割れる。
戻りパケット related
戻りパケットを識別するとき、次のように書く
nft add rule filter input ip daddr 192.168.2.1 ct state established,related accept
戻りパケットを受け入れるように設定する。
上記の例は、次のような構造になっていた。
ADDR=192.168.2.1 MATCH=ct state established,related ACTION=accept nft add rule filter input ip daddr $ADDR $MATCH $ACTION
ACTIONの決め方 ACCEPT/DENY/REJECT
iptablesには、デフォルト・ポリシーがあったが、nftablesにはデフォルトが無い。iptablesでデフォルトDENYで、必要なもの通すといった書き方はなくなってた。
なので、ルールを順番に追加して最終チェックでDENYをアクション登録する
全部拒否して22(ssh)だけを許可する。
input の 22 をACCEPTする(デフォルトポリシーがACCEPT)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT iptables -A INPUT -p tcp -j DROP
input の 22 をACCEPTする(デフォルトポリシーがDROP)
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
nftablesにはデフォルト・ポリシーはACCEPTなので、次のように書く。
nft add table filter nft add chain filter input { type filter hook input priority 0 \; } nft add rule filter input tcp dport 22 accept nft add rule filter input drop
デフォルト・ポリシーは、チェインごとに存在するのでベースチェインに書く。
icmp を許可する
nft 書いてて躓いたのがICMPだった。
v4 icmp は echo-request を明示しないとだめだった。
nft add table filter nft add chain filter input { type filter hook input priority 0 \; } nft insert rule filter input icmp type echo-request accept
ちなみに、上記のinsert
はiptables -I
に相当する。INSERTで先頭に追加する。add
は末尾に追加。
ルールをすべて表示して、個別に削除する
いくつかルールを足して試していて、ミスったものだけを削除したいとき。
iptablesなら IをDに変えればよかった。
iptables -I xxxx iptables -D xxxx
nftablesの場合は、iptablesのように単純でははい。
つぎのように、filter テーブルに chain input を作った場合
nft add table filter nft add chain filter input { type filter hook input priority 0 \; } nft add rule filter input tcp dport 22 accept nft insert rule filter input icmp type echo-request accept nft add rule filter input drop
チェインの中身を表示するとき、handleを明示する。
root@router01:~# nft --handle list chain filter input table ip filter { chain input { # handle 1 type filter hook input priority filter; policy accept; icmp type echo-request accept # handle 3 tcp dport 22 accept # handle 2 drop # handle 4 } }
この --handle
が大事で、追加したルールを個別に削除するときは、ハンドルを使う
個別に、IDを指定で削除
root@router01:~# nft delete rule filter input handle 3 root@router01:~# nft --handle list chain filter input table ip filter { chain input { # handle 1 type filter hook input priority filter; policy accept; tcp dport 22 accept # handle 2 drop # handle 4 } }
削除後の番号を見ると、欠番が生じているのがわかる。インデックスでありながらも欠番がでるので、採番は連番とは限らない。番号には注意を払うこと。
nft --handle list chain filter input nft -a list chain filter input
記述するとhandle は長いので、--handle
は -a
でも代用可能である。
## 次は、できない ## 中身を記述して削除は、将来的なサポート予定らしい nft delete rule filter input tcp dport 22 accept
add をdelete にかえただけの削除はサポートされる「予定」らしい。