Skip to content

首版配置契约

配置由冻结的标准库数据类表达。Python 库不自动从文件、环境变量或用户目录加载应用配置,也不修改全局日志设置;CLI 和服务入口显式加载并转换成这些对象。RTR 的默认 TLS 上下文遵循 Python/OpenSSL 的信任路径与诊断环境规则,具体例外见下文。以下值是未发布首版的工程初值,实施基准可以调整数值,但必须同步文档和验收;它们不是已测得的容量承诺。

配置对象

类型 字段及默认行为
ClientConfig groups 为 tuple,允许为空以便运行中加入来源;limits 默认 Limits;persistence 默认 None;required 默认仅 VRP;startup_timeout=30 秒;cleanup_timeout=5 秒(清理超时报告阈值,不是强制退出时限);clock_tolerance=5 秒
SourceGroupConfig id、priority、sources;failback_delay=30 秒;同一配置的来源 ID 和组 ID 分别唯一
RtrSourceConfig id、host、port、transport、trust_profile_id、server_name、ca_file、client_cert_file、client_key_file、可选 ssl_context/local_address/ssh、min_version=1、max_version=1、connect_timeout=10 秒、query_timeout=120 秒、check_order=True
SshConfig username、known_hosts、client_keys=()、password_provider=None、passphrase_provider=None;内置 SSH 的显式认证与信任配置
JsonFileSourceConfig id、path、format=auto、reader_profile_id=None、freshness、poll_interval=30 秒
HttpJsonSourceConfig id、url、format=auto、reader_profile_id=None、freshness、poll_interval=3600 秒、request_timeout=1200 秒、显式认证/TLS 配置
FreshnessPolicy max_age: 正秒数或 None;valid_until: UTC 时间或 None;至少指定一项,含两项时取更早约束
PersistenceConfig backend=sqlite 或 duckdb、显式 path、mode=owner、flush_timeout=600 秒;共享读取另用 SharedReaderConfig
ServiceConfig host=127.0.0.1、port=8323、workers=1、显式认证策略、请求和事件限制
SharedReaderConfig path、poll_interval=1 秒、limits、clock_tolerance=5 秒;固定 SQLite,无来源列表
Endpoint 规范化传输种类、host、port、可选源地址与 TLS server_name;恢复身份另包含信任配置标识

TCP 默认端口 323,TLS 默认端口 324,SSH 默认端口 22。local_address 是可选的本地源 IP,不接受接口名或 DNS 名。TLS 的连接地址 host 与证书身份 server_name 分开:host 可以为 IP;server_name 必须为缓存 DNS 名,host 为 DNS 名时可省略并使用该名称,host 为 IP 时必须显式提供。TLS 不使用 IP-ID 或 Common Name 回退认证服务器,必须验证可信证书链及 SAN dNSName。缓存证书按固定规范 SHOULD NOT 使用通配符。

内置 TLS 必须提供 client_cert_file;client_key_file 可省略,表示私钥包含在同一 PEM 文件内。ca_file 可指定运营者 CA,省略时调用 ssl.create_default_context() 使用该 Python/OpenSSL 安装的默认信任库;它不保证等同于操作系统或浏览器的全部信任库。默认 CA 文件和目录可能受 SSL_CERT_FILE、SSL_CERT_DIR 影响,可用 ssl.get_default_verify_paths() 检查本机路径。显式 ca_file 时只加载该文件的信任锚,不额外加载默认 CA 路径;注入 ssl_context 时完全使用宿主已经配置的信任锚。三者都要求可信链和服务器 DNS 身份校验。证书必须含 SAN iPAddress,宿主负责提供覆盖实际连接源 IP 的合规证书;缓存负责核对该 IP。证书及私钥只在进入会话/Client 上下文后的准备阶段读取,阻塞读取在受管理线程中完成,构造配置不读取文件或启动网络。内置传输在这个准备阶段、任何网络开始前检查证书是否已配置;注入 TransportFactory 或已认证 ConnectedTransport 的宿主自行履行相同的认证义务,无需伪造本地证书路径。内置文件路径不交互询问加密私钥密码;需要解密时由宿主安全加载 SSLContext 后注入。

上述默认上下文也保留 Python ssl.create_default_context 的诊断行为:宿主设置 SSLKEYLOGFILE 时,支持该功能的 Python 会将 TLS 会话密钥写入指定文件,包括显式 ca_file 的情况。这是宿主控制的 TLS 诊断输出,不进入库日志、快照或录制元数据;需要隔离该环境行为时由宿主直接构造并配置 SSLContext。依据见 Python 3.11 上下文创建与默认验证路径。HTTP JSON/远程 SDK 默认使用 certifi 和 trust_env=False,不继承 RTR 的默认信任路径规则。

