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 を行う場合、今は LibreswanstrongSwan のどちらかを使うのが一般的のようです。 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 encchild-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 を改めて再起動すれば再接続できるが、手動での操作を忘れると困るので何か対策しないといけない。