概要
VPNサーバをAWSに置くことで内部ネットワークに外部からアクセスする手法がありますが、どうしてそれができるのかを知りたくなって調べてみました。
環境
VPNサーバ
- Ubuntu v18.04
- pritunl v1.29.1958.76
クライアント
- macOS Mojave 10.14.2
ネットワーク環境
イメージは以下の図です。
対象 | IP |
---|---|
VPCのIP範囲 | 10.100.0.0/16 |
VPNサーバ (通常NIC) |
10.100.0.11 |
VPNサーバ (VPN NIC) |
192.168.10.1 |
VPCクライアント (通常NIC) |
172.28.10.5 |
VPCクライアント (VPN NIC) |
192.168.10.20 (VPN接続時に 192.168.10.0/24 の範囲で付与される) |
Pritunl設定
Server
VPN接続した時に用意されるVirtual Networkを192.168.10.0/24
にします。
Routing
デフォルトの0.0.0.0/0
は消して以下のように内部ネットワーク10.100.0.0/16
を追加します。
こんな感じになります。
VPNサーバをStartするとどうなるか(サーバ側)
NICが増える
Virtual Network用にNICが増えます。
これは192.168.10.0/24
でのGatewayになります。
$ ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9001 inet 10.100.0.11 netmask 255.255.255.0 broadcast 10.100.0.255 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 tun7: flags=4305<UP,POINTOPOINT,RUNNING,NOARP,MULTICAST> mtu 1500 inet 192.168.10.1 netmask 255.255.255.0 destination 192.168.10.1
NATテーブルが追加される
VPNサーバをStartするとiptables
にNATテーブルが追加されます。
$ sudo iptables -t nat -L -n Chain PREROUTING (policy ACCEPT) target prot opt source destination Chain INPUT (policy ACCEPT) target prot opt source destination Chain OUTPUT (policy ACCEPT) target prot opt source destination Chain POSTROUTING (policy ACCEPT) target prot opt source destination MASQUERADE all -- 192.168.10.0/24 10.100.0.0/16 /* pritunl-5c4abe091024300bbddb2707 */
つまり
パケットのIPヘッダ | 値 |
---|---|
送信元IP | 192.168.10.20 |
宛先IP | 10.100.0.0/16の範囲のIP。VPC内のIP |
のパケット(=VPNクライアントからのVPC内へのパケット)は、
パケットのIPヘッダ | 値 |
---|---|
送信元IP | 10.100.0.11 ※VPNサーバのIP |
宛先IP | 変わらない |
に書き換わることになります。
このIPマスカレードによって、VPC内の各リソースは受け取ったパケットをVPCに存在しない192.168.10.20
ではなく、存在する10.100.0.11
に返すことができるようになります。
このPOSTROUTING
というのはSNATの設定で、送信元 IP アドレスとポート番号の変換を行います。なので上記のように送信元IPがVPNサーバのものに変換されています。
ちなみにPOSTROUTING
は以下の図のタイミングで行われます。
ルーティングテーブルの後で行われる事がわかります。
ref: fosshelp: iptables example DNAT SNAT and MASQUERADE with network namespace
ちなみにルーティングテーブルは以下の通りになってます。
$ netstat -rn Kernel IP routing table Destination Gateway Genmask Flags MSS Window irtt Iface 0.0.0.0 10.100.0.1 0.0.0.0 UG 0 0 0 eth0 10.100.0.0 0.0.0.0 255.255.255.0 U 0 0 0 eth0 10.100.0.1 0.0.0.0 255.255.255.255 UH 0 0 0 eth0 192.168.10.0 0.0.0.0 255.255.255.0 U 0 0 0 tun7
gatewayでの0.0.0.0
はgateway(ルータ)を介さず、直接行き先へ向かってくださいという意味になります。
VPNサーバに接続するとどうなるか(クライアント側)
/etc/resolv.conf の更新
DNSサーバがPritunl側で指定した10.100.0.2
に変わります。
今回だとAmazonProvidedDNSです。
$ cat /etc/resolv.conf # # macOS Notice # # This file is not consulted for DNS hostname resolution, address # resolution, or the DNS query routing mechanism used by most # processes on this system. # # To view the DNS configuration used by this system, use: # scutil --dns # # SEE ALSO # dns-sd(1), scutil(8) # # This file is automatically generated. # nameserver 10.100.0.2
ルーティングテーブルの更新
宛先が10.100.0.0/16
の場合は、Gatewayが192.168.10.1
(VPNサーバのVirtual NetworkのNIC)となり、VPNサーバへ転送されます。
$ netstat -rn Routing tables Internet: Destination Gateway Flags Refs Use Netif Expire default 172.28.0.253 UGSc 76 0 en0 10.100/16 192.168.10.1 UGSc 1 0 utun1 127 127.0.0.1 UCS 0 0 lo0 127.0.0.1 127.0.0.1 UH 162 4618618 lo0 ...
NICが増える
Virtual Network(192.168.10.0/24
)用にNICが増えます。
接続するたびに余っているIPが動的に付与されます。
$ ifconfig eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 9001 inet 172.28.10.5 netmask 255.255.255.0 broadcast 172.28.10.255 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 utun1: flags=8051<UP,POINTOPOINT,RUNNING,MULTICAST> mtu 1500 inet 192.168.10.20 --> 192.168.10.20 netmask 0xffffff00
パケットフロー
パケットの応答は以下のフローとなります。
前提
ローカルPCからVPC内のインスタンス10.100.0.20
へアクセスするとします。
1. クライアント
- 送信元IP
192.168.10.20
、宛先IP10.100.0.20
としてパケットを作る - ルーティングテーブルでは宛先IPが
10.100.0.0/16
の場合、gateway(ルータ)が192.168.0.1
とある - パケットをgateway
192.168.10.1
(=VPNサーバ)へ送る
2. VPNサーバ(NAT)
- 送信元IP
192.168.10.20
、宛先IP10.100.0.20
のパケットが届く - ルーティングテーブルでは宛先IPが
10.100.0.0/24
の場合、gatewayが0.0.0.0
とある - gateway
0.0.0.0
なので、そのまま対象インスタンス10.100.0.20
へ送ろうとする - ↑を送る前にiptablesのNATテーブルによって送信元IP
192.168.10.20
をeth010.100.0.11
に書き換える
3. 対象インスタンス
- 送信元IP
10.100.0.11
、宛先IP10.100.0.20
のパケットが届く - 送信元IP
10.100.0.20
、宛先IP10.100.0.11
のパケットで返信する - ルーティングテーブルには宛先IPが
10.100.0.0/24
の場合、gatewayが0.0.0.0
とある - gateway
0.0.0.0
なので、直接10.100.0.11
(=VPNサーバ)へ送られる
4. VPNサーバ(NAT)
- 送信元IP
10.100.0.20
、宛先IP10.100.0.11
のパケットが届く - NATテーブルで変換したIP
10.100.0.11
を192.168.10.20
に戻す - ルーティングテーブルでは宛先IPが
192.168.10.0/24
の場合、gatewayが0.0.0.0
とある - gateway
0.0.0.0
なので、直接192.168.10.20
(=クライアント)へ送られる
5. クライアント
- 送信元IP
10.100.0.20
、宛先IP192.168.10.20
のパケットが届く
まとめ
VPNのパケットフローを整理し、どのようにVPC内にアクセスするかが分かりました。