ruzickap/container-image-scans

GitHub: ruzickap/container-image-scans

一个容器镜像 CVE 对比扫描平台,通过 Trivy 和 Grype 双引擎分析多种基础镜像变体的安全状况,结果存储于 Supabase 并通过 Next.js 仪表板可视化展示历史趋势。

Stars: 0 | Forks: 0

# 容器镜像扫描 使用 [trivy](https://trivy.dev/) 和 [grype](https://github.com/anchore/grype) 扫描器对比容器镜像间的 CVE 发现结果。结果存储在 [Supabase](https://supabase.com/) 中, 并在部署于 GitHub Pages 的 Next.js 仪表板中进行可视化。 ## 目录 - [架构](#architecture) - [容器镜像](#container-images) - [前置条件](#prerequisites) - [Supabase 设置](#supabase-setup) - [GitHub Secrets](#github-secrets) - [本地开发](#local-development) - [GitHub Actions](#github-actions) - [项目结构](#project-structure) ## 架构 ``` flowchart TD GHA["GitHub Actions\n(nightly cron)"] TRIVY["trivy"] GRYPE["grype"] DB["Supabase (PostgreSQL)"] WEB["Next.js Dashboard (GitHub Pages)"] GHA --> TRIVY GHA --> GRYPE TRIVY -- "SARIF + metadata" --> DB GRYPE -- "SARIF + metadata" --> DB DB -- "anon key (read)" --> WEB ``` ## 容器镜像 镜像按组进行组织: | Group | Image | |--------|--------------------------------------------------------------| | python | `docker.io/python:alpine` | | python | `gcr.io/distroless/python3-debian12:latest` | | python | `cgr.dev/chainguard/python:latest` | | python | `registry.access.redhat.com/ubi10/python-312-minimal:latest` | | node | `docker.io/node:24-alpine` | | node | `gcr.io/distroless/nodejs24-debian12:latest` | | node | `cgr.dev/chainguard/node:latest` | | node | `registry.access.redhat.com/ubi10/nodejs-24-minimal:latest` | | php | `docker.io/php:alpine` | | php | `cgr.dev/chainguard/php:latest` | | php | `registry.access.redhat.com/ubi10/php-83:latest` | | nginx | `docker.io/nginx:alpine` | | nginx | `cgr.dev/chainguard/nginx:latest` | | nginx | `registry.access.redhat.com/ubi10/nginx-126:latest` | | ruby | `docker.io/ruby:alpine` | | ruby | `cgr.dev/chainguard/ruby:latest` | | ruby | `registry.access.redhat.com/ubi10/ruby-33:latest` | | base | `docker.io/alpine:latest` | | base | `docker.io/debian:stable-slim` | | base | `docker.io/ubuntu:latest` | | base | `gcr.io/distroless/static:latest` | | base | `registry.access.redhat.com/ubi10-micro:latest` | 要添加更多镜像,请向 `container_images` 表中插入行 并指定相应的 `group_id`。夜间扫描会自动获取所有镜像。 ## 前置条件 - [mise](https://mise.jdx.dev/) (`curl https://mise.run | sh`) -- 通过 `.mise.toml` 管理 Node.js, Supabase CLI, sops, yq 和 lint 工具 - [sops](https://github.com/getsops/sops) + [age](https://github.com/FiloSottile/age) -- 用于加密 `.env.yaml` 中的环境变量(由 mise 自动安装) - 一个 [Supabase](https://supabase.com/) 项目(免费层即可) - [Docker](https://www.docker.com/)(用于在本地运行扫描) 克隆仓库后,运行 `mise install` 以安装 `.mise.toml` 中定义的版本所需的所有工具。 ## Supabase 设置 ### 1. 创建 Supabase 项目 在 [supabase.com](https://supabase.com/) 注册并创建一个新 项目。记录下 `Settings > API` 中的 **Project URL** 和 **API keys**。 ### 2. 应用数据库迁移 运行 `db:push` mise 任务,它会将 CLI 链接到你的项目并 推送 `supabase/migrations/` 中所有待处理的迁移: ``` export SUPABASE_ACCESS_TOKEN="your-access-token" export SUPABASE_PROJECT_REF="your-project-ref" export SUPABASE_DB_PASSWORD="your-db-password" mise run db:push ``` 上面的三个变量也可以加密存储在 `.env.yaml` 中 (参见 GitHub Secrets 部分中的 `MISE_SOPS_AGE_KEY` secret)。 或者,通过 Supabase Dashboard 中的 **SQL Editor** 手动应用 schema: 1. 在 Supabase Dashboard 中打开你的项目 2. 前往 **SQL Editor** 3. 粘贴 `supabase/migrations/20250301000000_initial_schema.sql` 的内容 4. 点击 **Run** ### 3. 验证表是否存在 在 Supabase Dashboard 中前往 **Table Editor** 并确认已创建这些 表: - `image_groups`(已填充 `python`, `node`, `php`, `nginx`, `ruby`, 和 `base`) - `container_images`(已填充所有 22 个镜像) - `scans`(空表,由夜间扫描填充) - `cves`(空表,由夜间扫描填充) ### 4. 添加新镜像 ``` -- First add the group if it does not exist INSERT INTO image_groups (name) VALUES ('your-group') ON CONFLICT (name) DO NOTHING; -- Then add the image INSERT INTO container_images (image, group_id) VALUES ( 'docker.io/your-image:tag', (SELECT id FROM image_groups WHERE name = 'your-group') ); ``` ## GitHub Secrets 在 `Settings > Secrets and variables > Actions` 中配置这些 secrets: | Secret | Description | |----------------------------------|----------------------------------------------------| | `SUPABASE_URL` | Supabase 项目 URL (`https://xxx.supabase.co`) | | `SUPABASE_SERVICE_ROLE_KEY` | Supabase **service_role** key (写入权限) | | `NEXT_PUBLIC_SUPABASE_URL` | 同 `SUPABASE_URL`(构建时使用) | | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase **anon** key (只读,公开) | | `MISE_SOPS_AGE_KEY` | 用于通过 sops 解密 `.env.yaml` 的 age 密钥 | | `MY_RENOVATE_GITHUB_APP_ID` | Renovate 的 GitHub App ID | | `MY_RENOVATE_GITHUB_PRIVATE_KEY` | Renovate 的 GitHub App 私钥 | | `MY_SLACK_BOT_TOKEN` | 用于 PR 通知的 Slack bot token | | `MY_SLACK_CHANNEL_ID` | 用于 PR 通知的 Slack 频道 ID | `service_role` key 由扫描脚本用于写入数据。 `anon` key 嵌入在静态 Web 应用中用于只读访问 (因为 RLS 策略将其限制为仅 SELECT,所以是安全的)。 `apply-schema` workflow 使用 `MISE_SOPS_AGE_KEY` 解密 `.env.yaml`(使用 sops + age 加密),其中包含 `SUPABASE_ACCESS_TOKEN`, `SUPABASE_PROJECT_REF`, 和 `SUPABASE_DB_PASSWORD`。 ## 本地开发 ### Web 应用 ``` mise install # install Node.js, Supabase CLI, sops, yq mise run web:dev ``` 或手动: ``` cd web || exit cp .env.example .env.local # 使用 Supabase credentials 编辑 .env.local npm install npm run dev ``` 打开 [http://localhost:3000](http://localhost:3000)。 ### 在本地运行扫描 扫描所有镜像并打印 CVE 摘要(无需 Supabase): ``` mise run scan ``` 若要将结果上传到 Supabase,请使用 `scan:upload` 任务: ``` export SUPABASE_URL="https://xxx.supabase.co" export SUPABASE_SERVICE_ROLE_KEY="your-service-role-key" mise run scan:upload ``` ### 推送数据库迁移 ``` mise run db:push ``` 这需要环境中存在 `SUPABASE_ACCESS_TOKEN`, `SUPABASE_PROJECT_REF`, 和 `SUPABASE_DB_PASSWORD`(或从 `.env.yaml` 解密)。 ### 可用的 mise 任务 | Task | Description | |---------------|------------------------------------------------| | `web:install` | 安装 Web 应用依赖 | | `web:dev` | 启动 Next.js 开发服务器 | | `web:build` | 将静态站点导出构建到 `web/out/` | | `scan` | 扫描所有容器镜像,打印 CVE 摘要 | | `scan:upload` | 扫描所有镜像并将结果上传到 Supabase | | `db:push` | 链接 Supabase 项目并推送迁移 | | `build` | 构建 Web 应用(`web:build` 的别名) | 镜像列表定义在仓库根目录的 `images.yml` 中。 ## GitHub Actions ### 核心 workflows #### 夜间扫描 (`.github/workflows/nightly-scans.yml`) - **Schedule**: 每天 00:00 UTC - **Runner**: `ubuntu-24.04-arm` - 安装 trivy 和 grype - 拉取每个容器镜像,使用两个扫描器进行扫描 - 将 SARIF 输出和提取的 CVE 上传到 Supabase - 每条扫描记录存储:镜像摘要、扫描器版本、 grype DB 状态、CVE 数量、完整 SARIF 和时间戳 #### 部署 Web (`.github/workflows/deploy-web.yml`) - **Trigger**: 推送到 `main` 且涉及 `web/**`,或手动触发 - 构建 Next.js 静态站点 - 部署到 GitHub Pages #### 应用 Schema (`.github/workflows/apply-schema.yml`) - **Trigger**: 推送到 `main` 且涉及 `supabase/migrations/**`, 或手动触发 - 通过 sops 解密 `.env.yaml`(使用 `MISE_SOPS_AGE_KEY`) - 链接到 Supabase 项目并运行 `supabase db push` ### CI / 质量 workflows | Workflow | File | Trigger | |-------------------|-----------------------------|-----------------------------| | MegaLinter | `mega-linter.yml` | 推送到非 main 分支 | | CodeQL | `codeql.yml` | 推送到 `main`, PRs, 每周 | | OSSF Scorecards | `scorecards.yml` | 推送到 `main`, 每周 | | Commit Check | `commit-check.yml` | 指向 main 的 PRs | | Semantic PR Title | `semantic-pull-request.yml` | PRs (opened, edited, sync) | ### 自动化 workflows | Workflow | File | Trigger | |-----------------------|-----------------------------|---------------------------------| | Release Please | `release-please.yml` | 推送到 `main` | | Renovate | `renovate.yml` | 推送到 `main`, 每周 | | Stale Issues/PRs | `stale.yml` | 每天 09:09 UTC | | PR Size Labeler | `pr-size-labeler.yml` | Pull requests | | PR Slack Notification | `pr-slack-notification.yml` | PR events (open, review, close) | ## 项目结构 ``` container-image-scans/ +-- .github/ | +-- workflows/ | +-- apply-schema.yml # Push DB migrations via Supabase CLI | +-- codeql.yml # CodeQL security analysis | +-- commit-check.yml # Commit message validation | +-- deploy-web.yml # Deploy dashboard to GH Pages | +-- mega-linter.yml # Comprehensive linting + security | +-- nightly-scans.yml # Nightly trivy + grype scans | +-- pr-size-labeler.yml # Auto-label PRs by size | +-- pr-slack-notification.yml # Slack notifications for PRs | +-- release-please.yml # Automated releases | +-- renovate.yml # Dependency updates | +-- scorecards.yml # OSSF Scorecard analysis | +-- semantic-pull-request.yml # PR title validation | +-- stale.yml # Close stale issues/PRs +-- scripts/ | +-- apply-schema.sh # Link Supabase + push migrations | +-- scan-and-upload.sh # Scan images & upload to Supabase +-- supabase/ | +-- migrations/ | | +-- 20250301000000_initial_schema.sql # Tables, RLS, seed data | +-- config.toml # Supabase CLI configuration | +-- schema.sql # Reference schema (seed + DDL) +-- web/ # Next.js 15 dashboard application | +-- src/ | | +-- app/ | | | +-- globals.css # Global styles | | | +-- layout.tsx # Root layout | | | +-- page.tsx # Main page | | +-- components/ | | | +-- CveDetailTab.tsx # CVE detail table with filters | | | +-- CveTooltip.tsx # Hover tooltip with markdown | | | +-- HistoryCharts.tsx # History line charts | | +-- lib/ | | +-- supabase.ts # Supabase client + data fetchers | +-- next.config.js | +-- package.json | +-- tsconfig.json +-- .checkov.yml # Checkov skip-check config +-- .env.yaml # Encrypted env vars (sops + age) +-- .gitignore +-- .mega-linter.yml # MegaLinter configuration +-- .mise.toml # mise tool versions + tasks +-- .pre-commit-config.yaml # pre-commit hooks +-- .rumdl.toml # rumdl markdown linter config +-- AGENTS.md # AI agent coding guidelines +-- images.yml # Container images to scan +-- LICENSE +-- lychee.toml # Link checker configuration +-- README.md +-- SECURITY.md # Security policy ```
标签:Angular, CI/CD安全, CVE分析, DevSecOps, GitHub Actions, Grype, Llama, Nginx安全, Node.js安全, SARIF, Supabase, Web截图, 上游代理, 仪表盘, 安全可视化, 容器安全, 开源安全工具, 提示词注入, 活动识别, 测试用例, 自动笔记, 请求拦截, 逆向工程平台, 镜像合规, 镜像扫描