labunix's blog

labunixのラボUnix

dockerのhwdsl2/ipsec-vpn-serverでIPsecVPNを試してみる。

■dockerのhwdsl2/ipsec-vpn-serverでIPsecVPNを試してみる。

$ docker search ipsec -f stars=3
NAME                        DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
hwdsl2/ipsec-vpn-server     VPN server with IPsec/L2TP, Cisco IPsec and …   360                                     [OK]
cpuguy83/ipsec                                                              6                                       [OK]
jpillora/ipsec-vpn-server   A simple, multi-user IPsec/L2TP and Cisco IP…   4                                       [OK]
ibotty/ipsec-libreswan      minimal priviliged ipsec container for use i…   3                                       [OK]

■hwdsl2にはインストールするタイプもある。

https://github.com/hwdsl2/setup-ipsec-vpn

■公式のドキュメントを参照する。

https://hub.docker.com/r/hwdsl2/ipsec-vpn-server

■dockerイメージのダウンロード

$ docker pull hwdsl2/ipsec-vpn-server

■PSK共有キー、ユーザ名、パスワードを決める。

$ echo 'VPN_IPSEC_PSK=your_ipsec_pre_shared_key
VPN_USER=your_vpn_username
VPN_PASSWORD=your_vpn_password' > vpn.env

■例えば。

$ sed -i 's/your_ipsec_pre_shared_key/myshare/' vpn.env
$ sed -i 's/your_vpn_username/labunix/' vpn.env
$ sed -i 's/your_vpn_password/xxxxx/' vpn.env
$ cat vpn.env
VPN_IPSEC_PSK=myshare
VPN_USER=labunix
VPN_PASSWORD=xxxxx

■起動してみる。

$ docker run \
    --name ipsec-vpn-server \
    --env-file ./vpn.env \
    --restart=always \
    -v ikev2-vpn-data:/etc/ipsec.d \
    -p 500:500/udp \
    -p 4500:4500/udp \
    -d --privileged \
    hwdsl2/ipsec-vpn-server

■ログの確認

$ docker logs ipsec-vpn-server | sed -e 's/[0-9]*\.[0-9]*\.[0-9]*\.70/X.X.X.X/g'

Trying to auto discover IP of this server...

Setting up IKEv2, please wait...

================================================

IPsec VPN server is now ready for use!

Connect to your new VPN with these details:

xl2tpd[1]: Not looking for kernel SAref support.
Server IP: X.X.X.X
IPsec PSK: myshare
Username: labunix
Password: xxxxx

Write these down. You'll need them to connect!

Important notes:   https://git.io/vpnnotes2
Setup VPN clients: https://git.io/vpnclients

------------------------------------------------

IKEv2 setup successful. Details for IKEv2 mode:

VPN server address: X.X.X.X
VPN client name: vpnclient

Client configuration is available at:

/etc/ipsec.d/vpnclient.p12 (for Windows)
/etc/ipsec.d/vpnclient.sswan (for Android)
/etc/ipsec.d/vpnclient.mobileconfig (for iOS & macOS)

*IMPORTANT* Password for client config files:
z5oZ8sgr7coK58pR
Write this down, you'll need it for import!

To start using IKEv2, see: https://git.io/ikev2docker

================================================

Redirecting to: /etc/init.d/ipsec start
Starting pluto IKE daemon for IPsec: .
xl2tpd[1]: Using l2tp kernel support.
xl2tpd[1]: xl2tpd version xl2tpd-1.3.12 started on bfb92615642d PID:1
xl2tpd[1]: Written by Mark Spencer, Copyright (C) 1998, Adtran, Inc.
xl2tpd[1]: Forked by Scott Balmos and David Stipp, (C) 2001
xl2tpd[1]: Inherited by Jeff McAdams, (C) 2002
xl2tpd[1]: Forked again by Xelerance (www.xelerance.com) (C) 2006-2016
xl2tpd[1]: Listening on IP address 0.0.0.0, port 1701

