暗号化」カテゴリーアーカイブ

暗号化に関すること

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)

openssl s_client で SMTP STARTTLS と SMTP AUTH を動作確認する

概要: openssl s_client コマンドについて

telnet コマンドで HTTP や SMTP、POP の接続テストを行うことがあるが、同様に openssl の s_client サブコマンドで、TLS 接続の手動確認をすることが可能だ。例えば、HTTPS の確認は以下のように実行できる。

$ openssl s_client -connect www.example.com:443
(中略)
GET / HTTP/1.0(Enter)
Host: www.example.com(Enter2回押す)

さらに、最初は平文接続して、アプリケーションプロトコル上の STARTTLS コマンドで TLS 状態に入りたい場合もある。SMTP、IMAP、LDAP、FTP などの STARTTLS が相当する。これも s_client サブコマンドの -starttls オプションで実現できる。

mail.example.com サーバの TCPポート 587 (SMTP Submission) で SMTP STARTTLS が使える場合、以下のコマンドで接続が可能となる。-starttls オプションの引数に、smtp をとる。

$ openssl s_client -connect mail.example.com:587 -starttls smtp

ただし、SMTP セッションの中で RCPT TO: を大文字で打った瞬間に、RENEGOTIATING という表示とともに先へ進めなくなってしまうので注意が必要である。

$ openssl s_client -connect mail.example.com:587 -starttls smtp
CONNECTED(00000003)
depth=2 C = JP, O = "SECOM Trust Systems CO.,LTD.", OU = Security Communication RootCA2
verify return:1
depth=1 C = JP, L = Academe, O = National Institute of Informatics, CN = NII Open Domain CA - G4
verify return:1
depth=0 C = JP, L = Academe, O = Example, OU = Example Dept, CN = mail.example.com
verify return:1
(中略)
250 DSN
EHLO mail.example.com
250-mail.example.com
250-PIPELINING
250-SIZE 52428800
250-ETRN
250-AUTH PLAIN LOGIN
250-AUTH=PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
MAIL FROM: test@example.com
250 2.1.0 Ok
RCPT TO: test@example.jp
RENEGOTIATING
depth=2 C = JP, O = "SECOM Trust Systems CO.,LTD.", OU = Security Communication RootCA2
verify return:1
depth=1 C = JP, L = Academe, O = National Institute of Informatics, CN = NII Open Domain CA - G4
verify return:1
depth=0 C = JP, L = Academe, O = Example, OU = Example Dept, CN = mail.example.com
verify return:1

標準入力の一文字目が大文字の「R」になっていると、openssl s_client の TLS 再ネゴシエーションコマンドとして解釈されてしまうためである。

再ネゴシエーションを回避するには、先頭の r を小文字で打つか、openssl s_client のオプションとして -ign_eof または -quiet を追加する。

$ openssl s_client -quiet -connect mail.example.com:587 -starttls smtp

SMTP AUTH のテスト

上記の openssl s_client コマンドを用いて、SMTP のセッションを手打ちで再現テストする。さらに SMTP セッションの中で、SMTP AUTH のテストも組み込んでみる。

実行例1: AUTH PLAIN 認証

SMTP AUTH の PLAIN コマンドで必要な文字列は、「ユーザ名\0ユーザ名\0パスワード」(\0はヌル文字)という合成文字列を BASE64 エンコードしたものである。あらかじめ文字列を作っておく。

$ printf 'username\0username\0password' | base64
dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ=

さきほどのエンコード文字列を AUTH PLAIN の引数として与える。

$ openssl s_client -quiet -connect mail.example.com:587 -starttls smtp
depth=2 C = JP, O = "SECOM Trust Systems CO.,LTD.", OU = Security Communication RootCA2
verify return:1
depth=1 C = JP, L = Academe, O = National Institute of Informatics, CN = NII Open Domain CA - G4
verify return:1
depth=0 C = JP, L = Academe, O = Example, OU = Example Dept, CN = mail.example.com
verify return:1
250 DSN
EHLO mail.example.com
250-mail.example.com
250-PIPELINING
250-SIZE 10485760
250-ETRN
250-AUTH PLAIN LOGIN
250-AUTH=PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
AUTH PLAIN dXNlcm5hbWUAdXNlcm5hbWUAcGFzc3dvcmQ=
235 2.7.0 Authentication successful ←認証成功
MAIL FROM: test@example.com
250 2.1.0 Ok
RCPT TO: test@example.jp
250 2.1.5 Ok ←メールリレー成功
QUIT
221 2.0.0 Bye

実行例2: AUTH LOGIN 認証

AUTH LOGIN コマンドでは、ユーザ名とパスワードをそれぞれ BASE64 エンコードした文字列が必要になる。

$ printf 'username' | base64
dXNlcm5hbWU=
$ printf 'password' | base64
cGFzc3dvcmQ=

サーバからの 334 VXNlcm5hbWU6 に対してはユーザ名のエンコード文字列を、334 UGFzc3dvcmQ6 に対してはパスワードのエンコード文字列を入力する。

