Linux サーバ間 IPsec 接続 (libreswan)

Linux サーバ同士の間で libreswan を使って IPsec を接続してみる。strongswan を使ったやり方は別記事にて。

I. 前提

環境は以下の通り。

vpn1、vpn2、host1、host2、router1 OSは全てUbuntu 18.04である。

vpn1←→vpn2の間で、libreswanでトンネルモードIPsec接続をする。10.0.1.0/24 から 10.0.2.0/24 へのパケット、またその逆方向のパケットはトンネルへ入るようにする。つまり、例えばhost1からhost2へpingを打つとトンネルを通ることになる。10.0.1.0/24や10.0.2.0/24へのスタティックルートはrouter1に追加しないようにしておくので、VPNトンネルが出来なければhost1からhost2へのpingは到達できない。

vpn2側に自動接続開始の設定を入れることで、VPNトンネルを自動的に張ることにする。

II. 設定

以下、設定を記述する(IPアドレス設定など基本的なところは省略)

1. router1 の設定:

通過パケットを転送できるように、カーネルパラメータを変更する。
user@router1:~$ sudo vi /etc/sysctl.conf

net.ipv4.ip_forward=1  #28行目のコメントを外す

上記カーネルパラメータを有効化する。
user@router1:~$ sudo sysctl -p /etc/sysctl.conf

2. vpn1の設定:

libreswan をインストールする。このネットワーク構成ではインターネットからの apt install 不可なので、インターネット接続可能なネットワークに一時的に接続しておく。
user@vpn1:~$ sudo apt install libreswan

インストールが終わったら、ネットワーク構成を検証用の構成に戻す。以下のように netplan 設定ファイルを編集する。
user@vpn1:~$ vi /etc/netplan/50-cloud-init.yaml

network:
    version: 2
    ethernets:
        ens160:
            addresses: [198.51.100.100/24]
            gateway4: 198.51.100.1
        ens192:
            addresses: [10.0.1.1/24]

編集したら適用する。
user@vpn1:~$ sudo netplan apply

カーネルパラメータを設定する。
user@vpn1:~$ sudo vi /etc/sysctl.conf

net.ipv4.ip_forward=1  #28行目のコメントを外す

上記カーネルパラメータを有効化する。
user@vpn1:~$ sudo sysctl -p /etc/sysctl.conf

IPsecの事前共有鍵を設定する。
user@vpn1:~$ sudo vi /etc/ipsec.d/linux-to-linux.secrets

: PSK "mypresharedkey"

一般ユーザで読めないようパーミッションを変えておく。
user@vpn1:~$ sudo chmod 600 /etc/ipsec.d/linux-to-linux.secrets

IPsecの接続設定を記述する。
user@vpn1:~$ sudo vi /etc/ipsec.d/linux-to-linux.conf

conn linux-to-linux
        authby=secret		# 共有鍵認証とする
        auto=add		# こちら側からはVPN接続を自動開始しない
        dpdaction=clear
        left=198.51.100.100	# 自ホストのIPアドレス
        leftsubnet=10.0.1.0/24	# 自分側のプライベートネットワーク
        right=203.0.113.100	# 対向側ホストのIPアドレス
        rightsubnet=10.0.2.0/24	# 対向側のプライベートネットワーク

デーモンを起動する。
user@vpn1:~$ sudo systemctl enable ipsec
user@vpn1:~$ sudo systemctl start ipsec

3. vpn2 の設定:

vpn1 と同様に、libreswan をインストールする。検証構成ではインターネットからの apt install 不可なのも vpn1 と同様である。一時的にインターネット接続可能なネットワークに接続しておく。
user@vpn2:~$ sudo apt install libreswan

インストールが終わったら、ネットワーク構成を検証用の構成に戻す。
user@vpn2:~$ vi /etc/netplan/50-cloud-init.yaml

network:
    version: 2
    ethernets:
        ens160:
            addresses: [203.0.113.100/24]
            gateway4: 203.0.113.1
        ens192:
            addresses: [10.0.2.1/24]

