index.ts 2.8 KB

12345678910111213141516171819202122232425262728293031323334353637
  1. /** Operator-started foreground service. Never started by Web/Runner or the model.
  2. * No setup of OS users, permissions, service manager or network rules is performed. */
  3. import { chmodSync } from 'node:fs'
  4. import { isAbsolute } from 'node:path'
  5. import { z } from 'zod'
  6. import { parse } from './schema.js'
  7. import { CredentialVaultStore } from './store.js'
  8. import { CredentialVaultBroker } from './broker.js'
  9. import { serveCredentialVault } from './transport.js'
  10. import { acquireServiceInstance } from '../serviceInstance.js'
  11. import { readPrivateUtf8 } from './privateFiles.js'
  12. import {OperatorIsolationGate} from './isolation.js'
  13. import {ProtectedCredentialExecutor} from './protectedExecutor.js'
  14. try {
  15. const index = process.argv.indexOf('--config'), path = index < 0 ? undefined : process.argv[index + 1]
  16. if (!path) throw new Error('用法:node credentialVault/index.js --config /私有路径/vault.json')
  17. const config = parse(z.object({ home: z.string().refine(isAbsolute), socket: z.string().refine(isAbsolute), tokenFile: z.string().refine(isAbsolute),
  18. hermesUid: z.number().int().positive(),execution:z.object({approvalFile:z.string().refine(isAbsolute),hermesPid:z.number().int().positive(),home:z.string().refine(isAbsolute)}).strict().optional() }).strict(), JSON.parse(readPrivateUtf8(path,8192)))
  19. if (!process.getuid || process.getuid() === 0 || process.getuid() === config.hermesUid) throw new Error('密码库需要独立非 root OS 身份,不能与 Hermes 同用户运行')
  20. const controlToken = readPrivateUtf8(config.tokenFile,4096).trim()
  21. const store = await CredentialVaultStore.create(config.home)
  22. process.umask(0o077)
  23. const instance = acquireServiceInstance(store.root, 'credential-vault-v1')
  24. const executor=config.execution?new ProtectedCredentialExecutor(config.execution.home,new OperatorIsolationGate(config.execution.approvalFile,config.hermesUid,config.execution.hermesPid)):undefined
  25. const broker = new CredentialVaultBroker(store,Date.now,executor)
  26. const server = serveCredentialVault(broker, controlToken)
  27. process.once('exit', () => { broker.close(); instance.release() })
  28. server.listen(config.socket, () => {
  29. try { chmodSync(config.socket, 0o600); process.stdout.write('密码库私有服务已启动,默认锁定;执行能力以专用管理界面状态为准\n') }
  30. catch { broker.close(); server.close(); process.exitCode = 1 }
  31. })
  32. const stop = () => { broker.close(); server.close(() => { instance.release() }); server.closeAllConnections() }
  33. server.once('error', () => { stop(); process.exitCode = 1; process.stderr.write('密码库私有服务无法监听,请检查路径或已有实例\n') })
  34. process.once('SIGTERM', stop); process.once('SIGINT', stop)
  35. } catch {
  36. process.stderr.write('密码库启动失败,请检查私有配置和独立 OS 身份;不会自动修改系统设置\n'); process.exitCode = 1
  37. }