Unix Like, VPN, 网络

Linux L2TPv3以及ip-xfrm的配置

L2TPv3:http://man7.org/linux/man-pages/man8/ip-l2tp.8.html

ip-xfrm:http://man7.org/linux/man-pages/man8/ip-xfrm.8.html

这两个都是kernel内置的功能,通过这两个可以直接构建加密的VPN。

本文尝试使用ip-xfrm创建加密的隧道,并基于此隧道构建L2TPv3 VPN。

1. 生成密钥与ID

不使用StrongSwan,手动配置ip-xfrm时需要用到:

HASH_KEY=0x`dd if=/dev/urandom count=32 bs=1 2> /dev/null| xxd -p -c 64`
ENCRYPT_KEY=0x`dd if=/dev/urandom count=32 bs=1 2> /dev/null| xxd -p -c 64`
ID=0x`dd if=/dev/urandom count=4 bs=1 2> /dev/null| xxd -p -c 8`
echo -e "HASH_KEY=${HASH_KEY}\nENCRYPT_KEY=${ENCRYPT_KEY}\nID=${ID}"

复制上面四行命令后输出的内容,粘贴到left和right上运行,用于设置变量

2. 配置ip-xfrm

这里得分两种情况,一种是left和right都不经过NAT直接持有公网IP,另一种是left或者right任何一端经过NAT。

为什么要这样区分?因为ip-xfrm传输加密报文使用的协议ESP是L3的,一般情况下无法NAT,需要使用L4封装才能正常使用;其次,L4被SNAT后的源端口(source port),可能会被改变。

下面以UDP端口1801作为L2TPv3的通讯端口,对ip-xfrm进行配置。

2.1 不经过NAT

拓扑如下:

首先在left配置ip-xfrm:

# 执行第一步所输出的
root@left:~# HASH_KEY=...
root@left:~# ENCRYPT_KEY=...
root@left:~# ID=...
# 设置IP地址变量
root@left:~# SOURCE_IP="192.168.1.1"
root@left:~# DESTINATION_IP="192.168.2.1"
root@left:~# L2TPV3_PORT="1801"

root@left:~# ip xfrm state add src ${SOURCE_IP} dst ${DESTINATION_IP} \
    proto esp spi ${ID} reqid ${ID} mode transport \
    replay-window 0 \
    auth sha1 ${HASH_KEY} \
    enc aes ${ENCRYPT_KEY}
root@left:~# ip xfrm state add src ${DESTINATION_IP} dst ${SOURCE_IP} \
    proto esp spi ${ID} reqid ${ID} mode transport \
    replay-window 32 \
    auth sha1 ${HASH_KEY} \
    enc aes ${ENCRYPT_KEY}

root@left:~# ip xfrm policy add src ${SOURCE_IP} dst ${DESTINATION_IP} proto udp sport ${L2TPV3_PORT} dport ${L2TPV3_PORT} \
    dir out tmpl src 0.0.0.0 dst 0.0.0.0 proto esp reqid ${ID} mode transport
root@left:~# ip xfrm policy add src ${DESTINATION_IP} dst ${SOURCE_IP} proto udp sport ${L2TPV3_PORT} dport ${L2TPV3_PORT} \
    dir out tmpl src 0.0.0.0 dst 0.0.0.0 proto esp reqid ${ID} mode transport

然后配置right:

# 执行第一步所输出的 
root@left:~# HASH_KEY=...
root@left:~# ENCRYPT_KEY=...
root@left:~# ID=...
# 设置IP地址变量
SOURCE_IP="192.168.2.1"
DESTINATION_IP="192.168.1.1"
L2TPV3_PORT="1801"

ip xfrm state add src ${SOURCE_IP} dst ${DESTINATION_IP} \
    proto esp spi ${ID} reqid ${ID} mode transport \
    replay-window 0 \
    auth sha1 ${HASH_KEY} \
    enc aes ${ENCRYPT_KEY}
ip xfrm state add src ${DESTINATION_IP} dst ${SOURCE_IP} \
    proto esp spi ${ID} reqid ${ID} mode transport \
    replay-window 32 \
    auth sha1 ${HASH_KEY} \
    enc aes ${ENCRYPT_KEY}

ip xfrm policy add src ${SOURCE_IP} dst ${DESTINATION_IP} proto udp sport ${L2TPV3_PORT} dport ${L2TPV3_PORT} \
    dir out tmpl src 0.0.0.0 dst 0.0.0.0 proto esp reqid ${ID} mode transport
ip xfrm policy add src ${DESTINATION_IP} dst ${SOURCE_IP} proto udp sport ${L2TPV3_PORT} dport ${L2TPV3_PORT} \
    dir out tmpl src 0.0.0.0 dst 0.0.0.0 proto esp reqid ${ID} mode transport

上面的配置命令,state是指定left到right的之间数据封装使用ESP协议,指定使用sha1算法进行签名,aes-128进行加密。policy指定当L2TPv3的UDP数据包在left和right之间传输时,使用刚刚在state里面配置的规则进行封装。

2.2 经过NAT

拓扑:

上图中left和right都被NAT过,这里需要注意:至少要保证left或者right的一个UDP端口能被对方访问,如果双方都被NAT,且路由器都没有映射端口到它们那,那这个VPN就无法构建。

这里假定right的路由把到2.3.5.6的流量都转发给了192.168.2.2。

NAT还有一个比较大的问题,就是SNAT后源端口可能会改变,假定left和right设定了ESP通过双方的UDP端口4500封装,如果left的kernel从源端口4500发送UDP封装的ESP数据包到2.3.5.6:4500,right收到的数据包的来源端口有可能不是4500,这样会内核就找不到对应的xfrm规则,不会对解除该ESP的封装。即使你通过抓包或者其它方法获取到了被SNAT后的端口,如果这个NAT记录一段时间后无任何流量,路由器会清理掉该记录,后面再SNAT的端口可能又是另一个了。所以,这就是为什么IPsec会有NAT-T。

不过,即使解决SNAT和NAT记录超时问题,也还不足,虽然可以直接手动配置espinudp的规则,但是还是得依赖外部程序,内核才会解除ESP的UDP封装。

2.2.1 方式一:使用StrongSwan配置ip-xfrm

StrongSwan的实现其实也是ip-xfrm,使用StrongSwan,需要开放UDP端口500和4500,这里假设right的路由已把这两个端口转发到right。

在left和right安装StrongSwan:

apt install strongswan

在left的/etc/ipsec.conf加入记录:

conn peer-2.3.5.6-1801
        left=192.168.1.2
        leftid="@left-id"
        right=2.3.5.6
        rightid="@right-id"
        leftprotoport=udp/1801
        rightprotoport=udp/1801
        ike=aes128-sha1-modp2048!
        keyexchange=ikev2
        reauth=no
        ikelifetime=28800s
        dpddelay=30s
        dpdtimeout=120s
        dpdaction=restart
        esp=aes128-sha1-modp2048!
        keylife=3600s
        rekeymargin=540s
        type=transport
        compress=no
        authby=secret
        auto=route
        keyingtries=%forever

在left的/etc/ipsec.secrets加入记录(把pre-shared-key改成你自己指定的密钥)

192.168.1.2 2.3.5.6 @left-id @right-id : PSK "pre-shared-key"

在right的/etc/ipsec.conf加入记录(这里的auto是add不是route,因为只有right的路由器是为right映射了端口的,left没有,right是无法主动向left发起协商请求的):

conn peer-1.2.3.2-1801
        left=192.168.2.2
        leftid="@right-id"
        right=1.2.3.2
        rightid="@left-id"
        leftprotoport=udp/1801
        rightprotoport=udp/1801
        ike=aes128-sha1-modp2048!
        keyexchange=ikev2
        reauth=no
        ikelifetime=28800s
        dpddelay=30s
        dpdtimeout=120s
        dpdaction=clear
        esp=aes128-sha1-modp2048!
        keylife=3600s
        rekeymargin=540s
        type=transport
        compress=no
        authby=secret
        auto=add
        keyingtries=%forever

在right的/etc/ipsec.secrets加入记录(left和right的PSK要一致):

192.168.2.2 1.2.3.2 @right-id @left-id : PSK "pre-shared-key"

 

注意,在StrongSwan的配置文件中的left和right并不代表拓扑图中的left和right,left是本机,right是对方;另外,ipsec.secrets的格式是:

left_ip right_ip left_id right_id : PSK "pre-shared-key"

上面StrongSwan加入的记录,大概意思就是该规则应用于left和right的udp端口1801之间的通讯,并且让内核在遇到匹配left和right 1801端口的通讯时自动配置ip-xfrm,此外,30秒进行一次心跳,120秒无回应则重建连接。

在两边都重启StrongSwan:

ipsec restart

正常来说,后面构建L2TPv3后,1801端口有流量,StrongSwan会自动配置ip-xfrm,不过可以手动up试试是否成功(这里只能在left上进行,前面已提过right是无法主动向left发起协商请求的):

