strongSwan 的安装和配置

本文目标

在 Ubuntu 上安装配置 strongSwan,使用 letsencrypt 的 SSL 证书,并采用 FreeRADIUS 作为用户认证审计等。

使用 ss-redir(包含在 shadowsocks-libev) 转发非 CN 的 IP 的请求到 ss-server(包含在)。

本文测试环境

本文假设 strongSwan 的 hostname 为 vpn.example.net,IP 地址为 1.2.3.4,FreeRADIUS 的 hostname 为 radius.example.net。
所以在下面的代码中如果看到这些假设的值,请自行修改为合适自己的值。

安装

$ apt install strongswan strongswan-plugin-eap-radius strongswan-plugin-xauth-eap
$ apt install letsencrypt
$ letsencrypt certonly --standalone -d vpn.example.net -m john.doe@example.net --agree-tos

$ cp /etc/letsencrypt/live/vpn.example.net/cert.pem /etc/ipsec.d/certs/
$ cp /etc/letsencrypt/live/vpn.example.net/privkey.pem /etc/ipsec.d/private/
$ cp /etc/letsencrypt/live/vpn.example.net/chain.pem /etc/ipsec.d/cacerts/

/etc/crontab 增加:

30 0    1 * *   root    letsencrypt renew
30 1    1 * *   root    /root/bin/renew-cert.sh

其中 /root/bin/renew-cert.sh (chmod +x) 内容如下:

#!/bin/sh
from="/etc/letsencrypt/live/vpn.example.net"
to="/etc/ipsec.d"

cp "${from}/chain.pem"   "${to}/cacerts/chain.pem"
cp "${from}/cert.pem"    "${to}/certs/cert.pem"
cp "${from}/privkey.pem" "${to}/private/privkey.pem"

配置文件

/etc/sysctl.conf

开启 IPv4 包转发:

...
# Uncomment the next line to enable packet forwarding for IPv4
net.ipv4.ip_forward=1
...

/etc/ipsec.conf

# ipsec.conf - strongSwan IPsec configuration file

# basic configuration

config setup
        # strictcrlpolicy=yes
        uniqueids = no

conn %default
        dpdaction=clear
        forceencaps=yes
        leftsubnet=0.0.0.0/0
        right=%any 
        rightsourceip=192.168.100.2/24
        rightdns=1.2.3.4

conn Cisco-IPSec
        keyexchange=ikev1
        auto=add
        left=%defaultroute
        leftauth=psk
        rightauth=psk
        rightauth2=xauth
  
conn IKEv2
        keyexchange=ikev2
        auto=add
        fragmentation=yes
        compress=yes    
        leftcert=cert.pem
        leftsendcert=always
        rightauth=eap-radius
        eap_identity=%identity
  
# Windows and BlackBerry clients usually goes here
conn IKEv2-mschapv2
        also=IKEv2
  
# Apple clients usually goes here
conn IKEv2-mschapv2-apple
        also=IKEv2
        leftid=vpn.example.net

说明:

/etc/ipsec.secrets

# This file holds shared secrets or RSA private keys for authentication.

# RSA private key for this host, authenticating it to any other host
# which knows the public part.

: RSA privkey.pem
: PSK "the Great Wall"

/etc/strongswan.d/charon/eap-radius.conf

