icearp/disco-cli

GitHub: icearp/disco-cli

一款多云资源清点 CLI 工具,将 AWS、Azure、GCP 资源及关系图谱扫描至本地数据库,支持离线查询、图分析及 OPA 策略合规检查。

Stars: 0 | Forks: 0

# disco `disco` 是一个 CLI 工具,用于将 AWS、Azure 和 GCP 账户扫描并录入本地 SQLite 数据库,包括资源之间的关系。扫描完成后,你可以离线查询数据库:哪些 Lambda 读取了特定的 secret,某个 IAM role 附加到了哪里,哪些 RDS 集群共享了同一个 KMS key。专为需要完整资产清单的安全和合规工作而构建,而不是控制台搜索返回的局部切片。 ## 它的功能 - `scan` 会遍历 AWS 账户、Azure 订阅或 GCP 组织,并写入它发现的每一个资源。 - `scan` 还会运行一个解析阶段,使用具有特定类型的边(`contains`、`uses`、`attached-to`、`routes-to`、`assumes`、`peer`、`bounded-by`,以及 `cross-account-trust` / `cross-sub-rbac` / `cross-project-iam` / `org-iam`)将资源连接起来。 - `resources`、`diff`、`graph`、`check`、`findings`、`history`、`coverage`、`summary`、`tag-coverage`、`scans`、`snapshot` 和 `verify` 会在不回调云的情况下直接查询本地数据库。 ## 为什么不使用 Resource Explorer、Resource Graph 或 Cloud Asset Inventory? 这些服务很方便,但不完整。`disco` 直接调用每个云各自的 per-service SDK,因此那些被统一 API 忽略的内容 —— KMS grants、EFS mount targets、CloudFormation 管理的资源、IAM Identity Center assignments、Entra ID identities、GCP VPC Service Controls perimeters 等 —— 都会出现在图中。 ## 为什么选择 disco 扫描只需几分钟;对于包含数千个资源的数据库,查询可在亚秒级完成。云 API 仅在扫描时被请求,因此 `resources` / `graph` / `check` 在飞机上和在办公网络上的运行效果完全一致。 支持查询命令的输出格式包括 JSON、JSONL、CSV、SARIF、DOT 和 Mermaid。`resources -o json`、`graph complete -o json` 和 `check -o json` 在多次运行中会生成完全一致的字节流(相同的 SHA-256),这使得它们可以安全地进行提交、比对并接入 CI。`disco check --output sarif` 可以直接无缝接入 GitHub code scanning。`disco snapshot` 将数据库连同清单以及内部数据库哈希打包成一个单独的文件以便交接。`disco coverage services --check-strict` 会标记出 disco 尚未扫描的新云资源类型。 ## 安装 构建需要使用 Go。不需要 C 工具链:SQLite 驱动是纯 Go 实现的 (`modernc.org/sqlite`),因此需要设置 `CGO_ENABLED=0`。Makefile 已经设置好了此项。 ``` make build # → ./disco, version-stamped from `git describe` make all # fmt + vet + test + build make test # CGO_ENABLED=0 go test ./... make dist # cross-compile linux/darwin/windows amd64+arm64 into dist/ ``` 如果你跳过 Makefile,直接使用 `go build` 也可以,但版本标记会回退为 `dev`(`-X cmd.Version` ldflag 是由 `make` 注入的): ``` CGO_ENABLED=0 go build -o disco . ``` ## 命令 ``` disco ├── scan │ ├── aws per-account, --regions, --profile, --skip-globals │ ├── azure per-subscription via DefaultAzureCredential │ └── gcp per-project fan-out across reachable projects ├── resources filter by --type, --providers, --regions, --discovered-since │ └── show full record for one resolved resource ├── diff resources added / gone stale between two scan runs ├── history every recorded version of one resource, oldest→newest ├── graph │ ├── blast reachability from a seed │ ├── path shortest path between two resources │ └── complete full graph (every resource + edges) ├── check OPA Rego policy eval; --packs, --rules, --output sarif, --persist ├── findings query findings recorded by `check --persist` │ ├── list persisted findings (defaults to latest run) │ └── runs recorded check-run history ├── coverage scanner-declared types vs cloud registry; --check-strict ├── summary portfolio rollup ├── tag-coverage per-tag coverage rate ├── scans │ └── show detail for one recorded scan run ├── snapshot freeze DB into single-file archive (.zip|.tar.gz|.tar.xz) ├── verify verify snapshot manifest + inner-DB SHA-256 ├── config │ └── init write a boilerplate config.yaml └── completion shell completion script (bash|zsh|fish|powershell) ``` 每个命令都接受 `--db` / `--config` / `-v` 参数。`--db-readonly` 以只读模式打开数据库,并拒绝任何写入路径(例如 scan、对写入锁定的数据库进行 snapshot 等)。 ## 快速开始 ``` # 扫描 disco scan aws --profile myprofile --regions us-east-1,us-west-2 disco scan azure --services azure:compute,azure:network disco scan gcp --services gcp:compute,gcp:storage # 查询 disco resources --type aws:ec2:instance --regions us-east-1,us-west-2 disco resources show i-0abc123 -o json disco graph --kinds contains --depth 2 --output dot disco coverage services --providers aws # 策略检查;--rules 接受自定义的 Rego 目录 disco check --packs aws-waf --output sarif > findings.sarif # 证据存档;格式遵循扩展名 (.zip, .tar.gz, .tar.xz) disco snapshot evidence-2026-05-06.tar.xz disco verify evidence-2026-05-06.tar.xz ``` Azure 会扫描每一个可访问的订阅,GCP 会展开至所有可访问的项目。可以通过配置进行覆盖。资源类型为小写形式的 `cloud:service:kind`:`aws:ec2:instance`、`azure:compute:virtual-machine`、`gcp:compute:instance`。 ## 使用场景 该 CLI 围绕一些常见的重复性任务而设计。每个代码块都是一个经过测试的调用;可根据需要将其与 `jq` / `awk` / 你自己的脚本串联使用。 ### 资产组合汇总 ("我们拥有什么?") 按提供商、账户、区域和资源类型对整个资产进行单页统计,并附带来自最近一次扫描的时间戳。输出也支持 JSON 或 CSV 格式,方便用于制作幻灯片。 ``` disco summary disco summary -o json | jq '.by_account, .by_provider' ``` ### 用于成本分摊的标签合规评分卡 每个标签 key 的覆盖率 —— 这是实施 chargeback / showback 的前提。覆盖率为零的 key 依然会显示,以便仪表盘能够捕捉到缺失标签的信号。形如 AWS access-key IDs 的标签 KEYS 会被标记为 `[suspicious:aws-access-key-id]`,从而避免将误粘贴凭证到标签中的操作显示为正常的评分卡行。 ``` disco tag-coverage owner cost-center environment --case-insensitive disco tag-coverage --type aws:ec2:instance -o json | jq '.[] | select(.coverage<0.95)' ``` ### CI 策略门禁 (SARIF → GitHub code-scanning) 针对本地资源数据库进行 OPA Rego 评估;输出的 SARIF 2.1.0 可直接无缝接入 GitHub / GitLab / Sonar 的 code-scanning 录入。默认情况下,任何报告的发现都会将退出代码限定为 1 —— 将 `disco check` 直接接入 CI 步骤无需额外参数。对于仅清点资产且应发布发现结果而不中断流水线的运行,可使用 `--exit-zero` 退出。`partialFingerprints` 确保重复的发现能在多次运行中保持去重。可通过 `--rules ./policies/` 自带 Rego;内置的 `aws-waf` 是一个包含 5 条规则的示例包。 ``` disco check --packs aws-waf --severity high -o sarif > findings.sarif disco check --rules ./policies --tag waf_pillar=security -o table disco check --packs aws-waf --exit-zero -o sarif > inventory.sarif # render only ``` ### 被攻陷的 IAM principal 的影响范围 从种子节点进行 BFS 可达性分析。Partial-ID 查找意味着从工单 / CloudTrail 日志行粘贴过来的短 ID 能够被顺利解析。IAM principals 接收边而不是发射边,因此 `graph blast` 会针对它们自动扩展为 `--direction both`。 ``` disco graph blast 8895a0bd # short ID from a ticket disco graph blast my-role --provider aws --type aws:iam:role --depth 4 -o dot | dot -Tpng > blast.png ``` ### 扫描间的漂移检测 每次 `disco scan` 都会在 `scans` 中记录一行。`--scan-id latest` 会解析为触及数据行的最近一次扫描。`--scan-as discovered|verified|any` 决定过滤器针对的是哪个扫描外键(FK)列 —— `discovered` 用于查看“本次运行有哪些新增”,`verified` 用于查看“本次运行重新验证了哪些”。 ``` disco scans disco resources --scan-id latest --scan-as discovered disco resources --discovered-since 2026-04-01 -o json | jq 'length' ``` ### 证据包交接 (只读快照 + 签名验证) `disco snapshot` 将数据库冻结到一个单独的归档文件中(`.zip`、`.tar.gz`/`.tgz`、`.tar.xz`/`.txz`),其中包含携带内部数据库 SHA-256、生成时间戳以及扫描 ID 的清单。`--db-readonly` 确保源数据库不会被修改。`--signing-payload` 输出规范的清单字节流,以便外部签名者(`openssl pkeyutl -sign`、`minisign`、`ssh-keygen -Y sign`、cosign)进行签名。接收方通过 `disco verify --signature --pubkey` 验证分离的 ed25519 签名。 ``` # Producer disco --db-readonly snapshot /tmp/audit-2026-q2.tar.gz --signing-payload /tmp/audit.payload openssl pkeyutl -sign -inkey priv.pem -rawin -in /tmp/audit.payload -out /tmp/audit.sig # Auditor disco verify /tmp/audit-2026-q2.tar.gz --signature /tmp/audit.sig --pubkey ed25519.pem ``` ### 供应链 SBOMs 每个打了标签的发布版本都会为**每个二进制文件**提供一份两种格式的软件物料清单: `disco---.cdx.json` (CycloneDX 1.7) 和 `.spdx.json` (SPDX 2.3),并与 已有的 `.sha256` 附加文件放在一起。它们由 [syft](https://github.com/anchore/syft) 生成 数据源是二进制文件的 Go buildinfo(在压缩之前),因此它们准确列出了 链接到该二进制文件中的模块。可将 CycloneDX 文档输入 Dependency-Track / Grype,或将 SPDX 文档输入 许可证/合规工具。使用 `make sbom` 在本地生成相同内容(写入 `dist/`)。 ### 发布时的漏洞门禁 打了标签的发布受 [govulncheck](https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck) 限制: CI `test` 作业会针对与 disco 发布时完全相同的构建配置 (`-tags grpcnotrace`, `CGO_ENABLED=0`) 运行符号级可达性扫描,并且任何**可达的** 已知漏洞都会导致作业失败,从而确保不会创建草稿发布版本。漏洞数据库 ([vuln.go.dev](https://vuln.go.dev)) 在扫描时进行实时 查询。使用 `make vulncheck` 在本地运行相同的检查。 该扫描对新构建的二进制文件使用 `-mode binary`,而不是源代码模式 —— 源代码模式 需要跨 disco 约 496 个模块的依赖图(三个完整的云 SDK)构建全程序 SSA, 并且需要超过 23GB 的内存。二进制模式则读取符号表,在 8GB 内存下保持 相同的符号级精度。 ### CI 中的覆盖率漂移门禁 每当扫描器声明的类型列表与当前的云提供商注册表不一致时(CloudFormation `ListTypes` / Azure ARM `Providers/List` / GCP Discovery API),`coverage --check-strict` 就会以非零状态退出。与 `--resolvers --only-unannotated` 配合使用,可显示没有声明任何 `EdgeDecl` 的解析器 —— 这些正是用于弥补图表缺失的候选扫描目标。 ``` disco coverage services --check-strict --providers aws disco coverage resolvers --only-unannotated --providers aws -o json | jq '.[].resolver' ``` ### 在事件排查中查找孤立资源 `graph complete --orphans-only` 仅在返回集中保留零入/出边的节点 —— 可暴露出未挂载的 EBS volumes、没有被任何实例使用的 key-pairs、以及没有任何 group/policy 挂载的 IAM principals。 ``` disco graph complete --orphans-only -o json | jq -r '.nodes[].resource | [.type, .name, .native_id] | @tsv' ``` ## 配置 配置文件位于 `$XDG_CONFIG_HOME/disco/config.yaml` (Viper 格式)。在 Linux 上是 `~/.config/disco/config.yaml`;macOS 和 Windows 使用平台的应用程序数据目录。任何键都可以使用带有 `DISCO_` 前缀的环境变量进行覆盖。数据库默认路径为 `$XDG_DATA_HOME/disco/disco.db`(在 Linux 上为 `~/.local/share/disco/disco.db`);可使用 `--db` 或 `$DISCO_DB` 覆盖。 ## 工作原理 ``` cmd/.go → internal/providers// → store/ → sqlite (scanners then resolvers) (sqlx + squirrel) ``` 扫描器通过 `init()` 注册,并将数据行写入 `resources` 中,每个服务对应一个文件。解析器随后运行,读取这些行,并向 `relationships` 和 `hierarchy_closure` 中发射边。指向未扫描目标的边会被静默跳过,而不是导致扫描失败,因此局部扫描依然能生成可用的图。密钥在存储边界处 (`store/sanitize.go`) 会被进行脱敏处理。 每个子目录中的 `CLAUDE.md` 文件记录了本地的代码约定;`CODE_STRUCTURE.md` 则是更高层面的结构导览。 ## 覆盖范围 三大云平台均得到了广泛的覆盖 —— 通过每个云各自专属的 SDK 扫描了数百个服务, 涵盖计算、存储、网络、身份、数据、安全以及 治理领域。大致覆盖广度(当前运行的二进制文件所声明的不同资源类型)如下: | Provider | Services | Resource types | |----------|---------:|---------------:| | AWS | ~300 | ~1,800 | | Azure | ~150 | ~420 | | GCP | ~40 | ~250 | 随着服务的不断上线,这些列表也会处于动态变化中,因此 README 不会对其进行手工维护。若要查看你所构建的二进制文件中实时且 权威的列表,请运行: ``` disco coverage services --providers aws # or azure, gcp disco coverage services --filter uncovered # what each registry exposes that disco doesn't yet scan ``` 检测 disco 扫描器与上游云注册表之间的差异正是 `coverage` 命令存在的目的(参见上文的覆盖率门禁用例)。`FEATURES.md` 以文字形式详细列出了 已交付的功能特性。 ## 开发说明 ``` CGO_ENABLED=0 go test ./... CGO_ENABLED=0 go test ./internal/providers/aws/... -run TestSomething -v go vet ./... ``` 主开发分支是 `dev`。功能分支从 `dev` 拉出,并最终合并回 `dev`。 ## 发布 打了标签的 `v*` 推送会触发 `.forgejo` 发布工作流,该工作流会交叉编译并 发布适用于 amd64+arm64 架构下 linux/darwin/windows 平台的二进制文件。构建版本号 取自标签。你随时可以使用 `make build` 从源码进行构建。 ## 许可证 MIT — 详情请见 [`LICENSE`](LICENSE)。 ## 鸣谢 绝大多数代码库由 Claude (Anthropic) 在人工指导和审查下使用 [Claude Code](https://claude.com/claude-code) 编写。架构决策、范围界定以及最终的提交均由本人完成;而具体到代码行级别的工作 —— 扫描器、解析器、测试、边逻辑、文档 —— 绝大部分都出自 Claude 之手。提交记录上的联合作者尾缀真实反映了这一点。
标签:DevSecOps, EVTX分析, 上游代理, 云资源管理, 图数据库, 无线安全, 日志审计, 资产盘点