root@left:~# ipsec up peer-2.3.5.6-1801
initiating IKE_SA peer-192.168.1.2-1801[4] to 2.3.5.6
generating IKE_SA_INIT request 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(FRAG_SUP) N(HASH_ALG) N(REDIR_SUP) ]
sending packet: from 192.168.1.2[500] to 2.3.5.6[500] (464 bytes)
received packet: from 2.3.5.6[500] to 192.168.1.2[500] (462 bytes)
parsed IKE_SA_INIT response 0 [ SA KE No N(NATD_S_IP) N(NATD_D_IP) N(FRAG_SUP) N(HASH_ALG) N(MULT_AUTH) ]
local host is behind NAT, sending keep alives
remote host is behind NAT
authentication of 'left' (myself) with pre-shared key
establishing CHILD_SA peer-2.3.5.6-1801
generating IKE_AUTH request 1 [ IDi N(INIT_CONTACT) IDr AUTH N(USE_TRANSP) SA TSi TSr N(MOBIKE_SUP) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_4_ADDR) N(MULT_AUTH) N(EAP_ONLY) ]
sending packet: from 192.168.1.2[4500] to 2.3.5.6[4500] (364 bytes)
received packet: from 2.3.5.6[4500] to 192.168.1.2[4500] (284 bytes)
parsed IKE_AUTH response 1 [ IDr AUTH N(USE_TRANSP) SA TSi TSr N(MOBIKE_SUP) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_4_ADDR) N(ADD_6_ADDR) ]
authentication of 'right-id' with pre-shared key successful
IKE_SA peer-2.3.5.6-1801[4] established between 192.168.1.2[left]...2.3.5.6[right-id]
scheduling rekeying in 27781s
maximum IKE_SA lifetime 28321s
CHILD_SA peer-2.3.5.6-1801{2} established with SPIs c903d293_i cbbfce0b_o and TS 192.168.1.2/32[udp/1801] === 2.3.5.6/32[udp/1801]
connection 'peer-2.3.5.6-1801' established successfully

2.2.2 方式二:手动配置ip-xfrm

下面的配置步骤,即使全部对了,应该也是无法通的,抓包可以看到封装为UDP的ESP数据包,但是内核不会解除封装,不过如果你left和right两边都运行StrongSwan(不需要进行任何配置),监听着4500端口,就可以通。

left:

# 执行第一步所输出的
root@left:~# HASH_KEY=...
root@left:~# ENCRYPT_KEY=...
root@left:~# ID=...
# 设置IP地址变量
root@left:~# SOURCE_IP="192.168.1.2"
root@left:~# DESTINATION_IP="2.3.5.6"
root@left:~# L2TPV3_PORT="1801"

root@left:~# ip xfrm state add src ${SOURCE_IP} dst ${DESTINATION_IP} proto esp spi ${ID} reqid ${ID} mode transport \
    replay-window 0 \
    encap espinudp 4500 4500 0.0.0.0 \
    auth sha1 ${HASH_KEY} enc aes ${ENCRYPT_KEY}
root@left:~# ip xfrm state add src ${DESTINATION_IP} dst ${SOURCE_IP} proto esp spi ${ID} reqid ${ID} mode transport \
    replay-window 32 \
    encap espinudp 4500 4500 0.0.0.0 \
    auth sha1 ${HASH_KEY} enc aes ${ENCRYPT_KEY}

root@left:~# ip xfrm policy add src ${SOURCE_IP} dst ${DESTINATION_IP} proto udp sport ${L2TPV3_PORT} dport ${L2TPV3_PORT} \
    dir out tmpl src 0.0.0.0 dst 0.0.0.0 proto esp reqid ${ID} mode transport
root@left:~# ip xfrm policy add src ${DESTINATION_IP} dst ${SOURCE_IP} proto udp sport ${L2TPV3_PORT} dport ${L2TPV3_PORT} \
    dir out tmpl src 0.0.0.0 dst 0.0.0.0 proto esp reqid ${ID} mode transport

right:

root@right:~# LEFT_ESPINUDP_PORT="4500" # 这里是left的4500端口被路由器SNAT后的端口,不一定是4500,可以先跳到第三步在left配置好L2TPv3并配置IP,产生流量并抓包查看SNAT后的端口,或者自己想办法获取吧

# 执行第一步所输出的
root@right:~# HASH_KEY=...
root@right:~# ENCRYPT_KEY=...
root@right:~# ID=...
# 设置IP地址变量
root@right:~# SOURCE_IP="192.168.2.2"
root@right:~# DESTINATION_IP="1.2.3.2"
root@right:~# L2TPV3_PORT="1801"

