4c8b7b5188
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 四
435 lines
8.0 KiB
Markdown
435 lines
8.0 KiB
Markdown
# API 参考
|
|
|
|
所有 `/api` 路径(除 `/login`、`/health`)都需要鉴权。
|
|
|
|
## 鉴权
|
|
|
|
登录获取 JWT token(12 小时有效期):
|
|
|
|
```http
|
|
POST /api/login
|
|
Content-Type: application/json
|
|
|
|
{"username": "admin", "password": "admin123"}
|
|
```
|
|
|
|
成功响应:
|
|
```json
|
|
{"token": "eyJhbG...", "username": "admin"}
|
|
```
|
|
|
|
后续请求携带:
|
|
```
|
|
Authorization: Bearer <token>
|
|
```
|
|
|
|
下载 `.ovpn` 也支持通过 query 携带 token:
|
|
```
|
|
GET /api/instances/<iid>/users/<uid>/ovpn?host=vpn.example.com&token=<token>
|
|
```
|
|
|
|
## 通用响应
|
|
|
|
成功返回 JSON 对象或数组;失败:
|
|
|
|
```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, ...]
|
|
```
|
|
|
|
每条:
|
|
```json
|
|
{
|
|
"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 条,倒序
|
|
```
|
|
|
|
每条:
|
|
```json
|
|
{
|
|
"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
|
|
```json
|
|
{
|
|
"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
|
|
```json
|
|
{
|
|
"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
|
|
```json
|
|
{
|
|
"id": "uuid",
|
|
"created_at": "...",
|
|
"size": 12345,
|
|
"note": "...",
|
|
"filename": "backup-20260809-123456-abcd1234.tar.gz",
|
|
"includes": ["pki", "instances", "clients"]
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## curl 示例
|
|
|
|
完整流程(假设服务在 `http://localhost:8089`):
|
|
|
|
```bash
|
|
# 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 条
|
|
- 备份文件不自动清理,需定期手动删除过期备份 |