下記の様な方法でウェルノウンポートへのアクセスを別の上位ポートへ転送したときに、上位ポートへの直接のアクセスを禁止する方法。
準備
3000 ポートでサーバを立てます。
$ nc -k -l 3000
iptables で 80 ポートに届いたパケットを 3000 ポートへ REDIRECT します。
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000
別ホストから 80 ポートと 3000 ポートに接続すると、どちらでも接続できます。
$ date | nc -n -v 192.0.2.123 80
Connection to 192.0.2.123 80 port [tcp/*] succeeded!
$ date | nc -n -v 192.0.2.123 3000
Connection to 192.0.2.123 3000 port [tcp/*] succeeded!
失敗その1
サーバのバインドアドレスを 127.0.0.1 にしてみました。
$ nc -k -l 127.0.0.1 3000
80 ポートと 3000 ポートの両方で繋がらなくなりました。
$ date | nc -n -v 192.0.2.123 80
nc: connect to 192.0.2.123 port 80 (tcp) failed: Connection refused
$ date | nc -n -v 192.0.2.123 3000
nc: connect to 192.0.2.123 port 3000 (tcp) failed: Connection refused
REDIRECT したからといって eth0 に来たパケットを lo に転送しているわけではないのでダメなのだと思います。
失敗その2
INPUT チェインで 3000 ポートをフィルタしてみました。
$ sudo iptables -F
$ sudo iptables -F -t nat
$ sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000
$ sudo iptables -A INPUT -p tcp --dport 3000 -j LOG
$ sudo iptables -A INPUT -p tcp --dport 3000 -j REJECT --reject-with tcp-reset
両方のポートで繋がらなくなりました。
$ date | nc -n -v 192.0.2.123 80
nc: connect to 192.0.2.123 port 80 (tcp) failed: Connection refused
$ date | nc -n -v 192.0.2.123 3000
nc: connect to 192.0.2.123 port 3000 (tcp) failed: Connection refused
filter テーブルの INPUT よりも nat テーブルの PREROUTING の方が先なので当たり前でした。
成功その1
nat テーブルの PREROUTING よりも前にフィルタすればいいのですが filter テーブルで PREROUTING よりも先に適用されるチェインはありません。
なので mangle テーブルの PREROUTING でマークして、マークされたパケットを filter テーブルの INPUT で REJECT します。
sudo iptables -F
sudo iptables -F -t nat
sudo iptables -F -t mangle
sudo iptables -t mangle -A PREROUTING -p tcp --dport 3000 -j MARK --set-mark 0x01
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000
sudo iptables -A INPUT -p tcp --dport 3000 -m mark --mark 0x01 -j REJECT --reject-with tcp-reset
80 ポートでだけ繋がるようになりました。
$ date | nc -n -v 192.0.2.123 80
Connection to 192.0.2.123 80 port [tcp/*] succeeded!
$ date | nc -n -v 192.0.2.123 3000
nc: connect to 192.0.2.123 port 3000 (tcp) failed: Connection refused
成功その2
REJECT ではなく DROP なら mangle テーブルの PREROUTING チェインでできました。
sudo iptables -F
sudo iptables -F -t nat
sudo iptables -F -t mangle
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000
sudo iptables -t mangle -A PREROUTING -p tcp --dport 3000 -j DROP
パケットがドロップされるのでタイムアウトまで待たされますけどね。
$ date | nc -n -v -w 3 192.0.2.123 80
Connection to 192.0.2.123 80 port [tcp/*] succeeded!
$ date | nc -n -v -w 3 192.0.2.123 3000
nc: connect to 192.0.2.123 port 3000 (tcp) timed out: Operation now in progress
成功その3
nat テーブルの PREROUTING でリッスンしていない適当なポートに REDIRECT すれば、REJECT と同じような動作になります。
sudo iptables -F
sudo iptables -F -t nat
sudo iptables -F -t mangle
sudo iptables -t nat -A PREROUTING -p tcp --dport 3000 -j REDIRECT --to-ports 1
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-ports 3000
1 ポートでリッスンしていないことが前提になりますけど。
$ date | nc -n -v 192.0.2.123 80
Connection to 192.0.2.123 80 port [tcp/*] succeeded!
$ date | nc -n -v 192.0.2.123 3000
nc: connect to 192.0.2.123 port 3000 (tcp) failed: Connection refused
さいごに
「成功その1」が一番妥当っぽい気がします。
「成功その2」は mangle テーブルで DROP とかしてもいいんですかね?
「成功その3」はちょっとワイルドすぎると思います。