eap-radius {

    # Send RADIUS accounting information to RADIUS servers.
    accounting = yes

    # Close the IKE_SA if there is a timeout during interim RADIUS accounting
    # updates.
    # accounting_close_on_timeout = yes

    # Interval in seconds for interim RADIUS accounting updates, if not
    # specified by the RADIUS server in the Access-Accept message.
    # accounting_interval = 0

    # If enabled, accounting is disabled unless an IKE_SA has at least one
    # virtual IP. Only for IKEv2, for IKEv1 a virtual IP is strictly necessary.
    # accounting_requires_vip = no

    # Use class attributes in Access-Accept messages as group membership
    # information.
    # class_group = no

    # Closes all IKE_SAs if communication with the RADIUS server times out. If
    # it is not set only the current IKE_SA is closed.
    # close_all_on_timeout = no

    # Send EAP-Start instead of EAP-Identity to start RADIUS conversation.
    # eap_start = no

    # Use filter_id attribute as group membership information.
    # filter_id = no

    # Prefix to EAP-Identity, some AAA servers use a IMSI prefix to select the
    # EAP method.
    # id_prefix =

    # Whether to load the plugin. Can also be an integer to increase the
    # priority of this plugin.
    load = yes

    # NAS-Identifier to include in RADIUS messages.
    # nas_identifier = strongSwan

    # Port of RADIUS server (authentication).
    # port = 1812

    # Base to use for calculating exponential back off.
    # retransmit_base = 1.4

    # Timeout in seconds before sending first retransmit.
    # retransmit_timeout = 2.0

    # Number of times to retransmit a packet before giving up.
    # retransmit_tries = 4

    # Shared secret between RADIUS and NAS. If set, make sure to adjust the
    # permissions of the config file accordingly.
    secret = yourPassword

    # IP/Hostname of RADIUS server.
    server = radius.example.net

    # Number of sockets (ports) to use, increase for high load.
    # sockets = 1

    dae {

        # Enables support for the Dynamic Authorization Extension (RFC 5176).
        # enable = no

        # Address to listen for DAE messages from the RADIUS server.
        # listen = 0.0.0.0

        # Port to listen for DAE requests.
        # port = 3799

        # Shared secret used to verify/sign DAE messages. If set, make sure to
        # adjust the permissions of the config file accordingly.
        # secret =

    }

    forward {

        # RADIUS attributes to be forwarded from IKEv2 to RADIUS.
        # ike_to_radius =

        # Same as ike_to_radius but from RADIUS to IKEv2.
        # radius_to_ike =

    }

    # Section to specify multiple RADIUS servers.
    servers {

    }

    # Section to configure multiple XAuth authentication rounds via RADIUS.
    xauth {

    }

}

说明:

注意修改 secret 和 server 参数:

    # Shared secret between RADIUS and NAS. If set, make sure to adjust the
    # permissions of the config file accordingly.
    secret = yourPassword

    # IP/Hostname of RADIUS server.
    server = radius.example.net

/etc/strongswan.d/charon/xauth-eap.conf

xauth-eap {

    # EAP plugin to be used as backend for XAuth credential verification.
    backend = radius

    # Whether to load the plugin. Can also be an integer to increase the
    # priority of this plugin.
    load = yes

}

ss-redir

在 /etc/rc.local 的 exit 0 前添加如下代码,便于开机时自动建立监听于 1080 端口的转发,它将请求转发到 ss-server:

ss-redir -c /usr/local/etc/shadowsocks-libev/config.json -u -A -l 1080 -b 0.0.0.0 > /dev/null 2>&1 &

iptables

将下面的内容编写文件脚本文件 /root/bin/iptable.sh,需要 ipset (apt install ipset):

#!/bin/sh

# 转发客户端请求。注意网段和 ipsec.conf 里要一致。网卡使用外网网卡。
out_iface="eth1"
server_IP="1.2.3.4"
subnet="192.168.100.0/24"

# strongswan
iptables -A INPUT -p udp --dport 500 -j ACCEPT
iptables -A INPUT -p udp --dport 4500 -j ACCEPT

# Moved to /etc/sysctl.conf: net.ipv4.ip_forward=1
# 打开网卡转发功能。要把客户端的请求转发到公网网卡
# echo 1 > /proc/sys/net/ipv4/ip_forward
iptables -t nat -A POSTROUTING -s ${subnet} -o ${out_iface} -j MASQUERADE
# 允许转发
iptables -A FORWARD -s ${subnet} -j ACCEPT

[ -r chnroute.txt ] || curl 'http://ftp.apnic.net/apnic/stats/apnic/delegated-apnic-latest' | grep ipv4 | grep CN | awk -F\| '{ printf("%s/%d\n", $4, 32-log($5)/log(2)) }' > chnroute.txt

