ArchLinuxでL2TP/IPsec VPN(Server/Client)のセットアップと注意点まとめ(2017年版)

Raspberry Pi に VPN を導入してからしばらく経ってますが、未だに Arch Linux をクライアントにして運用できない状態だったのでサーバー側デーモンを変更するついでに挑戦し直してみました。
今回はサーバー、クライアントともに libreswan を使用して、サーバー側を NAT の中に設置しています。
VPS など、非 NAT 環境へ配置する場合と若干設定が異なるため、以下の設定を流用する際はご注意ください

サーバー側の設定

$ yaourt -S libreswan xl2tpd
conn L2TP-NAT
	rightsubnet=0.0.0.0/0
	also=L2TP-noNAT

conn L2TP-noNAT
	type=transport
	authby=secret
	auto=add
	pfs=no
	rekey=no
	keyingtries=3
	ikelifetime=8h
	lifetime=1h
	left=%defaultroute
	leftprotoport=17/1701
	right=%any
	rightprotoport=17/%any
	# for iOS
	dpddelay=30
	dpdtimeout=120
	dpdaction=clear

IPsec の事前共有鍵を記述します。

: PSK "your preshared-key"
# chmod 600 /etc/ipsec.d/default.secrets

xl2tpd の設定を記述します。
local ip にはサーバーの IP アドレスを、ip range には VPN クライアントへの IP アドレス割当範囲をそれぞれ設定しておく。

[global]
auth file = /etc/ppp/chap-secrets

[lns default]
ip range = 192.168.0.102-192.168.0.149
local ip = 192.168.0.101
require authentication = yes
require chap = yes
refuse pap = yes
length bit = yes
ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd
name = LinuxVPN

pppd 起動時に渡されるオプションを記述します。
mtu、mru、ms-dns は環境に合わせ適宜修正してください。

refuse-pap
refuse-chap
refuse-mschap
require-mschap-v2

auth
nodefaultroute
proxyarp

idle 1800
mtu 1420
mru 1420

ms-dns 192.168.0.1

VPN のアカウント情報を記述する。
ここで設定した値が接続ユーザID、パスワードになります。

# Secrets for authentication using CHAP
# client	server	secret			IP addresses
hoge		*		hogefuga		*

各デーモンを起動させる。

# systemctl enable ipsec xl2tpd
# systemctl start ipsec xl2tpd

ファイアウォールルールを設定する

# iptables -A FORWARD -i ppp+ -j ACCEPT
# iptables -A FORWARD -o ppp+ -j ACCEPT
# iptables -A INPUT -p udp --dport 500 -j ACCEPT
# iptables -A INPUT -p udp --dport 1701 -j ACCEPT
# iptables -A INPUT -p udp --dport 4500 -j ACCEPT

クライアント側の設定

$ yaourt -S libreswan xl2tpd

right にはサーバーのグローバルアドレスを、rightid にはサーバーのローカルアドレスを記述しておきます。

conn L2TP-PSK
	type=transport
	authby=secret
	auto=add
	keyingtries=3
	ikelifetime=8h
	keylife=1h
	left=%defaultroute
	leftprotoport=17/1701
	right=xxx.xxx.yyy.yyy
	rightid=192.168.0.101
	rightprotoport=17/1701
%any xxx.xxx.yyy.yyy : PSK "your preshared-key"
[lac vpn-connection]
lns = xxx.xxx.yyy.yyy
length bit = yes
ppp debug = yes
pppoptfile = /etc/ppp/options.l2tpd

例によって pppd に渡すオプションを記述します。
ここで、require-mschap-v2 は noauth よりも上に記述してください。(詳しくは後に説明します)
なお、サンプル*1の設定ファイルなどに記述されている refuse-eap に関しては、Windows Server との相互運用性を保つためのものであるため今回は省いています。

require-mschap-v2

noauth
defaultroute
usepeerdns

name hoge
password hogefuga

