インターネット向けのルート(0.0.0.0/0)を持つ AWS Client VPN エンドポイントでスプリットトンネルを有効化してみた
コンバンハ、千葉(幸)です。
突然ですが、問題です。
以下のような構成の AWS Client VPN エンドポイントがあったとします。
- パブリックサブネットをターゲットネットワークとして関連づけ済み
- Client VPN ルートテーブルで 0.0.0.0/0 向けのルートを追加済み
- 承認済みの宛先ネットワークとして全てのユーザーを対象に 0.0.0.0/0 を定義済み
- スプリットトンネルを有効化済み
ここで、Client VPNエンドポイントに接続したクライアントがインターネット宛の通信を行いたい場合、その経路は以下イメージの①②いずれとなるでしょうか。
- ①VPN トンネルを経由せず直接アクセス
- ②ターゲットネットワークからインターネットゲートウェイ経由でアクセス
はい。
はい。
正解は、①です。
私は「スプリットトンネルを有効化したら、インターネット向けの通信って VPN トンネル経由しないんでしょ?」という雑な理解をしていました。
概ねその通りなのですが、もう少し深掘りして見えてきたものがあるので、今回はそれをとりあげます。
目次
先にまとめ
- スプリットトンネルが無効の場合、クライアントからのすべてのトラフィックは AWS Client VPN トンネルを経由してルーティングされる
- スプリットトンネルが有効の場合、「特定の宛先」に対するトラフィックのみ AWS Client VPN トンネル経由でルーティングされる
- 「特定の宛先」とは、AWS Client VPN エンドポイントルートテーブルで定義されている宛先を指す
- エンドポイントルートテーブルのルートがクライアントデバイスのルートテーブルにプッシュされることで実現する
- ただし 0.0.0.0/0 向けのルートはプッシュされない
やってみた
今回は以下のような構成で試してみます。
- クライアントデバイスが所属する CIDR :192.168.0.0/24
- クライアント CIDR:172.31.0.0/16
- VPC CIDR:192.168.0.0/16
Client VPN ルートテーブルには以下を登録します。
- 192.168.0.0/16(デフォルトで登録される VPC CIDR)
- 0.0.0.0/0
- 8.8.8.8/32
8.8.8.8/32
を含めているのは、デバイスのルートテーブルにルートがプッシュされる挙動が確認しやすいようにという意図です。
以下のパターンにおいて、クライアントデバイスのルートテーブルと、インターネットに接続する際の経路を確認します。
- Client VPN に接続しない場合
- スプリットトンネル無効の場合
- スプリットトンネル有効の場合
インターネットへの接続経路のチェックには、checkip.amazonaws.com
を使用します。接続に使用したグローバル IP を返却してくれるため、それによって判別します。
今回の環境で使用されるグローバル IP は以下の通りです。
- クライアントデバイス環境
- 202.xx.xx.xx
- ターゲットネットワーク ENI のパブリック IP
- 3.xx.xx.xx
- 18.xx.xx.xx
なお、クライアントに関しては以下の条件で試しています。
- クライアントデバイス:macOS Catalina 10.15.7
- VPN クライアント:AWS VPN Client 1.2.0
1. Client VPN に接続しない場合
素の状態でのクライアントデバイスのルートテーブルが以下です。
% netstat -rn Routing tables Internet: Destination Gateway Flags Netif Expire default 192.168.0.1 UGSc en0 127 127.0.0.1 UCS lo0 127.0.0.1 127.0.0.1 UH lo0 169.254 link#6 UCS en0 ! 192.168.0 link#6 UCS en0 ! 192.168.0.1/32 link#6 UCS en0 ! 192.168.0.1 90:f3:5:ee:a2:bb UHLWIir en0 1169 192.168.0.14/32 link#6 UCS en0 ! 224.0.0/4 link#6 UmCS en0 ! 224.0.0.251 1:0:5e:0:0:fb UHmLWI en0 239.255.255.250 1:0:5e:7f:ff:fa UHmLWI en0 255.255.255.255/32 link#6 UCS en0 !
使用されるグローバル IP アドレスを確認すると、手元の環境で使用しているものが返却されました。
% curl checkip.amazonaws.com 202.xx.xx.xx
当然と言えば当然ですね。
2. スプリットトンネル無効の場合
スプリットトンネルを無効にした状態の Client VPN に接続し、試します。
ルートテーブルはこのようになりました。
% netstat -rn Routing tables Internet: Destination Gateway Flags Netif Expire 0/1 172.31.0.33 UGSc utun2 default 192.168.0.1 UGSc en0 54.178.6.141/32 192.168.0.1 UGSc en0 127 127.0.0.1 UCS lo0 127.0.0.1 127.0.0.1 UH lo0 128.0/1 172.31.0.33 UGSc utun2 169.254 link#6 UCS en0 ! 172.31.0.32/27 172.31.0.34 UGSc utun2 172.31.0.34 172.31.0.34 UH utun2 192.168.0 link#6 UCS en0 ! 192.168.0.1/32 link#6 UCS en0 ! 192.168.0.1 90:f3:5:ee:a2:bb UHLWIir en0 1129 192.168.0.14/32 link#6 UCS en0 ! 224.0.0/4 link#6 UmCS en0 ! 224.0.0.251 1:0:5e:0:0:fb UHmLWI en0 239.255.255.250 1:0:5e:7f:ff:fa UHmLWI en0 255.255.255.255/32 link#6 UCS en0 !
クライアントデバイスルートテーブルの変更点
デフォルトの状態から変わった部分を確認していきます。
0/1 と 128.0/1
これはそれぞれ以下を表します。
- 0.0.0.0/1(0.0.0.1 ~ 127.255.255.254)
- 128.0.0.0/1(128.0.0.1 ~ 255.255.255.254)
ゲートウェイを見るとどちらも Client VPN を向いていることが分かります。
両者を合算して 0.0.0.0/0 のレンジをカバーすることになるのですが、なぜ異なるルートとして登録されているかが気になりました。
どうやら、デフォルトルート(0.0.0.0/0)よりロンゲストマッチ(最長プレフィックス一致)するように、このような方式になっているようです。
ただ、この方式がすべての環境で適用されるかは分かりません。使用しているデバイスや VPN クライアントによって変わる可能性もある旨ご留意ください。
ドキュメントでは「 0.0.0.0/0 で上書きする」と記載があります。
AWS Client VPN エンドポイントで分割トンネルを有効にすると、VPN が確立された場合に、AWS Client VPN ルートテーブル内のすべてのルートがクライアントルートテーブルに追加されます。この操作は、デフォルトのエンドポイントオペレーションとは異なります。デフォルトの AWS Client VPN エンドポイントオペレーションでは、クライアントルートテーブルがエントリ 0.0.0.0/0 で上書きされ、すべてのトラフィックが VPN 経由でルーティングされます。
Client VPN エンドポイント グローバル IP
ここでの54.178.6.141/32
はエンドポイントDNS名を名前解決した際に引ける IP(の一つ)です。ここに向けた通信は VPN トンネル向けにルーティングされては困るため、クライアントデバイスのデフォルトゲートウェイを向くように設定されています。
Client CIDR
この例においてクライアントに割り当てられている IP は172.31.0.34
です。(クライアント CIDR のプールの中から接続時にランダムで割り当てられます。)
そこをゲートウェイとするルートが定義されています。また、第4オクテットが1番違いの172.31.0.33
が、デフォルトルートのゲートウェイとして設定されています。
こういったルートの追加により、Client VPN に接続した際のルーティングが行われています。
インターネットへの疎通確認
確認を行うと、先ほど確認したものとは別のグローバル IP が返却されます。
% curl checkip.amazonaws.com 18.xxx.xx.xx
これは、冒頭で確認した通りターゲットネットワークに生成された ENI が持つパブリック IP アドレスです。
EC2 サービス画面のネットワークインタフェースの一覧画面から確認できます。
ENI は 2つあり、 Client VPN 接続時にどちらの ENI に割り振られるかが決定されます。 Client VPN 接続後の通信はこの ENI を経由して行われるため、この ENIに設定されている SecuriryGroup や NetworkACL 、ルートテーブルなどの設定に影響を受けます。
3. スプリットトンネル有効の場合
Client VPN エンドポイントの設定でスプリットトンネルを有効化し、再度クライアントから接続し直します。
クライアントデバイスのルートテーブルはこのようになりました。
% netstat -rn Routing tables Internet: Destination Gateway Flags Netif Expire default 192.168.0.1 UGSc en0 8.8.8.8/32 172.31.0.129 UGSc utun2 127 127.0.0.1 UCS lo0 127.0.0.1 127.0.0.1 UH lo0 169.254 link#6 UCS en0 ! 172.31.0.128/27 172.31.0.130 UGSc utun2 172.31.0.130 172.31.0.130 UH utun2 192.168.0 link#6 UCS en0 ! 192.168.0/16 172.31.0.129 UGSc utun2 192.168.0.1/32 link#6 UCS en0 ! 192.168.0.1 90:f3:5:ee:a2:bb UHLWIir en0 1152 192.168.0.14/32 link#6 UCS en0 ! 224.0.0/4 link#6 UmCS en0 ! 224.0.0.251 1:0:5e:0:0:fb UHmLWI en0 239.255.255.250 1:0:5e:7f:ff:fa UHmLWI en0 255.255.255.255/32 link#6 UCS en0 !
クライアントデバイスルートテーブルの変更点
先ほどと同じように確認していきます。
Client VPN ルートテーブルからプッシュされたもの
おさらいになりますが、今回は Client VPN ルートテーブルに以下 3 つのルートを登録しています。
- 192.168.0.0/16(デフォルトで登録される VPC CIDR)
- 0.0.0.0/0
- 8.8.8.8/32
% aws ec2 describe-client-vpn-routes --client-vpn-endpoint-id cvpn-endpoint-xxxxxxxxxx --output yaml Routes: - ClientVpnEndpointId: cvpn-endpoint-0ba622xxxxxxxxxx Description: Default Route DestinationCidr: 192.168.0.0/16 Origin: associate Status: Code: active TargetSubnet: subnet-0caa45223899b4b73 Type: Nat - ClientVpnEndpointId: cvpn-endpoint-0ba622xxxxxxxxxx DestinationCidr: 0.0.0.0/0 Origin: add-route Status: Code: active TargetSubnet: subnet-0caa45223899b4b73 Type: Nat - ClientVpnEndpointId: cvpn-endpoint-0ba622xxxxxxxxxx DestinationCidr: 8.8.8.8/32 Origin: add-route Status: Code: active TargetSubnet: subnet-0caa45223899b4b73 Type: Nat
そのうち、0.0.0.0/0 以外の 2つのルートがプッシュされていることが分かります。
スプリットトンネル無効の場合と異なり、0/1 と 128.0/1 によるデフォルトルートの上書きや、エンドポイントのグローバル IP 向けのルート追加は行われていません。
デフォルトゲートウェイはクライアントデバイスが持っていたもののままです。
つまり、(8.8.8.8 以外の)インターネット宛の通信の経路は Client VPN を経由しません。この挙動は今回試してみて初めて知りました。
Client CIDR
こちらは先ほどと同様に追加されています。今回クライアントに割り当てられた IP は172.31.0.130
です。
Client VPN ルートテーブルからプッシュされた宛先のゲートウェイは、1番違いの172.31.0.129
がターゲットとなっています。
インターネットへの疎通確認
デフォルトルートに変更が加わらないことから、Client VPN に接続しない場合と同様のルートでインターネットに疎通することになります。
% curl checkip.amazonaws.com 202.xx.xx.xx
ケース 1. での確認結果と同じです。
Traceroute による確認
今回の例ではクライアント VPN ルートテーブルから8.8.8.8/32
がプッシュされているため、クライアントからそこに向けた通信は経路が異なる、という点を確認します。
まずはクライアントデバイスのデフォルトルートを経由する際の結果のイメージです。
% traceroute 1.1.1.1 traceroute to 1.1.1.1 (1.1.1.1), 64 hops max, 52 byte packets 1 192.168.0.1 (192.168.0.1) 1.559 ms 1.205 ms 1.190 ms 2 * * * 3 10.202.xxx.xxx (10.202.xxx.xxx) 39.350 ms 17.774 ms 14.830 ms (……中略……) 9 one.one.one.one (1.1.1.1) 14.070 ms 65.633 ms *
続いて 8.8.8.8 向けの通信です。経路が異なることが分かります。
% traceroute 8.8.8.8 traceroute to 8.8.8.8 (8.8.8.8), 64 hops max, 52 byte packets 1 * * * 2 ec2-54-150-128-23.ap-northeast-1.compute.amazonaws.com (54.150.128.23) 28.393 ms ec2-54-150-128-25.ap-northeast-1.compute.amazonaws.com (54.150.128.25) 33.160 ms ec2-54-150-128-23.ap-northeast-1.compute.amazonaws.com (54.150.128.23) 27.409 ms 3 100.65.xx.xxx (100.65.xx.xxx) 25.229 ms (……中略……) 10 dns.google (8.8.8.8) 18.695 ms 19.311 ms 25.373 ms
(なお、これはターゲットネットワークの ENI を経由して通信を行っているため、そこにアタッチされるSecuriryGroupのアウトバウンドルールの開放などの考慮が必要です。)
0.0.0.0/0 という宛先でなければ、インターネット上のグローバル IP レンジに向けたルートをプッシュできることが確認できました。
終わりに
AWS Client VPN でスプリットトンネルを有効化しても、Client VPN ルートテーブル上の 0.0.0.0/0 はクライアントデバイスにプッシュされない、というお話でした。
実はこのエントリを書き始める前は全く逆の挙動を想定していて、「インターネット向けのルートを使っている場合はスプリットトンネルが無効でも有効でも結局変わらない」という方向の話をしようとしていました。
AWS Client VPN エンドポイントで分割トンネルを使用する場合、VPN が確立されると、AWS Client VPN ルートテーブル内のすべてのルートがクライアントルートテーブルに追加されます。
ドキュメントを見てそういった思い込みをしていたのですが、検証をする中で間違いに気づけたので何よりです。
私の環境だけかもしれませんが、接続して試す→切断してクライアント VPN の設定変更する→再度接続し直して挙動を確認する、というのを短いスパンで繰り返したらよく分からない挙動になったので、設定変更をしたあとは少し寝かせてから試した方が手戻りがなくて良さそうという学びを得ました。
以上、千葉(幸)がお送りしました。