この記事は「2026 Japan AWS Jr.Champions 真夏のQiitaリレー」の19日目の記事となります。
過去の投稿(リンク集)はこちらからご覧ください。
https://qiita.com/ys-yoshida/private/6f7c7f85155a993e2c86
はじめに
AWSのSite-to-Site VPN(拠点間VPN)をBGPの動的ルーティングで張る検証をしている中で、トラフィックセレクタ(トンネルに乗せる通信範囲の指定)を絞ったことが原因で拠点間の通信ができないということがありました。
セキュリティを考えても通信範囲をなるべく絞るという発想は普通であれば正しいですが、BGPのVPNに限ってはセレクタを絞ることでそもそもトンネルが張れなくなります。
この記事では、なぜBGPではトラフィックセレクタを 0.0.0.0/0 (全開)にするのが正しいのかを実際にハマった事例を元に整理してみました。
全体構成
今回検証した構成です。
AWS側:Virtual Private Gateway(VGW)+ Site-to-Site VPN(BGP動的ルーティング)
オンプレ側:FortiGate 60F
Site-to-Site VPNはデフォルトでトンネルが2本ありますが、今回は1本だけつないで検証しました。
セレクタを絞ったらつながらなかった
まず接続不可時の状況についてです。
VPN接続を作成した後、VPNトンネルの状態はこのような状態でした。
| トンネル | Status | メッセージ | 学習経路数 |
|---|---|---|---|
| 1 | DOWN | IPSEC IS UP |
0 |
| 2 | DOWN | IPSEC IS DOWN |
0 |
今回トンネル1だけを接続しているのでトンネル2がDOWNなのは正しいですが、トンネル1のメッセージは IPSEC IS UP(IPsecのPhase1/Phase2の暗号化トンネルは上がっている)なのにStatusはDOWNで、経路も学習できていない状態です。
オンプレFortiGate側でBGPステータスを見ると、ステータスは Active のままで Established に変わってくれません。
暗号化トンネルは張れているのに、BGPが確立しないというのが今回のハマりどころです。
冒頭にも書きましたが、原因はBGPのVPN接続なのにトラフィックセレクタを具体的なCIDR(192.168.x.0/24 や 10.x.0.0/16 等)にしていたことでした。
なぜこれが致命傷になっているのかを順番に解説していきます。
そもそもトラフィックセレクタって何?
Site-to-Site VPN接続の実現には役割の違う3つの要素があります。
- トラフィックセレクタ
- ルーティング
- アクセス制御
トラフィックセレクタ(AWSではローカルIPv4ネットワークCIDR / リモートIPv4ネットワークCIDR)は、「どの送信元・宛先の通信をIPsecトンネルに乗せて暗号化するか」を定義する値です。
IPsec用語では、IKEv1では「プロキシID」、IKEv2では「トラフィックセレクタ」と呼ばれているそうです。
ここで重要になるのは、セレクタ(Local / Remote IPv4 Network CIDR)は「実際にどこと通信できるか」を決めるルーティングやアクセス制御とは別物であり、あくまでトンネルに乗せる対象の広さであるという点です。
そして、この Local / Remote IPv4 Network CIDRのデフォルト値は 0.0.0.0/0 (IPv6なら ::/0)です。静的ルーティングでも動的(BGP)でも、作成時のデフォルト値は全開になっています。
実際の通信範囲はBGPの経路広告とルートテーブルが、「誰が何に通信して良いか」というアクセス制御はSG/NACL/FW等で制御します。
そのためセレクタを 0.0.0.0/0 にしても、経路がなければパケットは流れないし、経路があってもアクセス制御が可能なのでセキュリティリスクは特に変わりません。
なぜBGPだとセレクタを具体的にするとだめなのか
Site-to-Site VPNのBGPピアリングは、各トンネルの内側アドレスを使って行われます。
AWSのトラブルシューティングのドキュメントにも、BGPネイバーのアドレス宛の通信がトンネル内で正しく暗号化・復号されているかを確認するようにと書かれており、BGPのやりとり自体もIPsecトンネルを通る通信であることが前提になっています。
The virtual private gateway address is the address used as the BGP neighbor address. From your customer gateway device, ping this address to determine if IP traffic is being properly encrypted and decrypted.
引用元:Troubleshooting your customer gateway device
この内側アドレスは 169.254.0.0/16(リンクローカルアドレス)のうち /30 のブロックで割り当てられます。
/30には使えるホストが2つあり、片方がAWS側、もう片方がオンプレ(カスタマーゲートウェイ)側の内側IPになります。
この2つのIPでBGPネイバーを張っています。
169.254.0.0/30~169.254.5.0/30と169.254.169.252/30はAWS側で予約済みのため使用できません。
ここで今回の問題です。セレクタを 192.168.x.0/24 や 10.x.0.0/16 のように具体的なCIDRで固定した場合、BGPが使う 169.254. 系のIPがそのセレクタの範囲に入らず、暗号化して通す対象から、BGPの通信そのものが漏れてしまいます。
AWSの公式ドキュメントには「動的ルーティングではセレクタを 0.0.0.0/0 にすべき」という明記はありません。以下は今回の検証結果と、IPsecの仕様(SAのセレクタ範囲に含まれない通信は暗号化の対象にならない)から筆者が整理した内容です。
なお、AWSがカスタマーゲートウェイのベストプラクティスとして挙げている「デフォルトルート 0.0.0.0/0 を広告する」はBGPの経路広告の話であり、トラフィックセレクタとは別の設定です。
IPsec自体(Phase1/Phase2)は上がるので IPSEC IS UP と表示されるものの、その上のBGPが張れないためトンネルはDOWNのままになる、というのが冒頭の状態の正体でした。
パケットはどこで止まっていたのか
ここからは実際に切り分けたときの記録です。
セレクタが原因だと分かってオンプレ側だけを 0.0.0.0/0 に変更したものの、それでもBGPは確立しませんでした。
【Before】片側だけ 0.0.0.0/0 にした状態
FortiGate 側の SA AWS(VGW) 側の SA
┌────────────────────────┐ ┌────────────────────────┐
│ src: 0.0.0.0/0 │ │ src: 10.x.0.0/16 │
│ dst: 0.0.0.0/0 │ 不一致 ✗ │ dst: 192.168.x.0/24 │
│ + 古い proxyid が残存 │ │ │
└────────────────────────┘ └────────────────────────┘
┌────────────────┐ ┌────────────────┐
│ FortiGate │ BGP (TCP 179) │ AWS VGW │
│ 169.254.a.y/30 │ ─── syn ─────────► │ 169.254.a.x/30 │
│ │ ◄── syn-ack ────── │ │
│ │ ✗ ack が出ない │ │
└────────────────┘ └────────────────┘
└─ syn-ack は届くが、対応する SA が解決できず TCP が確立しない
→ 3-way ハンドシェイク未完 → BGP は Active のまま → トンネル DOWN
sniffer(FortiGateのパケットキャプチャ)でBGPのTCP179番ポートを見ると、syn に対して syn-ack は返ってきているのに、こちらから ack を返しておらず3-wayハンドシェイクが完了していませんでした。
さらに diagnose vpn tunnel list で見ると、変更前の古いプロキシIDが残ったまま新しいものと並存しており、flush しても消えない状態が続いていました。
セレクタは両側で一致させる必要がある
IPsecはPhase2で双方のセレクタをネゴシエートするので、オンプレ側だけ 0.0.0.0/0 にしてもAWS側が具体CIDRのままなら不一致は解消しません。
AWS側も modify-vpn-connection-options で 0.0.0.0/0 に変更し、そのうえでFortiGate側を diagnose vpn ike gateway flush で張り直して古いプロキシIDを消したところ、ようやくBGPが Established になりました。
【After】両側のセレクタを 0.0.0.0/0 に揃える
FortiGate 側・AWS(VGW) 側で一致した IPsec SA
┌──────────────────────────────────────────────┐
│ src: 0.0.0.0/0 ↕ dst: 0.0.0.0/0 │ ← 169.254 系も含まれる ✅
└──────────────────────────────────────────────┘
┌────────────────┐ ┌────────────────┐
│ FortiGate │ BGP (TCP 179) │ AWS VGW │
│ 169.254.a.y/30 │ ─── syn ─────────► │ 169.254.a.x/30 │
│ │ ◄── syn-ack ───── │ │
│ │ ═══ established ═► │ │
└────────────────┘ └────────────────┘
└─ BGPピア確立 → 経路交換 → トンネル UP ✅
0.0.0.0/0 は「緩めている」のではなく「正しい設定に戻している」
ここで、セレクタを全開にするのはセキュリティ的に大丈夫なのかという観点も出てくるかと思います。
途中にも記載しましたが、BGPの場合 0.0.0.0/0 にしてもセキュリティは下がりません。(理由は下記)
-
実際の通信範囲はBGPの経路広告が決めている。
オンプレからAWSへは、FortiGate(オンプレ側GW)がBGPで広告したプレフィックスだけがAWSのルートテーブルに乗る。AWSからオンプレも同様にAWS側が広告する経路だけ。
セレクタが0.0.0.0/0でも、経路のない宛先にパケットが行くことはない。 -
本当のアクセス制御は別レイヤにて実施している
「誰が何に通信して良いか」はFortiGate(オンプレ側FW/UTM)のファイアウォールポリシー、AWSのSG・NACL・FW等で制御させる。 -
暗号強度に影響しない
VPNに重要な暗号化の強さは暗号化アルゴリズム(AES、SHAなどなど)で決まるものであり、もちろんセレクタを0.0.0.0/0にしても暗号強度は一切変わらない。
セキュリティを気にするのであれば、より強いアルゴリズムを使うよう気を付ける。
静的ルーティングとの違い
静的ルーティングの場合でもセレクタCIDRの役割自体は変わりません。
静的の場合は暗号化の範囲を絞っても接続が可能です。
| 観点 | 静的ルーティング | BGP(動的) |
|---|---|---|
| 通信範囲を決めるもの | 静的ルート(Static IP Prefixes)+ ルートテーブル | BGPの経路広告 |
| セレクタCIDRの役割 | Phase2ネゴ用 | Phase2ネゴ用。0.0.0.0/0 がデフォルト |
| セレクタを絞るメリット | あり(範囲を限定できる) | なし(BGPが張れなくなる) |
| 冗長化・自動フェイルオーバー | 手動・限定的 | BGPで自動 |
設計の指針
基本的には動的(BGP)を選択しセレクタは 0.0.0.0/0 にして「BGP経路広告 + FW/SG」で通信制御をさせるのが良いと思います。
ただし、特定のサブネット間だけをIPsecに乗せたいという明確な要件があったり、あくまで一時的に接続するものでトンネルも1本でいいとかであれば静的ルーティングを選択するのが良いのではと思います。
補足:なるべくトンネルは2本とも使う
今回は検証だったのでトンネル1本だけで接続しましたが、基本的には2本のトンネルを使用することを推奨されています。(Site-to-Site VPNは1接続につき常に2本のトンネルを提供してくれる)
障害発生時だけでなく、AWS側のメンテナンス時にも冗長性が一時的に失われます。1本運用だと切れるため本番環境では特に2本とも接続してあげましょう。
まとめ
BGPのSite-to-Site VPNでのセレクタ設定の話でした。
ポイントは、トラフィックセレクタが「暗号化してトンネルに乗せる範囲」であって、通信範囲やアクセス制御とは別のレイヤだということでした。
当初はそんなに苦労せずに接続できるだろうと思っていましたが、BGPやIPsecの理解を深めるいい機会になったので苦労して良かったと感じています。
参考
- Site-to-Site VPN tunnel options for your Site-to-Site VPN connection
- Modify Site-to-Site VPN connection options
- Create a Site-to-Site VPN connection
- Troubleshooting your customer gateway device
- Tunnel endpoint replacements
- VpnTunnelOptionsSpecification - Amazon EC2 API Reference
- ModifyVpnConnectionOptions - Amazon EC2 API Reference
おまけ:切り分け時に使ったコマンド
オンプレ側(Fortigate 60F)
-
diagnose vpn tunnel list name <TUNNEL_IF>
プロキシID配下のsrc:/dst:でセレクタを確認する。同一トンネルに新旧2つのプロキシID(TUNNEL_IF名)が並存することがある。 -
diagnose sniffer packet any 'host <AWS_INSIDE_IP> and port 179'
BGPのTCPハンドシェイクがどこで止まっているかを特定する。syn/syn-ack止まりなら戻りパケットがSAに乗っていない可能性あり。 -
diagnose vpn ike gateway flush name <TUNNEL_IF>
トンネルを張りなおして古いプロキシIDを一掃する。(tunnel flushだけだと古いプロキシIDが残ることがある)
※トンネルを張りなおす操作であり、数分間VPNが切断されるため注意
AWS側(マネコンでもできます)
# トンネルテレメトリの確認(Status / メッセージ / 学習経路数)
aws ec2 describe-vpn-connections --vpn-connection-ids <VPN_ID> \
--query 'VpnConnections[0].VgwTelemetry[].{IP:OutsideIpAddress,Status:Status,Msg:StatusMessage,Routes:AcceptedRouteCount}'
# セレクタCIDR・ルーティング方式の確認
aws ec2 describe-vpn-connections --vpn-connection-ids <VPN_ID> \
--query 'VpnConnections[0].Options.{Local:LocalIpv4NetworkCidr,Remote:RemoteIpv4NetworkCidr,StaticOnly:StaticRoutesOnly}'
# トラフィックセレクタの値を変更
# 変更時は両トンネル同時に断(トンネルエンドポイント自体が再プロビジョニングされる)のため注意
aws ec2 modify-vpn-connection-options \
--vpn-connection-id <VPN_ID> \
--local-ipv4-network-cidr 0.0.0.0/0 \
--remote-ipv4-network-cidr 0.0.0.0/0
