前言
在云服务器上搭建自己的 VPN 服务,既能保障数据传输安全,又能灵活管理用户权限。本文记录了在 Ubuntu 24.04 服务器上从零部署 OpenVPN 的完整过程,包括:
- EasyRSA 证书体系搭建
- 服务端配置(UDP 1194)
- 证书 + 密码双重认证
- PAM 对接 MariaDB 数据库管理用户
- 客户端配置文件生成
- 路由模式选择(全流量代理 / 分流 / 仅内网)
- 踩坑记录和解决方案
环境信息
| 项目 | 配置 |
|---|---|
| 系统 | Ubuntu 24.04 LTS |
| CPU | 2 核 Intel Xeon Platinum |
| 内存 | 1.6GB + 4GB Swap |
| 磁盘 | 40GB |
| OpenVPN | 2.6.19 |
| MariaDB | 10.11.14 |
一、安装 OpenVPN 和 EasyRSA
apt update
apt install -y openvpn easy-rsa
二、搭建 CA 证书体系
# 进入 EasyRSA 目录
cd /etc/openvpn/easy-rsa
# 初始化 PKI
./easyrsa init-pki
# 生成 CA 证书(无密码,一路回车)
./easyrsa build-ca nopass
# 生成服务器证书和密钥
./easyrsa gen-req server nopass
./easyrsa sign-req server server <<< "yes"
# 生成 DH 参数
./easyrsa gen-dh
# 生成 TLS 认证密钥(防 DDoS 和端口扫描)
openvpn --genkey secret ta.key
生成的文件位于 /etc/openvpn/easy-rsa/pki/ 目录下:
ca.crt— CA 根证书issued/server.crt— 服务器证书private/server.key— 服务器私钥dh.pem— DH 参数
将这些文件复制到 OpenVPN 配置目录:
cp pki/ca.crt /etc/openvpn/
cp pki/issued/server.crt /etc/openvpn/
cp pki/private/server.key /etc/openvpn/
cp pki/dh.pem /etc/openvpn/
cp ta.key /etc/openvpn/
三、配置 OpenVPN 服务端
创建 /etc/openvpn/server/server.conf:
port 1194
proto udp
dev tun
# 证书
ca ca.crt
cert server.crt
key server.key
dh dh.pem
tls-auth ta.key 0
# 网段
server 10.8.0.0 255.255.255.0
ifconfig-pool-persist /var/log/openvpn/ipp.txt
# DNS(推荐使用国内 DNS)
push "dhcp-option DNS 223.5.5.5"
push "dhcp-option DNS 119.29.29.29"
# 路由(根据需求选择,见下方路由章节)
push "redirect-gateway def1 bypass-dhcp"
keepalive 10 120
cipher AES-256-GCM
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
# 降权运行
user nobody
group nogroup
persist-key
persist-tun
status /var/log/openvpn/openvpn-status.log
log-append /var/log/openvpn/openvpn.log
verb 3
DNS 选择:国内服务器建议使用阿里 DNS(223.5.5.5)和腾讯 DNSPod(119.29.29.29),比 Google DNS(8.8.8.8)解析更快。
启动服务
systemctl enable openvpn-server@server
systemctl start openvpn-server@server
注意:Ubuntu 使用
openvpn-server@server.service,它读取/etc/openvpn/server/server.conf。不要用旧的openvpn.service(那个会启动后立即退出)。
开启 IP 转发
echo "net.ipv4.ip_forward = 1" >> /etc/sysctl.conf
sysctl -p
配置 NAT
iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE
防火墙放行
ufw allow 1194/udp
如果是阿里云等云服务器,还需要在安全组中放行 UDP 1194 端口。
四、生成客户端证书
cd /etc/openvpn/easy-rsa
# 生成客户端密钥和请求(用 --batch 避免交互式 CN 输入问题)
./easyrsa --batch gen-req client1 nopass
# 签发客户端证书
./easyrsa --batch sign-req client client1
五、生成 .ovpn 配置文件
将所有证书和密钥内嵌到一个 .ovpn 文件中,方便客户端导入:
cat > /etc/openvpn/client/client1.ovpn << 'EOF'
client
dev tun
proto udp
remote YOUR_SERVER_IP 1194
resolv-retry infinite
nobind
persist-tun
auth-nocache
remote-cert-tls server
cipher AES-256-GCM
data-ciphers AES-256-GCM:AES-128-GCM:CHACHA20-POLY1305
verb 3
auth-user-pass
key-direction 1
EOF
# 内嵌证书和密钥
echo "<ca>" >> /etc/openvpn/client/client1.ovpn
cat /etc/openvpn/ca.crt >> /etc/openvpn/client/client1.ovpn
echo "</ca>" >> /etc/openvpn/client/client1.ovpn
echo "<cert>" >> /etc/openvpn/client/client1.ovpn
cat /etc/openvpn/easy-rsa/pki/issued/client1.crt >> /etc/openvpn/client/client1.ovpn
echo "</cert>" >> /etc/openvpn/client/client1.ovpn
echo "<key>" >> /etc/openvpn/client/client1.ovpn
cat /etc/openvpn/easy-rsa/pki/private/client1.key >> /etc/openvpn/client/client1.ovpn
echo "</key>" >> /etc/openvpn/client/client1.ovpn
echo "<tls-auth>" >> /etc/openvpn/client/client1.ovpn
cat /etc/openvpn/ta.key >> /etc/openvpn/client/client1.ovpn
echo "</tls-auth>" >> /etc/openvpn/client/client1.ovpn
客户端配置说明:
persist-tun:断线重连时保持 TUN 设备,不用重新创建auth-nocache:连接成功后立即清除内存中的密码,防止被 dump 进程获取- 不要使用
persist-key:新版 OpenVPN(2.6+)已将其设为默认行为,写了会产生 deprecated 警告
六、添加密码认证(PAM)
纯证书认证已经足够安全,但如果你需要管理大量用户、记录登录日志、对接统一身份系统,就需要加上密码认证。
方案对比
| 方案 | 优点 | 缺点 |
|---|---|---|
自定义脚本 (auth-user-pass-verify) | 灵活,可对接任意后端 | 以 nobody 运行,受 systemd 安全限制,踩坑多 |
PAM 共享库插件 (openvpn-auth-pam.so) | 特权分离、虚拟内存传密码、官方推荐 | 需要 PAM 模块支持 |
官方建议:生产环境用共享库插件,不要用脚本。
配置 PAM
创建 /etc/pam.d/openvpn:
# 方式一:使用系统用户(简单场景)
auth required pam_unix.so
account required pam_unix.so
# 方式二:对接 MariaDB(大量用户)
auth required pam_mysql.so user=vpn_auth passwd=YOUR_DB_PASSWORD host=localhost db=vpn_users table=users usercolumn=username passwdcolumn=password crypt=0
account required pam_mysql.so user=vpn_auth passwd=YOUR_DB_PASSWORD host=localhost db=vpn_users table=users usercolumn=username passwdcolumn=password crypt=0
修改服务端配置
在 server.conf 中添加:
plugin /usr/lib/openvpn/openvpn-plugin-auth-pam.so openvpn
verify-client-cert require
username-as-common-name
修改客户端配置
在 .ovpn 文件中添加:
auth-user-pass
七、对接 MariaDB 数据库
安装 MariaDB
apt install -y mariadb-server mariadb-client
systemctl enable mariadb
systemctl start mariadb
创建数据库和用户表
CREATE DATABASE vpn_users;
USE vpn_users;
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(64) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
email VARCHAR(128),
status TINYINT DEFAULT 1,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
last_login DATETIME NULL,
INDEX idx_username (username)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
CREATE USER 'vpn_auth'@'localhost' IDENTIFIED BY 'YourSecurePassword';
GRANT SELECT ON vpn_users.users TO 'vpn_auth'@'localhost';
FLUSH PRIVILEGES;
安装 pam_mysql
apt install -y libpam-mysql
重启 OpenVPN:
systemctl restart openvpn-server@server
八、路由模式选择
OpenVPN 支持多种路由模式,根据你的需求选择:
模式一:全流量代理(Full Tunnel)
效果:所有流量都走 VPN 服务器,对外 IP 变成服务器 IP。
适用场景:
- 需要隐藏真实 IP
- 在不安全的网络(公共 WiFi)下加密所有流量
- 翻越网络限制
服务端配置:
push "redirect-gateway def1 bypass-dhcp"
原理:def1 会向客户端推送三条路由:
0.0.0.0/1(覆盖所有流量)128.0.0.0/1(覆盖剩余流量)- 原默认网关(保留直连路由)
bypass-dhcp 排除 DHCP 广播流量,避免干扰本地网络。
模式二:分流模式(Split Tunnel)
效果:只有访问特定网段走 VPN,其余流量走本地网络。
适用场景:
- 只需要访问公司/学校内网
- 节省 VPN 服务器带宽
- 降低延迟(日常上网不绕路)
服务端配置:
# 删除或注释掉 redirect-gateway
# push "redirect-gateway def1 bypass-dhcp"
# 只推送内网路由(按需修改网段)
push "route 10.0.0.0 255.0.0.0"
push "route 172.16.0.0 255.240.0.0"
push "route 192.168.0.0 255.255.0.0"
模式三:仅 VPN 子网(Minimal)
效果:只路由 VPN 子网(10.8.0.0/24),客户端之间可以互通,其余流量全部走本地。
适用场景:
- 组建虚拟局域网(联机游戏、文件共享)
- 不需要通过 VPN 上网
服务端配置:
# 删除或注释掉 redirect-gateway
# push "redirect-gateway def1 bypass-dhcp"
# 不推送任何路由,VPN 子网自动路由
# server 指令已隐含 10.8.0.0/24 的路由
模式四:客户端自定义路由
效果:服务端不推送路由,由客户端 .ovpn 文件自行决定。
适用场景:
- 不同客户端需要不同路由策略
- 高级用户自行控制
服务端配置:
# 不推送路由
# push "redirect-gateway def1 bypass-dhcp"
客户端配置(在 .ovpn 中按需添加):
# 全流量代理
redirect-gateway def1
# 或者只代理特定网段
route 10.0.0.0 255.0.0.0
route 192.168.1.0 255.255.255.0
路由模式对比
| 模式 | 服务端配置 | 流量走向 | 带宽消耗 | 隐私保护 |
|---|---|---|---|---|
| 全流量代理 | push "redirect-gateway def1" | 全部走 VPN | 高 | 强 |
| 分流模式 | push "route x.x.x.x" | 指定网段走 VPN | 中 | 部分 |
| 仅 VPN 子网 | 不推送路由 | 仅 VPN 内互通 | 低 | 无 |
| 客户端自定义 | 不推送路由 | 客户端决定 | 取决于配置 | 取决于配置 |
提示:修改服务端路由配置后需要重启 OpenVPN:
systemctl restart openvpn-server@server。客户端需要重新连接才能生效。
九、踩坑记录
1. openvpn.service 启动后立即退出
现象:systemctl status openvpn 显示 active (exited),端口 1194 无进程监听。
解决:使用 openvpn-server@server.service,配置文件放在 /etc/openvpn/server/server.conf。
2. TLS 握手超时
现象:客户端显示 TLS key negotiation failed。
排查:服务端无连接记录 → 数据包未到达 → 检查云安全组。
解决:在阿里云安全组中添加 UDP 1194 入站规则。
3. 自定义认证脚本不执行
现象:脚本手动测试通过,OpenVPN 调用时失败。
原因:systemd 安全限制(PrivateTmp、ProtectSystem)+ via-env 密码编码问题。
解决:改用 PAM 共享库插件。
4. pam_mysql 密码格式不兼容
现象:MySQL SHA2() 的十六进制哈希,pam_mysql crypt=3 无法匹配。
原因:crypt=3 对应 MD5,不支持 SHA-256。
解决:使用 crypt=0(明文对比)或应用层使用兼容的哈希格式。
5. 密码文件权限问题
现象:nobody 用户无法读取权限 600 的密码文件。
解决:改用 PAM 插件(特权分离机制自动处理权限)。
6. 客户端红色告警:persist-key deprecated
现象:连接时显示 DEPRECATED OPTION: --persist-key option ignored。
原因:OpenVPN 2.6+ 已将密钥持久化作为默认行为,persist-key 选项被废弃。
解决:从 .ovpn 文件中删除 persist-key 这一行。
7. 客户端红色告警:password cache in memory
现象:连接时显示 WARNING: this configuration may cache passwords in memory。
原因:OpenVPN 默认会把用户名密码缓存在内存中,方便断线自动重连。
解决:在 .ovpn 文件中添加 auth-nocache。代价是断线后需要重新输入密码。
十、最终架构
客户端 (OpenVPN GUI / Tunnelblick)
│
├─ 证书认证 (TLS 1.3)
│
└─ 密码认证
│
▼
OpenVPN Server (nobody, UDP 1194)
│
▼
openvpn-auth-pam.so (特权分离)
│
▼
pam_mysql.so
│
▼
MariaDB (vpn_users.users)
十一、客户端使用
- 下载 OpenVPN 客户端(Windows: OpenVPN Connect,macOS: Tunnelblick,iOS/Android: OpenVPN Connect)
- 导入
.ovpn文件 - 连接,输入用户名和密码
总结
关键经验:
- 用
openvpn-server@server而不是openvpn.service - 云服务器别忘了安全组放行 UDP 1194
- 生产环境用 PAM 插件,不要用自定义脚本
- pam_mysql 的 crypt 参数要和密码存储格式匹配
- 国内服务器用阿里/腾讯 DNS,比 Google DNS 解析更快
- 客户端配置去掉
persist-key(已废弃),加上auth-nocache(安全) - 根据需求选择路由模式:全流量代理、分流、仅内网
OpenVPN + PAM + MariaDB 的组合,既能满足小团队几十人的使用,也能扩展到上万人的学校或企业场景。