2013/02/02(Sat)iptablesで特定のポートを別のホストへ転送する方法

2013/02/02 17:04 Software::Linux
諸般の事情でネットワークセグメントを分けたのだけど、どうしてもあるポートだけ疎通させたいと思ったので、iptablesのNAT機能を使って実現してみた。
ルールの書き方はiptables本来のfilterとは少し違うのと、他のサイトでは解説が不足している点があるな、と思ったのでしっかり書き留めておく。

やろうとしていること

iptables-nat1.png

この図の通り。
今回、iptablesを使ってNATさせる箱は、192.168.1.30と10.0.2.40と二つのサブネットのIPを持っていなければならない。

しかしクライアント(192.168.1.20)とサーバ(10.0.2.50)となる2ホストに、スタティックルートを書く必要はない所がポイント。
なぜならNAT箱でアドレスを変換するので、クライアントは192.168.1.30さえ到達可能であればよい。同様にサーバも10.0.2.40には到達可能なので、実現できる。

サンプル構成

iptables-nat2.png

今回ルータは関係無いので図から削除。
クライアントはサーバのTCP80番ポートを開きたいのだが、直接到達できないので、代わりにNAT箱の192.168.1.30:1234を叩くことによって開く。

ip_forward

まずは、NAT箱にパケットのルーティングをさせるように設定。どうせなので、設定ファイルにも書き込んでおく。
# sysctl -w net.ipv4.ip_forward=1

# echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf

NATテーブル

まずは、アドレスを変換するルール。
ちなみに以下で出てくるPREROUTINGとPOSTROUTINGというチェインの間に、FORWARDチェインが処理されると考えると良い。参考

NAT箱の192.168.1.30:1234宛に届いたパケットを、10.0.2.50:80へ投げるというNATを書く。
# iptables -t nat -A PREROUTING -m tcp -p tcp --dst 192.168.1.30 --dport 1234 -j DNAT --to-destination 10.0.2.50:80
しかし、このままだと宛先アドレスを書き換えるだけなので、サーバ(10.0.2.50)は192.168.1.20という知らないサブネットから受信することになり、戻りのパケットは自分のデフォルトゲートウェイ10.0.2.1へ送りつける。
これではマズいので、NATしたパケットの送信元アドレスを10.0.2.40に変更するのがミソ。
"10.0.2.50:80宛のパケットは、NAT箱の10.0.2.40というアドレスに付け替えてから送信する"というルールを書く。
# iptables -t nat -A POSTROUTING -m tcp -p tcp --dst 10.0.2.50 --dport 80 -j SNAT --to-source 10.0.2.40
逆方向に関しては、このルールだけで自動的に処理されるため心配しなくて良い。
クライアントの送信元ポート番号をきちんと覚えていて、そこに向かって戻りパケットを変換してくれる。

Filterテーブル

前述のルーティングだけ紹介しているサイトが多かったのだが、本来はここからきちんとパケット転送させるためのフィルタも書かなくてはならない。
用心深い設定をしている人なら、チェインのデフォルトポリシーがDROPだろうから、上の設定だけではパケットは飛ばないはず。
# iptables -nL FORWARD

Chain FORWARD (policy DROP)

target     prot opt source               destination

というわけで、こんな感じで転送するパケットを許可。
NATのPREROUTINGを通って変換されたアドレスに対して許可ルールを書いておく。
# iptables -A FORWARD -m tcp -p tcp --dst 10.0.2.50 --dport 80 -j ACCEPT
忘れちゃいけないのが、戻りのパケットも許可するということ。これはTCPのフラグを見て通すようにするおなじみのやり方で。
# iptables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

追記

今回は、MTUが揃っているので特にPath MTU周りの心配をすることはないのだけど、MTUが違うサブネット同士を結ぶ場合には以下の設定も入れておくと吉。
# iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

参考文献


DNATの説明があってもSNATの説明がないサイトが多かったり、FORWARDチェインをデフォルトをDROPにしてたり、戻りのFORWARDが落ちていることに気づかず、結構手間取った。