ssl_context 与 ca_file/client_cert_file/client_key_file 互斥。注入者负责加载合规客户端证书与私钥,并配置 CERT_REQUIRED、check_hostname=True、hostname_checks_common_name=False;本库检查这些公开设置,不修改宿主 context,也不通过 Python 私有 API 猜测是否已加载本端证书。无客户端证书的 context 无法完成要求双向认证的缓存握手。投入使用后不得修改 context。TLS 必须设置非敏感 trust_profile_id;信任条件变化时改变该 ID,它不是证书或凭据。SSLContext 对象不能被完整可靠地序列化或哈希为恢复身份。Endpoint 只含连接地址、源地址和 DNS 身份,不含 context、证书内容或私钥。具体握手与关闭行为见 RTR TLS 契约。

自定义 JsonReader 的来源必须提供非空、非敏感 reader_profile_id,作为解析规则的恢复身份;解析语义改变须更换此 ID。内置 reader 的身份由固定格式版本确定。format/reader_profile_id 相同不代表不同 reader 实例可在运行中无缝替换,实例替换仍按来源替换处理。SSLContext、认证和适配器实例投入使用后不得由宿主就地修改;以新实例及更新后的信任/解析标识显式提交。

实验性 RTR v2 必须显式启用 max_version=2;启用后先请求配置范围中的最高版本。min_version=2 禁止降级;v0 始终不支持。非空配置选择 ASPA 必需能力而所有配置来源都明确不能提供 ASPA 时启动或变更失败;格式尚未读取、能力暂时未知时允许运行并受 wait_ready 超时约束。

RTR refresh/retry/expire 来自协议 EOD,初始缺省值和范围见状态机。connect_timeout 和 query_timeout 是库的工作上限,不能被当成延长协议数据寿命的依据。

HTTP 来源的 1200 秒覆盖准入等待、同组完整构建排队、连接、正文、解析及发布;它也允许不可达或停滞来源更晚报告本次请求失败,部署可显式缩短。原始有效期和备用组准备窗口不随请求预算延长。600 秒 flush 是持久化等待预算,关闭时从开始关闭起计算剩余额度;已进入事务的工作仍须实际结束。没有 PersistenceConfig 的注入后端也使用默认 600 秒,显式 flush(timeout=...) 可覆盖单次等待。就绪等待使用独立期限。数值依据和测量条件见性能基线。

文件路径在配置加载时相对配置文件目录解析;Python 直接构造的相对路径相对调用时工作目录解析并尽早固定。缺失路径不自动创建数据库,只有明确 owner 持久化配置允许创建目标数据库文件。

shared_reader 模式只接受 SQLite 路径,不启动来源连接,不接受同时配置的 writer sources;该模式使用独立 SharedSnapshotClient,提供 Client 的读取、状态、就绪与 watch 子集。配置构造器应使用分开的 SharedReaderConfig,与允许暂时无来源、后续通过 apply_config 增加来源的 owner Client 区分。

SSH 配置

RtrSourceConfig(transport="ssh", trust_profile_id=..., ssh=SshConfig(...)) 选择内置 SSH。安装 rpkiparrot[ssh],应用运行在 asyncio;Trio 在上下文进入时、任何凭据读取或联网前报 ConfigurationError。自定义工厂仍可在 Trio 提供兼容且已认证的字节流,无需提供内置 SshConfig。SSH 与 TLS 字段互斥,SSH 不使用 server_name。

SshConfig 要求显式 username、OpenSSH known_hosts 文件,以及 client_keys 元组或 password_provider 至少一种。主机密钥必须由宿主通过可信渠道取得;不提供首次自动信任或跳过检查选项。未知、变更或吊销的主机密钥拒绝连接。非标准端口的 known_hosts 主机名使用 [host]:port。不读取用户 SSH config、默认私钥或 agent,不使用 keyboard-interactive、GSSAPI、hostbased 或无认证方式,也不打开 shell、exec、PTY 或转发。

私钥、known_hosts 和可选口令只在上下文进入后的受管理线程准备。password_provider / passphrase_provider 是返回非空 str 的同步回调,每个来源准备时调用一次;它们不得依赖正在等待该回调的事件循环或执行无期限阻塞。加密私钥由 passphrase_provider 解密,不交互提示。构造配置只规范化路径,不执行回调或文件 I/O。CLI 使用显式环境变量名提供密码与密钥口令,见 CLI;Python API 不隐式读取环境变量。

