写在前面
咱最近又在折腾 IPSec VPN,配合一个 GRE Tunnel 之类的就可以快乐的跑动态路由协议了。
此篇主要介绍的是通过证书认证的 IKEv2 IPSec VPN。
按照一般的思路,此篇我们先开始配置点对点的连线,之后我们再来配置站点到站点的连线。
主要介绍 IPv4 下的IPSec,IPv6 IPSec 本篇不涉及,可以自行类比。
期间需要生成 CA ,并用 CA 签发服务端和客户端的证书。
如果需要 RouterOS 之间或者 Linux之间起 IPSec VPN 的话,可以参考 libreswan 和 RouterOS 各自的文档。此处不再讲述。
有些人可能会提到,使用 PSK 认证配起来会快很多,为什么不使用 PSK 作为认证方式。
这里要强调的是 PSK 认证现在一般是不建议了,这种方式是不太安全的。
环境
Linux 端使用 LibreSwan。RouterOS 则是自己本身就带一个 IPSec 的功能了。
使用 Linux(Debian) LibreSwan 4.10 作为服务端,RouterOS 7.10.1 作为客户端。
客户端可以拥有一个变动的 IP,而服务端的 IP 需要为静态,NAT traversal是可选的。
IP的配置见下表:
IP类型 |
ROS 客户端(Los Angeles) |
Linux 服务端(Toronto) |
本地环回地址 |
192.168.102.2/32 |
192.168.102.1/32 |
下接的子网 |
192.168.22.0/24 |
192.168.21.0/24 |
对下接口的IP |
192.168.22.1 |
192.168.21.1 |
对应的公网IP |
变动的 |
155.138.129.109 |
那我们现在就开始吧。
Pt.1 构建一个点对点的连线
Linux 服务端的证书生成与导入
- 建立一个储存CA,服务端和客户端的公钥与私钥的数据库。
1
2
3
4
5
6
7
8
|
root@vps-tor:~# mkdir ${HOME}/tmpdb
root@vps-tor:~# certutil -N -d sql:${HOME}/tmpdb
Enter a password which will be used to encrypt your keys.
The password should be at least 8 characters long,
and should contain at least one non-alphabetic character.
Enter new password:
Re-enter password:
|
- 开立一个自签名 CA
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
|
root@vps-tor:~# certutil -S -x -n "A simple CA" -s "O=Anon Networking Solutions,CN=A simple CA" -k rsa -g 4096 -v 12 -d sql:${HOME}/tmpdb -t "CT,," -2
Enter Password or Pin for "NSS Certificate DB":
A random seed must be generated that will be used in the
creation of your key. One of the easiest ways to create a
random seed is to use the timing of keystrokes on a keyboard.
To begin, type keys on the keyboard until this progress meter
is full. DO NOT USE THE AUTOREPEAT FUNCTION ON YOUR KEYBOARD!
Continue typing until the progress meter is full:
|************************************************************|
Finished. Press enter to continue:
Generating key. This may take a few moments...
Is this a CA certificate [y/N]?
y
Enter the path length constraint, enter to skip [<0 for unlimited path]: >
Is this a critical extension [y/N]?
N
|
- 为 VPN 服务端生成服务端证书
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
root@vps-tor:~# certutil -S -c "A simple CA" -n "vpn.example.com" -s "O=Example,CN=vpn.example.com" \
-k rsa -g 4096 -v 12 -d sql:${HOME}/tmpdb -t ",," -1 -6 -8 "vpn.example.com"
Enter Password or Pin for "NSS Certificate DB":
A random seed must be generated that will be used in the
creation of your key. One of the easiest ways to create a
random seed is to use the timing of keystrokes on a keyboard.
To begin, type keys on the keyboard until this progress meter
is full. DO NOT USE THE AUTOREPEAT FUNCTION ON YOUR KEYBOARD!
Continue typing until the progress meter is full:
|************************************************************|
Finished. Press enter to continue:
Generating key. This may take a few moments...
0 - Digital Signature
1 - Non-repudiation
2 - Key encipherment
3 - Data encipherment
4 - Key agreement
5 - Cert signing key
6 - CRL signing key
Other to finish
> 0
0 - Digital Signature
1 - Non-repudiation
2 - Key encipherment
3 - Data encipherment
4 - Key agreement
5 - Cert signing key
6 - CRL signing key
Other to finish
> 2
0 - Digital Signature
1 - Non-repudiation
2 - Key encipherment
3 - Data encipherment
4 - Key agreement
5 - Cert signing key
6 - CRL signing key
Other to finish
> 8
Is this a critical extension [y/N]?
n
0 - Server Auth
1 - Client Auth
2 - Code Signing
3 - Email Protection
4 - Timestamp
5 - OCSP Responder
6 - Step-up
7 - Microsoft Trust List Signing
Other to finish
> 0
0 - Server Auth
1 - Client Auth
2 - Code Signing
3 - Email Protection
4 - Timestamp
5 - OCSP Responder
6 - Step-up
7 - Microsoft Trust List Signing
Other to finish
> 1
0 - Server Auth
1 - Client Auth
2 - Code Signing
3 - Email Protection
4 - Timestamp
5 - OCSP Responder
6 - Step-up
7 - Microsoft Trust List Signing
Other to finish
> 8
Is this a critical extension [y/N]?
n
|
- 为客户端生成一个证书。
1
|
root@vps-tor:~# certutil -S -c "A simple CA" -n "rosclient1.example.com" -s "O=Example,CN=rosclient1.example.com" -k rsa -g 4096 -v 12 -d sql:${HOME}/tmpdb -t ",," -1 -6 -8 "rosclient1.example.com"
|
扩展同服务端证书,此处不再重复。
Tips: 我们可以检查现在这个数据库内有什么:
1
2
3
4
5
6
7
|
root@vps-tor:~# certutil -L -d sql:${HOME}/tmpdb/
Certificate Nickname Trust Attributes
SSL,S/MIME,JAR/XPI
A simple CA CTu,u,u
vpn.example.com u,u,u
rosclient1.example.com u,u,u
|
- 从这个数据库导出 pkcs12 格式的客户端证书。
1
2
3
4
5
|
root@vps-tor:~# pk12util -o rosclient1.example.com.p12 -n "rosclient1.example.com" -d sql:${HOME}/tmpdb/
Enter Password or Pin for "NSS Certificate DB":
Enter password for PKCS12 file:
Re-enter password:
pk12util: PKCS12 EXPORT SUCCESSFUL
|
如果使用rosclient1.example.com.p12导入失败或者导入不完全,可以尝试将这个文件丢进 openssl 滚一次,命令为
openssl pkcs12 -in rosclient1.example.com.p12 -out rosclient1.example.com-conv.p12
之后使用 rosclient1.example.com-conv.p12
这个文件导入即可。
- 从数据库导出 pkcs12 格式的服务端证书
1
2
3
4
5
|
root@vps-tor:~# pk12util -o vpn.example.com.p12 -n "vpn.example.com" -d sql:${HOME}/tmpdb/
Enter Password or Pin for "NSS Certificate DB":
Enter password for PKCS12 file:
Re-enter password:
pk12util: PKCS12 EXPORT SUCCESSFUL
|
- 导出 pkcs12 格式的 CA 证书并仅保留 certificate 部分。(可选)
1
2
3
4
5
6
7
|
root@vps-tor:~# pk12util -o cacert.p12 -n "A simple CA" -d sql:${HOME}/tmpdb/
Enter Password or Pin for "NSS Certificate DB":
Enter password for PKCS12 file:
Re-enter password:
pk12util: PKCS12 EXPORT SUCCESSFUL
root@vps-tor:~# openssl pkcs12 -clcerts -nokeys -out cacert.pem -in cacert.p12
Enter Import Password:
|
我们得到的是 cacert.pem,即不带私钥的 CA 证书,在 Android/iOS 上需要单独导入。
- 为服务端初始化NSS并导入服务端证书。
如之前已初始化 nss 则此步不需要再次初始化,直接导入即可。
1
2
3
4
5
6
7
|
root@vps-tor:~# ipsec initnss
Initializing NSS database
root@vps-tor:~# ipsec import vpn.example.com.p12
Enter password for PKCS12 file:
pk12util: PKCS12 IMPORT SUCCESSFUL
correcting trust bits for A simple CA
|
Linux 服务端环回接口的设定
新建一个 dummy 接口来承载自己的 loopback 地址。
1
2
3
4
|
root@vps-tor:~# ip link add dummy1 type dummy
root@vps-tor:~# ip address add 192.168.102.1/32 dev dummy1
root@vps-tor:~# ip link set dummy1 up
root@vps-tor:~# ip addr show dev dummy1
|
我们可以通过以下命令检查一下这个 dummy 接口
1
2
3
4
5
6
7
|
root@vps-tor:~# ip addr show dev dummy1
4: dummy1: <BROADCAST,NOARP,UP,LOWER_UP> mtu 1500 qdisc noqueue state UNKNOWN group default qlen 1000
link/ether 46:0b:c9:2b:4b:a0 brd ff:ff:ff:ff:ff:ff
inet 192.168.102.1/32 scope global dummy1
valid_lft forever preferred_lft forever
inet6 fe80::440b:c9ff:fe2b:4ba0/64 scope link
valid_lft forever preferred_lft forever
|
Linux 服务端 IKEv2 IPSec VPN 设定
在 /etc/ipsec.d/ 创建一个 ikev2-test.conf,写入 IPSec 配置文件。
文件内容如下:
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
|
conn ikev2-cp
# The server's actual IP goes here - not elastic IPs
left=155.138.129.109
leftcert=vpn.example.com
[email protected]
leftsendcert=always
leftsubnet=192.168.102.1/32
leftrsasigkey=%cert
# Clients
right=%any
rightsubnet=192.168.102.2/32
# optional rightid with restrictions
# rightid="C=CA, L=Toronto, O=Libreswan Project, OU=*, CN=*, E=*"
rightca=%same
rightrsasigkey=%cert
narrowing=yes
# recommended dpd/liveness to cleanup vanished clients
dpddelay=30
dpdtimeout=120
dpdaction=clear
auto=add
ikev2=insist
rekey=no
# ikev2 fragmentation support requires libreswan 3.14 or newer
fragmentation=yes
# optional PAM username verification (eg to implement bandwidth quota
# pam-authorize=yes
# create an ipsec interface for routing
ipsec-interface=yes
|
写入之后我们就可以添加这个连接了。
1
2
3
|
root@vps-tor:/etc/ipsec.d# ipsec auto --add ikev2-cp
conn: "ikev2-cp" warning IKEv2 liveness uses retransmit-timeout=, dpdtimeout= ignored
002 "ikev2-cp": added IKEv2 connection
|
Router OS 的证书导入
假定已经上传客户端证书到服务端。
导入并验证导入的完整性:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
[admin@MikroTik] > certificate import file-name=rosclient1.example.com-conv.p12
passphrase: ******
certificates-imported: 2
private-keys-imported: 1
files-imported: 1
decryption-failures: 0
keys-with-no-certificate: 0
[admin@MikroTik] > certificate print
Flags: K - PRIVATE-KEY; T - TRUSTED
Columns: NAME, COMMON-NAME, SUBJECT-ALT-NAME
# NAME COMMON-NAME SUBJECT-ALT-NAME
0 T rosclient1.example.com-conv.p12_0 A simple CA
1 KT rosclient1.example.com-conv.p12_1 rosclient1.example.com DNS:rosclient1.example.com
|
需要注意,此处应导入的是两个证书(一个CA和一个客户端证书)和一个(客户端证书的)私钥。
Router OS 的环回接口设定
此处我们将 ROS 上的 bridge 作为环回接口。
- 添加环回接口
1
2
3
4
5
6
7
|
[admin@MikroTik] > interface/ bridge/ add name=lo
[admin@MikroTik] > interface/ print
Flags: R - RUNNING
Columns: NAME, TYPE, ACTUAL-MTU, L2MTU, MAC-ADDRESS
# NAME TYPE ACTUAL-MTU L2MTU MAC-ADDRESS
0 R ether1 ether 1500 56:00:04:7C:A4:6C
1 R lo bridge 1500 65535 DE:E8:6D:8D:BF:94
|
- 向环回接口添加地址
1
2
3
4
5
6
7
|
[admin@MikroTik] > ip address/ add address=192.168.102.2/32 interface=lo
[admin@MikroTik] > ip address/ print
Flags: D - DYNAMIC
Columns: ADDRESS, NETWORK, INTERFACE
# ADDRESS NETWORK INTERFACE
0 D 45.76.66.50/23 45.76.66.0 ether1
1 192.168.102.2/32 192.168.102.2 lo
|
RouterOS 的 IKEv2 IPSec VPN 设定
- 配置 IPSec/Profile(对应 Phase 1)
1
2
3
4
5
6
7
8
|
[admin@MikroTik] > /ip/ipsec/profile/ add name=ikev2 hash-algorithm=sha256 enc-algorithm=aes-256 dh-group=modp2048
[admin@MikroTik] > /ip/ipsec/profile/ print
Flags: * - default
0 * name="default" hash-algorithm=sha1 enc-algorithm=aes-128,3des dh-group=modp2048,modp1024 lifetime=1d
proposal-check=obey nat-traversal=yes dpd-interval=2m dpd-maximum-failures=5
1 name="ikev2" hash-algorithm=sha256 enc-algorithm=aes-256 dh-group=modp2048 lifetime=1d proposal-check=obey
nat-traversal=yes dpd-interval=2m dpd-maximum-failures=5
|
- 配置 IPSec/Proposal(对应 Phase 2)
1
2
3
4
5
6
7
|
[admin@MikroTik] > /ip/ipsec/proposal/ add name=ikev2 enc-algorithms=aes-256-gcm,chacha20poly1305 pfs-group=modp2048 auth-algorithms=""
[admin@MikroTik] > /ip/ipsec/proposal/ print
Flags: X - disabled; * - default
0 * name="default" auth-algorithms=sha1 enc-algorithms=aes-256-cbc,aes-192-cbc,aes-128-cbc lifetime=30m
pfs-group=modp1024
1 name="ikev2" auth-algorithms="" enc-algorithms=chacha20poly1305,aes-256-gcm lifetime=30m pfs-group=modp2048
|
- 配置 IPSec/Peer
1
2
3
4
|
[admin@MikroTik] > /ip/ipsec/peer/ add name=tor-server1 address=155.138.129.109 profile=ikev2 send-initial-contact=yes exchange-mode=ike2
[admin@MikroTik] > /ip/ipsec/peer/ print
Flags: X - disabled; D - dynamic; R - responder
0 name="tor-server1" address=155.138.129.109/32 profile=ikev2 exchange-mode=ike2 send-initial-contact=yes
|
- 配置 IPSec/Identity
1
2
3
4
5
|
[admin@MikroTik] > /ip/ipsec/identity/ add peer=tor-server1 auth-method=digital-signature certificate=rosclient1.example.com-conv.p12_1 match-by=remote-id
[admin@MikroTik] > /ip/ipsec/identity/ print
Flags: D - dynamic; X - disabled
0 peer=tor-server1 auth-method=digital-signature certificate=rosclient1.example.com-conv.p12_1
generate-policy=no
|
需注意,此处的 certificate 项目的值为带私钥的客户端证书,可以使用 certificate print
命令检查。
- 配置 IPSec/Policy
1
|
[admin@MikroTik] > /ip/ipsec/policy/ add peer=tor-server1 tunnel=yes src-address=192.168.102.2/32 dst-address=192.168.102.1/32 action=encrypt level=unique proposal=ikev2
|
tunnel mode 可以隐藏通过 IPSec 发送的 IP 报文头部,而 transport mode 则会暴露源 IP 报文的头部。具体参考此处
验证连接是否已建立
- 通过 ping 验证一下是否打通
1
2
3
4
5
6
|
[admin@MikroTik] > ping src-address=192.168.102.2 address=192.168.102.1
SEQ HOST SIZE TTL TIME STATUS
0 192.168.102.1 56 64 62ms905us
1 192.168.102.1 56 64 62ms721us
2 192.168.102.1 56 64 62ms734us
sent=3 received=3 packet-loss=0% min-rtt=62ms721us avg-rtt=62ms786us max-rtt=62ms905us
|
Linux这边:
1
2
3
4
5
6
7
8
9
10
|
root@vps-tor:~# ping -I 192.168.102.1 192.168.102.2
PING 192.168.102.2 (192.168.102.2) from 192.168.102.1 : 56(84) bytes of data.
64 bytes from 192.168.102.2: icmp_seq=1 ttl=64 time=62.7 ms
64 bytes from 192.168.102.2: icmp_seq=2 ttl=64 time=62.7 ms
64 bytes from 192.168.102.2: icmp_seq=3 ttl=64 time=62.7 ms
64 bytes from 192.168.102.2: icmp_seq=4 ttl=64 time=62.6 ms
^C
--- 192.168.102.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 62.645/62.664/62.688/0.015 ms
|
- 通过检查状态来验证
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
[admin@MikroTik] > /ip/ipsec/policy/ print
Flags: T - TEMPLATE; A - ACTIVE; * - DEFAULT
Columns: PEER, TUNNEL, SRC-ADDRESS, DST-ADDRESS, PROTOCOL, ACTION, LEVEL, PH2-COUNT
# PEER TUNNEL SRC-ADDRESS DST-ADDRESS PROTOCOL ACTION LEVEL PH2-COUNT
0 T * ::/0 ::/0 all
1 A tor-server1 yes 192.168.102.2/32 192.168.102.1/32 all encrypt unique 1
[admin@MikroTik] > ip/ipsec/installed-sa/ print
Flags: H - HW-AEAD; E - ESP
Columns: SPI, STATE, SRC-ADDRESS, DST-ADDRESS, ENC-ALGORITHM, ENC-KEY-SIZE
# SPI STATE SRC-ADDRESS DST-ADDRESS ENC-ALGORITHM ENC-KEY-SIZE
0 HE 0x96EA214 mature 155.138.129.109 45.76.66.50 aes-gcm 288
1 HE 0xC7D3BC01 mature 45.76.66.50 155.138.129.109 aes-gcm 288
[admin@MikroTik] > ip/ipsec/active-peers/ print
Columns: ID, STATE, UPTIME, PH2-TOTAL, REMOTE-ADDRESS
# ID STATE UPTIME PH2-TOTAL REMOTE-ADDRESS
0 vpn.example.com established 7m33s 1 155.138.129.109
|
Linux 这边:
1
2
|
root@vps-tor:~# ipsec trafficstatus
006 #6: "ikev2-cp"[3] 45.76.66.50, type=ESP, add_time=1688217852, inBytes=672, outBytes=672, maxBytes=2^63B, id='CN=rosclient1.example.com, O=Example'
|
可以看到对方有回包,并且也显示连接已建立,并且存在 in 和 out bytes。
Pt.2 构建一个站点到站点的连线
Note: 需要完成 Pt.1 才能从这里开始喔~
修改 Linux 服务端的 IPSec 配置文件
配置文件中,left为服务端,right为客户端。需要修改的是rightsubnet和leftsubnet。
rightsubnet改为right(也就是客户端下)挂的子网(subnet),leftsubnet改为left(也就是服务端下)挂的子网(subnet)。
此处修改的结果为:
1
2
|
leftsubnet=192.168.21.0/24
rightsubnet=192.168.22.0/24
|
Tips: 可以偷懒把服务端配置的rightsubnet和leftsubnet都改成0.0.0.0/0。这样就可以在RouterOS客户端的 policy 中更加灵活地配置了。
修改 RouterOS 客户端的 IPSec Policy
客户端这边则是反过来的。将 src-address 改为客户端下挂的子网,dst-address改为服务端下挂的子网即可。
改完之后可以两边都验证一下。
1
2
3
4
5
6
7
8
|
[admin@MikroTik] > ip/ipsec/policy/ edit number=1 src-address
[admin@MikroTik] > ip/ipsec/policy/ edit number=1 dst-address
[admin@MikroTik] > ip/ipsec/policy/ p
Flags: T - TEMPLATE; A - ACTIVE; * - DEFAULT
Columns: PEER, TUNNEL, SRC-ADDRESS, DST-ADDRESS, PROTOCOL, ACTION, LEVEL, PH2-COUNT
# PEER TUNNEL SRC-ADDRESS DST-ADDRESS PROTOCOL ACTION LEVEL PH2-COUNT
0 T * ::/0 ::/0 all
1 A tor-server1 yes 192.168.22.0/24 192.168.21.0/24 all encrypt unique 1
|
- 在 RouterOS 上 ping Linux 服务端下挂的子网。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
[admin@MikroTik] > ping src-address=192.168.22.1 address=192.168.21.1
SEQ HOST SIZE TTL TIME STATUS
0 192.168.21.1 56 64 63ms14us
1 192.168.21.1 56 64 62ms841us
2 192.168.21.1 56 64 62ms830us
sent=3 received=3 packet-loss=0% min-rtt=62ms830us avg-rtt=62ms895us max-rtt=63ms14us
[admin@MikroTik] > ping src-address=192.168.22.1 address=192.168.21.2
SEQ HOST SIZE TTL TIME STATUS
0 192.168.21.2 56 63 62ms709us
1 192.168.21.2 56 63 62ms679us
2 192.168.21.2 56 63 62ms748us
sent=3 received=3 packet-loss=0% min-rtt=62ms679us avg-rtt=62ms712us max-rtt=62ms748us
|
Linux 服务端处测试同理,此处不再重复。
一些提示
- Windows 和 iOS 的客户端对于 phase1 和 phase2 使用的加密方式和校验方式较为挑剔,可以参考 LibreSwan 的文档
- 如果需要自动分地址池,并从远端上网的话可以参考下面的第一个和第二个链接,需要设置 mode-config 以及 NAT规则,设置 modeconfig 之后服务端的 leftsubnet 和 rightaddresspool 就起决定客户端分配的 IPSec 内部 IP 和客户端所选择走 IPSec 的路由的作用了。
- 不想生成 CA 的话,可以在 LibreSwan 之间使用 rsasig 认证。咱找了 RouterOS 文档没有看到它支持这种认证方式。LibreSwan 之间想更安全一些的话可以加 PPK,但是也是实验性质的,别的 vendor 也不太可能支持。
- IPSec 只能 IPv4 的端点转发 IPv4 的包,IPv6 的端点转发 IPv6 的包,这是由它的特性决定的。如果需要更灵活的话可以尝试 GRE over IPSec 或者直接配置 WireGuard。
- 证书生成步骤中的 -v 选项后跟的是有效期(validity period)的月数,可以根据情况自行调整。
- 如果配置完成后无法建立连线,日志是你的好帮手!
参考资料
- [LibreSwan] VPN server for remote clients using IKEv2
- IPSec - RouterOS - MikroTik Documentation
- RedHat Customer Portal - Chapter 6. Configuring a VPN with IPsec
- How to convert a .P12 certificate into a .PEM Certificate.