How to Setup a IKEv2 VPN Server on CentOS 8

由于 Debian 8 官方不再支持,经过一番考虑和准备,最近将 VPS 的系统换成了 CentOS 8。Libreswan 的文档没有 VPN 时打不开,这就形成了一个死循环,所以将她换成了 StrongSwan 。StrongSwan 的文档个人觉得对新手不是很友好,我是看了好一会才看明白些,另外在支持 macOS,iOS,Windows 10 的过程中也遇到不少问题,所以这里记录下来,以备日后参考。

StrongSwan 的文档入口页有安装指南可用的配置示例。我个人建议先看下 Introduction to strongSwan,这对安装和配置很有帮助。

CentOS 8 中的 epel 仓库中有预编译的 strongswan 二进制安装包,我们直接安装就可以了。接下来就是配置,strongswan 有四种认证方式,出于安全和便捷的考虑,我选择了 Extensible Authentication Protocol (EAP)。

首先是准备证书。我们可以使用自签名证书或者通用证书颁发机构签发的证书,后者不需要在各个终端上安装自签名根证书,所以部署上更简单。

我们先来看看下自签名证书。

生成根证书:

1
2
3
cd /etc/strongswan/swanctl
strongswan pki --gen --outform pem > rsa/ca.pem
strongswan pki --self --ca --in rsa/ca.pem --type rsa --dn "C=CH, O=strongSwan, CN=strongSwan Root CA" --outform pem > x509ca/ca-cert.pem

生成服务器证书:

1
2
3
cd /etc/strongswan/swanctl
strongswan pki --gen --outform pem > rsa/server.pem
strongswan pki --pub --in rsa/server.pem --type rsa | strongswan pki --issue --cacert cacerts/ca.cert.pem --cakey rsa/ca.pem --dn "C=CH, O=strongSwan, CN=swan.tenneshop.com" --san swan.tenneshop.com --flag serverAuth --flag ikeIntermediate --outform pem > x509/server-cert.pem

再来看下使用 Let’s Encrypt 签发的证书。先按照官方指南安装并获取证书,然后安装到 StrongSwan:

1
2
3
4
5
6
7
8
9
10
11
# 根证书
cd /etc/strongswan/swanctl/x509ca
ln -s /etc/letsencrypt/live/www.example.com/chain.pem lets-encrypt-authority-x3-ca.pem

# 服务器证书
cd /etc/strongswan/swanctl/x509
ln -s /etc/letsencrypt/live/www.example.com/cert.pem www-example-com.pem

# 服务器证书私钥
cd /etc/strongswan/swanctl/private/
ln -s /etc/letsencrypt/live/www.example.com/privkey.pem www-example-com-privkey.pem

这里值得一提的是我们需要将 Let’s Encrypt 这个中间证书安装到 /etc/strongswan/swanctl/x509ca 目录下,不然 Windows 10 连接不成功。

两种方式我都折腾成功,因为现在 Let’s Encrypt 的免费证书很方便,考虑到便捷,最终我使用了通用证书颁发机构签发的证书。因为暂时没有找到关闭 ipsec 配置的方法,我将 ipsec.d 目录重命令为 ipsec-off.d 来 workaround。

其次是具体配置。新版本的 StrongSwan 推荐使用 swanctl.conf 来配置,而官方文档上 swanctl.conf 和 ipsec.conf 的配置信息杂糅在一起,网络上最大多是 ipsec.conf 的配置,从面向未来的角度考虑,我是完全使用 swanctl.conf ,这也让我有种神清气爽的感觉。我这张配置信息经过脱敏后如下:

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
# Section defining IKE connection configurations.
connections {

      ikev2-eap-mschapv2 {
          version = 2
          proposals = aes256-sha1-modp1024,aes128-sha1-modp1024,3des-sha1-modp1024
          rekey_time = 0s
          pools = primary-pool-ipv4, primary-pool-ipv6
          fragmentation = yes
          dpd_delay = 30s
      send_cert = always
          # dpd_timeout doesn't do anything for IKEv2. The general IKEv2 packet timeouts are used.
          local-1 {
                  certs = www-example-com.pem
                  id = @www.example.com
          }
          remote-1 {
                  auth = eap-mschapv2
                  # go ask the client for its eap identity.
                  eap_id = %any
          }
          children {
                  ikev2-eap-mschapv2 {
                      local_ts = 0.0.0.0/0,::/0
                      rekey_time = 0s
                      dpd_action = clear
              esp_proposals = aes256-sha1,aes256-sha1,3des-sha1
                  }
          }
      }
}

