跳转至

API

概述

BunkerWeb API 是用于以编程方式管理 BunkerWeb 实例的控制平面:列出和管理实例、重新加载/停止、处理封禁、插件、作业、配置等。它公开了一个有文档记录的 FastAPI 应用程序,具有强大的身份验证、授权和速率限制功能。

/docs(如果设置了 API_ROOT_PATH,则为 <root_path>/docs)打开交互式文档。OpenAPI 模式可在 /openapi.json 获取。

安全

API 是一个特权控制平面。不要在没有额外保护的情况下将其暴露在公共互联网上。

至少,限制源 IP (API_WHITELIST_IPS),启用身份验证(API_TOKEN 或 API 用户 + Biscuit),并考虑将其置于 BunkerWeb 之后,使用一个难以猜测的路径和额外的访问控制。

先决条件

API 服务需要访问 BunkerWeb 数据库 (DATABASE_URI)。它通常与调度器和可选的 Web UI 一起部署。推荐的设置是在前端运行 BunkerWeb 作为反向代理,并将 API 隔离在内部网络上。

请参阅快速入门指南中的快速入门向导和架构指导。

亮点

  • 实例感知:将操作性动作广播到已发现的实例。
  • 强身份验证:管理员的基本认证、Bearer 管理员覆盖或用于细粒度权限的 Biscuit ACL。
  • IP 允许列表和灵活的按路由速率限制。
  • 标准的健康/就绪信号和启动安全检查。

Compose 样板文件

使用 BunkerWeb 将 API 反向代理到 /api 下。

x-bw-env: &bw-env
  # BunkerWeb/Scheduler 的共享实例控制平面允许列表
  API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24"

services:
  bunkerweb:
    image: bunkerity/bunkerweb:testing
    ports:
      - "80:8080/tcp"
      - "443:8443/tcp"
      - "443:8443/udp"  # QUIC
    environment:
      <<: *bw-env
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-services

  bw-scheduler:
    image: bunkerity/bunkerweb-scheduler:testing
    environment:
      <<: *bw-env
      BUNKERWEB_INSTANCES: "bunkerweb"  # 匹配实例服务名称
      SERVER_NAME: "www.example.com"
      MULTISITE: "yes"
      DATABASE_URI: "mariadb+pymysql://bunkerweb:changeme@bw-db:3306/db"
      DISABLE_DEFAULT_SERVER: "yes"
      # 在 /api 上反向代理 API
      www.example.com_USE_REVERSE_PROXY: "yes"
      www.example.com_REVERSE_PROXY_URL: "/api"
      www.example.com_REVERSE_PROXY_HOST: "http://bw-api:8888"
    volumes:
      - bw-storage:/data
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-db

  bw-api:
    image: bunkerity/bunkerweb-api:testing
    environment:
      DATABASE_URI: "mariadb+pymysql://bunkerweb:changeme@bw-db:3306/db"  # 使用强密码
      API_WHITELIST_IPS: "127.0.0.0/8 10.20.30.0/24"                      # API 允许列表
      API_TOKEN: "secret"                                                 # 可选的管理员覆盖令牌
      API_ROOT_PATH: "/api"                                               # 匹配反向代理路径
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-db

  bw-db:
    image: mariadb:11
    # 避免大查询出现问题
    command: --max-allowed-packet=67108864
    environment:
      MYSQL_RANDOM_ROOT_PASSWORD: "yes"
      MYSQL_DATABASE: "db"
      MYSQL_USER: "bunkerweb"
      MYSQL_PASSWORD: "changeme"  # 使用强密码
    volumes:
      - bw-data:/var/lib/mysql
    restart: unless-stopped
    networks:
      - bw-db

volumes:
  bw-data:
  bw-storage:

networks:
  bw-universe:
    name: bw-universe
    ipam:
      driver: default
      config:
        - subnet: 10.20.30.0/24
  bw-services:
    name: bw-services
  bw-db:
    name: bw-db

与上面相同,但利用 Autoconf 服务自动发现和配置服务。API 通过在 API 容器上使用标签暴露在 /api 下。

x-api-env: &api-env
  AUTOCONF_MODE: "yes"
  DATABASE_URI: "mariadb+pymysql://bunkerweb:changeme@bw-db:3306/db"  # 使用强密码

