Files
openvpn-manager/docs/API.md
T
cnbugs 4c8b7b5188 Add per-instance/user access whitelist
Features:
- New Instance.AccessMode: "open" (default) or "whitelist"
- New Instance.AllowNetworks + VPNUser.AllowNetworks: list of CIDRs
- Effective whitelist = instance allow_networks ∪ user allow_networks (dedup)
- Auto-generates client-connect.sh / client-disconnect.sh for OpenVPN:
    * Reads ccd/<cn> to extract CIDRs
    * Pushes "route <ip> <mask>" to client (client side)
    * Inserts iptables ACCEPT rules in FORWARD chain (server side, defense in depth)
    * Cleans up rules on disconnect
- server.conf auto-includes client-connect / client-disconnect directives
  and push "redirect-gateway def1 bypass-dhcp" in whitelist mode
- ccd/<cn> file format: first line ifconfig-push (static IP), then one CIDR per line
- Editing instance allow_networks refreshes all users' ccd automatically
- New PUT /api/instances/:id/users/:uid endpoint
- CIDR format validation; reject malformed inputs with friendly errors
- Dashboard shows whitelist_instances count and per-instance allow_networks table

Docs:
- README: new section "三、访问控制(白名单模式)" with usage, validation, pitfalls
- docs/API.md: updated Instance / VPNUser model + create/update payloads
- Renumbered client usage section as 四
2026-08-09 20:47:21 +08:00

8.0 KiB

API 参考

所有 /api 路径(除 /login/health)都需要鉴权。

鉴权

登录获取 JWT token(12 小时有效期):

POST /api/login
Content-Type: application/json

{"username": "admin", "password": "admin123"}

成功响应:

{"token": "eyJhbG...", "username": "admin"}

后续请求携带:

Authorization: Bearer <token>

下载 .ovpn 也支持通过 query 携带 token:

GET /api/instances/<iid>/users/<uid>/ovpn?host=vpn.example.com&token=<token>

通用响应

成功返回 JSON 对象或数组;失败:

{"error": "错误描述"}

HTTP 状态码:200/400/401/404/500


1. 公共

健康检查

GET /api/health
→ 200 {"ok": true}

当前登录用户

GET /api/me
→ 200 {"username": "admin"}

登出(仅前端清理 token,服务端无状态)

POST /api/logout
→ 200 {"ok": true}

2. 仪表盘

汇总

GET /api/dashboard
→ 200 {
    "instances": 2,
    "running": 1,
    "users": 10,
    "active_users": 9,
    "online": 3,
    "expiring_certs": 1,
    "recent_audits": [...],
    "recent_conn_logs": [...]
  }

3. 实例

列表

GET /api/instances
→ 200 [Instance, ...]

详情

GET /api/instances/:id
→ 200 Instance
→ 404 {"error": "instance <id> not found"}

新建

POST /api/instances
{
  "name": "prod",
  "port": 1194,
  "proto": "udp",          // udp | tcp
  "dev": "tun",            // tun | tap
  "subnet": "10.8.0.0/24",
  "cipher": "AES-256-GCM", // 可选
  "auth_digest": "SHA256", // 可选
  "push_dns": "dhcp-option DNS 8.8.8.8\ndhcp-option DNS 1.1.1.1",
  "push_routes": "192.168.1.0 255.255.255.0",
  "extra": "",
  "access_mode": "whitelist",                 // open(默认) | whitelist
  "allow_networks": ["192.168.1.0/24", "10.0.0.0/8"]   // CIDR 列表
}
→ 200 Instance

access_mode=whitelist 时,服务端会生成 client-connect.sh / client-disconnect.sh, 通过 OpenVPN --learn-address 钩子在每次客户端连接/断开时修改 iptables FORWARD 链, 仅放行到 allow_networks 中网段的流量,其他内网访问被 REJECT。 allow_networks 为空表示完全隔离(最严格)。

更新

PUT /api/instances/:id
{...同上,id 在 URL 中}
→ 200 Instance

修改实例级 allow_networks 后,所有用户的 ccd 文件会被自动重写。

删除(级联删除该实例下所有用户/证书)

DELETE /api/instances/:id
→ 200 {"ok": true}

启动

POST /api/instances/:id/start
→ 200 {"ok": true}

停止

POST /api/instances/:id/stop
→ 200 {"ok": true}

在线客户端(来自 status-version 3)

GET /api/instances/:id/online
→ 200 [
    {
      "CommonName": "alice",
      "RealAddress": "203.0.113.10:54321",
      "VPNAddress": "10.8.0.10",
      "BytesRecv": 12345,
      "BytesSent": 6789,
      "ConnectedAt": "2026-08-09T12:34:56Z"
    }
  ]

4. 用户

列表(可选按实例过滤)

GET /api/instances/:id/users
→ 200 [VPNUser, ...]

新建(自动签发证书)

POST /api/instances/:id/users
{
  "username": "alice",
  "real_name": "Alice",
  "email": "alice@example.com",
  "static_ip": "10.8.0.10",            // 可选
  "allow_networks": ["172.16.0.0/16"]  // 可选,用户级白名单(在实例基础上叠加)
}
→ 200 VPNUser

修改(改 allow_networks / static_ip 等)

PUT /api/instances/:id/users/:uid
{...同上}
→ 200 VPNUser

