OpenVPN
本文介绍了 OpenVPN 的基本安装和配置,适用于个人和小企业使用。 更多详细信息,请参阅 openvpn(8) 手册页和 OpenVPN 文档。 OpenVPN 是一个强大且高度灵活的 VPN 守护程序。 它支持 SSL/TLS 安全性、以太网桥接、通过 代理 或 NAT 的 TCP 或 UDP 隧道传输。 此外,它还支持动态 IP 地址和 DHCP,可扩展到数百或数千用户,并可移植到大多数主要操作系统平台。
OpenVPN 与 OpenSSL 库紧密结合,并从中获得了大部分加密功能。 它支持使用 预共享密钥(静态密钥模式)或使用客户端和服务器证书的 公钥安全 (SSL/TLS 模式)的传统加密。 此外,它还支持未加密的 TCP/UDP 隧道。
OpenVPN 旨在与大多数平台上存在的 TUN/TAP 虚拟网络接口一起工作。 总体而言,它的目标是提供 IPSec 的许多关键功能,但占用空间相对较小。 OpenVPN 由 James Yonan 编写,并根据 GNU 通用公共许可证 (GPL) 发布。
安装
安装 openvpn 软件包,该软件包同时提供服务器和客户端模式。
可用的前端
- NetworkManager OpenVPN — 用于 OpenVPN 的 NetworkManager VPN 插件。
- QOpenVPN — 简单的 OpenVPN GUI,用 PyQt 为基于 systemd 的发行版编写。
- eOVPN — 用于连接、管理和更新 OpenVPN 配置的应用程序。 用 GTK 编写。 支持 OpenVPN 2 和 OpenVPN 3,并具有 数据通道卸载
内核配置
OpenVPN 需要 TUN/TAP 支持,这已在默认内核中配置。 自定义内核的用户应确保在设备驱动程序 > 网络设备支持 > 网络核心驱动程序支持 > 通用 TUN/TAP 设备驱动程序支持中启用 tun
模块 (CONFIG_TUN
)。
阅读 内核模块 以获取更多信息。
连接到第三方提供的 VPN
要连接到第三方提供的 VPN 服务,以下大部分内容很可能可以忽略,尤其是关于服务器设置。 从#客户端配置文件开始,然后跳到#启动 OpenVPN。 应该使用提供商证书和说明,请参阅Category:VPN providers 以获取可以适应其他提供商的示例。 Linux 容器中的 OpenVPN 客户端 也具有通用的适用说明,同时通过将 OpenVPN 客户端进程隔离到容器中更进一步。
从头开始创建公钥基础设施 (PKI)
在设置 OpenVPN 服务器时,用户需要创建 公钥基础设施 (PKI),这在 Easy-RSA 文章中详细介绍。 按照单独文章中的步骤创建所需的证书、私钥和相关文件后,此时应在 /etc/openvpn/server
中有 5 个文件
ca.crt dh.pem servername.crt servername.key ta.key
或者,从 OpenVPN 2.4 开始,可以使用 Easy-RSA 使用椭圆曲线生成证书和密钥。 有关详细信息,请参阅 OpenVPN 文档。
基本的 Layer-3 IP 路由配置
OpenVPN 是一个功能极其通用的软件,可以进行多种配置,实际上机器可以同时充当服务器和客户端。
随着 v2.4 的发布,服务器配置存储在 /etc/openvpn/server
中,客户端配置存储在 /etc/openvpn/client
中,并且每种模式都有其各自的 systemd 单元,即 openvpn-client@.service
和 openvpn-server@.service
。
示例配置
OpenVPN 软件包附带了一系列用于不同用途的示例配置文件。 示例服务器和客户端配置文件是基本 OpenVPN 设置的理想起点,具有以下功能
- 使用 公钥基础设施 (PKI) 进行身份验证。
- 使用虚拟 TUN 网络接口(OSI Layer-3 IP 路由)创建 VPN。
- 侦听 UDP 端口 1194 上的客户端连接(OpenVPN 的官方 IANA 端口号[1])。
- 从 10.8.0.0/24 子网向连接的客户端分配虚拟地址。
对于更高级的配置,请参阅 openvpn(8) 手册页和 OpenVPN 文档。
服务器配置文件
将示例服务器配置文件 /usr/share/openvpn/examples/server.conf
复制到 /etc/openvpn/server/server.conf
。
编辑文件,至少进行以下更改
/etc/openvpn/server/server.conf
ca ca.crt cert servername.crt key servername.key dh dh.pem tls-crypt ta.key # Replaces tls-auth ta.key 0 user nobody group nobody
如果使用带有椭圆曲线的 TLS,请指定 dh none
和 ecdh-curve secp521r1
(或 ecdh-curve ed25519
)。 使用椭圆曲线时,不使用 DH 参数文件。 从 OpenVPN 2.4.8 开始,需要在服务器配置中指定椭圆曲线的类型。 否则,服务器将无法识别曲线类型,并可能使用不兼容的曲线类型,从而导致身份验证错误。
服务器加固
如果安全是首要任务,建议进行额外的配置,包括:限制服务器使用强密码/身份验证方法,以及(可选)将启用的 TLS 密码集限制为较新的密码。 从 OpenVPN 2.4 开始,服务器和客户端将在 TLS 模式下自动协商 AES-256-GCM。
将以下内容添加到 /etc/openvpn/server/server.conf
前沿安全性
/etc/openvpn/server/server.conf
cipher AES-256-GCM auth SHA512 tls-version-min 1.3 #Uncomment tls-cipher to limit possible negotiation options to the strongest ciphers, doing so it's no longer possible to generate certs with current easyrsa, more information #tls-cipher TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
与大多数设备的兼容性
/etc/openvpn/server/server.conf
cipher AES-256-GCM auth SHA512 tls-version-min 1.2 tls-cipher TLS-DHE-RSA-WITH-AES-256-GCM-SHA384:TLS-DHE-RSA-WITH-AES-128-GCM-SHA256:TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA
- .ovpn 客户端配置文件必须包含匹配的 cipher 和 auth 行才能正常工作(至少对于 iOS 和 Android 客户端而言)。
- 错误地使用
tls-cipher
可能会导致调试连接困难,并且可能不是必需的。 有关更多信息,请参阅 OpenVPN 的社区 wiki。
启用压缩
上游不建议启用压缩; 这样做会使服务器面临所谓的 VORACLE 攻击向量。 请参阅 这篇文章。
偏离标准端口和/或协议
通常建议通过 UDP 使用 OpenVPN,因为 TCP over TCP 是一个坏主意[2]。
某些网络可能会禁止在默认端口和/或协议上进行 OpenVPN 连接。 规避此问题的一种策略是模拟极有可能畅通无阻的 HTTPS 流量。
为此,请将 /etc/openvpn/server/server.conf
配置为如下所示
/etc/openvpn/server/server.conf
port 443 proto tcp
在物理机上不同端口运行 OpenVPN 的多个实例
可以在同一台机器上同时运行 OpenVPN 的多个并发实例。 每个服务器都需要在 /etc/openvpn/server/
中定义为单独的 .conf 文件。 至少,并行服务器需要在不同的端口上运行。 更高级的设置超出了本指南的范围。
考虑以下示例,运行 2 个并发服务器,一个端口为 443/udp,另一个端口为 80/tcp。
首先修改创建的 /etc/openvpn/server/server.conf
,如下所示
/etc/openvpn/server/server.conf
port 443 proto udp server 10.8.0.0 255.255.255.0
现在复制它并修改副本以在 80/tcp 上运行
/etc/openvpn/server/server2.conf
port 80 proto tcp server 10.8.1.0 255.255.255.0
请务必在防火墙中设置相应的条目,请参阅 #防火墙配置 中的相关部分。
客户端配置文件
将示例客户端配置文件 /usr/share/openvpn/examples/client.conf
复制到 /etc/openvpn/client/
。
编辑以下内容
remote
指令以反映服务器的完全限定域名、主机名(客户端已知)或其 IP 地址。- 取消注释
user
和group
指令以降低权限。 ca
、cert
和key
参数以反映密钥和证书的路径和名称。- 启用 TLS HMAC 握手保护 (
--tls-crypt
或--tls-auth
)。
/etc/openvpn/client/client.conf
client remote elmer.acmecorp.org 1194 user nobody group nobody ca ca.crt cert client.crt key client.key tls-crypt ta.key # Replaces tls-auth ta.key 1
以非特权用户身份运行
在配置文件中使用选项 user nobody
和 group nobody
会使 OpenVPN 在建立连接后放弃其 root
权限。 缺点是,在 VPN 断开连接后,守护程序无法再次删除其设置的网络路由。 如果想要限制在没有 VPN 连接的情况下传输流量,则可能会认为持久路由是有益的。 但是,也可能发生 OpenVPN 服务器在隧道运行时将更新推送到路由。 权限降低的客户端将无法执行更新并退出并显示错误。
由于管理路由似乎需要手动操作,因此 user nobody
和 group nobody
选项可能看起来不受欢迎。 但是,根据设置,有不同的方法可以处理这些情况
- 对于单元的错误,一种简单的方法是编辑它,并在
[Service]
部分中添加Restart=on-failure
。 但是,仅此一项不会删除任何过时的路由,因此可能会发生重新启动的隧道未正确路由的情况。 - 该软件包包含
/usr/lib/openvpn/plugins/openvpn-plugin-down-root.so
,可用于让 openvpn fork 一个具有 root 权限的进程,其唯一任务是在从主进程接收到 down 信号时执行自定义脚本,该主进程正在处理具有降低权限的隧道(另请参阅其 README)。
下面链接的 OpenVPN HowTo 通过创建专用的非特权用户/组(而不是已存在的 nobody
)更进一步。 优点是避免了在守护程序之间共享用户时可能存在的潜在风险
- OpenVPN HowTo 解释了另一种创建非特权用户模式和包装器脚本以自动恢复路由的方法。
- 可以首先让 OpenVPN 以非特权用户身份启动,而无需以 root 身份运行,请参阅 此 OpenVPN wiki (howto)。 该 howto 假定存在 System V init,而不是 Systemd,并且不涵盖
--up
/--down
脚本的处理 - 这些脚本应以与 ip 命令相同的方式处理,并额外注意访问权限。 - 也可以从非特权 podman 容器中运行 OpenVPN,请参阅 OpenVPN HowTo 的此部分
将证书转换为加密的 .p12 格式
某些软件只会读取存储在密码加密的 .p12 文件中的 VPN 证书。 这些可以使用以下命令生成
# openssl pkcs12 -export -inkey keys/bugs.key -in keys/bugs.crt -certfile keys/ca.crt -out keys/bugs.p12
测试 OpenVPN 配置
在服务器上运行 openvpn /etc/openvpn/server/server.conf
(作为 root 用户),并在客户端上运行 openvpn /etc/openvpn/client/client.conf
(作为 root 用户)。 示例输出应类似于以下内容
# openvpn /etc/openvpn/server/server.conf
Wed Dec 28 14:41:26 2011 OpenVPN 2.2.1 x86_64-unknown-linux-gnu [SSL] [LZO2] [EPOLL] [eurephia] built on Aug 13 2011 Wed Dec 28 14:41:26 2011 NOTE: OpenVPN 2.1 requires '--script-security 2' or higher to call user-defined scripts or executables Wed Dec 28 14:41:26 2011 Diffie-Hellman initialized with 2048 bit key ... Wed Dec 28 14:41:54 2011 bugs/95.126.136.73:48904 MULTI: primary virtual IP for bugs/95.126.136.73:48904: 10.8.0.6 Wed Dec 28 14:41:57 2011 bugs/95.126.136.73:48904 PUSH: Received control message: 'PUSH_REQUEST' Wed Dec 28 14:41:57 2011 bugs/95.126.136.73:48904 SENT CONTROL [bugs]: 'PUSH_REPLY,route 10.8.0.1,topology net30,ping 10,ping-restart 120,ifconfig 10.8.0.6 10.8.0.5' (status=1)
# openvpn /etc/openvpn/client/client.conf
Wed Dec 28 14:41:50 2011 OpenVPN 2.2.1 i686-pc-linux-gnu [SSL] [LZO2] [EPOLL] [eurephia] built on Aug 13 2011 Wed Dec 28 14:41:50 2011 NOTE: OpenVPN 2.1 requires '--script-security 2' or higher to call user-defined scripts or executables ... Wed Dec 28 14:41:57 2011 GID set to nobody Wed Dec 28 14:41:57 2011 UID set to nobody Wed Dec 28 14:41:57 2011 Initialization Sequence Completed
查找服务器上分配给 tunX 接口的 IP 地址,并从客户端 ping 它。
查找客户端上分配给 tunX 接口的 IP 地址,并从服务器 ping 它。
配置 MTU、Fragment 和 MSS
如果在通过 OpenVPN 使用(远程)服务(例如 Web 浏览、DNS、NFS)时遇到问题,则可能需要手动设置 MTU 值。
以下消息可能表明应调整 MTU 值
read UDPv4 [EMSGSIZE Path-MTU=1407]: Message too long (code=90)
为了获得最大段大小 (MSS),客户端需要发现到服务器路径上的最小 MTU。 为了做到这一点,ping 服务器并禁用分片,然后指定最大数据包大小 [3]
# ping -M do -s 1500 -c 1 example.com
每次将 1500 值减小 10,直到 ping 成功。
更新客户端配置以使用成功的 MTU 值,例如
/etc/openvpn/client/client.conf
remote example.com 1194 tun-mtu 1400 mssfix 1360
可以指示 OpenVPN 在客户端连接时每次都测试 MTU。 请耐心等待,因为客户端可能不会通知正在运行的测试,并且连接在完成之前可能显示为无法正常工作。 以下内容将为 OpenVPN 启动时间增加约 3 分钟。 建议配置分片大小,除非客户端将通过许多不同的网络进行连接,并且瓶颈不在服务器端
/etc/openvpn/client/client.conf
remote example.com 1194 mtu-test
IPv6
通过 IPv6 连接到服务器
从 OpenVPN 2.4 开始,当仅使用 proto udp
或 proto tcp
时,OpenVPN 将使用操作系统定义的 AF_INET
,在大多数情况下,这将仅是 IPv4。 要同时使用 IPv4 和 IPv6,请使用 proto udp6
或 proto tcp6
。 要强制仅使用 IPv4,请使用 proto udp4
或 proto tcp4
。 在较旧的 OpenVPN 版本上,一个服务器实例只能支持 IPv4 或 IPv6。
在隧道内提供 IPv6
为了在隧道内提供 IPv6,请将 IPv6 前缀路由到 OpenVPN 服务器。 要么在网关上设置静态路由(如果分配了静态块),要么使用 DHCPv6 客户端通过 DHCPv6 前缀委派获取前缀(有关详细信息,请参阅 IPv6 前缀委派)。 还要考虑使用地址块 fc00::/7 中的唯一本地地址。 两种方法都有优点和缺点
- 许多 ISP 仅提供动态变化的 IPv6 前缀。 OpenVPN 不支持前缀更改,因此每次前缀更改时都更改 server.conf(也许可以使用脚本自动化)。
- ULA 地址未路由到 Internet,并且设置 NAT 不像 IPv4 那样简单。 这意味着无法通过隧道路由所有流量。 那些想要通过 IPv6 连接两个站点而无需通过隧道连接到 Internet 的人可能希望使用 ULA 地址以方便使用。
或者,NDP 代理应该可以工作。 请参阅 此 StackExchange 帖子。
收到前缀(建议使用 /64)后,将以下内容附加到 server.conf
server-ipv6 2001:db8:0:123::/64
这是 IPv6 等效于 OpenVPN 的默认 10.8.0.0/24 网络,需要从 DHCPv6 客户端获取。 或例如使用 fd00:1234::/64。
那些想要将路由推送到家庭网络(相当于 192.168.1.0/24)的人还需要附加
push "route-ipv6 2001:db8:0:abc::/64"
OpenVPN 尚不包含 DHCPv6,因此没有例如通过 IPv6 推送 DNS 服务器的方法。 这需要使用 IPv4 完成。 OpenVPN Wiki 提供了一些其他配置选项。
启动 OpenVPN
手动启动
要排除 VPN 连接故障,请以 root 身份使用 openvpn /etc/openvpn/client/client.conf
手动启动客户端守护程序。 可以使用服务器自己的配置文件以相同的方式启动服务器(例如,openvpn /etc/openvpn/server/server.conf
)。
systemd 服务配置
要在系统启动时自动启动 OpenVPN 服务器,请在适用的计算机上启用 openvpn-server@configuration.service
。 对于客户端,请启用 openvpn-client@configuration.service
。(从 configuration
字符串中省略 .conf
。)
例如,如果客户端配置文件为 /etc/openvpn/client/client.conf
,则服务名称为 openvpn-client@client.service
。 或者,如果服务器配置文件为 /etc/openvpn/server/server.conf
,则服务名称为 openvpn-server@server.service
。
openvpn-client@configuration.service
单元启动时间过长,则可能是网络管理器未在正确的时间触发 network-online.target
systemd 目标。 例如,当使用 systemd-networkd 时,请检查 systemd-networkd-wait-online.service
是否已正确配置。让 NetworkManager 启动连接
可能并非总是需要运行 VPN 隧道,和/或只想为特定的 NetworkManager 连接建立 VPN 隧道。 这可以通过将脚本添加到 /etc/NetworkManager/dispatcher.d/
来完成。 在以下示例中,“Provider”是 NetworkManager 连接的名称
/etc/NetworkManager/dispatcher.d/10-openvpn
#!/bin/sh case "$2" in up) if [ "$CONNECTION_ID" = "Provider" ]; then systemctl start openvpn-client@<configuration> fi ;; down) systemctl stop openvpn-client@<configuration> ;; esac
有关更多详细信息,请参阅 NetworkManager#使用 NetworkManager 调度器的网络服务。
NetworkManager 原生 VPN 配置
NetworkManager 支持使用 networkmanager-openvpn 管理 OpenVPN。
- NetworkManager 仅支持 mobile 配置文件(通常以 .ovpn 结尾),即包含内联证书且仅有一个文件(Linux 配置通常附带 userpass 文件和分离的证书)。
- 对于不支持的选项不会产生错误消息,请打开 日志 以查看 networkmanager 使用的选项。
GUI 配置
在您的桌面环境中,打开 网络设置 (或 nm-connection-editor
)。单击加号以添加新连接,然后选择 OpenVPN 并手动输入设置。您也可以选择导入 #客户端配置文件,方法是选择“导入已保存的 VPN 配置...”并选择相应的文件。
CLI 配置
用于导入配置
$ nmcli connection import type openvpn file file.ovpn
进行手动配置
$ nmcli connection add type vpn vpn-type openvpn ...
有关详细选项,请参阅 nm-settings-nmcli(5) § vpn 设置。
如果您想设置登录名和密码,请检查 openvpn 文件中是否没有 auth-user-pass
行,或者将其删除。然后在导入后
$ nmcli connection modify name \ +vpn.data "connection-type=password-tls, username=USERNAME" \ vpn.user-name USERNAME \ +vpn.secrets "password=PASS"
之后,您将能够使用以下命令连接,而无需重新输入密码
$ nmcli connection up name
与连接同步状态
NetworkManager 支持将 VPN 状态与 接口连接状态 同步,即在连接建立时同时启动 VPN,并在连接断开时关闭 VPN。
要实现此目的,请打开 nm-connection-editor
并选择一个网络连接(不是 VPN 连接),然后转到“常规”部分,勾选“自动连接到 VPN”,并在下拉菜单中选择相应的配置。
通过 CLI 自动连接到 VPN
首先,按如下方式列出您的连接
nmcli connection
NAME UUID TYPE DEVICE zrh-003 d46e4a92-778e-4792-b085-e1f638ecb8e3 vpn enp1s0 enp1s0 1715b889-3c47-3e21-a86f-94ce207297a9 ethernet enp1s0 tun0 7405f329-255d-4b50-b98d-c2e865a443a4 tun tun0
复制您要自动连接的 VPN 连接的 UUID(此处为 d46e4a92-778e-4792-b085-e1f638ecb8e3),然后编辑主连接(此处为以太网连接)以使其使用 VPN
$ nmcli c edit enp1s0 nmcli> set connection.secondaries d46e4a92-778e-4792-b085-e1f638ecb8e3 nmcli> save persistent Connection 'enp1s0' (1715b889-3c47-3e21-a86f-94ce207297a9) successfully updated.
之后,重启 NetworkManager.service
故障排除
无证书密码
如果您收到
Warning: password for 'vpn.secrets.password' not given in 'passwd-file' and nmcli cannot ask without '--ask' option. Error: Connection activation failed: No valid secrets
即使使用
[vpn] cert-pass-flags=0
您可以添加
[vpn-secrets] cert-pass=anything_you_want
通过服务器路由客户端流量
在没有进一步配置的情况下,只有直接往返 OpenVPN 服务器 IP 的流量才会通过 VPN。要使其他流量(例如 Web 流量)通过 VPN,必须添加相应的路由。您可以客户端配置中添加路由,或者配置服务器将这些路由推送给客户端。
要将流量重定向到服务器的子网或从服务器的子网重定向流量,请在 remote <address> <port> udp/tcp
之前添加 push "route <address pool> <subnet>"
,例如
route 192.168.1.0 255.255.255.0
要将包括 Internet 流量在内的所有流量重定向到服务器,请在客户端配置中添加以下内容
redirect-gateway def1 bypass-dhcp ipv6
如果运行仅 IPv4 服务器,请删除 ipv6
选项。如果运行仅 IPv6 服务器,请使用 redirect-gateway ipv6 !ipv4
。
要使服务器推送路由,请将 push "redirect-gateway def1 bypass-dhcp ipv6"
追加 到服务器的配置文件(即 /etc/openvpn/server/server.conf
)[4]。请注意,这不是必需的,甚至可能导致性能问题。
push "redirect-gateway def1 bypass-dhcp ipv6"
如果运行仅 IPv4 服务器,请删除 ipv6
选项。如果运行仅 IPv6 服务器,请使用 push "redirect-gateway ipv6 !ipv4"
使用 push "route <address pool> <subnet>"
选项允许客户端访问服务器后面的其他子网/设备
push "route 192.168.1.0 255.255.255.0" push "route 192.168.2.0 255.255.255.0"
可选地,将本地 DNS 设置推送到客户端(例如,路由器的 DNS 服务器和域名前缀 .internal
)
push "dhcp-option DNS 192.168.1.1" push "dhcp-option DOMAIN internal"
设置好配置文件后,在服务器上启用数据包转发。此外,还需要调整服务器的防火墙以允许 VPN 流量,这将在下面针对 ufw 和 iptables 进行描述。
防火墙配置
firewalld
如果使用默认端口 1194,请启用 openvpn
服务。否则,创建一个使用不同端口的新服务。
# firewall-cmd --zone=public --add-service openvpn
现在将伪装添加到区域
# firewall-cmd --zone=FedoraServer --add-masquerade
使这些更改永久生效
# firewall-cmd --runtime-to-permanent
ufw
为了允许 ufw 转发(VPN)流量,请将以下内容 追加 到 /etc/default/ufw
/etc/default/ufw
DEFAULT_FORWARD_POLICY="ACCEPT"
更改 /etc/ufw/before.rules
,并将以下代码 追加 到标头之后和 *filter
行之前
- 更改 IP/子网掩码以匹配 OpenVPN 服务器配置中设置的服务器。
- 将 网络接口 更改为 OpenVPN 服务器使用的连接。
/etc/ufw/before.rules
# NAT (Network Address Translation) table rules *nat :POSTROUTING ACCEPT [0:0] # Allow traffic from clients to the interface -A POSTROUTING -s 10.8.0.0/24 -o interface -j MASQUERADE # Optionally duplicate this line for each subnet if your setup requires it -A POSTROUTING -s 10.8.1.0/24 -o interface -j MASQUERADE # do not delete the "COMMIT" line or the NAT table rules above will not be processed COMMIT # Don't delete these required lines, otherwise there will be errors *filter
确保打开选择的 OpenVPN 端口(默认 1194/udp)
# ufw allow 1194/udp
# ufw reload
iptables
为了允许 VPN 流量通过 iptables 防火墙,首先在服务器上创建一个用于 NAT 转发的 iptables 规则 [5]。一个示例(假设要转发到的接口名为 eth0
)
# iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
如果在不同的 IP 池上运行多个服务器,请为每个服务器添加相应的行,例如
# iptables -t nat -A POSTROUTING -s 10.8.1.0/24 -o eth0 -j MASQUERADE
如果无法通过 VPN ping 通服务器,则可能需要添加显式规则以向所有流量开放 TUN/TAP 接口。如果是这种情况,请执行以下操作 [6]
# iptables -A INPUT -i tun+ -j ACCEPT # iptables -A FORWARD -i tun+ -j ACCEPT # iptables -A INPUT -i tap+ -j ACCEPT # iptables -A FORWARD -i tap+ -j ACCEPT
此外,请确保接受来自 OpenVPN 端口(默认 1194)和物理接口的连接
# iptables -A INPUT -i eth0 -m state --state NEW -p udp --dport 1194 -j ACCEPT # iptables -A FORWARD -i tun+ -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT # iptables -A FORWARD -i eth0 -o tun+ -m state --state RELATED,ESTABLISHED -j ACCEPT # iptables -A FORWARD -i tap+ -o eth0 -m state --state RELATED,ESTABLISHED -j ACCEPT # iptables -A FORWARD -i eth0 -o tap+ -m state --state RELATED,ESTABLISHED -j ACCEPT
满意后,按照 iptables#配置和用法 中所示,使更改永久生效。
那些拥有多个 tun
或 tap
接口,或多个 VPN 配置的用户可以通过在 OpenVPN 配置文件中指定接口名称来“固定”接口名称,例如 tun22
而不是 tun
。如果需要为不同的接口或 OpenVPN 配置设置不同的防火墙规则,这将非常有利。
防止 VPN 断开时泄漏
这将阻止所有通过默认接口(例如 enp3s0)的流量,并且仅允许通过 tun0
的流量。如果 OpenVPN 连接断开,系统将失去 Internet 访问权限,从而阻止通过默认网络接口的连接。
用户可能希望设置一个脚本,以便在 OpenVPN 断开时重新启动它。
ufw
# Default policies ufw default deny incoming ufw default deny outgoing # Openvpn interface (adjust interface accordingly to your configuration) ufw allow in on tun0 ufw allow out on tun0 # Local Network (adjust ip accordingly to your configuration) ufw allow in on enp3s0 from 192.168.1.0/24 ufw allow out on enp3s0 to 192.168.1.0/24 # Openvpn (adjust port accordingly to your configuration) ufw allow in on enp3s0 from any port 1194 ufw allow out on enp3s0 to any port 1194
或者,可以允许 DNS 泄漏。请务必信任您的 DNS 服务器!
# DNS ufw allow in from any to any port 53 ufw allow out from any to any port 53
vpnfailsafe
或者,客户端可以使用 vpnfailsafe (vpnfailsafe-gitAUR) 脚本来防止 DNS 泄漏,并确保所有到 Internet 的流量都通过 VPN。如果 VPN 隧道断开,Internet 访问将被切断,但与 VPN 服务器的连接除外。该脚本包含 update-resolv-conf
的功能,因此无需将两者结合使用。
dhcpcd
redirect-gateway
会向系统路由表添加特殊路由。如果运行 dhcpcd 并且本地 DHCP 配置发生更改(可能是因为 Wi-Fi 接入点已更改),则 dhcpcd 将删除所有这些特殊路由。恢复这些路由的最简单方法是提示 openvpn 使用 /etc/dhcpcd.exit-hook
重新连接。
# Prompt openvpn to reconnect when the network reconnects. if $if_configured && $if_up && [ "$reason" != ROUTERADVERT ]; then state="$(systemctl show -P ActiveState openvpn-client@config)" if [ "$state" == 'active' ]; then pid="$(systemctl show -P MainPID openvpn-client@config)" kill -USR1 "$pid" fi fi
将 @config
替换为您的客户端配置的名称。
Layer-3 IPv4 路由
本节介绍如何使用 Layer-3 IPv4 路由将客户端/服务器 LAN 相互连接。
路由 LAN 的先决条件
为了使主机能够在 LAN 和 VPN 之间转发 IPv4 数据包,它必须能够在 NIC 和其 tun/tap
设备之间转发数据包。有关配置详细信息,请参阅 Internet sharing#启用数据包转发。
路由表
默认情况下,LAN 上寻址到不同子网的所有 IP 数据包都会发送到默认网关。如果 LAN/VPN 网关也是默认网关,则没有问题,数据包将被正确转发。否则,网关无法知道将数据包发送到哪里。这个问题有几种解决方案。
- 向默认网关添加静态路由,将 VPN 子网路由到 LAN/VPN 网关的 IP 地址。
- 在 LAN 上需要将 IP 数据包发送回 VPN 的每台主机上添加静态路由。
- 在 LAN/VPN 网关上使用 iptables 的 NAT 功能来伪装传入的 VPN IP 数据包。
将服务器 LAN 连接到客户端
服务器位于使用 10.66.0.0/24
子网的 LAN 上。要告知客户端可用的子网,请向服务器配置文件添加 push
指令
/etc/openvpn/server/server.conf
push "route 10.66.0.0 255.255.255.0"
push
指令,但请记住,服务器端 LAN 需要知道如何路由到客户端。将客户端 LAN 连接到服务器
先决条件
- 客户端使用的任何子网都必须是唯一的,并且不能在服务器或任何其他客户端上使用。在本示例中,我们将使用
192.168.4.0/24
作为客户端 LAN。 - 每个客户端的证书都有唯一的 Common Name,在本例中为
bugs
。 - 服务器的配置文件中不得使用
duplicate-cn
指令。 - CCD 文件夹必须可通过服务器配置文件中定义的用户和组访问(通常为
nobody:nobody
)
在服务器上创建一个客户端配置目录。它将搜索与客户端的通用名称相同的文件名的文件,并且这些指令将在客户端连接时应用于客户端。
# mkdir -p /etc/openvpn/ccd
在客户端配置目录中创建一个名为 bugs
的文件,其中包含 iroute 192.168.4.0 255.255.255.0
指令。它告诉服务器应将哪个子网路由到客户端
/etc/openvpn/ccd/bugs
iroute 192.168.4.0 255.255.255.0
将 client-config-dir
和 route 192.168.4.0 255.255.255.0
指令添加到服务器配置文件。它告诉服务器应将哪个子网从 tun
设备路由到服务器 LAN
/etc/openvpn/server/server.conf
client-config-dir ccd route 192.168.4.0 255.255.255.0
如果从服务器 LAN 中的机器访问客户端 LAN 中的机器,请记住需要在客户端上启用数据包转发(Internet sharing#启用数据包转发)。
- 如果使用 systemd 将 openVPN 作为守护程序运行,则可能需要为您的
ccd
目录指定绝对路径。 - 要将更多 LAN 从客户端路由到服务器,请向相应的配置文件添加更多
iroute
和route
指令,但请记住,客户端 LAN 需要知道如何路由到服务器。
连接客户端和服务器 LAN
结合前两个部分
/etc/openvpn/server/server.conf
push "route 10.66.0.0 255.255.255.0" client-config-dir ccd route 192.168.4.0 255.255.255.0
/etc/openvpn/ccd/bugs
iroute 192.168.4.0 255.255.255.0
连接客户端和客户端 LAN
默认情况下,客户端彼此不可见。要允许 IP 数据包在客户端和/或客户端 LAN 之间流动,请向服务器配置文件添加 client-to-client
指令
/etc/openvpn/server/server.conf
client-to-client
为了使另一个客户端或客户端 LAN 可以看到特定的客户端 LAN,请为每个客户端子网向服务器配置文件添加 push
指令(这将使服务器向其他客户端通告可用的子网)
/etc/openvpn/server/server.conf
client-to-client push "route 192.168.4.0 255.255.255.0" push "route 192.168.5.0 255.255.255.0"
DNS
对于 Linux,OpenVPN 客户端可以从服务器接收 DNS 主机信息,但客户端期望外部命令来处理此信息。默认情况下,未配置此类命令。它们必须使用 up
和 down
选项指定。可以使用一些替代脚本,但 OpenVPN 官方未认可任何脚本,因此为了使任何脚本都能工作,script-security
必须设置为 2。如果以非特权用户身份运行,则可以使用 down-root
插件代替 down
选项。
pull-resolv-conf
自定义脚本
这些脚本由 OpenVPN 维护。它们是 client.up
和 client.down
,并且打包在 /usr/share/openvpn/contrib/pull-resolv-conf/
中。以下是使用这些脚本和 down-root
插件的客户端配置的摘录
/etc/openvpn/client/clienttunnel.conf
user nobody group nobody # Optional, choose a suitable path to chroot into chroot /srv script-security 2 up /usr/share/openvpn/contrib/pull-resolv-conf/client.up plugin /usr/lib/openvpn/plugins/openvpn-plugin-down-root.so "/usr/share/openvpn/contrib/pull-resolv-conf/client.down tun0"
这些脚本在存在 resolvconf
命令时使用它。systemd-resolvconf 和 Openresolv 都实现了此命令。有关获取可工作的 resolvconf
实现的更多信息,请参阅它们的 wiki 页面。
systemd-resolved
服务正在运行,systemd-resolvconf
就可以工作。Openresolv
无法开箱即用,因为 client.up
只会创建私有 DNS 服务器条目。这些需要额外配置 openresolv
才能工作。有关 openresolv
中私有 DNS 服务器的更多详细信息,请参阅 resolvconf(8)。如果不存在 resolvconf
的实现,client.up
会将现有的 resolv.conf
保存在 /etc/resolv.conf.ovpnsave
中,并写入一个新的 resolv.conf
。这个新的 resolv.conf
将不包含任何原始 DNS 服务器。
编辑这些脚本时,请将它们复制到其他位置并在那里编辑,以使更改不会被下一次 openvpn 软件包升级覆盖。/etc/openvpn/client/
是一个不错的选择。
# cp /usr/share/openvpn/contrib/pull-resolv-conf/* /etc/openvpn/client/
编辑 /etc/openvpn/client/client.up
和 /etc/openvpn/client/client.down
。
update-resolv-conf
自定义脚本
update-resolv-conf
的作者为使用 systemd 的系统推荐的另一个脚本。openvpn-update-resolv-conf 脚本可用作打包脚本的替代方案。它需要保存在例如 /etc/openvpn/update-resolv-conf
中,并设置为可执行。
喜欢软件包的用户可以使用 openvpn-update-resolv-conf-gitAUR,但仍需要执行以下操作
安装脚本后,将如下行添加到 OpenVPN 客户端配置文件中
script-security 2 up /etc/openvpn/update-resolv-conf down /etc/openvpn/update-resolv-conf
现在,当启动 OpenVPN 连接时,resolv.conf
应该会相应地更新,并且在连接关闭时也应该恢复正常。
-p
或 -x
选项的 openresolv
时(就像包含的 client.up
和 update-resolv-conf
脚本当前所做的那样),openresolv
需要 DNS 解析器(如 dnsmasq 或 unbound)才能正确更新 /etc/resolv.conf
。相反,当使用来自 libc
的默认 DNS 解析时,必须删除 -p
和 -x
选项,openresolv
才能正确更新 /etc/resolv.conf
。例如,如果脚本包含类似 resolvconf -p -a
的命令,并且正在使用来自 libc
的默认 DNS 解析器,请将脚本中的命令更改为 resolvconf -a
。update-systemd-resolved
自定义脚本
/etc/resolv.conf
由 systemd-resolved
管理时,诸如 openresolv 之类的工具可能无法可靠地工作,并且如果在使用 /etc/nsswitch.conf
中的 resolve
而不是 dns
时,则根本无法工作。update-systemd-resolved 脚本通过 DBus 将 OpenVPN 与 systemd-resolved
链接,以更新 DNS 记录。
将脚本复制到 /etc/openvpn/scripts
并标记为可执行(或安装 openvpn-update-systemd-resolvedAUR),并将以下行 追加 到 OpenVPN 客户端配置文件中
/etc/openvpn/client/client.conf
client remote example.com 1194 udp script-security 2 setenv PATH /usr/bin up /etc/openvpn/scripts/update-systemd-resolved down /etc/openvpn/scripts/update-systemd-resolved down-pre
为了通过 VPN 隧道发送所有 DNS 流量并防止 DNS 泄漏,还请添加以下行(参见 [7])
/etc/openvpn/client/client.conf
dhcp-option DOMAIN-ROUTE .
确保 systemd-resolved
服务已配置并正在运行。此外,由于 openvpn 2.5.0-3 脚本以 openvpn
用户而不是 root 用户身份运行。因此,添加 PolicyKit 规则以允许 OpenVPN systemd 单元使用 SetLinkDNS
调用 DBus
/etc/polkit-1/rules.d/00-openvpn-resolved.rules
polkit.addRule(function(action, subject) { if (action.id == 'org.freedesktop.resolve1.set-dns-servers' || action.id == 'org.freedesktop.resolve1.set-domains' || action.id == 'org.freedesktop.resolve1.set-dnssec') { if (subject.user == 'openvpn') { return polkit.Result.YES; } } });
client.conf
中 update-systemd-resolved
脚本的路径应从 /etc/openvpn/scripts/
更改为 /usr/bin/
,并且 polkit 规则已设置。使用 NetworkManager 覆盖 DNS 服务器
默认情况下,networkmanager-openvpn 插件将 OpenVPN 提供的 DNS 服务器附加到 /etc/resolv.conf
。
要验证是否配置了正确的 DNS 服务器,如果正在使用 systemd-resolved
,请参阅 resolvectl status
,对于其他解析器,请参阅 域名解析。
Layer-2 以太网桥接
建立以太网桥接可以访问服务器子网内的其他设备。例如,通过此方法可以访问 OpenVPN 服务器本地网络中通过 Samba 的其他机器。客户端将被分配一个 IP 地址,就好像它位于同一子网内一样。
这通常是一个两步过程:1) 在 OpenVPN 服务器上建立 tap
接口和网络桥接,以桥接 tap
接口和以太网接口;以及 2) 配置 OpenVPN 服务器。
请参阅 OpenVPN 桥接。
配置生成器
ovpngen
ovpngenAUR 软件包提供了一个简单的 shell 脚本,用于创建统一文件格式的 OpenVPN 兼容隧道配置文件,适用于 Android 和 iOS 的 OpenVPN Connect 应用程序。
只需使用 5 个令牌调用该脚本
- OpenVPN 服务器的服务器完全限定域名(或 IP 地址)。
- CA 证书的完整路径。
- 客户端证书的完整路径。
- 客户端私钥的完整路径。
- 服务器 TLS 共享密钥的完整路径。
- 可选的端口号。
- 可选的协议(udp 或 tcp)。
示例
# ovpngen example.org /etc/openvpn/server/ca.crt /etc/easy-rsa/pki/signed/client1.crt /etc/easy-rsa/pki/private/client1.key /etc/openvpn/server/ta.key > foo.ovpn
如果服务器配置为使用 tls-crypt
,如 #服务器配置文件 中建议的那样,请手动编辑生成的 foo.ovpn
,将 <tls-auth>
和 </tls-auth>
替换为 <tls-crypt>
和 </tls-crypt>
。
如果需要,可以编辑生成的 foo.ovpn
,因为该脚本确实插入了一些注释行。foo.ovpn
不会自动通过 VPN 路由所有流量,因此请考虑按照 #通过服务器路由客户端流量 来启用重定向。
客户端希望此文件位于 /etc/openvpn/client/foo.conf
中。请注意,在这种情况下,文件扩展名从“ovpn”更改为“conf”。
server.conf
包含指定的 cipher
和/或 auth
行,则强烈建议用户手动编辑生成的 .ovpn
文件,并添加匹配的 cipher
和 auth
行。否则可能会导致连接错误!openvpn-unroot
openvpn-unroot
之前将这些脚本添加到配置中。如果脚本需要 root 权限,则不这样做会导致问题。使用 openvpn-unroot (openvpn-unroot-gitAUR) 可以自动执行 OpenVPN #以非特权用户身份运行 所需的步骤。
它通过将其适配到 systemd 来自动化 OpenVPN howto 所需的操作,并且还解决了注释中提到的持久性 tun
设备的错误。
故障排除
客户端守护程序在挂起后未重新连接
AUR 上提供的 openvpn-reconnectAUR 通过在从挂起唤醒后向 openvpn 发送 SIGHUP 来解决此问题。
或者,通过创建以下 systemd 服务在挂起后重新启动 OpenVPN
/etc/systemd/system/openvpn-reconnect.service
[Unit] Description=Restart OpenVPN after suspend [Service] ExecStart=/usr/bin/pkill --signal SIGHUP --exact openvpn [Install] WantedBy=sleep.target
启用 此服务以使其生效。
连接在一段时间不活动后断开
如果 VPN 连接在停止传输数据后几秒钟断开连接,即使它表明已连接,但无法通过隧道传输数据,请尝试在服务器的配置中添加 keepalive
指令
/etc/openvpn/server/server.conf
keepalive 10 120
在这种情况下,服务器将每 10 秒向其所有客户端发送类似 ping 的消息,从而保持隧道畅通。如果服务器在 120 秒内未收到来自特定客户端的响应,则会认为该客户端已断开连接。
较小的 ping 间隔可以提高隧道的稳定性,但也会导致流量略有增加。根据连接情况,也可以尝试低于 10 秒的间隔。
PID 文件不存在
尽管创建了 /run/openvpn-client
,但 openvpn-client
的默认 systemd 服务文件未启用 --writepid
标志。如果这破坏了配置(例如 i3bar VPN 指示器),只需使用 drop-in snippet 更改 openvpn-client@.service
。
[Service] ExecStart= ExecStart=/usr/sbin/openvpn --suppress-timestamps --nobind --config %i.conf --writepid /run/openvpn-client/%i.pid
路由配置在 systemd-networkd 中失败
当使用 systemd-networkd 管理网络连接并尝试通过 VPN 隧道传输所有传出流量时,OpenVPN 可能无法添加路由。这是因为 systemd-networkd
尝试在 OpenVPN 完成配置路由之前管理 tun
接口。发生这种情况时,以下消息将出现在 OpenVPN 日志中。
openvpn[458]: RTNETLINK answers: Network is unreachable openvpn[458]: ERROR: Linux route add command failed: external program exited with error status: 2
从 systemd-233 开始,可以将 systemd-networkd
配置为忽略 tun
连接,并允许 OpenVPN 管理它们。为此,请创建以下文件
/etc/systemd/network/90-tun-ignore.network
[Match] Name=tun* [Link] Unmanaged=true
重启 systemd-networkd.service
以应用更改。要验证更改是否生效,请启动之前有问题的 OpenVPN 连接并运行 networkctl
。输出应包含类似于以下的行
7 tun0 none routable unmanaged
tls-crypt 解包错误:数据包太短
当不支持 tls-crypt
的客户端,或者配置错误以使用 tls-auth
但服务器配置为使用 tls-crypt
的客户端尝试连接时,服务器日志中会出现此错误。
为了支持不支持 tls-crypt
的客户端,请在 server.conf
中将 tls-crypt ta.key
替换为 tls-auth ta.key 0
(默认值)。还要在 client.conf
中将 tls-crypt ta.key
替换为 tls-auth ta.key 1
(默认值)。
NetworkManager 使用导入的配置连接失败
存在一个已知的上游错误 [8][9][10],与通过 GUI (plasma-nm 而非 nm-connection-editor) 在 KDE 下导入现有 OpenVPN 配置(.ovpn
文件)有关,这会导致某些高级选项(如 tls-crypt
)被静默忽略。因此,创建的 NetworkManager 连接配置文件将不完整,并且连接最终失败。
日志可能会显示如下错误
TLS Error: TLS handshake failed TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity)
一种解决方法是求助于 NetworkManager 的命令行实用程序 nmcli
来导入连接配置文件,如 #CLI 配置 中所示。
更新到 OpenSSL3 后 OpenVPN 连接失败
使用 OpenSSL3 的 PKCS#12 编码的用户证书/私钥文件(通常以 .pfx
或 .p12
文件结尾表示)存在问题,另请参见 [11]。这导致在更新到 OpenSSL3 之前工作正常的连接无法连接,并产生以下日志消息(在本例中通过 NetworkManager)
nm-openvpn[14025]: WARNING: No server certificate verification method has been enabled. See https://openvpn.net/howto.html#mitm for more info. nm-openvpn[14025]: NOTE: the current --script-security setting may allow this configuration to call user-defined scripts nm-openvpn[14025]: OpenSSL: error:0308010C:digital envelope routines::unsupported nm-openvpn[14025]: OpenSSL: error:11800071:PKCS12 routines::mac verify failure nm-openvpn[14025]: Decoding PKCS12 failed. Probably wrong password or unsupported/legacy encryption nm-openvpn[14025]: SIGUSR1[soft,private-key-password-failure] received, process restarting
原因是旧版本的 OpenSSL 使用了现在已弃用的算法来加密 PKCS#12 文件。
要解决此问题,必须使用非旧版算法重新加密 PKCS#12 文件。
$ cd path/to/the/pfx-file $ mv mykeys.pfx mykeys.pfx.bak $ openssl pkcs12 -in mykeys.pfx.bak -out mykeys.pfx -aes256 -legacy