feat(overlay): 覆盖网络线序①–⑮ 代码与测试产物入库
覆盖网络线累积产物(此前只在工作区、未入版本库):
- 新增 relay 子系统 src/net/relay/**(wire/duplex/server/client/dialer/switcher/directory/identity/keys/placement/network/addr-override/main/index)
- 新增 src/worker/relay-tunnel.ts、src/web/routes/overlay.ts
- 新增观测/演练脚本 overlay-probe、overlay-failover-drill、overlay-keyring、overlay-holepunch、overlay-jitter、overlay-wan、overlay-relaykey-add、relay-mem-calibrate
- 新增测试 12 个(relay / relay-failover / remote-spawner / instance-port / overlay-{network,auth,bootstrap,identity} / remote-user-fs 等)
验收基线:npm test = 162 pass / 0 fail / 1 skip;--scene all = 12 PASS / 0 SKIP / 0 FAIL;overlay-probe = 12/12
This commit is contained in:
1 parent
640813e84e
commit
146c3d25ef
53 files changed
+16187
-63
No files matched your search
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* 覆盖网络 序⑥ · relay HMAC 密钥表增删(**运维小工具**,⛔ 非产品路径)。
|
||||
*
|
||||
* 为什么要有它:`/etc/dshs/relay-keys.json` 是**直接编辑会毁掉整张表**的那类文件
|
||||
* (键 = 逻辑名 `<net>/<hostId>`,值 = 64 hex;写坏一个字符 ⇒ relay 起动即抛、
|
||||
* 全部节点同时被拒)。所以增删都走这个工具:**先备份、再原子写、写完自校验**。
|
||||
*
|
||||
* 用法:
|
||||
* node scripts/overlay-relaykey-add.cjs --file /etc/dshs/relay-keys.json --name ops/w-dev
|
||||
* node scripts/overlay-relaykey-add.cjs --file … --name ops/w-dev --secret <64hex>
|
||||
* node scripts/overlay-relaykey-add.cjs --file … --name ops/w-dev --remove
|
||||
*
|
||||
* @module scripts/overlay-relaykey-add
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
const { chmodSync, copyFileSync, readFileSync, renameSync, writeFileSync } = require('node:fs')
|
||||
const { randomBytes } = require('node:crypto')
|
||||
|
||||
const args = {}
|
||||
const argv = process.argv.slice(2)
|
||||
for (let i = 0; i < argv.length; i++) {
|
||||
if (!argv[i].startsWith('--')) continue
|
||||
const k = argv[i].slice(2)
|
||||
const v = argv[i + 1]
|
||||
if (v === undefined || v.startsWith('--')) args[k] = true
|
||||
else {
|
||||
args[k] = v
|
||||
i++
|
||||
}
|
||||
}
|
||||
if (typeof args.file !== 'string' || typeof args.name !== 'string') {
|
||||
process.stderr.write('usage: --file <relay-keys.json> --name <net/hostId> [--secret <64hex>] [--remove]\n')
|
||||
process.exit(2)
|
||||
}
|
||||
|
||||
const table = JSON.parse(readFileSync(args.file, 'utf8'))
|
||||
if (table === null || typeof table !== 'object' || Array.isArray(table)) {
|
||||
throw new Error(`${args.file} 不是对象`)
|
||||
}
|
||||
const before = Object.keys(table)
|
||||
copyFileSync(args.file, `${args.file}.bak-seq6-${Date.now()}`)
|
||||
|
||||
if (args.remove === true) {
|
||||
if (!(args.name in table)) {
|
||||
process.stderr.write(`⚠ ${args.name} 不在表里(无需删)\n`)
|
||||
} else {
|
||||
delete table[args.name]
|
||||
}
|
||||
} else {
|
||||
const secret =
|
||||
typeof args.secret === 'string' ? args.secret.trim().toLowerCase() : randomBytes(32).toString('hex')
|
||||
if (!/^[0-9a-f]{64}$/.test(secret)) throw new Error('secret 必须是 64 位 hex')
|
||||
table[args.name] = secret
|
||||
}
|
||||
|
||||
const tmp = `${args.file}.tmp`
|
||||
writeFileSync(tmp, `${JSON.stringify(table, null, 2)}\n`, { mode: 0o600 })
|
||||
renameSync(tmp, args.file)
|
||||
chmodSync(args.file, 0o600)
|
||||
|
||||
// 自校验:用产品代码自己的装载器读一遍(键名/密钥形状非法会在这里炸)
|
||||
const { loadKeysFile } = require('../lib/net/relay/keys.js')
|
||||
const parsed = loadKeysFile(args.file)
|
||||
process.stdout.write(
|
||||
`✓ ${args.file}:${before.length} → ${parsed.size} 条\n` +
|
||||
` 键:${[...parsed.keys()].join(', ')}\n` +
|
||||
(args.remove === true ? '' : ` ${args.name} 的 secret(仅本次打印,⛔ 别进日志):${table[args.name]}\n`),
|
||||
)
|
||||
Reference in new issue
Block a user