■ネットワークの確認。
 この環境ではbridgeに接続されている。

$ ip a | lsec -sep "^[0-9]" 172.17 | grep "^[0-9]\|inet "
6: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default 
    inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
1bc3de918d9d        bridge              bridge              local
b181ffc1729c        host                host                local
3511d6025db8        none                null                local

$ docker network inspect $(docker network ls | awk '$2 ~ /bri/{print $1}') | jq -r '.[].Containers' | grep "Name\|IPv4Address"
    "Name": "ipsec-vpn-server",
    "IPv4Address": "172.17.0.2/16",

■以下の構成なので、X.X.X.XからのNAT(500/udp,4500/udp)を開ける。
 X.X.X.XはWANでもDMZでも基本構成は同じなので、まずはDMZで構成してみる。

$ echo "[VPN-Client Z.Z.Z.Z] -- [X.X.X.X gw Y.Y.Y.252] -- [Y.Y.Y.92 host 172.17.0.1] -- [ipsec-vpn-server 172.17.0.2]"
[VPN-Client Z.Z.Z.Z] -- [X.X.X.X gw Y.Y.Y.252] -- [Y.Y.Y.92 host 172.17.0.1] -- [ipsec-vpn-server 172.17.0.2]

■dockerホストに接続すれば、ipsec-vpn-serverにポート転送される。

$ sudo iptables -L -n 2>/dev/null| lsec -sep '^$' 172.17

Chain DOCKER (1 references)
target     prot opt source               destination         
ACCEPT     udp  --  0.0.0.0/0            172.17.0.2           udp dpt:4500
ACCEPT     udp  --  0.0.0.0/0            172.17.0.2           udp dpt:500

■まずはdockerホストのポート転送を無視して、ipsec-vpn-serverまでルーティングしてみる。

■Fortigateのルーティング、NAT設定の例

# dockerホストに向けたルーティング
# show router static 3
config router static
    edit 3
        set dst 172.17.0.0 255.255.0.0
        set gateway Y.Y.Y.92
        set device "lan"
    next
end

# 500/udp、4500/udpを開放する定義
# show firewall service custom VPN
config firewall service custom
    edit "VPN"
        set category "Tunneling"
        set udp-portrange 500 4500
    next
end

# DMZからipsec-vpn-serverへのVIPを定義
# show firewall vip DMZ-VPN 
config firewall vip
    edit "DMZ-VPN"
        set uuid cdf4c53c-75f1-51eb-1080-afd0cbf057d1
        set extip X.X.X.X
        set extintf "dmz"
        set mappedip "172.17.0.2"
    next
end

# DMZ、LAN間のポリシー定義
# show firewall policy 18
config firewall policy
    edit 18
        set name "DMZ-VPN"
        set uuid XXXXX
        set srcintf "dmz"
        set dstintf "lan"
        set srcaddr "WLAN-Router"
        set dstaddr "DMZ-VPN"
        set action accept
        set schedule "always"
        set service "VPN"
        set nat enable
    next
end

■Androidからの接続ログをipsec-vpn-serverから確認してみる。
 ※Android側からの設定は以下を参照。

 Configure IPsec/L2TP VPN Clients
 https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md

$ docker exec -it ipsec-vpn-server ipsec status | \
    sed -e 's/^000 //g' | tail -n 12 | \
    sed -e 's/[,:;] /&\n  /g' | sed -e 's/[0-9]*\.[0-9]*\.[0-9]*\.252/Y.Y.Y.252/g'
Total IPsec connections: 
  loaded 4, 
  active 1
 
State Information: 
  DDoS cookies not required, 
  Accepting new IKE connections
IKE SAs: 
  total(1), 
  half-open(0), 
  open(0), 
  authenticated(1), 
  anonymous(0)
