LibreswanとUNIVERGE IX2105の間でIPsec IKEv2トンネルを張る
目次
はじめに
前々回および前回のブログにて、自宅と Amazon EC2 インスタンスとの間で単純な IPv4 over IPv6 トンネルや Wireguard トンネルを張り、EC2 から自宅経由で IPv4 インターネットと通信する方法について書きました。
本当はプロトコルがシンプルな Wireguard トンネルを常用したいのですが、自宅側にある機器が古い Raspberry PI 2 で信頼性が低いため、やはりルータと EC2 インスタンスとの間でトンネルを張りたくなります。
そこで今回は、ルータと EC2 インスタンスとの間で IPsec トンネルを張り、そのうえで IPv4 インターネットと通信する方法について試してみました。
目標
ルータ (UNIVERGE IX2105) は IKEv1 と IKEv2 の両方に対応しているので当然 IKEv2 を用い、ESP で IPsec トンネルを張ることにします。 IPsec の通信は IPv6 で行い、自宅も EC2 側も IPv6 通信に制限はないので NAT トラバーサルは利用しません。
EC2 インスタンスで IPsec を行う場合、今は Libreswan か strongSwan のどちらかを使うのが一般的のようです。 RHEL では Libreswan を使うのが標準 のようなので、それに倣って Libreswan を使うことにしました。
最終的に EC2 インスタンスの IPv4 のデフォルト経路を IPsec トンネルに向け、一部の経路を VPC 内に閉じて行うようにします。
環境説明
検証前のルータ設定
ルータとその設定は 「NEC UNIVERGE IX2105 で OCN 光の IPoE 接続を行う」 に書いたような内容です。
AWS 側
検証用の EC2 インスタンスや AWS 側のネットワークは次の通りです。
- VPC 関連
- VPC アドレス : 172.31.0.0/16
- サブネット : 172.31.0.0/20
- Internet Gateway : あり
- EC2 インスタンス関連
- パブリック IPv4 アドレス : なし
- プライベート IPv4 アドレス : 172.31.0.4
- IPv6 アドレス : 2406:Y:Y:Y::4
- OS : Ubuntu 22.04.3 LTS (kernel 6.2.0-1018-aws)
自宅側
- 回線/ISP
- OCN 光
- IPv6 インターネット : IPoE
- IPv4 インターネット : MAP-E (over IPoE)
- ルータ
- NEC UNIVERGE IX2105
- プライベート IPv4 アドレス : 192.168.1.1
- IPv6 アドレス : 2400:X:X:X::X
ルータ設定
まずはルータ側の設定を行っていきます。 設定内容はコマンドリファレンスマニュアルと設定事例集(29章)を参考にして作りました。
IPsec 設定
IPsec のパラメータとトンネル設定は次のようにしました。
1ikev2 authentication psk id ipv6 2400:X:X:X::X key char 事前共有鍵
2ikev2 authentication psk id ipv6 2406:Y:Y:Y::4 key char 事前共有鍵
3
4ikev2 default-profile
5 child-lifetime 3600
6 child-pfs 2048-bit
7 child-proposal enc aes-cbc-256
8 child-proposal integrity sha2-512
9 dpd interval 30
10 local-authentication psk id ipv6 2400:X:X:X::X
11 sa-lifetime 28800
12 sa-proposal enc aes-cbc-256
13 sa-proposal integrity sha2-512
14 sa-proposal dh 2048-bit
15
16interface Tunnel1.0
17 tunnel mode ipsec-ikev2
18 ip unnumbered GigaEthernet1.0
19 ip tcp adjust-mss auto
20 ikev2 connect-type trigger
21 ikev2 ipsec pre-fragment
22 ikev2 peer 2406:Y:Y:Y::4 authentication psk id ipv6 2406:Y:Y:Y::4
23 no shutdown
認証方式は RSA 鍵でもいけそうでしたが、冒険せずに事前共有鍵にしました。
IX2105 では事前共有鍵は128文字まで可能なので、"openssl rand -base64 96"
コマンドで768ビットの乱数から作成した128文字としました。
暗号化、MAC、DHなどのアルゴリズムはできるだけ強固なものを設定しています。
コマンドリファレンスでは暗号化アルゴリズム (sa-proposal enc
と child-proposal enc
パラメータ) として aes-gcm-256-16
などより強固なものが利用できるように記載されていますが、IX2105 では利用できないようでした。
基本的にルータは常時起動しており、EC2 インスタンスは停止や再起動を行うことがあるため、EC2 インスタンス側から IPsec 接続を開始する前提で ikev2 connect-type
パラメータを trigger
としています。
また、EC2 インスタンスの異常停止などで IPsec が正常に切断されなかったケースに備えて、DPD を有効にして EC2 インスタンス側の生存を確認できない場合はルータ側の SA を削除するようにしています。
パケットフィルタ設定
EC2 インスタンスとの間で行う IPsec 通信のパケットを明示的に許可しておきます。
1ipv6 access-list permit-ipsec-i permit udp src 2406:Y:Y:Y::4/128 sport eq 500 dest 2400:X:X:X::X/128 dport eq 500
2ipv6 access-list permit-ipsec-i permit 50 src 2406:Y:Y:Y::4/128 dest 2400:X:X:X::X/128
3ipv6 access-list permit-ipsec-o permit udp src 2400:X:X:X::X/128 sport eq 500 dest 2406:Y:Y:Y::4/128 dport eq 500
4ipv6 access-list permit-ipsec-o permit 50 src 2400:X:X:X::X/128 dest 2406:Y:Y:Y::4/128
5
6interface GigaEthernet0.0
7 ipv6 filter permit-ipsec-i 11 in
8 ipv6 filter permit-ipsec-o 11 out
UDP 500 番ポートは IKEv2 の通信、IPプロトコル 50 番は ESP の通信です。 NAT トラバーサルは利用しないので UDP 4500 番ポートの許可を行っていません。
経路設定
EC2 インスタンス向きの経路も忘れずに設定します。
1ip route 172.31.0.4/32 Tunnel1.0
トンネルのステータス確認
この時点では情報にあまり意味はありませんが、トンネルのステータスは次のようになっていました。
1Router(config)# show interfaces Tunnel1.0
2Interface Tunnel1.0 is up
3 Fundamental MTU is 1500 octets
4 Current bandwidth 100M b/s, QoS is disabled
5 Datalink header cache type is ipsec: 0/0 (standby/dynamic)
6 IPv4 subsystem connected, physical layer is up, 0:01:16
7 Dialer auto-connect is enabled
8 Inbound call is enabled
9 Outbound call is enabled
10 Dial on demand restraint is disabled, 0 disconnect
11 SNMP MIB-2:
12 ifIndex is 799
13 Logical INTERFACE:
14 Elapsed time after clear counters 0:01:38
15 0 packets input, 0 bytes, 0 errors
16 0 unicasts, 0 non-unicasts, 0 unknown protos
17 0 drops, 0 misc errors
18 0 output requests, 0 bytes, 0 errors
19 0 unicasts, 0 non-unicasts
20 0 overflows, 0 neighbor unreachable, 0 misc errors
21 1 link-up detected, 0 link-down detected
22 Encapsulation TUNNEL:
23 Tunnel mode is ipsec-ikev2
24 Tunnel is ready
25 Destination address is 2406:Y:Y:Y::4
26 Statistics:
27 0 packets input, 0 bytes, 0 errors
28 0 packets output, 0 bytes, 0 errors
EC2 インスタンス設定
Libreswan のインストール
Libreswan は Ubuntu 標準パッケージとして配布されているので apt でインストールできます。
1$ sudo apt install libreswan
2
3$ ipsec --version
4Linux Libreswan 3.32 (netkey) on 6.2.0-1018-aws
また、インストールしたことで systemd に ipsec.service が disabled で追加されていました。
Libreswan の設定
Libreswan の設定に一番苦労しました。 最終的に公式ドキュメントの次のページを参考に設定しました。
1$ sudo vim /etc/ipsec.d/home.conf
2$ sudo cat /etc/ipsec.d/home.conf
3conn home
4 left=2400:X:X:X::X
5 right=2406:Y:Y:Y::4
6 leftsubnet=0.0.0.0/0
7 rightsubnet=0.0.0.0/0
8 ipsec-interface=yes
9 encapsulation=no
10 dpddelay=10
11 dpdtimeout=30
12 dpdaction=restart
13 authby=secret
14 auto=start
15
16
17$ sudo vim /etc/ipsec.d/home.secrets
18$ sudo chmod 600 /etc/ipsec.d/home.secrets
19$ sudo cat /etc/ipsec.d/home.secrets
202400:X:X:X::X 2406:Y:Y:Y::4 : PSK "事前共有鍵"
IPv4 のデフォルト経路を IPsec トンネルに向け、一部の経路を VPC 内に閉じて行わせたいのですが、ポリシーベースでは期待通り動作させられなかったため、ipsec-interface=yes
としてルートベースの IPsec としました。
OS のルートテーブルを直接操作するほうが慣れているというのもあります。
NAT トラバーサルは行わないので encapsulation=no
としています。
auto=start
とすることで、ipsec.service を起動したときに上記の IPsec 接続が自動的に行われるようにしています。
ただし、それだけでは実際に IPv4 インターネット通信ができる状態にはならないのですが、これについては後述します。
セキュリティグループの設定
今回は EC2 インスタンスから自宅ルータに向けて IPsec 接続を開始し、トンネル内の通信も EC2 インスタンス側から行う想定です。
セキュリティグループの仕様では、今回のような IKEv2 の通信は UDP なのでデフォルト180秒、ESP の通信は600秒の間はインバウンド方向が自動的に許可されます。
- 参考 : セキュリティグループの接続の追跡
双方向に DPD を設定しているため IKEv2 のインバウンド許可は維持され続けるはずですし、トンネル内の通信がなくて ESP のインバウンド許可がなくなっても特に困りません。 そのため、EC2 インスタンスからアウトバウンド方向に許可されていれば、インバウンド方向の許可は基本的には不要のはずです。
念のためセキュリティグループのインバウンドルールを許可するなら次のような内容ですが、最終的には設定しませんでした。
- IKEv2 通信用
- タイプ : カスタム UDP
- プロトコル : UDP
- ポート範囲 : 500
- ソース : 2400:X:X:X::X/128
- ESP 通信用
- タイプ : カスタムプロトコル
- プロトコル : ESP (50)
- ソース : 2400:X:X:X::X/128
IPsec 接続の開始
設定の準備が整ったら、ipsec.service を起動して IPsec 接続を開始してみます。
1$ sudo systemctl start ipsec.service
すると、トンネルインターフェース ipsec1
が作られていました。
1$ ip a
21: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
3 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
4 inet 127.0.0.1/8 scope host lo
5 valid_lft forever preferred_lft forever
6 inet6 ::1/128 scope host
7 valid_lft forever preferred_lft forever
82: ens5: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001 qdisc mq state UP group default qlen 1000
9 link/ether 06:46:43:77:18:77 brd ff:ff:ff:ff:ff:ff
10 inet 172.31.0.4/20 metric 100 brd 172.31.15.255 scope global dynamic ens5
11 valid_lft 3326sec preferred_lft 3326sec
12 inet6 2406:Y:Y:Y::4/128 scope global dynamic noprefixroute
13 valid_lft 392sec preferred_lft 82sec
14 inet6 fe80::446:43ff:fe77:1877/64 scope link
15 valid_lft forever preferred_lft forever
163: ip_vti0@NONE: <NOARP> mtu 1480 qdisc noop state DOWN group default qlen 1000
17 link/ipip 0.0.0.0 brd 0.0.0.0
185: ipsec1@ens5: <NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
19 link/none
20 inet6 fe80::381e:d34e:b603:3f38/64 scope link stable-privacy
21 valid_lft forever preferred_lft forever
また、UDP 500 番ポートと 4500 番ポートも新しく Listen されていました。 pluto いう名前のプロセスが Libreswan です。 4500 番ポートの Listen は不要なのですが、Listen させないようにする設定がわからず、害はないのでそのままにしておきました。
1$ sudo ss -lntup
2Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
3udp UNCONN 0 0 127.0.0.1:500 0.0.0.0:* users:(("pluto",pid=1166,fd=19))
4udp UNCONN 0 0 172.31.0.4:500 0.0.0.0:* users:(("pluto",pid=1166,fd=17))
5udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=314,fd=13))
6udp UNCONN 0 0 172.31.0.4%ens5:68 0.0.0.0:* users:(("systemd-network",pid=312,fd=18))
7udp UNCONN 0 0 127.0.0.1:4500 0.0.0.0:* users:(("pluto",pid=1166,fd=20))
8udp UNCONN 0 0 172.31.0.4:4500 0.0.0.0:* users:(("pluto",pid=1166,fd=18))
9udp UNCONN 0 0 [2406:Y:Y:Y::4]:500 [::]:* users:(("pluto",pid=1166,fd=22))
10udp UNCONN 0 0 [::1]:500 [::]:* users:(("pluto",pid=1166,fd=21))
11udp UNCONN 0 0 [fe80::446:43ff:fe77:1877]%ens5:546 [::]:* users:(("systemd-network",pid=312,fd=21))
12tcp LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:* users:(("systemd-resolve",pid=314,fd=14))
13tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=661,fd=3))
14tcp LISTEN 0 128 [::]:22 [::]:* users:(("sshd",pid=661,fd=4))
さらに、tcpdump で見ると IPsec 接続開始時に次の通信が発生していました。
123:36:11.281517 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
223:36:11.351386 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: parent_sa ikev2_init[R]
323:36:11.353581 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa ikev2_auth[I]
423:36:11.370217 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: child_sa ikev2_auth[R]
523:36:11.376720 ipsec1 Out IP6 fe80::381e:d34e:b603:3f38 > ff02::2: ICMP6, router solicitation, length 8
IKEv2 なので2往復で IKE SA と Child SA が作られて IPsec 接続が完了していることがわかります。 5行目のパケットは ipsec1 インターフェースが作成されたことによる IPv6 の Router Solicication メッセージなので、IPsec とは無関係のものです。
ルータ側の接続確認
ルータ側でもちゃんと接続されているか確認しておきます。
1Router(config)# show ikev2 sa
2IKEv2 SA - 1 created
3Interface Tunnel1.0
4 SPI (I)0xdc546ede1082ce4b (R)0xd020ca89272281a0
5 Remain lifetime[sec] : 28693
6 Serial : 1
7 Direction : responder
8 Local Addr : [2400:X:X:X::X]:500
9 Peer Addr : [2406:Y:Y:Y::4]:500
10 Local ID : IPV6-ADDR 2400:X:X:X::X
11 Peer ID : IPV6-ADDR 2406:Y:Y:Y::4
12 Status : establish
13 Local message ID : 3
14 Peer message ID : 10
15 Encryption alg : AES-CBC-256
16 initiator key : 省略
17 responder key : 省略
18 Integrity alg : HMAC-SHA2-512-256
19 initiator key : 省略
20 responder key : 省略
21 PRF alg : HMAC-SHA2-512
22 DH group : MODP-2048
23 PFS : MODP-2048
24 DPD : interval 30[sec]
25 Child
26 Prot SPI(IN) SPI(OUT) Lifetime[sec]
27 ESP 0x31716c24 0xb7d5908a 3493
1Router(config)# show ikev2 child-sa
2Child SA - 1 connected
3Interface Tunnel1.0
4 IKE Peer ID : IPV6-ADDR 2406:Y:Y:Y::4
5 IKE SPI (I)0xdc546ede1082ce4b (R)0xd020ca89272281a0
6 IKE SA serial : 1
7 Child SA
8 Protocol : ESP
9 Local Addr : 2400:X:X:X::X
10 Peer Addr : 2406:Y:Y:Y::4
11 Enc alg : AES-CBC-256
12 Hash alg : HMAC-SHA2-512-256
13 Remain lifetime[sec] : 3483
14 Anti-replay : on
15 Direction is outbound
16 SPI : 0xb7d5908a
17 Serial : 1
18 Authkey : 省略
19 Enckey : 省略
20 Direction is inbound
21 SPI : 0x31716c24
22 Serial : 2
23 Authkey : 省略
24 Enckey : 省略
25 Local TS:
26 TS type : IPV4-ADDR-RANGE
27 Protocol any
28 Address Range 0.0.0.0 to 255.255.255.255
29 Port Range 0 to 65535
30 Peer TS:
31 TS type : IPV4-ADDR-RANGE
32 Protocol any
33 Address Range 0.0.0.0 to 255.255.255.255
34 Port Range 0 to 65535
35 Statistics
36 Outbound
37 0 packets, 0 octets
38 0 cipher failure, 0 out of memory, 0 ts unacceptable
39 0 misc error, 0 invalid packet, 0 packet too big
40 Inbound
41 0 packets, 0 octets
42 0 invalid sa, 0 replay detected, 0 integrity failure
43 0 cipher failure, 0 packet truncated, 0 invalid padding
44 0 unknown protocol, 0 out of memory, 0 ts unacceptable
45 0 misc error, 0 tcp chksum error, 0 invalid packet
46 0 recv error
47 History
48 Time Event
49 2024/02/03 23:36:11 Create
1Router(config)# show interfaces Tunnel1.0
2Interface Tunnel1.0 is up
3 Fundamental MTU is 1390 octets
4 Current bandwidth 100M b/s, QoS is disabled
5 Datalink header cache type is ipsec: 0/0 (standby/dynamic)
6 IPv4 subsystem connected, physical layer is up, 0:06:00
7 Dialer auto-connect is enabled
8 Inbound call is enabled
9 Outbound call is enabled
10 Dial on demand restraint is disabled, 0 disconnect
11 SNMP MIB-2:
12 ifIndex is 799
13 Logical INTERFACE:
14 Elapsed time after clear counters 0:06:22
15 0 packets input, 0 bytes, 0 errors
16 0 unicasts, 0 non-unicasts, 0 unknown protos
17 0 drops, 0 misc errors
18 0 output requests, 0 bytes, 0 errors
19 0 unicasts, 0 non-unicasts
20 0 overflows, 0 neighbor unreachable, 0 misc errors
21 1 link-up detected, 0 link-down detected
22 Encapsulation TUNNEL:
23 Tunnel mode is ipsec-ikev2
24 Tunnel is ready
25 Destination address is 2406:Y:Y:Y::4
26 Source address is 2400:X:X:X::X
27 Nexthop address is fe80::X:X:X
28 Outgoing interface is GigaEthernet0.0
29 Interface MTU is 1390
30 Statistics:
31 0 packets input, 0 bytes, 0 errors
32 0 packets output, 0 bytes, 0 errors
SA が作られており、アルゴリズムも設定したものが利用されていますので、期待通りの結果です。
トンネルインターフェースの MTU が 1390 になっているのも計算通りです。
- 回線 MTU : 1500
- IPv6 ヘッダ : 40
- ESP ヘッダ : 8
- ESP 初期化ベクトル : 16 (AES-CBC-256)
- ESP 認証データ : 32 (SHA2-512)
- ESP トレーラ : 2
- 計算式 :
rounddown((1500-40-8-16-32)/16, 0)*16-2
経路設定と MTU 変更
EC2 インスタンスのルートテーブルに適切に経路を追加し、IPv4 インターネットにアクセスできるようにします。
1$ sudo ip route add 169.254.0.0/16 via 172.31.0.1 dev ens5
2$ sudo ip route add 172.31.0.0/16 via 172.31.0.1 dev ens5
3$ sudo ip route add default dev ipsec1
4
5$ ip route
6default dev ipsec1 scope link
7default via 172.31.0.1 dev ens5 proto dhcp src 172.31.0.4 metric 100
8169.254.0.0/16 via 172.31.0.1 dev ens5
9172.31.0.0/20 dev ens5 proto kernel scope link src 172.31.0.4 metric 100
10172.31.0.0/16 via 172.31.0.1 dev ens5
11172.31.0.1 dev ens5 proto dhcp scope link src 172.31.0.4 metric 100
12172.31.0.2 dev ens5 proto dhcp scope link src 172.31.0.4 metric 100
リンクローカルや VPC ネットワーク (他のサブネット用) の経路も追加しないと、それら通信が自宅ルータ側に行ってしまうので、忘れずに設定しましょう。
また、EC2 インスタンス内のトンネルインターフェースの MTU が 1500 になっていて、これだと TCP 以外の通信の開始時にパケットがドロップされる可能性があるため、明示的に設定しておきます。
1$ sudo ip link set dev ipsec1 mtu 1390
IPv4 インターネットとの通信確認
それでは待望の IPv4 インターネットとの通信を確認してみます。
1$ ping 8.8.8.8 -c 2
2PING 8.8.8.8 (8.8.8.8) 56(84) bytes of data.
364 bytes from 8.8.8.8: icmp_seq=1 ttl=117 time=11.9 ms
464 bytes from 8.8.8.8: icmp_seq=2 ttl=117 time=10.7 ms
5
6--- 8.8.8.8 ping statistics ---
72 packets transmitted, 2 received, 0% packet loss, time 1002ms
8rtt min/avg/max/mdev = 10.684/11.294/11.905/0.610 ms
期待通り IPv4 インターネットと疎通があり、tcpdump で見ても期待通りの通信内容でした。
123:40:00.154980 ipsec1 Out IP 172.31.0.4 > 8.8.8.8: ICMP echo request, id 6, seq 1, length 64
223:40:00.155019 ens5 Out IP6 2406:Y:Y:Y::4 > 2400:X:X:X::X: ESP(spi=0x31716c24,seq=0x1), length 152
323:40:00.166848 ens5 In IP6 2400:X:X:X::X > 2406:Y:Y:Y::4: ESP(spi=0xb7d5908a,seq=0x1), length 152
423:40:00.166868 ipsec1 In IP 8.8.8.8 > 172.31.0.4: ICMP echo reply, id 6, seq 1, length 64
523:40:01.157049 ipsec1 Out IP 172.31.0.4 > 8.8.8.8: ICMP echo request, id 6, seq 2, length 64
623:40:01.157092 ens5 Out IP6 2406:Y:Y:Y::4 > 2400:X:X:X::X: ESP(spi=0x31716c24,seq=0x2), length 152
723:40:01.167679 ens5 In IP6 2400:X:X:X::X > 2406:Y:Y:Y::4: ESP(spi=0xb7d5908a,seq=0x2), length 152
823:40:01.167707 ipsec1 In IP 8.8.8.8 > 172.31.0.4: ICMP echo reply, id 6, seq 2, length 64
もちろん TCP や UDP でも IPv4 インターネットと通信でき、自宅のルータや他のマシンとも IPv4 で通信できることも確認できました。
DPD と再接続の確認 (異常系の動作確認)
メンテナンス等のために EC2 インスタンスやルータを停止したり、各機器やインターネット回線が異常で停止したりする可能性があり、そのような場合にどのような挙動になるのか少し確認しました。
EC2 インスタンスで IPsec サービスを停止した場合
EC2 インスタンスで IPsec サービスを停止すると、トンネルインターフェースが削除され、ルータ側の SA も削除されていました。 トンネルインターフェースが削除されたのでデフォルト経路は削除されましたが、リンクローカルや VPC ネットワーク宛の経路はそのまま残っていました。
この状態であれば、EC2 インスタンスで改めて IPsec サービスを起動し、デフォルト経路や MTU 設定を行えば元の状態に復帰できます。
EC2 インスタンスの OS を再起動した場合
EC2 インスタンスの OS を再起動すると、IPsec サービスの停止も実行されるため、当然ルータ側の SA も削除されていました。 EC2 インスタンスで IPsec サービスを起動し、各種経路や MTU 設定を行えば元の状態に復帰できます。
ルータを停止した場合
ルータを停止してみた時の動作も確認しました。 23:54:00 ごろに電源オフ、その5分後に電源オンにし、このときの EC2 インスタンス側の tcpdump 結果は次のようになっていました。
123:53:53.714681 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa inf2[I]
223:53:53.721701 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: child_sa inf2[R]
323:54:03.726976 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa inf2[I]
423:54:13.734383 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa inf2[I]
523:54:23.745455 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa inf2[I]
623:54:23.756543 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa inf2[I]
723:54:23.758804 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
823:54:24.259532 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
923:54:24.764576 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
1023:54:25.767970 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
1123:54:27.772374 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
1223:54:31.776806 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
1323:54:39.782290 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
1423:54:55.790614 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
1523:55:27.813994 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
1623:55:28.314738 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
1723:55:28.819523 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
1823:55:29.820736 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
1923:55:31.821589 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
2023:55:35.825808 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
2123:55:43.825547 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
2223:55:59.832869 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
2323:56:31.837537 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
2423:56:32.338263 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
2523:56:32.838991 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
2623:56:33.839857 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
2723:56:35.842092 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
2823:56:39.846567 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
2923:56:47.847848 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
3023:57:03.859358 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
3123:57:29.939865 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
3223:57:30.440627 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
3323:57:30.941367 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
3423:57:31.942593 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
3523:57:33.944805 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
3623:57:37.949034 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
3723:57:45.957462 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
3823:58:01.970821 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
3923:58:33.978312 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
4023:58:34.479126 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
4123:58:34.979856 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
4223:58:35.981052 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
4323:58:37.983287 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
4423:58:41.987557 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
4523:58:49.990264 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
4623:59:06.006490 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
4723:59:29.938544 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
4823:59:30.437408 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
4923:59:30.938126 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
5023:59:31.939356 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
5123:59:33.941585 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
5223:59:37.945880 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
5323:59:45.954152 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
5400:00:01.966609 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
5500:00:02.033332 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: parent_sa ikev2_init[R]
5600:00:02.035402 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa ikev2_auth[I]
5700:00:02.052437 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: child_sa ikev2_auth[R]
5800:00:32.059636 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa inf2[I]
5900:00:32.066647 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: child_sa inf2[R]
6000:00:32.069852 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: parent_sa inf2
6100:00:32.069981 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa inf2[IR]
6200:00:42.072301 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa inf2[I]
6300:00:42.079946 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: child_sa inf2[R]
10秒間隔で DPD を行うようにしており、3回失敗で再接続を開始するような設定としていましたので、23:54:23 から IKE_SA_INIT が送信されているのは期待通りです。 その後、よくわからない間隔で IKE_SA_INIT が送信されていますが、ルータが起動して 00:00:02 に応答が返ってくると、IKE_AUTH も送信され IPsec 接続が確立していました。
ルータで IKE SA / Child SA を削除した場合
ルータで誤って IKE SA や Child SA を削除してしまった場合の動作も一応確認しました。
まず、IKE SA を削除すると、すぐに EC2 インスタンス側から IPsec 再接続が開始されて復帰していました。
123:51:03.541265 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: child_sa inf2
223:51:03.541552 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa inf2[IR]
323:51:03.551155 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: parent_sa ikev2_init[I]
423:51:03.619471 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: parent_sa ikev2_init[R]
523:51:03.621501 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa ikev2_auth[I]
623:51:03.638401 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: child_sa ikev2_auth[R]
Child SA を削除した場合はすぐに再作成されることはなく、おそよ3分20秒後に EC2 インスタンス側から再作成が開始されて復帰していました。 この間、ルータ側からの DPD は継続していましたが、EC2 インスタンス側からの DPD は行われていませんでした。
また、Child SA を削除した後、再作成される前にトンネル内の通信を行うと、そのタイミングで再作成されて復帰しました。 再作成のトリガーとなったパケットはバッファされていないようであり、そのパケットはドロップされてしまいました。
123:46:45.141940 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: child_sa inf2
223:46:45.142248 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa inf2[IR]
3
423:49:48.057125 ipsec1 Out IP 172.31.0.4 > 8.8.8.8: ICMP echo request, id 2, seq 1, length 64
523:49:48.057175 lo In IP 172.31.0.4 > 172.31.0.4: ICMP host 8.8.8.8 unreachable, length 92
623:49:48.059999 ens5 Out IP6 2406:Y:Y:Y::4.500 > 2400:X:X:X::X.500: isakmp: child_sa child_sa[I]
723:49:48.126798 ens5 In IP6 2400:X:X:X::X.500 > 2406:Y:Y:Y::4.500: isakmp: child_sa child_sa[R]
823:49:49.067819 ipsec1 Out IP 172.31.0.4 > 8.8.8.8: ICMP echo request, id 2, seq 2, length 64
923:49:49.067861 ens5 Out IP6 2406:Y:Y:Y::4 > 2400:X:X:X::X: ESP(spi=0x481cffbf,seq=0x1), length 152
1023:49:49.079561 ens5 In IP6 2400:X:X:X::X > 2406:Y:Y:Y::4: ESP(spi=0x7c6d1f3b,seq=0x1), length 152
1123:49:49.079589 ipsec1 In IP 8.8.8.8 > 172.31.0.4: ICMP echo reply, id 2, seq 2, length 64
インターネット回線が停止した場合
実はこの項目は動作確認していません。
インターネット回線の停止が30秒以内であれば、EC2 インスタンスとルータのどちらも SA は削除されていないため、回線が復帰すれば IPsec の通信も復帰するはずです。 また、インターネット回線の停止が90秒以上であれば、EC2 インスタンスとルータのどちらも DPD によって SA は削除されているので、回線が復帰すれば IPsec の再接続が行われて復帰するはずです。
しかし、インターネット回線の停止が30~90秒の間だと、EC2 インスタンス側の SA は削除されているもののルータの SA は残ったままなので、回線が復帰してもすぐには IPsec は再接続できず、ルータの SA が DPD で削除されてようやく再接続できるようになるはずです。 これは、EC2 インスタンスとルータの DPD の間隔を異なるものにしたことが原因なので、同じにしたほうが良いかもしれません。
IPsec の自動接続など
前項では IPsec サービスの起動や経路の設定を手動で行いましたが、EC2 インスタンスを再起動したときに自動で IPv4 インターネットと通信できる状態に設定していきます。
IPsec サービスの自動起動
IPsec サービスの自動起動は簡単で、単に systemctl で有効化すれば良いだけです。
1$ sudo systemctl enable ipsec.service
経路の自動設定
IPv4 インターネットへの経路を設定するには、IPsec 接続が確立してトンネルインターフェースが作成されている必要があります。 Libreswan には IPsec 接続の状態が変化したときにスクリプトを実行する起動が用意されていますので、これを用いて設定していきます。
Libreswan の設定で rightupdown
に実行したいスクリプトを指定できます。
今回は right 側がローカルなので rightupdown
を指定しており、この項目の説明は設定ドキュメントの leftupdown
と同じです。
試しに適当なスクリプトを用意して rightupdown
に指定し、ipsec.service を起動してみると、スクリプトが3回実行されました。
そのとき、スクリプトには次のような環境変数が追加されて渡ってきていました。
1PLUTO_ADDTIME=0
2PLUTO_CFG_CLIENT=0
3PLUTO_CFG_SERVER=0
4PLUTO_CONNECTION=home
5PLUTO_CONN_ADDRFAMILY=ipv6
6PLUTO_CONN_KIND=CK_PERMANENT
7PLUTO_CONN_POLICY=PSK+ENCRYPT+TUNNEL+PFS+UP+IKEV2_ALLOW+SAREF_TRACK+IKE_FRAG_ALLOW+ESN_NO
8PLUTO_INTERFACE=ens5
9PLUTO_IS_PEER_CISCO=0
10PLUTO_ME=2406:Y:Y:Y::4
11PLUTO_MY_CLIENT=0.0.0.0/0
12PLUTO_MY_CLIENT_MASK=0.0.0.0
13PLUTO_MY_CLIENT_NET=0.0.0.0
14PLUTO_MY_ID=2406:Y:Y:Y::4
15PLUTO_MY_PORT=0
16PLUTO_MY_PROTOCOL=0
17PLUTO_NEXT_HOP=2400:X:X:X::X
18PLUTO_NM_CONFIGURED=0
19PLUTO_PEER=2400:X:X:X::X
20PLUTO_PEER_BANNER=
21PLUTO_PEER_CA=
22PLUTO_PEER_CLIENT=0.0.0.0/0
23PLUTO_PEER_CLIENT_MASK=0.0.0.0
24PLUTO_PEER_CLIENT_NET=0.0.0.0
25PLUTO_PEER_DNS_INFO=
26PLUTO_PEER_DOMAIN_INFO=
27PLUTO_PEER_ID=2400:X:X:X::X
28PLUTO_PEER_PORT=0
29PLUTO_PEER_PROTOCOL=0
30PLUTO_SA_REQID=16388
31PLUTO_SA_TYPE=ESP
32PLUTO_STACK=netkey
33PLUTO_VERB=up-client-v6
34PLUTO_VERSION=2.0
35PLUTO_VIRT_INTERFACE=ipsec1
36PLUTO_XFRMI_FWMARK=
37PLUTO_XFRMI_ROUTE=yes
38SPI_IN=0xa654983e
39SPI_OUT=0x861101d0
40VTI_IFACE=
41VTI_ROUTING=no
42VTI_SHARED=no
スクリプトが3回実行されたときに、環境変数 PLUTO_VERB
だけはそれぞれ異なる値 up-client-v6
, prepare-client-v6
, route-client-v6
となっていました。
また、ipsec.service を停止した時は rightupdown
に指定したスクリプトが2回実行され、環境変数 PLUTO_VERB
はそれぞれ down-client-v6
, unroute-client-v6
となっていました。
さらに、rightupdown
を指定しない場合は "ipsec _updown"
コマンドが実行されており、そのコマンドによって "/usr/libexec/ipsec/_updown"
スクリプトが実行されていることもわかりました。
これらの情報から、IPsec 接続確立時に経路の追加と MTU 変更を行い、切断時に経路を削除するような次のスクリプトを作成しました。
1#!/bin/bash
2
3if [ "${PLUTO_VERB}" == "route-client-v6" ]; then
4
5 if [ -z "`ip route show 169.254.0.0/16`" ]; then
6 ip route add 169.254.0.0/16 via 172.31.0.1 dev ens5
7 fi
8
9 if [ -z "`ip route show 172.31.0.0/16`" ]; then
10 ip route add 172.31.0.0/16 via 172.31.0.1 dev ens5
11 fi
12
13 ip route add default dev ${PLUTO_VIRT_INTERFACE}
14 ip link set dev ${PLUTO_VIRT_INTERFACE} mtu 1390
15
16elif [ "${PLUTO_VERB}" == "unroute-client-v6" ]; then
17
18 ip route del 169.254.0.0/16 via 172.31.0.1 dev ens5
19 ip route del 172.31.0.0/16 via 172.31.0.1 dev ens5
20
21fi
22
23ipsec _updown
自動的に作成されるトンネルインターフェース名は固定ではない (※IPsec 接続が1本だと実質的に固定ですが) ため、環境変数から参照するようにしています。
また、"ipsec_updown"
コマンドを実行しないと IPsec トンネルが動作しなかったので、元の仕組みと同等になるよう最後に実行するようにしています。
あとはこのスクリプトを配置し、Libreswan の設定に追記します。
1$ sudo vim /etc/ipsec.d/home_updown.sh
2$ sudo chmod a+x /etc/ipsec.d/home_updown.sh
3
4$ sudo vim /etc/ipsec.d/home.conf
5$ sudo cat /etc/ipsec.d/home.conf
6conn home
7 left=2400:X:X:X::X
8 right=2406:Y:Y:Y::4
9 leftsubnet=0.0.0.0/0
10 rightsubnet=0.0.0.0/0
11 ipsec-interface=yes
12 encapsulation=no
13 dpddelay=10
14 dpdtimeout=30
15 dpdaction=restart
16 authby=secret
17 auto=start
18 rightupdown=/etc/ipsec.d/home_updown.sh
自動接続の動作確認
EC2 インスタンスを正常に再起動してみると、期待通り IPsec 接続が確立して IPv4 インターネットとの通信が行える状態に復帰しました。
まとめ
EC2 インスタンスとルータとの間で IPsec IKEv2 トンネルを張り、EC2 インスタンスから自宅経由で IPv4 インターネットにアクセスできるようにしました。 これにより、前々回の記事 に記載したセキュリティ上の問題を解消しつつ、引き続き料金を節約できるようになりました。 ルータでトンネルを張るようにしたことで、Raspberry PI を使っていることによる不安感も拭えました。
Libreswan 側の設定は今後も調整や改良をしていくはずですが、ひとまずは本記事の内容で安定稼働するか長期的な動作を確認してきます。
追記
1週間ぐらい経過しても IPsec が切断されることなく稼働していた。
ただし、apt upgrade を実行して ipsec.service と systemd-networkd.service が同時に再起動されたとき、IPsec が正常に再接続されなかった。
1Feb 11 08:26:14 scm.ymstmsys.jp pluto[20290]: "home": we cannot identify ourselves with either end of this connection. 2400:X:X:X::X or 2406:Y:Y:Y::4 are not usable
ipsec.service を改めて再起動すれば再接続できるが、手動での操作を忘れると困るので何か対策しないといけない。