AWS に検証用の閉域環境を作り、自宅オンプレミスのルーター(YAMAHA RTX1200)と Site-to-Site で接続するのに安い方法はないかと調べていたのですが、AWS 側で VPN 用のグローバル IPv4 アドレスを払い出すと課金されるため、無料で付与されるグローバル IPv6 アドレスを使ってうまく費用を抑えられないか?と思い立ちました。
しかし、AWS VPN Gateway は IPv6 でのトンネルに対応しておらず、またオンプレミス側にも固定のグローバル IPv4 アドレスがないと難しいです。IPv6 でのトンネルに対応している Transit Gateway はアタッチメント料金が高くて厳しく、Direct Connect は論外……。
そこで、VPC のパブリックサブネットに EC2 インスタンスを立てて、IPv6 で自宅のルーターと Site-to-Site の IPSec VPN を張れば、安価に自宅と AWS の閉域環境(プライベートサブネット)を接続できるのではないかと思い、検証してみました。
結論から言うと、EC2 の Linux で strongSwan を立てていわゆるルーターインスタンスとすることで、EC2 に払い出される IPv6 アドレスで自宅のルーターと IPSec で Site-to-Site VPN を張り、自宅オンプレミスから AWS の VPC にあるプライベートサブネットへ接続することができました。以下に構築時のメモを記載します。
構成図
今回作成する検証環境は次のとおりです。パブリックサブネットにいる EC2 にパブリック IPv6 アドレスで IPSec トンネルを作り、その中にプライベート IPv4 アドレスの通信を通すことで、自宅オンプレミスとプライベートサブネットとの相互通信を目指します。
AWS 側の設定
IPv6 対応のパブリックサブネットを作成する
オンプレミスから接続したいプライベートサブネットのある VPC に、「IPv6 CIDR ブロック」を設定したパブリックサブネットを作成します。IPv4 CIDR ブロックの方は今回の検証では特に使わないので、任意の設定で問題ありません。
パブリックサブネットに EC2 インスタンスを起動する
パブリックサブネットに VPN Gateway 代わりとする EC2 インスタンスを立てます。AMI には Ubuntu Server 24.04 LTS を選択します。Amazon Linux にしなかった理由は、Amazon Linux だと公式リポジトリから strongSwan をインストールできないためです(このインスタンスはグローバル IPv4 アドレスを持たず、VPC に NAT Gateway もない想定なので、外部のパッケージを入れなくても完結するようにしたい)。
また、ポイントとして、EC2 の「ネットワーク設定」で「パブリック IP アドレスの自動割り当て」を「無効化」とします。これによりグローバル IPv4 アドレスは払い出されないため、ENI の IPv4 アドレス料金はかからなくなります。逆に「IPv6 IP を自動で割り当てる」は「有効化」します。
EC2 インスタンスのネットワークの送信元アドレス検証を無効化
EC2 の ENI 上にトンネルを通して通信できるようにするため、送信元アドレス検証を無効にします。
IPv6 用のインターネットゲートウェイとルートテーブルを作成する
VPC にインターネットゲートウェイを作成してアタッチします。
次に作成したパブリックサブネットと関連付けるルートテーブルを作成し、IPv6 のデフォルトルート(::/0)の宛先をこのインターネットゲートウェイに設定します。
これでパブリックサブネットから IPv6 で外部のインターネットと接続できるので、パブリックサブネットの EC2 インスタンスから自宅オンプレミスのルーターへまずはグローバル IPv6 アドレス同士で疎通できるようになります。
また、併せて自宅オンプレミス側のサブネットのネクストホップを EC2 の ENI に設定しておきます。
IPSec に必要なポートをセキュリティグループで開放する
自宅オンプレミスのルーターと EC2 インスタンスで IPSec を張れるようにするため、セキュリティグループで UDP 500 番ポートとプロトコル番号 50(ESP)を自宅オンプレミスのルーターのグローバル IPv6 アドレスから接続できるよう設定します。
また、IPSec とは無関係ですが、併せて EC2 インスタンスに SSH できるよう TCP 22 番ポートも開けておきます。今回 EC2 インスタンスにグローバル IPv4 アドレスを設定していないため、Session Manager で EC2 インスタンスを操作できません。VPC エンドポイントを作れば Session Manager で接続できますが、今回費用を安く抑える目的があるので、SSH はグローバル IPv6 アドレスで接続しています。
strongSwan の設定
strongSwan のインストール
Ubuntu Server 22.04 LTS の公式リポジトリから apt で strongSwan をインストールします。
# apt install strongswan
swanctl と systemd で strongSwan を管理したいため、strongswan-starter と strongswan-charon をアンインストールし、strongswan-swanctl と charon-systemd をインストールします。
# apt remove strongswan-starter strongswan-charon
# apt install strongswan-swanctl charon-systemd
strongSwan を起動し、以降 OS 起動時に自動で strongSwan を起動するようにします。
# systemctl start strongSwan
# systemctl enable strongSwan
strongSwan の設定ファイルの編集
strongSwan の設定ファイルは以下のとおり。
- /etc/strongswan.conf
- /etc/strongswan.d/charon.conf
- /etc/swanctl/swanctl.conf
/etc/strongswan.conf と /etc/swanctl/swanctl.conf はデフォルトから変更せず、/etc/strongswan.d/conf.d ディレクトリに次のファイルを追加して設定することにします。
- /etc/swanctl/conf.d/connection.conf
また、今回 strongSwan をトンネルモード IPSec(Route-based VPN)で設定し、トンネルに VTI を割り当てたいので、strongSwan 起動時に VTI を作るシェルスクリプトを追加します。
- /etc/swanctl/updown.sh
以下具体的な設定例です。strongSwan の公式ドキュメントを参考にして設定しました。
トンネルモードの場合、charon の istall_routes を無効にする必要があります。これが有効なままだと、VTI をネクストホップに指定したルーティングテーブルを設定しても動作しません。また、VTI にはシェルスクリプトで IP アドレスを割り当てたいので、仮想 IP アドレスプールから仮想 IP を払い出されないよう istall_virtual_ip も無効にしています。
charon {
install_routes = no
install_virtual_ip = no
# 中略
}
コネクションに関する設定です。IPSec のパラメータは以下のとおりとします。
項目 | 設定値 |
---|---|
IKE バージョン | 2 |
IKE 認証方式 | 事前共有鍵 |
IKE グループ識別子 | modp2048 |
ハッシュアルゴリズム | sha256 |
暗号ペイロード | ESP |
暗号アルゴリズム | AES128-CBC |
検証目的なので認証方式は事前共有鍵としました。トンネルモードの場合、local_ts と reomote_ts は 0.0.0.0/0 にする必要があります。
connections {
site-to-site {
local_addrs = 2001:db8:dead:beaf::1:2:3 # EC2 インスタンスの IPv6 アドレス
remote_addrs = 2001:db8:feed::4:5:6 # 自宅オンプレミスのルーターの IPv6 アドレス
local {
auth = psk
id = 2001:db8:dead:beaf::1:2:3 # EC2 インスタンスの IPv6 アドレス
}
remote {
auth = psk
id = 2001:db8:feed::4:5:6 # 自宅オンプレミスのルーターの IPv6 アドレス
}
children {
net-net {
local_ts = 0.0.0.0/0
remote_ts = 0.0.0.0/0
esp_proposals = aes128-sha256
mode = tunnel
mark_in = 42
mark_out = 42
updown = /etc/swanctl/updown.sh # 後述する VTI 作成用シェルスクリプトのパス
}
}
version = 2
mobike = no
proposals = aes128-sha256-modp2048
}
}
secrets {
ike-1 {
id = 2001:db8:dead:beaf::1:2:3 # EC2 インスタンスの IPv6 アドレス9
secret = "hogehogefugafuga" # 事前共有鍵
}
}
strongSwan 起動時に読み込ませて VTI を作るシェルスクリプトです。
#!/bin/bash
set -o nounset
set -o errexit
VTI_IF="vti1"
VTI_LOCALADDR=10.255.0.253/30 # トンネルに割り当てる IP アドレス
VTI_DESTINATION=10.1.0.0/16 # 自宅オンプレミス側のサブネット
case "${PLUTO_VERB}" in
up-client)
ip -6 tunnel add "${VTI_IF}" local "${PLUTO_ME}" remote "${PLUTO_PEER}" mode vti6 key "${PLUTO_MARK_OUT%%/*}"
sysctl -w "net.ipv4.conf.${VTI_IF}.disable_policy=1" # トンネルモードのため SPD を無効に
sysctl -w "net.ipv4.conf.${VTI_IF}.rp_filter=0" # 送信元アドレス検証を無効に
ip addr add "${VTI_LOCALADDR}" dev "${VTI_IF}"
ip link set "${VTI_IF}" up
ip route add "${VTI_DESTINATION}" dev "${VTI_IF}"
;;
down-client)
ip tunnel del "${VTI_IF}"
;;
esac
設定が終わったら strongSwan を再起動します。
自宅オンプレミスルーター(YAMAHA RTX1200)の設定
IPSec VPN に関する部分のみを抜粋します。
ip route 10.128.128.0/20 gateway tunnel 2 # VPC のプライベートサブネット
tunnel select 2
ipsec tunnel 2
ipsec sa policy 2 2 esp aes-cbc
ipsec ike version 2 2
ipsec ike group 2 modp2048
ipsec ike hash 2 sha256
ipsec ike keepalive log 2 on
ipsec ike keepalive use 2 on rfc4306 10 6 0
ipsec ike local address 2 2001:db8:feed::4:5:6 # 自宅オンプレミスのルーターの IPv6 アドレス
ipsec ike local name 2 2001:db8:feed::4:5:6 ipv6-addr
ipsec ike pre-shared-key 2 hogehogefugafuga # 事前共有鍵
ipsec ike remote address 2 2001:db8:dead:beaf::1:2:3 # EC2 インスタンスの IPv6 アドレス
ipsec ike remote name 2 2001:db8:dead:beaf::1:2:3 ipv6-addr
ip tunnel address 10.255.0.254/30 # トンネルのオンプレミス側 IP アドレス
ip tunnel remote address 10.255.0.253 # トンネルの EC2 側 IP アドレス
ip tunnel tcp mss limit auto
tunnel enable 2
IPSec の動作確認
EC2(Ubuntu Server 22.04 LTS)の動作確認
vti が作成され、IP アドレスが設定されています。
# ip addr
6: vti1@NONE: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 8961 qdisc noqueue state UNKNOWN group default qlen 1000
link/tunnel6 2001:db8:dead:beaf::1:2:3 peer 2001:db8:feed::4:5:6 permaddr 2001:db8:abcd::
inet 10.255.0.253/30 scope global vti1
valid_lft forever preferred_lft forever
inet6 fe80::dead:beaf:feed:1234/64 scope link
valid_lft forever preferred_lft forever
自宅オンプレミス側のサブネットへのルーティングテーブルが正しく設定されていることを確認します。charon.ip_routes を無効にしていないと、スタティックルートを設定しても自宅オンプレミス側のサブネットへのネクストホップが VTI ではなく実インターフェイスに向いてしまい、正しく通信できないので注意です。
# ip route
10.1.0.0/16 dev vti1 scope link
10.255.0.252/30 dev vti1 proto kernel scope link src 10.255.0.253
# ip route get 10.1.0.1
10.1.0.1 dev vti1 src 10.255.0.253 uid 1000
cache
オンプレミス自宅ルーター(YAMAHA RTX1200)の動作確認
SA が確立していることを確認します。
sa sgw isakmp connection dir life[s] remote-id
-----------------------------------------------------------------------------
1 2 - ike - 26744 2001:db8:dead:beaf::1:2:3
9
3 2 1 tun[002]esp send 26744 2001:db8:dead:beaf::1:2:3
9
6 2 1 tun[002]esp recv 26744 2001:db8:dead:beaf::1:2:3
ルーティングテーブルが正しく設定されていることを確認します。
show ip route
Destination Gateway Interface Kind Additional Info.
10.128.128.0/20 - TUNNEL[2] static
10.255.0.252/30 - TUNNEL[2] implicit
あとは自宅オンプレミス側から VPC のプライベートサブネットに通信が通れば、IPv4 over IPv6 の Site-to-Site IPSec VPN が完成です。
BGP の設定
せっかくなので、ルーターインスタンスにした EC2 と BGP で経路交換してルーティングテーブルを設定できるようにします。
ただ残念ながら、本当の仮想プライベートゲートウェイではないため、VPC のルートテーブルに BGP で経路情報を伝搬できないので、VPC のルートテーブルは静的に追加する必要があります。
BIRD のインストールと設定
Ubuntu Server 22.04 LTS の公式リポジトリから apt で BIRD の version 2 系をインストールします。
# apt install bird2
ログファイルを BIRD デーモンから書き込めるようにしておきます。
# touch /var/log/bird.log
# chown bird:bird /var/log/bird.log
BIRD を起動し、以降 OS 起動時に自動で BIRD を起動するようにします。
# systemctl start bird
# systemctl enable bird
BIRD の設定ファイルを変更します。やり方は色々あると思いますが、ポイントは VPC のプライベートサブネットへの経路情報をどう自宅オンプレミス側に流すかで、今回は BIRD の設定ファイルでプライベートサブネットへのスタティックルートを設定するようにしました。なお、プライベートサブネットへのネクストホップは、EC2 のいるパブリックサブネットのデフォルトゲートウェイになります。
また、検証機材が YAMAHA RTX1200 と古く、そのままだと Unsupported optional error が発生して BGP が確立しませんでした。RTX1210 以降であれば bgp neighbor のオプションで capability を無視できるのですが RTX1200 だとできないので、BIRD 側で capabilities を無視する設定としました。
router id 10.255.0.253; # EC2 側のトンネルの IP アドレス
protocol device {
}
protocol direct direct1 {
ipv4; # Connect to default IPv4 table
}
protocol kernel {
ipv4 {
import none; # Import to table, default is import all
export filter {
if proto = "direct1" then reject;
accept;
};
};
}
protocol static {
ipv4; # Again, IPv4 channel with default options
route 10.128.128.0/20 via 10.128.32.1; # VPC のプライベートサブネットへのネクストホップ
}
protocol bgp bgp1 {
local as 65532; # 一意のプライベート AS 番号
source address 10.255.0.253; # EC2 側のトンネルの IP アドレス
neighbor 10.255.0.254 as 65534; # 自宅オンプレミスルーター側に設定する AS 情報
ipv4 {
import all;
export filter {
if net ~ [ 10.128.0.0/16+] then accept; # VPC のサブネットへのルートは全部自宅オンプレミス側に流す
if source = RTS_BGP then accept;
reject;
};
};
capabilities off; # RTX1200 では Unsupported option error となるため
};
設定が終わったら BIRD を再起動します。
オンプレミス自宅ルーター(YAMAHA RTX1200)の BGP 設定
一旦スタティックルートで追加したパブリックサブネットへのルーティングテーブルを削除します。
# no ip route 10.128.128.0/20 gateway tunnel 2
BIRD に設定した BGP の情報と合わせるように RTX1200 側も設定します。
bgp use on
bgp autonomous-system 65534 # 一意のプライベート AS 番号
bgp neighbor 1 65532 10.255.0.253 # EC2 側のトンネルの IP アドレス
bgp router id 10.255.0.254 # 自宅オンプレミスルーター側のトンネルの IP アドレス
bgp import filter 1 include 10.1.0.0/16 # 自宅オンプレミスルーターの LAN 側のサブネット
bgp export filter 1 include 10.128.0.0/16 # VPC のパブリックサブネット
設定が終わったら BGP 設定を再読込みします。
bgp configure refresh
BGP の動作確認
BIRD で正しく BGP が確立してルーティングテーブルが設定されているか確認します。
# birdc show protocol
BIRD 2.14 ready.
Name Proto Table State Since Info
device1 Device --- up 09:20:06.823
direct1 Direct --- up 09:20:06.823
kernel1 Kernel master4 up 09:20:06.823
static1 Static master4 up 09:20:06.823
bgp1 BGP --- up 09:20:10.965 Established
# birdc show route
BIRD 2.14 ready.
Table master4:
10.1.0.0/16 unicast [bgp1 09:20:10.988] * (100) [AS65534i]
via 10.255.0.254 on vti1
10.128.128.0/20 unicast [static1 09:20:06.823] * (200)
via 10.128.32.1 on ens5
10.255.0.252/30 unicast [direct1 09:20:06.825] * (240)
dev vti1
10.128.32.0/20 unicast [direct1 09:20:06.825] * (240)
dev ens5
# ip route
default via 10.128.32.1 dev ens5 proto dhcp src 10.128.36.186 metric 100
10.1.0.0/16 dev vti1 scope link # 元々シェルスクリプトで設定していたスタティックルート
10.1.0.0/16 via 10.255.0.254 dev vti1 proto bird metric 32 # 新たに BIRD が BGP で受け取ったルート
10.128.0.2 via 10.128.32.1 dev ens5 proto dhcp src 10.128.36.186 metric 100
10.128.32.0/20 dev ens5 proto kernel scope link src 10.128.36.186 metric 100
10.128.32.1 dev ens5 proto dhcp scope link src 10.128.36.186 metric 100
10.128.128.0/20 via 10.128.32.1 dev ens5 proto bird metric 32 # 新たに BIRD で設定したスタティックルート
10.255.0.252/30 dev vti1 proto kernel scope link src 10.255.0.253
BGP が Established になっています。
また、Ubuntu のルーティングテーブルに proto bird となっているものが BIRD で設定された経路情報で、自宅オンプレミス側のサブネットへの経路情報と、パブリックサブネットへの経路情報が追加されていることが分かります。
RTX1200 側でも確認します。
> show status bgp neighbor 10.255.0.253
BGP neighbor is 10.255.0.253, remote AS 65532, local AS 65534, external link
BGP version 4, remote router ID 10.255.0.253
BGP state = Established, up for 01:25:37
Last read 00:00:28, hold time is 240, keepalive interval is 80 seconds
Received 75 messages, 0 notifications, 0 in queue
Sent 69 messages, 0 notifications, 0 in queue
Connection established 26; dropped 25
Last reset 01:25:41
Local host: 10.255.0.254, Local port: 179
Foreign host: 10.255.0.253, Foreign port: 55421
> show ip route
Destination Gateway Interface Kind Additional Info.
10.1.0.0/24 10.1.0.254 LAN3 implicit
10.1.15.0/24 10.1.15.254 LAN1 implicit
10.128.32.0/20 10.255.0.253 TUNNEL[2] BGP path=65532
10.128.128.0/20 10.255.0.253 TUNNEL[2] BGP path=65532
こちらも正しく設定されていることが確認できました。
これで自宅オンプレミス側と AWS VPC のプライベートサブネット側で互いに通信できるようになっていると思います。
IPv6 を使えば少ない費用でオンプレミスと閉域 AWS 環境とのハイブリッド接続を検証できますね。
参考サイト