控制平面 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-challengeCNAME 目标覆盖值。
节点管理
src/cirrus/api/nodes.py 暴露声明式的 PUT /api/v1/nodes 端点:
- 接受由
NodeDefinition对象(定义于schemas.py)组成的列表,并校验 IPv4/IPv6 地址格式。 - 确保提交负载中的 ID 唯一。
- 移除未包含在本次提交中的节点。
- 保留 Celery 健康检查维护的运行元数据,如
active、health_fails、health_succs。 - 完成后广播 DNS 区域重建请求。
节点信息以 Redis 哈希存储,字段如下:
| 字段 | 描述 |
|---|---|
name | 便于运维识别的标签。 |
a / aaaa | 规范化的 IPv4/IPv6 地址(未设置时为空字符串)。 |
active | "1" 或 "0",决定是否写入 DNS 记录。 |
health_fails、health_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——返回当前用户名、认证方式(session、token或master)以及令牌 ID(若存在)。/auth/change-password——验证当前密码,更新为新哈希,并通过清理会话强制登出。
环境变量控制的特性开关(如 ENABLE_PASSWORD_AUTH)请参见第 11 章。
ACME API
src/cirrus/api/acme.py 挂载在 /domains 命名空间下:
GET /{domain}/acme——合并域名配置与 ACME 注册信息;若启用 ACME,则通过调用acme_common.py的ensure_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:
- 接受
domain和path查询参数。 - 规范化路径,确保以
/开头。 - 将 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_start、acme_done、acme_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 在请求时如何解释同一份配置。