今回は Network Namespace で作ったネットワーク上で nftables 1 を使った Destination NAT を試してみる。 このエントリは、以下のエントリの続きとなっている。
上記は Source NAT だったのが、今回は Destination NAT になっている。 使っているネットワーク構成は変わらない。
Destination NAT は、よく「ポートを開ける」とか「ポートを開放する」といった表現をされるもの。 ようするに LAN 側のノードに対して、インターネットを起点とした通信を可能にする。
使った環境は次のとおり。
$ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=22.04 DISTRIB_CODENAME=jammy DISTRIB_DESCRIPTION="Ubuntu 22.04.3 LTS" $ uname -srm Linux 5.15.0-87-generic x86_64 $ nft --version nftables v1.0.2 (Lester Gooch)
もくじ
下準備
あらかじめ必要なパッケージをインストールしておく。
$ sudo apt-get -y install nftables iproute2 tcpdump
nftables のサービスを稼働させる。
$ sudo systemctl start nftables
$ sudo systemctl enable nftables
nftables のカーネルモジュールがロードされていることを確認する。
$ lsmod | grep ^nf_tables nf_tables 258048 0
Network Namespace でネットワークを作成する
作成するネットワークの論理構成を以下に示す。 構成は Source NAT で使ったものと変わらない。
203.0.113.0/24
をグローバル、192.0.2.0/24
をプライベートなセグメントに見立てている。
Destination NAT の例では、宛先 IP アドレスが 203.0.113.254
のパケットを lan
の持っている 192.0.2.1
に書き換える。
ただし、書き換えるのはトランスポート層のプロトコルが TCP でポート番号が 54321
の場合だけに限る。
ここからは Network Namespace を使ってネットワークを作成していく。 といっても Source NAT の時とやることは変わらない。 まずは Network Namespace を作成する。
$ sudo ip netns add lan $ sudo ip netns add router $ sudo ip netns add wan
次に Network Namespace 同士をつなぐ veth インターフェイスを追加する。
$ sudo ip link add lan-veth0 type veth peer name gw-veth0 $ sudo ip link add wan-veth0 type veth peer name gw-veth1
作成したインターフェイスを Network Namespace に所属させる。
$ sudo ip link set lan-veth0 netns lan $ sudo ip link set gw-veth0 netns router $ sudo ip link set gw-veth1 netns router $ sudo ip link set wan-veth0 netns wan
インターフェイスの状態を UP に設定する。
$ sudo ip netns exec lan ip link set lan-veth0 up $ sudo ip netns exec router ip link set gw-veth0 up $ sudo ip netns exec router ip link set gw-veth1 up $ sudo ip netns exec wan ip link set wan-veth0 up
lan
について、インターフェイスに IP アドレスを付与する。
また、デフォルトルートを設定する。
$ sudo ip netns exec lan ip address add 192.0.2.1/24 dev lan-veth0 $ sudo ip netns exec lan ip route add default via 192.0.2.254
router
について、インターフェイスに IP アドレスを付与する。
また、ルータとして動作するようにカーネルパラメータの net.ipv4.ip_forward
に 1
を設定する。
$ sudo ip netns exec router ip address add 192.0.2.254/24 dev gw-veth0 $ sudo ip netns exec router ip address add 203.0.113.254/24 dev gw-veth1 $ sudo ip netns exec router sysctl net.ipv4.ip_forward=1
最後に wan
について、インターフェイスに IP アドレスを付与する。
また、デフォルトルートを設定する。
$ sudo ip netns exec wan ip address add 203.0.113.1/24 dev wan-veth0 $ sudo ip netns exec wan ip route add default via 203.0.113.254
nftables を設定する
ここからは nftables を使って Destination NAT を設定していく。
nftables の設定は nft list ruleset
コマンドで確認できる。
初期状態では何も設定されていないため、結果は何も表示されない。
$ sudo ip netns exec router nft list ruleset
テーブルを追加する
まずは nft create table
コマンドでテーブルを追加する。
テーブルはアドレスファミリとチェーンタイプを指定して追加する。
$ sudo ip netns exec router nft create table ip nat
上記ではアドレスファミリが ip
でチェーンタイプが nat
のテーブルを作っている。
テーブルを追加すると、次のように nft list ruleset
の結果にテーブルが表示される。
$ sudo ip netns exec router nft list ruleset table ip nat { }
チェーンを追加する
続いて、処理のタイミングを表すチェーンをテーブルに追加する。
以下では先ほど作った ip nat
のテーブルに PREROUTING
という名前でチェーンを追加している。
カッコ内は追加するチェーンの種類と、処理されるタイミングを示している。
$ sudo ip netns exec router nft add chain ip nat PREROUTING { type nat hook prerouting priority dstnat\; }
次のように nft list ruleset
の結果にチェーンが表示される。
$ sudo ip netns exec router nft list ruleset table ip nat { chain PREROUTING { type nat hook prerouting priority dstnat; policy accept; } }
ルールを追加する
最後に、具体的な処理を表すルールをチェーンに追加する。
以下では、先ほど作った ip nat
テーブルの PREROUTING
チェーンにルールを追加している。
$ sudo ip netns exec router nft add rule ip nat PREROUTING ip daddr 203.0.113.254 tcp dport 54321 dnat to 192.0.2.1
上記では、宛先 IP アドレスが 203.0.113.254
で TCP の宛先ポートが 54321
ポートを 192.0.2.1
に Destination NAT で転送するように指定している。
次のように nft list ruleset
の結果にルールが表示される。
$ sudo ip netns exec router nft list ruleset table ip nat { chain PREROUTING { type nat hook prerouting priority dstnat; policy accept; ip daddr 203.0.113.254 tcp dport 54321 dnat to 192.0.2.1 } }
動作を確認する
すべての設定が完了したので、ここからは動作を確認していこう。
まずは lan
で nc(1) を使って TCP の 54321 ポートで通信を待ち受けておく。
$ sudo ip netns exec lan nc -lnv 54321
次に、別のターミナルを開いて、同様に wan
側でも TCP の 54321 ポートに関する通信をキャプチャできるようにしておく。
$ sudo ip netns exec wan tcpdump -tnl -i wan-veth0 "tcp and port 54321"
さらに別のターミナルを開いて lan
側でも TCP の 54321 ポートに関する通信をキャプチャできるようにする。
$ sudo ip netns exec lan tcpdump -tnl -i lan-veth0 "tcp and port 54321"
準備ができたら wan
で nc(1) を使って router
の 203.0.113.254
の 54321 ポートに接続する。
$ sudo ip netns exec wan nc 203.0.113.254 54321
その上で lan
側のキャプチャを確認してみよう。
すると 203.0.113.1
が送信元で 192.0.2.1
を宛先にしたパケットを起点に通信が発生していることが確認できる。
$ sudo ip netns exec lan tcpdump -tnl -i lan-veth0 "tcp and port 54321" tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on lan-veth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes IP 203.0.113.1.40154 > 192.0.2.1.54321: Flags [S], seq 2852554981, win 64240, options [mss 1460,sackOK,TS val 2906027089 ecr 0,nop,wscale 7], length 0 IP 192.0.2.1.54321 > 203.0.113.1.40154: Flags [S.], seq 3924803357, ack 2852554982, win 65160, options [mss 1460,sackOK,TS val 451299770 ecr 2906027089,nop,wscale 7], length 0 IP 203.0.113.1.40154 > 192.0.2.1.54321: Flags [.], ack 1, win 502, options [nop,nop,TS val 2906027089 ecr 451299770], length 0
上記は Destination NAT によって、宛先 IP アドレスが書き換えられたことによって生じている。
同様に wan
側のキャプチャも確認しよう。
すると、こちらでは 203.0.113.1
が送信元で 203.0.113.254
が宛先のパケットが起点になっていることが確認できる。
$ sudo ip netns exec wan tcpdump -tnl -i wan-veth0 "tcp and port 54321" tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on wan-veth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes IP 203.0.113.1.49200 > 203.0.113.254.54321: Flags [S], seq 3470368251, win 64240, options [mss 1460,sackOK,TS val 2906122441 ecr 0,nop,wscale 7], length 0 IP 203.0.113.254.54321 > 203.0.113.1.49200: Flags [S.], seq 2999511574, ack 3470368252, win 65160, options [mss 1460,sackOK,TS val 451395122 ecr 2906122441,nop,wscale 7], length 0 IP 203.0.113.1.49200 > 203.0.113.254.54321: Flags [.], ack 1, win 502, options [nop,nop,TS val 2906122441 ecr 451395122], length 0
つまり、宛先 IP アドレスが 203.0.113.254
で TCP の宛先ポート番号も 54321 番だったことから、先ほど設定した nftables のルールに合致した。
その結果として Destinatio NAT の処理が実行されて宛先 IP アドレスが 203.0.113.254
から 192.0.2.1
に書き換えられたというわけ。
まとめ
今回は Network Namespace を使って作成したネットワーク上で、nftables を使って Destination NAT を試してみた。