services:
  bunkerweb:
    image: bunkerity/bunkerweb:testing
    ports:
      - "80:8080/tcp"
      - "443:8443/tcp"
      - "443:8443/udp"  # QUIC
    environment:
      AUTOCONF_MODE: "yes"
      API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24"
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-services

  bw-scheduler:
    image: bunkerity/bunkerweb-scheduler:testing
    environment:
      <<: *api-env
      BUNKERWEB_INSTANCES: ""    # 由 Autoconf 发现
      SERVER_NAME: ""            # 通过标签填充
      MULTISITE: "yes"           # Autoconf 必需
      API_WHITELIST_IP: "127.0.0.0/8 10.20.30.0/24"
    volumes:
      - bw-storage:/data
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-db

  bw-autoconf:
    image: bunkerity/bunkerweb-autoconf:testing
    depends_on:
      - bunkerweb
      - bw-docker
    environment:
      <<: *api-env
      DOCKER_HOST: "tcp://bw-docker:2375"
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-docker
      - bw-db

  bw-api:
    image: bunkerity/bunkerweb-api:testing
    environment:
      <<: *api-env
      API_WHITELIST_IPS: "127.0.0.0/8 10.20.30.0/24"
      API_TOKEN: "secret"
      API_ROOT_PATH: "/api"
    labels:
      - "bunkerweb.SERVER_NAME=www.example.com"
      - "bunkerweb.USE_REVERSE_PROXY=yes"
      - "bunkerweb.REVERSE_PROXY_URL=/api"
      - "bunkerweb.REVERSE_PROXY_HOST=http://bw-api:8888"
    restart: unless-stopped
    networks:
      - bw-universe
      - bw-db

  bw-db:
    image: mariadb:11
    command: --max-allowed-packet=67108864
    environment:
      MYSQL_RANDOM_ROOT_PASSWORD: "yes"
      MYSQL_DATABASE: "db"
      MYSQL_USER: "bunkerweb"
      MYSQL_PASSWORD: "changeme"
    volumes:
      - bw-data:/var/lib/mysql
    restart: unless-stopped
    networks:
      - bw-db

  bw-docker:
    image: tecnativa/docker-socket-proxy:nightly
    environment:
      CONTAINERS: "1"
      LOG_LEVEL: "warning"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock:ro
    restart: unless-stopped
    networks:
      - bw-docker

volumes:
  bw-data:
  bw-storage:

networks:
  bw-universe:
    name: bw-universe
    ipam:
      driver: default
      config:
        - subnet: 10.20.30.0/24
  bw-services:
    name: bw-services
  bw-db:
    name: bw-db
  bw-docker:
    name: bw-docker

反向代理路径

保持 API 路径难以猜测,并与 API 允许列表和身份验证结合使用。

如果您已经在同一服务器名称上使用模板(例如 USE_TEMPLATE)公开了另一个应用程序,请为 API 使用一个独立的主机名以避免冲突。

一体化 (All-In-One)

如果您使用一体化镜像,可以通过设置 SERVICE_API=yes 来启用 API:

docker run -d \
  --name bunkerweb-aio \
  -e SERVICE_API=yes \
  -e API_WHITELIST_IPS="127.0.0.0/8" \
  -p 80:8080/tcp -p 443:8443/tcp -p 443:8443/udp \
  bunkerity/bunkerweb-all-in-one:testing

身份验证

支持的请求身份验证方式:

  • 基本管理员认证:当凭据属于管理员 API 用户时,受保护的端点接受 Authorization: Basic <base64(username:password)>
  • 管理员 Bearer 覆盖:如果配置了 API_TOKENAuthorization: Bearer <API_TOKEN> 将授予完全访问权限。
  • Biscuit 令牌(推荐):使用基本凭据或包含 usernamepassword 的 JSON/表单体从 POST /auth 获取令牌。在后续调用中将返回的令牌用作 Authorization: Bearer <token>

示例:获取一个 Biscuit,列出实例,然后重新加载所有实例。

