通过 WireGuard 搭建 VPN 访问家里内网

家里网络没有公网 IP,因此需要一台具有公网 IP 的服务器作为 WireGuard 网络的“server”。家中需要有一台设备作为 WireGuard 网络中的节点。我们将使用手机,在 4G 网络下检查 VPN 是否搭建成功。

IP 段选择

WireGuard 组网需要使用一个不与你的任何设备的网络相冲突的 IP 地址段。像 192.0.2.0/24 、198.51.100.0/24 、203.0.113.0/24 这些分配为用于文档和示例中的“TEST-NET”,这些地址段通常不会被你需要连接的其他网络所使用。

在下面的配置中,我会分别将 192.0.2.1、192.0.2.2、192.0.2.3 分配给公网服务器、家中的 Mac 和 iPhone。

在服务器上配置 WireGuard

要使用 WireGuard,首先需要确保 Linux 内核支持。可使用 modinfo wireguard 命令检查是否内置了 WireGuard。也可用过 uname -r 检查内核版本是否为 5.6 以上。

安装 wireguard

Debian

apt install wireguard

其他系统参考:install

完成服务器端的配置

在正确安装 wireguard 后,你可以通过如下命令快速创建一组公钥和私钥。

$ wg genkey | tee peer_A.key | wg pubkey > peer_A.pub && cat peer_A.key && cat peer_A.pub

创建 /etc/wireguard/wg0.conf 并填配置

[Interface]
PrivateKey = (your server private key here)
Address = 192.0.2.1/24
ListenPort = 51820
#PreUp = echo WireGuard PreUp
#PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE; ip6tables -A FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
#PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
#PostUp = iptables -I INPUT -p udp --dport 51820 -j ACCEPT
#PostDown = iptables -D INPUT -p udp --dport 51820 -j ACCEPT
#ipv4 局域网够用配置
PostUp = iptables -A FORWARD -i wg0 -j ACCEPT
PostDown = iptables -D FORWARD -i wg0 -j ACCEPT
#ipv4防火墙放行转发和NAT(访问公共网络)
#PostUp = iptables -A FORWARD -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
#PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
#ipv4防火墙放行转发和NAT(访问公共网络) 扩展防火墙规则(节点之间双向互联)
#PostUp = iptables -I FORWARD -i wg0 -j ACCEPT; iptables -I FORWARD -o wg0 -j ACCEPT; iptables -I INPUT -i wg0 -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
#PostDown = iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; iptables -D INPUT -i wg0 -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
#PreDown = echo WireGuard PreDown

[Peer]
# Mac at home
PublicKey = (Mac public key here)
AllowedIPs = 192.0.2.2/32, 192.168.1.0/24

[Peer]
# iPhone
PublicKey = (iPhone public key here)
AllowedIPs = 192.0.2.3/32

在 WireGuard 中,你需要手动给各个设备分配 IP,并确保每个设备都有唯一的 IP。Interface 包含了当前设备的设置,对于“服务端”来说,ListenPort 是必须的。下面的每一个 Peer 段代表了能连接到本设备的一个其他设备。

配置文件保存后,我们可以使用 wg-quick up wg0 来启用配置文件。wg-quick 会自动配置路由表,无需我们手动设置。

记得放行 51820 UDP 端口。

iptables 配置参考

家中Mac端的配置

创建 /etc/wireguard/wg0.conf 并填配置

[Interface]
PrivateKey = (private key of Mac)
Address = 192.0.2.2/24
DNS = 1.1.1.1

[Peer]
PublicKey = (public key of server)
AllowedIPs = 192.0.2.0/24
Endpoint = (server ip address):51820
PersistentKeepalive = 10

在这里,我们将公网服务器作为唯一的 Peer,通过设置 PersistentKeepalive 来进行连接的保活。这里 AllowedIPs 的作用是确保来自于我们 WireGuard 子网网段来的流量能被本机的 WireGuard 虚拟网卡进行处理。

iphone 配置

安装 WireGuard  Download from App Store

这个配置可以参考应用商店的截屏。

设置开机启动

如果你的系统使用systemd,如ubuntu,设置wireguard开机启动命令如下

systemctl enable wg-quick@wg0

启用服务器Peer端转发

打开 /etc/sysctl.conf 修改

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

echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
echo 'net.ipv6.conf.all.forwarding=1' >> /etc/sysctl.conf
sysctl -p

附注

保留地址段

保留IP地址

小插曲

当遇到错误提示:

/usr/bin/wg-quick: line 31: resolvconf: command not found [WireGuard | Debian]

可以创建软链接解决

ln -s /usr/bin/resolvectl /usr/local/bin/resolvconf

当遇到错误提示:

[SELF-SOLVED] Unit dbus-org.freedesktop.resolve1.service not found

可以通过启动 systemd-resolved 服务解决

systemctl start systemd-resolved.service
systemctl enable systemd-resolved.service

配置详解

