ChrisInvictus/CloudCanary
GitHub: ChrisInvictus/CloudCanary
CloudCanary 是一款无 agent 的 GCP 资产与身份漂移检测工具,通过定期快照对比将所有云资源配置变更在 30 分钟内推送至 Slack,帮助安全团队在没有 SIEM 的阶段以接近零成本建立检测能力。
Stars: 0 | Forks: 0
# CloudCanary 🐤
 
**针对 GCP 的身份漂移检测 —— 谁创建了密钥,谁获取了角色,发生了什么变更。30 分钟内呈现在你的 Slack 中。**
**概览:** 身份漂移、service-account 密钥创建、权限变更、公开暴露以及 AI/ML 服务启用 —— 以 30 分钟为周期进行无 agent 检测,并向 Slack 发送警报,成本几乎为零 · 架构上坚持最小权限原则(canary 本身通过 WIF 实现无密钥运行) · 防御部分:[paved-org](https://github.com/ChrisInvictus/paved-org)。
用于 Google Cloud 的轻量级、无 agent 变更检测 canary。CloudCanary
定期扫描你的 GCP 组织,将实时的资源清单与
上次已知状态进行对比,并将所有差异发布到 Slack —— 这样你的安全团队就能在
新 VM、防火墙规则、存储桶、service account、**service-account 密钥**
和 **IAM 权限变更**发生几分钟后看到它们。
这是我作为第一位安全雇员在生产环境中运行的工具:在
组织拥有 SIEM 预算之前,它就是检测层。作为
零成本、零 agent 的绊线,它在与更重型的工具并行(或超前)使用时依然非常有用。
CloudCanary 是一对工具中的检测部分:paved-org 是防御部分。
这个组织基线使得大多数此类恶劣事件从一开始就无法发生。
## 监控内容
| 信号 | 重要性 |
|---|---|
| 项目创建/删除(全组织) | 影子基础设施,未经授权的环境 |
| Compute 实例 | 挖矿劫持,恶意工作负载 |
| 防火墙规则 | 暴露变更,数据外泄路径 |
| 预留地址 | 新的入口/出口攻击面 |
| GCS 存储桶 | 数据暴露面 |
| Service account | 进入资产范围的新身份 |
| **用户管理的 SA 密钥** | 长效凭证创建 —— 顶级的 GCP 凭证泄露途径 |
| **针对每个 SA 的 IAM 角色绑定** | 每个身份的权限提升 / 漂移 |
| **针对所有 principal 的 IAM 绑定** | 某个人或组悄无声息地获得了 Editor/Owner 权限 |
| **已启用的 API** | 作为早期持久化 / 侦察信号的服务启用 |
| **对互联网开放的防火墙规则** | `0.0.0.0/0` 入站 —— 资产安全态势警报,而不仅仅是漂移 |
| **公开的存储桶** | 授予 `allUsers` / `allAuthenticatedUsers` 权限 —— 数据暴露 |
| **AI/ML API 启用** | 启用了 Vertex / Gemini —— 每一种其他 AI 风险前的 AI 采用先兆 |
| **`aiplatform.*` 角色授权** | Agent 身份权限(Excessive Agency, LLM06)—— AI 工作负载能做什么 |
| **公开的模型 endpoint** | Vertex endpoint 上的 `allUsers` 权限 —— 模型服务暴露在组织之外 |
最后两点是关键所在:大多数“云 canary”监控的是计算资源。CloudCanary
将**身份视为主要的攻击面**。
AI 工作负载也是通过同样的身份视角进行监控的:AI API 是一个
启用事件,`aiplatform.*` 授权是一个权限事件,而公开的
endpoint 是一个暴露事件。请参阅
[paved-org](https://github.com/ChrisInvictus/paved-org/blob/main/docs/threat-models/mcp-trust-boundaries.md) 中的相关威胁模型。
## 工作原理
```
Jenkins (every 30 min)
└── cloudcanary.sh
├── gcloud enumerates projects → resources → identities
├── diff vs. state dir (last-known inventory)
└── deltas → Slack webhook (security channel)
```
除了纯文本状态目录外,设计上无状态 —— 没有数据库,
没有 agent,除了只读 API 调用外,云端没有任何痕迹。
## 设置
1. **Canary 的身份。** 创建一个专用的 service account 并赋予
只读角色 —— `roles/browser`、`roles/compute.viewer`、
`roles/iam.securityReviewer`、`roles/storage.objectViewer`(存储桶
列表)和 `roles/aiplatform.viewer`(Vertex endpoint / IAM 读取)。在 runner 上首选使用 Workload Identity / 附加的 SA,而不是
导出的密钥文件。(如果一个用于检测的工具本身需要长效密钥,
那它就是在暴露自身。)
2. **Slack webhook。** 为你的安全频道创建一个传入的 webhook
并将其存储为 secret —— 在提供的 pipeline 中作为 Jenkins Secret Text 凭证
`cloudcanary-slack-webhook`。它永远不会被
提交到代码库中。
3. **配置。** 复制 `config.example.env`,进行调整,或者在你的调度器中设置相同的
变量:
| 变量 | 默认值 | 用途 |
|---|---|---|
| `SLACK_WEBHOOK_URL` | *(必填)* | 差异通知 |
| `STATE_DIR` | `~/.cloudcanary/state` | 上次已知的清单 |
| `PROJECT_INCLUDE_FILTER` | *(所有可见项)* | `gcloud --filter` 作用域 |
| `PROJECT_EXCLUDE_REGEX` | *(无)* | 跳过嘈杂/沙箱项目 |
| `WATCH_SA_KEYS` | `true` | 密钥创建检测 |
| `WATCH_SA_ROLES` | `true` | 权限漂移检测 |
| `DRY_RUN` | `false` | 仅记录差异但不发送 |
4. **调度它。** `Jenkinsfile` 每 30 分钟运行一次
(`cron('H/30 * * * *')`),并禁用并发(状态文件不支持
并发安全)。原生的 cron 或 Cloud Scheduler + Cloud Run 任务的工作方式
完全相同。
首次运行会播种状态并保持静默;从第二次运行开始才会发送漂移警报。
## 本仓库的安全实践
- **代码中无 secret。** Webhook 在运行时从凭证
存储中注入;如果没有它,脚本将拒绝启动。
- **防注入通知。** Slack payload 使用 `jq` 构建,而不是
字符串插值。
- **最小权限。** 只读查看者角色;canary 无法
对资产进行任何修改。
- **高调报错。** Pipeline 失败时会发布自己的警报 —— 死掉的 canary 就是
检测盲区,并且它会明确告诉你这一点。
- **`set -euo pipefail`**,加引号的变量展开,以及单次调用隔离错误,
这样一个抖动的 API 就不会悄无声息地跳过某个项目。
## 为什么不用 Security Command Center / CNAPP?
如果你能负担得起,就去用它们。SCC Premium 和 Wiz 级别的平台是预算充足时的正确
选择。CloudCanary 的存在是为了填补它们未覆盖的阶段和缺口:
- **预算建立前:** 它最初是一个没有 SIEM
或 CNAPP 支出的组织的检测层。免费,无 agent,30 分钟内即可运行。
- **独立的故障模式:** 一个不与你的主要工具共享凭证、
pipeline 或供应商的绊线,当那些
工具配置错误、欠费或被入侵时,它依然会触发。
- **演进路径:** 当 CNAPP 上线后,canary 并不会退役 ——
它会变成监控那些监控者的工具。
- **防御部分:paved-org**
最强烈的警报是永远不会触发的警报。paved-org
是本仓库的姊妹项目 - 一个以代码形式实现的 GCP 组织基线,其
第一个组织策略 iam.disableServiceAccountKeyCreation 就阻止了
CloudCanary 最想检测的确切事件。
两者都用:用防御应对已知风险,用检测应对配置漂移。
护栏拦截你能预测到的;
canary 抓住你未能预测到的。
## 局限性(坦诚说明)
- 轮询,而非事件驱动:检测延迟 = 调度间隔。如果需要
实时性,请配合使用 Cloud Asset Inventory feeds / Audit Log sinks;将
CloudCanary 保留为不共享它们故障
模式的独立绊线。
- 基于名称级别的对比:重命名会显示为删除+创建。
- 状态保存在 runner 上:请将任务固定在单个 agent 上,或将状态迁移到
存储桶中。
## 按你的方式进行部署
- **Jenkins** —— 包含 `Jenkinsfile`(30 分钟 cron,绑定凭证的 webhook)
- **GitHub Actions** —— `.github/workflows/canary.yml`,通过 Workload
Identity Federation (OIDC) 进行无密钥认证,状态通过缓存持久化
- **Terraform** —— `terraform/` 将 canary 自身的身份配置为
安全默认的蓝图:专用的只读 service account,仅限组织级
查看者角色,可选的 WIF 绑定,因此**永远不会导出密钥**。
检测工具永远不应依赖于它旨在检测的凭证类型。
CI 会在每次推送时运行 shellcheck(`.github/workflows/lint.yml`)。
## License
MIT
标签:GCP, HTTP/HTTPS抓包, 告警通知, 态势感知, 漂移检测