接続に必要なデーモンを起動しておく。
毎度のことながら、認証情報を記述したファイルのパーミッション修正を忘れないようにしておきましょう。

# chmod 600 /etc/ipsec.d/default.secrets
# chmod 600 /etc/ppp/options.l2tpd
# mkdir /var/run/xl2tpd
# touch /var/run/xl2tpd/l2tp-control

# systemctl enable ipsec xl2tpd
# systemctl start ipsec xl2tpd

接続する

# ipsec auto --up L2TP-PSK
# xl2tpd-control connect vpn-connection
pppd[10138]: Plugin pppol2tp.so loaded.
pppd[10138]: pppd 2.4.7 started by root, uid 0
pppd[10138]: Using interface ppp0
pppd[10138]: Connect: ppp0 <-->
pppd[10138]: CHAP authentication succeeded: Access granted
pppd[10138]: CHAP authentication succeeded
pppd[10138]: local  IP address 192.168.0.102
pppd[10138]: remote IP address 192.168.0.101
pppd[10138]: primary   DNS address 192.168.0.1
pppd[10138]: secondary DNS address 192.168.0.1

こんな感じのログが出て ppp0 が生えますが、このままだとパケットが流れないので経路を追加してあげます。

# ip route add <server global ip> via <local gateway ip> dev <interface name>
# ip route add default via <ppp0 interface ip>

切断する

接続の逆を行えばOK

# ip route del default via <ppp0 interface ip>
# ip route del <server global ip> via <local gateway ip> dev <interface name>

# xl2tpd-control disconnect vpn-connection
# ipsec auto --down L2TP-PSK

ちなみに、/etc/ppp/ip-up.d にスクリプトを置くと ppp0 が上がった時点で、/etc/ppp/ip-down.d に置くと ppp0 が落ちた時点で実行されるので経路を自動追加・削除するスクリプトを書いておくと便利。

注意点

・pppoptfile に crtscts 及び lock を書くと unrecognized option になる

ArchWiki や Libreswan Wiki などの設定サンプルに記述されているこのオプションですが、最近の pppd の変更により削除された(?)のか設定するとエラーになるので注意。

・pppoptfile の noauth の下に require-mschap-v2 を書くと接続エラーになる

なぜか noauth がスルーされて認証を要求してくるので必ず上に書くようにする。
これに気づかずドツボにはまって半日潰しました…。

xl2tpd[7966]: /usr/sbin/pppd: The remote system is required to authenticate itself
xl2tpd[7966]: /usr/sbin/pppd: but I couldn't find any suitable secret (password) for it to use to do so.

・Android 6.x 系及び 7.x 系から接続できない

どうやら、Android の L2TP/IPsec における SHA2 の実装がドラフト版の古いものであることが原因のようです。
Issue も上がってます。*2
Libreswan の Wiki*3 によるとサーバー側の /etc/ipsec.d/l2tp-psk.conf に sha2-truncbug=yes を追加すればいいみたいですが、RFC に則った実装をしているクライアントが全部死ぬっぽいので、回避策として ike=、phase2alg= オプションを指定する必要があるようです。*4

ike=3des-sha1,aes-sha1,aes256-sha2_256,aes256-sha2_512
phase2alg=3des-sha1,aes-sha1,aes256-sha2_256,aes256-sha2_512
sha2-truncbug=yes

ちなみに、自分はこのオプションを追記してもなお Windows と Android 5.x 系から接続出来なかったので捨てました。

・Windows から接続できない

過去記事*5を参照。

*1 – VPN server for remote clients using IKEv1 with L2TP – Swan
*2 – Issue 196939 – android – L2TP/IPsec VPN does not work in Android 6.0.1 – Android Open Source Project – Issue Tracker – Google Project Hosting
*3 – FAQ – Swan
*4 – Android 6 MarshmallowでL2TP/IPsec VPNが動作しない問題を解決する(xl2tpd+openswan) | Web Net Force
*5 – RPi(Arch)でL2TP/IPsecなVPNを構築する – Gomasy's blog