修改用户的 allow_networks 后,会重新生成 ccd 文件, 用户重新连接即可拿到新的 push route。

吊销

POST /api/instances/:id/users/:uid/revoke
→ 200 {"ok": true}

删除(同时删除证书与 .ovpn)

DELETE /api/instances/:id/users/:uid
→ 200 {"ok": true}

下载客户端配置

GET /api/instances/:id/users/:uid/ovpn?host=vpn.example.com
→ 200 application/octet-stream (.ovpn 文件)

5. 证书

到期清单

GET /api/certs
→ 200 [
    {
      "instance_id": "uuid",
      "username": "alice",
      "not_before": "2026-08-09T00:00:00Z",
      "not_after": "2036-08-06T00:00:00Z",
      "days_left": 3649
    }
  ]

days_left < 0 表示已过期,< 30 在仪表盘会显示为"即将到期"。


6. 连接日志

GET /api/connlogs?instance=<id>
→ 200 [ConnectionLog, ...]

每条:

{
  "instance_id": "uuid",
  "common_name": "alice",
  "real_ip": "203.0.113.10:54321",
  "vpn_ip": "10.8.0.10",
  "bytes_in": 12345,
  "bytes_out": 6789,
  "connected_at": "...",
  "disconnected_at": null
}

7. 备份

列表

GET /api/backups
→ 200 [Backup, ...]

创建

POST /api/backups
{"note": "before upgrade"}
→ 200 Backup

恢复(覆盖现有数据)

POST /api/backups/:id/restore
→ 200 {"ok": true}

删除

DELETE /api/backups/:id
→ 200 {"ok": true}

8. 审计

GET /api/audits
→ 200 [AuditLog, ...]   // 最近 500 条,倒序

每条:

{
  "id": "uuid",
  "time": "2026-08-09T12:34:56Z",
  "user": "admin",
  "action": "create_user",
  "target": "alice",
  "result": "ok",
  "detail": "instance=<id>",
  "ip": "127.0.0.1"
}

可能的 action:create_instance / update_instance / delete_instance / start_instance / stop_instance / create_user / revoke_user / delete_user / create_backup / restore_backup


数据模型

Instance

{
  "id": "uuid",
  "name": "prod",
  "port": 1194,
  "proto": "udp",
  "dev": "tun",
  "subnet": "10.8.0.0/24",
  "cipher": "AES-256-GCM",
  "auth_digest": "SHA256",
  "push_dns": "...",
  "push_routes": "...",
  "extra": "",
  "access_mode": "open | whitelist",
  "allow_networks": ["192.168.1.0/24", "10.0.0.0/8"],
  "status": "running | stopped | error",
  "pid": 12345,
  "created_at": "...",
  "updated_at": "..."
}

VPNUser

{
  "id": "uuid",
  "instance_id": "uuid",
  "username": "alice",
  "real_name": "Alice",
  "email": "alice@example.com",
  "enabled": true,
  "static_ip": "10.8.0.10",
  "allow_networks": ["172.16.0.0/16"],
  "created_at": "...",
  "revoked_at": null
}

Backup

{
  "id": "uuid",
  "created_at": "...",
  "size": 12345,
  "note": "...",
  "filename": "backup-20260809-123456-abcd1234.tar.gz",
  "includes": ["pki", "instances", "clients"]
}

curl 示例

完整流程(假设服务在 http://localhost:8089):

# 1. 登录
TOKEN=$(curl -s -X POST http://localhost:8089/api/login \
  -H 'content-type: application/json' \
  -d '{"username":"admin","password":"admin123"}' | jq -r .token)

# 2. 创建实例
curl -s -X POST http://localhost:8089/api/instances \
  -H "Authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{
    "name":"prod",
    "port":1194,
    "proto":"udp",
    "dev":"tun",
    "subnet":"10.8.0.0/24",
    "cipher":"AES-256-GCM",
    "auth_digest":"SHA256",
    "push_dns":"dhcp-option DNS 1.1.1.1"
  }'

# 3. 创建用户
IID=$(curl -s http://localhost:8089/api/instances \
  -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')

curl -s -X POST http://localhost:8089/api/instances/$IID/users \
  -H "Authorization: Bearer $TOKEN" \
  -H 'content-type: application/json' \
  -d '{"username":"alice","email":"alice@example.com"}'

# 4. 拿 .ovpn
UID=$(curl -s http://localhost:8089/api/instances/$IID/users \
  -H "Authorization: Bearer $TOKEN" | jq -r '.[0].id')

curl -s -o alice.ovpn \
  "http://localhost:8089/api/instances/$IID/users/$UID/ovpn?host=vpn.example.com" \
  -H "Authorization: Bearer $TOKEN"

错误码

HTTP 含义
400 请求参数错误或操作失败(响应体有 error 字段)
401 未登录或 token 无效/过期
404 资源不存在
500 服务器内部错误

限制

  • 单实例:Web 管理界面并发 100+ 连接无压力
  • 单实例 OpenVPN:理论上限 1024 个并发客户端(受限于 topology subnet 子网大小,可改用 net30 提高)
  • 审计日志最多保留 5000 条
  • 连接日志最多保留 20000 条
  • 备份文件不自动清理,需定期手动删除过期备份