VyOSでつくるAWS Site-to-Site VPN — IPsec+BGPの基礎

VyOSをカスタマーゲートウェイに見立てて、AWS Site-to-Site VPN(IPsec + BGPダイナミックルーティング)をVirtualBox上に構築する手順を、設定値の出どころまで含めて解説します。

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

  1. VyOSでつくるAWS Site-to-Site VPN — IPsec+BGPの基礎(この記事)
  2. AWS Site-to-Site VPNの冗長化とBGP経路制御
  3. VGWからTransit Gatewayへ — VPNのECMP冗長化
  4. マルチリージョン接続 — DX-GWの限界とTransit Gatewayピアリング

0. Overview

オンプレ拠点とAWS VPCを暗号化トンネルでつなぐのが AWS Site-to-Site VPN です。 本来はオンプレのルーターが担う「カスタマーゲートウェイ(顧客側VPN装置)」の役割を、ここでは VyOS(Linuxベースのソフトウェアルーター)で代用し、VirtualBox上に検証環境を作ります。

全体像と作業の流れ

この記事で一番つまずきやすいのは、後半に出てくる xx.xx.xx.xx169.254.244.109、PSK(事前共有鍵)といった**「謎の値」がどこから来るのか**です。結論から言うと、これらはAWS側でVPN接続を作ると自動生成される「設定ファイル」に全部書いてあります。だから作業順序が重要で、以下の順で進めます。

1. VyOS を起動して初期設定(SSH・タイムゾーン)
2. VyOS をインターネットに出す(AWSと鍵交換するため)
3. AWS側で VPN接続を作成 → 設定ファイルをダウンロード  ← ここで全パラメータが決まる
4. ダウンロードした値を VyOS に流し込む(IPsec + BGP)
5. AWS側でルート伝搬を有効化
6. 双方向 ping で疎通確認

登場する主なコンポーネントを先に地図にしておきます。

コンポーネント役割
VGW(仮想プライベートゲートウェイ)AWS側のVPN終端。VPCにアタッチする
CGW(カスタマーゲートウェイ)オンプレ側VPN装置の「AWSへの登録情報」(=今回のVyOSのグローバルIPとASN)
VPN ConnectionVGWとCGWを結ぶトンネル定義。作成すると2本のトンネルとPSK・内側IPが払い出される
IPsec(IKE/ESP)トンネルを張り、中身を暗号化するプロトコル群
VTI暗号化トンネルをOS上の仮想NICとして見せるインターフェース
BGPトンネル越しに「このネットワークはこっちにあるよ」を動的に教え合うルーティングプロトコル

参考: AWS White Paper

https://docs.aws.amazon.com/ja_jp/whitepapers/latest/aws-vpc-connectivity-options/aws-managed-vpn.html

構成図

image

VyOS Version

VyOSは設定構文がバージョンで変わるため、まず版を控えておきます(後述のIPsec構文は1.4系前提)。

vyos@vyos:~$ show version
Version:          VyOS 1.4-rolling-202211190627
Release train:    current

Built by:         autobuild@vyos.net
Built on:         Sat 19 Nov 2022 06:27 UTC
Build UUID:       687dfd47-11ca-4c0c-8045-944ed60279be
Build commit ID:  cd6f8ab1040139

Architecture:     x86_64
Boot via:         installed image
System type:      KVM guest

Hardware vendor:  innotek GmbH
Hardware model:   VirtualBox
Hardware S/N:     0
Hardware UUID:    91d658fc-16f6-074b-b5a6-59f4f5c3c14b

Copyright:        VyOS maintainers and contributors

1. VyOS初期設定

ライブCDから起動した直後はメモリ上で動いているだけなので、install image でディスクへインストールしてから各種設定を入れます。ここでは管理用にホストオンリーアダプタ(PCとVMだけが通信できる閉じたネットワーク)を割り当て、SSHで入れるようにします。

VirtualBox側

  • LAN3 を Host Only Adapter として有効化(管理アクセス用。eth2 に対応)

VyOS Config

//起動後
$ install image //各種設定の後、CDを取り出し再起動。

