Linux 容器/使用 VPN

出自 ArchWiki

本文介绍如何设置 Linux 容器 以运行多种 VPN 协议,并使用“kill switch”(终止开关)来安全/私密地使用互联网。与使用像 VirtualBoxQEMU 这样的全功能虚拟化相比,这样做的一个显著优势是资源开销极小,并且能够在低功耗设备上运行。

容器设置

需要对 Linux 容器 的基本设置和理解。本文假设读者已经拥有基本的 LXC 设置并使其正常运行。

服务器模式下的 OpenVPN

本小节详细介绍了在容器中提供 OpenVPN 服务所需的一些额外设置。想要使用提供的 OpenVPN 配置文件的用户无需阅读本小节。

主机设置

  1. 主机操作系统需要桥接以太网设置,以允许容器运行。请参阅 Linux 容器#主机网络配置 以了解相关信息。
  2. 需要启用数据包转发。请参阅 Internet 共享#启用数据包转发 以了解相关信息。
  3. 虽然不是严格必需的,但强烈建议使用防火墙。

客户端模式下的 OpenVPN

需要修改容器的配置以使用 OpenVPN,如下所示

/var/lib/lxc/playtime/config
...

## for OpenVPN
lxc.mount.entry = /dev/net dev/net none bind,create=dir
lxc.cgroup2.devices.allow = c 10:200 rwm

安装 openvpn。如果使用容器连接到第三方 VPN 提供商,只需将配置文件 foo.conf 放在 /etc/openvpn/client/foo.conf 中即可使用。要验证容器内的 OpenVPN 功能,启动 OpenVPN,通过 openvpn-client@foo.service,并在满意后 启用 它以在启动时运行。

对于其他用例和设置,请参阅 OpenVPN

注意:非特权容器中运行 OpenVPN 的用户将需要创建一个自定义 systemd 单元,以在容器内启动它。在替换单元文件中,注释掉以 LimitNPROC... 开头的行。

WireGuard

安装 wireguard-tools。用户将拥有第三方 VPN 服务提供的 WireGuard 配置文件,或者将设置 WireGuard 以在此角色中提供服务。如果使用容器连接到 VPN 提供商,只需将配置文件 foo.conf 放在 /etc/wireguard/ 中即可使用。

要验证容器内的 WireGuard 功能,启动 WireGuard,通过 wg-quick@foo.service,并在满意后 启用 它以在启动时运行。

对于其他用例,请参阅 WireGuard

容器内的防火墙配置

强烈建议在容器内运行正确配置的 防火墙。容器内防火墙的作用有两个方面

  1. 提供一个功能性的“kill switch”(终止开关),以在 VPN 连接失败时保持隐私。
  2. 阻止恶意内容进入。

本指南使用易于配置的 ufw,但当然也可以使用其他示例。

提示: 要完全重置 ufw 的配置文件,请使用 reset 参数调用它:ufw reset

功能性“kill switch”(终止开关)的策略很简单,就是设置拒绝策略,然后仅允许 VPN 设备上的特定服务和流量。这样,如果该设备的连接中断,则不会有本地回退。

注意: 下面显示的方法的局限性在于 VPN 配置文件不能使用域名,例如 www.myvpn.com,它们需要使用相应的 IP 地址。如上所述,当 VPN 未连接时,容器 DNS 解析将按设计禁用。因此,为了连接,必须提供数字 IP。

编辑 /etc/default/ufw 并将 DEFAULT_OUTPUT_POLICY 从 “ACCEPT” 更改为 “DROP”

/etc/default/ufw
DEFAULT_OUTPUT_POLICY="DROP"
注意: 以下调用 ufw 的命令需要以 root 用户身份执行;以 “#” 符号为前缀的这些命令按照标准的 wiki 注释方式已被省略,以便于干净地复制/粘贴到终端。

设置拒绝策略

ufw default deny outgoing
ufw default deny incoming

可选地,添加在文件中定义的任何预定义或自定义规则,例如 /etc/ufw/applications.d/custom

ufw allow ssh
ufw allow from my-custom-app1
ufw allow from my-custom-app2

可选地,进一步限制来自内部 LAN IP 范围甚至单个 IP 地址的访问

ufw allow from 192.168.1.0/24

WireGuard 用户将创建一个与其配置文件同名的接口,例如,/etc/wireguard/foo.conf,而 OpenVPN 用户可能正在使用 tun0。在下面的一行中,将 'foo' 替换为 WireGuard 配置的名称(省略 .conf 后缀),或者如果使用 OpenVPN,则将 'foo' 替换为 tun0 或正在使用的任何设备

