OpenVPN

出自 ArchWiki
(重定向自 Networkmanager-openvpn)

本文介绍 OpenVPN 的基本安装和配置,适用于个人和小企业使用。有关更详细的信息,请参阅 openvpn(8)OpenVPN 文档。OpenVPN 是一个强大且高度灵活的 VPN 守护程序。它支持 SSL/TLS 安全、以太网桥接、通过 代理NATTCPUDP 隧道传输。此外,它还支持动态 IP 地址和 DHCP、可扩展到数百或数千用户,以及可移植到大多数主要操作系统平台。

OpenVPN 与 OpenSSL 库紧密结合,并从中获得了大部分加密功能。它支持使用预共享密钥(静态密钥模式)或 公钥安全SSL/TLS 模式)使用客户端和服务器证书的传统加密。此外,它还支持未加密的 TCP/UDP 隧道。

OpenVPN 旨在与大多数平台上存在的 TUN/TAP 虚拟网络接口一起工作。总的来说,它的目标是提供 IPSec 的许多关键功能,但具有相对轻量级的占用空间。OpenVPN 由 James Yonan 编写,并根据 GNU 通用公共许可证 (GPL) 发布。

安装

安装 openvpn 软件包,该软件包同时提供服务器和客户端模式。

可用的前端

  • NetworkManager OpenVPN — 用于 OpenVPN 的 NetworkManager VPN 插件。
https://wiki.gnome.org/Projects/NetworkManager/VPN || networkmanager-openvpn
  • QOpenVPN — 使用 PyQt 编写的用于基于 systemd 发行版的简单 OpenVPN GUI。
https://github.com/xmikos/qopenvpn || qopenvpn
  • eOVPN — 用于连接、管理和更新 OpenVPN 配置的应用程序。使用 GTK 编写。支持 OpenVPN 2 和带有 数据通道卸载 的 OpenVPN 3
https://github.com/jkotra/eOVPN || eovpnAUR

内核配置

OpenVPN 需要 TUN/TAP 支持,这在默认内核中已配置。自定义内核的用户应确保在设备驱动程序 > 网络设备支持 > 网络核心驱动程序支持 > 通用 TUN/TAP 设备驱动程序支持中启用 tun 模块 (CONFIG_TUN)。

阅读 内核模块 了解更多信息。

连接到第三方提供的 VPN

要连接到第三方提供的 VPN 服务,以下大部分内容很可能可以忽略,特别是关于服务器设置。从 #客户端配置概要文件 开始,然后跳到 #启动 OpenVPN。应使用提供商证书和说明,请参阅 Category:VPN providers 以获取可用于其他提供商的示例。Linux 容器中的 OpenVPN 客户端 也具有通用的适用说明,同时通过将 OpenVPN 客户端进程隔离到容器中更进一步。

注意: 大多数免费 VPN 提供商将(通常只)提供 PPTP,它更容易设置和配置,但 不安全