編集したら適用する。
user@vpn2:~$ sudo netplan apply

カーネルパラメータを設定する。
user@vpn2:~$ sudo vi /etc/sysctl.conf

net.ipv4.ip_forward=1  #28行目のコメントを外す

上記カーネルパラメータを有効化する。
user@vpn2:~$ sudo sysctl -p /etc/sysctl.conf

IPsec 事前共有鍵を設定する。
user@vpn2:~$ sudo vi /etc/ipsec.d/linux-to-linux.secrets

: PSK "mypresharedkey"

一般ユーザで読めないようパーミッションを変えておく。
user@vpn2:~$ sudo chmod 600 /etc/ipsec.d/linux-to-linux.secrets

IPsec の設定を記述する。right/left を vpn1 側とは入れ換える。
user@vpn2:~$ sudo vi /etc/ipsec.d/linux-to-linux.conf

conn linux-to-linux
        authby=secret
        auto=start		# こちら側から VPN 接続を自動開始する
        dpdaction=restart
        left=203.0.113.100
        leftsubnet=10.0.2.0/24
        right=198.51.100.100
        rightsubnet=10.0.1.0/24

デーモンを起動する。
user@vpn2:~$ sudo systemctl enable ipsec
user@vpn2:~$ sudo systemctl start ipsec

これで完成。

III. 確認

ipsec status コマンドで、接続状況を確認できる。

user@vpn1:~$ sudo ipsec status
(snip)
000 #3: "linux-to-linux":500 STATE_MAIN_R3 (sent MR3, ISAKMP SA established); EVENT_SA_REPLACE in 3326s; newest ISAKMP; lastdpd=-1s(seq in:0 out:0); idle; import:not set
000 #4: "linux-to-linux":500 STATE_QUICK_R2 (IPsec SA established); EVENT_SA_REPLACE in 28526s; newest IPSEC; eroute owner; isakmp#3; idle; import:not set
000 #4: "linux-to-linux" esp.a61da06f@203.0.113.100 esp.53ec235d@198.51.100.100 ref=0 refhim=0 Traffic: ESPin=0B ESPout=0B! ESPmax=4194303B

router1 で tcpdump を仕掛けておき、host1 から host2 あてに ping を打ってみる。
user@host1:~$ ping 10.0.2.100
user@router1:~$ sudo tcpdump -n -i ens192 not tcp port 22
15:27:34.103230 IP 198.51.100.100 > 203.0.113.100: ESP(spi=0xa61da06f,seq=0x1), length 132
15:27:34.103475 IP 203.0.113.100 > 198.51.100.100: ESP(spi=0x53ec235d,seq=0x1), length 132
15:27:35.131026 IP 198.51.100.100 > 203.0.113.100: ESP(spi=0xa61da06f,seq=0x2), length 132
15:27:35.131271 IP 203.0.113.100 > 198.51.100.100: ESP(spi=0x53ec235d,seq=0x2), length 132

ESPにカプセル化されてパケットが通過していることが確認できた。

※パケットがトンネルに入るか入らないかは、IP ルーティングではなく xfrm ポリシーによって決まっている。
ip xfrm policy コマンドで確認できる。

user@vpn1:~$ sudo ip xfrm policy
src 10.0.1.0/24 dst 10.0.2.0/24
        dir out priority 2344
        tmpl src 198.51.100.100 dst 203.0.113.100
                proto esp reqid 16389 mode tunnel
src 10.0.2.0/24 dst 10.0.1.0/24
        dir fwd priority 2344
        tmpl src 203.0.113.100 dst 198.51.100.100
                proto esp reqid 16389 mode tunnel
src 10.0.2.0/24 dst 10.0.1.0/24
        dir in priority 2344
        tmpl src 203.0.113.100 dst 198.51.100.100
                proto esp reqid 16389 mode tunnel
(snip)