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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 执行第一步所输出的
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 执行第一步所输出的
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:
在left的/etc/ipsec.conf加入记录:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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发起协商请求的):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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:
正常来说,后面构建L2TPv3后,1801端口有流量,StrongSwan会自动配置ip-xfrm,不过可以手动up试试是否成功(这里只能在left上进行,前面已提过right是无法主动向left发起协商请求的):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 执行第一步所输出的
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:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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的虚拟网卡:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
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是附带以太网头的,所以还可以桥接组网。