翻译自 Routing Docker Host And Container Traffic Through WireGuard。
介绍
WireGuard 是一个非常简单但速度极快的开源虚拟专用网络(VPN)解决方案,它在行业中引起了轰动。与 OpenVPN 的超过 70,000 行代码相比,其代码仅约 4,000 行,这使得它更容易审计,并且具有相对较小的攻击面。自 2020 年初作为 Linux 内核 5.6 的一部分被引入以来,其受欢迎程度迅速飙升,尤其是在家庭实验室领域。
我们最初发布了 WireGuard Docker 镜像,主要是为了替代我们麻烦的 OpenVPN 服务器镜像,当时这是一种非常流行的 VPN 服务器解决方案。然而,OpenVPN 服务器是一个封闭源代码的商业产品,这意味着当发生重大更改时,我们的镜像很难修复,因为我们甚至无法看到问题所在。此外,我们的镜像经常在更新时出现问题。我们很高兴能加入 WireGuard 的行列,并在 2020 年初发布了带有内置和自动化服务器功能的第一版实现。最初的目标是提供一个服务器解决方案,后来我们为镜像添加了必要的功能,以支持客户端和站点到站点的 VPN 场景。
Warning
免责声明:我们不正式为站点到站点的 VPN 配置提供支持,因为它们可能非常复杂,并需要针对您的网络设置进行具体的定制。我们无法提供足够的带宽来为这些场景提供个性化支持。不过,您可以始终在我们 Discord 服务器的 #other-support 频道上寻求社区支持。
我们的镜像还具有在内核中没有内置 WireGuard 时构建 WireGuard 内核模块的功能。对于基于 Ubuntu 和 Debian 的发行版,只要主机使用的是标准内核,我们的容器就会自动安装内核头文件并构建模块。如果在不同发行版上或未使用标准内核的情况下运行,容器允许映射已安装在主机上的内核头文件,并使用它们来构建模块。或者,您也可以在主机上安装 WireGuard 并构建模块,我们的容器将检测并使用这些模块。
虽然此镜像最初是作为 VPN 服务器解决方案发布的,但由于最近的法律和对在线隐私的攻击,它作为客户端 VPN 获得了很大的欢迎。许多人转向商业 VPN 提供商(如 Mullvad),以保证隐私,并通过其私人远程服务器路由部分或全部流量。
在本文中,我们将重点介绍三种使用 WireGuard 镜像可以实现的场景。第一个场景将展示如何通过 WireGuard 容器将主机的全部流量路由出去,利用主机的路由表。第二和第三个场景将展示如何通过 WireGuard 容器为其它 Docker 容器的流量提供替代路由。
Warning
本文并非分步指南,而是展示使用 WireGuard 镜像可以实现的功能。如前所述,对于站点到站点的 VPN 配置,我们也不正式提供支持,也不为通过 WireGuard 容器路由部分或全部流量(即分流)提供路由配置支持,因为这可能非常复杂,并需要针对您的网络设置和 VPN 提供商进行具体定制。不过,您可以始终在我们 Discord 服务器的 #other-support 频道寻求社区支持。
通过 WireGuard 路由所有流量
为了通过路由表进行路由,我们将使用容器的 IP 地址,因此最好在定义的子网中为其分配一个静态 IP。首先,我们确保通过以下命令创建一个名为 wgnet
的 Docker 桥接网络,并定义一个子网:
docker network create --subnet 172.20.0.0/24 wgnet
接下来,通过 docker inspect wgnet
命令检查我们新创建的网络:
[
{
"Name": "wgnet",
"Id": "65debd3cb4f053bdb6ccdfd1f60598755041ad17bbcf48c1756930fec74c2b58",
"Created": "2022-04-16T20:48:45.073776159Z",
"Scope": "local",
"Driver": "bridge",
"EnableIPv6": false,
"IPAM": {
"Driver": "default",
"Options": {},
"Config": [
{
"Subnet": "172.20.0.0/24",
"Gateway": "172.20.0.1"
}
]
},
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
"Network": ""
},
"ConfigOnly": false,
"Containers": {},
"Options": {},
"Labels": {}
}
]
这是一个具有正确子网的用户定义桥接网络。现在让我们检查当前的路由:
$ ip route show
default via 192.168.1.1 dev enp1s0 proto dhcp src 192.168.1.209 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.20.0.0/24 dev br-65debd3cb4f0 proto kernel scope link src 172.20.0.1 linkdown
192.168.1.0/24 dev enp1s0 proto kernel scope link src 192.168.1.209
192.168.1.1 dev enp1s0 proto dhcp scope link src 192.168.1.209 metric 100
我们可以看到,Docker 已自动为子网 172.20.0.0/24
的新桥接网络创建了路由。然而,它被标记为 linkdown
,因为当前没有任何运行中的容器连接到该网络上。根据这些规则,所有未在规则中明确定义的范围的连接都将使用默认规则。目前,所有这些连接(包括到公共 IP 的连接)都通过我们的局域网网关 192.168.1.1
路由,其源 IP 是我们的 Docker 主机的局域网 IP 192.168.1.209
。一旦设置好 WireGuard 容器并且有一个隧道,我们将修改这些规则,通过 WireGuard 隧道而不是局域网(LAN)网关路由所有内容。
首先,为 WireGuard 容器创建配置文件夹:
mkdir -p /home/aptalca/appdata/wireguard-client
接下来,我们将设置 wg0.conf
文件,其中包含隧道的详细信息。以下是我从 Mullvad 提供的 VPN 提供商处获取的示例配置。当我创建它时,我选择了禁用 IPv6 的选项,因此它只会为 IPv4 连接设置。我的 ISP 不提供 IPv6 地址,因此我也不需要它。另外,Docker 的 IPv6 支持也比较棘手。
[Interface]
PrivateKey = 8AFbMaOQFaOYBrxrq7Kk/mt3jxa5Z1H27CIWNXs4vmY=
Address = 10.64.133.56/32
DNS = 193.138.218.74
[Peer]
PublicKey = M+KYHvnMLh57umbiaBOaivAnProWCAGeQpyFfwFF2iI=
AllowedIPs = 0.0.0.0/0
Endpoint = 89.45.90.197:51820
此配置定义了本地 WireGuard 对等体的私钥,以及我们将连接到的 WireGuard 服务器的公钥。Address
定义了 Mullvad 分配给我们账户/对等体的隧道地址。DNS
指向 Mullvad 的 DNS 服务器,但可以更改为您喜欢的任何地址。AllowedIPs
定义了应通过隧道发送连接的目标 IP 和/或网络。我们将其设置为 0.0.0.0/0
,即所有网络,这意味着所有流量都将通过隧道发送。最后,Endpoint
定义了 Mullvad WireGuard 服务器的 IP 地址。
通过此配置,将创建一个隧道,所有来自 WireGuard 容器内部的连接都将通过隧道发送。然而,我们希望将来自主机的连接正确路由到容器,这就需要使用 iptables
NAT(网络地址转换)中的 masquerade
。通常情况下,可以通过以下命令为 wg0
网络启用该功能:
iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE
我们可以轻松地通过将该命令包含在 WireGuard 配置的 PostUp
和 PreDown
部分中来实现自动化。这些部分定义了 WireGuard 隧道创建后和销毁前要运行的脚本。以下语句正是这样实现的:
PostUp = iptables -t nat -A POSTROUTING -o wg+ -j MASQUERADE
PreDown = iptables -t nat -D POSTROUTING -o wg+ -j MASQUERADE
让我们创建最终配置并将其保存为 /home/aptalca/appdata/wireguard-client/wg0.conf
:
[Interface]
PrivateKey = 8AFbMaOQFaOYBrxrq7Kk/mt3jxa5Z1H27CIWNXs4vmY=
Address = 10.64.133.56/32
DNS = 193.138.218.74
PostUp = iptables -t nat -A POSTROUTING -o wg+ -j MASQUERADE
PreDown = iptables -t nat -D POSTROUTING -o wg+ -j MASQUERADE
[Peer]
PublicKey = M+KYHvnMLh57umbiaBOaivAnProWCAGeQpyFfwFF2iI=
AllowedIPs = 0.0.0.0/0
Endpoint = 89.45.90.197:51820
现在,我们可以创建 WireGuard 容器了。以下的 compose YAML 文件将设置一个容器,并将其连接到具有静态 IP 172.20.0.50
的 wgnet
网络。我们选择一个较高的数字(如 50
),因为 Docker Compose 无法正确地匹配静态和动态 IP 地址,或者说无法实现地址的正确保留。如果同一个 Compose YAML 文件中列出了 10 个容器,而没有指定静态 IP,Docker Compose 将从动态地址开始分配,从 2
开始(1
通常分配给网关)。将 WireGuard 容器的 IP 设置为 50
,使我们有足够的空间为动态分配保留地址(例如,WireGuard 之前分配了 48 个容器地址)。正确的做法是为 Compose YAML 文件中的每个容器分配静态 IP,而不是仅仅依赖动态分配,这样可以避免由于容器数量的变化而导致的不良后果,但根据容器的数量,这可能是一项不可取的任务。让我们将以下配置保存为 docker-compose.yml
,并运行 docker compose up -d
来创建并启动它。
services:
wireguard:
image: lscr.io/linuxserver/wireguard
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- /home/aptalca/appdata/wireguard-client:/config
- /lib/modules:/lib/modules
networks:
default:
ipv4_address: 172.20.0.50
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
networks:
default:
name: wgnet
external: true
在容器创建完成后,让我们检查日志以确保隧道已正确创建。运行 docker logs wireguard
,日志的末尾应该显示如下内容:
[services.d] starting services
[services.d] done.
Warning: `/config/wg0.conf' is world accessible
[#] ip link add wg0 type wireguard
[#] wg setconf wg0 /dev/fd/63
[#] ip -4 address add 10.64.133.56/32 dev wg0
[#] ip link set mtu 1420 up dev wg0
[#] resolvconf -a wg0 -m 0 -x
[#] wg set wg0 fwmark 51820
[#] ip -4 route add 0.0.0.0/0 dev wg0 table 51820
[#] ip -4 rule add not fwmark 51820 table 51820
[#] ip -4 rule add table main suppress_prefixlength 0
[#] sysctl -q net.ipv4.conf.all.src_valid_mark=1
sysctl: setting key "net.ipv4.conf.all.src_valid_mark", ignoring: Read-only file system
[#] iptables-restore -n
[#] iptables -t nat -A POSTROUTING -o wg+ -j MASQUERADE
从上面的日志中可以看到,它成功创建了包含隧道地址的隧道,为 0.0.0.0/0
创建了路由,并最终设置了用于路由的 iptables NAT masquerade。主机上的路由现在应该显示 172.20.0.0/24
的路由为激活状态(不再标记为 linkdown
):
$ ip route show
default via 192.168.1.1 dev enp1s0 proto dhcp src 192.168.1.209 metric 100
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.20.0.0/24 dev br-65debd3cb4f0 proto kernel scope link src 172.20.0.1
192.168.1.0/24 dev enp1s0 proto kernel scope link src 192.168.1.209
192.168.1.1 dev enp1s0 proto dhcp scope link src 192.168.1.209 metric 100
此时,主机上没有任何流量通过 WireGuard 路由:
$ curl https://am.i.mullvad.net/connected
You are not connected to Mullvad. Your IP address is 182.68.23.15
为了通过 WireGuard 进行路由,我们首先需要删除 default
路由,并创建一个新的默认路由,该路由通过 WireGuard 容器。然而,这样做会导致一个重大问题。WireGuard 容器需要连接到 Mullvad 服务器(即 VPN 终端节点)以建立隧道。如果此连接也通过容器进行路由,将导致一个循环,隧道将失败。因此,我们需要绕过隧道连接到 VPN 终端节点 89.45.90.197
的流量。以下命令将确保到 VPN 终端节点的连接通过我们的局域网网关路由,而其它所有内容都通过 WireGuard 容器进行路由:
sudo ip route del default
sudo ip route add 89.45.90.197 via 192.168.1.1
sudo ip route add default via 172.20.0.50
现在让我们检查更新后的路由表:
$ ip route
default via 172.20.0.50 dev br-65debd3cb4f0
89.45.90.197 via 192.168.1.1 dev enp1s0
172.17.0.0/16 dev docker0 proto kernel scope link src 172.17.0.1 linkdown
172.20.0.0/24 dev br-65debd3cb4f0 proto kernel scope link src 172.20.0.1
192.168.1.0/24 dev enp1s0 proto kernel scope link src 192.168.1.209
192.168.1.1 dev enp1s0 proto dhcp scope link src 192.168.1.209 metric 100
89.45.90.197 via 192.168.1.1 dev enp1s0
确保到 VPN 终端节点的连接绕过了隧道,而 default via 172.20.0.50 dev br-65debd3cb4f0
确保所有其它流量通过 wgnet
网桥上的 WireGuard 容器进行路由。接下来检查我们的互联网连接:
$ curl https://am.i.mullvad.net/connected
You are connected to Mullvad (server us68-wireguard). Your IP address is 89.45.90.206
现在,我们主机的互联网连接已通过 WireGuard 容器路由,并通过 Mullvad 终端节点发送。在这种情况下,由于所有主机连接都通过隧道,因此默认情况下,其它 Docker 容器的连接也会通过 WireGuard 隧道。
然而,请记住,路由表在重启时会动态生成,其中一些条目是由 Docker 服务创建的,而另一些是由管理网络连接的守护程序(在最近的 Ubuntu 中是 netplan
)创建的,因此当我们重启主机时,这些更改将会丢失。有多种方法可以确保在重启时应用这些路由,其中一种方法是创建一个 systemd 服务。
例如,在使用 systemd 的 Ubuntu 上,我们可以创建一个新的服务文件 /lib/systemd/system/iproute.service
,内容如下:
[Unit]
Description=Route everything through WireGuard
After=docker.service
[Service]
Type=oneshot
Restart=on-failure
ExecStart=ip route del default
ExecStart=ip route add 89.45.90.197 via 192.168.1.1
ExecStart=ip route add default via 172.20.0.50
[Install]
WantedBy=multi-user.target
然后,我们可以通过运行 sudo systemctl enable iproute.service
启用该服务。当我们重启主机时,该服务将会等待 Docker 服务启动(我们需要 WireGuard 容器运行),然后运行更改默认路由到 WireGuard 并为 VPN 终端节点设置绕过的命令。这只是一个非常基本的示例,您应该根据您的环境制定更合适的解决方案。
通过 WireGuard 路由 Docker 容器流量
有几种方法可以通过 WireGuard 容器路由特定的 Docker 容器流量。最常见的方法(大多数在线指南提到)是将容器的网络设置为使用 WireGuard 容器(或服务)的网络栈。然而,当以这种方式路由多个容器时,由于所有容器都使用相同的网络栈,可能会出现端口冲突,因为多个容器会尝试在同一个内部端口上监听。在本文中,我们将提出一种可以避免该问题的解决方案。不过,首先让我们从常见的方法开始。
配置容器以使用 WireGuard 容器的网络栈
下面的 compose yaml 将设置一个 WireGuard 容器和一个 qBittorrent 容器:
services:
wireguard:
image: lscr.io/linuxserver/wireguard
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- /home/aptalca/appdata/wireguard-client:/config
- /lib/modules:/lib/modules
ports:
- 8080:8080
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent
container_name: qbittorrent
network_mode: service:wireguard
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- WEBUI_PORT=8080
restart: unless-stopped
在这个 Compose 配置中,qBittorrent 服务被定义为 network_mode: service:wireguard
,这告诉 Docker 让其使用名为 wireguard
的服务的网络栈,而该服务正在运行 WireGuard 容器。因此,qBittorrent 本身并没有自己的网络栈,而是附加到 WireGuard 容器的网络栈(类似于 network_mode: host
,即容器直接使用主机的网络栈)。因此,qBittorrent 的 GUI 端口 8080 实际上是在 WireGuard 容器内监听的。为了将该端口映射到主机上以供本地访问,我们需要将端口映射指令放在 WireGuard 服务的配置中,如上所示。
但这并未结束。即使端口已经映射,一旦隧道启动,它也不会响应来自主机的请求,因为它被配置为通过隧道发送所有传出的连接。我们需要设置 PostUp
和 PreDown
规则以允许传出连接到我们的局域网。
以下规则应该覆盖所有私有范围(可以根据您的本地环境自由调整):
PostUp = DROUTE=$(ip route | grep default | awk '{print $3}'); HOMENET=192.168.0.0/16; HOMENET2=10.0.0.0/8; HOMENET3=172.16.0.0/12; ip route add $HOMENET3 via $DROUTE;ip route add $HOMENET2 via $DROUTE; ip route add $HOMENET via $DROUTE;iptables -I OUTPUT -d $HOMENET -j ACCEPT;iptables -A OUTPUT -d $HOMENET2 -j ACCEPT; iptables -A OUTPUT -d $HOMENET3 -j ACCEPT; iptables -A OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = HOMENET=192.168.0.0/16; HOMENET2=10.0.0.0/8; HOMENET3=172.16.0.0/12; ip route delete $HOMENET; ip route delete $HOMENET2; ip route delete $HOMENET3; iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT; iptables -D OUTPUT -d $HOMENET -j ACCEPT; iptables -D OUTPUT -d $HOMENET2 -j ACCEPT; iptables -D OUTPUT -d $HOMENET3 -j ACCEPT
首先,我们为 WireGuard 创建配置文件夹:
mkdir -p /home/aptalca/appdata/wireguard-client
接下来,我们创建 WireGuard 配置,并将其保存为 /home/aptalca/appdata/wireguard-client/wg0.conf
:
[Interface]
PrivateKey = 8AFbMaOQFaOYBrxrq7Kk/mt3jxa5Z1H27CIWNXs4vmY=
Address = 10.64.133.56/32
DNS = 193.138.218.74
PostUp = DROUTE=$(ip route | grep default | awk '{print $3}'); HOMENET=192.168.0.0/16; HOMENET2=10.0.0.0/8; HOMENET3=172.16.0.0/12; ip route add $HOMENET3 via $DROUTE; ip route add $HOMENET2 via $DROUTE; ip route add $HOMENET via $DROUTE; iptables -I OUTPUT -d $HOMENET -j ACCEPT; iptables -A OUTPUT -d $HOMENET2 -j ACCEPT; iptables -A OUTPUT -d $HOMENET3 -j ACCEPT; iptables -A OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT
PreDown = HOMENET=192.168.0.0/16; HOMENET2=10.0.0.0/8; HOMENET3=172.16.0.0/12; ip route delete $HOMENET; ip route delete $HOMENET2; ip route delete $HOMENET3; iptables -D OUTPUT ! -o %i -m mark ! --mark $(wg show %i fwmark) -m addrtype ! --dst-type LOCAL -j REJECT; iptables -D OUTPUT -d $HOMENET -j ACCEPT; iptables -D OUTPUT -d $HOMENET2 -j ACCEPT; iptables -D OUTPUT -d $HOMENET3 -j ACCEPT
[Peer]
PublicKey = M+KYHvnMLh57umbiaBOaivAnProWCAGeQpyFfwFF2iI=
AllowedIPs = 0.0.0.0/0
Endpoint = 89.45.90.197:51820
当我们运行 docker compose up -d
时,WireGuard 和 qBittorrent 容器都应该被创建并启动,qBittorrent 容器应将其所有流量通过 WireGuard 隧道发送,除了发往私有 IP 范围的连接,它们将通过我们的局域网,从而允许我们本地访问 qBittorrent 的 GUI。
这种方式对于单个容器效果很好,但如果在同一个 compose yaml 中包含多个容器,并且其中多个尝试在相同端口上监听,则可能会出现端口冲突。如果您无法修改内部端口(许多 linuxserver.io 容器不允许这样做),那么在这些情况下,第二种方法会更有效。
通过路由表将容器的流量路由到 WireGuard 容器
此方法与上面标题为“通过 WireGuard 路由所有流量”部分非常相似,在该部分中,我们修改了路由表以通过 WireGuard 容器路由流量。然而,由于我们现在是针对单个容器操作,我们将修改容器的路由表,而不是主机的路由表。
修改容器的路由表需要 Docker 默认未授予的额外权限,该权限是 NET_ADMIN
。有两种方法可以实现这一点。我们可以在创建容器时授予该权限,使容器内的进程能够修改路由表;或者,我们可以在容器创建后附加该权限,并通过 exec
进入容器以修改路由表。我们先关注后一种方法。
与本文的第一个场景一样,我们首先通过 docker network create --subnet 172.20.0.0/24 wgnet
创建一个用户定义的桥接网络,并通过 mkdir -p /home/aptalca/appdata/wireguard-client
创建文件夹。然后,我们可以使用以下 Compose 文件创建容器:
services:
wireguard:
image: lscr.io/linuxserver/wireguard
container_name: wireguard
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=America/New_York
volumes:
- /home/aptalca/appdata/wireguard-client:/config
- /lib/modules:/lib/modules
networks:
default:
ipv4_address: 172.20.0.50
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped
qbittorrent:
image: lscr.io/linuxserver/qbittorrent
container_name: qbittorrent
environment:
- PUID=1000
- PGID=1000
- TZ=Europe/London
- WEBUI_PORT=8080
ports:
- 8080:8080
restart: unless-stopped
networks:
default:
name: wgnet
external: true
我们将在 /home/aptalca/appdata/wireguard-client/wg0.conf
文件中使用 PostUp
和 PreDown
指令,与第一个示例相同,以启用通过 WireGuard 容器的路由:
[Interface]
PrivateKey = 8AFbMaOQFaOYBrxrq7Kk/mt3jxa5Z1H27CIWNXs4vmY=
Address = 10.64.133.56/32
DNS = 193.138.218.74
PostUp = iptables -t nat -A POSTROUTING -o wg+ -j MASQUERADE
PreDown = iptables -t nat -D POSTROUTING -o wg+ -j MASQUERADE
[Peer]
PublicKey = M+KYHvnMLh57umbiaBOaivAnProWCAGeQpyFfwFF2iI=
AllowedIPs = 0.0.0.0/0
Endpoint = 89.45.90.197:51820
在此示例中,qBittorrent 将使用其自己的网络栈,因此端口映射将在 qBittorrent
服务下定义。两个容器都将连接到相同的用户定义桥接网络 wgnet
,但是 qBittorrent 的流量默认情况下将通过主机的网关路由,如其路由表所示:
$ docker exec qbittorrent ip route show
default via 172.20.0.1 dev eth0
172.20.0.0/24 dev eth0 scope link src 172.20.0.2
$ docker exec qbittorrent curl -s https://am.i.mullvad.net/connected
You are not connected to Mullvad. Your IP address is 182.68.23.15
我们需要修改默认路由,以便流量通过 WireGuard 容器路由(通过以 --privileged
模式运行命令以获取修改路由的 NET_ADMIN
权限来实现):
$ docker exec --privileged qbittorrent ip route del default
$ docker exec --privileged qbittorrent ip route add default via 172.20.0.50
接着我们检查路由:
$ docker exec qbittorrent ip route
default via 172.20.0.50 dev eth0
172.20.0.0/24 dev eth0 scope link src 172.20.0.2
并测试连接:
$ docker exec qbittorrent curl -s https://am.i.mullvad.net/connected
You are connected to Mullvad (server us68-wireguard). Your IP address is 89.45.90.206
太棒了!现在所有容器流量都通过 WireGuard 隧道进行路由。
通过这些路由,除了发往 172.20.0.0/24
的流量外,所有连接都会被强制通过 WireGuard 隧道。这对公共连接非常有用,但如果尝试从局域网访问 qBittorrent 的 WebGUI,可能会出现一些问题。如果我们使用像 SWAG 这样的反向代理来访问 qBittorrent 的 GUI(该代理与 qBittorrent 位于相同的 Docker 网络 wgnet
上),那么我们不需要任何额外的路由。SWAG 将具有 172.20.0.0/24
范围内的 IP 地址,而现有的路由将允许 qBittorrent 通过 Docker 网络向 SWAG 发送数据包,而 SWAG 可以自由地向我们的局域网(或广域网)发送数据包。
然而,如果我们尝试直接通过局域网访问 qBittorrent 的 WebGUI,则 qBittorrent 试图发送到本地浏览器的所有数据包都会匹配 default route
,并被强制通过隧道,无法到达局域网。为了访问 WebGUI(更准确地说,是从 WebGUI 接收到响应),我们需要创建一条返回局域网的路由,以便连接能够到达本地浏览器。如果我们从局域网子网 192.168.1.0/24
连接到 GUI,则可以为其创建如下路由:
$ docker exec --privileged qbittorrent ip route add 192.168.1.0/24 via 172.20.0.1
现在,我们应该能够直接通过局域网访问 qBittorrent 的 WebGUI(或者更确切地说,从中接收到响应),因为发往局域网子网的数据包将匹配新路由,并通过 Docker 网关进行路由。
如上面第一个示例所述,这些对路由表的更改在重启时会被重置。因此,如果我们重启或重新创建 qBittorrent 容器,就必须重新运行命令来更改默认路由以指向 WireGuard 容器。我们可以通过主机上的 systemd 服务(如第一个示例所示)或其它允许您运行启动脚本的系统方法来完成,或者直接在容器内部通过自定义脚本完成。请注意,如果您在容器内部执行此操作,而不是通过 exec
进入容器,则需要在 Compose 文件中添加 cap_add: NET_ADMIN
。
入站端口转发
qBittorrent 依赖入站端口与其它节点建立连接。上文中,我们已经建立了通过 WireGuard 隧道路由出站数据包的规则。为了让入站数据包到达 qBittorrent 容器,我们需要首先让我们的 VPN 提供商为我们转发一个端口,然后将该端口转发到 qBittorrent 容器。VPN 提供商的端口转发支持各不相同。Mullvad 每个账户最多允许转发 5 个端口,并随机分配。转发的端口与 WireGuard 服务器所在的城市和本地的公钥有关。
假设 Mullvad 为我们在选择的城市转发了端口 58787
,并且我们的 qBittorrent 客户端的 Docker IP 是 172.20.0.2
。我们可以通过以下 iptables 规则告诉 WireGuard 将该入站端口转发到 qBittorrent 容器:
iptables -t nat -A PREROUTING -p tcp --dport 58787 -j DNAT --to-destination 172.20.0.2:58787
我们将此规则添加到 wg0.conf
中,这样规则会在隧道创建时设置,并在销毁前删除。
[Interface]
PrivateKey = 8AFbMaOQFaOYBrxrq7Kk/mt3jxa5Z1H27CIWNXs4vmY=
Address = 10.64.133.56/32
DNS = 193.138.218.74
PostUp = iptables -t nat -A POSTROUTING -o wg+ -j MASQUERADE; iptables -t nat -A PREROUTING -p tcp --dport 58787 -j DNAT --to-destination 172.20.0.2:58787
PreDown = iptables -t nat -D POSTROUTING -o wg+ -j MASQUERADE; iptables -t nat -D PREROUTING -p tcp --dport 58787 -j DNAT --to-destination 172.20.0.2:58787
[Peer]
PublicKey = M+KYHvnMLh57umbiaBOaivAnProWCAGeQpyFfwFF2iI=
AllowedIPs = 0.0.0.0/0
Endpoint = 89.45.90.197:51820
现在端口已经在 Mullvad 和 WireGuard 客户端容器上完成了转发,我们可以将 qBittorrent 设置中的入站连接端口设置为 58787
,这样其它节点就可以通过端口 58787
连接到我们的 qBittorrent 客户端,使用的是 Mullvad 的公网 IP 地址。
进一步阅读
通过以上场景,您可以保护自己的在线隐私,避免被 ISP 和其他人监视,但请记住,这意味着您将信任转移到您的 VPN 提供商。因此,请谨慎选择。
还需要注意的是,并非所有服务都接受来自公共 VPN 的连接。许多流媒体服务会主动屏蔽公共 VPN 的连接,一些网站(例如 Craigslist、Etsy 和 GameStop)也是如此。如果您决定通过公共 VPN 路由流量,可能需要创建额外的规则和路由,以绕过此类连接上的 VPN。
以下是一些可能对您有帮助的其它指南: