DefGuard 2.1.1 API 使用详解

DefGuard 2.1.1 Core REST API 全实测:会话 Cookie 认证、系统信息、用户/组/设备/网络/网关 CRUD、WireGuard 配置获取、OIDC 发现,含 curl 请求与真实 JSON 响应、分页排序约定与错误码。

DefGuardDefGuardSSO网络双因素认证2026/10/25

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 协议与版本

3.2 认证方式

DefGuard API 支持两种认证:

方式头/Cookie适用版本获取方式
会话 CookieCookie: defguard_session=<值>开源 + 企业POST /api/v1/auth 返回
Bearer TokenAuthorization: Bearer <token>仅企业版POST /api/v1/user/{username}/api_token

⚠️ 注意:API Token(Bearer 认证)是企业版功能,开源核心调用会返回 403 Enterprise features are disabled。开源部署必须用会话 Cookie。

3.3 无需认证的端点

  • GET /api/v1/health:健康检查,返回 alive
  • GET /.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"}

字段含义:

字段含义
versionCore 版本号
network_present是否已配置至少一个 VPN 网络
external_openid_enabled是否启用了外部 OIDC 登录
ldap_infoLDAP/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_orderasc 或 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位置名
addressGateway 在 VPN 内的地址(CIDR)
portWireGuard 监听端口
pubkeyGateway 的 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。

defguard_session 的 Domain=localhost 会导致 curl 的 cookie jar 不保存(curl 对 localhost 域名的 cookie 有特殊处理)。解决方法是用 -D - 抓取响应头后手动提取,或直接用 -H "Cookie: defguard_session=<值>" 传入。

十三、参考链接