信任或认证改变时构造新的 SshConfig 并更新 trust_profile_id,通过 apply_config 替换来源;原文件内容或回调的就地修改不触发自动重读。SSH 恢复身份包含用户名和非敏感 trust_profile_id,私钥、密码、文件内容及回调不序列化为来源状态、快照或录制。key/known_hosts 路径和回调实例变化也触发进程内来源替换。重连沿用同一次准备的材料,新的上下文或来源替换才重新准备。完整连接、取消和错误语义见 SSH 传输,公开签名见 API 参考。

本库不启用 AsyncSSH 的调试转储,也不修改宿主 logger。宿主同时将 AsyncSSH 日志设为 DEBUG 并调用 asyncssh.set_debug_level(3) 时,依赖的完整 packet dump 可能包含明文认证密码;该显式诊断设置不属于本库脱敏日志或 RTR 录制,见 AsyncSSH 官方说明。普通公开错误、状态和 RTR 录制仍不包含 SSH 凭据。

HTTP 服务配置

ServiceConfig 从 rpkiparrot.config 或 rpkiparrot.service 导入;create_app 的 lifespan 拥有一个 Client。配置构造不读取凭据、证书或数据库。服务支持 asyncio,固定单 worker;宿主须把 host、port、TLS 证书路径传给实际 ASGI 监听器,并关闭其自动代理头重写。完整参数、请求准入和错误规则见 HTTP 契约。可执行服务示例 展示宿主管理的真实本地 HTTP 服务与 SDK。

auth_mode 默认为 none,可选 bearer;none 只接受实际连接 peer 为回环 IP 的请求,peer 缺失也拒绝。bearer 必须注入 token_provider: Callable[[], str],在启动的受管理线程中调用一次。秘密不进入配置序列化、状态或错误;轮换需要新 lifespan。tls_mode 为 none、direct 或 proxy。direct 需要 tls_cert_file 和 tls_key_file,逐次请求仍验证实际 ASGI scheme 为 https;proxy 必须启用 bearer 并提供 trusted_proxy_ips 的显式 IP/CIDR,按未被重写的原始连接 peer 核对,且只接受唯一的 X-Forwarded-Proto: https。不接受全网 allowlist 或重复认证/协议头。非回环监听必须同时启用 bearer 和 TLS。

普通请求 max_concurrent_requests=4、max_pending_requests=16,总截止 request_timeout=120 秒;请求体 max_body_bytes=4 MiB,完整普通响应 max_response_bytes=16 MiB。长轮询独立 max_concurrent_polls=32 且不排队,long_poll_timeout=25 秒,watch 无活动 watch_idle_timeout=60 秒。分页 page_size=1000、max_page_size=10000。保留快照、订阅与事件窗口、批量、完整导出使用 ClientConfig.limits。以上配置都是有限工程初值;取消后仍在运行的受管理计算继续占用准入名额直到真实结束。

运行中修改

首版通过 Client.apply_config 原子替换完整配置,支持来源增删、组调整、来源参数及允许的资源限制修改。字段行为、配置版本冲突、注入映射、删除最后一个来源及需要重建 Client 的项目见运行中配置变更。冻结配置对象不允许就地修改;库不自动监视文件或读取全局配置。

资源限制

