120 lines
13 KiB
Markdown
120 lines
13 KiB
Markdown
# 批量接入正式适配器
|
||
|
||
本专题是后续批量接入的默认技术入口。适配器实现、隔离测试及文档于2026-10-17完成;**本轮没有新增线上节点、部署新运维版本或运行真实SSH/客户端验收**。上一轮三节点实际验收使用的是已冻结旧任务适配器,不能套用为新实现的生产验收。
|
||
|
||
`batch.py`保留原命令适配器兼容,新计划默认用`configure-site.py`生成`runtime.kind=ssh-site-v1`。旧计划、账本、任务handler一律保留,不修改源码摘要后直接续跑旧任务。生产操作仍须具体接入授权、独立助手审核、Git运维部署及现场验收。
|
||
|
||
## 文件与职责
|
||
|
||
| 文件 | 职责 |
|
||
| --- | --- |
|
||
| `configure-site.py` | 本地合并明确站点参数,固定解释器、Paramiko版本、源码与非秘密配置摘要;不联网、不写账本、不覆盖旧计划 |
|
||
| `site_adapter.py` | 复用正式stage/provision/promote流程;按国家、组、批次和路径参数执行,按需连接服务器,进行动态正式订阅验收 |
|
||
| `ssh_pool.py` | 线程内复用白色机及下游SSH会话;下游强制通过白色机direct-tcpip,不提供直连回退 |
|
||
| `panel-acceptance.php` | 开工前选择有效正式组账号;缺少时创建本批受限非管理员测试用户,记录归属并收尾禁用 |
|
||
| `acceptance.py` | 动态集合、逐节点结果、原始证据摘要、QA身份/禁用/拒绝/下发排除和时效的完整自动审计 |
|
||
|
||
## 准备与Git部署
|
||
|
||
先按CoDocs确认VPS、白色机访问路线、正式组、展示编号及资源冲突。Python执行环境使用`requirements.txt`的Paramiko 5.0.0;配置与执行必须使用同一解释器。安装来源限官方包源/已审核环境,不临时导入任务目录中的未知transport模块。
|
||
|
||
生产前先提交并推送源码,再生成、推送和部署独立运维Git版本:保留原安装脚本,加入`panel-acceptance.php`及其`operations.php`,还有stage/promote/rollback/readiness/verify-node入口;面板、网关和目标节点的对应运维目录必须为计划中完整`provision_commit`且无工作区差异。配置文件和凭据不入Git。独立快照由正式`bundle-ops.py`生成,保持基础运维树、工作目录和真实Git索引不变,仅复制10个明确的已提交运维文件,默认拒绝已有分支或bundle,不联网:
|
||
|
||
```sh
|
||
python3 scripts/node-onboarding/bundle-ops.py --source FULL_SOURCE_COMMIT --base FULL_OPS_BASE_COMMIT --branch codex/node-tools-BATCH --bundle /private/task/provision.bundle
|
||
```
|
||
|
||
同一运维分支的后续修订只有显式`--advance`才允许,且`--base`必须精确等于当前分支头;新提交以该base为父并通过旧值比较更新,仅快进,不强推或覆盖并行修改。bundle仍要求新路径。随后按已授权任务推送返回的运维分支、审核commit/bundle摘要,再经白色机部署到明确版本。当前源码/运维快照的提交推送与远端部署分别报告;本轮不自动切换任何生产应用或旧任务的运维目录。
|
||
|
||
适配器在stage前检查本地依赖、远端Git版本及工具、正式组和账号、节点既有配置、独立端口、原服务、证书归属与钩子。既有证书无本任务owner、全局证书钩子或目标证书的额外pre/post钩子均停止。缺依赖须在准备阶段解决,不先创建一批面板对象后再临时安装工具。
|
||
|
||
## 本地参数文件
|
||
|
||
全部放在权限700的任务目录,文件600。基础计划沿用[NODE_BATCH](NODE_BATCH.md#参数格式),包括本批任意数量、任意已支持国家的候选节点;不固定五节点、日本、组1或任务日期。新增站点配置JSON如下,示例路径必须换为实际已核实值:
|
||
|
||
```json
|
||
{
|
||
"ops_root": "/opt/dpl-batch/provision",
|
||
"node_root": "/opt/dpl-node/provision",
|
||
"batch_backup_root": "/root/codex-backups/dpl/BATCH",
|
||
"node_backup_root": "/root/codex-backups/dpl/BATCH-nodes",
|
||
"qa_root": "/var/lib/dpl-sgw/BATCH",
|
||
"provider_snapshot": "/private/task/providers.json",
|
||
"exit_registry": "/private/task/exits.json",
|
||
"ssh_config": "/private/task/ssh.json",
|
||
"preserved_service": {
|
||
"unit": "XrayR", "binary": "/usr/local/XrayR/XrayR",
|
||
"config": "/etc/XrayR/config.yml", "port": 443
|
||
},
|
||
"formal_socks_port": 19203,
|
||
"client_sha256": "填已核验官方客户端文件的64位SHA256",
|
||
"gateway_containers": ["dpl-subscribe-gateway", "dpl-subscribe-admin", "subscribe-gateway", "subscribe-admin"]
|
||
}
|
||
```
|
||
|
||
批次备份目录必须独立且固定,不跨实际批次复用旧资源基线。新节点端口、正式客户端端口均查重预留;共享网关客户端还使用同一账本内的进程锁。当前适配器用于独立AnyTLS节点及明确列出的既有服务;不自动把白色机、面板或网关当节点宿主。
|
||
|
||
`ssh.json`含`known_hosts`绝对路径及`hosts`映射,key为准确CoDocs VPS ID。每台登记`host`、`port`、`username`、用户已批准的`host_key_sha256`(OpenSSH SHA256格式)、可选`credential_ref`;白色机额外登记`role=white_jump`,ID须与`context.jump_vps`一致。候选IP必须与冻结计划相同。`credential_ref`只引用既定安全存储中的600文件。默认JSON支持password/key_filename/passphrase;既有CoDocs Markdown可设`credential_format=codocs-password`,仅按原文件中唯一的“密码:”字段在内存读取,不能为了适配而复制或改写原凭据正文。内容不得进入计划、日志、argv或Git。配置和出口登记摘要固定,凭据只按需读取,不默认自动轮换。
|
||
|
||
只从known-hosts文件选择与冻结配置中已批准指纹完全相符的公钥;找不到匹配时在读取/发送凭据前停止。全局known-hosts增加无关记录无需改变本批受信任指纹。不关闭主机密钥检查,不自动TOFU;首次信任先按访问规则核验/批准精确指纹,保存公开known-host条目。会话丢失只标记未知,不自动重发安装/开放命令;下次仍从现场probe恢复。
|
||
|
||
`exits.json`按SNI登记独立已知预期,例如:
|
||
|
||
```json
|
||
{"node.example.invalid":{"expected_exit":"192.0.2.11","country":"JP"}}
|
||
```
|
||
|
||
覆盖该账号当前应获得的全部旧节点与新候选,不能从本次实测结果反填预期来让检查通过。增加或改变节点时,按CoDocs/授权现场证据更新新任务的登记;遇到未知预期或不支持协议停止,不能静默省略。当前站点适配器针对IPv4 A记录、固定端口AnyTLS;端口范围或其他协议停止而非静默省略,扩展需另补验证能力。
|
||
|
||
`providers.json`是实际已登记后台UI或授权官方API的近期观察,最多900秒有效,不使用语言相关DOM字符串:
|
||
|
||
```json
|
||
{
|
||
"captured_at": 0,
|
||
"dns": [{"type":"A","name":"node.example.invalid","address":"192.0.2.11","proxy":false}],
|
||
"relays": [{"name":"dpl-new-anytls","host":"relay.example.invalid","port":47011,"target":"192.0.2.11","listen_port":24443,"proxy_protocol_in":false,"proxy_protocol_out":false}]
|
||
}
|
||
```
|
||
|
||
`captured_at`填实际采集Unix时间。观察文件可以刷新,不能把静态勾选当现场证据。资源存在但参数/归属不符停止;尚未创建的DNS/中转仍按注册后台批量操作、更新观察后续跑。本实现没有新增Cloudflare/中转官方API写入权限,也不默认直连其源站。
|
||
|
||
## 执行与等待
|
||
|
||
```sh
|
||
/path/to/reviewed/python scripts/node-onboarding/configure-site.py --plan /private/task/base.json --site /private/task/site.json --output /private/task/frozen.json
|
||
/path/to/reviewed/python scripts/node-onboarding/batch.py plan --plan /private/task/frozen.json --state-root /private/task/ledger
|
||
/path/to/reviewed/python scripts/node-onboarding/batch.py run --plan /private/task/frozen.json --state-root /private/task/ledger --jobs 3 --timeout 900 --execute --events
|
||
```
|
||
|
||
配置/plan只做本地工作,不创建线上测试用户。`run --execute`进入准备前检时才允许准备验收账号,随后全批前检屏障通过才stage。`--events`输出无秘密的阶段状态JSON行,最后一行为批次结果;不加此参数保持原单JSON输出协议。账本`events.jsonl`仅追加状态变化,等待时使用进程输出/事件,不每几秒重复查全部状态。只有有新依赖证据、外部DNS/中转变化或用户恢复指令时续跑,不能无限重试。
|
||
|
||
SSH会话仅在本次进程、同一工作线程内复用,线程不共用可变SSH客户端;进程退出关闭连接,不建立后台守护进程。每个阶段通过lazy连接只访问必要主机。Paramiko channel有远端命令超时;进程内模式不承诺`--timeout`能杀死已发送远端命令,停止后仍须核实实际结果。
|
||
|
||
## 动态验收和测试用户
|
||
|
||
准备阶段先按正式组、banned、额度和有效期选既有账号,优先管理员且最多检查20个候选,避免全表巡检客户凭据;检查真实`ServerService::getAvailableServers`及`getAvailableUsers`,包括插件过滤;候选被实际节点下发过滤时不算可用。未找到可用账号时自动创建本任务测试用户:非管理员、100MiB、1小时、1设备、无订单/套餐改写及通知消息。私有journal在DB提交前记录身份,丢失回包时恢复同一用户,不重复创建或接管同名用户。
|
||
|
||
新节点仍沿用各自隔离QA→严格TLS/出口/上报→独立助手按精确报告审核→append开放并禁用QA。适配器直接生成`key-evidence.json`,包含原始报告字节SHA256、请求摘要、QA身份、报告时间及最新在线/双向上报状态,600原子保存,不再需要任务专用采集脚本。助手完成独立审核后,主协调者在同目录记录`key-reviewed.json`:`request_sha256`、`report_sha256`、`qa_user_id`、`verified_at`必须与该报告完全相同;未审核不得自动填批准文件。全部本组候选开放后,正式验收按账号**当前实际应获得的集合**生成完整快照,与客户端完整订阅逐条校验,并逐条实际联网。集合绑定节点ID、标签、端点、SNI、独立预期出口/国家;验收后再取一次集合,增加、删除或改变任一项均阻止旧报告通过,不固定节点数。旧节点同样必须覆盖。
|
||
|
||
每组正式客户端串行运行,保留完整订阅DNS/路由,只给诊断目的地固定出站。新建测试用户可能等待节点拉取用户列表;只对通用客户端运行失败最多做3轮诊断读测试,间隔15秒且共用900秒预算。结构、TLS或出口断言错误不重试;不重放生产写入、不另建用户来绕过失败。证据绑定完整计划摘要、节点集合摘要、原始报告及QA拒绝文件SHA256、QA实际ID、403、banned、实际下发排除及900秒时效。任一缺项或摘要/集合变化失败。旧节点关键配置、全部套餐、原用户关联和订阅地址按批次原始基线核对,网关容器ID/启动时间/重启数再次核对。
|
||
|
||
正式验收成功或失败都禁用本任务新建的正式测试用户;既有账号不被禁用或改密码/权限。进程正常退出在finally收尾,即使等待或部分前检失败也尝试清理;遇到断网/SIGKILL无法保证立即清理,记录的归属、1小时有效期与额度是兜底,恢复后先核对旧journal。已关闭用户绝不重新启用;下一次需要用户时创建新受限租约,保留历史并在最终审计核对其403/下发排除。完成要求由服务端实际集合/订阅拒绝证明,不承诺立即踢断已经建立的客户端会话。
|
||
|
||
## 续跑与原服务调查
|
||
|
||
preflight/publish/audit始终现场执行。只有先前状态为done、收到真实前检依赖摘要、请求/代码/config未变、证据未过期,才可复用stage/dns/provision/relay/verify。前驱依赖变化使后续缓存失效;running/unknown/conflict绝不跳过。旧适配器不提供这些证据时仍按原逐阶段probe逻辑执行。生产写入仍先probe、持久化意图、apply后probe,不为速度跳过归属检查。
|
||
|
||
原服务保存最初unit/程序/配置SHA256、监听、active及PID/启动时间/重启数。配置/程序/监听改变或不可用停止;仅进程变化分类为“需调查”,不宣称是DPL导致,也不自动刷新基线。记录观察后由操作者及助手核查生命周期、OOM、操作时间线和稳定采样;认可时在本节点私有备份登记`preserved-service-review.json`,绑定最初/本次观察摘要、原因及证据SHA256。原基线保持;再次重启不匹配旧审核,须重新调查。
|
||
|
||
## 验证范围
|
||
|
||
新增Python测试覆盖动态1/2/7/23节点、集合变化/漏旧节点、出口/TLS/QA身份/摘要/时效、白色机跳转及严格密钥策略、线程会话复用/断线、依赖续跑及未知/过期状态、原服务精确调查回执、远端脚本与PHP构造。PHP隔离测试使用真实Eloquent/SQLite和ServerService,覆盖自动受限用户、既有管理员复用、真实下发、无效账号、journal恢复、关闭后新租约、归属漂移、插件排除及失败收尾;不加载.env/应用provider/Redis/HTTP。生产停机、失败回退、远端真实会话复用性能及十节点耗时尚未演练,不承诺固定提速倍数。
|
||
|
||
隔离回归入口(不使用项目默认PHPUnit配置):
|
||
|
||
```sh
|
||
python3 -B -m unittest discover -s tests/node-onboarding -p 'test_*.py' -q
|
||
php vendor/bin/phpunit --no-configuration tests/node-onboarding/PanelChangeIsolatedTest.php
|
||
php vendor/bin/phpunit --no-configuration tests/node-onboarding/AcceptanceUserIsolatedTest.php
|
||
```
|