跳到主要内容

控制平面 API 与数据模型

位于 src/cirrus/app.py 的 FastAPI 应用是配置 Cirrus CDN 的权威接口。本章介绍应用生命周期、认证模型、关键路由及支撑域名、节点与用户管理的 Redis 数据契约。

应用生命周期

src/cirrus/app.py 通过自定义 lifespan 处理器启动 FastAPI 实例:

  • 启动阶段——加载 CNAME 配置(cname/settings.py),使用 redis.asyncio 建立 Redis 连接,并启动 CnameService,该服务会派生隐藏主 DNS 服务器并订阅 cdn:cname:dirty 通知。
  • 关闭阶段——优雅停止 CNAME 服务并关闭异步 Redis 连接。

路由统一挂载在 /api/v1 下,按领域划分:

  • 认证(api/auth.py
  • 用户管理(api/users.py
  • 节点管理(api/nodes.py
  • 域名管理(api/domains.py
  • ACME 自动化(api/acme.py
  • 缓存刷新(api/purge.py
  • 服务令牌(api/tokens.py

静态化的前端资源在 API 路由之后挂载到根路径,确保 API 端点优先处理。

认证与授权

安全模型由 src/cirrus/core/security.py 实现:

  • 会话——/auth/login 端点根据存储的 Argon2 哈希验证凭据(verify_password),并以随机令牌作为键写入内存会话,同时下发名为 SESSION_COOKIE_NAME 的 Cookie。会话在每次认证请求时刷新 TTL。Cookie 默认为 HTTP-only 且 SameSite=lax
  • Bearer 令牌——带有 Authorization: Bearer <token> 的请求会同时检查两类来源:环境变量 MASTER_API_TOKEN(视为超级用户)以及存储在 Redis 的哈希化服务令牌(core/tokens.py)。
  • 访问控制——require_user 决定是否强制认证。当系统尚未创建任何用户或令牌时,会允许匿名访问以便开发初始配置。
  • 服务令牌——core/tokens.py 通过 SHA-256 存储令牌哈希,并记录后缀元数据(最后两个字符提升可识别性)、创建时间与可选创建者字段。

请求状态会捕获认证方式与令牌 ID,下游路由可据此实施更严格的策略(例如 api/tokens.py 中仅限 master 的端点)。

域名管理

src/cirrus/api/domains.py 提供域名配置的 CRUD 操作:

  • 创建(POST /api/v1/domains/{domain}——校验唯一性,将 DomainConf 负载作为 JSON 写入 cdn:dom:{domain},并把域名加入 cdn:domains;若启用 DNS-01,则初始化 ACME 元数据。随后广播 cdn:cname:dirty 触发 DNS 重建。
  • 读取(GET /api/v1/domains/{domain}——返回 JSON 配置;若不存在则返回 404,存储数据异常则返回 500
  • 更新(PUT /api/v1/domains/{domain}——覆盖配置,同时按需更新 ACME 状态(启用/禁用),并再次发布 dirty 事件。
  • 子资源——通过 PUT 对特定字段进行更新:
    • /cache-rules:切换缓存开关并更新规则定义。
    • /upstreams:替换源站列表。
    • /upstream-headers:设置转发至源站的请求头。
    • /cert:持久化手动上传的 TLS PEM 资料。
  • 删除——移除域名配置、证书与 ACME 锁,再次通知 DNS 重建。
  • CNAME 解析(GET /{domain}/cname——计算访问 FQDN,使用一致性哈希选择服务节点并返回副本元数据。需在环境中开启 CNAME 支持(CNAME_BASE_DOMAIN)。

DomainConf 模型

DomainConf 模型定义于 api/schemas.py,主要字段包括:

  • origins:按序排列的 Upstream 列表(协议、主机、端口、权重)。
  • upstream_headers:针对源站请求的键值覆盖。
  • cache_enable:是否启用缓存;默认 true
  • slice_enable:是否启用切片下载;默认 false
  • rules:缓存规则列表(TTL、方法、忽略头、按规则启用切片等)。
  • use_acme_dns01:是否启用 ACME 自动化;开启后 API 会确保存在 cdn:acme:{domain}
  • acme_cname_target:可选的 _acme-challenge CNAME 目标覆盖值。

节点管理

src/cirrus/api/nodes.py 暴露声明式的 PUT /api/v1/nodes 端点:

  • 接受由 NodeDefinition 对象(定义于 schemas.py)组成的列表,并校验 IPv4/IPv6 地址格式。
  • 确保提交负载中的 ID 唯一。
  • 移除未包含在本次提交中的节点。
  • 保留 Celery 健康检查维护的运行元数据,如 activehealth_failshealth_succs
  • 完成后广播 DNS 区域重建请求。

节点信息以 Redis 哈希存储,字段如下:

字段描述
name便于运维识别的标签。
a / aaaa规范化的 IPv4/IPv6 地址(未设置时为空字符串)。
active"1""0",决定是否写入 DNS 记录。
health_failshealth_succs用于判定状态转换的计数器。

用户管理

src/cirrus/api/users.py 提供运维账号的 CRUD:

  • GET /users——列出用户名、邮箱、状态与上次登录时间(默认 "Never"),元数据来自 cdn:user:{username} 哈希。
  • POST /users——将用户加入 cdn:users 集合,并存储密码哈希、邮箱与状态。
  • PUT /users/{username}——更新邮箱、密码(重新哈希)与状态;不存在时返回 404
  • DELETE /users/{username}——从 Redis 集合移除并删除对应哈希。

这些操作受 require_user 保护,仅认证运维可执行。

认证端点

src/cirrus/api/auth.py 包含:

  • /auth/login——验证凭据、写入会话 Cookie、更新 last_login
  • /auth/logout——移除会话 Cookie 并从内存映射删除会话。
  • /auth/me——返回当前用户名、认证方式(sessiontokenmaster)以及令牌 ID(若存在)。
  • /auth/change-password——验证当前密码,更新为新哈希,并通过清理会话强制登出。

环境变量控制的特性开关(如 ENABLE_PASSWORD_AUTH)请参见第 11 章。

ACME API

src/cirrus/api/acme.py 挂载在 /domains 命名空间下:

  • GET /{domain}/acme——合并域名配置与 ACME 注册信息;若启用 ACME,则通过调用 acme_common.pyensure_acme_registered 确保存储了 acme-dns 凭据。
  • POST /{domain}/acme——入队 Celery 任务执行证书签发/续签。利用 Redis 锁(cdn:acme:lock:{domain}cdn:acme:task:{domain})避免并发冲突。
  • DELETE /{domain}/acme——清理 ACME 状态与锁,为全新注册流程做准备。

错误会通过适当的 HTTP 状态码返回(例如 ACME DNS-01 被禁用时返回 400,并发任务返回 409)。

清缓存端点

src/cirrus/api/purge.py 提供 POST /api/v1/purge

  • 接受 domainpath 查询参数。
  • 规范化路径,确保以 / 开头。
  • 将 JSON 负载发布到配置的清缓存通道(CDN_PURGE_CHANNEL,默认为 cdn:purge)。
  • 订阅该通道的 OpenResty worker 负责执行实际的 PURGE(见第 6 章)。

服务令牌管理

src/cirrus/api/tokens.py 将令牌操作限制为 master 令牌:

  • GET /auth/tokens——列出已存储的令牌(不包含完整秘钥)。
  • POST /auth/tokens——生成带随机后缀的令牌,存储哈希,并仅向调用者返回一次明文。
  • DELETE /auth/tokens/{token_id}——移除令牌及其哈希索引。

令牌允许外部自动化在无用户会话的情况下调用控制平面。

错误处理与日志

  • FastAPI 会将未捕获异常包装为 500 响应;代码在可预见的错误状态(冲突、配置无效)下主动抛出带意义的 HTTPException
  • ACME 路由会在 uvicorn.error 上记录关键事件(如 acme_queued),而 Celery worker 则在 cirrus.acme 日志记录任务进度或失败(acme_startacme_doneacme_fail)。
  • DNS 服务启动异常会被记录并向上抛出以便快速失败。

开发与测试

  • tests/ 下的测试使用 pytest 与异步 fixture(参见 tests/helpers/)。just quicktest 配方会跳过耗时测试(ACME、DNS 健康)。
  • 启用 ENABLE_TEST_ENDPOINTS=true(如 docker-compose.yml 中所示)后,可在 /__test__/ 下访问辅助端点,主动触发 ACME 扫描或 CNAME 重建,无需等待计划任务。

控制平面 API 确立了后续章节依赖的数据契约。第 4 章将详解作用于这些状态的自动化任务;第 5 章介绍 DNS 子系统如何消费域名与节点数据;第 6 章展示 OpenResty 在请求时如何解释同一份配置。