Limits 字段 初始值 超限行为
max_sources 16 个配置来源 初始配置或变更整体拒绝;退休中工作继续计入在途资源预算
max_concurrent_builds 2 构建前有界排队;同组来源完整构建串行,等待者仍占额度;HTTP 计入 request_timeout,RTR EOD 发布与文件组等待无硬性截止
max_pending_builds 16 个等待构建请求,配置候选最多 1 个 不接纳更多候选,不预读完整输入;配置繁忙报 ResourceLimitError
max_total_staging_wire_bytes 512 MiB,所有来源合计 拒绝或终止本次未提交构建;保留旧有效状态
max_json_depth 128 层 在完整解码前拒绝过深结构,dict 输入同样检查
max_json_bytes 256 MiB,按解压后字节计算 读取或解析失败,旧状态保持
max_records_per_source 5,000,000 整次更新失败
max_total_records 10,000,000 个原始支持记录 拒绝新提交,报告容量不足
max_staging_wire_bytes 512 MiB / 来源 / 事务 终止事务;协议状态按对应错误处理
max_pdu_bytes_v1 1 MiB 本地防护限制;不声称是 RFC 8210 固定上限
max_pdu_bytes_v2_legacy 1 MiB -10/-13 的本地防护限制,另按对应草案校验载荷结构
max_batch_items 10,000 执行前拒绝,不隐式拆成多个快照
max_path_asns 16,384 输入错误或资源限制;不截断路径
max_subscribers 128 拒绝新增订阅
subscription_queue_size 64 个事件 进入 ResyncRequired
event_window_size 1,024 个事件且总计最多 16 MiB 淘汰窗口并使落后游标失效
max_event_changes 10,000 转成明确的整类重验通知
max_source_diff_records 10,000 个输出载荷及支持差异项 整次 SourceDiff 失败,不返回截断结果
max_source_diff_bytes 16 MiB,按差异规范 JSON 的 UTF-8 字节 恰等可返回,超出整次失败
max_explanation_records 100 标记截断并提供查询条件
retained_snapshots 4 个服务端可定位版本,最长 600 秒 淘汰后远程查询 SnapshotGone
max_export_bytes 512 MiB 不产生成功的截断导出
max_recording_bytes 256 MiB 停止录制并报告不完整
recording_queue_size 128 项且最多 8 MiB 标记 gap,不阻塞 RTR

来源记录预算按规范化支持计数:每份 VRP/Router Key 支持计一项,ASPA 按 customer→provider 支持边计数(包括 AS0),不能只按 customer 数计费;不同来源的相同载荷分别计数。输入原始条数另外记录,重复输入仍消耗字节与解析预算。

记录数和字节数分别约束,不能声称以上值就是 Python 堆内存上限。单个事件不能突破字节窗口;用户无限持有自己取得的快照仍会占用应用内存,库只承诺限制自己保留的引用。新订阅、快照和更新构建也要计入内部保留预算,不能仅限制队列长度。

v2 的 8210bis-27 profile 固定受该草案的 65,535 字节上限约束,不能用配置放宽。历史 -10/-13 不沿用这个上限;它们的合法 ASPA PDU 可以超过 65,535 字节,按对应 count/长度规则及 max_pdu_bytes_v2_legacy 接纳。具体规则见 RTR profile 契约。所有自定义限制必须有限且合法;可提高工程限制,但不能突破协议硬约束。基准测量实际 RSS、索引和暂存峰值后再确认发布默认值。

这些初值的现实数据依据、工作负载、全局准入和 RSS/磁盘测量规则见规模与资源基线。提高单来源上限时还须检查全局上限;不能让等待队列中的输入先分配完整解析内存。

CLI 和服务加载

显式命令行选项优先于环境变量,再优先于 TOML,最后使用默认值。未传入的 CLI 默认参数不能错误覆盖文件值。来源列表整体替换,不按列表位置合并;修改一个来源时必须显式定位 ID。

首版环境变量仅暴露 RPKIPARROT_CONFIG、RPKIPARROT_FORMAT、RPKIPARROT_TIMEOUT、RPKIPARROT_LOG_LEVEL 和 RPKIPARROT_NO_COLOR;复杂来源及组使用文件或 Python 对象,不发明难以预测的嵌套环境变量规则。凭据使用显式 secret provider 或服务进程独立注入,不能直接复制进可导出的配置。

CLI 已实现显式 schema 1 TOML 加载器,加载规则见 CLI 配置说明。以下展示完整首版配置形状;SQLite 与 DuckDB 由 Client 的固定工作线程维护完整状态,SharedSnapshotClient 只读 SQLite。配置检查会实际验证后端可打开和恢复,未启动同步时不写入新 head:

schema_version = 1
required = ["vrp"]

[[groups]]
id = "primary"
priority = 0
failback_delay = 30

[[groups.sources]]
id = "cache-a"
kind = "rtr"
host = "cache.example.net"
transport = "tls"
port = 324
min_version = 1
max_version = 2
trust_profile_id = "operator-ca-v1"
server_name = "cache.example.net"
ca_file = "./credentials/operator-ca.pem"
client_cert_file = "./credentials/router-cert.pem"
client_key_file = "./credentials/router-key.pem"

[persistence]
backend = "sqlite"
path = "./state.sqlite3"
mode = "owner"

配置未知键、重复 ID、非法模式组合、非有限时间、矛盾版本范围均报 ConfigurationError。配置 schema_version 与数据交换格式版本分开演进。帮助和版本命令只需解析命令结构,不加载这个文件或验证数据库。