Linux 和 RouterOS 之间设定 IKEv2 IPSec VPN

写在前面

咱最近又在折腾 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 服务端的证书生成与导入

  1. 建立一个储存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:
  1. 开立一个自签名 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
  1. 为 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. 为客户端生成一个证书。
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
  1. 从这个数据库导出 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 这个文件导入即可。

  1. 从数据库导出 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
  1. 导出 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 上需要单独导入。

  1. 为服务端初始化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. 添加环回接口
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. 向环回接口添加地址
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 设定

  1. 配置 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
  1. 配置 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 
  1. 配置 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 
  1. 配置 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 命令检查。

  1. 配置 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 报文的头部。具体参考此处

验证连接是否已建立

  1. 通过 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. 通过检查状态来验证
 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
  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 服务端处测试同理,此处不再重复。

一些提示

  1. Windows 和 iOS 的客户端对于 phase1 和 phase2 使用的加密方式和校验方式较为挑剔,可以参考 LibreSwan 的文档
  2. 如果需要自动分地址池,并从远端上网的话可以参考下面的第一个和第二个链接,需要设置 mode-config 以及 NAT规则,设置 modeconfig 之后服务端的 leftsubnet 和 rightaddresspool 就起决定客户端分配的 IPSec 内部 IP 和客户端所选择走 IPSec 的路由的作用了。
  3. 不想生成 CA 的话,可以在 LibreSwan 之间使用 rsasig 认证。咱找了 RouterOS 文档没有看到它支持这种认证方式。LibreSwan 之间想更安全一些的话可以加 PPK,但是也是实验性质的,别的 vendor 也不太可能支持。
  4. IPSec 只能 IPv4 的端点转发 IPv4 的包,IPv6 的端点转发 IPv6 的包,这是由它的特性决定的。如果需要更灵活的话可以尝试 GRE over IPSec 或者直接配置 WireGuard。
  5. 证书生成步骤中的 -v 选项后跟的是有效期(validity period)的月数,可以根据情况自行调整。
  6. 如果配置完成后无法建立连线,日志是你的好帮手!

参考资料

  1. [LibreSwan] VPN server for remote clients using IKEv2
  2. IPSec - RouterOS - MikroTik Documentation
  3. RedHat Customer Portal - Chapter 6. Configuring a VPN with IPsec
  4. How to convert a .P12 certificate into a .PEM Certificate.
Built with Hugo
Theme Stack designed by Jimmy