从头开始创建公钥基础设施 (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 路由配置

注意: 除非另有明确说明,否则本文的其余部分假定为基本的 Layer-3 IP 路由配置。

OpenVPN 是一款功能极其强大的软件,可以进行多种配置,实际上,机器可以同时作为服务器和客户端。

随着 v2.4 的发布,服务器配置存储在 /etc/openvpn/server 中,客户端配置存储在 /etc/openvpn/client 中,并且每种模式都有其各自的 systemd 单元,即 openvpn-client@.serviceopenvpn-server@.service

配置示例

OpenVPN 软件包附带了用于不同目的的示例配置文件集合。示例服务器和客户端配置文件是基本 OpenVPN 设置的理想起点,具有以下功能

  • 使用 公钥基础设施 (PKI) 进行身份验证。
  • 使用虚拟 TUN 网络接口(OSI Layer-3 IP 路由)创建 VPN。
  • 在 UDP 端口 1194 上监听客户端连接(OpenVPN 的官方 IANA 端口号[1])。
  • 从 10.8.0.0/24 子网向连接的客户端分配虚拟地址。

有关更高级的配置,请参阅 openvpn(8) 手册页和 OpenVPN 文档

服务器配置文件

注意: 请注意,如果服务器位于防火墙或 NAT 转换路由器之后,则必须将 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 noneecdh-curve secp521r1(或 ecdh-curve ed25519)。使用椭圆曲线时,不使用 DH 参数文件。从 OpenVPN 2.4.8 开始,必须在服务器配置中指定椭圆曲线的类型。否则,服务器将无法识别曲线类型,并可能使用不兼容的曲线类型,从而导致身份验证错误。

服务器加固

如果安全是首要任务,建议进行额外的配置,包括:限制服务器使用强密码/身份验证方法,以及(可选)将启用的 TLS 密码集限制为较新的密码。从 OpenVPN 2.4 开始,服务器和客户端将在 TLS 模式下自动协商 AES-256-GCM。

将以下内容添加到 /etc/openvpn/server/server.conf

此文章或章节需要改进语言、wiki 语法或风格。请参阅 Help:Style 以供参考。

原因: 使用说明应放在 wiki 文本中,而不是代码块中。避免使用“前沿安全”等主观术语。(在 Talk:OpenVPN 中讨论)

前沿安全

/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

此文章或章节需要改进语言、wiki 语法或风格。请参阅 Help:Style 以供参考。

原因: 兼容性选项不适合放在加固部分。(在 Talk:OpenVPN 中讨论)

与大多数设备的兼容性

/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
注意: .ovpn 客户端配置文件必须包含匹配的 port 和 proto 行才能正常工作!

在物理机上的不同端口运行 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 地址。
  • 取消注释 usergroup 指令以放弃特权。
  • cacertkey 参数以反映密钥和证书的路径和名称。
  • 启用 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 nobodygroup nobody 选项会使 OpenVPN 在建立连接后放弃其 root 特权。缺点是,VPN 断开连接后,守护程序无法再次删除其设置的网络路由。如果想要限制在没有 VPN 连接的情况下传输流量,则持久路由可能被认为是很有益的。但是,也可能发生 OpenVPN 服务器在隧道运行时将更新推送到路由的情况。具有放弃特权的客户端将无法执行更新并退出并显示错误。

由于管理路由似乎需要手动操作,因此 user nobodygroup 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 的此部分
提示: #openvpn-unroot 描述了一个自动化上述设置的工具。

将证书转换为加密的 .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 它。

注意: 如果使用防火墙,请确保 TUN 设备上的 IP 数据包未被阻止。

配置 MTU、Fragment 和 MSS

如果在通过 OpenVPN 使用(远程)服务时遇到问题(例如,网页浏览、DNSNFS),则可能需要手动设置 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 成功。

注意: 不支持 'fragment' 指令的客户端(例如,OpenELEC、iOS 应用程序)无法连接到使用 fragment 指令的服务器。请参阅 mtu-test 作为替代解决方案。

更新客户端配置以使用成功的 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 udpproto tcp 时,OpenVPN 将使用操作系统定义的 AF_INET,在大多数情况下,这将仅是 IPv4。要同时使用 IPv4 和 IPv6,请使用 proto udp6proto tcp6。要仅强制使用 IPv4,请使用 proto udp4proto 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 连接建立隧道。这可以通过将脚本添加到 /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 仅支持移动配置文件(通常以 .ovpn 结尾),即带有内联证书且只有一个文件(Linux 配置通常带有 userpass 文件和分离的证书)。
  • 对于不支持的选项不会生成错误消息,请打开 日志 以查看 networkmanager 使用的选项。

图形界面配置

在您的桌面环境网络设置(或 nm-connection-editor)中。单击加号以添加新连接,选择 OpenVPN 并手动输入设置。您也可以选择导入 #客户端配置文件,方法是选择“导入已保存的 VPN 配置...”并选择相应的文件。

命令行配置

用于导入配置

$ nmcli connection import type openvpn file file.ovpn

用于手动配置

$ nmcli connection add type vpn vpn-type openvpn ...

有关详细选项,请参阅 nm-settings-nmcli(5) § vpn setting

如果您想设置登录名和密码,请检查 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 名称

与连接同步状态

NetworkManager 支持将 VPN 状态与接口连接状态同步,即在连接建立时启动 VPN,并在连接断开时关闭 VPN。

要实现此目的,请打开 nm-connection-editor 并选择一个网络连接(不是 VPN 连接),然后转到“常规”部分,勾选“自动连接到 VPN”,并在下拉菜单中选择相应的配置。

通过命令行自动连接到 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)。