IPsec SAs: 
  total(1), 
  authenticated(1), 
  anonymous(0)
 
#4: 
  "l2tp-psk"[4] Y.Y.Y.252:64916 STATE_MAIN_R3 (IKE SA established); 
  EVENT_SA_EXPIRE in 28559s; 
  newest ISAKMP; 
  lastdpd=27s(seq in:12602 out:0); 
  idle;
#5: 
  "l2tp-psk"[4] Y.Y.Y.252:64916 STATE_QUICK_R2 (IPsec SA established); 
  EVENT_SA_EXPIRE in 28561s; 
  newest IPSEC; 
  eroute owner; 
  isakmp#4; 
  idle;
#5: 
  "l2tp-psk"[4] Y.Y.Y.252 esp.ed47405@Y.Y.Y.252 esp.16d48007@172.17.0.2 Traffic: 
  ESPin=3KB ESPout=847B! ESPmax=4194303B 
 
Bare Shunt list:
 
■ipsec-vpn-serverのトラフィックログ

$ docker exec -it ipsec-vpn-server ipsec whack --trafficstatus | sed -e 's/[0-9]*\.[0-9]*\.[0-9]*\.252/Y.Y.Y.252/g'
006 #5: "l2tp-psk"[4] Y.Y.Y.252, type=ESP, add_time=1614098929, inBytes=4585, outBytes=981, id='Z.Z.Z.Z'

■Androidからイントラ内のWebサーバに接続してみる。

$ echo "[VPN-Client Z.Z.Z.Z] -- [X.X.X.X gw Y.Y.Y.252] -- [Y.Y.Y.92 host 172.17.0.1] \
  -- [ipsec-vpn-server 172.17.0.2],[Y.Y.Y.100 Web-server]" | \
  graph-easy --dot | dot -T svg -o vpn.png

$ sudo awk '/[0-9]*\/[0-9]*\.[0-9]*\.92/{gsub(" \042","\n&",$0);print $0}' /var/log/apache2/access.log
Y.Y.Y.92 - - [24/Feb/2021:01:56:42 +0900]
 "GET / HTTP/1.1" 401 815
 "android-app://com.google.android.googlequicksearchbox/"
 "Mozilla/5.0 (Linux; Android 8.0.0; ASUS_X00PD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36"
Y.Y.Y.92 - basic-auth [24/Feb/2021:01:56:54 +0900]
 "GET / HTTP/1.1" 200 5378
 "android-app://com.google.android.googlequicksearchbox/"
 "Mozilla/5.0 (Linux; Android 8.0.0; ASUS_X00PD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36"
Y.Y.Y.92 - basic-auth [24/Feb/2021:01:56:55 +0900]
 "GET /web.css HTTP/1.1" 200 849
 "http://Y.Y.Y.100/"
 "Mozilla/5.0 (Linux; Android 8.0.0; ASUS_X00PD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36"
Y.Y.Y.92 - basic-auth [24/Feb/2021:01:56:55 +0900]
 "GET /labunix.png HTTP/1.1" 200 5928
 "http://Y.Y.Y.100/"
 "Mozilla/5.0 (Linux; Android 8.0.0; ASUS_X00PD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36"
Y.Y.Y.92 - - [24/Feb/2021:01:57:46 +0900]
 "-" 408 0
 "-"
 "-"

■WAN側のポリシーを作成、DDNSの名前でVPN接続してみる。
 ※接続元のIPはfromJPでGeoIPの国内からのみに制限している。

# show firewall vip WAN-VPN_500 
config firewall vip
    edit "WAN-VPN_500"
        set uuid XXXXX
        set extintf "wan"
        set portforward enable
        set mappedip "172.17.0.2"
        set protocol udp
        set extport 500
        set mappedport 500
    next
end

