Skip to content

Control Server 配置

配置类:apps/control-server/app/core/config.pyControlServerConfig / load())。加载纪律见 分层构造

环境变量统一以 DN42_CONTROL_ 为前缀,唯一例外是 CAP_SECRET_KEY(变量名与部署侧约定同名,不带前缀)。


核心

环境变量TOML 键类型默认说明
DN42_CONTROL_DATABASE_URLdatabase.urlstrsqlite+aiosqlite:///<仓库根>/control.dbSQLAlchemy 异步 DSN。默认锚定在仓库根目录(避免随 cwd 漂移),适合本地与 CI。生产用 postgresql+asyncpg://user:pass@host:5432/db。Alembic 迁移读同一变量
DN42_CONTROL_DB_AUTO_MIGRATEdatabase.auto_migrateboolFalse启动时是否跑 alembic upgrade head(迁移失败即启动中止,fail-fast)。FalseBase.metadata.create_all——只建缺失的表、绝不应用 ALTER。⚠️ 生产当前置 false:库由 create_all 建起、未被 alembic 纳管,切迁移链前必须先 alembic stamp,见 数据库迁移
DN42_CONTROL_ADMIN_TOKENserver.admin_tokenstr | NoneNone管理面 Bearer token(机器与自动化凭据,与账号会话令牌并行接受)。fail-closed:为 None 时管理面整体 403,账号登录同样锁定。生产必填
DN42_CONTROL_ENROLLMENT_TOKENserver.enrollment_tokenstr | NoneNone全局 bootstrap 注册 token,与 enrollment_tokens 表中按节点签发的 token 并行接受fail-closed:为 None 时全局 token 关闭,只允许表内一次性 token 注册。本地照教程练手须显式设置
DN42_CONTROL_CORS_ORIGINSserver.cors_origins列表("http://localhost:5173", "http://127.0.0.1:5173")浏览器管理面跨源直连的白名单。未设置返回默认(放行本地 dev server);显式设为空字符串表示关闭跨源。生产填控制台与首页的真实源
DN42_CONTROL_REDIS_URLredis.urlstr | NoneNoneL5 派生缓存的 DSN(如 redis://redis:6379/1,db 1 = control-server)。缓存 desired-state(按 generation 键)、节点健康、路由聚合、ASN 名与 token 解析,写时主动失效。None 即不启用,全部读直接走 DB;不可用时同样自动回落——缓存是旁路,不影响正确性
DN42_CONTROL_KV_URLkv.urlstr | NoneNoneL4 持久小状态的 DSN(如 redis://valkey:6379/1)。存 flap 热态基准、agent 心跳、登录限速、WS 订阅注册表、门户登录事务——都是唯一副本,故指向 noeviction + 落盘的 Valkey 而非会淘汰键的 Redis。None 时回落 redis.url(行为同旧版,但热态会随缓存淘汰或容器重建丢失)。角色与判据见 数据层参考
DN42_CONTROL_PARTITIONED_TIMESERIESdatabase.partitioned_timeseriesboolfalse时序 rollup 表是否已转成按周分区。置真后按行裁剪的 DELETE 全部停掉,保留期改由分区维护循环 DROP 整个分区实现。⚠️ 必须在真的转换完之后才置真——未分区却置真等于旧行永不回收。见 时序表分区
DN42_CONTROL_AGENT_RELEASES_DIRserver.agent_releases_dirPath/data/agent-releases首方 wheel 发布库目录(agent 自更新分发源)。管理员上传构建产物到这里,agent 经鉴权通道拉取;默认落持久卷
DN42_CONTROL_RECOVERY_PUBLIC_KEYserver.recovery_public_keystr | NoneNone离线托管恢复公钥。取值既可是内联 PEM(以 -----BEGIN 开头),也可是 PEM 文件路径。节点用它封装 WG 私钥后上报,控制面只存密文、永不持有恢复私钥None 表示未启用托管——节点仍上报公钥做一致性校验,但不产生密文。给的是文件路径且文件不存在时启动期抛 FileNotFoundError

† 标记表示 token 类语义:显式设空 = 关闭。


健康判定

环境变量TOML 键类型默认说明
DN42_CONTROL_HEALTH_STALE_AFTERhealth.stale_after_secondsfloat 秒900.0在此时长内未上报的 ok 节点降为 stale
DN42_CONTROL_HEALTH_DOWN_AFTERhealth.down_after_secondsfloat 秒3600.0超过此时长完全无上报判为 down,覆盖任何已知状态

两个阈值在读取时叠加,改阈值无需改库。判定顺序见 Control Server 内部


管理员登录

环境变量TOML 键类型默认说明
DN42_CONTROL_CAP_API_ENDPOINTauth.cap_api_endpointstr | NoneNoneCap 实例地址。生产走容器网内地址(http://cap:3000)避免公网回环
DN42_CONTROL_CAP_SITE_KEYauth.cap_site_keystr | NoneNoneCap site key(公开值)
CAP_SECRET_KEYauth.cap_secret_keystr | NoneNoneCap secret key(服务端专用)
DN42_CONTROL_SESSION_TTL_SECONDSauth.session_ttl_secondsfloat 秒86400.0账号会话令牌 TTL(24h)。过期后任意接口 401,前端全局登出;改密时该用户全部存活会话立即吊销
DN42_CONTROL_BOOTSTRAP_ADMINauth.bootstrap_adminstr | NoneNoneuser:password——仅当 admin_users 表为空时创建首个管理员(幂等:已有账号则跳过,变量残留不会覆盖改过的密码)。密码以 argon2id 散列入库

人机验证 = 自托管 Cap,与 auth-server 共用同一实例同一 widget。三项 Cap 配置不齐时 CapVerifier 自身 fail-closed:账号登录一律 400,静态 admin token 不受影响。不配 bootstrap_admin 则不自动建账号(也可手动入库)。


registry 副本服务

环境变量TOML 键类型默认说明
DN42_CONTROL_REGISTRY_SERVICE_URLregistry.service_urlstr | NoneNoneregistry-server 地址(容器网内,如 http://registry-server:8200
DN42_CONTROL_REGISTRY_SERVICE_TOKENregistry.service_tokenstr | NoneNoneregistry-server API 的 Bearer token

两项任一不配 = 客户端 fail-closed:榜单名称富化回落占位符 AS<asn>/ui/registry/* 显式查询 503。同步源 token 配在 registry-server 侧,控制面不再直连官方 registry。


认证服务接入

控制面作为 auth-server 会话 JWT 的消费方

环境变量TOML 键类型默认说明
DN42_CONTROL_AUTH_SERVICE_BASE_URLauth_service.base_urlstr | NoneNoneauth-server 公开基址(如 https://auth.natlan.io,容器网内可用 http://auth-server:8100)。JWKS 端点由它推导为 <base_url>/.well-known/jwks.json,公钥缓存并按 TTL 刷新,未知 kid 强制刷新一次。fail-closed:未配置时 /autopeering/session 一律 503
DN42_CONTROL_AUTH_SERVICE_AUDIENCEauth_service.audiencestrdn42-peering会话 JWT 的 aud 校验值,须与 auth-server 的 token.audience 一致
DN42_CONTROL_AUTH_SERVICE_ISSUERauth_service.issuerstr | NoneNone会话 JWT 的 iss 校验值。不配则只锚 aud 不校验 iss;要锚死时配成 auth-server 的 token.issuer

自动对等门户(OIDC 依赖方)

控制面自己对接 auth-server 的授权码流程,门户会话落在控制库。

环境变量TOML 键类型默认说明
DN42_CONTROL_PORTAL_OIDC_ISSUERportal.oidc_issuerstrhttps://auth.natlan.io上游 issuer。discovery / JWKS / token 端点全由 <issuer>/.well-known/openid-configuration 推导,不单独配
DN42_CONTROL_PORTAL_OIDC_CLIENT_IDportal.oidc_client_idstr | NoneNone门户在 auth-server 侧注册的机密客户端 id
DN42_CONTROL_PORTAL_OIDC_CLIENT_SECRETportal.oidc_client_secretstr | NoneNone机密客户端凭据,只在服务端使用,永不下发浏览器
DN42_CONTROL_PORTAL_OIDC_REDIRECT_URIportal.oidc_redirect_uristrhttp://127.0.0.1:8000/control/v1/autopeering/auth/callback回调地址,必须与 auth-server 侧登记值逐字节一致(精确匹配)
DN42_CONTROL_PORTAL_OIDC_SCOPESportal.oidc_scopes列表("openid","profile","email","dn42")授权请求的 scope 集
DN42_CONTROL_PORTAL_FRONTEND_BASE_URLportal.frontend_base_urlstrhttp://localhost:5173门户前端基址:回调换完令牌后 302{base}/auth/complete?handoff=…。生产指 https://peering.natlan.io
DN42_CONTROL_PORTAL_SESSION_TTL_SECONDSportal.session_ttl_secondsfloat 秒10368000.0(120 天)门户自有会话 TTL,与上游 1h access token 解耦(上游令牌由服务端静默 refresh)。对等是低频慢流程,频繁重登伤体验;令牌只解锁门户自身,长时效风险可接受。上游 refresh token 为 120 天滑动窗口,与本 TTL 对齐

client_id / client_secret 未配齐时门户登录端点 fail-closed 503,控制面其余功能不受影响。


flap 打分参数

[flap]仅文件可调,无对应环境变量。默认值即代码内置值;三个评估方(会话打分、前缀榜单、告警循环)装配时从同一份配置取值,自动同参——不同参会让判定口径分叉。

TOML 键默认说明
flap.session_half_life_s1800会话级(P0)衰减半衰期(秒)。约 5min 的快照节奏下,30min 半衰期意味着连续 4 份抖动快照才过线
flap.session_threshold3.0会话级「抖动中」判定线,同时是告警开单线;消警线自动取一半做迟滞
flap.prefix_list_threshold20前缀级(P1)榜单「抖动中」线,仅作可观测标记
flap.prefix_alert_threshold100前缀级告警开单线(P2),与榜单线分层——低烈度背景抖动是 DN42 常态,按榜单线开单会告警疲劳;消警线自动取一半
flap.alert_loop_enabledtrue告警评估循环开关。与主面共库的只读副本必须关掉:同一份分数两个评估者做边沿判定会竞态开出重复告警

刻意不开放的两个结构性常量:前缀级衰减半衰期(600s)必须与全 fleet agent 的 flapfeed_scoring.HALF_LIFE_S 同参——控制面单方面改就是打分口径分叉;速率存档桶宽(60s)改了会毁历史 rollup 的语义。

算法见 flap 打分


开发与已废弃键位

环境变量TOML 键默认说明
DN42_CONTROL_SEED_BOOTSTRAP_NODEdev.seed_bootstrap_nodeFalseDB 为空时是否播种内置 demo 节点。默认关闭——生产启动即空库,节点数据由导入或 provision 流程写入
DN42_CONTROL_BOOTSTRAP_NODE_IDdev.bootstrap_node_idedge1demo 节点的 node_id,仅在上项开启时有意义
DN42_CONTROL_BOOTSTRAP_AGENT_TOKENdev.bootstrap_agent_tokenmvp-agent-token与 demo 节点关联的初始 Bearer token,便于本地联调
DN42_CONTROL_MAIL_CF_ACCOUNT_ID / _MAIL_CF_API_TOKEN / _MAIL_FROM_ADDRESS / _MAIL_FROM_NAMEmail.*已废弃。挑战验证与邮件发送整体由 auth-server 承载,控制面不再装配任何邮件发送器。键位保留只为让既有部署不致启动报错;新部署应把这些配置挪到 auth-server.toml[mail]
DN42_CONTROL_DEV_LOG_MAIL_SENDERdev.log_mail_senderFalse已废弃,同上

fail-closed 语义速查

变量未设置设为空字符串
DN42_CONTROL_ADMIN_TOKENNone → 管理面全 403(含账号登录)同左
DN42_CONTROL_ENROLLMENT_TOKENNone → 全局 token 关闭,仅表内 token 可注册同左
CAP_SECRET_KEYNone → 账号登录全 400同左
DN42_CONTROL_BOOTSTRAP_ADMINNone → 不自动建账号同左
DN42_CONTROL_REGISTRY_SERVICE_TOKENNone → registry 查询 503、名称回落占位符同左
DN42_CONTROL_RECOVERY_PUBLIC_KEYNone → 不托管同左
DN42_CONTROL_PORTAL_OIDC_CLIENT_SECRETNone → 门户登录 503同左
DN42_CONTROL_REDIS_URLNone → 不启用缓存同左
DN42_CONTROL_CORS_ORIGINS默认本地白名单空元组 → 关闭跨源

数据库 URL 与迁移

Alembic 迁移与运行时共享同一变量 DN42_CONTROL_DATABASE_URL

  • migrations/env.py_resolve_url() 优先读该环境变量,缺失时退回 alembic.inisqlalchemy.url(默认 SQLite,仅为 alembic --help 的无害占位)。
  • 迁移期把异步驱动替换为同步驱动:+aiosqlite → 去除、+asyncpg+psycopg2+asyncmy+pymysql
  • 从仓库根运行 alembic upgrade head

完整步骤见 数据库迁移


最小配置示例

toml
# /etc/dn42-control/control-server.toml(chmod 600)
[server]
admin_token = "change-me-admin"
enrollment_token = "change-me"
cors_origins = ["https://console.example.dn42"]

[database]
url = "postgresql+asyncpg://dn42:secret@postgres:5432/dn42_control"
# 全新部署可置 true 走迁移链;从 create_all 库升级来的实例需先 alembic stamp
auto_migrate = false

[redis]
url = "redis://redis:6379/0"

等价的纯环境变量形态:

dotenv
DN42_CONTROL_DATABASE_URL=postgresql+asyncpg://dn42:secret@postgres:5432/dn42_control
DN42_CONTROL_DB_AUTO_MIGRATE=0
DN42_CONTROL_ADMIN_TOKEN=change-me-admin
DN42_CONTROL_ENROLLMENT_TOKEN=change-me
DN42_CONTROL_CORS_ORIGINS=https://console.example.dn42
DN42_CONTROL_REDIS_URL=redis://redis:6379/0

生产实配见 deploy/docker/control-server.toml,部署步骤见 控制面部署