注意: 可能需要使用像 BIND 这样的简单 DNS 转发器,并将 OpenVPN 服务器的 IP 地址作为 DNS 推送给客户端。
push "dhcp-option DNS 192.168.1.1"
push "dhcp-option DOMAIN internal"

设置配置文件后,在服务器上启用数据包转发。此外,还需要调整服务器的防火墙以允许 VPN 流量,下面分别针对 ufwiptables 进行了描述。

注意: 通过 VPN 服务器路由所有流量可能存在潜在的缺陷。有关更多信息,请参阅 OpenVPN 文档

防火墙配置

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 服务器配置中设置的 server
  • 网络接口更改为 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

# 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]

警告: 如果不信任连接到服务器的所有客户端,则以下规则存在安全隐患。有关更多详细信息,请参阅 OpenVPN 关于此主题的文档
# 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#配置和使用 中的说明使更改永久生效。

那些具有多个 tuntap 接口,或者多个 VPN 配置的用户可以通过在 OpenVPN 配置文件中指定接口名称来“固定”接口名称,例如 tun22 而不是 tun。如果需要为不同的接口或 OpenVPN 配置设置不同的防火墙规则,这将很有优势。

防止 VPN 断开时泄漏

这可以阻止所有通过默认接口(例如 enp3s0)的流量,并且仅允许流量通过 tun0。如果 OpenVPN 连接断开,系统将失去互联网访问,从而阻止通过默认网络接口的连接。

可能需要设置一个脚本,以便在 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
警告: 除非运行像 BIND 这样的专用 DNS 服务器,否则 DNS 将无法工作。

或者,可以允许 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 泄漏,并确保所有到互联网的流量都通过 VPN。如果 VPN 隧道断开,互联网访问将被切断,但与 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#启用数据包转发

路由表

本文或本节的事实准确性存在争议。

原因: 调查是否可以使用 RIP、QUAGGA、BIRD 等路由协议(在 Talk:OpenVPN 中讨论)

默认情况下,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"
注意: 要从服务器向客户端路由更多 LAN,请在服务器配置文件中添加更多 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-dirroute 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,请将更多 irouteroute 指令添加到相应的配置文件,但请记住,客户端的 LAN 需要知道如何路由到服务器。

同时连接客户端 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 或所需的客户端都可以路由到所有目标。

连接客户端和客户端 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"
注意: 可能需要调整防火墙配置以允许客户端流量通过 VPN 服务器。

DNS

对于 Linux,OpenVPN 客户端可以从服务器接收 DNS 主机信息,但客户端希望外部命令对该信息进行操作。默认情况下未配置此类命令。它们必须使用 updown 选项指定。有一些可供选择的脚本,但 OpenVPN 官方均未认可,因此为了使任何脚本都能工作,script-security 必须设置为 2。如果以非特权用户身份运行,则可以使用 down-root 插件代替 down 选项。

pull-resolv-conf 自定义脚本

这些脚本由 OpenVPN 维护。它们是 client.upclient.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-resolvconfOpenresolv 都实现了此命令。有关获取可用的 resolvconf 实现的更多信息,请参阅它们的 wiki 页面。

注意: 截至 2019 年 10 月,只要 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-systemd-resolved 的作者建议对于使用 systemd 的系统使用另一个脚本 update-systemd-resolved。

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
注意: 如果手动将脚本放在文件系统上,请确保已安装 openresolv

