OpenVPN

VPN 配置

首先跟着 ArchWiki 的流程,一直到五个加密相关的文件到位。然后修改 /etc/openvpn/server/server.conf

port 1194
proto udp
dev tun
ca ca.crt
cert openvpn.crt
key openvpn.key  # This file should be kept secret
tls-crypt ta.key
dh dh.pem

# The IP of server in the vpn.
server 10.8.0.0 255.255.255.0

ifconfig-pool-persist ipp.txt
push "dhcp-option DNS 192.168.1.1"
push "dhcp-option DOMAIN internal"
keepalive 10 120

# v2.4 client/server will automatically negotiate AES-256-GCM
# in TLS mode. So this line is probabaly useless.
cipher AES-256-CBC

user nobody
group nobody
persist-key
persist-tun
status openvpn-status.log
verb 3
explicit-exit-notify 1

注意这个文件也可以不叫 server.conf,而且目录里可以有多个配置文件,对应多个 server,可以同时开。

流量转发

现在这个状态的 VPN 虽然可以用,但是不能和外界连接,需要把 VPN 界面的流量转发到对外的 NIC 上,这个过程和配置 wifi 热点是一样的。首先打开转发支持,

# sysctl net.ipv4.ip_forward=1

并修改 /etc/sysctl.d/30-ipforward.conf

net.ipv4.ip_forward=1
net.ipv6.conf.default.forwarding=1
net.ipv6.conf.all.forwarding=1

然后安装 iptables,设置转发。假设对外的 NIC 是 internet0,

# iptables -t nat -A POSTROUTING -o internet0 -j MASQUERADE
# iptables -A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
# iptables -A FORWARD -i tun0 -o internet0 -j ACCEPT

最后保存 iptables 设置,并开启服务,

# iptables-save > /etc/iptables/iptables.rules
# systemctl enable --now iptables.service

服务器部分这样就算配置好了,开启服务,

# systemctl enable --now openvpn-server@server.service

注意这个 @ 后面的 server 对应配置文件 server.conf 的名字。

生成客户端配置

我使用了 ovpngen 这个程序。但是生成出来的配置不能直接用,还是需要做些修改。

首先这个配置没有路由信息,在 remote 这一行的后面,添加

route 192.168.1.1 255.255.255.0

然后在 remote 这一行的前面添加

redirect-gateway def1 bypass-dhcp ipv6

由于服务器使用了 tls-crypt,客户端文件的 tls-auth 节点名字也要替换成 tls-crypt。最后检查文件中的其他设置是否和服务端一致。

WireGuard

那个 OpenVPN 我用了一天,一切正常,但是后来有人向我推荐了 WireGuard。官网上说,

It intends to be considerably more performant than OpenVPN.

所以我决定尝试换成 WireGuard。

服务端

WireGuard 的配置比 OpenVPN 简单很多,而且神奇的是,networkd 居然原生支持 WireGuard 协议。所以如果我手上已经有一组密钥的话,根本就不用装任何东西⋯⋯

⋯⋯

⋯⋯

但是我没有密钥,所以还是要安装 wireguard-tools。无废话生成密钥:

$ mkdir server client
$ cd server
$ wg genkey | tee privatekey | wg pubkey > publickey
$ cd ../client
$ wg genkey | tee privatekey | wg pubkey > publickey

这些密钥不是一般的 PEM 文件,而是很短的一行。Networkd 不会去读这些文件,里面的内容要直接写到配置里。首先创建 /etc/systemd/network/99-wireguard-server.netdev 这个文件,

[NetDev]
Name = wg0
Kind = wireguard
Description = Wireguard

[WireGuard]
ListenPort = 51820
PrivateKey = <server private key>

[WireGuardPeer]
PublicKey = <client public key>
AllowedIPs = 10.8.200.2/32

这个文件让 networkd 开一个新的 NIC,叫 wg0,里面跑着 WireGuard 协议。我这里只设置了一个 client,如果需要多个 client 的话只要产生新的密钥并在这里添加对应的 WireGuardPeer section 即可。接下来配置服务,创建 /etc/systemd/network/99-server.network 文件,

[Match]
Name = wg0

[Network]
Address = 10.8.200.1/32

[Route]
Gateway = 10.8.200.1
Destination = 10.8.200.0/24

服务端就这样配置好了。当然流量转发也是要设的,方法和 OpenVPN 里的一样。

客户端

客户端的配置更简单,

[Interface]
Address = 10.8.200.2/24
PrivateKey = <server private key>
DNS = 192.168.1.1

[Peer]
PublicKey = <client public key>
AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = <hostname:port>

在 Mac 的客户端里直接添加这个文件即可。把这个文件编码成 QR code,就可以给手机用了。

关于 DNS 的一个坑

我的网络里没有 DNS,域名转 IP 全靠 mDNS。然而我发现从外来连 VPN 进来的时候 mDNS 是不管用的,只能用 IP。Resolved 的 mDNS 服务有个功能,就是会在 127.0.0.53 开一个 DNS 服务,这样本地的程序可以直接把它当成一个正常的 DNS 服务器来用。我尝试了在网内架一个 BIND 服务,然后把 .local 的请求转发给 resolved,但是这样也不行,对于这样的请求,耿直的 BIND 是拒绝的,因为它认为这是内网的 mDNS 请求泄漏到了外网。我到现在也没找到什么好的办法。