安装和配置 OpenVPN

2025-02-07 21:31:00
丁国栋
原创 329
摘要:本文记录如何安装和配置OpenVPN。

OpenVPN 是一种简单好用又安全的一种 VPN 工具,分为服务端和客户端,服务端负责处理VPN接入和路由(代理服务),客户端用于连接到服务端并且设置客户端路由等。通过 OpenVPN,客户端可以将接入服务端所在的网络,客户端可以以服务端的网络身份去访问服务端可以访问到的任何资源,这种场景下客户端的出口IP地址会变为服务端的出口IP地址,可以实现类似代理的功能。客户端也可以在服务端允许的情况下访问服务端所在的内部网络,从而实现访问内部资源的能力。

一、服务端部分

推荐使用容器部署

注:在这里单独补充下为什么要推荐使用容器部署。因为OpenVPN的主要版本在2.4.x、2.5.x、2.6.x时以后都出现了破坏性更新,即有些组件(例如依赖或者适配的SSL/TLS的部分 )不再向后兼容,一些操作系统只能安装特定的OpenVPN版本,而容器的兼容性相对于主机操作系统部署指定版本是更好的。

关于OpenVPN版本的选择:一般来说,最好的情况就是服务端版本和客户端版本是一致的,如果不一致,那么版本的大版本和主要版本最好一致,例如都是2.5.x或者都是2.6.x。


使用Docker镜像:kylemanna/openvpn

需要注意的是,这个镜像虽然依然可用,但版本已经不是最新的,如果有条件还是要自己构建镜像,以便享受更好的安全性、兼容性和可维护性等。

详细配置可以参考这里:

https://store.docker.com/community/images/kylemanna/openvpn
https://hub.docker.com/r/kylemanna/openvpn
https://github.com/kylemanna/docker-openvpn
https://github.com/kylemanna/docker-openvpn/tree/master/docs
以下是一个简单粗略但能用的步骤

需要认真规划 vpn 用户使用的子网避免冲突(主要是避免与内部网络和客户端网络的冲突),以下以 172.16.248.0/24 为例,假设内部网络为 10.8.0.0/255.255.0.0 和 10.0.0.0/255.255.248.0 两个内部网络。

注意:如果是在国内使用,那么OpenVPN的网络协议可以是TCP或UDP,但如果是跨国家地区使用建议使用UDP协议,因为TCP需要比较稳定的网络质量以及众所周知的安全性考虑。

docker run -v /data/docker/openvpn/ovpn-data:/etc/openvpn --rm kylemanna/openvpn ovpn_genconfig -u tcp://0.0.0.0 -D -c -p "route 10.8.0.0 255.255.0.0" -p "route 10.0.0.0 255.255.248.0" -s "172.16.248.0/24" -r "172.16.248.0/24"
sudo sed -i '/block-outside-dns/d' /data/docker/openvpn/ovpn-data/openvpn.conf
# 初始化CA
docker run -v /data/docker/openvpn/ovpn-data:/etc/openvpn --rm -it kylemanna/openvpn ovpn_initpki
# 验证和设置路由参数,允许IP转发以便能通过VPN服务器访问到内部网络的主机或服务
sudo sysctl -a |grep [n]et.ipv4
sudo sysctl -w net.ipv4.conf.default.accept_source_route=1
sudo sysctl -w net.ipv4.conf.all.rp_filter=0
sudo sysctl -w net.ipv4.ip_forward=1
# 创建并启动VPN服务
# https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities
# NET_RAW   Use RAW and PACKET sockets. Such as "iptables"
# NET_ADMIN Perform various network-related operations.
docker run -v /data/docker/openvpn/ovpn-data:/etc/openvpn -d --name "openvpn" --hostname="openvpn" -v /etc/localtime:/etc/localtime -p 1194:1194 --cap-add NET_ADMIN --cap-add NET_RAW kylemanna/openvpn
# 设置和确认容器运行策略为自动启动
docker update --restart=always openvpn
docker inspect openvpn | jq '.[0].HostConfig.RestartPolicy'
# 创建一个vpn用户
OVPN_USERNAME="example-username"
docker run -v /data/docker/openvpn/ovpn-data:/etc/openvpn --rm -it kylemanna/openvpn easyrsa build-client-full ${OVPN_USERNAME} 
# 通常我们不期望每次启动、关闭openvpn客户端还得输入客户端证书的密码,因此可以使用 nopass 参数,例如 docker run -v /data/docker/openvpn/ovpn-data:/etc/openvpn --rm -it kylemanna/openvpn easyrsa build-client-full ${OVPN_USERNAME} nopass
# 导出 vpn 用户的配置
docker run -v /data/docker/openvpn/ovpn-data:/etc/openvpn --rm kylemanna/openvpn ovpn_getclient $OVPN_USERNAME | sudo tee ${OVPN_USERNAME}.ovpn
# 注意替换配置文件中的 0.0.0.0 为VPN服务的公网地址,例如 sudo sed -i 's/0.0.0.0/vpn.thedf.cc/g' ${OVPN_USERNAME}.ovpn
# 如果不希望VPN服务器作为VPN客户端的网络出口,那么需要执行:sudo sed -i '/redirect-gateway def1/d' ${OVPN_USERNAME}.ovpn 来避免客户端公网IP地址变为VPN的IP地址
# 列出用户:docker run -v /data/docker/openvpn/ovpn-data:/etc/openvpn --rm -it kylemanna/openvpn  ovpn_listclients
# 移除用户:docker run --rm -it -v /data/docker/openvpn/ovpn-data:/etc/openvpn kylemanna/openvpn easyrsa revoke example-username
# 在移除用户后需要重新加载配置 docker run --rm -it -v /data/docker/openvpn/ovpn-data:/etc/openvpn kylemanna/openvpn easyrsa gen-crl

