OpenVPN 服务器部署与用户证书管理完整流程

给团队提供一条加密的远程接入通道,是很多运维场景的刚需:管理员从家里或出差途中安全地访问内网机器、新机房与办公网之间临时打通运维通道,都可以用自建 OpenVPN 解决。本文给出一套完整的正规自建流程:用 easy-rsa 管理 PKI,逐项解释服务端配置,给每个用户签发独立证书并打包成单个 .ovpn 文件,最后把证书吊销(CRL)的生效验证也走完——吊销不验证,等于没吊销。

适用环境:服务端为一台有公网地址的 Linux 服务器(Rocky Linux 9 / Debian 12 均可,下文以 Rocky 系为例,Debian 系把 dnf 换成 apt、firewalld 换成 ufw 或 nftables 即可)。文中公网地址用文档示例 192.0.2.10,内网网段用 10.20.0.0/24,VPN 虚拟网段为 10.8.0.0/24。

前置条件

  • 服务器有公网 IP(或已做好 UDP 1194 端口映射),且云厂商安全组放行 UDP 1194。
  • 有 root 权限,系统时间已同步(证书校验对时间敏感,先确认 chronyd 正常)。
  • 明确要下发给客户端的内网网段与 DNS,本文示例下发 10.20.0.0/24 和内部 DNS 192.0.2.53。
  • CA 私钥的保管方式想清楚:PKI 目录只放在服务端本机、权限收紧,并纳入加密备份。

第一步:安装与 PKI 初始化

dnf install -y epel-release
dnf install -y openvpn easy-rsa

