月別アーカイブ: 2018年7月

X.509 証明書による IPsec 認証 (libreswan)

libreswan を使って、IPsec の X.509 証明書認証を検証する。

検証環境は前回記事と同様で、以下の図の通りである。

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

1. 証明書の用意

libreswan で証明書を扱う場合、NSS Tools の流儀に従って管理しなければならない。strongswan のようにファイルベースでの証明書管理はできないようなので、少し不便である。

vpn1 側の NSS Tools でオレオレ CA を作って、vpn1・vpn2 両方の証明書をここで作成することにする。あとでエクスポートして vpn2 へ持っていく。

1.1 初期化

最初に、NSS ライブラリの証明書ストアを両サーバで初期化する。Ubuntu の場合は標準のパスが /var/lib/ipsec/nss になっている。

user@vpn1:~$ sudo rm -f /var/lib/ipsec/nss/*.db
user@vpn1:~$ sudo ipsec initnss
user@vpn2:~$ sudo rm -f /var/lib/ipsec/nss/*.db
user@vpn2:~$ sudo ipsec initnss

1.2 CA鍵・証明書の作成

次に、vpn1 上で CA 鍵と証明書のセットを作成する。以下のように certutil コマンドを使用する。

user@vpn1:~$ sudo certutil -S -k rsa -n ca -s "CN=ca.example.com" -v 120 -t "CT,C,C" -x -d sql:/var/lib/ipsec/nss

A random seed must be generated that will be used in the
creation of your key. One of the easiest ways to create a
random seed is to use the timing of keystrokes on a keyboard.


To begin, type keys on the keyboard until this progress meter
is full. DO NOT USE THE AUTOREPEAT FUNCTION ON YOUR KEYBOARD!


Continue typing until the progress meter is full:

|***************************************************************|

Finished. Press enter to continue:

Generating key. This may take a few moments…

途中で、乱数の種としてキーボードからの入力を求められるので、適当にランダム入力する。

コマンドラインオプションについては、それぞれ以下のような意味がある。
-S は鍵・証明書の生成とデータベース登録を指示する。
-k は鍵のタイプを指定する。ここでは RSA を使う。
-n は、この鍵/証明書の NSS ストア内でのニックネームを指定する。
-s は、CA の CN (コモンネーム) を指定する。適当に決めてよい。
-v は、証明書の有効期限を指定する。(単位: 月)
-t "CT,C,C" は、証明書の信頼属性を指定する。
3つのフィールドの最初は SSL、2番目はメール、3番目はオブジェクト署名の用途を表す。
C は CA、T はクライアント認証証明書発行のための CA を表す。
IPsec の認証以外にこの CA を使うことがなければ、C,, だけでも良い。
-x は、自己署名証明書にすることを指定する。
-d sql:/var/lib/ipsec/nss は、証明書ストアのパスを表す。sql: を付けておかないと libreswan から利用できないので注意。

1.3 ホスト鍵・証明書の作成

次に、vpn1 のホスト鍵/証明書セットを作る。途中でキーボードからの入力を求められるのは先ほどと同様である。
user@vpn1:~$ sudo certutil -S -k rsa -c ca -n vpn1 -s "CN=vpn1.example.com" -v 60 -t "u,u,u" -d sql:/var/lib/ipsec/nss

コマンドラインオプションについては以下の通り。
-c で、先ほど作成した CA 鍵 (ニックネームが “ca”) での署名を指定する。
-n は、この鍵/証明書の NSS ストア内でのニックネームを指定する。あとでこれを利用する。
-s は、証明書の CN (コモンネーム) を指定する。ここでは vpn1 の FQDN にしておく。
-t "u,u,u" は、証明書の信頼属性を指定する。u はユーザ証明書を表す。

さらに、同じ作り方で vpn2 用の鍵・証明書を作る。同様に、ランダムキー入力が求められる。
user@vpn1:~$ sudo certutil -S -k rsa -c ca -n vpn2 -s "CN=vpn2.example.com" -v 60 -t "u,u,u" -d sql:/var/lib/ipsec/nss

ここまでで CA と vpn1 と vpn2 の 3セットの鍵・証明書が作成できた。

1.4 vpn2 用の鍵と証明書をエクスポート・インポート

vpn2 の鍵・証明書と、CA 証明書をファイルへエクスポートして、vpn2 へ持っていく。鍵を含むファイルは PEM 形式でエクスポートする方法が無いので、PKCS#12 形式でエクスポートする。
user@vpn1:~$ sudo certutil -L -n ca -a -d sql:/var/lib/ipsec/nss > ca.pem
user@vpn1:~$ sudo pk12util -n vpn2 -o vpn2.p12 -d sql:/var/lib/ipsec/nss
Enter password for PKCS12 file: password (適当に決めた PKCS#12 ファイル用パスワード)
Re-enter password: password (再度入力)
pk12util: PKCS12 EXPORT SUCCESSFUL
user@vpn1:~$ sudo chown user vpn2.p12

エクスポートしたファイルを vpn2 へネットワークコピーする。
user@vpn1:~$ scp ca.pem vpn2.p12 vpn2:
user@vpn1:~$ rm ca.pem vpn2.p12

vpn2 側でインポートする。
user@vpn2:~$ sudo certutil -A -a -i ca.pem -n ca -t 'CT,,' -d sql:/var/lib/ipsec/nss
user@vpn2:~$ sudo pk12util -i vpn2.p12 -d sql:/var/lib/ipsec/nss
Enter password for PKCS12 file: password (先ほど決めたパスワード)
pk12util: PKCS12 IMPORT SUCCESSFUL
user@vpn2:~$ rm ca.pem vpn2.p12

2. libreswan の設定

2.1 vpn1 側

vpn1 の libreswan 設定を編集する。

user@vpn1:~$ sudo vi /etc/ipsec.d/linux-to-linux.conf

conn linux-to-linux
	authby=rsasig
	auto=add
	dpdaction=clear
	leftcert=vpn1
	leftid="CN=vpn1.example.com"
	left=198.51.100.100
	leftsubnet=10.0.1.0/24
	rightid="CN=vpn2.example.com"
	right=203.0.113.100
	rightsubnet=10.0.2.0/24

leftcert に vpn1 を指定する。これは vpn1 証明書を作成した時のニックネームを指定している。

leftid、rightid は、それぞれの端点の Peer ID である。証明書の CN フィールドをダブルクォーテーションで囲って指定する。

2.2 vpn2 側

vpn2 の方も同様に設定する。

user@vpn2:~$ sudo vi /etc/ipsec.d/linux-to-linux.conf

conn linux-to-linux
	authby=rsasig
	auto=start
	dpdaction=restart
	leftcert=vpn2
	leftid="CN=vpn2.example.com"
	left=203.0.113.100
	leftsubnet=10.0.2.0/24
	rightid="CN=vpn1.example.com"
	right=198.51.100.100
	rightsubnet=10.0.1.0/24

2.3 再起動

デーモンを再起動する。
user@vpn1:~$ sudo systemctl restart ipsec
user@vpn2:~$ sudo systemctl restart ipsec

2.4 確認

接続状態を確認する。

user@vpn1:~$ sudo ip xfrm state
src 203.0.113.100 dst 198.51.100.100
        proto esp spi 0xc8f7a785 reqid 16389 mode tunnel
        replay-window 32 flag af-unspec
        auth-trunc hmac(sha1) 0xed85061c48c4fbc2dcce034191fb5a5a7c12d9e3 96
        enc cbc(aes) 0x7e39a613f56f3cb06b022561c0a8e65b
        anti-replay context: seq 0x0, oseq 0x0, bitmap 0x00000000
src 198.51.100.100 dst 203.0.113.100
        proto esp spi 0x90027aeb reqid 16389 mode tunnel
        replay-window 32 flag af-unspec
        auth-trunc hmac(sha1) 0xf3fa1c2609590b83e4c9c95b1ad38d80492f267a 96
        enc cbc(aes) 0x9e039bf741f7a4c7a9e1920d241fae65
        anti-replay context: seq 0x0, oseq 0x0, bitmap 0x00000000

参考:
HOWTO: Using NSS with libreswan
certutilによる証明書管理 [Fedora14]

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)