# show firewall vip WAN-VPN_4500 
config firewall vip
    edit "WAN-VPN_4500"
        set uuid XXXXX
        set extintf "wan"
        set portforward enable
        set mappedip "172.17.0.2"
        set protocol udp
        set extport 4500
        set mappedport 4500
    next
end

# show firewall policy 19
config firewall policy
    edit 19
        set name "WAN-VPN"
        set uuid XXXXX
        set srcintf "wan"
        set dstintf "lan"
        set srcaddr "fromJP"
        set dstaddr "WAN-VPN_4500" "WAN-VPN_500"
        set action accept
        set schedule "always"
        set service "VPN"
        set nat enable
    next
end

■GWが同じなので、statusはDMZからの接続と違いは無い。

$ docker exec -it ipsec-vpn-server ipsec status | \
    sed -e 's/^000 //g' | tail -n 12 | \
    sed -e 's/[,:;] /&\n  /g' | sed -e 's/[0-9]*\.[0-9]*\.[0-9]*\.252/Y.Y.Y.252/g'
Total IPsec connections: 
  loaded 4, 
  active 1
 
State Information: 
  DDoS cookies not required, 
  Accepting new IKE connections
IKE SAs: 
  total(1), 
  half-open(0), 
  open(0), 
  authenticated(1), 
  anonymous(0)
IPsec SAs: 
  total(1), 
  authenticated(1), 
  anonymous(0)
 
#6: 
  "l2tp-psk"[6] Y.Y.Y.252:53976 STATE_MAIN_R3 (IKE SA established); 
  EVENT_SA_EXPIRE in 28763s; 
  newest ISAKMP; 
  lastdpd=5s(seq in:12182 out:0); 
  idle;
#7: 
  "l2tp-psk"[6] Y.Y.Y.252:53976 STATE_QUICK_R2 (IPsec SA established); 
  EVENT_SA_EXPIRE in 28764s; 
  newest IPSEC; 
  eroute owner; 
  isakmp#6; 
  idle;
#7: 
  "l2tp-psk"[6] Y.Y.Y.252 esp.684272a@Y.Y.Y.252 esp.69c4b187@172.17.0.2 Traffic: 
  ESPin=45KB ESPout=608B! ESPmax=4194303B 
 
Bare Shunt list:

■trafficstatusのZ.Z.Z.ZはAndroidのキャリアのグローバルIPになる。

$ docker exec -it ipsec-vpn-server ipsec whack --trafficstatus | sed -e 's/[0-9]*\.[0-9]*\.[0-9]*\.252/Y.Y.Y.252/g'
006 #7: "l2tp-psk"[6] Y.Y.Y.252, type=ESP, add_time=1614101840, inBytes=77030, outBytes=688, id='Z.Z.Z.Z'

■WebサーバのログもDMZからのアクセスと違いはない。

$ sudo awk '/[0-9]*\.[0-9]*\.[0-9]*\.92/&& !/2021:01:56/ \
    {if($3 ~ /[A-Za-z0-9]/) \
      {gsub(".*","basic-auth",$3);gsub(" \042","\n&",$0);print $0} \
  else{gsub(" \042","\n&",$0);print $0}}' /var/log/apache2/access.log | \
  sed -e 's/[0-9]*\.[0-9]*\.[0-9]*\.100/Y.Y.Y.100/g' -e 's/[0-9]*\.[0-9]*\.[0-9]*\.92/Y.Y.Y.92/g'
Y.Y.Y.92 - - [24/Feb/2021:01:57:46 +0900]
 "-" 408 0
 "-"
 "-"
Y.Y.Y.92 - - [24/Feb/2021:02:42:50 +0900]
 "GET / HTTP/1.1" 401 815
 "android-app://com.google.android.googlequicksearchbox/"
 "Mozilla/5.0 (Linux; Android 8.0.0; ASUS_X00PD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36"
Y.Y.Y.92 - basic-auth [24/Feb/2021:02:43:01 +0900]
 "GET / HTTP/1.1" 200 5378
 "android-app://com.google.android.googlequicksearchbox/"
 "Mozilla/5.0 (Linux; Android 8.0.0; ASUS_X00PD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36"
