Carpe Diem

備忘録

VPN接続するとどうして別ネットワークにアクセスできるのか

概要

VPNサーバをAWSに置くことで内部ネットワークに外部からアクセスする手法がありますが、どうしてそれができるのかを知りたくなって調べてみました。

環境

VPNサーバ

  • Ubuntu v18.04
  • pritunl v1.29.1958.76

クライアント

ネットワーク環境

イメージは以下の図です。

f:id:quoll00:20190125234530p:plain

対象 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にします。

f:id:quoll00:20190125173128p:plain

Routing

デフォルトの0.0.0.0/0は消して以下のように内部ネットワーク10.100.0.0/16を追加します。

f:id:quoll00:20190125173203p:plain

こんな感じになります。 f:id:quoll00:20190125173249p:plain

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は以下の図のタイミングで行われます。
ルーティングテーブルの後で行われる事がわかります。

f:id:quoll00:20190125185225p:plain

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.0gateway(ルータ)を介さず、直接行き先へ向かってくださいという意味になります。

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の場合は、Gateway192.168.10.1VPNサーバの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. クライアント

  1. 送信元IP192.168.10.20、宛先IP10.100.0.20としてパケットを作る
  2. ルーティングテーブルでは宛先IPが10.100.0.0/16の場合、gateway(ルータ)が192.168.0.1とある
  3. パケットをgateway192.168.10.1(=VPNサーバ)へ送る

2. VPNサーバ(NAT)

  1. 送信元IP192.168.10.20、宛先IP10.100.0.20のパケットが届く
  2. ルーティングテーブルでは宛先IPが10.100.0.0/24の場合、gateway0.0.0.0とある
  3. gateway0.0.0.0なので、そのまま対象インスタンス10.100.0.20へ送ろうとする
  4. ↑を送る前にiptablesNATテーブルによって送信元IP192.168.10.20をeth010.100.0.11に書き換える

3. 対象インスタンス

  1. 送信元IP10.100.0.11、宛先IP10.100.0.20のパケットが届く
  2. 送信元IP10.100.0.20、宛先IP10.100.0.11のパケットで返信する
  3. ルーティングテーブルには宛先IPが10.100.0.0/24の場合、gateway0.0.0.0とある
  4. gateway0.0.0.0なので、直接10.100.0.11(=VPNサーバ)へ送られる

4. VPNサーバ(NAT)

  1. 送信元IP10.100.0.20、宛先IP10.100.0.11のパケットが届く
  2. NATテーブルで変換したIP10.100.0.11192.168.10.20に戻す
  3. ルーティングテーブルでは宛先IPが192.168.10.0/24の場合、gateway0.0.0.0とある
  4. gateway0.0.0.0なので、直接192.168.10.20(=クライアント)へ送られる

5. クライアント

  1. 送信元IP10.100.0.20、宛先IP192.168.10.20のパケットが届く

まとめ

VPNのパケットフローを整理し、どのようにVPC内にアクセスするかが分かりました。

ソース