root@right:~# ip xfrm state add src ${SOURCE_IP} dst ${DESTINATION_IP} proto esp spi ${ID} reqid ${ID} mode transport \
    replay-window 0 \
    encap espinudp 4500 ${LEFT_ESPINUDP_PORT} 0.0.0.0 \
    auth sha1 ${HASH_KEY} enc aes ${ENCRYPT_KEY}
root@right:~# ip xfrm state add src ${DESTINATION_IP} dst ${SOURCE_IP} proto esp spi ${ID} reqid ${ID} mode transport \
    replay-window 32 \
    encap espinudp ${LEFT_ESPINUDP_PORT} 4500 0.0.0.0 \
    auth sha1 ${HASH_KEY} enc aes ${ENCRYPT_KEY}

root@right:~# ip xfrm policy add src ${SOURCE_IP} dst ${DESTINATION_IP} proto udp sport ${L2TPV3_PORT} dport ${L2TPV3_PORT} \
    dir out tmpl src 0.0.0.0 dst 0.0.0.0 proto esp reqid ${ID} mode transport
root@right:~# ip xfrm policy add src ${DESTINATION_IP} dst ${SOURCE_IP} proto udp sport ${L2TPV3_PORT} dport ${L2TPV3_PORT} \
    dir out tmpl src 0.0.0.0 dst 0.0.0.0 proto esp reqid ${ID} mode transport

3. 配置L2TPv3

在left和right上都执行一样的命令配置L2TPv3:

L2TP_ID="1000" # 如果有多个,就换成不同的ID

# 下面的三行变量设置,如果前面使用了StrongSwan,未设置的话这里再设置一次,否则直接跳过
SOURCE_IP="本地的IP" # 本地经NAT,是内网IP就填内网IP,无NAT就是公网IP
DESTINATION_IP="对方的IP" # 对方的公网IP
L2TPV3_PORT="1801"
ip l2tp add tunnel tunnel_id ${L2TP_ID} peer_tunnel_id ${L2TP_ID} \
    encap udp local ${SOURCE_IP} remote ${DESTINATION_IP} \
    udp_sport ${L2TPV3_PORT} udp_dport ${L2TPV3_PORT}
ip l2tp add session name l2tpeth100 tunnel_id ${L2TP_ID} session_id ${L2TP_ID} \
    peer_session_id ${L2TP_ID} # name后面的l2tpeth100是虚拟网卡的名称

无误的话,在left和right上执行ip link show会看到一个名为l2tpeth100的虚拟网卡:

root@left:~# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
...
9: l2tpeth100: <BROADCAST,MULTICAST> mtu 1404 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 86:d4:ea:0c:6a:dc brd ff:ff:ff:ff:ff:ff
root@left:~# ip link set l2tpeth100 up
root@left:~# ip addr add 192.168.200.1/24 dev l2tpeth100

root@right:~# ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
...
24: l2tpeth100: <BROADCAST,MULTICAST> mtu 1404 qdisc noop state DOWN mode DEFAULT group default qlen 1000
    link/ether 92:85:53:77:f1:b6 brd ff:ff:ff:ff:ff:ff
root@right:~# ip link set l2tpeth100 up
root@right:~# ip addr add 192.168.200.2/24 dev l2tpeth100
root@right:~# ping 192.168.200.1
PING 192.168.200.1 (192.168.200.1) 56(84) bytes of data.
64 bytes from 192.168.200.1: icmp_seq=1 ttl=64 time=1.54 ms
64 bytes from 192.168.200.1: icmp_seq=2 ttl=64 time=0.940 ms
64 bytes from 192.168.200.1: icmp_seq=3 ttl=64 time=1.00 ms
64 bytes from 192.168.200.1: icmp_seq=4 ttl=64 time=0.982 ms
64 bytes from 192.168.200.1: icmp_seq=5 ttl=64 time=1.22 ms
64 bytes from 192.168.200.1: icmp_seq=6 ttl=64 time=1.22 ms
^C
--- 192.168.200.1 ping statistics ---
6 packets transmitted, 6 received, 0% packet loss, time 5002ms
rtt min/avg/max/mdev = 0.940/1.153/1.548/0.211 ms
root@right:~# arp -an | grep l2tpeth100
? (192.168.200.1) at 86:d4:ea:0c:6a:dc [ether] on l2tpeth100

L2TPv3是附带以太网头的,所以还可以桥接组网。

105 Posts

自信、努力、活出精彩;以前未所见的颜色,绘大千世界!
View all posts

Leave a reply

Your email address will not be published. Required fields are marked *