それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

xl2tpd でサーバ&クライアントを作り、PPP通信をする(IPSecなし)

L2TP/IPsec について

L2TP/IPSec と一言でいっても、Linux上では、次の3つの機能によって成り立ってる。

  • ipsec ( ikev1 )
  • xl2tpd
  • pppd

このうち、xl2tpd + pppd はペアで1つのPPP接続(≒vpn)を作る、ipsecは暗号化である。ipsecで指定したIP/ポートを暗号化するトンネル(xfrm)を作る。ipsecのESP(カプセル化)の中身にL2TPを通してパケットを安全に転送する機能である。xfrm のfrm はtransform のfrm である

なので、次の2つが独立して動作している。

ipsec とペアに使うが、独立して動作するxlt2pdとpppd である。ipsecの設定を抜いても動くはずである。

xl2tpd のみでppp通信を行う。

最初には、xl2tpd(+pppd)を使ってPPPデバイスを作り通信をすることをやってみる。

つぎに、ipsec でパケットを暗号化するトンネルを作り、ESPを見てみる。

最後に、L2TP/IPsec ( エルツーティピー オーバ アイピーセック)を試すことにする。

xl2tpd には、LAC/LNS双方で認証する仕組みがあり、Google検索で見つかった記事では双方を同名にして誤魔化しがあったので、その部分を詳しく見直してみた。また、LAC/LNSでLAC(クライアント)がLNS(サーバ)を認証する機構をスキップすることもできるので、併せて検証した。

準備1:仮想マシン・コンテナでマシンを作る

コンテナだと、PPPデバイスがうまく作れない(特権を入れてもうまく行かないかもしれません)

lxc の場合、明示的にPPPデバイスを作ってあげると動く。Dockerの場合は知らん。

 lxc config device add vpn-c dev_ppp unix-char path=/dev/ppp   

または、仮想マシンでデバイスを作成します。

NAME=u2204-02
UEFI=/usr/share/AAVMF/AAVMF_CODE.fd
NVRAM=/var/lib/libvirt/qemu/nvram/$NAME_VARS.fd
DISK=/var/lib/libvirt/images/$NAME.qcow2
virt-install \
    --name=$NAME \
    --machine=virt\
    --arch=aarch64 \
    --virt-type=kvm \
    --cdrom=$ISO_IMG \
    --serial pty \
    --disk path=$DISK,format=qcow2,device=disk,bus=virtio,cache=none \
    --boot loader=$UEFI,loader_ro=yes,nvram=,loader_type=pflash \
    --boot cdrom,hd \
    --network type=direct,source=eth0,source_mode=bridge,model=virtio \
    --memory=2048 \
    --vcpu=2 \
    --os-variant ubuntu22.04 

クライアント側とサーバー側で2台用意しました。

使った環境

$ uname -a
Linux vpn-server 5.15.0-78-generic #85-Ubuntu SMP Fri Jul 7 15:29:30 UTC 2023 aarch64 aarch64 aarch64 GNU/Linux
$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=22.04
DISTRIB_CODENAME=jammy
DISTRIB_DESCRIPTION="Ubuntu 22.04.2 LTS"

ソフトウェア

ネットワーク構成を確認するためのツール群

sudo apt install tcpdump iputils-ping iperf3 bind9-dnsutils

サーバー側の構成

サーバ側では、次の設定を行う

  • インストール
  • xl2tpd設定
    • /etc/xl2tpd/xl2tpd.conf
  • ppptd設定
    • /etc/ppp/options.l2tpd
    • /etc/ppp/chap-secrets
    • ip route 設定

インストール

sudo apt install xl2tpd

デバッグのためにxl2tpd 自動起動を停止

xl2tpd の設定周りは、設定ミスがあるときに、systemd / journalctl で状況を見づらいために、停止してしまう。

systemctl stop xl2tpd
systemctl disable xl2tpd

停止してから、手動で起動する。

/usr/sbin/xl2tpd -D \
  -c /etc/xl2tpd/xl2tpd.conf\
  -C /var/run/xl2tpd/xl2tpd-control

コマンドのオプションについては次の通り。

/usr/sbin/xl2tpd -h

xl2tpd version:  xl2tpd-1.3.16
Usage: xl2tpd [-c <config file>] [-s <secret file>] [-p <pid file>]
              [-C <control file>] [-D] [-l] [-q <tos decimal value for control>]
              [-v, --version]

/etc/xl2tpd/xl2tpd.conf

xl2tpd の設定 /etc/xl2tpd/xl2tpd.conf サーバー構成なので[lns]

cat << EOF > /etc/xl2tpd/xl2tpd.conf
[global]
port = 1701

