搭建 StrongSwan VPN 连接家中局域网

StrongSwan 是 Linux 上 IPsec 的开源实现项目。是已停止的 FreeS/WAN 开源项目的一个分支,同属 FreeS/WAN 后代的还包括 OpenSwan 和 LibreSwan 。
首先,对 IPsec 有个简单的了解。IPsec 是一种安全 VPN 协议,用于端到端通信安全,连接多台计算机建立虚拟局域网。IPsec 位于 OSI 模型的第三层网络层,同属这层的是 IP、ICMP 这些协议,TCP 、UDP 是第四层传输层,想要在互联网中传输离不开互联网协议,所以 IPsec 常用 UDP 作为其传输协议,源端口和目标端口为 500。
IPsec 协议套件还包括 AH 身份验证标头协议、ESP 封装安全协议、SA 安全关联协议。IKEv2 是 IPsec 最新的密钥交换协议,用于建立 SA 安全关联。IPsec 包含两种模式,传输模式和隧道模式,传输模式最初旨在保护 IPv6主机到主机直接连接,隧道模式区别在于原始 IP 由 ESP 封装加密,再套上外部 IP 头。

StrongSwan 包含 swanctl.conf 和旧版 ipsec.conf 两种配置方式,文章中使用新版 swanctl 进行配置。

配置选择

根据 StrongSwan 的官方文档,目前 StrongSwan 支持与 Windows内置客户端认证的方式有三种:

  • 使用 RSA 签名 X.509 机器证书。
  • 使用 EAP-TLS X.509 用户证书。
  • 使用 EAP-MSCHAPv2 密码认证。

考虑到可能共享给朋友进行连接,配置密码认证更为方便,所以选择配置 EAP-MSCHAPv2 密码认证。

安装 StrongSwan

环境:Debian 12 。

sudo apt install charon-systemd strongswan-swanctl libcharon-extra-plugins strongswan-pki libstrongswan-extra-plugins libtss2-tcti-tabrmd0

生成自签名证书

RSA 签名的证书兼容性更强,接下来使用 StrongSwan 的 pki 工具生成证书。 生成网关证书的 san 字段需要更改,moon.strongswan.org 是限定的主机名, carol@strongswan.org 的邮箱,10.0.20.100 是 IP,最好把公网和内网 IP 都设置上。

# 生成 ca 密钥
pki --gen --type rsa --size 2048 --outform pem > strongswanKey.pem
# 生成 ca 证书
pki --self --ca --lifetime 3652 --in strongswanKey.pem \
--dn "C=CH, O=strongSwan, CN= My strongSwan Root CA" \
--outform pem > strongswanCert.pem
# 生成网关密钥
pki --gen --type rsa --size 2048 --outform pem > moonKey.pem
# 生成网关证书
pki --req --type priv --in moonKey.pem \
--dn "C=CH, O=strongswan, CN=moon.strongswan.org" \
--san moon.strongswan.org \
--san carol@strongswan.org \
--san 10.0.20.100 \
--outform pem > moonReq.pem
pki --issue --cacert strongswanCert.pem --cakey strongswanKey.pem \
--type pkcs10 \
--in moonReq.pem \
--serial 01 \
--lifetime 1826 \
--flag serverAuth \
--outform pem > moonCert.pem
pki --print --in moonCert.pem

拷贝证书到配置目录。

sudo cp moonKey.pem /etc/swanctl/private/moonKey.pem
sudo cp moonCert.pem /etc/swanctl/x509/moonCert.pem
sudo cp strongswanCert.pem /etc/swanctl/x509ca/strongswanCert.pem

使用 Let’s Encrypt 生成的证书

优点是不用导入 ca 证书。可以参考 Fedora + Alpine 部署 Frp 内网穿透 Cerbot 申请 SSL 证书,证书类型选 rsa 。 复制证书。

sudo cp fullchain.pem /etc/swanctl/x509/moonCert.pem
sudo cp privkey.pem /etc/swanctl/private/moonKey.pem
sudo cp chain.pem /etc/swanctl/x509ca/strongswanCert.pem

配置 swanctl

编辑 /etc/swanctl/swanctl.conf 。
secrets 为连接的用户账号密码,pools 是虚拟 IP 池,local.id 是限定的主机名。

connections {
  eap {
    proposals = 3des-aes128-aes192-aes256-sha1-sha256-sha384-modp1024,aes256-sha1-sha256-sha384-modp2048
    pools = ipv4
    send_cert = always

    local {
      auth = pubkey
      certs = moonCert.pem
      id = moon.strongswan.org
    }
    remote {
      auth = eap-mschapv2
      eap_id = %any
    }
    children {
      eap {
        local_ts = 0.0.0.0/0
      }
    }
  }
}

pools {
  ipv4 {
    addrs = 10.0.100.0/24
  }
}
secrets {
   eap {
      id = strongswan
      secret = strongswan123
   }
}

开启 IP 转发。

echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.conf
sudo sysctl -p 

开启 NAT

如果设置虚拟 IP 还需要开启网卡 NAT,10.0.100.0/24 为虚拟 IP 段,eth0 为网卡接口。

iptables -t nat -A POSTROUTING -s 10.0.100.0/24 -o eth0 -m policy --dir out --pol ipsec -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.0.100.0/24 -o eth0 -j MASQUERADE

DHCP

可以使用局域网内的 DHCP 分配 IP,而不是 NAT 的虚拟 IP。
修改 /etc/swanctl/swanctl.conf , pools 改成 dhcp 。

connections {
  eap {
    pools = dhcp
...

dhcp 配置文件 /etc/strongswan.d/charon/dhcp.conf。

# server = 255.255.255.255

dhcp server 默认是广播地址,可以指定自己局域网内的 dhcp 服务器地址。

开启 systemd 日志

编辑 /etc/strongswan.conf 。

charon-systemd {
  journal {
    default = 1
    ike = 2
    knl = 3
  }
}

实时观察日志。

sudo journalctl -f -u strongswan

启动 strongswan 服务

sudo systemctl start strongswan

配置 Windows 客户端

传输根 CA 证书 strongswanCert.pem 到 Windows 上,修改后缀为 crt。

双击证书 -> 安装证书 -> 存储位置 本地计算机 -> 将所有证书都放入下列存储 -> 受信任的根证书颁发机构 -> 完成。

设置 -> 网络和 Intelnet -> VPN -> 添加 VPN 连接 -> 填写域名或者 IP -> VPN类型 IKEv2 -> 用户名密码。 最后点击连接即可。

Frp 内网穿透

当 StrongSwan 部署在内网,没有公网 IP 时,可以通过 Frp 内网穿透来访问。
127.0.0.1 为 StrongSwan 所在 IP 。

[[proxies]]
name = "isakmp"
type = "udp"
localIP = "127.0.0.1"
localPort = 500
remotePort = 500

[[proxies]]
name = "ipsec-nat-t"
type = "udp"
localIP = "127.0.0.1"
localPort = 4500
remotePort = 4500

发表评论

目录