现在,当启动 OpenVPN 连接时,resolv.conf 应该会相应更新,并且在连接关闭时也应该恢复正常。

注意: 当在脚本中使用带有 -p-x 选项的 openresolv 时(就像包含的 client.upupdate-resolv-conf 脚本当前所做的那样),需要像 dnsmasqunbound 这样的 DNS 解析器,以便 openresolv 正确更新 /etc/resolv.conf。相反,当使用来自 libc 的默认 DNS 解析时,必须删除 -p-x 选项,以便 openresolv 正确更新 /etc/resolv.conf。例如,如果脚本包含类似 resolvconf -p -a 的命令,并且正在使用来自 libc 的默认 DNS 解析器,请将脚本中的命令更改为 resolvconf -a

update-systemd-resolved 自定义脚本

注意:systemd 229 起,systemd-networkd 通过 DBus 公开了一个 API,允许按链接管理 DNS 配置。当 /etc/resolv.confsystemd-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;
        }
    }
});
注意: 当使用 openvpn-update-systemd-resolvedAUR 软件包时,client.confupdate-systemd-resolved 脚本的路径应从 /etc/openvpn/scripts/ 更改为 /usr/bin/,并且 polkit 规则已设置。

使用 NetworkManager 覆盖 DNS 服务器

默认情况下,networkmanager-openvpn 插件将 OpenVPN 提供的 DNS 服务器附加到 /etc/resolv.conf

要验证是否配置了正确的 DNS 服务器,如果正在使用 systemd-resolved,请参阅 resolvectl status,对于其他解析器,请参阅 域名解析

Layer-2 以太网桥接

建立以太网桥接可以访问服务器子网内的其他设备。例如,通过 Samba 访问 OpenVPN 服务器本地网络中的其他计算机,使用此方法是可能的。客户端将被分配一个 IP 地址,就好像它在同一个子网中一样。

这通常是一个两步过程:1) 在 OpenVPN 服务器上建立 tap 接口和网络桥接,以桥接 tap 接口和以太网接口;2) 配置 OpenVPN 服务器。

请参阅 OpenVPN Bridge

配置生成器

警告: 强烈建议用户在使用任何其他自动化脚本之前,先了解上述手动配置,以获得有关选项和用法的知识。

ovpngen

ovpngenAUR 软件包提供了一个简单的 shell 脚本,用于创建 OpenVPN 兼容的隧道配置文件,该文件采用统一的文件格式,适用于 Android 和 iOS 的 OpenVPN Connect 应用程序。

只需使用 5 个令牌调用脚本

  1. OpenVPN 服务器的服务器完全限定域名(或 IP 地址)。
  2. CA 证书的完整路径。
  3. 客户端证书的完整路径。
  4. 客户端私钥的完整路径。
  5. 服务器 TLS 共享密钥的完整路径。
  6. 可选端口号。
  7. 可选协议(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

注意: 如果使用自定义脚本(可能用于配置 #DNS),请在对其调用 openvpn-unroot 之前将这些脚本添加到配置中。否则,如果脚本需要 root 权限,则会导致问题。

OpenVPN 以 #非特权用户身份运行所需的步骤可以使用 openvpn-unroot (openvpn-unroot-gitAUR) 自动执行。

它通过将其适配到 systemd 来自动化 OpenVPN howto 所需的操作,并且还解决了注释中提到的持久 tun 设备的错误。

故障排除

客户端守护程序在挂起后未重新连接

openvpn-reconnectAUR(在 AUR 上可用)通过在从挂起状态唤醒后向 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 代码段更改 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 使用导入的配置连接失败

在 KDE 下通过 GUI(plasma-nm 而不是 nm-connection-editor)导入现有 OpenVPN 配置 (.ovpn 文件) 时,存在一个已知的上游错误 [8][9][10],该错误会导致某些高级选项(例如 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

另请参阅