ufw allow out on foo from any to any

最后,允许访问 VPN 提供商在预期端口上的 IP 地址,并定义预期的协议。在下面的一行中,有三个变量需要考虑,定义如下

  • “xxx” 代表 WireGuard 对等方/OpenVPN 服务器的 IP 地址。它将在 VPN 提供商提供的相应配置文件中定义。
  • “yyy” 代表通信发生的端口。同样,这将位于配置文件中。
  • “zzz” 代表要使用的协议,并从 udp 或 tcp 中选择。请注意,WireGuard 仅支持 udp,而 OpenVPN 支持两者。
ufw allow out to xxx port yyy proto zzz
注意: 如果预期使用多个服务器,请为 VPN 提供商定义的每个 IP 地址 (xxx) 重复此操作。

启动 ufw 并 启用 ufw.service 以在启动时启动。

在配置文件中使用 VPN 域名的一种蹩脚的变通方法

如果希望在 VPN 配置文件中使用域名,则主机上的 shell 脚本可以预先将其解析为数字 IP,然后通过将其存储在写入容器内文件的变量中,将该 IP 地址传递给容器。该文件反过来可以被修改后的 VPN systemd 服务读取。它有效,但有点蹩脚。

编辑两个变量以匹配容器名称和与您的用例对应的服务器名称

在主机上

安装 bind (dig 所需) 并创建以下脚本

/path/to/container-start.sh
#!/bin/bash
# this script should be called as root
container=foo
server=www.myvpnserver.org
 
if ! systemctl is-active lxc@"$container" &>/dev/null; then
  ToUse=$(dig +short "$server")
  [[ -d /var/lib/lxc/$container/rootfs/etc/conf.d ]] || mkdir -p /var/lib/lxc/$container/rootfs/etc/conf.d
  echo "SERVER=$ToUse" > /var/lib/lxc/$container/rootfs/etc/conf.d/server.hack.txt
  systemctl start lxc@"$container"
fi

从现在开始,调用该脚本以启动容器。它将使用 dig 从域名获取 IP 地址,然后它将启动容器。

从容器内部

修改启动 VPN 的 systemd 服务,并创建一个骨架配置文件,该文件可以使用我们在 /var/lib/lxc/$container/rootfs/etc/conf.d/server.hack.txt 中定义的 IP 地址进行修改,该文件由我们刚刚创建的脚本创建。

要制作骨架配置文件,只需将活动配置文件重命名为另一个名称。

例如使用 WireGuard

mv /etc/wireguard/foo.conf /etc/wireguard/foo.skel

现在编辑 /etc/wireguard/foo.skel 以将 Endpoint = www.myvpnserver.org 替换为 @@@,例如

Endpoint = @@@:51820

或者如果使用 OpenVPN

mv /etc/openvpn/client/foo.conf /etc/openvpn/client/foo.skel

编辑 /etc/openvpn/client/foo.skel 以将 remote www.myvpnserver.org 替换为 @@@,例如

remote @@@

最后,创建一个 drop-in 文件,以读取 IP 并将其替换为实际配置文件。

使用 WireGuard 的示例

/etc/systemd/system/wg-quick@foo.service.d/override.conf
[Service]
EnvironmentFile=-/etc/conf.d/server.hack.txt
ExecStartPre=/bin/bash -ac "sed s/@@@/$SERVER/ </etc/wireguard/foo.skel >/etc/wireguard/foo.conf"

使用 OpenVPN 的示例

/etc/systemd/system/openvpn-client@foo.service.d/override.conf
[Service]
EnvironmentFile=-/etc/conf.d/server.hack.txt
ExecStartPre=/bin/bash -ac "sed s/@@@/$SERVER/ </etc/openvpn/client/foo.skel >/etc/openvpn/client/foo.conf"

测试服务

从正在运行的容器内部(通过 ssh 或 lxc-attach -n playtime 连接),通过将浏览器导出到主机的 X 服务器来测试设置

$ DISPLAY=:0 firefox
提示: 通过 ssh 连接将需要允许本地显示接受连接。通过 xhost +SI:localuser:yourusername 执行此操作,然后通过 ssh 连接到容器。

结果应该是在主机的 X 服务器中出现一个标题为“Mozilla Firefox (playtime)”的 firefox 窗口。许多网站可用于验证 IP 地址和 DNS 条目的状态。其中一个网站是 ipleak dot net

此时,应该只显示与配置文件中定义的条目相对应的 DNS 条目。