DNAT, SNATをつかった通信の転送

グローバルIPをもっていないMySQLに外部から接続したい場合、グローバルIPをもっている別のサーバのiptablesのDNAT,SNATを使って通信を転送することで実現できる。

カーネルパラメータの設定

別のサーバへ転送するにはカーネルパラメータnet.ipv4.ip_forwardを有効にしなければならない。

# 確認
sysctl -a | grep net.ipv4.ip_forward
# 設定
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
sysctl -p
# 確認
sysctl -a | grep net.ipv4.ip_forward

iptablesの設定

PREROUTING, FORWARD

リクエストを受けるとPREROUTINGで処理され、他のサーバへ転送されることになった場合、FORWARDチェインに移る。今回はFORWARDは特に制限を入れずデフォルトACCEPTとしておく。

POSTROUTING

別のサーバへ転送された後は、そのサーバから直接接続元に通信を返そうとしてしまう。しかし接続元とコネクションをはっているのはグローバルIPを持っているサーバであり、グローバルIPをもっていない別のサーバとは通信できないため、このままでは転送しても接続が確立できない。

接続元 -> 【PREROUTING -> FORWARD -> POSTROUTING】 -> 転送先 というようにiptablesのチェインを通るが、POSTROUTINGでこの問題を解決できる。

POSTROUTINGSNATで送信元IPアドレスを変更することができる。

別のサーバのプライベートIPがdestinationになっている場合、PREROUTINGのDNATで転送しようとしているはず。送信元を本来の接続先からこのサーバのプライベートIPに変更することで、通信の戻りについてもこのサーバが中継することができる。

iptablesの設定

*filter
:INPUT DROP [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
#-A FORWARD -p tcp -j LOG --log-prefix "FORWARD "
略
COMMIT

*nat
:PREROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
# -A PREROUTING -p tcp -j LOG --log-prefix "PREROUTING "
-A PREROUTING -p tcp --dport 13306 -j DNAT --to-destination 別のサーバのプライベートIP:3306
# -A POSTROUTING -p tcp -j LOG --log-prefix "POSTROUTING "
-A POSTROUTING -p tcp --dport 3306 -d 別のサーバのプライベートIP -j SNAT --to-source このサーバのプライベートIP
COMMIT

ちなみにここではコメントアウトしているが、各チェインでログを出力した場合、このサーバが中継地点としてFORWARDを大量に行っている様子が見れる。

tailf /var/log/messages | grep 3306