<cite>
**本文引用的文件列表**
- [main.go](file://cmd/execgo/main.go)
- [shell.go](file://internal/executor/shell.go)
- [executor.go](file://internal/executor/executor.go)
- [http.go](file://internal/executor/http.go)
- [file.go](file://internal/executor/file.go)
- [task.go](file://internal/models/task.go)
- [handler.go](file://internal/api/handler.go)
- [scheduler.go](file://internal/scheduler/scheduler.go)
- [config.go](file://internal/config/config.go)
- [README.md](file://README.md)
</cite>
目录
简介
本文件聚焦于 Shell 执行器的参数规范与安全机制,围绕以下目标展开:
- 参数结构:执行命令字符串、工作目录设置、参数数组、超时控制等
- 安全机制:命令白名单配置与管理、参数转义与注入防护、执行沙箱环境限制
- 安全实践:安全的命令编写示例、常见风险与防范措施
- 参数验证与执行前安全检查:校验规则与前置检查流程
项目结构
ExecGo 采用分层架构,Shell 执行器作为内置执行器之一,通过统一的执行器注册表接入调度与状态管理。关键模块如下:
- 入口与配置:cmd/execgo/main.go、internal/config/config.go
- 执行器接口与注册表:internal/executor/executor.go
- Shell 执行器:internal/executor/shell.go
- HTTP 执行器与文件执行器:internal/executor/http.go、internal/executor/file.go
- 任务模型与校验:internal/models/task.go
- API 层:internal/api/handler.go
- 调度器:internal/scheduler/scheduler.go
graph TB
A["入口 main.go<br/>初始化配置/注册执行器/启动 HTTP 服务"] --> B["配置 config.go<br/>加载运行参数"]
A --> C["执行器注册表 executor.go<br/>RegisterBuiltins()"]
C --> D["Shell 执行器 shell.go<br/>Type()/Execute()"]
C --> E["HTTP 执行器 http.go<br/>Type()/Execute()"]
C --> F["文件执行器 file.go<br/>Type()/Execute()"]
A --> G["API 层 handler.go<br/>路由与请求处理"]
G --> H["调度器 scheduler.go<br/>DAG 调度/并发/超时/重试"]
H --> D
H --> E
H --> F
H --> I["状态管理 state.go<br/>内存+持久化"]
H --> J["可观测性 observability.go<br/>日志/指标/追踪"]
图表来源
- main.go:25-104
- config.go:18-47
- executor.go:62-67
- shell.go:34-79
- http.go:27-75
- file.go:25-113
- handler.go:39-99
- scheduler.go:47-190
章节来源
- main.go:25-104
- README.md:149-177
核心组件
- 执行器接口与注册表:定义统一的执行器接口与全局注册表,支持按类型获取执行器实例,并在启动时注册内置执行器。
- Shell 执行器:实现 Type() 与 Execute(),负责解析 ShellParams、执行白名单校验、构造命令并运行。
- 任务模型:Task 结构包含 id、type、params、depends_on、retry、timeout、status 等字段;TaskGraph 提供校验与拓扑检测。
- API 层:接收任务提交请求,进行 JSON 解码与任务图校验,随后提交给调度器。
- 调度器:基于 DAG 的并发调度器,支持超时控制与指数退避重试。
章节来源
- executor.go:14-67
- shell.go:24-79
- task.go:21-79
- handler.go:58-99
- scheduler.go:18-190
架构总览
下图展示 Shell 执行器在整体系统中的位置与交互流程。
sequenceDiagram
participant Client as "客户端"
participant API as "API 层 handler.go"
participant Sched as "调度器 scheduler.go"
participant Reg as "执行器注册表 executor.go"
participant Shell as "Shell 执行器 shell.go"
participant OS as "操作系统进程"
Client->>API : "POST /tasks 提交任务图"
API->>API : "解码/校验 TaskGraph"
API->>Sched : "Submit(graph)"
Sched->>Reg : "Get(task.Type)"
Reg-->>Sched : "返回 ShellExecutor"
Sched->>Shell : "Execute(ctx, task)"
Shell->>Shell : "解析 ShellParams/白名单校验"
Shell->>OS : "exec.CommandContext(...).Run()"
OS-->>Shell : "stdout/stderr/退出码"
Shell-->>Sched : "返回结果或错误"
Sched-->>API : "更新状态/记录指标"
API-->>Client : "202 Accepted + 任务 ID 列表"
图表来源
- handler.go:58-99
- scheduler.go:127-190
- executor.go:38-48
- shell.go:36-79
详细组件分析
Shell 执行器参数规范
- 参数结构
- command:必填,要执行的基础命令名称或绝对/相对路径
- args:可选,命令参数数组
- dir:可选,工作目录
- 执行流程
- 解析 JSON 参数为 ShellParams
- 校验 command 非空
- 从 command 中提取基础命令名(去除路径部分)
- 在白名单中查找基础命令
- 构造 exec.CommandContext,设置 Dir(若提供)
- 运行命令并收集 stdout、stderr、退出码
- 返回结果或错误信息
flowchart TD
Start(["进入 Execute"]) --> Parse["解析 JSON 为 ShellParams"]
Parse --> CheckCmd{"command 是否为空?"}
CheckCmd --> |是| ErrCmd["返回错误:command 为空"]
CheckCmd --> |否| Base["提取基础命令名去路径"]
Base --> Whitelist{"基础命令在白名单中?"}
Whitelist --> |否| ErrWL["返回错误:不在白名单"]
Whitelist --> |是| Build["构建 exec.CommandContext"]
Build --> Run["运行命令并收集输出"]
Run --> Result["封装结果:stdout/stderr/退出码"]
Result --> End(["返回结果"])
ErrCmd --> End
ErrWL --> End
图表来源
- shell.go:36-79
章节来源
- shell.go:24-29
- shell.go:36-79
- README.md:202-208
命令白名单配置与管理
- 白名单定义:在 Shell 执行器内部维护允许执行的命令集合,覆盖常用系统工具与跨平台命令。
- 校验策略:仅对命令的基础名称进行匹配,避免路径绕过;不直接执行用户提供的完整命令路径。
- 管理方式:白名单集中维护,便于审计与更新;新增命令需评估安全影响后纳入。
classDiagram
class ShellExecutor {
+Type() string
+Execute(ctx, task) (json.RawMessage, error)
}
class ShellParams {
+string command
+[]string args
+string dir
}
ShellExecutor --> ShellParams : "解析/使用"
图表来源
- shell.go:32-29
章节来源
- shell.go:14-22
- shell.go:46-54
参数转义与注入防护
- 命令解析:Shell 执行器将参数拆分为 command 与 args 数组,避免将用户输入拼接为 shell 字符串导致的注入风险。
- 路径提取:从 command 中剥离路径,仅以基础命令名进行白名单匹配,降低路径绕过风险。
- 环境变量:当前实现未显式传入环境变量,避免污染执行上下文;如需扩展,建议通过独立字段显式传递并进行白名单过滤。
章节来源
- shell.go:36-59
执行沙箱环境限制
- 工作目录:可通过 dir 字段指定工作目录,限制命令执行范围。
- 超时控制:调度器根据任务的 timeout 字段为每次执行创建带超时的 context,避免长时间阻塞。
- 并发与资源:调度器通过信号量控制最大并发,防止资源耗尽。
- 输出限制:当前未对 stdout/stderr 设置大小限制,建议在需要时增加限制以防止内存占用过高。
章节来源
- shell.go:57-59
- scheduler.go:163-173
- scheduler.go:41
参数验证规则与执行前安全检查
- 任务图校验:确保任务 ID 唯一、类型非空、依赖引用合法且无环。
- 执行器可用性:提交阶段检查任务类型是否存在对应执行器。
- Shell 执行器校验:command 非空、基础命令在白名单中。
- 超时与重试:调度器按任务 timeout 与 retry 配置执行带超时与指数退避的多次尝试。
flowchart TD
TStart(["提交任务图"]) --> Decode["解码 JSON"]
Decode --> GraphV["校验 TaskGraph"]
GraphV --> TypesOK{"所有任务类型存在执行器?"}
TypesOK --> |否| Fail["返回错误:未知类型"]
TypesOK --> |是| Enqueue["提交到调度器"]
Enqueue --> Exec["调度器执行"]
Exec --> Timeout["构建带超时的 context"]
Timeout --> ShellCheck["Shell 执行器:校验 command/白名单"]
ShellCheck --> Run["执行命令"]
Run --> Result["返回结果/错误"]
图表来源
- handler.go:63-99
- task.go:41-79
- scheduler.go:127-190
- shell.go:42-54
章节来源
- handler.go:63-99
- task.go:41-79
- scheduler.go:127-190
- shell.go:42-54
安全的命令编写示例与常见风险防范
- 示例参考:README 中提供了 Shell 执行器的参数示例,包括 command、args、dir 等字段。
- 风险与防范:
- 不要在 command 中拼接用户输入;使用 args 数组传递参数
- 仅使用白名单中的命令
- 通过 dir 限定工作目录,避免路径穿越
- 为任务设置合理的 timeout,防止长时间阻塞
- 如需跨平台兼容,注意 Windows 常用命令的差异
章节来源
- README.md:202-208
- shell.go:14-22
依赖关系分析
- 执行器注册表:统一管理执行器类型与实例,ShellExecutor 通过 Type() 与 Execute() 接口参与调度。
- API 层:负责请求解码、任务图校验与执行器可用性检查。
- 调度器:负责并发控制、超时与重试、状态更新与下游级联。
graph LR
API["API 层 handler.go"] --> REG["执行器注册表 executor.go"]
REG --> SHELL["Shell 执行器 shell.go"]
API --> SCH["调度器 scheduler.go"]
SCH --> SHELL
SCH --> HTTP["HTTP 执行器 http.go"]
SCH --> FILE["文件执行器 file.go"]
图表来源
- executor.go:38-48
- handler.go:76-85
- scheduler.go:127-190
- http.go:27-75
- file.go:25-113
章节来源
- executor.go:38-48
- handler.go:76-85
- scheduler.go:127-190
性能考量
- 并发控制:调度器通过固定容量的信号量限制最大并发,避免资源争用。
- 超时与重试:为每个执行尝试设置超时,失败时按指数退避重试,减少抖动。
- 输出读取:当前未对 stdout/stderr 设置大小限制,建议在需要时增加限制以控制内存占用。
- I/O 优化:Shell 执行器直接使用缓冲区收集输出,避免额外拷贝。
章节来源
- scheduler.go:41
- scheduler.go:152-179
- shell.go:61-64
故障排查指南
- 常见错误
- 任务类型未知:API 层会返回可用执行器列表,确认任务 type 是否正确
- 任务图校验失败:检查任务 ID、依赖引用与环路
- Shell 执行器:command 为空或不在白名单
- 执行超时:调整任务 timeout 或优化命令
- 排查步骤
- 查看健康与指标端点确认服务状态
- 通过查询任务详情定位具体失败原因
- 检查调度器日志与执行器返回结果
章节来源
- handler.go:76-85
- task.go:41-79
- shell.go:42-54
- scheduler.go:152-179
结论
Shell 执行器通过严格的白名单机制、参数数组化传递与超时控制,有效降低了命令注入与资源滥用的风险。结合调度器的并发与重试策略,能够在保证安全性的同时提升任务执行的可靠性。建议在生产环境中:
- 明确白名单范围并定期审计
- 对输出进行大小限制
- 为关键任务设置合理超时与重试
- 通过可观测性端点持续监控执行状态
附录
- 配置项
- addr:HTTP 监听地址
- data-dir:数据持久化目录
- max-concurrency:最大并发数
- shutdown-timeout:优雅关闭超时(秒)
章节来源
- config.go:18-47
- main.go:25-104