$ openssl s_client -quiet -connect mail.example.com:587 -starttls smtp
depth=2 C = JP, O = "SECOM Trust Systems CO.,LTD.", OU = Security Communication RootCA2
verify return:1
depth=1 C = JP, L = Academe, O = National Institute of Informatics, CN = NII Open Domain CA - G4
verify return:1
depth=0 C = JP, L = Academe, O = Example, OU = Example Dept, CN = mail.example.com
verify return:1
250 DSN
EHLO mail.example.com
250-mail.example.com
250-PIPELINING
250-SIZE 10485760
250-ETRN
250-AUTH PLAIN LOGIN
250-AUTH=PLAIN LOGIN
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
AUTH LOGIN
334 VXNlcm5hbWU6 ←"Username:" が BASE64 エンコードされている
dXNlcm5hbWU=
334 UGFzc3dvcmQ6 ←"Password:" が BASE64 エンコードされている
cGFzc3dvcmQ=
235 2.7.0 Authentication successful ←認証成功
MAIL FROM: test@example.com
250 2.1.0 Ok
RCPT TO: test@example.jp
250 2.1.5 Ok ←メールリレー成功
QUIT
221 2.0.0 Bye

参考URL:

Linux 同士の IPv6 IPsec 接続

個人的なことだが、自宅のフレッツ回線とプロバイダがIPv6ネイティブ方式 (IPoE) に対応したため、自宅内のノードに IPv6 グローバルアドレスを割り当てられるようになった。

これを利用して、自分で借りた「さくらの VPS」(こちらも IPv6 グローバルアドレスが付与される)と自宅内サーバの間に IPv6 IPsecトンネルを作成してみる。

前提環境:
・自宅サーバ: Raspbian 9.4 + strongswan
・VPS: CentOS 7.5 + strongswan
・認証方式: X.509 証明書

ネットワーク構成図

1. 証明書の作成

myserver1 上に CA のためのディレクトリと設定を準備する。
user@myserver1:~$ sudo mkdir /etc/ssl/CA
user@myserver1:~$ sudo mkdir /etc/ssl/newcerts
user@myserver1:~$ sudo sh -c "echo '01' > /etc/ssl/CA/serial"
user@myserver1:~$ sudo sh -c "echo '01' > /etc/ssl/CA/crlnumber"
user@myserver1:~$ sudo touch /etc/ssl/CA/index.txt
user@myserver1:~$ sudo vi /etc/ssl/openssl.cnf

/etc/ssl/openssl.cnfの抜粋:

dir		= /etc/ssl		# Where everything is kept
database	= $dir/CA/index.txt	# database index file.
certificate	= $dir/certs/ca1.crt 	# The CA certificate
serial		= $dir/CA/serial 		# The current serial number
crlnumber	= $dir/CA/crlnumber # the current crl number
                    # must be commented out to leave a V1 CRL
crl     = $dir/crl/crl.pem
private_key	= $dir/private/ca1.key

CA 鍵・証明書を作成する。
user@myserver1:~$ sudo openssl req -new -x509 -extensions v3_ca -keyout /etc/ssl/private/ca1.key -out /etc/ssl/certs/ca1.crt -days 3652
(snip)
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Aichi
Locality Name (eg, city) []:Nagoya
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Home
Organizational Unit Name (eg, section) []:CA
Common Name (e.g. server FQDN or YOUR name) []:ca1.home.example.com
Email Address []:

myserver1 の秘密鍵を作成する。
user@myserver1:~$ openssl genrsa -aes256 -out /etc/ssl/private/myserver1.key 2048
Enter pass phrase for myserver1.key:********
(後でパスワードを削除するので、パスワードはここでは適当に決める)
Verifying - Enter pass phrase for myserver1.key:********

秘密鍵ファイルのパスワードを削除しておく。
user@myserver1:~$ openssl rsa -in /etc/ssl/private/myserver1.key -out /etc/ssl/private/myserver1.key
Enter pass phrase for myserver1.key:********
writing RSA key

myserver1 の CSR を作成する。
user@myserver1:~$ openssl req -new -days 1826 -key /etc/ssl/private/myserver1.key -out /etc/ssl/cert/myserver1.csr
(snip)
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Aichi
Locality Name (eg, city) []:Nagoya
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Home
Organizational Unit Name (eg, section) []:Server
Common Name (e.g. server FQDN or YOUR name) []:myserver1.home.example.com
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

CA 鍵を使って CSR に署名する。
user@myserver1:~$ sudo openssl ca -in /etc/ssl/cert/myserver1.csr -config /etc/ssl/openssl.cnf
Enter pass phrase for /etc/ssl/private/ca.key:********
(snip)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
(snip)
Data Base Updated

CA 証明書とホスト証明書・鍵を strongswan 用に配置する。
user@myserver1:~$ sudo cp /etc/ssl/certs/ca1.crt /etc/ipsec.d/cacerts/
user@myserver1:~$ sudo cp /etc/ssl/newcerts/01.pem /etc/ipsec.d/certs/myserver1.crt
user@myserver1:~$ sudo cp /etc/ssl/private/myserver1.key /etc/ipsec.d/private/

myvps1 用の証明書も同様に作成する。
user@myserver1:~$ openssl genrsa -aes256 -out /etc/ssl/private/myvps1.key 2048
user@myserver1:~$ openssl rsa -in /etc/ssl/private/myvps1.key -out /etc/ssl/private/myvps1.key
user@myserver1:~$ openssl req -new -days 1826 -key /etc/ssl/private/myvps1.key -out /etc/ssl/cert/myvps1.csr
user@myserver1:~$ sudo openssl ca -in /etc/ssl/cert/myvps1.csr -config /etc/ssl/openssl.cnf

いま作成した myvps1 用のホスト証明書・鍵、CA証明書を myvps1 側にコピーする。
user@myserver1:~$ cp /etc/ssl/certs/ca1.crt .
user@myserver1:~$ cp /etc/ssl/newcerts/02.pem ./myvps1.crt
user@myserver1:~$ sudo cp /etc/ssl/private/myvps1.key .
user@myserver1:~$ sudo chown user myvps1.key
user@myserver1:~$ scp ca1.crt myvps1.crt myvps1.key myvps1:

