CUBE SUGAR CONTAINER

技術系のこと書きます。

Network Namespace と nftables で Destination NAT を試す

今回は Network Namespace で作ったネットワーク上で nftables 1 を使った Destination NAT を試してみる。 このエントリは、以下のエントリの続きとなっている。

blog.amedama.jp

上記は 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_forward1 を設定する。

$ 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) を使って router203.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 を試してみた。

参考

manpages.ubuntu.com