[lns default]
ip range = 10.0.3.100-10.0.3.200
local ip = 10.0.3.1
refuse chap = yes
refuse pap = yes
require authentication = yes
ppp debug = yes
pppoptfile=/etc/ppp/options.l2tpd
EOF

/etc/ppp/options.l2tpd

上記で指定した/etc/ppp/options.l2tpd

cat <<EOF > /etc/ppp/options.l2tpd

ms-dns  192.168.1.1
mtu 1358
mru 1358

refuse-pap
refuse-chap
refuse-mschap
require-mschap-v2
asyncmap 0
name user
EOF

/etc/ppp/chap-secrets

PPPパスワードを/etc/ppp/chap-secrets にかく

cat <<EOF > /etc/ppp/chap-secrets
user  * shoi7Za6Po  *
EOF

デバッグ有効でフロント起動

/usr/sbin/xl2tpd -D \
  -c /etc/xl2tpd/xl2tpd.conf\
  -C /var/run/xl2tpd/xl2tpd-control

起動したら、その端末画面を開いたまま、クライアント設定を行う。

クライアント側の構成

クライアント側の設定も、サーバー側とだいたい同じである。

ただ、ファイル名は意図的に違うものにしている。

  • インストール
  • xl2tpd設定
    • /etc/xl2tpd/xl2tpd.conf
  • ppptd設定
    • /etc/ppp/options.l2tpd.lac-vpn.conf
    • /etc/ppp/chap-secrets
    • ip route 設定

インストール

sudo apt install xl2tpd

自動起動を解除する

sudo systemctl stop xl2tpd
sudo systemctl disable xl2tpd

/etc/xl2tpd/xl2tpd.conf

cat << EOF > /etc/xl2tpd/xl2tpd.conf
[global]
port = 1701


[lac vpn]
lns = 10.17.238.246
refuse chap = yes
refuse pap = yes
require authentication = yes
ppp debug = yes
length bit = yes
pppoptfile = /etc/ppp/options.l2tpd.lac-vpn.conf
EOF

/etc/ppp/options.l2tpd.lac-vpn.conf

上記で指定した、ファイル/etc/ppp/options.l2tpd.lac-vpn.conf

cat <<EOF >/etc/ppp/options.l2tpd.lac-vpn.conf
name user
mtu 1380
mru 1380
nodefaultroute
EOF

/etc/ppp/chap-secrets

サーバー側と共通のパスワードにする。

PPPパスワードを/etc/ppp/chap-secrets にかく

cat <<EOF > /etc/ppp/chap-secrets
user        *       shoi7Za6Po  *
EOF

クライアント→サーバーへ接続

起動する。(サーバー側と同じコマンドになる。)

/usr/sbin/xl2tpd -D \
  -c /etc/xl2tpd/xl2tpd.conf\
  -C /var/run/xl2tpd/xl2tpd-control 

専用コマンドから接続

起動したら、xlt2pd を操作するために、専用のコマンドを使う。

別の端末でログインしてxl2tpdコントローラーを使う、

xl2tpd-control -d -c /var/run/xl2tpd/xl2tpd-control available
xl2tpd-control -d -c /var/run/xl2tpd/xl2tpd-control connect-lac $LAC
xl2tpd-control -d -c /var/run/xl2tpd/xl2tpd-control status-lac $LAC

xl2tpd-controlで xl2tpd に命令を与えて、接続切断を行わせる。

設定ミスが有る場合

pppデバイスが起動しなくて落ちたり、接続エラーが画面に出てくる

Terminating pppd: sending TERM signal
xl2tpd[2067]: Call established with 192.168.2.190, PID: 2074, Local: 61079, Remote: 17360, Serial: 4
xl2tpd[2067]: control_finish: Connection closed to 192.168.2.190, port 1701 (Server closing), Local: 58560, Remote: 6568
xl2tpd[2067]: Terminating pppd: sending TERM signal to pid 2074

接続したことを確認

pingを使って疎通確認する。

サーバー側から

ping 10.0.3.100

クライアント側から

ping 10.0.3.1

デバッグ用の設定

/etc/xl2tpd/xl2tpd.conf

[lns default]
ppp debug = yes

割当アドレスを固定する

サーバー側のchap-secret を使って、クライアント割当アドレスを固定できる。

/etc/ppp/chap-secrets

user        *      shoi7Za6Po  10.0.3.102

LNS-LAC間の相互認証 ( name 共通にしたとき )

LNS-LAC間は、お互いに相手を認証するようになっているらしい。

SSHにおけるサーバー公開鍵のチェック」をイメージするとわかりやすいかもしれない。

サーバー側の設定は次のようになっている。

vpn-server:/etc/xl2tpd/xl2tpd.conf

[lns default]
require authentication = yes
pppoptfile=/etc/ppp/options.l2tpd

