
OneUptime Runbook 配置与安全指南Agent 分发模型、超时限制、权限与数据库结构全解析【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime运行手册Runbook是 OneUptime 在事故响应与自动化修复场景中的核心编排模块当告警触发时Runbook 按照预先定义的步骤序列自动或手动执行诊断、止损与修复操作。本篇技术指南以官方文档 Runbook 配置与安全英文原版见 configuration.md为主线深入剖析 OneUptime Runbook 的执行模型、超时与输出限制、权限体系、队列调度、安全加固机制以及底层数据库结构并结合仓库源码给出可验证的实现细节。读完本文你将能够正确规划 Runbook Agent即 Runner的部署、合理配置步骤超时、理解权限矩阵与数据流并掌握在生产环境中安全运行自动化步骤的实战要点。一、Bash 与 JavaScript 步骤的真实执行方式Agent 分发模型OneUptime 的 Runbook 步骤类型丰富详见 RunbookStepType其中 Bash 和 JavaScript 两类步骤有一个最关键的设计前提它们永远不会在 OneUptime 的 Worker 进程上执行。它们会被作为作业Job派发给一个特定的 Runbook Agent——一个安装在你自己基础设施内某台主机上的小型进程在 OneUptime 中现在正式命名为Runner。官方文档给出的分发模型分为四步Runbook 步骤的作者在编写步骤时从下拉框中选定一个 Runbook AgentRunner。步骤执行时Worker 在RunnerJob表中插入一行记录其中targetAgentId指向所选 Agent 的 ID状态为Pending。只有那个特定的 Agent且仅有它能以原子方式认领claim该作业在本地执行脚本——Bash 通过bash -c scriptJavaScript 在isolated-vm沙箱内运行——然后将结果回传。Worker 拿到结果后继续推进 Runbook 的下一个步骤。这一模型在源码中体现得非常清晰。在 StepExecutors.ts 中Bash、JavaScript、SSH、Kubernetes 四类步骤共享同一条dispatchToAgent分发路径先校验agentId非空且为合法 ObjectID然后调用RunnerJobService.enqueue创建作业再通过RunnerJobService.pollUntilTerminal轮询直到作业到达终态。runJavaScriptStep与runBashStep的唯一差异只是stepType不同——Agent 依据该字段在本地选择对应的执行器// runJavaScriptStep 与 runBashStep 的核心差异仅在于 stepType export async function runBashStep(step: RunbookStep, ctx: StepExecutionContext): PromiseStepRunResult { const config: BashStepConfig step.config as BashStepConfig; return dispatchToAgent({ stepType: RunbookStepType.Bash, step, ctx, script: config.script || , timeoutInMs: resolveStepExecutionTimeoutInMs(config.timeoutInMs), claimTimeoutInMs: resolveAgentClaimTimeoutInMs(config.claimTimeoutInMs), agentId: config.agentId || , missingAgentError: Bash step is missing a Runbook Agent. Pick an agent under Runbooks → Agents., }); }源码中的报错信息也印证了“脚本不再在 Worker 上运行”这一事实runJavaScriptStep的missingAgentError明确写着JavaScript no longer runs on the OneUptime Worker.。一个重要的事实变更RUNBOOK_BASH_ENABLED这个环境变量标志已经不存在了。某个部署中 Bash/JavaScript 步骤能否工作完全取决于项目里是否至少有一个已连接的 Runbook AgentRunner。这意味着启用自动化步骤的前提是正确安装并注册 Runner。二、输出上限与超时控制为了保障 Worker 与 Agent 的资源安全OneUptime 对步骤执行施加了严格的输出与超时限制限制项默认值说明单步输出上限50 KB超出部分被截断并附加截断标记单步执行超时JavaScript / Bash / HTTP30 秒可在 Runbook 的「步骤Steps」页面按步骤设置留空则使用默认值单步认领超时Claim timeoutBash / JavaScript2 分钟Worker 等待所选 Agent 认领作业的最长时间超时则判定步骤失败同样按步骤可配超时取值范围1 秒 ~ 1 小时超出范围的值在步骤实际执行时被钳制clamp到边界值超时取值范围的钳制逻辑具有重要的健壮性意义一份写错的配置既不能关掉超时也不能无限期占用一个 Worker 槽位。源码层面的证据在 RunbookStepTimeout.ts 中DEFAULT_STEP_EXECUTION_TIMEOUT_IN_MS 30 * 1000MIN_STEP_EXECUTION_TIMEOUT_IN_MS 1000MAX_STEP_EXECUTION_TIMEOUT_IN_MS 60 * 60 * 1000DEFAULT_AGENT_CLAIM_TIMEOUT_IN_MS 2 * 60 * 1000最小/最大同样为 1 秒与 1 小时。关键的解析函数resolveTimeoutInMs的语义是任何不可用的输入未设置、空字符串、非数字、零或负数都回退到默认值而不是让步骤失败——因为在事故进行中用文档化的默认值跑完一个 Runbook远比因配置损坏而拒绝执行更有用。可用值则取整到毫秒并钳制进[min, max]区间。Dashboard 步骤编辑器与 Worker 执行端都通过该模块解析超时保证作者看到的边界与实际执行的边界永远一致。输出上限 50 KB 则在 StepExecutors.ts 中实现MAX_OUTPUT_BYTES 50_000truncate函数按 UTF-8 字节数截断输出超出时追加\n... [output truncated]标记。三、权限体系Runbook 权限组Runbook 的所有权限都归属在Runbook权限组之下。官方文档列出以下权限项权限作用范围CreateRunbook/EditRunbook/DeleteRunbook/ReadRunbook管理 Runbook 模板CreateRunbookExecution/EditRunbookExecution/ReadRunbookExecution启动、勾选完成、读取执行记录CreateRunbookRule/EditRunbookRule/DeleteRunbookRule/ReadRunbookRule管理自动触发规则CreateRunner/EditRunner/DeleteRunner/ReadRunner管理在你自身基础设施中执行步骤的 Runner值得注意的兼容性说明*Runner系列权限在 Runner 改名之前叫*RunbookAgent现有授权已自动迁移无需重新分配。此外还有三个可分配给团队的角色RunbookAdmin——完整控制权聚合了上面全部细粒度权限RunbookMember——日常使用RunbookViewer——只读访问。这些权限在实际的数据库访问控制中有严格落地。例如 RunnerJob.ts 的TableAccessControl明确规定create与update均为空数组该表不可由用户直接写入只能由 Worker 与 Agent 管理读取则要求ProjectOwner/ProjectAdmin/ProjectMember/Viewer/RunbookAdmin/RunbookMember/RunbookViewer/ReadRunbookExecution之一。又如 Runner.ts 中 Agent 密钥key字段的读取权限被收紧到仅ProjectOwner/ProjectAdmin/RunbookAdmin——注释里写得很明白拿到这把密钥就等于拿到了该 Runner 能接触的所有 Runbook 密钥与凭据因为它会在认领作业时被用于解密注入因此其读取门槛必须与管理 Runner 本身同级避免让只读的 Viewer 成员间接拿到项目 SSH 私钥和 kubeconfig。四、队列与 Worker调度机制Runbook 执行运行在名为Runbook的BullMQ 队列上。Worker 的并发度concurrency为25——如果你的部署存在大量并发执行可以在部署中调整该值。文档中还提到一个调度细节当某个手动步骤通过 API 被勾选完成后执行会被重新入队以便从下一步继续推进。这样做的目的是保持 Worker 处于“热”状态keep the worker hot为 Runbook 的其余步骤持续服务。与此相关的执行上下文见 StepExecutors.ts 的StepExecutionContext它携带runbookExecutionId、runbookName、incidentId、alertId、scheduledMaintenanceId、triggeredByUserId以及previousStepExecutions此前各步骤的状态快照这些上下文正是后续步骤尤其是 AI 步骤判断触发来源与历史状态的依据。五、安全加固要点Hardening Notes5.1 JavaScript 与 Bash 的隔离执行JavaScript运行在isolated-vm沙箱中并注入一段标准的 prelude前奏代码其职责包括切断原型链severs prototype chains、移除Function与eval、冻结内置原型freezes built-in prototypes从而削弱逃逸沙箱的常见手段。Bash通过bash -c执行并且超时强制在 Agent 侧实施——即 Agent 本地负责在超时后终止脚本进程。由于这两类脚本运行在由你控制的 Agent 主机上而非 OneUptime Worker 上即便脚本行为异常其影响范围也被限定在 Agent 所在的主机环境内。5.2 HTTP 步骤的宽松状态校验HTTP 步骤使用一个宽松的状态校验器permissive status validator4xx/5xx 响应会被记录为步骤失败而不是作为异常抛出。这样设计的好处是捕获到的输出能真实反映上游实际返回的内容状态码、响应头、响应体都会进入输出。源码佐证在 StepExecutors.ts 的runHttpStep它使用原始 axios 发起请求validateStatus: () true令所有状态码都不触发 axios 的 reject随后拼装Status/Headers/Body三段文本作为输出2xx~3xx 判定成功其余状态判定失败并附带HTTP status错误信息。同时HTTP 步骤的安全设计值得一提因为步骤的 URL、方法、请求头、请求体来自 Runbook 的 steps JSON可被项目成员读取且响应会原样回传给调用方这本质上是一个“攻击者可控形状”的出站请求。因此 OneUptime 通过DataSourceEgressGuard.assertUrlAllowedAndPin见 DataSource/EgressGuard校验目标地址并固定其解析后的 IP同时禁止重定向maxRedirects: 0——因为 IP 固定只覆盖被校验的那个主机如果放行 3xx 重定向就会绕过校验把请求引向未经验证的目标例如云元数据服务 IMDSv2。5.3 Agent 认证ID 密钥权威身份来自数据库Agent 的认证方式是ID 密钥secret key二者以环境变量的形式配置在 Agent 容器中。服务端侧Agent 的权威身份来自数据库中以所提交的 ID/密钥为键的那一行记录——这意味着即使某客户端拿到了一个 Agent 的密钥它也只能以该 Agent 的身份行事无法伪装成另一个 Agent。实现见 RunnerAuthorization.ts中间件从请求体或x-agent-id/x-agent-key请求头提取凭据缺少任一即返回BadDataException(agentId or agentKey is missing)随后调用RunnerService.findByIdAndKey按 ID密钥查库查不到则返回Invalid agentId or agentKey。身份校验通过后req.runner被赋值后续的认领与心跳接口都基于该身份工作。六、数据库表结构Runbook 功能背后涉及五张核心表Runbook——模板存储 Runbook 模板本身名称name、slug、描述、isEnabled、以及步骤 JSONsteps JSON其中包含各步骤的配置如 agentId、script、超时值等。RunbookExecution——一次运行一条记录每次执行一行带有可空的incidentId、alertId、scheduledMaintenanceId外键以及一个 JSON 类型的stepExecutions数组快照记录每个步骤及其实时状态。HTTP 步骤的完整状态/响应头/响应体也会被复制进stepExecutions供项目成员查看。RunbookRule——自动触发规则带triggerEntityType判别字段取值为 Incident / Alert / ScheduledMaintenance并与要启动的 Runbooks 构成多对多关系。这类规则是 Runbook 自动化的入口详见 rules.md。Runner——每个已安装 Agent 一行包含名称、密钥key、lastAlive最近心跳时间、connectionStatusConnected/Disconnected、hostInfo主机名、OS、架构等自报信息以及能力开关字段canRunRunbooks默认开启、canRunCodeFixTasks默认关闭、canRunAiCommands默认关闭开启后 AI 自动修复命令才可能在其上执行。表名保持Runner不变改名会牵扯所有外键与索引但产品名已统一为 OneUptime Runner。RunnerJob——每个被派发的 Bash/JavaScript 步骤一行关键字段包括targetAgentId——步骤作者选定的 Agent ID只有它能认领该作业stepType——步骤类型Bash 或 JavaScriptscript——要执行的脚本内容status——生命周期状态完整流转为Pending→Claimed→Running→Succeeded/Failed/TimedOut/CancelledclaimDeadlineAt——认领截止时间到期无人认领则 Worker 以TimedOut判定失败leaseExpiresAt——租约到期时间Agent 未按时心跳则 Worker 收回作业output——Agent 回传的合并 stdout/stderr服务端有 50 KB 上限exitCode——进程退出码超时时为空errorMessage——失败时的简短错误说明。从 RunnerJob.ts 的列定义看该表还支持origin字段Runbook 或 AiRemediation用于区分作业来源并据此决定 Runner 的认领能力门槛canRunRunbooks或canRunAiCommands。另外注意script虽在数据库层面 NOT NULL但 SSH/Kubernetes 这类“以 payload 携带结构化指令”的步骤其脚本为空是正常形态因此列元数据层面required为 false——脚本/载荷的按类型约束由RunnerJobService.enqueue统一执行。七、实战运维建议官方文档在结尾给出了三条重要的生产实践建议确保每个步骤所选 Agent 处于健康状态。如果需要冗余可以部署第二个 Agent 并把步骤分散到多个 Agent 上或者维护一个指向另一 Agent 的备用 Runbook。判断 Agent 健康状况的字段是Runner.lastAlive与connectionStatus二者均由 Agent 每次心跳更新。捕获 URL而不是数据块Capture URLs, not blobs。如果某个步骤会产生超过几 KB 的输出不要试图把大段文本塞回步骤输出——把产物写到 S3 或你的日志栈然后只返回对应的 URL。因为单步输出有 50 KB 的硬上限超出部分会被截断丢失。幂等性至关重要。自动化步骤HTTP、JavaScript、Bash在以下场景可能被执行多次Worker 在步骤中途重启、或 Agent 的租约在脚本仍在运行时过期。因此设计这些步骤时必须保证重复执行是安全的。关于第 3 点的实现细节源码里有一个很好的补充dispatchToAgent在重新入队前会先调用RunnerJobService.findLatestJobForStep查找该步骤已有的作业记录——如果已存在终态作业就直接采用其结果如果存在进行中的作业则“重新挂接re-attach”而非再次派发见 StepExecutors.ts。这是对“Worker 重启导致重复派发”的第一道防线但作为 Runbook 作者你仍应按“脚本可能跑两次”的前提来编写逻辑。结语OneUptime 的 Runbook 模块通过Agent 分发模型把最危险的脚本执行Bash/JavaScript从中心 Worker 隔离到自托管 Runner 上配合 50 KB 输出上限、双超时钳制、基于数据库权威身份的 ID密钥认证、以及isolated-vm沙箱等机制构建了一套适合生产事故响应场景的安全执行框架。结合本文对 StepExecutors.ts、RunbookStepTimeout.ts、RunnerJob.ts、Runner.ts 等源码的剖析你现在可以从执行模型、资源限制、权限矩阵、队列调度与数据模型五个层面完整理解 Runbook 的配置与安全语义。进一步的实操指引可继续阅读同一目录下的 agents.mdAgent 安装、authoring.md步骤编写、running.md执行与手动操作与 credentials.md凭据管理。【免费下载链接】oneuptimeComplete open-source monitoring and observability platform.项目地址: https://gitcode.com/GitHub_Trending/on/oneuptime创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考