需要注意的是,一个vpn文件在同一时间只能在一个客户端上使用,否则会导致冲突至少有一个客户端无法连接成功。

使用包管理器安装

例如 Debian/Ubuntu 里可以直接使用 apt install openvpn 进行安装。注意:包管理的OpenVPN包通常客户端和服务端都在同一个包内。

使用脚本安装

例如可以使用 https://github.com/Nyr/openvpn-install.git 脚本安装,它也是最终通过包管理器安装,但简化了配置。需要注意的是,它默认生成的配置文件中默认是全局路由(流量全部经过服务器),以及缺少推送路由。

这个脚本运行后会让用户选择监听的IP地址、判断是否在NAT内显示公网IP地址、选择TCP或UDP协议、选择端口号、输入客户端名称(俗称OpenVPN账号),然后就开始安装过程并将客户端文件保存到当前目录下。

再次运行这个脚本可以选择创建账号、移除账号或卸载OpenVPN服务


二、客户端部分

推荐使用apt、yum等包管理器安装

deb和rpm两种包的配置略有不同,最大的不同是vpn配置文件需要存放的位置(工作目录、配置文件目录、启动命令行)不同,deb包通常是 /etc/openvpn下,而rpm包通常是 /etc/openvpn/client 下,具体的可以通过查看 /usr/lib/systemd/system/openvpn@.service 文件的内容来确认。

以 2.6.12-0ubuntu0.24.04.1 版本为例


# /usr/lib/systemd/system/openvpn@.service
[Unit]
Description=OpenVPN connection to %i
PartOf=openvpn.service
Before=systemd-user-sessions.service
After=network-online.target
Wants=network-online.target
Documentation=man:openvpn(8)
Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
Documentation=https://community.openvpn.net/openvpn/wiki/HOWTO
[Service]
Type=notify
PrivateTmp=true
WorkingDirectory=/etc/openvpn
ExecStart=/usr/sbin/openvpn --daemon ovpn-%i --status /run/openvpn/%i.status 10 --cd /etc/openvpn --script-security 2 --config /etc/openvpn/%i.conf --writepid /run/openvpn/%i.pid
PIDFile=/run/openvpn/%i.pid
KillMode=process
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE CAP_AUDIT_WRITE
TasksMax=10
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
ProtectSystem=true
ProtectHome=true
RestartSec=5s
Restart=on-failure
[Install]
WantedBy=multi-user.target
而在 openvpn-2.4.12-2.el8.x86_64 中,systemd unit文件是这样的:



# cat /usr/lib/systemd/system/openvpn-client@.service 
[Unit]
Description=OpenVPN tunnel for %I
After=syslog.target network-online.target
Wants=network-online.target
Documentation=man:openvpn(8)
Documentation=https://community.openvpn.net/openvpn/wiki/Openvpn24ManPage
Documentation=https://community.openvpn.net/openvpn/wiki/HOWTO
[Service]
Type=notify
PrivateTmp=true
WorkingDirectory=/etc/openvpn/client
ExecStart=/usr/sbin/openvpn --suppress-timestamps --nobind --config %i.conf
CapabilityBoundingSet=CAP_IPC_LOCK CAP_NET_ADMIN CAP_NET_RAW CAP_SETGID CAP_SETUID CAP_SYS_CHROOT CAP_DAC_OVERRIDE
LimitNPROC=10
DeviceAllow=/dev/null rw
DeviceAllow=/dev/net/tun rw
ProtectSystem=true
ProtectHome=true
KillMode=process
[Install]
WantedBy=multi-user.target




yum install openvpn -y || apt install openvpn -y
cp example-username.ovpn /etc/openvpn/client/ || cp example-username.ovpn /etc/openvpn/
rpm -ql openvpn | grep service || dpkg -L openvpn| grep service
cat /usr/lib/systemd/system/openvpn@.service || cat /usr/lib/systemd/system/openvpn-client@.service
ln -s /etc/openvpn/example-username.ovpn /etc/openvpn/example-username.conf
systemctl status openvpn@example-username.service || systemctl status openvpn-client@example-username.service
systemctl stop openvpn@example-username.service || systemctl stop openvpn-client@example-username.service

注:在较新版本的 OpenVPN 客户端中,对压缩相关的参数做了一些调整。例如连接报错:


2025-08-24 18:03:08 Compression or compression stub framing is not allowed since data-channel offloading is enabled.
2025-08-24 18:03:08 OPTIONS ERROR: server pushed compression settings that are not allowed and will result in a non-working connection. See also allow-compression in the manual.
2025-08-24 18:03:08 ERROR: Failed to apply push options
2025-08-24 18:03:08 Failed to open tun/tap interface
这时需要在 .ovpn 配置文件中添加 comp-lzo no ,再重新连接。

三、进阶技巧

如何在低版本的操作系统中安装高版本的OpenVPN服务?

一般来说高版本的OpenVPN包需要更高版本的libc、ssl库,直接在操作系统中安装高版本的libc和ssl库的风险比较高。我们可以先正常安装包管理器中的OpenVPN(备份配置文件 /etc/openvpn/server/server.conf),此时OpenVPN版本较低。然后我们去容器或者另一个高版本的操作系统中安装高版本的OpenVPN服务器,再将高版本的OpenVPN服务器的配置文件(例如 /etc/openvpn/server)目录同步到要使用的操作系统里,再将备份的配置文件还原,然后就可以正常使用了。原因就是主要的不兼容点在于SSL/TLS部分(生成证书文件),我们只需要把高版本的OpenVPN生成的证书等文件复制到低版本使用就可以了。

--




发表评论
博客分类