ファイル vpn-server: /etc/ppp/options.l2tpd

require-mschap-v2
name user

ファイル vpn-server:/etc/ppp/chap-secrets

user  * shoi7Za6Po  *
EOF

クライアント側 

ファイル client:/etc/xl2tpd/xl2tpd.conf

[lac vpn]
lns = 10.17.238.246
require authentication = yes
pppoptfile = /etc/ppp/options.l2tpd.lac-vpn.conf

ファイルclient:/etc/ppp/options.l2tpd.lac-vpn.conf

name user
EOF

ファイル client:/etc/ppp/chap-secrets

user        *       shoi7Za6Po  *

双方で name XXX を共通にして、お互いに同じ名前で認証を通している。

  • LAC は LNS が name user であると認識し chap-secret を見る
  • LNS は LAC が name user であると認識し chap-secret を見る

chap-secrets は次のようになっている。

# Secrets for authentication using CHAP
# client        server  secret                  IP addresses

このときの「クライアント側のログ」

using channel 8
Using interface ppp0
Connect: ppp0 <-->
Overriding mtu 1500 to 1380
PPPoL2TP options: debugmask 0
Overriding mru 1500 to mtu value 1380
sent [LCP ConfReq id=0x1 <mru 1380> <asyncmap 0x0> <auth eap> <magic 0xfb873186>]
rcvd [LCP ConfReq id=0x1 <mru 1358> <asyncmap 0x0> <auth chap MS-v2> <magic 0x8b662d0a>]
sent [LCP ConfAck id=0x1 <mru 1358> <asyncmap 0x0> <auth chap MS-v2> <magic 0x8b662d0a>]
rcvd [LCP ConfAck id=0x1 <mru 1380> <asyncmap 0x0> <auth eap> <magic 0xfb873186>]
PPPoL2TP options: debugmask 0
sent [LCP EchoReq id=0x0 magic=0xfb873186]
sent [EAP Request id=0xc4 Identity <Message "Name">]
rcvd [LCP EchoReq id=0x0 magic=0x8b662d0a]
sent [LCP EchoRep id=0x0 magic=0xfb873186]
rcvd [CHAP Challenge id=0x9e <8069033abb134e530e69959ad24c9139>, name = "user"]
added response cache entry 0
sent [CHAP Response id=0x9e <aea6b01f224095eda07b4bf98ec0f5d30000000000000000436400e2b7ec862d4c45fea947a5937b2bc471484018d6e000>, name = "user"]
rcvd [LCP EchoRep id=0x0 magic=0x8b662d0a]
rcvd [EAP Response id=0xc4 Identity <Name "user">]
EAP: unauthenticated peer name "user"
EAP id=0xc4 'Identify' -> 'MD5Chall'
sent [EAP Request id=0xc5 MD5-Challenge <Value a4 bd 6d db 79 34 da 12 56 7d c4 0b 67 58 2b 11 d5 94 b2 bf e8 c6> <Name "user">]
rcvd [CHAP Success id=0x9e "S=D6F9FEC17FB41730A770D03AAD76928EFD2087D0 M=Access granted"]
response found in cache (entry 0)
CHAP authentication succeeded

このときの「サーバー側のログ」

Connect: ppp0 <-->
Overriding mtu 1500 to 1358
PPPoL2TP options: lnsmode tid 58986 sid 32582 debugmask 0
Overriding mru 1500 to mtu value 1358
sent [LCP ConfReq id=0x1 <mru 1358> <asyncmap 0x0> <auth chap MS-v2> <magic 0x8b662d0a>]
rcvd [LCP ConfReq id=0x1 <mru 1380> <asyncmap 0x0> <auth eap> <magic 0xfb873186>]
sent [LCP ConfAck id=0x1 <mru 1380> <asyncmap 0x0> <auth eap> <magic 0xfb873186>]
rcvd [LCP ConfAck id=0x1 <mru 1358> <asyncmap 0x0> <auth chap MS-v2> <magic 0x8b662d0a>]
Overriding mtu 1380 to 1358
PPPoL2TP options: lnsmode tid 58986 sid 32582 debugmask 0
sent [LCP EchoReq id=0x0 magic=0x8b662d0a]
sent [CHAP Challenge id=0x9e <8069033abb134e530e69959ad24c9139>, name = "user"]
rcvd [LCP EchoReq id=0x0 magic=0xfb873186]
sent [LCP EchoRep id=0x0 magic=0x8b662d0a]
rcvd [EAP Request id=0xc4 Identity <Message "Name">]
EAP: Identity prompt "Name"
sent [EAP Response id=0xc4 Identity <Name "user">]
rcvd [LCP EchoRep id=0x0 magic=0xfb873186]
rcvd [CHAP Response id=0x9e <aea6b01f224095eda07b4bf98ec0f5d30000000000000000436400e2b7ec862d4c45fea947a5937b2bc471484018d6e000>, name = "user"]
sent [CHAP Success id=0x9e "S=D6F9FEC17FB41730A770D03AAD76928EFD2087D0 M=Access granted"]
rcvd [EAP Request id=0xc5 MD5-Challenge <Value a4 bd 6d db 79 34 da 12 56 7d c4 0b 67 58 2b 11 d5 94 b2 bf e8 c6> <Name "user">]
sent [EAP Response id=0xc5 MD5-Challenge <Value 2c 78 e3 33 90 c7 e0 06 1e 56 01 83 41 70 21 d9> <Name "user">]
rcvd [EAP Success id=0xc6]
EAP authentication succeeded