mkdir -p /etc/openvpn/pki && cd /etc/openvpn/pki
cp -r /usr/share/easy-rsa/3/* .
./easyrsa init-pki
./easyrsa build-ca nopass
./easyrsa build-server-full server nopass
./easyrsa build-dh
openvpn --genkey secret ta.key
./easyrsa gen-crl
chmod 700 /etc/openvpn/pki

先把 PKI 里几个角色的关系理清,后面所有操作就都是顺的。CA 是签发者,它的证书放在每一端做信任锚,私钥只用来签新证书和吊销列表,平时不参与连接;服务端证书证明“你连的确实是这台服务器”,客户端连接时会用 CA 证书校验它,并靠 remote-cert-tls 拒绝拿客户端证书冒充服务端的情形;客户端证书则是一人一证,是服务端识别“谁连进来了”的唯一凭据,配合账号台账就能做到按人授权、按人吊销。ta.key 与三者不同,它不是证书体系的一部分,而是控制通道的预共享密钥,握手第一步就先验它,验不过连证书交换的机会都没有。理解了这套分工,排障时就能快速定位问题出在哪一层。

说明:build-ca nopass 让 CA 私钥不加密

,方便自动化签发,代价是必须把 PKI 目录当作最高敏感数据来保管;生产环境也可以给 CA 私钥加口令,签发时人工输入,安全性更高。ta.key 是 tls-crypt 的静态密钥,给控制通道再加一层加密和认证,能直接挡掉大量无密钥的扫描与握手垃圾流量。gen-crl 现在就生成一份空的 CRL,后面服务端配置会引用它。

第二步:服务端配置 server.conf

把生成的证书文件复制到 /etc/openvpn/server/ 下,然后写配置文件:

cp pki/ca.crt pki/issued/server.crt pki/private/server.key pki/dh.pem ta.key /etc/openvpn/server/

cat > /etc/openvpn/server/server.conf <<'EOF'
port 1194
proto udp
dev tun
ca ca.crt
cert server.crt
key server.key
dh dh.pem
tls-crypt ta.key
server 10.8.0.0 255.255.255.0
push "route 10.20.0.0 255.255.255.0"
push "dhcp-option DNS 192.0.2.53"
data-ciphers AES-256-GCM:AES-128-GCM
auth SHA256
keepalive 10 120
persist-key
persist-tun
user nobody
group nobody
crl-verify crl.pem
status /var/log/openvpn/status.log
log-append /var/log/openvpn/openvpn.log
verb 3
explicit-exit-notify 1
EOF

逐项解释几个容易含糊的点:server 10.8.0.0/24 是隧道虚拟网段,服务端自己占 10.8.0.1,与任何真实网段都不要重叠;push route 告诉客户端“去 10.20.0.0/24 的流量走隧道”,只推需要的网段,不要轻易推默认路由全量转发;data-ciphers 限定数据通道算法,优先 AES-GCM 这类带硬件加速的 AEAD 算法,去掉 CBC 等老算法;auth SHA256 是数据通道 HMAC(用 GCM 时主要作用于控制与兼容路径,保留无妨);crl-verify 指向吊销列表文件,第四、五步会用到,漏了这一行,吊销永远不生效;服务以 nobody 身份降权运行,所以证书和 CRL 文件必须对 nobody 可读——这点在常见问题里还会出现。

把 CRL 放到配置目录并保持可更新:

cp /etc/openvpn/pki/pki/crl.pem /etc/openvpn/server/crl.pem
chmod 644 /etc/openvpn/server/crl.pem

第三步:内核转发与防火墙

客户端的流量要经服务端转发进内网,需要开启 IPv4 转发,并对隧道网段做 NAT(或在核心路由器上加回程路由,二选一,NAT 更省事):

echo 'net.ipv4.ip_forward = 1' > /etc/sysctl.d/99-openvpn.conf
sysctl -p /etc/sysctl.d/99-openvpn.conf

firewall-cmd --permanent --add-service=openvpn
firewall-cmd --permanent --add-masquerade
firewall-cmd --reload
firewall-cmd --list-all

iptables 系发行版的等价 NAT 写法是:iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADE,记得用发行版的持久化机制保存。别忘了云安全组同样要放行 UDP 1194,本机防火墙放行了、安全组没放行是新手最常见的“连不上”。

第四步:启动服务与签发客户端证书

systemctl enable --now openvpn-server@server
systemctl status openvpn-server@server

cd /etc/openvpn/pki
./easyrsa build-client-full alice nopass

每个用户一张独立证书(示例用户 alice),绝不共用一张“万能证书”——共用证书无法按人吊销,出了问题只能换全员的钥匙。把客户端配置写成模板,再把 ca.crt、用户证书、私钥、ta.key 内嵌进同一个 .ovpn 文件,用户拿到一个文件就能导入使用:

cat > alice.ovpn <<'EOF'
client
dev tun
proto udp
remote 192.0.2.10 1194
resolv-retry infinite
nobind
persist-key
persist-tun
remote-cert-tls server
data-ciphers AES-256-GCM:AES-128-GCM
auth SHA256
verb 3
<ca>
EOF
cat pki/ca.crt >> alice.ovpn
echo '</ca>' >> alice.ovpn
echo '<cert>' >> alice.ovpn
sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' pki/issued/alice.crt >> alice.ovpn
echo '</cert>' >> alice.ovpn
echo '<key>' >> alice.ovpn
cat pki/private/alice.key >> alice.ovpn
echo '</key>' >> alice.ovpn
echo '<tls-crypt>' >> alice.ovpn
cat ta.key >> alice.ovpn
echo '</tls-crypt>' >> alice.ovpn
chmod 600 alice.ovpn

.ovpn 里包含私钥,按敏感文件对待:通过安全通道交付给本人,交付后从中间位置删除,不要发到群聊或工单附件里长期留存。

验证

  • 服务端:ss -lunp | grep 1194 确认监听;ip addr show tun0 应有 10.8.0.1。
  • 客户端导入 alice.ovpn 连接,日志出现 Initialization Sequence Completed 即握手成功。
  • 客户端执行 ping 10.8.0.1 通,再 ping 一台内网机器 10.20.0.x,验证转发与路由下发。
  • 服务端看 /var/log/openvpn/status.log,能看到 alice 的虚拟地址与真实来源地址。

第五步:证书吊销与 CRL 生效验证

人员离职或设备丢失时,立刻吊销对应证书并重新生成 CRL,再把新 CRL 同步到服务端配置目录。OpenVPN 每次有新连接时都会读 crl-verify 指向的文件(文件被更新后新连接即按新列表校验,已有连接不受影响,需要时再踢下线):

cd /etc/openvpn/pki
./easyrsa revoke-full alice
./easyrsa gen-crl
cp pki/crl.pem /etc/openvpn/server/crl.pem
chmod 644 /etc/openvpn/server/crl.pem

验证不可省略:拿 alice 的 .ovpn 重新连接,应当被拒绝,服务端日志里出现类似 CRL: certificate revoked 或 VERIFY ERROR 字样,才算吊销真正生效;同时用另一张正常证书(如自己的管理员证书)连接确认服务没被误伤。若吊销后仍能连上,基本是两个原因:server.conf 没写 crl-verify,或更新的 crl.pem 没覆盖到配置里那个路径。

日常管理清单

  • 证书台账:记录每张证书的持有人、签发日期、到期日期;CA 默认有效期 10 年、服务端与客户端证书默认 825 天,提前纳入巡检。
  • 到期检查命令:openssl x509 -in pki/issued/alice.crt -noout -enddate,批量时对 issued 目录循环执行。
  • PKI 备份:整个 /etc/openvpn/pki 加密备份并离线保存一份;丢了 CA 就只能全员换证。
  • 定期复核在线用户:对照 status.log 检查有没有陌生来源 IP 或异常时段连接。
  • CRL 有效期:easy-rsa 生成的 CRL 默认 180 天,到期前重新 gen-crl 并同步,避免 CRL 过期导致校验异常。

常见问题

客户端报 TLS handshake failed。先对时间:客户端或服务端时间偏差过大会让证书校验失败;其次确认两端的 ta.key 是同一份、proto 与端口一致,且中间网络没有拦截 UDP 1194。

连上了但访问不了内网。按顺序查:服务端 ip_forward 是否为 1;NAT 或回程路由有没有做;客户端路由表里是否真的多了 10.20.0.0/24 指向 tun 的路由(Windows 客户端需要管理员权限才能写路由,这是经典坑);内网目标机器自身防火墙是否放行来自 10.8.0.0/24 的访问。

服务端启动即退出,日志说无法读取 crl.pem 或证书。服务降权为 nobody 运行后,文件权限不对就读不到。确认 ca.crt、server.crt、crl.pem 对 nobody 可读,server.key 至少对服务用户可读,目录 /etc/openvpn/server 可进入。

某位用户的证书快到期或私钥丢了,怎么处理?不要试图“续期”原证书,easy-rsa 的正规做法是当新证书处理:用 build-client-full 以新名字(如 alice2)签发一张新证,交付并确认能连后,再吊销旧证书并刷新 CRL。私钥丢失按泄露对待,顺序反过来——先吊销旧证、推 CRL,确认旧证连不上之后再签新证,避免出现两张同时有效的证书给同一个人。整个过程写进证书台账,台账与 issued 目录对不上账时,以台账为准逐张核对。

小结

自建 OpenVPN 的骨架并不复杂:easy-rsa 管 PKI、一人一证、.ovpn 单文件交付、吊销靠 CRL 且必须实测验证。真正决定长期可用性的是管理动作——证书台账、到期巡检、PKI 备份和吊销演练。把这些纳入日常运维清单,这条通道就能稳定承担团队的远程接入需求。建议每季度做一次完整演练:签发一张测试证书、连接、吊销、再连接确认被拒,全程计时并记录,这比任何文档都更能证明这套流程在关键时刻真的可用。

发表回复

后才能评论