DefGuard 2.1.1 API 使用详解
DefGuard 2.1.1 Core REST API 全实测:会话 Cookie 认证、系统信息、用户/组/设备/网络/网关 CRUD、WireGuard 配置获取、OIDC 发现,含 curl 请求与真实 JSON 响应、分页排序约定与错误码。
DefGuard 2.1.1 API 使用详解
目标:通过 curl 实测,讲清 DefGuard 2.1.1 Core REST API 的认证方式、主要端点的请求与响应、分页约定与错误码,覆盖认证、用户、组、设备、网络、网关、统计与 OIDC 发现八大域。所有命令与回显均在已部署的 DefGuard 2.1.1 实例上实测,取材日期 2026-10-25。部署见《DefGuard 2.1.1 Docker Compose 部署指南》。
一、环境信息
- Core 版本:
defguard 2.1.1+bbc29a1 - API 基础路径:
http://<core-host>:8000/api/v1 - 认证方式:会话 Cookie(开源版)或 Bearer Token(企业版)
- 测试账号:
admin/Q1w2e3r4!(管理台首启向导创建的管理员)
二、前置条件
- 已部署 DefGuard 并完成首启向导(至少存在一个管理员);
- 能访问 Core 的 HTTP 端口(默认 8000);
- 安装
curl与jq(可选,用于格式化 JSON)。
三、API 总览
3.1 协议与版本
- 协议:HTTPS/HTTP + JSON
- 基础路径:
/api/v1 - 文档:https://docs.defguard.net/for-developers/rest-api
3.2 认证方式
DefGuard API 支持两种认证:
| 方式 | 头/Cookie | 适用版本 | 获取方式 |
|---|---|---|---|
| 会话 Cookie | Cookie: defguard_session=<值> | 开源 + 企业 | POST /api/v1/auth 返回 |
| Bearer Token | Authorization: Bearer <token> | 仅企业版 | POST /api/v1/user/{username}/api_token |
⚠️ 注意:API Token(Bearer 认证)是企业版功能,开源核心调用会返回
403 Enterprise features are disabled。开源部署必须用会话 Cookie。
3.3 无需认证的端点
GET /api/v1/health:健康检查,返回aliveGET /.well-known/openid-configuration:OIDC 发现文档
四、认证与首次调用
4.1 登录获取会话
curl -s -X POST "http://127.0.0.1:8000/api/v1/auth" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"Q1w2e3r4!"}' \
-D - -o /tmp/auth.json
实测响应头(节选):
HTTP/1.1 200 OK
set-cookie: defguard_session=L8Qzw5aWwDZFozu7Lbvux6v9; HttpOnly; SameSite=Lax; Path=/; Domain=localhost; Max-Age=604800
content-type: application/json
响应体:
{"url":null,"user":{"authorized_apps":[],"devices":[],"email":"admin@example.com","email_mfa_enabled":false,"enrolled":true,"first_name":"Admin","groups":["admin"],"has_non_mfa_location_access":true,"has_non_posture_location_access":true,"id":1,"is_active":true,"is_admin":true,"last_name":"User","mfa_enabled":false,"mfa_method":"None","name":"Admin User","password_management_disabled":false,"phone":null,"totp_enabled":false,"username":"admin"}}
💡 说明:
set-cookie中的defguard_session就是后续请求的认证凭证。Max-Age=604800表示会话有效期 7 天。
4.2 提取会话并调用受保护接口
SESSION=$(curl -s -X POST "http://127.0.0.1:8000/api/v1/auth" \
-H "Content-Type: application/json" \
-d '{"username":"admin","password":"Q1w2e3r4!"}' \
-D - -o /dev/null 2>&1 | grep -i set-cookie | grep -oP 'defguard_session=\K[^;]+')
curl -s "http://127.0.0.1:8000/api/v1/me" \
-H "Cookie: defguard_session=$SESSION"
实测回显:
{"authorized_apps":[],"devices":[],"email":"admin@example.com","email_mfa_enabled":false,"enrolled":true,"first_name":"Admin","groups":["admin"],"has_non_mfa_location_access":true,"has_non_posture_location_access":true,"id":1,"is_active":true,"is_admin":true,"last_name":"User","mfa_enabled":false,"mfa_method":"None","name":"Admin User","password_management_disabled":false,"phone":null,"totp_enabled":false,"username":"admin"}
4.3 未认证时的响应
curl -s "http://127.0.0.1:8000/api/v1/me" -w "\nHTTP:%{http_code}\n"
实测回显:
{"msg":"Session is missing or invalid."}
HTTP:401
五、系统信息域
5.1 GET /api/v1/info — 实例信息
curl -s "http://127.0.0.1:8000/api/v1/info" -H "Cookie: defguard_session=$SESSION"
实测回显:
{"external_openid_enabled":false,"ldap_info":{"ad":false,"enabled":false},"network_present":true,"smtp_enabled":false,"version":"2.1.1"}
字段含义:
| 字段 | 含义 |
|---|---|
version | Core 版本号 |
network_present | 是否已配置至少一个 VPN 网络 |
external_openid_enabled | 是否启用了外部 OIDC 登录 |
ldap_info | LDAP/AD 同步状态 |
smtp_enabled | 是否配置了 SMTP(影响邮件 MFA 与密码重置) |
5.2 GET /api/v1/health — 健康检查(无需认证)
curl -s "http://127.0.0.1:8000/api/v1/health" -w "\nHTTP:%{http_code}\n"
实测回显:
alive
HTTP:200
5.3 GET /api/v1/enterprise_info — 企业许可信息
curl -s "http://127.0.0.1:8000/api/v1/enterprise_info" -H "Cookie: defguard_session=$SESSION"
实测回显(开源版无许可):
{"license_info":null}
六、用户域
6.1 GET /api/v1/user — 列出用户(分页)
curl -s "http://127.0.0.1:8000/api/v1/user?page=1&sort_by=name&sort_order=asc" \
-H "Cookie: defguard_session=$SESSION"
实测回显:
{"data":[{"authorized_apps":[],"devices":[],"email":"admin@example.com","email_mfa_enabled":false,"enrolled":true,"first_name":"Admin","groups":["admin"],"has_non_mfa_location_access":true,"has_non_posture_location_access":true,"id":1,"is_active":true,"is_admin":true,"last_name":"User","mfa_enabled":false,"mfa_method":"None","name":"Admin User","password_management_disabled":false,"phone":null,"totp_enabled":false,"username":"admin"}],"pagination":{"current_page":1,"next_page":null,"page_size":50,"total_items":1,"total_pages":1}}
分页参数:
| 参数 | 含义 |
|---|---|
page | 页码,从 1 开始 |
page_size | 每页条数(默认 50) |
sort_by | 排序字段,如 name |
sort_order | asc 或 desc |
6.2 POST /api/v1/user — 创建用户
curl -s -X POST "http://127.0.0.1:8000/api/v1/user" \
-H "Cookie: defguard_session=$SESSION" \
-H "Content-Type: application/json" \
-d '{"username":"testuser","first_name":"Test","last_name":"User","email":"test@example.com","password":"Q1w2e3r4!","groups":[]}' \
-w "\nHTTP:%{http_code}\n"
实测回显:
{"authorized_apps":[],"devices":[],"email":"test@example.com","email_mfa_enabled":false,"enrolled":true,"first_name":"Test","groups":[],"has_non_mfa_location_access":true,"has_non_posture_location_access":true,"id":2,"is_active":true,"is_admin":false,"last_name":"User","mfa_enabled":false,"mfa_method":"None","name":"Test User","password_management_disabled":false,"phone":null,"totp_enabled":false,"username":"testuser"}
HTTP:201
请求体字段:
| 字段 | 必填 | 含义 |
|---|---|---|
username | 是 | 用户名(唯一) |
first_name | 是 | 名 |
last_name | 是 | 姓 |
email | 是 | 邮箱 |
password | 是 | 初始密码 |
groups | 否 | 加入的组名数组 |
6.3 GET /api/v1/user/{username} — 获取用户详情
注意:路径参数是用户名,不是数字 ID。
curl -s "http://127.0.0.1:8000/api/v1/user/testuser" \
-H "Cookie: defguard_session=$SESSION"
实测回显:
{"biometric_enabled_devices":[],"security_keys":[],"user":{"authorized_apps":[],"devices":[],"email":"test@example.com","email_mfa_enabled":false,"enrolled":true,"first_name":"Test","groups":[],"has_non_mfa_location_access":true,"has_non_posture_location_access":true,"id":2,"is_active":true,"is_admin":false,"last_name":"User","mfa_enabled":false,"mfa_method":"None","name":"Test User","password_management_disabled":false,"phone":null,"totp_enabled":false,"username":"testuser"}}
若用数字 ID 调用会返回 404:
{"msg":"user 2 not found"}
6.4 DELETE /api/v1/user/{username} — 删除用户
curl -s -X DELETE "http://127.0.0.1:8000/api/v1/user/testuser" \
-H "Cookie: defguard_session=$SESSION" \
-w "\nHTTP:%{http_code}\n"
实测回显:
null
HTTP:200
七、组域
7.1 GET /api/v1/group-info — 列出组及详情
curl -s "http://127.0.0.1:8000/api/v1/group-info" \
-H "Cookie: defguard_session=$SESSION"
实测回显:
[{"id":1,"is_admin":true,"members":["admin"],"name":"admin","vpn_locations":[]}]
7.2 GET /api/v1/group — 列出组名(分页)
curl -s "http://127.0.0.1:8000/api/v1/group" \
-H "Cookie: defguard_session=$SESSION"
实测回显:
{"data":["admin"],"pagination":{"current_page":1,"next_page":null,"page_size":50,"total_items":1,"total_pages":1}}
7.3 POST /api/v1/group — 创建组
curl -s -X POST "http://127.0.0.1:8000/api/v1/group" \
-H "Cookie: defguard_session=$SESSION" \
-H "Content-Type: application/json" \
-d '{"name":"developers","members":[],"is_admin":false}' \
-w "\nHTTP:%{http_code}\n"
请求体必须包含 name、members(数组成员用户名)、is_admin(布尔)三个字段。缺少任一字段返回 422:
Failed to deserialize the JSON body into the target type: missing field `is_admin` at line 1 column 44
八、设备域
8.1 POST /api/v1/device/{username} — 为用户添加设备
路径参数是用户名。设备会被加到该用户可访问的所有位置。
PUBKEY="ZZjfYdR0Kop/WrhgO8dIkyJoUaVnzYmPYrq3GVT0wgg="
curl -s -X POST "http://127.0.0.1:8000/api/v1/device/testuser" \
-H "Cookie: defguard_session=$SESSION" \
-H "Content-Type: application/json" \
-d "{\"name\":\"laptop\",\"wireguard_pubkey\":\"$PUBKEY\"}"
实测回显(成功时返回设备与各位置的 WireGuard 配置):
{"configs":[{"network_id":1,"network_name":"Gateway","config":"[Interface]\n...","address":["10.10.0.2"],"endpoint":"localhost:51820","allowed_ips":["10.10.0.0/24"],"pubkey":"jTuESOQHFTNz4YYRf9dST9UdmMUuUs5IhBCHAodUfks=","keepalive_interval":25,"location_mfa_mode":"disabled","service_location_mode":"disabled","posture_check_required":false}],"device":{"configured":true,"created":"2026-10-25T12:11:04.425403","description":null,"device_type":"user","id":1,"name":"laptop","user_id":2,"wireguard_pubkey":"ZZjfYdR0Kop/WrhgO8dIkyJoUaVnzYmPYrq3GVT0wgg="}}
若公钥已存在,返回 400:
{"msg":"Failed to add device laptop, identical pubkey (ZZjfYdR0Kop/WrhgO8dIkyJoUaVnzYmPYrq3GVT0wgg=) already exists"}
8.2 GET /api/v1/device — 列出所有设备
curl -s "http://127.0.0.1:8000/api/v1/device" \
-H "Cookie: defguard_session=$SESSION"
实测回显:
[{"configured":true,"created":"2026-10-25T12:11:04.425403","description":null,"device_type":"user","id":1,"name":"laptop","user_id":2,"wireguard_pubkey":"ZZjfYdR0Kop/WrhgO8dIkyJoUaVnzYmPYrq3GVT0wgg="}]
8.3 GET /api/v1/device/{id}/config — 获取设备 WireGuard 配置
curl -s "http://127.0.0.1:8000/api/v1/device/1/config" \
-H "Cookie: defguard_session=$SESSION"
实测回显:
[{"config":"[Interface]\nPrivateKey = YOUR_PRIVATE_KEY\nAddress = 10.10.0.2\nDNS = 8.8.8.8\n\n[Peer]\nPublicKey = jTuESOQHFTNz4YYRf9dST9UdmMUuUs5IhBCHAodUfks=\nAllowedIPs = 10.10.0.0/24\nEndpoint = localhost:51820\nPersistentKeepalive = 25","location_mfa_mode":"disabled","network_id":1,"network_name":"Gateway","posture_check_required":false}]
💡 说明:配置中的
YOUR_PRIVATE_KEY是占位符,需替换为该设备公钥对应的私钥(由客户端生成,Core 不保存私钥)。
九、网络与网关域
9.1 GET /api/v1/network — 列出 VPN 网络(位置)
curl -s "http://127.0.0.1:8000/api/v1/network" \
-H "Cookie: defguard_session=$SESSION"
实测回显(节选):
[{"acl_default_allow":false,"acl_enabled":false,"address":["10.10.0.1/24"],"allow_all_groups":true,"allowed_groups":[],"allowed_ips":["10.10.0.0/24"],"connected_at":"2026-10-25T12:05:50.276382","dns":"8.8.8.8","endpoint":"localhost","gateways":[{"address":"gateway","certificate_expiry":"2031-10-24T12:03:00","connected":true,"id":1,"name":"Gateway","port":50066,"version":"2.1.0+d3a2e9a"}],"id":1,"keepalive_interval":25,"location_mfa_mode":"disabled","mtu":1420,"name":"Gateway","port":51820,"pubkey":"jTuESOQHFTNz4YYRf9dST9UdmMUuUs5IhBCHAodUfks=","service_location_mode":"disabled"}]
关键字段:
| 字段 | 含义 |
|---|---|
name | 位置名 |
address | Gateway 在 VPN 内的地址(CIDR) |
port | WireGuard 监听端口 |
pubkey | Gateway 的 WireGuard 公钥 |
endpoint | 客户端连接的公网地址 |
allowed_ips | 该位置下发给客户端的路由网段 |
gateways[].connected | 该网关是否在线 |
9.2 GET /api/v1/gateway — 列出所有网关
curl -s "http://127.0.0.1:8000/api/v1/gateway" \
-H "Cookie: defguard_session=$SESSION"
实测回显:
[{"address":"gateway","certificate_expiry":"2031-10-24T12:03:00","connected":true,"connected_at":"2026-10-25T12:05:50.278610","disconnected_at":null,"enabled":true,"id":1,"location_id":1,"location_name":"Gateway","modified_at":"2026-10-25T12:03:00.951984","modified_by":"Automatic setup","name":"Gateway","port":50066,"version":"2.1.0+d3a2e9a"}]
9.3 GET /api/v1/proxy — 列出 Edge 实例
curl -s "http://127.0.0.1:8000/api/v1/proxy" \
-H "Cookie: defguard_session=$SESSION"
实测回显:
[{"address":"edge","certificate_expiry":"2031-10-24T12:03:00","connected_at":"2026-10-25T12:05:50.269694","disconnected_at":null,"enabled":true,"id":1,"modified_at":"2026-10-25T12:03:00.922855","modified_by":"Automatic setup","name":"Edge","port":50051,"version":"2.1.0+34d4a9f"}]
十、OIDC 发现(无需认证)
DefGuard 自身是一个 OIDC Provider,发现文档公开可访问:
curl -s "http://127.0.0.1:8000/.well-known/openid-configuration"
实测回显:
{"authorization_endpoint":"http://localhost:8000/api/v1/oauth/authorize","claims_supported":["iss","sub","aud","exp","iat","name","given_name","family_name","email","phone_number","groups"],"grant_types_supported":["authorization_code","refresh_token"],"id_token_signing_alg_values_supported":["HS256","RS256"],"issuer":"http://localhost:8000/","jwks_uri":"http://localhost:8000/api/v1/oauth/discovery/keys","response_types_supported":["code"],"scopes_supported":["openid","profile","email","phone","groups"],"subject_types_supported":["public"],"token_endpoint":"http://localhost:8000/api/v1/oauth/token","userinfo_endpoint":"http://localhost:8000/api/v1/oauth/userinfo"}
关键端点:
| 端点 | 用途 |
|---|---|
authorization_endpoint | 授权码获取 |
token_endpoint | 用授权码换令牌 |
userinfo_endpoint | 获取用户信息 |
jwks_uri | 令牌签名公钥集 |
十一、分页、过滤与排序通用约定
- 列表接口统一返回
{"data":[...],"pagination":{...}}结构; pagination含current_page、page_size、total_items、total_pages、next_page(下一页页码或null);- 排序通过
sort_by=<字段>与sort_order=asc|desc; - 错误响应统一为
{"msg":"<错误信息>","code":"<机器可读码>"},code可空。
十二、常见问题
12.1 用数字 ID 访问用户返回 404
用户相关接口(/api/v1/user/{username}、/api/v1/device/{username})的路径参数是用户名而非数字 ID。若传数字 ID,返回 {"msg":"user N not found"}。
12.2 创建组返回 422
POST /api/v1/group 的请求体必须同时包含 name、members、is_admin 三个字段,缺一会返回反序列化错误。
12.3 创建设备返回 400 公钥已存在
WireGuard 公钥必须全局唯一。若该公钥已被其他设备使用,返回 400 并提示 identical pubkey ... already exists。
12.4 创建 API Token 返回 403
{"msg":"Enterprise features are disabled"}
API Token(Bearer 认证)是企业版功能,开源核心不支持。开源部署请使用会话 Cookie。
12.5 会话 Cookie 无法用 curl -c 保存
defguard_session 的 Domain=localhost 会导致 curl 的 cookie jar 不保存(curl 对 localhost 域名的 cookie 有特殊处理)。解决方法是用 -D - 抓取响应头后手动提取,或直接用 -H "Cookie: defguard_session=<值>" 传入。
十三、参考链接
- REST API 文档:https://docs.defguard.net/for-developers/rest-api
- OIDC Provider 文档:https://docs.defguard.net/features/openid-connect
- 官方仓库:https://github.com/DefGuard/defguard