NATインスタンスで通信元インスタンスによって接続元IPアドレスを切り替える

はじめに

AWSで、外部からのアクセスを遮断、あるいは外部へのアクセスを制限することを目的に、外部への通信経路を持たないプライベートサブネットを作成するパターンがあります。この場合プライベートサブネットに配置したインスタンスは外部へ一切通信が出来ないため、ミドルウェアのアップデートや外部サービスとの通信などは、外部アクセスが可能なパブリックサブネットに配置したNATインスタンスを経由するように設計します。

しかし、場合によっては通信元のインスタンスによって、外部に接続する際の接続元のグローバルIPアドレスを切り替えたい場合があります。例えば外部サービスにて接続元グローバルIPアドレスにより振り分けを行っていて、接続元グローバルIPアドレスが全て同じだと正常動作しない場合などです。

この「NATインスタンスで、通信元インスタンスによって、接続元のグローバルIPアドレスを切り替えたい」というwishが今回のスタート地点です。

mat00

やってみる

NATインスタンスはAmazon Linux AMI2014.03で構築しました。NATインスタンス用AMIは使っていません。

準備

NATインスタンスをLaunchする際に、Network Interfaceを2つ割り当てておきます。

nat01また、NATインスタンスのSecurity Groupでは、通信元となるプライベートサブネットに配置されたインスタンスからの通信は許可しておきます。

nat02プライベートサブネットのルーティングテーブルでは、NATインスタンスのネットワークインターフェースをデフォルトゲートに指定します。

nat03

NATインスタンスでは、Source/Dest. CheckをDisabledにする必要があります...が、EC2 DashboardのInstances画面のActionから実行出来る[Change Source/Dest. Check]では、eth0しか変更されません。

nat04

このため、EC2 DashboardのNetwork Interfaces画面のActionから、2つのネットワークインターフェースに対しそれぞれSource/Dest. CheckをDisabledに変更します。

nat05

NATインスタンスの設定

さて、本題です。今回のwishを実現するためにNATインスタンスで行うことは

  • iptablesによるIPマスカレードの設定
  • ip ruleによるルーティングテーブルの設定

の2つになります。

iptablesによるIPマスカレードの設定

まず/etc/sysctl.confを変更し、IPマスカレードをする為のカーネルパラメータの変更を行います。

### net.ipv4.ip_forwardが無効になっていることを確認。併せてsend_redirectsも変更するので確認。
$ sysctl net.ipv4.ip_forward net.ipv4.conf.eth0.send_redirects net.ipv4.conf.eth1.send_redirects
net.ipv4.ip_forward = 0
net.ipv4.conf.eth0.send_redirects = 1
net.ipv4.conf.eth1.send_redirects = 1

### /etc/sysctl.confに設定値を記述。
$ sudo vi /etc/sysctl.conf
net.ipv4.ip_forward = 1
net.ipv4.conf.eth0.send_redirects = 0
net.ipv4.conf.eth1.send_redirects = 0

### /etc/sysctl.confの内容を即時反映。
$ sudo sysctl -p /etc/sysctl.conf

### 反映された値を確認する。
$ sysctl net.ipv4.ip_forward net.ipv4.conf.eth0.send_redirects net.ipv4.conf.eth1.send_redirects
net.ipv4.ip_forward = 1
net.ipv4.conf.eth0.send_redirects = 0
net.ipv4.conf.eth1.send_redirects = 0

次にiptablesコマンドにて、eth0とeth1の2つのネットワークインターフェースで、それぞれIPマスカレードを有効にします。通信元IPアドレスではそのVPC全体のIPサブネットを指定しています。

$ sudo iptables -t nat -A POSTROUTING -o eth0 -s 172.31.0.0/16 -j MASQUERADE
$ sudo iptables -t nat -A POSTROUTING -o eth1 -s 172.31.0.0/16 -j MASQUERADE

### 設定された値を確認
$ sudo iptables -nvL -t nat
Chain PREROUTING (policy ACCEPT 1 packets, 60 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 MASQUERADE  all  --  *      eth0    172.31.0.0/16        0.0.0.0/0           
    0     0 MASQUERADE  all  --  *      eth1    172.31.0.0/16        0.0.0.0/0  

### 設定した値を保存 
$ sudo service iptables save

ip ruleによるルーティングテーブルの設定

iptablesでIPマスカレードの設定を行っただけでは、全ての通信はeth0を経由してしまいます。EC2でネットワークインターフェースを2つ追加すると、以下のように「eth1に割り当てられたIPアドレスのみがIPルールテーブルID:10001を使う」というように設定されています。

$ sudo ip rule
0:	from all lookup local 
32765:	from 172.31.8.208 lookup 10001 
32766:	from all lookup main 
32767:	from all lookup default 

このIPルールテーブルID:10001はeth1で使われています。

$ sudo cat /etc/sysconfig/network-scripts/route-eth1
default via 172.31.0.1 dev eth1 table 10001
default via 172.31.0.1 dev eth1 metric 10001

そこで、eth1から出力したい(eth1に割り当てられたグローバルIPアドレスを接続元IPアドレスとして使いたい)インスタンスからの通信は、全てのこのIPルールテーブルID:10001を使うように設定します。
なお、IPアドレスを直接指定してしまうと、インスタンスのプライベートIPアドレスが変更された場合に動作しなくなってしまうため、AWS CLIなどを組み合わせて適切なIPアドレスが定義されるような仕組みを用いるべきです。

### EC2-Bはeth1から出力されるようにip ruleを追加
$ sudo ip rule add from 172.31.26.176 lookup 10001

### 設定値の確認
$ sudo ip rule
0:	from all lookup local 
32764:	from 172.31.26.176 lookup 10001 
32765:	from 172.31.8.208 lookup 10001 
32766:	from all lookup main 
32767:	from all lookup default 

これで設定は完了です。

動作確認

EC2-Aから外部に接続し、接続元IPアドレスを確認してみます。

$ curl http://www.ugtop.com/index.shtml
---snip---
54.178.213.178<BR>
ec2-54-178-213-178.ap-northeast-1.compute.amazonaws.com<BR>
---snip---

同じようにEC2-Bから外部に接続し、接続元IPアドレスを確認してみます。

$ curl http://www.ugtop.com/index.shtml
---snip---
54.178.197.195<BR>
ec2-54-178-197-195.ap-northeast-1.compute.amazonaws.com<BR>
---snip---

それぞれ違うIPアドレスになっていますね!

まとめ

今回は少々トリッキーな使い方について調べてみました。NATインスタンスを複数Launchするという手もありますが、今回ご紹介した手段を用いることでインスタンス数を増やさずに済みます。