sauharddobhal/incident-runbook-automation

GitHub: sauharddobhal/incident-runbook-automation

一个 AWS 工作负载自动化框架,通过统一的安全防护层(标签选择加入、默认 dry-run、冷却去重、全局熔断开关)来安全地执行事件修复、定时调度、成本治理等六类运维操作。

Stars: 0 | Forks: 0

# incident-runbook-automation **六个 AWS 工作负载自动化处理程序(事件自动修复、定时 启动/停止、备份验证、安全自动修复、成本异常响应、 日志保留清理),全部通过一个共享的安全层路由:显式 按资源启用的标签选择加入、默认 dry-run 以及冷却去重。** 这是一个通用的参考实现,不与任何真实雇主的 基础设施、告警或账户绑定。本仓库中任何地方都没有使用来自任何实际环境的 真实资源 ID、账号或告警名称。 ## 为什么安全层置于六个处理程序之前 本仓库中的其他所有内容都遵循相同的结构:一条 EventBridge 规则或 SNS topic 触发一个 Lambda,该 Lambda 执行真实的、具有写入权限的 AWS 操作。这与 只读工具有着显著的区别,匹配逻辑中的错误或 意外扩大的告警可能会导致具有写入权限的代码针对 没人打算自动化的资源运行。三个独立的防护措施适用于每一个 处理程序,且不可豁免: 1. **显式启用标签。** 只有当资源带有 `AutomationEnabled=true` 时,才会对其执行操作。没有标签就意味着没有操作,无论 告警或发现结果怎么说。这是在一个地方(`common/safety.py`)强制执行的, 每个处理程序都通过它调用,而不是在每个处理程序中重新实现。 2. **默认 dry-run。** 每个处理程序都默认使用 `DRY_RUN=true`。一个操作只会被 记录和报告,永远不会执行,除非环境显式设置 `DRY_RUN=false`。该词的大小写变体("False"、"FALSE")也可以被接受;任何 不是该词本身的内容(拼写错误、"0"、"no"、未设置)都将保持安全。 3. **冷却去重。** 如果相同的(资源、操作)对已经在 可配置的时间窗口内触发过,则会被拒绝,因此摆动的告警或重复的 发现结果无法循环触发相同的修复。 针对每个执行或跳过的操作(无论是 dry-run 还是真实的),都会发送通知,因此始终 会留有审计追踪。 ### 超越单个处理程序的安全层 还有四个组件在所有六个处理程序中协同工作,而不是仅局限于任何一个: - **全局熔断开关。** 一个 SSM 参数 (`/incident-runbook-automation/kill-switch`),当设置为 `"true"` 时,强制每个 处理程序进入 dry-run 状态,而不管该处理程序自身的 `DRY_RUN` 设置如何,并且 无需重新部署。它的实现尽可能精准:每个处理程序的 Lambda 入口点会检查它一次,如果被激活,则在调用该处理程序已经过测试的 核心逻辑之前,将该次调用的 `DRY_RUN=true` 进行设置,而核心逻辑完全不知道 熔断开关的存在。参见 `common/kill_switch.py`。 - **持久化审计追踪。** 每个处理程序中的每次 `notifier.send()` 调用, 都会通过 `AuditingNotifier` 写入一条持久的 DynamoDB 记录,而不仅仅是短暂的 SNS 推送。它作为一个 `Notifier` 包装器实现,因此不需要更改任何处理程序的实际决策逻辑 就能实现这一点。参见 `common/audit.py`。 - **通知摘要。** 第七个定时处理程序 (`notification_digest`)会读取审计追踪并发送一份合并的总结 (按处理程序统计的操作、已执行/dry-run/已跳过的计数),而不是让你从一连串单独的按操作发送的邮件中去 还原“整体上发生了什么”。 - **交互式演示。** `python -m incident_runbook_automation demo` 会在四个(共六个)处理程序中运行示例 事件,包括熔断开关检查和摘要,所有这些 都针对模拟数据进行,无需任何 AWS 凭证。这个作品集中的所有其他仓库都有一个 零配置的 demo 命令;这个仓库在添加此功能之前只有 pytest。 - **故障处理。** 每个处理程序都有一个指向其自身 SQS 死信队列的异步调用失败目标, 外加一个基于其错误率的可选 CloudWatch 告警 (在调用 `lambda-automation` 模块时设置 `alarm_topic_arn`,在 `environments/example` 中已经连接到 共享的通知 topic)。如果没有这个,处理程序内部未处理的异常 除了在 CloudWatch Logs 中之外会静默失败,这比 自动化根本不存在还要糟糕,因为你会信任它正在运行,而实际上并没有。 - **预检报告。** `python -m incident_runbook_automation preflight` 会查询你 真实的 AWS 账户(与 `demo` 不同,需要真实凭证),并准确显示 `scheduled_start_stop`、`cost_anomaly_response` 和 `log_retention_cleanup` 将对哪些当前已标记的资源 执行操作。在此明确声明而不是含糊带过: 这仅涵盖那三个处理程序。`auto_remediation`、`backup_verification` 和 `security_remediation` 是事件驱动的,它们要操作的资源是任何 触发告警或发现结果的事物,这是无法提前预知或列出的。 ## 架构 ``` flowchart TD A1[CloudWatch Alarm] --> B1[EventBridge] --> C1[auto_remediation] A2[Schedule] --> B2[EventBridge] --> C2[scheduled_start_stop] A3[Step Functions] --> C3[backup_verification] A4[GuardDuty Finding] --> B4[EventBridge] --> C4[security_remediation] A5[Cost Anomaly Detection] --> B5[SNS] --> C5[cost_anomaly_response] A6[Schedule] --> B6[EventBridge] --> C6[log_retention_cleanup] C1 --> S[Safety layer
opt-in tag + dry-run + cooldown + kill switch] C2 --> S C3 --> S C4 --> S C5 --> S C6 --> S S --> N[AuditingNotifier] N --> SNS[SNS notification
every action, real or dry-run] N --> AUD[(DynamoDB audit trail)] AUD --> C7[notification_digest] C7 --> SNS C1 -. on unhandled exception .-> DLQ[(SQS dead-letter queue,
one per function)] C2 -.-> DLQ C3 -.-> DLQ C4 -.-> DLQ C5 -.-> DLQ C6 -.-> DLQ DLQ -.-> ALARM[CloudWatch error-rate alarm] ALARM -.-> SNS ``` ### 处理程序 | 处理程序 | 触发器 | 操作 | |---|---|---| | `auto_remediation` | 通过 EventBridge 的 CloudWatch 告警状态变更 | 根据告警名称前缀,强制执行 ECS 重新部署、ASG 实例刷新或 RDS 故障转移 | | `scheduled_start_stop` | 两个 EventBridge 定时计划(停止时间、启动时间) | 停止/启动已标记的非生产环境 EC2 和 RDS 资源 | | `backup_verification` | Step Functions(见下文) | 创建快照,还原到临时实例,确认其可恢复,然后销毁 | | `security_remediation` | 通过 EventBridge 的 GuardDuty 发现结果 | 撤销公共入站规则,或将实例隔离到隔离安全组中(原始组会被保存,可通过一次调用恢复,见下文) | | `cost_anomaly_response` | SNS (Cost Anomaly Detection) | 发送丰富的通知;在超过 $ 阈值时,可选择停止预先标记的非关键资源 | | `log_retention_cleanup` | EventBridge 定时计划 | 为没有保留策略的已标记日志组设置保留策略 | | `notification_digest` | EventBridge 定时计划 | 读取审计追踪,发送一份合并的总结,而不是针对单个操作的通知 | ## 什么是真实的模式,什么是为本作品集简化的内容 **真实的 / 代表真实生产模式:** - 将每个处理程序通过一个共享的启用/dry-run/冷却层进行路由,而不是 让每个处理程序实现自己的安全检查,这是构建一套 自动化处理程序的正确方式,人们最终会信任它并将其指向 生产环境。 - 将安全修复的范围限制在仅可逆操作(撤销规则,替换 安全组)且永不进行终止/删除,这反映了它实际上应该如何被设计:针对可能是误报的 发现结果的自动响应,绝不应执行人类无法 撤消的操作。“可逆”有实际的恢复路径作为支撑:`security_remediation` 在隔离实例之前将其原始安全组保存到一个小型 DynamoDB 表中,将其包含在通知中,并且 `process_restore()` 可通过一次调用撤消隔离状态,而不是事后从 CloudTrail 日志中重建 原始组。 - 使用 Resource Groups Tagging API 通过一次调用即可 跨 EC2 和 RDS 查找带有特定标签的资源(scheduled_start_stop、cost_anomaly_response),而不是为每个服务编写单独的 标签匹配逻辑,这就是在规模化实践中完成此任务的方法。 **为作品集目的而简化:** - `backup_verification` 被拆分为多个离散阶段(create_snapshot、 initiate_restore、check_restore_status、cleanup),特别是因为真实的 RDS 恢复通常需要 10-30 分钟以上,远超单个 Lambda 调用的 15 分钟上限。此仓库为每个阶段配置了 Lambda 函数;围绕它们配置具有适当等待/重试间隔的 Step Functions 状态机的工作留给了任何部署它的人, 而不是在这里伪装成比实际更自动化的样子。 - `backup_verification` 确认还原后的实例达到 `available` 状态(它是 可恢复的),而不是应用程序可以完全连接到它(针对还原后的 数据库进行真实查询)。真实的连接性验证需要 VPC 访问权限和来自 Lambda 内部的 数据库凭证,这是本仓库未采取的合理下一步。 - `auto_remediation` 的告警到资源映射是一种命名约定(告警名称 编码了目标资源),而不是更复杂的查找方式。这很简单并且 完全可以通过阅读告警定义进行审计,但这确实意味着告警名称是承重 配置,而不仅仅是标签。 - `cost_anomaly_response` 不会尝试识别导致异常的“那个”特定资源。AWS Cost Anomaly Detection 的根本原因数据是服务/账户级别的,而不是单个资源级别的;根据该数据猜测 特定的资源,恰恰是本仓库的安全理念所反对的那种 虚假自信。自动停止路径仅对在匹配服务中响应成本异常而明确预先标记为可安全停止的资源执行操作。 ## 技术栈 `Python 3.12` · `AWS Lambda` · `Amazon EventBridge` · `Amazon SNS` · `Amazon DynamoDB` · `AWS Step Functions`(用于 backup_verification 编排) · `Terraform >= 1.5` · `boto3` ## 设置 ``` git clone https://github.com/sauharddobhal/incident-runbook-automation.git cd incident-runbook-automation python -m venv .venv && source .venv/bin/activate pip install -r requirements.txt ``` ### 运行演示(无需 AWS 凭证) ``` python -m incident_runbook_automation demo ``` 针对模拟数据运行 auto_remediation、scheduled_start_stop、security_remediation 和 log_retention_cleanup 的示例事件,检查熔断开关,然后打印一份摘要, 总结发生的所有事情。 ### 运行预检报告(需要真实的 AWS 凭证) ``` python -m incident_runbook_automation preflight ``` 在将 `dry_run` 切换为 `false` 之前,针对你的真实账户运行此命令。显示 `scheduled_start_stop`、`cost_anomaly_response` 和 `log_retention_cleanup` 当前将操作的对象; 其他三个处理程序是事件驱动的,无法提前列出,有关原因请参见上文的 “超越单个处理程序的安全层”。 ### 运行测试 ``` pytest tests/ ``` 所有 106 个测试都针对注入的模拟数据(模拟的 boto3 客户端,内存中的冷却 存储)运行,不需要真实的 AWS 访问权限或凭证。 ### 部署 ``` cd terraform/environments/example cp terraform.tfvars.example terraform.tfvars # fill in your VPC ID, notification email cp backend.tf.example backend.tf # point at your own state backend terraform init terraform plan ``` 保持 `dry_run = "true"`(默认值),直到你审查了每个处理程序发送的 dry-run 通知, 并确信它实际会执行什么操作。然后,慎重地 将其设置为 `"false"`。 **通过标记资源选择启用:** 在你添加 `AutomationEnabled=true` 之前,任何资源都不会发生任何事情。`scheduled_start_stop` 额外需要 `Schedule=office-hours`;`cost_anomaly_response` 的自动停止路径额外需要 `CostAnomalyAction=auto-stop` 和 `CostAnomalyService=<受影响的服务名称>`。 ### 在没有 AWS 凭证的情况下验证 Terraform ``` terraform fmt -check -recursive terraform validate # requires `terraform init` to have completed ``` ## 许可证 MIT,参见 `LICENSE`。
标签:AWS, DPI, 安全运营, 扫描框架, 模块化设计, 自动化修复, 运维自动化, 逆向工具