AWS Site-to-Site VPNの冗長化とBGP経路制御

Site-to-Site VPNを2本に冗長化し、BGPのWeight・AS-Path Prepend・MEDで「行き」と「戻り」の経路を意図した方向に寄せるトラフィックエンジニアリングを、VyOSとAWS VGWの実機で検証します。

連載: VyOSで作るAWSハイブリッド接続(全4回)

  1. 単一Site-to-Site VPN — VyOS×AWSの基礎
  2. トンネル冗長化とBGP経路制御(この記事)
  3. Transit Gatewayへの置き換えとECMP
  4. マルチリージョン接続 — DX-GWの限界とTGWピアリング

0. この回のテーマ

第1回では、VyOSをカスタマーゲートウェイ(CGW)に見立てて、AWSとの間に 1本のSite-to-Site VPNトンネルを張りました。ただ実運用で1本だけ、というのはまずありません。AWSはVPN接続を作ると必ず2本のトンネル(別々のAWS側エンドポイント・別々の内側 /30)を払い出してきます。冗長化が前提の設計になっているからです。

そこで2本目を足すと、今度は新しい問題が出てきます。「2本あるとき、トラフィックはどっちのトンネルを通るのか? そしてそれを自分の意図どおりに制御できるのか?」 これがこの回の主題、BGPトラフィックエンジニアリングです。

結論を先に言うと、経路制御は 「行き」と「戻り」で使う道具が違います。ここを最初に掴んでおくと、後半が一気に楽になります。

制御したい方向使う属性性質
VyOS → AWS(自分から出ていく経路の選択)Weight自分のルータ内だけの判断。隣に伝わらない
AWS → VyOS(相手が自分宛に選ぶ経路への influence)AS-Path Prepend広告に乗って相手に伝わる。相手の判断を動かす

ネットワーク屋の感覚で言えば、Weightは「自分のルーティングテーブルのローカルな優先度」、AS-Path Prependは「相手に渡すルート広告をわざと遠回りに見せる小細工」です。前者は自分の意思で決められること、後者は相手にお願いすること、という非対称があります。

構成図

第1回の単一トンネルに、2本目(vti1)を足した姿です。

dual tunnel overview

1. 2本目のトンネルを追加する

設定の中身は第1回のトンネル1とほぼ同じです。AWSのVPN設定ファイルに記載された トンネル2側のPSK・エンドポイントIP・内側 /30 を、vti1 と新しいピアに当てはめていきます。

## IPSec(トンネル2)
set vpn ipsec site-to-site peer AWS_backup authentication mode 'pre-shared-secret'
set vpn ipsec site-to-site peer AWS_backup authentication pre-shared-secret 'YOUR_PRE_SHARED_SECRET_2'
set vpn ipsec site-to-site peer AWS_backup description 'VPC tunnel 2'
set vpn ipsec site-to-site peer AWS_backup ike-group 'AWS'
set vpn ipsec site-to-site peer AWS_backup local-address '10.0.2.15'
set vpn ipsec site-to-site peer AWS_backup remote-address 'xx.xx.xx.xx'  # トンネル2のAWS側IP
set vpn ipsec site-to-site peer AWS_backup vti bind 'vti1'
set vpn ipsec site-to-site peer AWS_backup vti esp-group 'AWS'
### NAT-T対応(第1回と同じ理由)
set vpn ipsec site-to-site peer AWS_backup authentication local-id 'yy.yy.yy.yy'
set vpn ipsec site-to-site peer AWS_backup authentication remote-id 'xx.xx.xx.xx'

## VTI(トンネル2の仮想インターフェース)
set interfaces vti vti1 address '169.254.61.114/30'
set interfaces vti vti1 description 'VPC tunnel 2'
set interfaces vti vti1 mtu '1436'

## BGP(トンネル2のネイバー)
set protocols bgp neighbor 169.254.61.113 address-family ipv4-unicast soft-reconfiguration inbound
set protocols bgp neighbor 169.254.61.113 remote-as '64512'
set protocols bgp neighbor 169.254.61.113 timers holdtime '30'
set protocols bgp neighbor 169.254.61.113 timers keepalive '10'

esp-group / ike-group は第1回で定義した AWS を使い回します(暗号化条件は両トンネルで同じ)。これで物理的には2本のトンネルとBGPネイバーが立ち上がります。

2. 初期状態の経路を観察する

何も制御を入れない状態でBGPテーブルを見ると、面白いことが起きています。

vyos@vyos:~$ show ip bgp ipv4 unicast
   Network          Next Hop            Metric LocPrf Weight Path
*> 10.0.2.0/24      0.0.0.0                  0         32768 i
*> 10.99.0.0/16     169.254.61.113         100             0 64512 i
*                   169.254.234.49         200             0 64512 i

VPC(10.99.0.0/16)への経路が2本見えていて、*>(ベストパス)が付いているのは .113(トンネル2 = Backup側) の方です。Metric(MED)を見ると、トンネル2が 100、トンネル1が 200

