IPSec
A real beast.
1. Good reads
(我都没有看过)
"It's just an astonishingly-complex suite of protocols." – An Illustrated Guide to IPsec
Nftables 指南:packet flow ipsec expressions
2. Caveats
2.1. Let's Encrypt
Android 的 strongswan client 似乎需要:
- x509 下面的 cert 是 fullchain.pem
- x509ca 下面的 cert 是 ca cert(chain.pem)
iOS 的原生 IKEv2 Client 要求:
- certbot –key-type 选 rsa。现在默认是 ecdsa 了
- 按 chatgpt 的说法,iOS 支持 ecdsa 但是 必须要使用特定算法
- 但根据 https://github.com/strongswan/strongswan/discussions/1881 和 https://apple.stackexchange.com/questions/412089/ios-native-ikev2-client-and-ecdsa-server-certificates 看,iOS 可能根本不支持用 ecdsa 认证服务器
3. 安装和配置
可以参考 ../bootstrap/ansible/roles/networking/tasks/strongswan.yml。Archlinux 安装 strongswan ,Debian 安装 charon-systemd 。
Strongswan 有旧的 ipsec.conf 的配置形式和新的 charon 配置形式,我只会新的。
参考的 Server 配置:
# /etc/swanctl/conf.d/example.conf pools { v4pool { addrs = 172.31.2.0/24 } v6pool { addrs = fdff:ffff:ffff:fff0::1:2:0/112 } } connections { # '-eap' 的后缀是有含义的!换成别的不行,我不知道为什么,或许是和下面 secrets 里的 'eap-' 对应, # 也或许是必须就要这么写。 # 下面的 local 是本机侧, remote 是远端侧。 rw-eap { local_addrs = example.com pools = v4pool, v6pool local { auth = pubkey certs = example.com.pem id = example.com } remote { # 用户名+密码的登录方式 auth = eap-mschapv2 eap_id = %any } children { net { local_ts = 0.0.0.0/0, ::/0 } } version = 2 send_cert = always send_certreq = no } # https://docs.strongswan.org/docs/latest/config/IPv6Ndp.html ndp { children { ns { local_ts = ::/0[ipv6-icmp/135] remote_ts = ::/0[ipv6-icmp/135] mode = pass start_action = trap } na { local_ts = ::/0[ipv6-icmp/136] remote_ts = ::/0[ipv6-icmp/136] mode = pass start_action = trap } } } } secrets { eap-yourname { id = yourname secret = yourpassword } }
指定 DNS 服务器
# /etc/strongswan.d/charon/attr.conf # Section to specify arbitrary attributes that are assigned to a peer via # configuration payload (CP). attr { load = yes # <attr> is an attribute name or an integer, values can be an IP address, # subnet or arbitrary value. # <attr> = dns = 8.8.8.8 }
证书文件:
# 私钥 /etc/swanctl/private: example.com.pem # 公钥,我选的是 letsecrypt 的 fullchain 证书 /etc/swanctl/x509: exmpale.comf.pem # CA 证书。不知道为什么加了 fullchain 还是不够,我从 client 认证失败的例子看说缺这些 CA 就加上了。 # openssl x509 -in x.pem -text -noout 也能用来看最上层的 CA 是谁。 # 从 https://letsencrypt.org/certificates/ 能下载 pem 格式的证书文件。 /etc/swanctl/x509ca: le-R11.pem le-R12.pem
4. 使用本地 DHCP 为 roadwarrier 提供 LAN 地址
dhcp + farp plugin 能让远端设备获得如同在本地 LAN 一样的二层接入,仅限 IPv4。这样的好处是能用本地 DHCP 服务为远端设备提供固定地址。
连接配置:v4 地址池改为特殊的 "dhcp" 地址池
connections { rw-eap { local_addrs = example.com pools = dhcp, v6pool } }
dhcp 插件配置:dnsmasq 不会响应本地回环设备来的请求,必须要发往它 LAN 网段的广播地址
# /etc/strongswan.d/charon/dhcp.conf dhcp { load = yes # 强行让 strongswan 到广播地址去发请求 force_server_address = yes # DHCP server unicast or broadcast IP address. server = 172.31.0.255 # 虽然可能没有必要,但是让 MAC 地址稳定或许更像一个标准的二层设备 # Derive user-defined MAC address from hash of IKE identity and send client # identity DHCP option. identity_lease = yes }
farp 插件配置:加载就好,我不确定它是否起到关键作用
bypass-lan 插件:先关闭,否则它默认在 route table 220 里增加对每个 netif 的网段的 throw 路由。 这是为了让 roadwarrier 能访问自己本地同网段的设备。我其实不太理解为什么在 router 上加 throw 路由 能影响 roadwarrier 的路由选择。
# /etc/strongswan.d/charon/bypass-lan.conf bypass-lan { load = no }