iptables -t nat -N SHADOWSOCKS

iptables -t nat -A SHADOWSOCKS -d $server_IP -j RETURN

# 内网网段
iptables -t nat -A SHADOWSOCKS -d 0.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 10.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 127.0.0.0/8 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 169.254.0.0/16 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 172.16.0.0/12 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 192.168.0.0/16 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 224.0.0.0/4 -j RETURN
iptables -t nat -A SHADOWSOCKS -d 240.0.0.0/4 -j RETURN

# 创建ipset列表
#ipset -exist create gfwlist hash:net
ipset -exist create chnroute hash:net
cat chnroute.txt | sudo xargs -I ip ipset -exist add chnroute ip

# gfwlist 走ss转发
#iptables -t nat -A SHADOWSOCKS -m set --match-set gfwlist dst -p tcp  -j REDIRECT --to-ports 1080

# 国内ip表直连
iptables -t nat -A SHADOWSOCKS -m set --match-set chnroute dst -j RETURN
# 其他连接走ss转发
iptables -t nat -A SHADOWSOCKS -p tcp  -j REDIRECT --to-ports 1080

iptables -t nat -A PREROUTING -s ${subnet} -p tcp -j SHADOWSOCKS

# ss-redir
iptables -A INPUT -p tcp -s ${subnet}--dport 1080 -j ACCEPT
iptables -A INPUT -p tcp --dport 1080 -j DROP

注意这段代码只是示意,请根据实际情况改进,比如定期更新 ipset chnroute 的 IP 列表,使用 iptables-persistent(apt install iptables-persistent) 来持久化 iptables 规则等。

在使用 iptables-persistent 时可能会用到的命令:

$ iptables-save > /etc/iptables/rules.v4
$ ip6tables-save > /etc/iptables/rules.v6

测试

$ ipsec start --nofork

在 PostgreSQL 数据库 radius 中执行如下 SQL 语句:

/* 用户名为 yourVPNUsername 的密码为 yourVPNPassword */
insert into radcheck(username, attribute, op, value) values('yourVPNUsername', 'Password', '==', 'yourVPNPassword');
/* 可以用来配置月流量为 1GiB */
/* insert into radcheck(username, attribute, op, value) values('yourVPNUsername', 'Max-Monthly-Traffic', ':=', '1024'); */
/* 控制组 plan-monthly-1GiB 的用户的并发会话为 2 */
insert into radgroupcheck(groupname, attribute, op, value) values('plan-monthly-1GiB', 'Simultaneous-Use', ':=', '2');
/* 限制组 plan-monthly-1GiB 的用户的月流量为 1024 MiB */
insert into radgroupcheck(groupname, attribute, op, value) values('plan-monthly-1GiB', 'Max-Monthly-Traffic', ':=', '1024');
/* 将用户 yourVPNUsername 添加到组 plan-monthly-1GiB */
insert into radusergroup(username, groupname) values('yourVPNUsername', 'plan-monthly-1GiB');

注意这里用到的 Max-Monthly-Traffic 是在在 FreeBSD 上安装 FreeRADIUS中定义的。

在 VPN 客户端可以使用 IKEv2 或者 Cisco IPSec 来连接。

IKEv2:
Server Address(服务器地址): vpn.example.net
Remote ID(远程 ID): vpn.example.net
Authentication Settings(鉴定设置): Username(用户名)
Username(用户名): yourVPNUsername
Password(密码): yourVPNPassword

Cisco IPSec:
Server Address(服务器地址): vpn.example.net
Account Name(帐户名称): yourVPNUsername
Password(密码): yourVPNPassword
Authentication Settings(鉴定设置):
Machine Authentication(机器鉴定):
Shared Secret(共享的密钥): the Great Wall
(Shared Secret 是配置在 /etc/ipsec.secrets 中的 PSK)

启动

service strongswan start

#cisco-ipsec, #freeradius, #ikev2, #ipset, #iptables, #iptables-persistent, #letsencrypt, #postgresql, #shadowsocks-libev, #ss-redir, #strongswan, #ubuntu