myvps1 上で証明書・鍵を配置する。
[user@myvps1 ~]$ sudo cp ca1.crt /etc/strongswan/ipsec.d/cacerts/
[user@myvps1 ~]$ sudo cp myvps1.crt /etc/strongswan/ipsec.d/certs/
[user@myvps1 ~]$ sudo cp myvps1.key /etc/strongswan/ipsec.d/private/

作業用ファイルを削除する。
[user@myvps1 ~]$ rm ca1.crt myvps1.crt myvps1.key
user@myserver1:~$ rm ca1.crt myvps1.crt myvps1.key

2. VPS 側の IPsec 設定

strongswan をインストールする。
[user@myvps1 ~]$ sudo yum install epel-release
[user@myvps1 ~]$ sudo yum install strongswan

カーネルモジュールをロードする。
strongswan公式ドキュメントを参照して、必要なモジュールがロードされていない場合は手動でロードする。
[user@myvps1 ~]$ sudo modprobe xfrm6_tunnel

設定ファイルを編集する。
[user@myvps1 ~]$ vi /etc/strongswan/ipsec.conf

conn myhome-to-vps6
        authby=rsasig
        auto=add
        closeaction=clear
        dpdaction=clear
        leftid="C=JP, ST=Aichi, O=Home, OU=Server, CN=myvps1.example.com"
        leftsubnet=2001:db8:abcd:efab:cdef:abcd:efab:cdef/128
        leftcert=myvps1.crt
        right=%any
        rightid="C=JP, ST=Aichi, O=Home, OU=Server, CN=myserver1.home.example.com"
        rightsubnet=2001:db8:1234:5678::/64
        ike=aes256-sha512-modp8192!
        esp=aes256-sha512

[user@myvps1 ~]$ sudo vi /etc/strongswan/ipsec.secrets

: RSA myvps1.key

strongswanサービス起動
[user@myvps1 ~]$ sudo systemctl enable strongswan
[user@myvps1 ~]$ sudo systemctl start strongswan

3. 自宅内サーバ側の設定

strongswanをインストールする。こちらは Raspbian なので apt コマンドで。
user@myserver1:~$ sudo apt install strongswan

必要なカーネルモジュールをロードする。
user@myserver1:~$ sudo modprobe xfrm6_tunnel
user@myserver1:~$ sudo modprobe esp6

設定ファイルを編集する。
user@myserver1:~$ sudo vi /etc/ipsec.conf

conn myhome-to-vps6
        authby=rsasig
        auto=start
        closeaction=restart
        dpdaction=restart
        leftid="C=JP, ST=Aichi, O=Home, OU=Server, CN=myserver1.home.example.com"
        leftsubnet=2001:db8:1234:5678::/64
        leftcert=myserver1.crt
        right=2001:db8:abcd:efab:cdef:abcd:efab:cdef
        rightid="C=JP, ST=Aichi, O=Home, OU=Server, CN=myvps1.example.com"
        rightsubnet=2001:db8:abcd:efab:cdef:abcd:efab:cdef/128
        ike=aes256-sha512-modp8192!
        esp=aes256-sha512

user@myserver1:~$ sudo vi /etc/ipsec.secrets

: RSA myserver1.key

strongswanサービスを起動する。
user@myserver1:~$ sudo systemctl enable strongswan
user@myserver1:~$ sudo systemctl start strongswan

openswan vs strongswan vs libreswan

IPsec の実装として、openswan / strongswan / libreswan どれを使えばいいの?というお話。

どの実装もかつてのFreeS/WAN IPsecの末裔であって、似たような設定で動作するのだけど微妙に書式が違う、というやっかいなことになっている。

openswan:
もうメンテされていない古い実装。Ubuntu 18.04 LTS も CentOS 7 もパッケージを用意していないので、あえて使う理由がない。Debian 9.x (stretch) には残っているが…
libreswan:
openswanのフォークで、現状メンテされている。RedHat/CentOS 系や Fedora、Ubuntu にも標準パッケージで用意されている。Debian 9.x (stretch) にはパッケージなし。
strongswan:
FreeS/WAN から分岐した別実装。Debian/Ubuntu 系 OS、また Fedora では標準パッケージで用意されている。RedHat/CentOS 系の場合は epel リポジトリに入っている。

ということで openswan は使わないものとして、ざっくり RedHat 系は libreswan、Debian 系は strongswan を使えば良いのではと考える。

※RedHat Enterprise Linux の場合は libreswan を推奨しているようなので、これを使うべきと思われる。
RedHat セキュリティーガイド 4.6. Libreswan を使用した仮想プライベートネットワーク (VPN) のセキュリティー保護

重要: Libreswan および Linux カーネルが実装する IKE/IPsec VPN は、Red Hat Enterprise Linux 7 で使用することが推奨される唯一の VPN テクノロジーです。その他の VPN テクノロジーを使用するリスクを理解せずに使用しないでください。

参考:
IPsec for Linux – strongSwan vs Openswan vs Libreswan vs other(?)

HTTP/2 (ALPN) 対応の nginx を SRPM からビルド

※以下はCentOS 7.3までの古い内容です。CentOS 7.4 では OpenSSL 1.0.2 が標準となったため、以下の作業は不要です。

CentOS7.3 に含まれる標準パッケージの OpenSSL が 1.0.1 系なので、それをリンクしてビルドした nginx 公式のバイナリでは HTTP/2 (ALPN) が利用できない。そこで、openssl-1.0.2 系のソースツリーを使って nginx 公式の SRPM をリビルドすることとする。

