OpenVPN Manager 教程:Web 管理控制台从部署到证书签发
搭 OpenVPN 最繁琐的从来不是 openvpn 本身,而是证书:CA 怎么建、客户端证书怎么签、吊销了怎么生效、证书快过期了谁提醒。OpenVPN Manager 把这些全部收进一个 Web 控制台:Go(Gin)+ Vue 3 + Element Plus + ECharts,单二进制部署,多实例托管、客户端证书一键签发、流量审计、证书到期提醒、自动备份恢复全都有。本文从一键安装讲到白名单访问控制、内网转发和全平台客户端接入。
一、项目简介
OpenVPN Manager 是 AI-Agent 组织下的开源项目(MIT 协议),定位是“开箱即用的 OpenVPN Web 管理控制台”。后端是 Go 的 Gin 框架,前端是 Vue 3 单页应用(Element Plus 组件库 + ECharts 图表),编译后前端 dist 和后端二进制一起部署,单二进制就能跑。管理端口默认 8089,浏览器打开就能用。
它和直接手写 server.conf + easy-rsa 的区别在于:实例、用户、证书、备份、审计全部进数据库和文件系统统一管理,操作都在 Web 上点几下完成;客户端配置是一键生成的 .ovpn 文件,CA、证书、私钥、TLS-Auth 全内嵌在一个文件里,发给用户就能用,不用再单独传一堆 crt/key 文件。
二、功能特性
| 模块 | 说明 |
|---|---|
| 多实例管理 | 同一台机器上跑多个 OpenVPN 实例,每个独立端口 / 协议 / 子网 / PKI |
| 客户端证书 | 一键签发,自动生成 .ovpn(内嵌 CA / Cert / Key / TLS-Auth),无需额外文件 |
| 固定 IP | 通过 CCD(client-config-dir)为指定用户分配固定 VPN IP |
| 访问控制(白名单) | 实例 / 用户两层 allow_networks,合并生效;服务端 iptables 强制隔离 |
| 多账号管理 | db.json 中存 bcrypt 哈希,可创建多个 admin / operator 账号,首登强制改密 |
| 启停控制 | Web 一键启动 / 停止实例,显示 PID 与状态 |
| 流量审计 | 解析 status-version 3 输出,记录上下行字节 / 连接时长 |
| 证书到期提醒 | 仪表盘统计 30 天内到期的证书,单独证书管理页查看完整清单 |
| 自动备份 | 一键打包 PKI + 实例配置 + 客户端配置为 tar.gz |
| 一键恢复 | 上传 / 选择已有备份,直接覆盖还原 |
| 操作审计 | 所有写操作(创建 / 修改 / 删除 / 吊销 / 备份)持久化,记录操作者 / IP / 结果 |
| JWT 鉴权 | 登录后 12 小时有效 token,无状态可水平扩展 |
| 内网转发 | 文档化 IP 转发 + NAT 一键配置,客户端可访问服务端 LAN |
三、架构概览
整体是经典的前后端分离 + 进程托管结构:
浏览器(Vue 3 SPA,Element Plus + ECharts)
│ HTTPS / HTTP(8089)
▼
Go HTTP 服务(Gin)
- JWT 鉴权
- REST API
- OpenVPN 进程管理
- OpenSSL 证书签发 / 吊销
│
├──► systemd:openvpn-manager.service
│
└──► /usr/sbin/openvpn --config <conf> --cd <dir>
→ status.log
数据目录 <DataDir>
pki/ # CA、DH 参数、TLS-Auth 密钥
instances/<name>/... # server.conf、status.log、ipp.txt、ccd/、logs/
clients/<inst>/<user> # 生成的 .ovpn
backups/ # 自动备份目录
db.json # 元数据(JSON):实例 / 用户 / 审计
所有写操作都会进审计日志。Go 服务本身不直接处理 VPN 流量——流量由它拉起的 openvpn 进程处理,Go 服务负责进程的生死、配置生成、证书签发和状态解析(读 status.log 做流量审计和在线统计)。数据目录建议定期备份,丢了 pki/ca.key 就等于丢了整个 CA,所有客户端证书都要重签。
四、系统要求与安装参数
- 操作系统:Ubuntu 20.04+ / Debian 11+ / CentOS Stream 9+ / Rocky 9+
- CPU:1 核即可;内存:最低 512 MB;磁盘:1 GB(证书 + 用户越多占用越大)
- 网络:开放 8089 端口(管理界面)+ 每个 OpenVPN 实例一个 UDP / TCP 端口(默认 1194)
- 权限:必须以 root 启动(OpenVPN 需要 TUN/TAP 设备权限)
- 依赖工具(脚本自动安装):openvpn ≥ 2.5、openssl ≥ 1.1、nodejs ≥ 18、npm、golang ≥ 1.21
| 参数 | 默认值 | 说明 |
|---|---|---|
| --port | 8089 | Web 管理端口 |
| --user | admin | 初始管理员用户名 |
| --pass | admin123 | 初始管理员密码,生产环境必须改 |
| --dir | /opt/openvpn-manager | 安装目录 |
| -u, --uninstall | - | 卸载(停服务、删除安装目录、清理 systemd) |
JWT 签名密钥会在安装时自动生成 64 位随机十六进制串,写入 /etc/systemd/system/openvpn-manager.service 的 OVPNMGR_JWT_SECRET 环境变量。不要复制生产环境的这个文件到别的机器,否则 token 可以跨机器伪造。
五、一键安装与手动安装
5.1 一键安装
git clone ssh://git@git.cnbugs.com:10022/AI-Agent/openvpn-manager.git
cd openvpn-manager
chmod +x scripts/install.sh
sudo ./scripts/install.sh
脚本会做这些事:识别发行版,自动选 apt / dnf / yum;安装系统依赖;npm install + npm run build 构建前端;go build 编译后端二进制;拷贝到 /opt/openvpn-manager;注册 systemd 单元并启动;健康检查 /api/health;打印访问地址和默认账号。成功后会看到:
================================================================
OpenVPN Manager 安装完成
================================================================
访问地址 : http://<服务器IP>:8089
用户名 : admin
密码 : admin123
安装目录 : /opt/openvpn-manager
数据目录 : /opt/openvpn-manager/data
配置单元 : /etc/systemd/system/openvpn-manager.service
================================================================
也可以在安装时直接指定参数:
sudo ./scripts/install.sh \
--port 9090 \
--user admin \
--pass 'StrongPass!2026' \
--dir /opt/openvpn-manager
5.2 手动安装
适合不想用 systemd 的场景(比如 Docker):
# 1. 装依赖
apt-get install -y openvpn openssl nodejs npm golang-go git # Debian/Ubuntu
# 或
dnf install -y openvpn openssl nodejs npm golang git # RHEL/Fedora
# 2. 编译
git clone <this-repo> && cd openvpn-manager
cd frontend && npm install --include=dev && npm run build && cd ..
cd backend && go build -o ../bin/openvpn-manager ./cmd/server && cd ..
# 3. 运行
mkdir -p data
OVPNMGR_PORT=8089 \
OVPNMGR_ADMIN_USER=admin \
OVPNMGR_ADMIN_PASS='your-password' \
OVPNMGR_JWT_SECRET=$(openssl rand -hex 32) \
./bin/openvpn-manager --data ./data --dist ./dist
5.3 目录结构与 systemd 管理
/opt/openvpn-manager/
├── bin/openvpn-manager # Go 编译后的二进制(约 21MB)
├── dist/ # Vue 构建产物(index.html + assets/)
├── data/ # 数据目录,定期备份
│ ├── db.json # 元数据(JSON):实例 / 用户 / 审计
│ ├── pki/
│ │ ├── ca.crt # CA 证书(全局共享)
│ │ ├── ca.key # CA 私钥(权限 0600)
│ │ ├── dh.pem # DH 参数
│ │ └── ta.key # TLS-Auth 共享密钥
│ ├── instances/
│ │ └── <instance-name>/
│ │ ├── server.conf # OpenVPN 服务端配置
│ │ ├── status.log # OpenVPN 状态文件(10 秒周期)
│ │ ├── ipp.txt # IP 池持久化
│ │ ├── ccd/ # 客户端配置目录(固定 IP)
│ │ ├── logs/openvpn.log
│ │ └── pki/
│ │ ├── issued/<cn>.crt
│ │ └── private/<cn>.key
│ ├── clients/
│ │ └── <instance>/<user>.ovpn
│ └── backups/ # 自动备份目录
└── .env # 环境变量(权限 0600)
systemctl status openvpn-manager # 状态
systemctl restart openvpn-manager # 重启
systemctl stop openvpn-manager # 停止
journalctl -u openvpn-manager -f # 跟踪日志(Ctrl+C 退出)
journalctl -u openvpn-manager -n 200 # 最近 200 行
# 修改配置后需要:
systemctl edit openvpn-manager # 改环境变量(创建 override.conf)
systemctl daemon-reload
systemctl restart openvpn-manager
升级时先 git pull 再跑一次 install.sh,脚本会重新编译并覆盖二进制,保留 data/、backups/、dist/,然后重启服务。重要:升级前请先在 Web 界面“备份与恢复”页面手动做一次备份,以防万一。卸载用 sudo ./scripts/install.sh -u,会停服务、禁用自启、删除 systemd 单元和 /opt/openvpn-manager,源码目录不会被删除,如需彻底清理请手动 rm -rf。
六、首次配置:登录、改密、建实例、建用户
6.1 登录与修改默认密码
浏览器访问 http://<服务器IP>:8089/,默认账号 admin / admin123。生产环境第一步就是改密码:首次用默认账号登录后,系统强制弹出“修改初始密码”对话框,必须改成不少于 6 位的强密码才能继续使用,无法跳过。改完写进 data/db.json,之后不再需要重启服务,密码以 bcrypt 哈希持久化。
平时改自己的密码:右上角 admin 下拉 → “修改密码” → 填当前密码 + 新密码 → 保存,立即生效。管理员可以在左侧“管理员”菜单管理其他账号:新建账号(admin / operator 角色)、重置密码、启用 / 禁用、删除(不能删除自己、不能删除最后一个 admin)。所有账号操作都进审计日志。
忘记密码时可以临时用环境变量密码覆盖(只用于“首次登录然后改密”,不持久):编辑 systemd 单元把 OVPNMGR_ADMIN_PASS 改成临时密码,重启后登录,系统会触发 SeedDefaultAdminIfEmpty。注意这条路径只在 db.json 中没有任何 admin 时生效;否则会校验 db.json 中的账号。更可靠的做法是直接编辑 data/db.json,把 password_hash 替换为 bcrypt 哈希(用 htpasswd -bnBC 10 "" yourpass | tr -d ':\n' 生成)后重启服务。环境变量里的 OVPNMGR_ADMIN_USER / OVPNMGR_ADMIN_PASS 仅在数据库为空时作为初始 seed 使用。
6.2 创建第一个实例
Web → “实例管理” → “新建实例”,字段说明如下:
| 字段 | 推荐值 | 说明 |
|---|---|---|
| 名称 | prod | 字母数字下划线,作为目录名,不可重复 |
| 端口 | 1194 | OpenVPN 监听端口,不能与已有服务冲突 |
| 协议 | udp | udp 性能好,tcp 穿透性强 |
| 设备 | tun | tun 路由模式(常用),tap 桥接模式 |
| 子网 | 10.8.0.0/24 | 给客户端分配的 VPN 内网网段 |
| 加密 | AES-256-GCM | AES-128-GCM 更快,CHACHA20-POLY1305 适合 ARM |
| 摘要 | SHA256 | |
| 推送 DNS | 1.1.1.1、8.8.8.8 | 一行一条 dhcp-option DNS |
| 推送路由 | 192.168.1.0 255.255.255.0 | 让客户端能访问内网,每行一条 CIDR |
点“保存”会自动创建实例目录、用全局 CA 签发服务端证书、生成 server.conf。然后点列表里的“启动”按钮即可。如果失败,看 journalctl -u openvpn-manager -n 50。OpenVPN 需要 root 启动,如果 systemd 单元不是 root 运行,启动会失败,检查 ps aux | grep openvpn-manager 确认主进程是 root,以及单元文件里的 User=root。
6.3 创建客户端用户并下载配置
Web → “用户管理”:顶部下拉框选择实例,点“新建用户”,填用户名(CN,如 alice,字母数字下划线,作为证书 CN)、备注、邮箱,固定 IP 留空为动态,若填 10.8.0.10,Alice 每次连上都是这个 VPN IP。保存后表格出现 alice,点“下载 .ovpn”。
下载的文件约 4-5 KB,里面已经内嵌了 CA 证书、客户端证书、客户端私钥、TLS-Auth 密钥,直接发给用户即可,无需额外的 ca.crt 等文件。注意 remote 行默认是 vpn.example.com,需要改成你自己的服务器公网域名或 IP:可以在 Web 界面“用户管理”页面顶部的“客户端连接的远端域名/IP”输入框填入服务器地址再点下载,或者下载后用文本编辑器手动改 remote 行。
七、IP 转发与内网访问
场景:客户端连上 VPN 后,想访问服务端所在的局域网设备(比如 172.16.2.0/24 网段的服务器、打印机、NAS)。前提是客户端能 ping 通 VPN 网关(10.8.0.1),但访问不了 LAN。OpenVPN 默认只让客户端访问 VPN 子网本身,不能直接访问服务端 LAN。要打通,服务端需要做两件事:开启 IP 转发(net.ipv4.ip_forward=1),让内核把 VPN 客户端的包从 tun 设备转到 LAN 网卡;配置 NAT(MASQUERADE),让 VPN 客户端的源 IP 在离开服务端时被替换成 LAN 网卡 IP,这样 LAN 设备知道怎么回包。
7.1 一键启用(推荐)
# 1. 立即启用 IP 转发
sudo sysctl -w net.ipv4.ip_forward=1
# 2. 持久化(重启不丢)
echo "net.ipv4.ip_forward=1" | sudo tee /etc/sysctl.d/99-openvpn-manager.conf
# 3. 添加 NAT 规则
# eth0 是服务端接 LAN 的网卡名(可能是 ens192 / eno1 / enp0s3,用 ip a 查看)
LAN_IF="eth0" # ← 改成实际网卡名
sudo iptables -t nat -A POSTROUTING -s 10.8.0.0/24 -o "$LAN_IF" -j MASQUERADE
sudo iptables -A FORWARD -i "$LAN_IF" -o tun+ -m state --state RELATED,ESTABLISHED -j ACCEPT
sudo iptables -A FORWARD -i tun+ -o "$LAN_IF" -j ACCEPT
# 4. 持久化 iptables(重启不丢)
sudo apt-get install -y iptables-persistent # Debian/Ubuntu
sudo netfilter-persistent save # 保存当前规则
# 或:sudo service iptables save # CentOS/RHEL
tun+ 里的 + 是通配,匹配所有 tun 设备(tun0、tun1……),多个实例并存也能用。找对 LAN 网卡的方法:ip -br a 简略列出所有网卡和 IP,ip route | grep default 看默认路由走哪块网卡,通常就是 LAN 网卡,例如 default via 172.16.2.254 dev ens192 说明 LAN 网卡是 ens192。
7.2 push 路由与验证
光在服务端做 NAT 不够,还得让客户端知道“要去 172.16.2.0/24 就走 VPN”。两种做法:方法 A 是在实例里 push 路由,编辑实例的 server.conf 添加 push "route 172.16.2.0 255.255.255.0",然后在 Web 面板重启实例;方法 B 是在 Web 面板“实例管理”里配置,把 LAN 网段加到实例的 AccessMode 白名单或 push 路由列表,白名单模式下 allow_networks 会自动生成对应的 push 路由。
# 服务端验证
sudo iptables -t nat -L POSTROUTING -n -v | grep MASQUERADE
# 客户端验证(VPN 连接后)
ping 10.8.0.1 # 必通(网关)
ping <服务端 LAN 上的某个 IP> # 通了说明成功
curl ifconfig.me # 显示服务端公网 IP,说明 NAT 生效
| 症状 | 排查 |
|---|---|
| 客户端能 ping 通 10.8.0.1,ping 不通 LAN | 没开 IP 转发,或没加 MASQUERADE 规则 |
| 能 ping 通 LAN IP,但 SSH / 服务不通 | LAN 设备防火墙拒绝 VPN 子网;在 LAN 设备上 iptables -I INPUT -s 10.8.0.0/24 -j ACCEPT |
| 重启后规则丢失 | 没装 iptables-persistent 或没保存 |
| ping 得通但访问慢 / 丢包 | MTU 问题;在 server.conf 加 tun-mtu 1400 mssfix 1360 |
| ip_forward 已开但客户端仍上不了网 | push 路由缺失;客户端路由表里没有目标 LAN 网段 |
检查清单:cat /proc/sys/net/ipv4/ip_forward 必须输出 1;iptables -t nat -L POSTROUTING -n 应该有 10.8.0.0/24 的 MASQUERADE;iptables -L FORWARD 看到 policy DROP 就需要显式 ACCEPT tun+ 和 LAN 之间的包;客户端连上后 ip route 应该看到 10.8.0.0/24 dev tun0 和 172.16.2.0/24 via 10.8.0.1 dev tun0。如果服务端有多块 LAN 网卡,MASQUERADE 规则要分别添加,或者直接用 -o eth+ 一次性匹配所有以太网卡(不太安全,慎用)。
八、访问控制(白名单模式)
核心需求:VPN 用户登录后,只能访问你明确允许的内网网段,默认与所有内网隔离。每个实例有一个 access_mode 字段:open(默认)不限制,客户端可访问所有可达网段;whitelist 仅允许访问白名单中列出的内网网段。
白名单模式的工作原理分五步:实例配置 allow_networks 是该实例下所有用户共享的允许网段;用户配置 allow_networks 是单个用户的额外允许网段,在实例基础上叠加;实际生效的是两者并集(去重);服务端强制执行——OpenVPN 自动生成 client-connect.sh / client-disconnect.sh 脚本,每个用户连接时,服务端的 FORWARD 链插入 iptables ACCEPT 规则,只放行到白名单网段的流量,其他内网流量在服务端被 REJECT(客户端看到的是“无法连接”,而非“超时”);客户端推送——自动 push "redirect-gateway def1 bypass-dhcp" 让客户端把所有流量都走 VPN(否则白名单没意义),并按白名单 push route ... 让客户端知道这些网段在 VPN 后。
双重防护:即使客户端操作系统被绕过,iptables 仍会拒绝非法流量。Web 端用法是:实例管理 → 新建 / 编辑实例 → “访问控制”下拉选“白名单(whitelist)” → “允许网段”每行一个 CIDR(如 192.168.1.0/24、10.0.0.0/8)→ 保存;用户管理 → 编辑用户 → “额外允许网段”填这个用户特有的网段 → 保存;启动实例后会执行一次 iptables -A FORWARD -i tunX -j REJECT(仅白名单模式)作为兜底。
# 创建白名单实例
curl -X POST http://localhost:8089/api/instances \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{
"name": "internal",
"port": 1194,
"subnet": "10.8.0.0/24",
"access_mode": "whitelist",
"allow_networks": ["192.168.1.0/24", "10.0.0.0/8"]
}'
# 创建用户,额外允许一个网段
curl -X POST http://localhost:8089/api/instances/$IID/users \
-H "Authorization: Bearer $TOKEN" \
-d '{"username":"alice","allow_networks":["172.16.0.0/16"]}'
验证白名单生效:客户端连上 VPN,先确认拿到 VPN IP(ip addr show | grep 10.8.0),测允许的网段应该通(ping 192.168.1.1),测未允许的网段应该 REJECT(ping 192.168.50.1 不通,返回 ICMP net unreachable)。服务端验证 iptables 规则:iptables -L FORWARD -n --line-numbers,应该看到 -i tun0 -j REJECT 在底部,前面若干条 -s <vpn-ip> -d <allowed-net> -j ACCEPT。
| 项 | 说明 |
|---|---|
| 仅 IPv4 | IPv6 白名单需要扩展 client-connect.sh 使用 route-ipv6,目前未实现 |
| 需要 root + iptables | 若运行在容器内 / 无 root,白名单模式会以 open 模式退化运行 |
| 端口转发必须由客户端发起 | 客户端连入后,服务端只允许它主动访问白名单中的目标;不能用 VPN 当跳板从外部进入内网 |
| 修改 allow_networks 后 | 已有用户需重新连接一次才能拿到新的 push route;新用户即时生效 |
| 删除 / 吊销用户 | 会清掉 iptables 中对应的 ACCEPT 规则,不影响其他用户 |
常见误用有三个:客户端关掉 VPN 网关也没用,服务端 iptables 仍会拒绝非白名单流量;客户端把 allowed 网段路由改成另一条也没用,服务端在 FORWARD 链过滤,客户端改路由无效;忘记添加 DNS 服务器,客户端没法解析域名——在“允许网段”里加上 DNS 服务器的 IP(如 8.8.8.8/32)。
九、客户端使用(全平台)
下载的 alice.ovpn 是单一文件,内容大致为:
client
dev tun
proto udp
remote vpn.example.com 1194
resolv-retry infinite
nobind
persist-key
persist-tun
cipher AES-256-GCM
auth SHA256
remote-cert-tls server
verb 3
<ca>
-----BEGIN CERTIFICATE-----
... CA 证书内容 ...
-----END CERTIFICATE-----
</ca>
<cert>
-----BEGIN CERTIFICATE-----
... 客户端证书 ...
-----END CERTIFICATE-----
</cert>
<key>
-----BEGIN PRIVATE KEY-----
... 客户端私钥(请勿泄露)...
-----END PRIVATE KEY-----
</key>
<tls-auth>
-----BEGIN OpenVPN Static key V1-----
... TLS-Auth 共享密钥 ...
-----END OpenVPN Static key V1-----
</tls-auth>
key-direction 1
9.1 Windows
推荐 OpenVPN 官方 GUI 客户端:去 OpenVPN 官网社区下载页下载 Windows 64-bit MSI 安装包,一路下一步安装(会装一个虚拟网卡驱动,需要管理员权限);把 alice.ovpn 放到 C:\Users\<你>\OpenVPN\config\;启动 OpenVPN GUI,右下角出现托盘图标,右键 → Connect;第一次会弹窗请求管理员权限(用于配置路由);连接成功后托盘变绿,会分配一个 10.8.0.x 的 VPN IP。验证:ipconfig /all 看到 10.8.0.x 的 Tap adapter 地址即成功,ping 10.8.0.1 应该通(10.8.0.1 是 OpenVPN 服务端在子网里的网关)。
9.2 macOS
选项一是 Tunnelblick(免费开源,推荐):下载安装后会自动装 tun 驱动,双击 alice.ovpn,问“是否为所有用户安装”时选“仅我”,菜单栏点 Tunnelblick 图标 → Connect alice,状态变绿即成功。选项二是官方 OpenVPN Connect,从 Mac App Store 搜索安装。
9.3 Linux
# Debian/Ubuntu
sudo apt-get install -y openvpn
# RHEL/Fedora
sudo dnf install -y openvpn
# 连接(前台运行能看到日志)
sudo openvpn --config alice.ovpn
# 或后台运行
sudo openvpn --config alice.ovpn --daemon
图形客户端可以用 NetworkManager:装 network-manager-openvpn 和 network-manager-openvpn-gnome,设置 → 网络 → 添加 VPN → “从文件导入 VPN” → 选 alice.ovpn → 保存 → 连接。
9.4 Android 与 iOS
Android 装 OpenVPN Connect(Google Play / F-Droid 都有),把 alice.ovpn 通过 USB、邮件或网盘传到手机,用文件管理器打开 .ovpn,系统会询问用 OpenVPN 打开,点连接,首次会提示接受 VPN 配置,通知栏出现钥匙图标即连接成功。iOS 在 App Store 装 OpenVPN,用“文件”App 把 .ovpn 传到手机(隔空投送也行),在“文件”里点击 .ovpn 选择用 OpenVPN 打开,点 ADD 导入 → 点开关连接,系统弹窗请求添加 VPN 配置,允许后在设置 → 通用 → VPN 里可以看到状态。
9.5 验证连接与排障
无论哪个平台,连接成功后都可以这样验证:VPN IP 是否在配置的子网里(如 10.8.0.x);从客户端 ping 10.8.0.1 应该通;curl ifconfig.me 应显示服务器公网 IP;如果推送了 DNS,客户端 resolv.conf 应看到推送的 DNS。在 Web 控制台,“仪表盘”实时显示当前在线客户端数(10 秒刷新),“连接日志”显示历史连接记录。
| 症状 | 可能原因 |
|---|---|
| 连接后立刻断开 | 客户端证书与 CA 不匹配;服务端证书过期 |
| 拿到 IP 但 ping 不通服务端 | 防火墙没允许 UDP 1194;服务端没启用 IP 转发 |
| 拿到 IP 但访问不了互联网 | 没推送 DNS,或客户端没把 VPN 设为默认网关 |
| 拿到 IP 能 ping 通网关,访问不了 LAN | 没做服务端 NAT(见第七节) |
| Android 连不上 | 服务器在 NAT 后,检查运营商是否屏蔽 UDP |
| iOS 连不上 | 看 OpenVPN 日志(应用内 OpenVPN → Settings → Log) |
十、API 参考
所有 /api 路径(除 /login、/health)都需要 Authorization: Bearer <token>。完整列表见仓库里的 docs/API.md 或启动后查看 internal/api/router.go。下面是常用的:
| 方法 | 路径 | 用途 |
|---|---|---|
| POST | /api/login | 登录,返回 {token, username} |
| GET | /api/dashboard | 仪表盘汇总 |
| GET / POST | /api/instances | 列出 / 创建实例 |
| GET / PUT / DELETE | /api/instances/:id | 单实例 CRUD |
| POST | /api/instances/:id/start / /stop | 启停 |
| GET / POST | /api/instances/:id/users | 用户列表 / 新建 |
| POST | /api/instances/:id/users/:uid/revoke | 吊销 |
| DELETE | /api/instances/:id/users/:uid | 删除 |
| GET | /api/instances/:id/users/:uid/ovpn?host=X | 下载 .ovpn |
| GET | /api/certs | 证书到期清单 |
| GET | /api/connlogs?instance=X | 连接日志 |
| GET / POST / DELETE | /api/backups / /api/backups/:id | 备份管理 |
| POST | /api/backups/:id/restore | 恢复 |
| GET | /api/audits | 审计日志 |
# 登录
TOKEN=$(curl -s -X POST http://localhost:8089/api/login \
-H 'content-type: application/json' \
-d '{"username":"admin","password":"admin123"}' | jq -r .token)
# 创建实例
curl -X POST http://localhost:8089/api/instances \
-H "Authorization: Bearer $TOKEN" \
-H 'content-type: application/json' \
-d '{"name":"prod","port":1194,"subnet":"10.8.0.0/24"}'
十一、防火墙与公网暴露
Web 管理端口(默认 8089)不建议直接暴露公网,建议用防火墙只允许公司 / 家庭 IP 访问,或反代 + HTTPS + Basic Auth,或 SSH 端口转发 ssh -L 8089:127.0.0.1:8089 user@server。OpenVPN 实例端口必须开放给需要连入的客户端,UDP 优先(性能好):
# ufw
ufw allow 1194/udp
ufw allow 1194/tcp # 如果实例用 TCP
# firewalld
firewall-cmd --permanent --add-port=1194/udp
firewall-cmd --reload
若服务器在 NAT 后(如家用宽带),需要在路由器做端口映射 UDP 1194 → 服务器内网 IP。Nginx 反代 + HTTPS 的示例配置如下,注意 OpenVPN 自身的 UDP / TCP 端口(1194 等)不能走 Nginx,要单独放行:
server {
listen 443 ssl http2;
server_name vpn-admin.example.com;
ssl_certificate /etc/letsencrypt/live/vpn-admin.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/vpn-admin.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8089;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
十二、备份恢复与升级卸载
备份在 Web 界面“备份与恢复”页面一键打包 PKI + 实例配置 + 客户端配置为 tar.gz。恢复时选中备份点“恢复”,会覆盖现有 pki/、instances/、clients/,操作前先做一次新的备份以防万一。升级流程是 git pull 后重新跑 install.sh,脚本保留 data/、backups/、dist/。卸载用 sudo ./scripts/install.sh -u,会停服务、禁用自启、删除 systemd 单元和 /opt/openvpn-manager。
十三、常见问题
- 服务启动后访问 8089 提示“无法连接”:按顺序排查
systemctl status openvpn-manager(进程在不在)、journalctl -u openvpn-manager -n 50(启动报错)、ss -tlnp | grep 8089(端口在听吗)、curl http://127.0.0.1:8089/api/health(本机能访问吗)。常见原因是 go / nodejs / openvpn 没装或版本太低; - 实例列表点“启动”提示成功,但状态一直 stopped:OpenVPN 需要 root 启动,检查
ps aux | grep openvpn-manager确认主进程是 root,单元文件里的 User 应该是 root; - 下载的 .ovpn 客户端连不上:确认服务器防火墙 / 路由器开放了对应端口(UDP 1194 等),客户端能 ping 通服务器公网 IP,服务端 status.log 有没有客户端连接尝试;
- 支持 IPv6 吗:服务端支持(把 proto 改成 udp6 或 tcp6,子网用 IPv6 CIDR),但 UI 目前主要按 IPv4 写,IPv6 子网需要手动编辑 server.conf(用“自定义”字段)。
十四、开发与运维建议
# 后端开发
cd backend
go run ./cmd/server --data ../data --dist ../dist
# 前端开发(热重载,会自动代理 /api 到 :8089)
cd frontend
npm install
npm run dev
# 浏览器打开 http://localhost:3000
# 调试模式日志
OVPNMGR_LOG_LEVEL=debug ./bin/openvpn-manager --data ./data --dist ./dist
# 跑测试
cd backend && go test ./...
cd frontend && npm run build # 顺便当 type/lint 校验
# 构建发布版
cd frontend && npm run build && cd ..
cd backend
CGO_ENABLED=0 go build -ldflags "-s -w" -trimpath -o ../bin/openvpn-manager ./cmd/server
- 备份是生命线:定期备份
data/,尤其是pki/ca.key(权限 0600)和db.json,丢了 CA 私钥所有客户端证书都要重签; - 证书到期盯紧点:仪表盘会统计 30 天内到期的证书,单独证书管理页有完整清单,到期前及时重签和重发 .ovpn;
- 吊销要及时:员工离职、设备丢失时在 Web 端吊销对应用户证书,吊销会清掉 iptables 里对应的 ACCEPT 规则,不影响其他用户;
- 管理口别裸奔:8089 不要直接暴露公网,走反代 + HTTPS 或 SSH 隧道;
- 审计日志定期看:所有写操作都记了操作者、IP 和结果,异常操作能第一时间发现;
- JWT 密钥别外泄:安装时生成的 OVPNMGR_JWT_SECRET 只存在 systemd 单元里,不要复制生产环境的单元文件到别的机器。
总结一下:OpenVPN Manager 把 OpenVPN 最繁琐的证书和实例管理收进了一个 Web 控制台——多实例、证书一键签发、白名单隔离、流量审计、备份恢复,一套流程下来,搭一个能给全公司用的 VPN 服务就是一键安装加几次点击的事。记住三个要点:首登强制改密、CA 私钥备份好、管理口不要裸奔。