WireGuard 使用 INI 语法作为其配置文件格式。配置文件可以放在任何路径下,但必须通过绝对路径引用。默认路径是 /etc/wireguard/wg0.conf

配置文件的命名形式必须为 ${WireGuard 接口的名称}.conf。通常情况下 WireGuard 接口名称以 wg 为前缀,并从 0 开始编号,但你也可以使用其他名称,只要符合正则表达式 ^[a-zA-Z0-9_=+.-]{1,15}$ 就行。

你可以选择使用 wg 命令来手动配置 VPN,但一般建议使用 wg-quick,它提供了更强大和用户友好的配置体验,可以通过配置文件来管理配置。

[Interface]

定义本地 VPN 配置。例如:

1.本地节点是客户端,只路由自身的流量,只暴露一个 IP。

[Interface]
# Name = phone.example-vpn.dev
Address = 192.0.2.5/32
PrivateKey = <private key for phone.example-vpn.dev>

本地节点是中继服务器,它可以将流量转发到其他对等节点(peer),并公开整个 VPN 子网的路由。

[Interface]
# Name = public-server1.example-vpn.tld
Address = 192.0.2.1/24
ListenPort = 51820
PrivateKey = <private key for public-server1.example-vpn.tld>
DNS = 1.1.1.1

# Name

这是 INI 语法中的标准注释,用于展示该配置部分属于哪个节点。这部分配置会被 WireGuard 完全忽略,对 VPN 的行为没有任何影响。

Address

定义本地节点应该对哪个地址范围进行路由。如果是常规的客户端,则将其设置为节点本身的单个 IP(使用 CIDR 指定,例如 192.0.2.3/32);如果是中继服务器,则将其设置为可路由的子网范围。 例如:

常规客户端,只路由自身的流量:

Address = 192.0.2.3/32

中继服务器,可以将流量转发到其他对等节点(peer):

Address = 192.0.2.1/24

也可以指定多个子网或 IPv6 子网:

Address = 192.0.2.1/24,2001:DB8::/64

ListenPort

当本地节点是中继服务器时,需要通过该参数指定端口来监听传入 VPN 连接,默认端口号是 51820。常规客户端不需要此选项。

PrivateKey

本地节点的私钥,所有节点(包括中继服务器)都必须设置。不可与其他服务器共用。

私钥可通过命令 wg genkey > example.key 来生成。

DNS

通过 DHCP 向客户端宣告 DNS 服务器。客户端将会使用这里指定的 DNS 服务器来处理 VPN 子网中的 DNS 请求,但也可以在系统中覆盖此选项。例如:

#如果不配置则使用系统默认 DNS
#可以指定单个 DNS:
DNS = 1.1.1.1
#也可以指定多个 DNS:
DNS = 1.1.1.1,8.8.8.8

Table

定义 VPN 子网使用的路由表,默认不需要设置。该参数有两个特殊的值需要注意:

 Table = off : 禁止创建路由
 Table = auto(默认值) : 将路由添加到系统默认的 table 中,并启用对默认路由的特殊处理。

例如:Table = 1234

MTU

定义连接到对等节点(peer)的 MTU(Maximum Transmission Unit,最大传输单元),默认不需要设置,一般由系统自动确定。

PreUp

启动 VPN 接口之前运行的命令。这个选项可以指定多次,按顺序执行。

例如: 添加路由:

 PreUp = ip rule add ipproto tcp dport 22 table 1234

PostUp

启动 VPN 接口之后运行的命令。这个选项可以指定多次,按顺序执行。

例如:

从文件或某个命令的输出中读取配置值:
 PostUp = wg set %i private-key /etc/wireguard/wg0.key <(some command here)
添加一行日志到文件中:
 PostUp = echo "$(date +%s) WireGuard Started" >> /var/log/wireguard.log
调用 WebHook:
 PostUp = curl https://events.example.dev/wireguard/started
添加路由:
 PostUp = ip rule add ipproto tcp dport 22 table 1234
添加 iptables 规则,启用数据包转发:
 PostUp = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
强制 WireGuard 重新解析对端域名的 IP 地址:
 PostUp = resolvectl domain %i "~."; resolvectl dns %i 192.0.2.1; resolvectl dnssec %i yes

PreDown

停止 VPN 接口之前运行的命令。这个选项可以指定多次,按顺序执行。 例如:

添加一行日志到文件中:
 PreDown = echo "$(date +%s) WireGuard Going Down" >> /var/log/wireguard.log

PostDown

停止 VPN 接口之后运行的命令。这个选项可以指定多次,按顺序执行。 例如:

添加一行日志到文件中:
PostDown = echo "$(date +%s) WireGuard Down" >> /var/log/wireguard.log

[Peer]

定义能够为一个或多个地址路由流量的对等节点(peer)的 VPN 设置。对等节点(peer)可以是将流量转发到其他对等节点(peer)的中继服务器,也可以是通过公网或内网直连的客户端。