ついでに、geoipモジュールとdav_extモジュールも使いたいので、追加した上でビルドする。

1. コンパイルに必要なパッケージをインストール

$ sudo yum install expat-devel pcre-devel zlib-devel GeoIP-devel

2. nginxの公式レポジトリを追加

レポジトリファイルを作成する。
$ sudo vi /etc/yum.repos.d/nginx.repo

[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1

[nginx-source]
name=nginx repo - Source
baseurl=http://nginx.org/packages/centos/$releasever/SRPMS/
gpgcheck=0
enabled=1

3. ダウンロード

SRPMをダウンロード・インストールする。
$ sudo yum install -y yum-utils
$ yumdownloader --source nginx
$ rpm -ivh --nosignature nginx-1.12.1-1.el7.ngx.src.rpm

今後yum updateで勝手にアップデートされないように、nginxレポジトリを無効化しておく。
$ sudo yum-config-manager --disable nginx
$ sudo yum-config-manager --disable nginx-source

OpenSSL 1.0.2 のソースをダウンロードして展開する。
$ cd /tmp
$ wget https://www.openssl.org/source/openssl-1.0.2l.tar.gz
$ tar xzf openssl-1.0.2l.tar.gz
$ cd ~

OpenSSL のビルドはしなくてよい。

4. カスタマイズ

SRPM の spec ファイルを編集する。
$ vi rpmbuild/SPECS/nginx.spec

青字の部分は追加、赤字で打消し線のところは削除する。

#
%define nginx_home %{_localstatedir}/cache/nginx
%define nginx_user nginx
%define nginx_group nginx
%define nginx_loggroup adm

# distribution specific definitions
%define use_systemd (0%{?fedora} && 0%{?fedora} >= 18) || (0%{?rhel} && 0%{?rhel
} >= 7) || (0%{?suse_version} == 1315)

%if 0%{?rhel} == 5
%define _group System Environment/Daemons
Requires(pre): shadow-utils
Requires: initscripts >= 8.36
Requires(post): chkconfig
Requires: openssl
BuildRequires: openssl-devel
%endif

%if 0%{?rhel} == 6
%define _group System Environment/Daemons
Requires(pre): shadow-utils
Requires: initscripts >= 8.36
Requires(post): chkconfig
Requires: openssl >= 1.0.1
BuildRequires: openssl-devel >= 1.0.1
%endif

%if 0%{?rhel} == 7
%define _group System Environment/Daemons
%define epoch 1
Epoch: %{epoch}
Requires(pre): shadow-utils
Requires: systemd
Requires: openssl >= 1.0.1
BuildRequires: systemd
BuildRequires: openssl-devel >= 1.0.1
BuildRequires: GeoIP-devel
BuildRequires: expat-devel
%endif

%if 0%{?suse_version} == 1315
%define _group Productivity/Networking/Web/Servers
%define nginx_loggroup trusted
Requires(pre): shadow
Requires: systemd
BuildRequires: libopenssl-devel
BuildRequires: systemd
%endif

# end of distribution specific definitions

%define main_version 1.12.1
%define main_release 1%{?dist}.ngx

%define bdir %{_builddir}/%{name}-%{main_version}

%define WITH_CC_OPT $(echo %{optflags} $(pcre-config --cflags)) -fPIC
%define WITH_LD_OPT -Wl,-z,relro -Wl,-z,now -pie
%define BASE_CONFIGURE_ARGS $(echo "--prefix=%{_sysconfdir}/nginx --sbin-path=%{
_sbindir}/nginx --modules-path=%{_libdir}/nginx/modules --conf-path=%{_sysconfdi
r}/nginx/nginx.conf --error-log-path=%{_localstatedir}/log/nginx/error.log --htt
p-log-path=%{_localstatedir}/log/nginx/access.log --pid-path=%{_localstatedir}/r
un/nginx.pid --lock-path=%{_localstatedir}/run/nginx.lock --http-client-body-tem
p-path=%{_localstatedir}/cache/nginx/client_temp --http-proxy-temp-path=%{_local
statedir}/cache/nginx/proxy_temp --http-fastcgi-temp-path=%{_localstatedir}/cach
e/nginx/fastcgi_temp --http-uwsgi-temp-path=%{_localstatedir}/cache/nginx/uwsgi_
temp --http-scgi-temp-path=%{_localstatedir}/cache/nginx/scgi_temp --user=%{ngin
x_user} --group=%{nginx_group} --with-compat --with-file-aio --with-threads --wi
th-http_addition_module --with-http_auth_request_module --with-http_dav_module -
-with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module -
-with-http_mp4_module --with-http_random_index_module --with-http_realip_module 
--with-http_secure_link_module --with-http_slice_module --with-http_ssl_module -
-with-http_stub_status_module --with-http_sub_module --with-http_v2_module --wit
h-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-s
tream_ssl_module --with-stream_ssl_preread_module --with-http_geoip_module --add
-module=./nginx-dav-ext-module --with-openssl=/tmp/openssl-1.0.2l")

Summary: High performance web server
Name: nginx
Version: %{main_version}
Release: %{main_release}
Vendor: Nginx, Inc.
URL: http://nginx.org/
Group: %{_group}

Source0: http://nginx.org/download/%{name}-%{version}.tar.gz
Source1: logrotate
Source2: nginx.init.in
Source3: nginx.sysconf
Source4: nginx.conf
Source5: nginx.vh.default.conf
Source7: nginx-debug.sysconf
Source8: nginx.service
Source9: nginx.upgrade.sh
Source10: nginx.suse.logrotate
Source11: nginx-debug.service
Source12: COPYRIGHT
Source13: nginx.check-reload.sh

License: 2-clause BSD-like license

BuildRoot: %{_tmppath}/%{name}-%{main_version}-%{main_release}-root
BuildRequires: zlib-devel
BuildRequires: pcre-devel

Provides: webserver

%description
nginx [engine x] is an HTTP and reverse proxy server, as well as
a mail proxy server.

%if 0%{?suse_version} == 1315
%debug_package
%endif

%prep
%setup -q
cp %{SOURCE2} .
sed -e 's|%%DEFAULTSTART%%|2 3 4 5|g' -e 's|%%DEFAULTSTOP%%|0 1 6|g' \
    -e 's|%%PROVIDES%%|nginx|g' < %{SOURCE2} > nginx.init
sed -e 's|%%DEFAULTSTART%%||g' -e 's|%%DEFAULTSTOP%%|0 1 2 3 4 5 6|g' \
    -e 's|%%PROVIDES%%|nginx-debug|g' < %{SOURCE2} > nginx-debug.init
git clone https://github.com/arut/nginx-dav-ext-module.git

%build
./configure %{BASE_CONFIGURE_ARGS} \
    --with-cc-opt="%{WITH_CC_OPT}" \
    --with-ld-opt="%{WITH_LD_OPT}" \
    --with-debug --with-openssl-opt="-fPIC"
make %{?_smp_mflags}
%{__mv} %{bdir}/objs/nginx \
    %{bdir}/objs/nginx-debug
./configure %{BASE_CONFIGURE_ARGS} \
    --with-cc-opt="%{WITH_CC_OPT}" \
    --with-ld-opt="%{WITH_LD_OPT}" --with-openssl-opt="-fPIC"
make %{?_smp_mflags}

(以下略)

5. ビルド、インストール

ビルドを実行する。
$ rpmbuild -ba rpmbuild/SPECS/nginx.spec

出来上がったら、RPMをインストールする。
$ sudo yum localinstall rpmbuild/RPMS/x86_64/nginx-1.12.1-1.el7.centos.ngx.x86_64.rpm

展開した openssl ソースを掃除しておく。
$ rm -r /tmp/openssl-1.0.2l

6. 起動

デーモンを起動する。
$ sudo systemctl enable nginx
$ sudo systemctl start nginx

参考URL:
HTTP/2対応nginxのrpmパッケージ作成とインストール

~/.ssh/known_hosts の記述

どうでもいい小ネタ。

仮想サーバをコピーして増やした場合、コピーサーバ群で同一の ssh ホスト鍵を使うことになる。それらに ssh ログインを繰り返すと、ssh クライアントの known_hosts ファイルには同一のホスト公開鍵が大量に残ることになる。
まあ気にしなければ良いのだが、ワイルドカードが書けるので集約することもできる。

~/.ssh/known_hosts:

server1.example.com ssh-rsa ホスト公開鍵文字列
server2.example.com ssh-rsa ホスト公開鍵文字列
server3.example.com ssh-rsa ホスト公開鍵文字列
192.168.100.1 ssh-rsa ホスト公開鍵文字列
192.168.100.2 ssh-rsa ホスト公開鍵文字列
192.168.100.3 ssh-rsa ホスト公開鍵文字列
...

上記を、以下のようにまとめられる。

*.example.com ssh-rsa ホスト公開鍵文字列
192.168.100.* ssh-rsa ホスト公開鍵文字列

これをやっておくと、サーバをさらにコピーして増やした時に改めて
Are you sure you want to continue connecting (yes/no)?
みたいなことを聞かれなくて済む。

外部からVPSへのL2TP/IPsecとVPS-自宅間IPsecの組み合わせ

前回記事のリモートアクセスVPN (L2TP/IPsec) と、さらにその前の記事で構築済みのVPS-自宅間VPN (IPsec) を組み合わせて利用する。

これにより、動的IPアドレスの自宅へ、外部からいつでもログイン可能となる。

前提:
・サーバはすべて Ubuntu 14.04 LTS である。
・IPsec実装は strongswan を利用し、サーバ間はRSA証明書認証を行う。
・外部端末-VPS間はL2TP/IPsecとし、認証はIPsec事前共有鍵+L2TPのMSCHAPv2とする。

ネットワーク構成は以下の通り。
06

すでに記事としては前回前々回で記述している。
今回のポイントは、VPS への L2TP アクセスで割り振られる PPP IPアドレスを、VPS-自宅間IPsecのトンネルに入るよう設定することである。

1. myvps1 と myserver1 の /etc/sysctl.conf

$ sudo vi /etc/sysctl.conf

net.ipv4.ip_forward=1  #28行目のコメントを外す
# 以下、追記する
net.ipv4.conf.default.send_redirects=0
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.eth0.accept_redirects=0
net.ipv4.conf.eth0.send_redirects=0
net.ipv4.conf.lo.accept_redirects=0
net.ipv4.conf.lo.send_redirects=0
net.ipv6.conf.eth0.accept_redirects=0
net.ipv6.conf.lo.accept_redirects=0
vpn1:~$ sudo sysctl -p /etc/sysctl.conf

2. myserver1上にCAを作成し、myserver1用とmyvps1用の証明書・秘密鍵の組を作成する

前々回記事を参照。

myserver1 には
/etc/ipsec.d/cacerts/ca1.crt (CA証明書)
/etc/ipsec.d/certs/myserver1.crt (サーバ証明書)
/etc/ipsec.d/private/myserver1.key (サーバ秘密鍵)

myvps1 には
/etc/ipsec.d/cacerts/ca1.crt (CA証明書)
/etc/ipsec.d/certs/myvps1.crt (サーバ証明書)
/etc/ipsec.d/private/myvps1.key (サーバ秘密鍵)

以上のファイルを配置する。

3. strongswan の設定 (myserver1側)

/etc/ipsec.conf:

config setup セクションの後に以下の内容を追加する。rightsubnet に L2TP クライアントの IP アドレス領域を追加しておく。

conn myhome-to-vps
	authby=rsasig
	auto=start
	closeaction=restart
	dpdaction=restart
	left=192.168.100.240
	leftsubnet=192.168.100.0/24
	leftcert=myserver1.crt
	right=203.0.113.180
	rightsubnet=203.0.113.180/32,172.16.1.0/24 # ←L2TPのクライアントアドレスを追加
	rightid="C=JP, ST=Aichi, O=Home, OU=Server, CN=myvps1.vpsnet.example.jp"

※strongswan だと leftsubnet / rightsubnet に複数のサブネットが記述できるが、openswan で複数サブネットの場合は leftsubnets / rightsubnets と書く必要がある。

/etc/ipsec.secrets:

: RSA myserver1.key

4. strongswan の設定 (myvps1側)

/etc/ipsec.conf:

config setup セクションの後に、以下の2つの記述を追加する。
myhome-to-vps の方の leftsubnet には、L2TP クライアントアドレス領域を追加しておく。

conn myhome-to-vps
	authby=rsasig
	auto=add
	closeaction=clear
	dpdaction=clear
	left=203.0.113.180
	leftsubnet=203.0.113.180/32,172.16.1.0/24 # ←L2TPのクライアントアドレスを追加
	leftcert=myvps1.crt
	right=%any
	rightsubnet=192.168.100.0/24
	rightid="C=JP, ST=Aichi, O=Home, OU=Server, CN=myserver1.domain.local"

conn L2TP-PSK
        authby=secret
        auto=add
        closeaction=clear
        dpdaction=clear
        type=transport
        rekey=no
        left=203.0.113.180
        leftprotoport=17/1701
        right=%any
        rightprotoport=17/%any

/etc/ipsec.secrets:
自宅IPsec用のRSA秘密鍵と、L2TP/IPsec用の事前共有鍵文字列を両方記述する。

: RSA myvps1.key
: PSK "mypresharedkey"

5. xl2tpd の設定 (myvps1側)

/etc/xl2tpd/xl2tpd.conf:

[global]
port = 1701

[lns default]
ip range = 172.16.1.11-172.16.1.30
local ip = 172.16.1.254
length bit = yes
require chap = yes
refuse pap = yes
require authentication = yes
name = myvps1.vpsnet.example.jp
ppp debug = no
pppoptfile = /etc/ppp/xl2tpd-options

/etc/ppp/xl2tpd-options:

ipcp-accept-local
ipcp-accept-remote
ms-dns 172.16.1.254  (myvps1にキャッシュDNSサーバが立っている前提)
noccp
auth
crtscts
idle 1800
mtu 1300
mru 1300
nodefaultroute
lock
connect-delay 5000
refuse-pap
refuse-chap
refuse-mschap
require-mschap-v2

/etc/ppp/chap-secrets:

username	*	"l2tppassworddesu"	*

一般ユーザで読めない権限にしておくこと。

6. サービス起動と確認

myserver1:~$ sudo service strongswan restart
myvps1:~$ sudo service strongswan restart
myvps1:~$ sudo service xl2tpd restart

iOS/Android 端末からの L2TP/IPsec 接続 (strongswan+xl2tpd)

iOSやAndroid端末、あるいは Windows PC などからもリモートアクセスできるよう、VPS マシンに VPN 接続を設定しておく。

PPTPについては既に脆弱性が発見されており、OSによってはサポート対象外になっているため、VPN プロトコルとしては L2TP/IPsec を使う。

前提:
対象サーバはVPSのCentOS 7。

IPsec 実装としては strongswan、L2TP 実装としては xl2tpd を利用する。

ネットワーク図は以下の通り。
05

1. インストール

[user@myvps1 ~]$ sudo apt-get install strongswan xl2tpd

カーネルパラメータをパケット転送できるよう設定する。
[user@myvps1 ~]$ sudo vi /etc/sysctl.conf

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

[user@myvps1 ~]$ sudo sysctl -p /etc/sysctl.conf

ファイアウォール(ufw)を閉じている場合は、ESP、UDP 500・4500 を空けておくこと。

2. ipsec.confの設定

[user@myvps1 ~]$ sudo vi /etc/strongswan/ipsec.conf

以下の内容を追記する。

conn L2TP-PSK
        authby=secret
        auto=add
        closeaction=clear
        dpdaction=clear
        type=transport
        rekey=no
        left=203.0.113.180
        leftprotoport=17/1701
        right=%any
        rightprotoport=17/%any

IPsec はトランスポートモードを使用する。ペイロードに L2TP を通すので、ルーティングはそちらに任せる。

再接続等はクライアント側に任せたいため、こちらからは何もしない設定である。接続が切れた場合はセッションをクリアする。

3. IPsecの事前共有鍵の設定

[user@myvps1 ~]$ sudo vi /etc/strongswan/ipsec.secrets

事前共有鍵を平文で記述する。

: PSK "mypresharedkey"

4. xl2tpd.conf の設定

[user@myvps1 ~]$ sudo vi /etc/xl2tpd/xl2tpd.conf

以下の設定を追記する。

[global]
port = 1701

[lns default]
ip range = 172.16.1.11-172.16.1.30
local ip = 172.16.1.254
length bit = yes
require chap = yes
refuse pap = yes
require authentication = yes
name = myvps1.vpsnet.example.jp
ppp debug = no
pppoptfile = /etc/ppp/xl2tpd-options

接続クライアント側には、172.16.1.11 から 30 の間でアドレスが割り振られる。

5. xl2tpd-options の設定

[user@myvps1 ~]$ sudo vi /etc/ppp/xl2tpd-options

L2TPの設定を記述する。

ipcp-accept-local
ipcp-accept-remote
ms-dns 172.16.1.254  (myvps1:172.16.1.254にキャッシュDNSサーバが立っている前提)
noccp
auth
crtscts
idle 1800
mtu 1300
mru 1300
nodefaultroute
lock
connect-delay 5000
refuse-pap
refuse-chap
refuse-mschap
require-mschap-v2

6. chap-secrets の設定

[user@myvps1 ~]$ sudo vi /etc/ppp/chap-secrets

username	*	"l2tppassworddesu"	*

一般ユーザで読めない権限にしておく。

7. デーモンの起動

[user@myvps1 ~]$ sudo service strongswan restart
[user@myvps1 ~]$ sudo service xl2tpd restart

この状態で、iPhoneなどから接続してみる。
iPhoneであれば「設定」→「VPN」→「VPN構成を追加…」で「L2TP」を選択して設定を追加する。
「サーバ」欄にはVPSのホスト名(IPアドレス)、「アカウント」にはL2TPユーザ名、「パスワード」にはL2TPパスワード、「シークレット」にはIPsec事前共有鍵を設定する。

クライアント機器が公衆無線LANなどのプライベートネットワーク中に居る状態では、自動的に NAT-Traversal が利用される。ESP だけでなく、UDP 4500番ポートについてファイアウォールが空いている必要がある。

自宅-VPS間でIPsec (strongswan X.509証明書認証)

前の記事と同じ構成で、認証に証明書を導入してみた。

ネットワーク構成は前の記事と同じで、以下の図のようになる。

前提:
・VPS の OS は CentOS 7.5、自宅側サーバは Raspbian 9.4 である。
・IPsec 実装には strongswan を利用する。
・証明書作成にはOpenSSLを使い、PEM ファイルの形で管理する。
・自宅側のIPsec端点 (ホスト名:myserver1) と同一のサーバに CA を作成しておき、この CA 上で両側の端点用の証明書を発行する。

1. strongswan インストール

user@myserver1:~$ sudo apt-get install strongswan
[user@myvps1 ~]$ sudo yum install epel-release
[user@myvps1 ~]$ sudo yum install strongswan

カーネルパラメータは前々回の記事と同一の設定をしておく。

自宅内サーバ側の設定。
user@myserver1:~$ sudo vi /etc/sysctl.conf

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

user@myserver1:~$ sudo sysctl -p /etc/sysctl.conf

VPS の方にも同じ設定を実施する。
[user@myvps1 ~]$ sudo vi /etc/sysctl.conf

net.ipv4.ip_forward=1  #追記する

[user@myvps1 ~]$ sudo sysctl -p /etc/sysctl.conf

ファイアウォール(ufw)を閉じている場合は、UDP 500 と 4500 を空けておくこと。

2. 証明書の作成

myserver1 上に CA のためのディレクトリと設定を準備する。
user@myserver1:~$ sudo mkdir /etc/ssl/CA
user@myserver1:~$ sudo mkdir /etc/ssl/newcerts
user@myserver1:~$ sudo sh -c "echo '01' > /etc/ssl/CA/serial"
user@myserver1:~$ sudo sh -c "echo '01' > /etc/ssl/CA/crlnumber"
user@myserver1:~$ sudo touch /etc/ssl/CA/index.txt
user@myserver1:~$ sudo vi /etc/ssl/openssl.cnf

/etc/ssl/openssl.cnfの抜粋:

dir		= /etc/ssl		# Where everything is kept
database	= $dir/CA/index.txt	# database index file.
certificate	= $dir/certs/ca1.crt 	# The CA certificate
serial		= $dir/CA/serial 		# The current serial number
crlnumber	= $dir/CA/crlnumber # the current crl number
                    # must be commented out to leave a V1 CRL
crl     = $dir/crl/crl.pem
private_key	= $dir/private/ca1.key

CA 鍵・証明書を作成する。
user@myserver1:~$ sudo openssl req -new -x509 -extensions v3_ca -keyout /etc/ssl/private/ca1.key -out /etc/ssl/certs/ca1.crt -days 3652
(snip)
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Aichi
Locality Name (eg, city) []:Nagoya
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Home
Organizational Unit Name (eg, section) []:CA
Common Name (e.g. server FQDN or YOUR name) []:ca1.home.example.com
Email Address []:

myserver1 の秘密鍵を作成する。
user@myserver1:~$ openssl genrsa -aes256 -out /etc/ssl/private/myserver1.key 2048
Enter pass phrase for myserver1.key:********
(後でパスワードを削除するので、パスワードはここでは適当に決める)
Verifying - Enter pass phrase for myserver1.key:********

秘密鍵ファイルのパスワードを削除しておく。
user@myserver1:~$ openssl rsa -in /etc/ssl/private/myserver1.key -out /etc/ssl/private/myserver1.key
Enter pass phrase for myserver1.key:********
writing RSA key

myserver1 の CSR を作成する。
user@myserver1:~$ openssl req -new -days 1826 -key /etc/ssl/private/myserver1.key -out /etc/ssl/cert/myserver1.csr
(snip)
Country Name (2 letter code) [AU]:JP
State or Province Name (full name) [Some-State]:Aichi
Locality Name (eg, city) []:Nagoya
Organization Name (eg, company) [Internet Widgits Pty Ltd]:Home
Organizational Unit Name (eg, section) []:Server
Common Name (e.g. server FQDN or YOUR name) []:myserver1.home.example.com
Email Address []:
Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:

CA 鍵を使って CSR に署名する。
user@myserver1:~$ sudo openssl ca -in /etc/ssl/cert/myserver1.csr -config /etc/ssl/openssl.cnf
Enter pass phrase for /etc/ssl/private/ca.key:********
(snip)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
(snip)
Data Base Updated

CA 証明書とホスト証明書・鍵を strongswan 用に配置する。
user@myserver1:~$ sudo cp /etc/ssl/certs/ca1.crt /etc/ipsec.d/cacerts/
user@myserver1:~$ sudo cp /etc/ssl/newcerts/01.pem /etc/ipsec.d/certs/myserver1.crt
user@myserver1:~$ sudo cp /etc/ssl/private/myserver1.key /etc/ipsec.d/private/

myvps1 用の証明書も同様に作成する。
user@myserver1:~$ openssl genrsa -aes256 -out /etc/ssl/private/myvps1.key 2048
user@myserver1:~$ openssl rsa -in /etc/ssl/private/myvps1.key -out /etc/ssl/private/myvps1.key
user@myserver1:~$ openssl req -new -days 1826 -key /etc/ssl/private/myvps1.key -out /etc/ssl/cert/myvps1.csr
user@myserver1:~$ sudo openssl ca -in /etc/ssl/cert/myvps1.csr -config /etc/ssl/openssl.cnf

いま作成した myvps1 用のホスト証明書・鍵、CA証明書を myvps1 側にコピーする。
user@myserver1:~$ cp /etc/ssl/certs/ca1.crt .
user@myserver1:~$ cp /etc/ssl/newcerts/02.pem ./myvps1.crt
user@myserver1:~$ sudo cp /etc/ssl/private/myvps1.key .
user@myserver1:~$ sudo chown user myvps1.key
user@myserver1:~$ scp ca1.crt myvps1.crt myvps1.key myvps1:

myvps1 上で証明書・鍵を配置する。
[user@myvps1 ~]$ sudo cp ca1.crt /etc/strongswan/ipsec.d/cacerts/
[user@myvps1 ~]$ sudo cp myvps1.crt /etc/strongswan/ipsec.d/certs/
[user@myvps1 ~]$ sudo cp myvps1.key /etc/strongswan/ipsec.d/private/

作業用ファイルを削除する。
[user@myvps1 ~]$ rm ca1.crt myvps1.crt myvps1.key
user@myserver1:~$ rm ca1.crt myvps1.crt myvps1.key

3. strongswan の設定 (myserver1側)

設定ファイルを編集する。
user@myserver1:~$ sudo vi /etc/ipsec.conf

config setup セクションの後に以下の内容を追加する。

conn myhome-to-vps
	authby=rsasig
	auto=start
	closeaction=restart
	dpdaction=restart
	left=192.168.100.240
	leftsubnet=192.168.100.0/24
	leftcert=myserver1.crt
	right=203.0.113.180
	rightsubnet=203.0.113.180/32
	rightid="C=JP, ST=Aichi, O=Home, OU=Server, CN=myvps1.vpsnet.example.com"

こちら側から接続を開始するため、auto=start を記述する。また接続が切れたときにはこちら側から再接続を実行するため、closeaction=restart と dpdaction=restart を記述する。

パスワードファイルには秘密鍵のファイル名を記述する。
user@myserver1:~$ sudo vi /etc/ipsec.secrets

: RSA myserver1.key

4. strongswan の設定 (myvps1側)

こちらも設定ファイルを編集する。
[user@myvps1 ~]$ sudo vi /etc/strongswan/ipsec.conf

以下の内容を追加する。

conn myhome-to-vps
	authby=rsasig
	auto=add
	closeaction=clear
	dpdaction=clear
	left=203.0.113.180
	leftsubnet=203.0.113.180/32
	leftcert=myvps1.crt
	right=%any
	rightsubnet=192.168.100.0/24
	rightid="C=JP, ST=Aichi, O=Home, OU=Server, CN=myserver1.home.example.com"

パスワードファイルには秘密鍵のファイル名を記述する。
[user@myvps1 ~]$ sudo vi /etc/strongswan/ipsec.secrets

: RSA myvps1.key

5. サービス起動と確認

サービスを起動する。

user@myserver1:~$ sudo service strongswan start
[user@myvps1 ~]$ sudo service strongswan start

接続できたかどうか、ip xfrm state コマンドで確認する。

user@myserver1:~$ sudo ip xfrm state
src 192.168.100.240 dst 203.0.113.180
	proto esp spi 0xbaef12dc reqid 1 mode tunnel
	replay-window 32 flag af-unspec
	auth-trunc hmac(sha1) 0x6bde34f03f729b5a3c1d93c112ea6a40bf312742 96
	enc cbc(aes) 0x4f2fa3876e41e825be32a4e710a5f193
	encap type espinudp sport 4500 dport 4500 addr 0.0.0.0
src 203.0.113.180 dst 192.168.100.240
	proto esp spi 0xbb4b32e0 reqid 1 mode tunnel
	replay-window 32 flag af-unspec
	auth-trunc hmac(sha1) 0x44b52fad2bf912e10b61706add1337f823ec344e 96
	enc cbc(aes) 0xaf32405118ac467efb02f5f76e59aad1
	encap type espinudp sport 4500 dport 4500 addr 0.0.0.0