こんにちは、えのもとです。

弊社では本番リリース直前検証環境として、ステージング環境を利用しています。
この環境が古くなってしまったため、環境再構築を実施することになりました。

再構築する上で、今後のこともあるので再構築しやすい環境に切り替えることにしました。
構築上でポイントとなったのは以下の2つです。
  • 本番環境はVMで稼働しているので、そのバックアップを活用する
  • サーバ設定(ネットワーク設定など)はできる限り変更したくない
これらを達成するため、閉鎖されたネットワークを構築しその中に環境を構築することにしました。そこで問題となるのは閉鎖された環境内外との通信なのですが、iptablesを利用してnatサーバを構築することで解決しました。今回はこのiptablesの検証の様子を紹介します。

NATとiptablesについて

今回環境を構築するに当たり、以下を参考にしました。

@IT:第10回 IPパケットの構造とIPフラグメンテーション
@IT:natテーブルを利用したLinuxルータの作成

要点だけ羅列すると以下のような内容になります。
  • パケット内IPヘッダに送信元アドレスと宛先アドレスが記載されている。
  • 送信元アドレスを変換するNATのことをSNAT
    宛先アドレスを書き換えるNATのことをDNATと呼ぶ。
  • iptablesのSNATはPOSTROUTINGチェインで、DNATはPREROUTINGチェインで行う。


検証内容と環境の紹介


存在しないIPアドレス宛てのパケットをiptablesでnatし、別のIPアドレスで受けとることができるか検証しました。検証には以下のような環境を利用しました。

セグメント
  1. A(192.168.100.0/24)
  2. B(192.168.200.0/24)
仮想マシン
  1. vm01           eth0:【 192.168.100.1 】
  2. vm02           eth0:【 192.168.100.2 】 eth1:【 192.168.200.2 】
  3. vm03           eth0:【 192.168.200.3 】
  4. gateway_A   eth0:【 192.168.100.254 】
  5. gateway_B   eth0:【 192.168.200.254 】

eno_2015120901

vm01から存在しないIP【 192.168.100.3 】 宛にパケットを送り、
vm02でNATさせてvm03のIP【 192.168.200.3 】へ届けます。
vm01からするとIP【 192.168.100.3 】 が存在するように見えます。(赤線)
実際はvm02を経由してセグメントBにいるvm03へ届きます。(青線)

eno_2015120902


事前確認

iptablesに余計な設定があると誤作動の原因になるので、一旦どのサーバもクリアしておきます。

# iptables クリア
iptables -F
iptables -F -t nat
# iptables 確認
iptables -L
iptables -L -t nat
# iptables 保存
service iptables save

また、vm02はパケットを転送する必要があるので以下の設定を加えます。
vi /etc/sysctl.conf
#net.ipv4.ip_forward=0
net.ipv4.ip_forward=1

sysctl -p



ステップ 1 : vm01にルーティング追加

手始めにvm01から【 192.168.100.3 】宛のpingを実行し、その様子をvm01とvm02の両方でtcpdumpを使いパケットをキャプチャして確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
From 192.168.100.1 icmp_seq=2 Destination Host Unreachable
From 192.168.100.1 icmp_seq=3 Destination Host Unreachable
From 192.168.100.1 icmp_seq=4 Destination Host Unreachable

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:28:24.003479 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:25.003469 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:26.003479 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46
13:28:28.004484 ARP, Request who-has 192.168.100.3 tell 192.168.100.1, length 46

vm01から【 192.168.100.3 】へパケットを送る場合、同一セグメント内なのでゲートウェイなどは経由せず直接通信することになります。直接通信するためには相手のMACアドレスを知る必要があります。ARPリクエストをブロードキャストへ送信しMACアドレスを探している様子が確認できました。

これを無理やりvm02へ送るようにvm01にスタティックルーティングを追加します。

# vm01
[root@vm01 ~]# ip route add 192.168.100.3 via 192.168.100.2 dev eth0
[root@vm01 ~]# ip route show
192.168.100.3 via 192.168.100.2 dev eth0
192.168.100.0/24 dev eth0  proto kernel  scope link  src 192.168.100.1
169.254.0.0/16 dev eth0  scope link  metric 1002
default via 192.168.100.254 dev eth0