この設定では、相互ともに name=user を送り合うので認証が通る。

「相互」にPeerを認証している(auth)

  • phase 1 と書いた部分は、サーバーがクライアントを Authenticate している
  • phase 2 と書いた部分は、クライアントがサーバーを Authenticate している。

どうやらこういうことらしい。

LNS-LACでそれぞれname を設定する。

サーバー側の設定は次のようになっている。

vpn-server: /etc/ppp/options.l2tpd

require-mschap-v2
name server

vpn-server:/etc/ppp/chap-secrets

server user   secretA
user   server secretB

クライアント側 client:/etc/ppp/options.l2tpd.lac-vpn.conf

name user

client:/etc/ppp/chap-secrets

server user   secretA
user   server secretB
  • phase 1 で、サーバーがクライアントを Authenticate(user server secretB ) で認証
  • phase 2 で、クライアントがサーバを Authenticate(server user secretA ) で認証

ということになるのかな。

CHAPのmd5の仕組みの

auth は peer がお互いにお互いを認識しているようです。

Challenge-Handshake Authentication Protocol (CHAP)をみると CHAPではmd5 と name を使って相手を認証する仕組みであるとわかる。

noauth にする。片側認証にする。

xlt2pd はLAC-LNSが相互に相手を認証する仕組みであったが、相互だと煩雑である。

PPPクライアントからみてサーバーは認証を通してくれて、接続さえできてしまえば良い。

PPPサーバーは固定ですし、L2tp/ipsecなら、なりすましや改竄はipsecで防げるはずなので、片側だけでも十分であるはずである。

クライアントはサーバをわざわざ区別しない。 この設定が noauth であるらしい。

クライアント側(lac) /etc/ppp/options.l2tpd.lac-vpn.conf

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

noauth
#name server # サーバを区別しない
name user

/etc/ppp/chap-secrets

#server user secretA *
user server secretB *

サーバー側(lns) /etc/ppp/options.l2tpd

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


name server

/etc/ppp/chap-secrets

#server user   secretA * # サーバー認証に行かない。
user   server secretB *

単純にパスワードを書くだけで通るはずである。

SSHにおけるサーバー公開鍵のチェックを無効化」をイメージするとわかりやすいかもしれない。

複数ユーザーに対応する

ここまでわかった通り、クライアント側がnoatuh を入れるのであれば、サーバーがユーザのパスワード(シークレット)を複数持っていればOKである。

サーバー側(lns) /etc/ppp/options.l2tpd

require-mschap-v2
name server

/etc/ppp/chap-secrets

#server user   secretA * # サーバー認証に行かない。
user1   server secretB *
user2   server secretC *

UNIXパスワードを使う。

PAMに認証を丸投げしてしまえば、PPPのCHAPはLinuxユーザのパスワードを使えるはずである。

ただ、PAMを通さなくても login だけで使えてしまう。

サーバーの設定は次のように。(参考資料

/etc/xl2tpd/xl2tpd.conf

unix authentication = yes

/etc/ppp/options.l2tpd

login
name myserver 

/etc/ppp/pap-secrets

* myserver ""

ppp は古くからあるので、tty 接続と同じように扱うことができるからのようです。

ユーザーの管理が、/etc/passwd (getent / 名前サービス)に移行できるのは管理が便利ですよね。ldapとかnsswtichとかPAMとか

Openvpnやwireguardやipsec ikev2 でVPNを作ると、PAMが面倒でユーザー管理に難があるので、xlt2pのユーザ管理は一日の長があるかもしれない。

xlt2pd の設定

細かい設定はマニュアルを参考にする。

# デフォルト値
# listen-addr = 0.0.0.0
# port = 1701 
# debug network
# auth file = /etc/l2tpd/l2tp-secrets

ppp の設定

参考サイト - https://wiki.archlinux.org/title/PPTP_Client - https://man.openbsd.org/pppd.8

# ポートをロックする
lock
# bsd compression を使わない?
nobsdcomp
# パケット圧縮を使用しない
nodeflate