设备安装配置——服务器最小化、代理智能化
本文记录了服务器如何描述设备安装任务,以及智能代理如何执行这些任务来部署证书和自签名 CA。
目标
- 保持服务器简单:描述“做什么”,而不是“如何做”(每个平台由代理负责)。
- 让代理处理所有平台细节、校验、回滚以及密钥管理。
- 使用按对象拆分的安装项(copy/exec),统一存放在 JSON 数组中;不需要
config_type。 - 通过
ob_*字段让安装项直接引用所需资源,确保代理能安全获取。 - 保证幂等性与安全性(原子写入、备份)并便于审计。
核心原则
- 服务器与平台无关,无需记录操作系统或发行版信息。
- 只有代理持有用于解密证书私钥的密钥;服务器永远不会看到明文。
- 安装项只描述动作:复制文件、执行命令或脚本。检测、验证、回滚等全部由代理实现。
- 如果用户下发了某个平台无法执行的命令(例如 Windows 上的 bash),代理会明确失败;服务器不会提前阻止。
数据模型影响
- 表:
device_install_configs- 仅使用
installsJSON 数组,每个元素就是一个动作项。 - 通过对
user_device_id的唯一约束,确保每个设备只有一条配置。 - 需要分组或标记时,使用安装项自身的
tags字段;不要依赖额外的表字段。
- 仅使用
资源引用模型
- 每个安装项可以直接引用所需资源:
ob_type:"cert"或"ca"ob_id: 对应cert_records.id或selfca_authorities.idob_name: 可选的人类可读名称,便于界面和日志展示
- 代理会使用设备身份和短期令牌从服务器获取所需资源,并在本地完成解密。
- 证书资源会提供标准虚拟文件名:
private.key、certificate.pem、chain.pem、fullchain.pem、certificate.der、bundle.pfx、meta.json - 自签名 CA 资源提供:
ca.pem、ca.der、meta.json
代理本地目录结构
代理通过 --config-dir <PATH> 指定可写的工作目录(必填)。所有本地状态、缓存、临时文件均位于此目录。启动时会自动创建缺失的目录并设置安全权限。
<CONFIG_DIR>/
resources/
certs/
<cert_id>/
releases/
<version>/
certificate.pem
private.key
chain.pem
fullchain.pem
certificate.der
bundle.pfx
meta.json
current -> releases/<version>
cas/
<ca_id>/
releases/
<version>/
ca.pem
ca.der
meta.json
current -> releases/<version>
cache/
downloads/
state/
installs_applied.json
resource_index.json
tmp/
logs/
注意:
<version>是代理生成的单调、易读的版本号,例如2025-09-16T12-00-00Z-<shortSerial>或ts-<epoch>-<shortSha>。current指向当前生效版本,更新时在完成验证后原子替换。- 若平台不支持符号链接,可使用
current.txt记录当前版本路径。 - 目录权限默认为
0750,私钥文件0600,公开文件0644。后续如需特殊权限,可通过后续exec步骤处理。 - 默认保留最近 N 个版本(默认 3 个),并在清理时避免删除当前版本。
- 所有写入均在
<CONFIG_DIR>/tmp下暂存后原子替换。
虚拟文件名映射
copy.from[]中的虚拟文件名会解析到对应资源当前版本中的具体文件路径。- 证书资源:
private.key、certificate.pem、chain.pem、fullchain.pem、certificate.der、bundle.pfx、meta.json - 自签名 CA:
ca.pem、ca.der、meta.json - 代理会逐条执行
(from[i] -> to[i])的复制操作,确保原子性与回滚能力。
动作项(JSON 合同 v0)
顶层是数组,每个元素代表一个动作。目前支持 copy、exec、import_ca。
通用字段
id: 字符串,建议填写,用于排序、审计、depends_ontype:"copy" | "exec" | "import_ca"enabled?: 默认为true,为false时代理会跳过但仍保留记录continue_on_error?: 默认为falsedepends_on?: 字符串数组,引用前置步骤的idtags?: 字符串数组,用于打标签ob_type?:"cert" | "ca"ob_id?: 数字,资源 IDob_name?: 字符串,便于阅读
Copy
from: 字符串数组,虚拟文件名to: 字符串数组,与from一一对应的绝对路径
备注:
- 协议中没有单独的权限/归属开关,代理会提供安全默认值(原子写入、备份一次、合理权限、幂等)。
- 如需特殊权限或属主,可在复制后追加
exec操作。
Exec
cmd?: 字符串,通过 shell 执行(POSIX:/bin/sh -c,Windows:cmd.exe /C)cmd_argv?: 字符串数组,直接调用(推荐)timeout_ms?: 超时时间,默认 30 秒run_as?: 切换运行用户(POSIX)env?: 环境变量字典verify?: 自定义校验,例如{ "type": "command", "cmd": [...] }
执行行为说明
cmd形式保留 shell 能力,但需注意后台进程等情况。cmd_argv形式不会经过 shell,更安全,适合生产使用。- 代理会捕获 stdout/stderr 写入日志(必要时截断)。
timeout_ms超时会终止子进程。- 提供
env时子进程仅包含这些变量;未提供则继承代理环境。 run_as仅在代理具备权限的 POSIX 平台启用;Windows v0 暂不支持。continue_on_error为真时失败会记录但继续后续步骤。- 若使用 shell 后台执行,代理以 shell 返回码为准,请谨慎使用。
Import CA
- 负责平台信任库导入,必须包含
ob_type: "ca"与有效ob_id。 - 推荐
from:["ca.pem"]以确保代理先准备 PEM。 enabled: false时跳过导入和任何复制行为。
示例
[
{
"id": "copy-key",
"type": "copy",
"enabled": true,
"ob_type": "cert",
"ob_id": 12345,
"ob_name": "api.example.com",
"from": ["private.key", "certificate.pem"],
"to": ["/opt/cert/private.key", "/opt/cert/certificate.pem"]
},
{
"id": "trust-store",
"type": "import_ca",
"ob_type": "ca",
"ob_id": 6789,
"ob_name": "corp-root",
"from": ["ca.pem"],
"tags": ["ca-install"],
"enabled": false
},
{
"id": "reload-service",
"type": "exec",
"enabled": true,
"cmd": ["bash", "-lc", "systemctl reload nginx"],
"timeout_ms": 15000,
"depends_on": ["copy-key"],
"verify": { "type": "command", "cmd": ["bash", "-lc", "systemctl is-active nginx"] }
}
]
代理职责
- 根据
ob_type/ob_id获取所需资源,使用设备身份和短期令牌。 - 在本地解密私钥,服务器不会看到明文。
- 暴露标准虚拟文件名供 copy 使用。
- 按
depends_on执行,必要时并行。 - 保证幂等性、原子写入、权限、备份和回滚。
- 输出每个动作的状态、耗时、日志摘要。
import_ca需要记录详细的信任库操作日志。
服务器职责
- 原样存储并下发
installs数组。 - 提供认证接口供代理获取资源,并使用短期令牌控制权限。
- 可做轻量校验,但不强制平台差异。
- 后续可增加审计表,但不修改该合同。
幂等、校验与回滚
- 校验类型:
file_hashcert_fingerprintcommand
copy失败会尝试回滚到备份;exec未来可扩展回滚命令。
安全性
- 私钥只在设备端解密。
- 获取资源使用短期令牌,避免长时间存储敏感信息。
- 推荐通过
cmd_argv调用可执行文件,减少被 shell 注入的风险。 - 目标文件的权限默认安全,如需特殊要求可以通过后续命令处理。
迁移注意事项
device_install_configs.installs是唯一真源。- 不使用
config_type;需要分组可使用tags或未来的绑定模型。 - 后续可增加运行/状态审计表,不影响该 JSON 合同。
未来扩展
variables:简单模板变量。- 更丰富的对象选择(按标签、版本)。
- 计划版本化与设备/设备组绑定。
附录:JSON Schema(节选)
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "DeviceInstallItemsV0",
"type": "array",
"items": { "$ref": "#/definitions/item" },
"definitions": {
"item": {
"type": "object",
"required": ["type"],
"properties": {
"id": { "type": "string" },
"type": { "enum": ["copy", "exec", "import_ca"] },
"enabled": { "type": "boolean" },
"continue_on_error": { "type": "boolean" },
"depends_on": { "type": "array", "items": { "type": "string" } },
"tags": { "type": "array", "items": { "type": "string" } },
"ob_type": { "enum": ["cert", "ca"] },
"ob_id": { "type": "number" },
"ob_name": { "type": "string" },
"from": { "type": "array", "items": { "type": "string" } },
"to": { "type": "array", "items": { "type": "string" } },
"cmd": { "anyOf": [ {"type": "string"}, {"type": "array", "items": {"type": "string"}} ] },
"timeout_ms": { "type": "number", "minimum": 0 },
"run_as": { "type": "string" },
"env": { "type": "object", "additionalProperties": { "type": "string" } },
"verify": { "$ref": "#/definitions/verify" }
},
"allOf": [
{ "if": { "properties": { "type": { "const": "copy" } } },
"then": {
"required": ["from", "to"],
"properties": {
"from": { "type": "array", "items": { "type": "string" }, "minItems": 1 },
"to": { "type": "array", "items": { "type": "string" }, "minItems": 1 }
}
}
},
{ "if": { "properties": { "type": { "const": "exec" } } },
"then": { "required": ["cmd"] } },
{ "if": { "properties": { "type": { "const": "import_ca" } } },
"then": {
"required": ["ob_type", "ob_id"],
"properties": {
"ob_type": { "const": "ca" },
"from": { "type": "array", "items": { "type": "string" } }
}
}
}
]
},
"verify": {
"type": "object",
"oneOf": [
{ "properties": { "type": { "const": "file_hash" }, "expected": { "type": "string" } }, "required": ["type", "expected"] },
{ "properties": { "type": { "const": "cert_fingerprint" }, "expected": { "type": "string" } }, "required": ["type"] },
{ "properties": { "type": { "const": "command" }, "cmd": { "anyOf": [ {"type": "string"}, {"type": "array", "items": {"type": "string"}} ] } }, "required": ["type", "cmd"] }
]
}
}
}
该 v0 合同让服务器保持最小化,只描述 copy/exec 动作,同时赋予平台感知的代理足够能力,安全地在设备上部署证书和自签名 CA。