Y.Y.Y.92 - basic-auth [24/Feb/2021:02:43:02 +0900]
 "GET /labunix.png HTTP/1.1" 200 5928
 "http://Y.Y.Y.100/"
 "Mozilla/5.0 (Linux; Android 8.0.0; ASUS_X00PD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36"
Y.Y.Y.92 - - [24/Feb/2021:02:43:52 +0900]
 "-" 408 0
 "-"
 "-"

■ipsec-vpn-serverのIPは変わる可能性があるので、
 dockerホストのポート転送を使用するように設定変更を行う。

■まずはDMZ

# show firewall vip DMZ-VPN | grep 172
        set mappedip "172.17.0.2"

# config firewall vip
  edit DMZ-VPN
  set mappedip "Y.Y.Y.92"
  end

# show firewall vip DMZ-VPN | grep [0-9].92
        set mappedip "Y.Y.Y.92"

$ docker exec -it ipsec-vpn-server ipsec whack --trafficstatus | \
    sed -e 's/[0-9]*\.[0-9]*\.[0-9]*\.252/Y.Y.Y.252/g' -e 's/[0-9]*\.[0-9]*\.[0-9]*\.223/Z.Z.Z.223/g'
006 #9: "l2tp-psk"[8] Y.Y.Y.252, type=ESP, add_time=1614179787, inBytes=63257, outBytes=15064, id='Z.Z.Z.223'

$ sudo awk '/[0-9]*\.[0-9]*\.[0-9]*\.92/&& !/2021:01:56|2021:02:4[23]/ \
      {if($3 ~ /[A-Za-z0-9]/){gsub(".*","basic-auth",$3);gsub(" \042","\n&",$0);print $0} \
  else{gsub(" \042","\n&",$0);print $0}}' /var/log/apache2/access.log | \
  sed -e 's/[0-9]*\.[0-9]*\.[0-9]*\.100/Y.Y.Y.100/g' -e 's/[0-9]*\.[0-9]*\.[0-9]*\.92/Y.Y.Y.92/g' | \
  lsec -sep ^[A-Z0-9] "/ HTTP/1.1. 200 "
Y.Y.Y.92 - basic-auth [25/Feb/2021:00:17:07 +0900]
 "GET / HTTP/1.1" 200 5378
 "android-app://com.google.android.googlequicksearchbox/"
 "Mozilla/5.0 (Linux; Android 8.0.0; ASUS_X00PD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36"

■そしてWAN

# show firewall vip WAN-VPN_500 | grep 172
        set mappedip "172.17.0.2"

# show firewall vip DMZ-VPN | grep 172
        set mappedip "172.17.0.2"

# config firewall vip
  edit WAN-VPN_500
  set mappedip "Y.Y.Y.92"
  next
  edit WAN-VPN_4500
  set mappedip "Y.Y.Y.92"
  end

$ docker exec -it ipsec-vpn-server ipsec whack --trafficstatus | \
    sed -e 's/[0-9]*\.[0-9]*\.[0-9]*\.252/Y.Y.Y.252/g' -e 's/[0-9]*\.[0-9]*\.[0-9]*\.198/Z.Z.Z.198/g'
006 #19: "l2tp-psk"[11] Y.Y.Y.252, type=ESP, add_time=1614180508, inBytes=9705, outBytes=26136, id='Z.Z.Z.198'

$ sudo awk '/[0-9]*\.[0-9]*\.[0-9]*\.92/&& !/2021:01:56|2021:02:4[23]|2021:00:17/ \
      {if($3 ~ /[A-Za-z0-9]/){gsub(".*","basic-auth",$3);gsub(" \042","\n&",$0);print $0} \
  else{gsub(" \042","\n&",$0);print $0}}' /var/log/apache2/access.log | \
  sed -e 's/[0-9]*\.[0-9]*\.[0-9]*\.100/Y.Y.Y.100/g' -e 's/[0-9]*\.[0-9]*\.[0-9]*\.92/Y.Y.Y.92/g' | \
  lsec -sep ^[A-Z0-9] "/ HTTP/1.1. 200 "