中继服务器必须将所有的客户端定义为对等节点(peer),除了中继服务器之外,其他客户端都不能将位于 NAT 后面的节点定义为对等节点(peer),因为路由不可达。对于那些只为自己路由流量的客户端,只需将中继服务器作为对等节点(peer),以及其他需要直接访问的节点。

配置示例:

对等节点(peer)是路由可达的客户端,只为自己路由流量

[Peer]
# Name = public-server2.example-vpn.dev
Endpoint = public-server2.example-vpn.dev:51820
PublicKey = <public key for public-server2.example-vpn.dev>
AllowedIPs = 192.0.2.2/32

对等节点(peer)是位于 NAT 后面的客户端,只为自己路由流量

[Peer]
# Name = home-server.example-vpn.dev
Endpoint = home-server.example-vpn.dev:51820
PublicKey = <public key for home-server.example-vpn.dev>
AllowedIPs = 192.0.2.3/32

对等节点(peer)是中继服务器,用来将流量转发到其他对等节点(peer)

[Peer]
# Name = public-server1.example-vpn.tld
Endpoint = public-server1.example-vpn.tld:51820
PublicKey = <public key for public-server1.example-vpn.tld>
# 路由整个 VPN 子网的流量
AllowedIPs = 192.0.2.1/24
PersistentKeepalive = 25

Endpoint

指定远端对等节点(peer)的公网地址。如果对等节点(peer)位于 NAT 后面或者没有稳定的公网访问地址,就忽略这个字段。通常只需要指定中继服务器的 Endpoint,当然有稳定公网 IP 的节点也可以指定。例如:

通过 IP 指定:

Endpoint = 123.124.125.126:51820

通过域名指定:

Endpoint = public-server1.example-vpn.tld:51820

AllowedIPs

允许该对等节点(peer)发送过来的 VPN 流量中的源地址范围。同时这个字段也会作为本机路由表中 wg0 绑定的 IP 地址范围。如果对等节点(peer)是常规的客户端,则将其设置为节点本身的单个 IP;如果对等节点(peer)是中继服务器,则将其设置为可路由的子网范围。可以使用 , 来指定多个 IP 或子网范围。该字段也可以指定多次。

当决定如何对一个数据包进行路由时,系统首先会选择最具体的路由,如果不匹配再选择更宽泛的路由。例如,对于一个发往 192.0.2.3 的数据包,系统首先会寻找地址为 192.0.2.3/32 的对等节点(peer),如果没有再寻找地址为 192.0.2.1/24 的对等节点(peer),以此类推。

例如:

对等节点(peer)是常规客户端,只路由自身的流量:

AllowedIPs = 192.0.2.3/32

对等节点(peer)是中继服务器,可以将流量转发到其他对等节点(peer):

AllowedIPs = 192.0.2.1/24

对等节点(peer)是中继服务器,可以转发所有的流量,包括外网流量和 VPN 流量:

AllowedIPs = 0.0.0.0/0,::/0

对等节点(peer)是中继服务器,可以路由其自身和其他对等节点(peer)的流量:

AllowedIPs = 192.0.2.3/32,192.0.2.4/32

对等节点(peer)是中继服务器,可以路由其自身的流量和它所在的内网的流量:

AllowedIPs = 192.0.2.3/32,192.168.1.1/24

PublicKey

对等节点(peer)的公钥,所有节点(包括中继服务器)都必须设置。可与其他对等节点(peer)共用同一个公钥。

公钥可通过命令 wg pubkey < example.key > example.key.pub 来生成,其中 example.key 是上面生成的私钥。

PersistentKeepalive

如果连接是从一个位于 NAT 后面的对等节点(peer)到一个公网可达的对等节点(peer),那么 NAT 后面的对等节点(peer)必须定期发送一个出站 ping 包来检查连通性,如果 IP 有变化,就会自动更新Endpoint。

例如:

本地节点与对等节点(peer)可直连:该字段不需要指定,因为不需要连接检查。

对等节点(peer)位于 NAT 后面:该字段不需要指定,因为维持连接是客户端(连接的发起方)的责任。

本地节点位于 NAT 后面,对等节点(peer)公网可达:需要指定该字段 PersistentKeepalive = 25,表示每隔 25 秒发送一次 ping 来检查连接。

共享一个 peers.conf 文件

如果某个 peer 的公钥与本地接口的私钥能够配对,那么 WireGuard 会忽略该 peer。利用这个特性,我们可以在所有节点上共用同一个 peer 列表,每个节点只需要单独定义一个 [Interface] 就行了,即使列表中有本节点,也会被忽略。具体方式如下:

每个对等节点(peer)都有一个单独的 /etc/wireguard/wg0.conf 文件,只包含 [Interface] 部分的配置。

每个对等节点(peer)共用同一个 /etc/wireguard/peers.conf 文件,其中包含了所有的 peer。

Wg0.conf 文件中需要配置一个 PostUp 钩子,内容为

PostUp = wg addconf /etc/wireguard/peers.conf

文章来源:

Author:白菜
link:https://blog.baicai.me/article/2023/using_wireguard/