ここがVGW(仮想プライベートゲートウェイ)の挙動の肝です。AWSはトンネル1に200、トンネル2に100というMEDを付けて広告してきます。BGPはMEDが小さい方を優先するので、何もしなければBackup側(トンネル2)が勝つshutdown→復旧させても、MEDの差でちゃんとBackup側にトラフィックが戻ってきます(参考: Site-to-Site VPNのBGP経路制御)。

つまりデフォルトのままだと、自分が「Main」と思っているトンネル1が使われない、という状況です。これを意図どおりに寄せにいきます。

VGWの経路選択の優先順位

AWS(VGW)が複数経路から1つを選ぶときの順序を、先に整理しておきます。上から順に評価されます。

  1. ロンゲストマッチ(宛先がより詳細なルートが優先)
  2. Direct Connect > Site-to-Site VPN(DXがあればDX優先)
  3. 静的ルート > BGPルート(同じVPN内で)
  4. AS-PATHが最短のBGPルート
  5. AS-PATHが同じなら MEDが最小

今回いじれるのは下2つ、AS-PATHMEDです。

3. TE1: VyOS → AWS の経路をMain系に寄せる(Weight)

まず「自分から出ていく」方向。VyOSがVPC宛のパケットをどちらのトンネルに流すかは、VyOS自身のBGPテーブルのベストパス選択で決まります。ここは相手は関係ない、自分のルータ内の話なので、Weightを使います。

Weightは特定のパスを選ぶために使う、そのルータ内だけで有効な属性です。他のBGPルータには一切交換されません。同じプレフィックスなら、値が大きい方のルートが優先されます。

トンネル1(.49)のWeightを大きくして、こちらをベストパスに引き上げます。

set protocols bgp neighbor 169.254.234.49 address-family ipv4-unicast weight 300

変更後:

vyos@vyos:~$ show ip bgp
   Network          Next Hop            Metric LocPrf Weight Path
*> 10.0.2.0/24      0.0.0.0                  0         32768 i
*  10.99.0.0/16     169.254.61.113         100             0 64512 i
*>                  169.254.234.49         200           300 64512 i  ## weight 300

MEDは200のまま(不利)なのに、Weight 300が効いて .49(Main側)がベストパスに変わりました。Weightはローカルの最優先属性なので、MEDの不利を上書きできるわけです。

TE1: weight

これでVyOSから出ていくトラフィックはMain系に乗りました。ただしこれは片方向だけの解決です。

4. TE2: AWS → VyOS の経路をMain系に寄せる(AS-Path Prepend)

次に「相手から戻ってくる」方向。AWS側がVyOS(10.0.2.0/24)宛にどちらのトンネルを選ぶかは、AWSのBGPテーブルの判断です。こちらのWeightは相手に伝わらないので無力。代わりに、相手に渡すルート広告を細工して、相手の判断を動かします

使うのは AS-Path Prepend。広告するルートに自分のAS番号を余分に付け足して、「この経路は遠回りですよ」と相手に見せかける手法です。AWSの選択順位(前述)で4番目の「AS-PATH最短」が効くので、Backup側のAS-PATHを長くすれば、相手はMain側を短い=近いと判断します。

Backup側(.113)に向けて広告するときだけ、AS-PATHを1つ伸ばします。

set policy route-map VPC-Tunnel-2-OUT rule 10 action permit
set policy route-map VPC-Tunnel-2-OUT rule 10 set as-path prepend 65000
set protocols bgp neighbor 169.254.61.113 address-family ipv4-unicast route-map export VPC-Tunnel-2-OUT

Main側(.49)へ広告した経路はAS-PATHが素のまま:

vyos@vyos:~$ show ip bgp ipv4 unicast neighbors 169.254.234.49 advertised-routes
   Network          Next Hop            Metric LocPrf Weight Path
*> 10.0.2.0/24      0.0.0.0                  0         32768 i
*> 10.99.0.0/16     0.0.0.0                              300 64512 i

Backup側(.113)へ広告した経路はAS-PATHに 65000 が水増しされている:

vyos@vyos:~$ show ip bgp ipv4 unicast neighbors 169.254.61.113 advertised-routes
   Network          Next Hop            Metric LocPrf Weight Path
*> 10.0.2.0/24      0.0.0.0                  0         32768 65000 i   ## prependされている
*> 10.99.0.0/16     0.0.0.0                              300 65000 64512 i

AWSから見ると、Backup経由のVyOS宛経路はAS-PATHが1ホップ長い=遠回りに見えるので、Main側を選ぶようになります。

TE2: as-path prepend

5. 完成形

これで「行き(Weight)」も「戻り(AS-Path Prepend)」も、両方Main系(トンネル1)に寄りました。Mainが落ちればBGPが自動でBackupに切り替え、復旧すればMainに戻ってくる、アクティブ/スタンバイの冗長構成です。

dual tunnel final

ポイントを一枚にまとめると:

方向制御点はどこ道具なぜそれか
VyOS → AWSVyOS自身のベストパス選択Weightローカルで完結。相手に伝える必要がない
AWS → VyOSAWSのベストパス選択AS-Path Prepend自分のWeightは伝わらない。広告を細工して相手を動かす
この回時点のVyOS設定(全体・秘匿値はプレースホルダ)
set interfaces ethernet eth0 address 'dhcp'
set interfaces ethernet eth2 address '169.254.153.1/16'
set interfaces ethernet eth2 description 'Host-Only'
set interfaces vti vti0 address '169.254.234.50/30'
set interfaces vti vti0 description 'VPC tunnel 1'
set interfaces vti vti0 mtu '1436'
set interfaces vti vti1 address '169.254.61.114/30'
set interfaces vti vti1 description 'VPC tunnel 2'
set interfaces vti vti1 mtu '1436'
set policy route-map VPC-Tunnel-2-OUT rule 10 action 'permit'
set policy route-map VPC-Tunnel-2-OUT rule 10 set as-path prepend '65000'
set protocols bgp address-family ipv4-unicast network 10.0.2.0/24
set protocols bgp neighbor 169.254.61.113 address-family ipv4-unicast route-map export 'VPC-Tunnel-2-OUT'
set protocols bgp neighbor 169.254.61.113 address-family ipv4-unicast soft-reconfiguration inbound
set protocols bgp neighbor 169.254.61.113 remote-as '64512'
set protocols bgp neighbor 169.254.61.113 timers holdtime '30'
set protocols bgp neighbor 169.254.61.113 timers keepalive '10'
set protocols bgp neighbor 169.254.234.49 address-family ipv4-unicast soft-reconfiguration inbound
set protocols bgp neighbor 169.254.234.49 address-family ipv4-unicast weight '300'
set protocols bgp neighbor 169.254.234.49 remote-as '64512'
set protocols bgp neighbor 169.254.234.49 timers holdtime '30'
set protocols bgp neighbor 169.254.234.49 timers keepalive '10'
set protocols bgp system-as '65000'
set vpn ipsec esp-group AWS lifetime '3600'
set vpn ipsec esp-group AWS mode 'tunnel'
set vpn ipsec esp-group AWS pfs 'enable'
set vpn ipsec esp-group AWS proposal 1 encryption 'aes128'
set vpn ipsec esp-group AWS proposal 1 hash 'sha1'
set vpn ipsec ike-group AWS dead-peer-detection action 'restart'
set vpn ipsec ike-group AWS dead-peer-detection interval '15'
set vpn ipsec ike-group AWS dead-peer-detection timeout '30'
set vpn ipsec ike-group AWS lifetime '28800'
set vpn ipsec ike-group AWS proposal 1 dh-group '2'
set vpn ipsec ike-group AWS proposal 1 encryption 'aes128'
set vpn ipsec ike-group AWS proposal 1 hash 'sha1'
set vpn ipsec site-to-site peer AWS authentication mode 'pre-shared-secret'
set vpn ipsec site-to-site peer AWS authentication pre-shared-secret 'YOUR_PRE_SHARED_SECRET_1'
set vpn ipsec site-to-site peer AWS authentication local-id 'yy.yy.yy.yy'
set vpn ipsec site-to-site peer AWS authentication remote-id 'xx.xx.xx.xx'
set vpn ipsec site-to-site peer AWS ike-group 'AWS'
set vpn ipsec site-to-site peer AWS local-address '10.0.2.15'
set vpn ipsec site-to-site peer AWS remote-address 'xx.xx.xx.xx'
set vpn ipsec site-to-site peer AWS vti bind 'vti0'
set vpn ipsec site-to-site peer AWS vti esp-group 'AWS'
set vpn ipsec site-to-site peer AWS_backup authentication mode 'pre-shared-secret'
set vpn ipsec site-to-site peer AWS_backup authentication pre-shared-secret 'YOUR_PRE_SHARED_SECRET_2'
set vpn ipsec site-to-site peer AWS_backup authentication local-id 'yy.yy.yy.yy'
set vpn ipsec site-to-site peer AWS_backup authentication remote-id 'xx.xx.xx.xx'
set vpn ipsec site-to-site peer AWS_backup ike-group 'AWS'
set vpn ipsec site-to-site peer AWS_backup local-address '10.0.2.15'
set vpn ipsec site-to-site peer AWS_backup remote-address 'xx.xx.xx.xx'
set vpn ipsec site-to-site peer AWS_backup vti bind 'vti1'
set vpn ipsec site-to-site peer AWS_backup vti esp-group 'AWS'

まとめ

  • AWSは冗長化前提でVPNトンネルを2本払い出す。両方使うなら経路制御が必要になる
  • VGWはデフォルトでトンネル1にMED 200、トンネル2にMED 100を付ける(放置するとBackup側が優先される)
  • 経路制御は方向で道具が変わる。出ていく方向はWeight(ローカル完結)、戻る方向はAS-Path Prepend(広告を細工して相手を動かす)
  • このアクティブ/スタンバイ構成に対し、次回はVGWをTransit Gatewayに置き換え、2本を**同時に使う(ECMP)**構成へ進みます。MEDの挙動もVGWとは変わります。
Hugo で構築されています。
テーマ StackJimmy によって設計されています。