はじめに
現在、VPNサーバーに Windows 10 ビルドイン の設定を使って接続しているが、これを AWS 上の Linux インスタンスから実施したかった。
正直、WindowsではGUIで簡単にできていたし簡単にできるだろうと高を括っていたら丸一日ぐらい調査と検証に時間をかけることになりましたので、メモします。
今回の結果はAWSの東京リージョン (ap-northeast-1) 上で検証したものになります。
使用したOS、ツールなど
- CentOS Linux release 7.6.1810 (Core)
- ami-045f38c93733dd48d (AWS Merketplace でサブスクリプション必要、無料)
- 最初はAmazon Linux (ami-0064e711cbc7a825e) でやろうとしたけれど、当時は libreswan をインストールしても ipsec pluto がうまく入ってこなかったので今回はあきらめた
- (追記) 少し手順を変えることで amzn2-ami-hvm-2.0.20191217.0-x86_64-gp2 (ami-011facbea5ec0363b) では VPN 接続できたのでこの点を追記
- Public Subnet に存在していて、EIPを eth0 に付与しているインスタンス
- libreswan (libreswan-3.25-8.1.el7_7.x86_64)
- xl2tpd (xl2tpd-1.3.14-1.el7.x86_64)
VPNサーバーについて
Windows 10 のVPN接続設定で以下の設定で接続できたVPNサーバー。
- VPNプロバイダー: Windows (ビルドイン)
- VPNの種類: 事前共有キーを使った L2TP/IPSec
- サインイン情報の種類: ユーザー名とパスワード
各種ツールのインストール
ローンチした直後の CentOS 7 で以下のコマンドを実行し、libreswan および xl2tpd をインストールします。
初期ログインユーザーは centos ですが、以下の全てのコマンドは sudo su -
後の root で実行しています。 sudo で実行する場合は適時読み替えてください。
yum -y update
yum -y install epel-release # xl2tpd に必要
yum -y install libreswan xl2tpd
yum -y install bind-utils # dig が必要な場合
ミドルウェアの設定
ここから各ミドルウェアの設定をしていきます。
ただ、いくつかの説明サイトではlibreswanとxl2tpdの設定がまとめて書かれていますが、実際には/で分離されている通り、IPSec と L2TP は別の目的があり、これらを組み合わせてVPN接続を実現しています。
言い換えると(実用性は別として)IPSec, L2TP は個別に設定を確認できるということです。
そこで、本記事ではこれらを分けて設定していきます。
IPSec の設定
1. ipsec verify
まずは何も記載していない状態で ipsec verify
を確認します。
ここでエラーとなっている部分を直していきます。
$ systemctl restart ipsec
$ ipsec verify
Verifying installed system and configuration files
Version check and ipsec on-path [OK]
Libreswan 3.25 (netkey) on 3.10.0-957.1.3.el7.x86_64
Checking for IPsec support in kernel [OK]
NETKEY: Testing XFRM related proc values
ICMP default/send_redirects [NOT DISABLED]
Disable /proc/sys/net/ipv4/conf/*/send_redirects or NETKEY will act on or cause sending of bogus ICMP redirects!
ICMP default/accept_redirects [NOT DISABLED]
Disable /proc/sys/net/ipv4/conf/*/accept_redirects or NETKEY will act on or cause sending of bogus ICMP redirects!
XFRM larval drop [OK]
Pluto ipsec.conf syntax [OK]
Two or more interfaces found, checking IP forwarding [FAILED]
Checking rp_filter [ENABLED]
/proc/sys/net/ipv4/conf/all/rp_filter [ENABLED]
/proc/sys/net/ipv4/conf/default/rp_filter [ENABLED]
/proc/sys/net/ipv4/conf/eth0/rp_filter [ENABLED]
/proc/sys/net/ipv4/conf/ip_vti0/rp_filter [ENABLED]
rp_filter is not fully aware of IPsec and should be disabled
Checking that pluto is running [OK]
Pluto listening for IKE on udp 500 [OK]
Pluto listening for IKE/NAT-T on udp 4500 [OK]
Pluto ipsec.secret syntax [OK]
Checking 'ip' command [OK]
Checking 'iptables' command [OK]
Checking 'prelink' command does not interfere with FIPS [OK]
Checking for obsolete ipsec.conf options [OBSOLETE KEYWORD]
warning: could not open include filename: '/etc/ipsec.d/*.conf'
ほとんどのエラーは Linux のカーネルパラメータの設定によるものなので、/etc/sysctl.conf
に値を設定し、反映します。
# Disable /proc/sys/net/ipv4/conf/*/send_redirects
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.send_redirects=0
net.ipv4.conf.eth0.send_redirects=0
net.ipv4.conf.ip_vti0.send_redirects=0
net.ipv4.conf.lo.send_redirects=0
# Disable /proc/sys/net/ipv4/conf/*/accept_redirects
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.eth0.accept_redirects=0
net.ipv4.conf.ip_vti0.accept_redirects=0
net.ipv4.conf.lo.accept_redirects=0
# Two or more interfaces found, checking IP forwarding [FAILED]
net.ipv4.ip_forward=1
# Checking rp_filter
net.ipv4.conf.all.rp_filter=0
net.ipv4.conf.default.rp_filter=0
net.ipv4.conf.eth0.rp_filter=0
net.ipv4.conf.ip_vti0.rp_filter=0
以下のように結果が全て [OK]
となることを確認。
$ vi /etc/sysctl.conf # 上記の項目を追加
$ sysctl -p
$ touch /etc/ipsec.d/vpn.conf # 最後の include するファイルが1つもない部分に対応
$ ipsec verify
Verifying installed system and configuration files
Version check and ipsec on-path [OK]
Libreswan 3.25 (netkey) on 3.10.0-957.1.3.el7.x86_64
Checking for IPsec support in kernel [OK]
NETKEY: Testing XFRM related proc values
ICMP default/send_redirects [OK]
ICMP default/accept_redirects [OK]
XFRM larval drop [OK]
Pluto ipsec.conf syntax [OK]
Two or more interfaces found, checking IP forwarding [OK]
Checking rp_filter [OK]
Checking that pluto is running [OK]
Pluto listening for IKE on udp 500 [OK]
Pluto listening for IKE/NAT-T on udp 4500 [OK]
Pluto ipsec.secret syntax [OK]
Checking 'ip' command [OK]
Checking 'iptables' command [OK]
Checking 'prelink' command does not interfere with FIPS [OK]
Checking for obsolete ipsec.conf options [OK]
2. /etc/ipsec.d/vpn.conf
の記述
先ほど touch コマンドで作成したところに書き込んでいきます。
ここでは vpn.conf
としましたが、 *.conf
にマッチする名称であれば何でも構いません。 ルート設定の /etc/ipsec.conf
内で /etc/ipsec.d/*.conf
が include されるためです。
今回は EIP: AAA.BBB.CCC.DDD
から VPNサーバー WWW.XXX.YYY.ZZZ
へと VPN 接続を行うこととします。 以下の2つのファイルを作成・変更してください。
conn L2TP
authby=secret
pfs=no
auto=add
keyingtries=3
dpddelay=30
dpdtimeout=120
dpdaction=clear
keyexchange=ike
phase2=esp
encapsulation=yes
rekey=yes
ikelifetime=8h
keylife=1h
type=transport
left=%defaultroute
leftid=AAA.BBB.CCC.DDD
right=WWW.XXX.YYY.ZZZ
# rightid=SSS.TTT.UUU.VVV
AAA.BBB.CCC.DDD WWW.XXX.YYY.ZZZ : PSK "[事前共有キー]"
なお、この設定では auto=add
なので ipsec デーモンを立ち上げたとしても、ユーザーが明示的にupしない限り ipsec 交換は行いません。 up するコマンドは ipsec auto --up [connの後に書いたコネクションの名前]
です。
以上の設定が終了した場合、以下のコマンドを実行してください。
ipsec auto --up L2TP
で色々とメッセージが出ますが、すぐに終わった場合は大体成功です。
$ systemctl restart ipsec
$ ipsec auto --up L2TP
...(ここにたくさん出てくるが、エラーがない場合はすぐ終わる)...
...(エラーがある場合は下記の備考1を参照にして修正)...
$ ipsec status
...(中略)...
000 Total IPsec connections: loaded 1, active 1 # 設定は1つしか書いていないので active 1 となっていれば OK
...(中略)...
うまくいけば IPSec については終了です。 設定だけを行いたい場合は L2TP まで進んでください。
備考1: 失敗する場合
私の試行中には ipsec auto --up L2TP
実行後に終了しない場合、画面に以下のようなエラーが出力されましたので、修正方法を記載しておきます。
修正後は systemctl restart ipsec && ipsec auto --up L2TP
で再試行してください。
- ERR1: 事前共有キーがうまく読み取れていません
- vpn.secretsの値と一致しているかを再確認してください
Can't authenticate: no preshared key found for `AAA.BBB.CCC.DDD' and `WWW.XXX.YYY.ZZZ'. Attribute OAKLEY_AUTHENTICATION_METHOD
- ERR2: VPNサーバーIPの裏にある別のIPをIDに指定する必要があります
-
/etc/ipsec.d/vpn.conf
の rightidの行の#を外す -
/etc/ipsec.d/vpn.secrets
の値をWWW.XXX.YYY.ZZZ
をSSS.TTT.UUU.VVV
に置き換える
-
we require IKEv1 peer to have ID 'WWW.XXX.YYY.ZZZ', but peer declares 'SSS.TTT.UUU.VVV'
備考2: Amazon EC2 インスタンスの制約
設定にはさらっと書いていますが、EC2インスタンスから libreswan で IPSec を貼る場合、EC2のネットワークを見越した設定が必要になります。
具体的には以下の3つです。 より詳しくはlibreswanの公式Wiki(英語)から。
- left=%defaultroute
- leftid=AAA.BBB.CCC.DDD
- encapsulation=yes
追記: さくらの VPC ルータに接続した場合
2023/12/27 追記 : さくらの VPC ルータに接続しようとすると、上記設定では NG だった。
以下のように設定した。
- 暗号化アルゴリズムが AES256, 3DES、ハッシュアルゴリズムは SHA (=SHA1)、DHグループが MODP1024 のため、
ike
パラメータをそれに準じたものに明示的に指定。
conn L2TP
authby=secret
pfs=no
auto=add
dpddelay=30
dpdtimeout=120
dpdaction=clear
keyexchange=ike
ike=3des-sha;modp1024,aes256-sha;modp1024
phase2=esp
ikelifetime=28800s
keylife=1800s
type=transport
encapsulation=yes
left=%defaultroute
leftid=AAA.BBB.CCC.DDD
leftprotoport=17/1701
right=WWW.XXX.YYY.ZZZ
rightprotoport=17/1701
L2TP (xl2tpd) の設定
こちらが L2TP を行ってくれるミドルウェアです。
ここで使われる設定ファイル3つを以下の通り修正します。
なお、xl2tpd.conf の [lns default]
セクションが初めから記載されていますが、ここは L2TP サーバーとしての機能の設定なので、クライアントとしてのみ使う今回の場合はコメントアウト or 消去してください。
[lac L2TP]
lns = WWW.XXX.YYY.ZZZ
ppp debug = yes
pppoptfile = /etc/ppp/options.xl2tpd.client
length bit = yes
autodial = yes
redial = yes
redial timeout = 10
max redials = 6
user [VPNの接続ユーザー名]
debug
noauth
mtu 1400
mru 1400
; ipv6cp-use-ipaddr
; +ipv6
# Secrets for authentication using CHAP
# client server secret IP addresses
"[VPNの接続ユーザー名]" * "[VPNの接続パスワード(※事前共有キーではない)]" *
設定後、以下のコマンドを実行して IPSec + L2TP の同時実行が行われます。
$ systemctl stop xl2tpd && systemctl stop ipsec # 1度止める
$ systemctl start ipsec && systemctl start xl2tpd && ipsec auto --up L2TP
...(IPSecの起動メッセージが表示)...
$ ifconfig
...(ppp0というI/Fが作られていれば成功)...
上記メッセージ中にもありますが、ppp0というインタフェースが表示されていれば成功です。
問題がない場合は以下の備考は飛ばして次に進んでください。
ppp0 が表示されない場合はどこかに問題があることになります。 今回の設定だと、xl2tpd および pppd のメッセージログは /var/log/messages
に記入されますので、ここを見て問題解決をしていきます。 以下の備考も参考にしてください。
なお、ここで示している設定では debug モードを on にしているため、表示されるメッセージは多めになっています。 正常に接続でき、以後詳細メッセージが不要であればこの部分は取り除いてください。
備考1: 検証中に出てきたエラー
うまく ppp0 が作成されない場合、 /var/log/messages
の中身を確認してみてください。 以下に私が遭遇したエラーを紹介します。
- ERR1: Config File が間違えている場合
- xl2tpd, pppd の起動時に設定が間違えている場合、以下のようなメッセージが表示されるので、マニュアルや
man
などを参考にして修正してください - ちなみに、以下は ipv6 を有効にしようとして失敗したケースです。 詳しくは備考3で。
- xl2tpd, pppd の起動時に設定が間違えている場合、以下のようなメッセージが表示されるので、マニュアルや
xl2tpd: /usr/sbin/pppd: In file /etc/ppp/options.xl2tpd.client: too few parameters for option 'ipv6'
pppd[16075]: In file /etc/ppp/options.xl2tpd.client: too few parameters for option 'ipv6'
- ERR2: [lac VPN]のセクションに local ip として自分の Private IPを書いてしまった場合
- 今までの設定を見ていると書きたくなった
- ここにlocal ip を書く場合はVPN側ネットワークで割り振られる local ip を書く必要がある
- local ip は書かずに、素直にVPNサーバーが割り振ってくれるものを使おう
Peer refused to agree to our IP address
備考2: l2tp-control への書き込み
参考にした記事では以下のようにして ppp0 を有効にしているものもありましたが、今回検証した環境ではこれを実行しなくても ppp0 は有効になりました。
echo "c [xl2tpd.confで設定したlac名称]" > /var/run/xl2tpd/l2tp-control
もし /var/log/messages
にエラーが表示されないにも関わらず、ppp0 が作られない場合は echo "c VPN" > /var/run/xl2tpd/l2tp-control
を1度実行してみてください。
ちなみに、既に有効になっている場合に上記を再度実行すると、
xl2tpd: xl2tpd[15058]: Session 'VPN' already active!
のように表示されます(特に影響はありません)。
備考3: Unsupported protocol 'IPv6 Control Protocol' (0x8057) received
接続に時間がかかった理由の1つが /var/log/messages
の中に以下のようなメッセージが出ていたためでした。
pppd[18449]: CHAP authentication succeeded
pppd[18449]: Unsupported protocol 'IPv6 Control Protocol' (0x8057) received
pppd[18449]: Peer refused to agree to our IP address
結果的に、2行目と3行目は別物でした。 3行目は備考1:ERR2として紹介した方法で解消するものです。
しかし、これが問題と思っていたので解消方法を色々と探しました。
結果として、上の /etc/ppp/options.xl2tpd.client
でコメントアウトされている部分を復活させれば IPv6 Control Protocol にも対応可能です。
user [VPNの接続ユーザー名]
debug
noauth
mtu 1400
mru 1400
ipv6cp-use-ipaddr
+ipv6
最後の +ipv6
ですが + は間違いではありませんので忘れないでください。
なお、このオプションは Enable the IPv6CP and IPv6 protocolsと説明されている ページがあったり、廃止されたオプションとして無効化されていると説明されている ところがあったり、そもそもマニュアルに載ってなかったりするのですが、今回の環境では利用可能でした。
これを記述するとIPv6のローカルIPが払い出されます。
pppd[18920]: CHAP authentication succeeded
pppd[18920]: local LL address XXXX::XXXX:XXXX:XXXX:XXXX
pppd[18920]: remote LL address YYYY::YYYY:YYYY:YYYY:YYYY
route の設定
これで L2TP/IPSec の接続はできましたが、VPN先から通信をしようと思う場合、通信として ppp0 から出力されるようにしなくてはいけません。
その他の手順を見ると 0.0.0.0/0
を ppp0 に流せば全ての通信が VPN 経由になる、という説明があるところもありますが、今回は絶対にこの方法を採るべきではありません 。
なぜなら、EC2インスタンスにはあなたのローカルPCから何らかの方法(おそらくSSH)で接続をしていますので、全ての通信を VPN の口に向けてしまうと、接続のレスポンスが一切返ってこなくなります 。
万が一やってしまった場合はAWS Management Consoleなどからインスタンスを再起動しましょう。 永続追加してしまった場合はやりようが難しいので、基本は一時的なものにするとよいでしょう。
基本、LinuxをL2TP/IPSecクライアントとして利用する場合、宛先が決まっていることがほとんどだと思いますので、宛先を指定するには以下のコマンドを使います。
# 特定IPに対してVPN先のIPでアクセスする場合
$ route add -net [接続先IPアドレス] netmask 255.255.255.255 dev ppp0
# 上記設定を削除する場合
$ route del -net [接続先IPアドレス] netmask 255.255.255.255 dev ppp0
ネットワークの範囲指定例
http://ifconfig.me/ というサイトで接続元IPをチェックできるので、ここへの接続をVPN越しに実施することにします。
$ dig ifconfig.me
; <<>> DiG 9.11.4-P2-RedHat-9.11.4-9.P2.el7 <<>> ifconfig.me
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 46330
;; flags: qr rd ra; QUERY: 1, ANSWER: 4, AUTHORITY: 0, ADDITIONAL: 1
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4096
;; QUESTION SECTION:
;ifconfig.me. IN A
;; ANSWER SECTION:
ifconfig.me. 60 IN A 216.239.38.21
ifconfig.me. 60 IN A 216.239.32.21
ifconfig.me. 60 IN A 216.239.34.21
ifconfig.me. 60 IN A 216.239.36.21
;; Query time: 1 msec
;; SERVER: 172.31.0.2#53(172.31.0.2)
;; WHEN: Thu Nov 14 13:48:38 UTC 2019
;; MSG SIZE rcvd: 104
これら全てを個別に route add
しても良いのですが、以下のように 216.239.32.0/19
として設定することでも対処できます。
$ curl http://ifconfig.me
# (EC2インスタンスのEIP AAA.BBB.CCC.DDD が出力)
$ route add -net 216.239.32.0 netmask 255.255.224.0 dev ppp0
$ curl http://ifconfig.me
# (VPNのIPアドレス WWW.XXX.YYY.ZZZ が出力)
これでVPNのIPアドレスが出力されていれば、VPN接続先から正常に通信が行えています。
これは永続化は設定していないので、再起動をすればルートは消えてしまいますが、もし永続化が必要な場合 range
や ip
コマンドを別途調べて下さい。
なお、ルートテーブルのメトリクスが同じ場合、複数のルールにマッチした場合はレンジの小さい方 (/24よりも/32の方が小さい) が優先されます。
これも踏まえてルールを設定してください。
VPNの切断
L2TP/IPSec のサービスを中断すれば止まります。
$ systemctl stop xl2tpd && systemctl stop ipsec
あるいは、L2TPのコネクションのみを切りたいのであれば、
echo "d VPN" > /var/run/xl2tpd/l2tp-control
のようにすればコネクションを切断できます。
終わりに
調べてみたら予想よりも L2TP/IPSec クライアントとして使うことを説明する記事が少なかったです。 サーバーは多かったのですが。。。
あとはこの情報をもとにスクリプト化すれば、色々と自動化が捗ります。
参考資料
- https://qiita.com/ryouma_nagare/items/a57fd8241f6b39f71676
- http://network.station.ez-net.jp/client/remote/vpn/l2tp-linux.asp
- https://qiita.com/__cooper/items/4b5ab7ae40b0e95f0d19
- https://qiita.com/kure/items/ad5b91d62d23b9928997
- https://mo.kerosoft.com/0215
- https://libreswan.org/wiki/Interoperability
2020/01/30 追記: Amazon Linux 2 での接続
CentOS 8 の Free AMI がなかなか出てこないので、Amazon Linux 2 でもう一度検証した。
結果、少し変更は必要だったが、概ね上記の手順で実行できた。
- 検証AMI: amzn2-ami-hvm-2.0.20191217.0-x86_64-gp2 (ami-011facbea5ec0363b)
変更点
- epel のインストールは
amazon-linux-extras install -y epel
となる- xl2tpd のインストールは epel がないと No Available になるので順序に注意
- そのままだと xl2tpd が起動しないので対処が必要。
cp /usr/lib/systemd/system/xl2tpd.service /etc/systemd/system/
[Unit]
Description=Level 2 Tunnel Protocol Daemon (L2TP)
Wants=network-online.target
After=network-online.target
After=ipsec.service
# Some ISPs in Russia use l2tp without IPsec, so don't insist anymore
#Wants=ipsec.service
[Service]
Type=simple
PIDFile=/var/run/xl2tpd/xl2tpd.pid
# ここをコメントアウト: ExecStartPre=/sbin/modprobe -q l2tp_ppp
ExecStart=/usr/sbin/xl2tpd -D
Restart=always
[Install]
WantedBy=multi-user.target
原因は ExecStartPre でロードしようとするモジュール l2tp_ppp が Amazon Linux 2 のカーネル内に含まれないこと。
推測になるが、 xl2tpd の Github の書き込みの中に、以下の通り Amazon Linux 2 (と思う、AWS の AMI) では l2tp_ppp
がサポートされておらず、カーネルの再コンパイルが必要らしい。
No, I contacted aws and they said it's not supported on their kernels.
They claimed it may be possible by compiling a custom kernel but I took a different route. So this can be closed.
引用元 - https://github.com/xelerance/xl2tpd/issues/150
幸い、これがなくても動くので、systemctl 用の service ファイルから ExecStartPre 部分を除いてやれば動くようになる。
同様の解決をしたという例を AWS Forum でも見つけた。
とりあえず、これで気軽にAmazon Linux 2でも使えるようになりました。