再度vm01から【 192.168.100.3 】へpingを実行し確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
From 192.168.100.2: icmp_seq=2 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2: icmp_seq=3 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2: icmp_seq=4 Redirect Host(New nexthop: 192.168.100.3)
From 192.168.100.2 icmp_seq=2 Destination Host Unreachable
From 192.168.100.2 icmp_seq=3 Destination Host Unreachable
From 192.168.100.2 icmp_seq=4 Destination Host Unreachable

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:48:19.136230 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 1, length 64
13:48:20.135528 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 2, length 64
13:48:21.135501 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 3, length 64
13:48:22.135497 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 28934, seq 4, length 64

【 192.168.100.3 】宛のパケットがvm02へ届くようになりました。



ステップ2 : vm02にDNAT設定を追加

次に存在しない宛先IPアドレス【 192.168.100.3 】をvm03【 192.168.200.3 】変換するため、vm02のiptablesにDNATの設定を追加します。

# vm02
[root@vm02 ~]# iptables -t nat -A PREROUTING -d 192.168.100.3 -i eth0 -j DNAT --to-destination 192.168.200.3
[root@vm02 ~]# iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       all  --  0.0.0.0/0            192.168.100.3       to:192.168.200.3

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination


vm01から【 192.168.100.3 】へpingを実行し、vm02とvm03で確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.

# vm02
[root@vm02 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:55:28.318556 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 1, length 64
13:55:28.318586 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 1, length 64
13:55:29.318498 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 2, length 64
13:55:29.318513 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 2, length 64
13:55:30.318483 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 3, length 64
13:55:30.318496 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 3, length 64
13:55:31.318515 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 30982, seq 4, length 64
13:55:31.318529 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 4, length 64

# vm03
[root@vm03 ~]# tcpdump -i any src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
13:55:28.381794 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 1, length 64
13:55:29.381711 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 2, length 64
13:55:30.381702 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 3, length 64
13:55:31.381730 IP 192.168.100.1 > 192.168.200.3: ICMP echo request, id 30982, seq 4, length 64

vm03にvm01のPINGが届いていることが確認できました。



ステップ3 : vm02にSNAT(NAPT)設定を追加

vm01から送るpingは無事vm03へ届けることができたのですが、vm03がセグメントAの情報を持っていないため、送信元であるvm01に返信することができません。そのため、vm02へSNAT(NAPT)を追加します。

# vm02
[root@vm02 ~]# iptables -t nat -A POSTROUTING -o eth1 -s 192.168.100.0/24  -j MASQUERADE
[root@vm02 ~]# iptables -L -n -t nat
Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination
DNAT       all  --  0.0.0.0/0            192.168.100.3       to:192.168.200.3

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination
MASQUERADE  all  --  192.168.100.0/24     0.0.0.0/0

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

確認します。

# vm01
[root@vm01 ~]# ping 192.168.100.3
PING 192.168.100.3 (192.168.100.3) 56(84) bytes of data.
64 bytes from 192.168.100.3: icmp_seq=1 ttl=63 time=0.235 ms
64 bytes from 192.168.100.3: icmp_seq=2 ttl=63 time=0.164 ms
64 bytes from 192.168.100.3: icmp_seq=3 ttl=63 time=0.164 ms

# vm02
[root@vm02 ~]# tcpdump -i eth0 src host 192.168.100.1 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth0, link-type EN10MB (Ethernet), capture size 65535 bytes
14:04:40.915680 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.915507 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.915500 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.915525 IP 192.168.100.1 > 192.168.100.3: ICMP echo request, id 35078, seq 4, length 64

# vm02
[root@vm02 ~]# tcpdump -i eth1 src host 192.168.200.2
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on eth1, link-type EN10MB (Ethernet), capture size 65535 bytes
14:04:40.915714 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.915522 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.915513 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.915546 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 4, length 64

# vm03
[root@vm03 ~]# tcpdump -i any src host 192.168.200.2 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 65535 bytes
14:04:40.978924 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 1, length 64
14:04:41.978721 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 2, length 64
14:04:42.978718 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 3, length 64
14:04:43.978754 IP 192.168.200.2 > 192.168.200.3: ICMP echo request, id 35078, seq 4, length 64

無事にpingが返ってくることが確認できました。



まとめ

作成した環境を利用して、どんなサービスが利用できるのかも検証しました。

動作OK : ssh http https dns mysql
動作NG : ftp oracle

ipヘッダ以外でipのやり取りをするものについては正常に動作しないのですが、動作したものについては、特に問題もなく使えることが分かりました。

以上です。何かの役に立てばと思います。