80番ポートへ届いたパケットをiptablesでローカルの上位ポートに転送する


例えばNode.jsとかで作ったWEBサービスを外部に公開しようとした時に、80番ポートで公開したいけど一般ユーザだと1024番より上のポートしか使えない、かと言ってrootで実行はしたくない。そんなときは iptables でREDIRECTまたはDNATしてしまうのが便利です。

雑然としたメモレベルだけど備忘録として書いておく。

簡単な例

3000番ポートで動かしているサービスを80番で公開する例はこんなコマンドを打てはOK。

iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000

設定ファイルに保存する場合はfilterとは別にnatテーブルの設定を追加して↓こんなふうに書けばよいです。

/etc/sysconfig/iptables
*nat
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000
COMMIT

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# filter settings here...
COMMIT

service iptables restart とかしてやれば反映される。

複数IPが割り当てられてるサーバで受けたIPによって転送先のローカルポートを変える例

例えば 10.0.0.80 と 10.0.0.81 のIPを持っていた場合に、10.0.0.81に来たパケットは3001番ポートへ転送、それ以外のIPで受けた場合は3000番ポートへ転送とかする場合は↓こんな風に書けばいいです。

/etc/sysconfig/iptables
*nat
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp -d 10.0.0.81 --dport 80 -j REDIRECT --to-port 3001
-A PREROUTING -p tcp              --dport 80 -j REDIRECT --to-port 3000
COMMIT

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# filter settings here...
COMMIT

こうすることで1台のサーバで開発環境と本番環境を別ポートで使い分けつつ、ポート指定無しのURLでアクセスが出来るようになります。

ローカルプロセスからの通信にも対応する場合はOUTPUTチェーンで書き換える

上述の設定だと外からきたパケットに対しては上手く働くけどローカルのプロセスで発生したパケットには効きません。ローカルプロセスで発生したパケットはPREROUTINGチェインを通らないからです。代わりにOUTEPUTチェインを通ります。なので外からとローカルからの両方対応しようとすると↓こうなります。

/etc/sysconfig/iptables
*nat
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -p tcp -d 10.0.0.81 --dport 80 -j REDIRECT --to-port 3001
-A OUTPUT     -p tcp -d 10.0.0.81 --dport 80 -j REDIRECT --to-port 3001
-A PREROUTING -p tcp -d 10.0.0.80 --dport 80 -j REDIRECT --to-port 3000
-A OUTPUT     -p tcp -d 10.0.0.80 --dport 80 -j REDIRECT --to-port 3000
-A OUTPUT     -p tcp -d 127.0.0.1 --dport 80 -j REDIRECT --to-port 3000
COMMIT

*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
# filter settings here...
COMMIT

OUTPUTチェーンで80版を全部書き換えてしまうと外部へのhttpアクセスもできなくなってしまうので、面倒ですが-dで自分のIP全てを列挙した書き換えを行ってやる必要があります。

DNATでも出来る

DNATの場合は↓こうなる。

iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination :3000

またDNATの場合はポートと一緒にIPも書き換えることが出来るという違いがある。

iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.90:3000

参考

natテーブルのPREROUTINGチェインがどこに効くかとかは↓このへんの図とかが分かりやすいです。