//管理インターフェース & SSH
set interfaces ethernet eth2 address '169.254.153.1/16'
set interfaces ethernet eth2 description 'HOST-Only'
set service ssh
set system time-zone 'Asia/Tokyo'

2. インターネット接続

IPsecのIKE(鍵交換)はインターネット越しにAWSのVPNエンドポイントと行うため、VyOSが外に出られる必要があります。VirtualBoxのNATを使い、eth0をDHCPで自動設定します。

補足: この構成ではVyOSはVirtualBoxのNATの内側にいます(VyOSが受け取るのは 10.0.2.15 のようなプライベートIP)。カスタマーゲートウェイがNAT配下にある=NAT-T(NAT Traversal)が必要になり、これが後のIPsec設定に効いてきます(セクション4で再登場)。

VirtualBox側

  • LAN1 を NAT として有効化(eth0 に対応)

VyOS Config

//追加
set interfaces ethernet eth0 address 'dhcp'

//ルーティングテーブル確認(デフォルトルートがeth0に向いていればOK)
vyos@vyos:~$ show ip route
~~
S>* 0.0.0.0/0 [210/0] via 10.0.2.2, eth0, 00:00:11
C>* 10.0.2.0/24 is directly connected, eth0, 00:00:13
C>* 169.254.0.0/16 is directly connected, eth2, 00:07:14

//外部疎通確認
vyos@vyos:~$ ping 8.8.8.8
PING 8.8.8.8 (8.8.8.8): 56 data bytes
64 bytes from 8.8.8.8: icmp_seq=0 ttl=56 time=27.054 ms
64 bytes from 8.8.8.8: icmp_seq=1 ttl=56 time=17.119 ms

3. AWS側でVPN接続を作成し、設定をダウンロードする

ここが後半のすべての「謎の値」の出どころです。VyOSの設定を書く前に、必ずAWS側を先に作ります。

作成するリソース(順番)

  1. 仮想プライベートゲートウェイ(VGW) を作成し、対象のVPCに アタッチ
    • ASNはデフォルトの 64512(AWS側のBGP AS番号。後でVyOSのBGPネイバー remote-as に書く値になる)
  2. カスタマーゲートウェイ(CGW) を作成
    • IPアドレス = VyOS(オンプレ装置)のグローバルIP
    • BGP ASN = 自分側のAS番号。プライベートAS番号の範囲(64512〜65534)から選び、今回は 65000
  3. Site-to-Site VPN接続 を作成
    • 上で作ったVGWとCGWを指定
    • ルーティングは 動的(Dynamic / BGP) を選択

設定ファイルのダウンロード

VPN接続を選び 「設定をダウンロード」 から、ベンダー/プラットフォーム/ソフトウェアを選びます(一覧にVyOSが無ければ Generic を選択。CLIなら aws ec2 get-vpn-connection-device-sample-configuration でも取得可)。

ダウンロードしたファイルには、VyOSに流し込む値がほぼ全部入っています。後半のコマンドに出てくるプレースホルダと対応づけると:

設定ファイル内の項目VyOSコマンドのどこに使うか
Pre-Shared Key(トンネル1の鍵)authentication pre-shared-secret
Virtual Private Gateway の外部IPpeer AWS remote-addressxx.xx.xx.xx
Customer Gateway の外部IPlocal-id / local-address 相当
Tunnel Inside IP(CGW側 /30)interfaces vti vti0 address169.254.244.110/30
Tunnel Inside IP(AWS側 /30)BGPネイバー(169.254.244.109
Virtual Private Gateway ASNBGP remote-as64512
暗号化/ハッシュ/DHグループ/ライフタイムesp-group / ike-group の各値

トンネルが2本ある理由: AWSは冗長化のため必ず2本のトンネル(別々のAWS側エンドポイント・別々の 169.254.x.x/30)を払い出します。本記事は片側(Tunnel 1, vti0)のみ設定しています。本番では両方設定し、BGPでアクティブ/スタンバイを取るのが推奨です。

4. VyOSのIPsec / BGP設定

VyOS 1.4から vpn ipsec 系の構文が変更されています(1.2/1.3系のブログをそのまま真似ると通りません)。以下は1.4系の構文です。

ダウンロードした設定ファイルの値を、VyOSのコマンドに流し込んでいきます。ブロックごとに「何をしているか」を補足します。

① ESP / IKE グループ(暗号化の条件を定義)

IPsecは2段構えです。IKE(Internet Key Exchange)がまず鍵を安全に交換してトンネルの「制御チャネル」を作り、ESP(Encapsulating Security Payload)が実際のデータを暗号化して運びます。ここで定義する暗号化方式・ハッシュ・DHグループ・ライフタイムは、AWS側とピッタリ一致していないとトンネルが上がりません(だからダウンロードした値をそのまま使う)。

## ESP & IKE
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'

主なパラメータの意味:

パラメータ意味補足
lifetime鍵の有効期間(秒)。切れると自動で再交換IKE=28800秒(8h)、ESP=3600秒(1h)はAWSの既定値
pfs enablePerfect Forward Secrecy。鍵更新ごとに新しいDH鍵を使う過去の鍵が漏れても過去通信は復号されない
dh-group 2Diffie-Hellman鍵交換のグループ(鍵強度)グループ2は古い。両端が対応するなら14以上を推奨
dead-peer-detection相手の生死を定期確認し、死んでいたら張り直すinterval 15 / timeout 30 ごとに検知
encryption/hashAES128 / SHA1AWSの最小要件。両端が対応すればAES256/SHA256に上げる方が望ましい

セキュリティ注記(迎合しないために正直に書くと): AES128 + SHA1 + DH2 はAWSが受け付ける最低ラインで、現代の基準では弱めです。検証用途なら問題ありませんが、実運用なら両端の対応状況を見て AES256 / SHA256 / DH14以上 に引き上げてください。

② VTI(暗号化トンネルを仮想NICにする)

IPsecで張ったトンネルを、OSから「普通のネットワークインターフェース」として扱えるようにするのが VTI(Virtual Tunnel Interface) です。こうすることで、トンネル上でBGPのような通常のルーティングプロトコルを素直に喋れます(=ルートベースVPN)。

## vti
set interfaces vti vti0 address '169.254.244.110/30'
set interfaces vti vti0 description 'VPC tunnel 1'
set interfaces vti vti0 mtu '1436'
  • 169.254.244.110/30: トンネル内側のリンクローカルアドレス。AWSが払い出した /30 のうちCGW側。対向(.109)がAWS側で、後のBGPネイバーになります。
  • mtu 1436: IPsecはパケットにヘッダ(暗号化のオーバーヘッド)を付け足すため、通常の1500のままだと中身が入りきらず断片化やブラックホール化を招きます。トンネル用に小さくするのが定石で、AWSの生成configもこの値を指定してきます。

③ Site-to-Site Peer(トンネルの相手と認証)

「誰と」「どの鍵で」トンネルを張るかを定義します。

## IPSec
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'
set vpn ipsec site-to-site peer AWS description 'VPC tunnel 1'
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'
### NAT-T 対応(VyOSがNAT配下にいるため)
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'
  • pre-shared-secret: ダウンロードしたPSKに置き換える。
  • remote-address xx.xx.xx.xx: AWS側VPNエンドポイントのグローバルIP(設定ファイルに記載)。
  • local-address 10.0.2.15: VyOSが認識している自分のIP。今回はVirtualBox NAT配下なのでグローバルではなくプライベートになっている点に注意。
  • local-id / remote-id: NAT-T対策。VyOSがNATの内側にいるとIPsecが本来見るIPと実際のIPがズレるため、IDを明示してIKEを成立させます。local-id には自分のグローバルIP(=CGWに登録したIP)、remote-id にはAWS側IPを指定します。

④ BGP(経路を動的に教え合う)

トンネルが上がっただけでは「どのサブネットがトンネルの先にあるか」をお互い知りません。BGPで「自分側には 10.0.2.0/24 があるよ」と広告し、AWSからはVPC内のネットワークを受け取ります。静的ルートを手で書く代わりに自動化できるのが動的ルーティングの利点です。

//BGP Config
set protocols bgp system-as '65000'
set protocols bgp neighbor 169.254.244.109 address-family ipv4-unicast soft-reconfiguration inbound
set protocols bgp neighbor 169.254.244.109 remote-as '64512'
set protocols bgp neighbor 169.254.244.109 timers holdtime '30'
set protocols bgp neighbor 169.254.244.109 timers keepalive '10'
set protocols bgp address-family ipv4-unicast network 10.0.2.0/24
  • system-as 65000: 自分側のAS番号(CGWで登録した値と一致させる)。
  • neighbor 169.254.244.109 ... remote-as 64512: BGPの相手はトンネル内側 /30 のAWS側アドレス、そのAS番号はVGWの 64512
  • network 10.0.2.0/24: 自分側ネットワークをAWSへ広告。
  • timers: keepalive 10秒 / holdtime 30秒で生死監視(holdtimeはkeepaliveの3倍が定石)。

Note

set protocols bgp 65000 neighbor 169.254.233.169 address-family ipv4-unicast soft-reconfiguration inbound
# この事前設定により、ソフトウェアは、アップデートがインバウンド ポリシーによって受け入れられるかどうかに関係なく、受信したすべてのアップデートを変更せずに保存します。
# アップデートの保存は大量のメモリを要するため、可能な限り回避する必要があります。

5. AWS側でルート伝搬を有効化

BGPでAWSが受け取った経路(=VyOS側の 10.0.2.0/24)を、実際にVPCのルートテーブルへ反映させる設定です。これを忘れると、トンネルとBGPは上がっているのにVPC内のEC2からオンプレ宛のパケットが返らない、という「半分つながった」状態になります。

対象VPCのルートテーブルで 「ルート伝搬(Route Propagation)」を有効化 し、VGWを指定します。

image

6. 疎通確認

トンネルは双方向で確認します。片方向だけ通っても、戻りの経路(ルート伝搬やBGP広告)が欠けていると実用にならないためです。

AWS => VyOS

VPC内のホストから、VyOS側のアドレスへ。

:~/environment/ans-exam (master) $ ping 10.0.2.15
PING 10.0.2.15 (10.0.2.15) 56(84) bytes of data.
64 bytes from 10.0.2.15: icmp_seq=1 ttl=64 time=19.6 ms
64 bytes from 10.0.2.15: icmp_seq=2 ttl=64 time=18.2 ms

VyOS => AWS

VyOSからVPC内のホストへ。source-address を付けるのは、広告している 10.0.2.0/24 を送信元にしないとBGPで学習した経路に乗らないためです。

vyos@vyos:~$ ping 10.99.31.172 source-address 10.0.2.15
PING 10.99.31.172 (10.99.31.172) from 10.0.2.15 : 56(84) bytes of data.
64 bytes from 10.99.31.172: icmp_seq=1 ttl=254 time=16.3 ms
64 bytes from 10.99.31.172: icmp_seq=2 ttl=254 time=30.0 ms
64 bytes from 10.99.31.172: icmp_seq=3 ttl=254 time=24.4 ms
64 bytes from 10.99.31.172: icmp_seq=4 ttl=254 time=18.3 ms

両方向で応答が返れば、IPsecトンネル・BGP経路・ルート伝搬がすべて噛み合っている状態です。

7. うまくいかないときの確認ポイント

つながらない場合、上のレイヤーから順に切り分けると早いです。

症状確認コマンド / 見るところ
トンネルが上がらないshow vpn ipsec sa でIKE/ESPのSA状態を確認。down なら暗号化条件(esp/ike-group)の不一致かPSK誤りを疑う
IKEは上がるがESPが張れない暗号化方式・DHグループ・ライフタイムがAWS側設定ファイルと一致しているか再確認
トンネルは上がるがBGPが上がらないshow ip bgp summary でネイバー状態を確認。ネイバーIP(AWS側 /30)とASNが正しいか
BGPは上がるが通信できないshow ip route bgp で経路学習を確認。AWS側はルート伝搬(セクション5)が有効か
NAT配下で全く張れないNAT-Tの local-id / remote-id 設定漏れを疑う