Y.Y.Y.92 - basic-auth [25/Feb/2021:00:28:39 +0900]
 "GET / HTTP/1.1" 200 5377
 "android-app://com.google.android.googlequicksearchbox/"
 "Mozilla/5.0 (Linux; Android 8.0.0; ASUS_X00PD) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.181 Mobile Safari/537.36"

■ルーティングは不要になったので削除。
 ※DMZ-router側のルーティングも削除

# show router static 3 | grep 172.17
        set dst 172.17.0.0 255.255.0.0

# config router static
  delete 3
  end

■PSK共有キー、ユーザ名、パスワードの変更のために、
 ipsec-vpn-serverを終了する。
 また、併せてDMZ-VPN用とWAN-VPN用でセッションを分けるために、
 以下を参考に別々のユーザ、パスワードを設定する。

VPN_ADDL_USERS=additional_username_1 additional_username_2
VPN_ADDL_PASSWORDS=additional_password_1 additional_password_2

$ docker ps
CONTAINER ID        IMAGE                     COMMAND             CREATED             STATUS              PORTS                                          NAMES
bfb92615642d        hwdsl2/ipsec-vpn-server   "/opt/src/run.sh"   24 hours ago        Up 24 hours         0.0.0.0:500->500/udp, 0.0.0.0:4500->4500/udp   ipsec-vpn-server

$ docker stop ipsec-vpn-server
ipsec-vpn-server

$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

$ docker rm $(docker ps -a | awk '/ipsec-vpn-server/{print $1}')
bfb92615642d

$ awk -F'[ =]' '{gsub(".*","XXX",$2);print $1"="$2}' vpn.env 
VPN_IPSEC_PSK=XXX
VPN_USER=XXX
VPN_PASSWORD=XXX
VPN_ADDL_USERS=XXX
VPN_ADDL_PASSWORDS=XXX

■イメージ差し替え等のメンテナンスのために、dockerコマンドをスクリプト化しておく。

$ cat hwdsl2 
docker run \
    --name ipsec-vpn-server \
    --env-file ./vpn.env \
    --restart=always \
    -v ikev2-vpn-data:/etc/ipsec.d \
    -p 500:500/udp \
    -p 4500:4500/udp \
    -d --privileged \
    hwdsl2/ipsec-vpn-server

$ chmod +x hwdsl2
$ ./hwdsl2

■差し替え方法

$ docker stop ipsec-vpn-server

$ docker rm $(docker ps -a | awk '/ipsec-vpn-server/{print $1}')

$ docker rmi $(docker image ls | awk '/ipsec-vpn-server/{print $3}')

$ docker pull hwdsl2/ipsec-vpn-server

$ test vpn.env && ./hwdsl2

■普段は以下のように停止、起動、確認をする。

$ docker stop ipsec-vpn-server
$ docker start $(docker ps -a | awk '/ipsec-vpn-server/{print $1}')

$ docker inspect $(docker ps -a | awk '/ipsec-vpn-server/{print $1}') | jq -r '.[].State.Status'
running

$ sudo iptables -L -n 2>/dev/null| lsec -sep '^$' :[4]500

Chain DOCKER (1 references)
target     prot opt source               destination         
ACCEPT     udp  --  0.0.0.0/0            172.17.0.2           udp dpt:4500
ACCEPT     udp  --  0.0.0.0/0            172.17.0.2           udp dpt:500

$ docker exec -it ipsec-vpn-server ipsec status
$ docker exec -it ipsec-vpn-server ipsec whack --trafficstatus