# 1) 使用管理员凭据获取一个 Biscuit 令牌
TOKEN=$(curl -s -X POST -u admin:changeme http://api.example.com/auth | jq -r .token)

# 2) 列出实例
curl -H "Authorization: Bearer $TOKEN" http://api.example.com/instances

# 3) 重新加载所有实例的配置(无测试)
curl -X POST -H "Authorization: Bearer $TOKEN" \
     "http://api.example.com/instances/reload?test=no"

Biscuit 事实和检查

令牌嵌入了诸如 user(<username>)client_ip(<ip>)domain(<host>) 等事实,以及从数据库权限派生的粗粒度角色 role("api_user", ["read", "write"])。管理员包含 admin(true),而非管理员则携带细粒度的事实,如 api_perm(<resource_type>, <resource_id|*>, <permission>)

授权将路由/方法映射到所需的权限;admin(true) 总是通过。当缺少细粒度事实时,守卫会回退到粗粒度角色:GET/HEAD/OPTIONS 需要 read;写入动词需要 write

密钥存储在 /var/lib/bunkerweb/.api_biscuit_private_key/var/lib/bunkerweb/.api_biscuit_public_key。您还可以通过环境变量提供 BISCUIT_PUBLIC_KEY/BISCUIT_PRIVATE_KEY;如果文件和环境变量都未设置,API 会在启动时生成一个密钥对并安全地持久化它。

权限 (ACL)

此 API 支持两个授权层:

  • 粗粒度角色:令牌携带 role("api_user", ["read"[, "write"]]) 用于没有细粒度映射的端点。Read 映射到 GET/HEAD/OPTIONS;write 映射到 POST/PUT/PATCH/DELETE。
  • 细粒度 ACL:令牌嵌入 api_perm(<resource_type>, <resource_id|*>, <permission>),并且路由声明它们需要什么。admin(true) 绕过所有检查。

支持的资源类型:instancesglobal_configservicesconfigspluginscachebansjobs

按资源类型划分的权限名称:

  • instances: instances_readinstances_updateinstances_deleteinstances_createinstances_execute
  • global_config: global_config_readglobal_config_update
  • services: service_readservice_createservice_updateservice_deleteservice_convertservice_export
  • configs: configs_readconfig_readconfig_createconfig_updateconfig_delete
  • plugins: plugin_readplugin_createplugin_delete
  • cache: cache_readcache_delete
  • bans: ban_readban_updateban_deleteban_created
  • jobs: job_readjob_run

资源 ID:对于细粒度检查,当有意义时,第二个路径段被视为 resource_id。示例:/services/{service} -> {service}/configs/{service}/... -> {service}。使用 "*"(或省略)为资源类型全局授予权限。

用户和 ACL 配置:

  • 管理员用户:设置 API_USERNAMEAPI_PASSWORD 以在启动时创建第一个管理员。要稍后轮换凭据,请设置 OVERRIDE_API_CREDS=yes(或确保管理员是使用 manual 方法创建的)。只存在一个管理员;额外的尝试会回退到非管理员创建。
  • 非管理员用户和授权:提供指向 JSON 文件的 API_ACL_BOOTSTRAP_FILE,或挂载 /var/lib/bunkerweb/api_acl_bootstrap.json。API 在启动时读取它以创建/更新用户和权限。
  • ACL 缓存文件:一个只读的摘要在启动时写入 /var/lib/bunkerweb/api_acl.json 以供内省;授权评估嵌入在 Biscuit 令牌中的数据库支持的授权。

引导 JSON 示例(两种形式都支持):

{
  "users": {
    "ci": {
      "admin": false,
      "password": "Str0ng&P@ss!",
      "permissions": {
        "services": {
          "*": { "service_read": true },
          "app-frontend": { "service_update": true, "service_delete": false }
        },
        "configs": {
          "app-frontend": { "config_read": true, "config_update": true }
        }
      }
    },
    "ops": {
      "admin": false,
      "password_hash": "$2b$13$...bcrypt-hash...",
      "permissions": {
        "instances": { "*": { "instances_execute": true } },
        "jobs": { "*": { "job_run": true } }
      }
    }
  }
}

或列表形式:

{
  "users": [
    {
      "username": "ci",
      "password": "Str0ng&P@ss!",
      "permissions": [
        { "resource_type": "services", "resource_id": "*", "permission": "service_read" },
        { "resource_type": "services", "resource_id": "app-frontend", "permission": "service_update" }
      ]
    }
  ]
}

注意:

  • 密码可以是明文(password)或 bcrypt(password_hash / password_bcrypt)。在非调试版本中,弱明文密码会被拒绝;如果缺少,会生成一个随机密码并记录警告。
  • resource_id: "*"(或 null/空)在该资源类型上全局授予权限。
  • 现有用户可以通过引导更新密码并应用额外的授权。

功能参考

API 按资源聚焦的路由器组织。使用以下部分作为功能地图;/docs 处的交互式模式详细记录了请求/响应模型。

核心和身份验证

  • GET /pingGET /health:API 服务本身的轻量级存活探针。
  • POST /auth:用基本凭据(或管理员覆盖令牌)交换 Biscuit。接受 JSON、表单或 Authorization 标头。管理员如果需要,也可以直接在受保护的路由上继续使用 HTTP 基本认证。

实例控制平面

  • GET /instances:列出注册的实例,包括创建/最后一次看到的时间戳、注册方法和元数据。
  • POST /instances:注册一个新的 API 管理的实例(主机名、可选端口、服务器名称、友好名称、方法)。
  • GET /instances/{hostname} / PATCH /instances/{hostname} / DELETE /instances/{hostname}:检查、更新可变字段或删除 API 管理的实例。
  • DELETE /instances:批量删除;跳过非 API 实例并在 skipped 中报告它们。
  • GET /instances/pingGET /instances/{hostname}/ping:对所有或单个实例进行健康检查。
  • POST /instances/reload?test=yes|noPOST /instances/{hostname}/reload:触发配置重新加载(测试模式执行空跑验证)。
  • POST /instances/stopPOST /instances/{hostname}/stop:向实例中继停止命令。

全局配置

  • GET /global_config:获取非默认设置(使用 full=true 获取完整配置,methods=true 包含来源信息)。
  • PATCH /global_config:更新或插入 API 拥有的(method="api")全局设置;验证错误会指出未知或只读的键。

服务生命周期

  • GET /services:枚举服务及其元数据,包括草稿状态和时间戳。
  • GET /services/{service}:检索服务的非默认覆盖层(full=false)或完整配置快照(full=true)。
  • POST /services:创建服务,可选择作为草稿,并设定带前缀的变量({service}_{KEY})。原子地更新 SERVER_NAME 名册。
  • PATCH /services/{service}:重命名服务、切换草稿标志并更新带前缀的变量。为安全起见,忽略对 variablesSERVER_NAME 的直接编辑。
  • DELETE /services/{service}:删除一个服务及其派生的配置键。
  • POST /services/{service}/convert?convert_to=online|draft:快速切换草稿/在线状态,而不更改其他变量。

自定义配置片段

  • GET /configs:列出服务的自定义配置片段(HTTP/服务器/流/ModSecurity/CRS 挂钩)(默认为 service=global)。with_data=true 在可打印时嵌入 UTF-8 内容。
  • POST /configsPOST /configs/upload:从 JSON 负载或上传的文件创建新片段。接受的类型包括 httpserver_httpdefault_server_httpmodsecmodsec_crsstreamserver_stream 和 CRS 插件挂钩。名称必须匹配 ^[\w_-]{1,64}$
  • GET /configs/{service}/{type}/{name}:检索带有可选内容的片段(with_data=true)。
  • PATCH /configs/{service}/{type}/{name}PATCH .../upload:更新或移动 API 管理的片段;模板或文件管理的条目保持只读。
  • DELETE /configsDELETE /configs/{service}/{type}/{name}:修剪 API 管理的片段,同时保留模板管理的片段,并为被忽略的条目返回一个 skipped 列表。

封禁编排

  • GET /bans:聚合所有实例报告的活动封禁。
  • POST /bansPOST /bans/ban:应用一个或多个封禁。负载可以是 JSON 对象、数组或字符串化的 JSON。service 是可选的;省略时封禁是全局的。
  • POST /bans/unbanDELETE /bans:使用同样灵活的负载全局或按服务删除封禁。

插件管理

  • GET /plugins?type=all|external|ui|pro:列出带有元数据的插件;with_data=true 在可用时包含打包的字节。
  • POST /plugins/upload:从 .zip.tar.gz.tar.xz 存档安装 UI 插件。存档可以捆绑多个插件,只要每个插件都包含一个 plugin.json
  • DELETE /plugins/{id}:按 ID (^[\w.-]{4,64}$) 删除 UI 插件。

作业缓存和执行

  • GET /cache:列出由调度器作业产生的缓存工件,按服务、插件 ID 或作业名称过滤。with_data=true 包含可打印的文件内容。
  • GET /cache/{service}/{plugin}/{job}/{file}:获取特定的缓存文件(download=true 以附件形式流式传输)。
  • DELETE /cacheDELETE /cache/{service}/{plugin}/{job}/{file}:删除缓存文件并通知调度器受影响的插件。
  • GET /jobs:检查已知的作业、它们的调度元数据和缓存摘要。
  • POST /jobs/run:通过将关联的插件标记为已更改来请求作业执行。

操作说明

  • 写入端点持久化到共享数据库;实例通过调度器同步或在 /instances/reload 后获取更改。
  • 错误被规范化为 { "status": "error", "message": "..." },并带有适当的 HTTP 状态码(422 验证,404 未找到,403 ACL,5xx 上游故障)。

速率限制

客户端的速率限制由 SlowAPI 处理。通过环境变量或 /etc/bunkerweb/api.yml 启用/禁用它并调整限制。

  • API_RATE_LIMIT_ENABLED (默认: yes)
  • 默认限制: API_RATE_LIMIT_TIMESAPI_RATE_LIMIT_SECONDS (例如 10060)
  • API_RATE_LIMIT_RULES: 内联 JSON/CSV,或指向包含按路由规则的 YAML/JSON 文件的路径
  • 存储后端: 内存或当 USE_REDIS=yes 且提供了 REDIS_* 变量时使用 Redis/Valkey (支持 Sentinel)
  • 标头: API_RATE_LIMIT_HEADERS_ENABLED (默认: yes)

示例 YAML (挂载在 /etc/bunkerweb/api.yml):

yaml API_RATE_LIMIT_ENABLED: yes API_RATE_LIMIT_DEFAULTS: ["200/minute"] API_RATE_LIMIT_RULES: - path: "/auth" methods: "POST" times: 10 seconds: 60 - path: "/instances*" methods: "GET|POST" times: 100 seconds: 60

配置

您可以通过环境变量、Docker secrets 以及可选的 /etc/bunkerweb/api.yml/etc/bunkerweb/api.env 文件来配置 API。关键设置:

  • 文档和模式: API_DOCS_URL, API_REDOC_URL, API_OPENAPI_URL, API_ROOT_PATH
  • 认证基础: API_TOKEN (管理员覆盖 Bearer), API_USERNAME/API_PASSWORD (创建/更新管理员), OVERRIDE_API_CREDS
  • ACL 和用户: API_ACL_BOOTSTRAP_FILE (JSON 路径)。
  • Biscuit 策略: API_BISCUIT_TTL_SECONDS (0/关闭禁用 TTL), CHECK_PRIVATE_IP (将令牌绑定到客户端 IP,除非是私有 IP)。
  • IP 允许列表: API_WHITELIST_ENABLED, API_WHITELIST_IPS
  • 速率限制 (核心): API_RATE_LIMIT_ENABLED, API_RATE_LIMIT_TIMES, API_RATE_LIMIT_SECONDS, API_RATE_LIMIT_HEADERS_ENABLED
  • 速率限制 (高级): API_RATE_LIMIT_AUTH_TIMES, API_RATE_LIMIT_AUTH_SECONDS, API_RATE_LIMIT_RULES, API_RATE_LIMIT_DEFAULTS, API_RATE_LIMIT_APPLICATION_LIMITS, API_RATE_LIMIT_STRATEGY, API_RATE_LIMIT_KEY, API_RATE_LIMIT_EXEMPT_IPS, API_RATE_LIMIT_STORAGE_OPTIONS
  • 速率限制存储: 内存或当 USE_REDIS=yes 且设置了 Redis 相关变量时使用 Redis/Valkey,如 REDIS_HOST, REDIS_PORT, REDIS_PASSWORD, REDIS_DATABASE, REDIS_SSL 或 Sentinel 变量。请参阅 docs/features.md 中的 Redis 设置表。
  • 网络/TLS: API_LISTEN_ADDR, API_LISTEN_PORT, API_FORWARDED_ALLOW_IPS, API_SSL_ENABLED, API_SSL_CERTFILE, API_SSL_KEYFILE, API_SSL_CA_CERTS

配置加载方式

优先级从高到低:

  • 环境变量 (例如容器 environment: 或导出的 shell 变量)
  • /run/secrets 下的 Secrets 文件 (Docker/K8s secrets; 文件名匹配变量名)
  • /etc/bunkerweb/api.yml 处的 YAML 文件
  • /etc/bunkerweb/api.env 处的 Env 文件 (key=value 行)
  • 内置默认值

注意:

  • YAML 支持使用 <file:relative/path> 内联 secret 文件;路径相对于 /run/secrets 解析。
  • 将文档 URL 设置为 off/disabled/none 以禁用端点 (例如 API_DOCS_URL=off)。
  • 如果 API_SSL_ENABLED=yes,您还必须设置 API_SSL_CERTFILEAPI_SSL_KEYFILE
  • 如果启用了 Redis (USE_REDIS=yes),请提供 Redis 详细信息;请参阅 docs/features.md 中的 Redis 部分。

身份验证和用户

  • 管理员引导:设置 API_USERNAMEAPI_PASSWORD 以创建第一个管理员。要稍后重新应用,请设置 OVERRIDE_API_CREDS=yes
  • 非管理员和权限:提供 API_ACL_BOOTSTRAP_FILE 并指定一个 JSON 路径(或挂载到 /var/lib/bunkerweb/api_acl_bootstrap.json)。该文件可以列出用户和细粒度的授权。
  • Biscuit 密钥:可以设置 BISCUIT_PUBLIC_KEY/BISCUIT_PRIVATE_KEY,或在 /var/lib/bunkerweb/.api_biscuit_public_key/var/lib/bunkerweb/.api_biscuit_private_key 处挂载文件。如果均未提供,API 将在启动时生成并持久化一个密钥对。

TLS 和网络

  • 绑定地址/端口:API_LISTEN_ADDR (默认 0.0.0.0),API_LISTEN_PORT (默认 8888)。
  • 反向代理:将 API_FORWARDED_ALLOW_IPS 设置为代理 IP,以便 Gunicorn 信任 X-Forwarded-* 标头。
  • API 中的 TLS 终止:API_SSL_ENABLED=yes 加上 API_SSL_CERTFILEAPI_SSL_KEYFILE;可选的 API_SSL_CA_CERTS

速率限制快速配方

  • 全局禁用:API_RATE_LIMIT_ENABLED=no
  • 设置一个简单的全局限制:API_RATE_LIMIT_TIMES=100API_RATE_LIMIT_SECONDS=60
  • 按路由规则:将 API_RATE_LIMIT_RULES 设置为 JSON/YAML 文件路径或在 /etc/bunkerweb/api.yml 中内联 YAML。

启动安全

如果未配置身份验证路径(没有 Biscuit 密钥,没有管理员用户,也没有 API_TOKEN),API 将退出。请确保在启动前至少设置一种方法。

启动安全:如果没有任何身份验证路径可用(没有 Biscuit 密钥、没有管理员 API 用户、也没有 API_TOKEN),API 将退出。确保至少配置了一种。

根路径和代理

如果您将 API 部署在 BunkerWeb 后面的子路径上,请将 API_ROOT_PATH 设置为该路径,以便 /docs 和相对路由在代理时能正常工作。

操作

  • 健康状况:当服务正常时,GET /health 返回 {"status":"ok"}
  • Linux 服务:打包了一个名为 bunkerweb-api.servicesystemd 单元。通过 /etc/bunkerweb/api.env 进行自定义,并使用 systemctl 进行管理。
  • 启动安全:当没有可用的身份验证路径时(没有 Biscuit 密钥、没有管理员用户、没有 API_TOKEN),API 会快速失败。错误会写入 /var/tmp/bunkerweb/api.error