secrets {
  
  private-www {
      file = www-example-com-privkey.pem
  }

  eap-carol {
        id = carol
        secret = "carolspassword" 
   }

}

# Section defining named pools.
pools {
    primary-pool-ipv4 {
        addrs = 10.11.1.0/24
        dns = 8.8.8.8,8.8.4.4
    }
    primary-pool-ipv6 {
        addrs = fec1::0/16

    }
}

# Include config snippets
include conf.d/*.conf

再次是配置转发和防火墙。

开启 IPv4 转发

1
2
3
echo 1 > /proc/sys/net/ipv4/ip_forward
echo 0 > /proc/sys/net/ipv4/conf/all/accept_redirects
echo 0 > /proc/sys/net/ipv4/conf/all/send_redirects

这个是临时性的,如果想永久更改,则修改 /etc/sysctl.conf

1
2
3
net.ipv4.ip_forward = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0

允许 IPSec 端口监听

1
2
3
iptables -A INPUT -p udp --dport 500 --j ACCEPT
iptables -A INPUT -p udp --dport 4500 --j ACCEPT
iptables -A INPUT -p esp -j ACCEPT

允许 VPN 到外网的流量

1
iptables -t nat -A POSTROUTING -s 10.11.1.0/24 -o eth0 -j MASQUERADE

防火墙配置可以写到系统防火墙配置文件或利用开机启动脚本设置。我是参考鸟哥的做法,使用开机启动脚本设置:

防火墙设置脚本:

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
# /usr/local/virus/iptables/iptables.rule
#!/bin/bash

# Network card infos
EXTIF="eth0"
INIF="eth0"
INNET="10.11.1.0/24"

export EXTIF INIF INNET

# Reset rules
PATH=/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/sbin:/usr/local/bin
export PATH

iptables -F
iptables -X
iptables -Z
iptables -P INPUT DROP
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

# Allowed services
iptables -A INPUT -p tcp -i $EXTIF --dport 21 --sport 1024:65534 -j ACCEPT #FTP
iptables -A INPUT -p tcp -i $EXTIF --dport 22 --sport 1024:65534 -j ACCEPT #SSH
iptables -A INPUT -p tcp -i $EXTIF --dport 80 --sport 1024:65534 -j ACCEPT #HTTP
iptables -A INPUT -p tcp -i $EXTIF --dport 8080 --sport 1024:65534 -j ACCEPT #HTTP
iptables -A INPUT -p tcp -i $EXTIF --dport 443 --sport 1024:65534 -j ACCEPT #HTTPS
iptables -A INPUT -p tcp -i $EXTIF --dport 8443 --sport 1024:65534 -j ACCEPT #HTTPS
# Frp
iptables -A INPUT -p tcp -i $EXTIF --dport 8008 --sport 1024:65534 -j ACCEPT
iptables -A INPUT -p tcp -i $EXTIF --dport 48557 --sport 1024:65534 -j ACCEPT
iptables -A INPUT -p tcp -i $EXTIF --dport 9090 --sport 1024:65534 -j ACCEPT
iptables -A INPUT -p tcp -i $EXTIF --dport 48559 --sport 1024:65534 -j ACCEPT

# Reset nat rules

iptables -F -t nat
iptables -X -t nat
iptables -Z -t nat
iptables -t nat -P PREROUTING ACCEPT
iptables -t nat -P POSTROUTING ACCEPT
iptables -t nat -P OUTPUT ACCEPT

# Setup ip share if need
if [ "$INIF" != "" ]; then

  iptables -A INPUT -i $INIF -j ACCEPT

  echo "1" > /proc/sys/net/ipv4/ip_forward
  if [ "$INNET" != "" ]; then
      for innet in $INNET
      do
          iptables -t nat -A POSTROUTING -s $innet -o $EXTIF -j MASQUERADE
          # Redirect 80 to 8080, 443 to 8443 unless it from innet
          iptables -t nat -A PREROUTING -j REDIRECT -p tcp ! -s $innet --destination-port 80 --to-ports 8080
          iptables -t nat -A PREROUTING -j REDIRECT -p tcp ! -s $innet --destination-port 443 --to-ports 8443
      done
  fi

fi

开机启动脚本:

1
2
3
4
5
6
7
8
9
10
/etc/systemd/system/custom-iptables.service
[Unit]
Description=Custom firewall

[Service]
Type=oneshot
ExecStart=/bin/bash /usr/local/virus/iptables/iptables.rule

[Install]
WantedBy=multi-user.target

开启开机启动:systemctl enable custom-iptables.service

最后就是各终端连接。IKEv2 的连接很容易, macOS,iOS,Windows 10 都内置支持。

macOS 的具体连接为:System Preferences > Network > + > Interface: VPN, VPN Type: IKEv2, Service Name: your favorite service name ,最后填上服务器域名,Remote ID 和用户的帐号密码就能连接了,Remote ID 通常就是服务器域名。iOS 和 Windows 10 的连接和 macOS 类似。

问题解决

安装配置过程中可能会遇到各种各样的问题,这时候我们首先是要开启 StrongSwan 的调试日志,这样会输出更多信息帮助我们解决问题。

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
83
84
85
86
87
88
89
90
#/etc/strongswan/strongswan.d/charon-logging.conf

charon {

    # Section to define file loggers, see LOGGER CONFIGURATION in
    # strongswan.conf(5).
    filelog {

        # <name> may be the full path to the log file if it only contains
        # characters permitted in section names. Is ignored if path is
        # specified.
        # <name> {

            # Loglevel for a specific subsystem.
            # <subsystem> = <default>

            # If this option is enabled log entries are appended to the existing
            # file.
            # append = yes

            # Default loglevel.
            # default = 1

            # Enabling this option disables block buffering and enables line
            # buffering.
            # flush_line = no

            # Prefix each log entry with the connection name and a unique
            # numerical identifier for each IKE_SA.
            # ike_name = no

            # Optional path to the log file. Overrides the section name. Must be
            # used if the path contains characters that aren't allowed in
            # section names.
            # path =

            # Adds the milliseconds within the current second after the
            # timestamp (separated by a dot, so time_format should end with %S
            # or %T).
            # time_add_ms = no

            # Prefix each log entry with a timestamp. The option accepts a
            # format string as passed to strftime(3).
            # time_format =

        # }

  charon-debug-log {
                    # this setting is required with 5.7.0 and newer if the path contains dots
                    path = /var/log/charon_debug.log
                    time_format = %a, %Y-%m-%d, %H:%M:%S
                    default = 2
                    mgr = 0
                    net = 1
                    enc = 1
                    asn = 1
                    job = 1
                    ike_name = yes
                    append = no
                    flush_line = yes
            }

    }

    # Section to define syslog loggers, see LOGGER CONFIGURATION in
    # strongswan.conf(5).
    syslog {

        # Identifier for use with openlog(3).
        # identifier =

        # <facility> is one of the supported syslog facilities, see LOGGER
        # CONFIGURATION in strongswan.conf(5).
        # <facility> {

            # Loglevel for a specific subsystem.
            # <subsystem> = <default>

            # Default loglevel.
            # default = 1

            # Prefix each log entry with the connection name and a unique
            # numerical identifier for each IKE_SA.
            # ike_name = no

        # }

    }

}

其次是 swanctl 命令可以动态加载连接和密钥配置,以及查看加载的证书,是个不错的帮手。

1
2
3
swanctl --load-conns
swanctl --load-creds
swanctl --list-certs

有时日志也没输出什么有效信息,我们只能用有限的线索求助 Google 了。

Reference:

修改记录

  • 2020/09/07: 修改成 CentOS 8 上安装 StrongSwan
  • 2017/10/11: 第一次完成