Brumbelow/layerleak

GitHub: Brumbelow/layerleak

一款基于 OCI 镜像分层结构的容器镜像机密扫描器,无需 Docker daemon 即可直接扫描公共 Registry 上镜像层、配置元数据及已删除层残留中的敏感信息泄露。

Stars: 38 | Forks: 1

# layerleak,OCI 镜像机密扫描器 [![made-with-Go](https://img.shields.io/badge/Made%20with-Go-1f425f.svg)](https://go.dev/) 请查阅 [CONTRIBUTING.md](./CONTRIBUTING.md) 了解贡献指南。 - OCI 镜像机密扫描器,适用于任何符合 OCI 标准的公共 Registry (Docker Hub、GHCR、Quay、GCR、MCR、Amazon ECR Public、自托管)。它会分析镜像层、配置元数据以及镜像历史,然后根据 manifest 摘要存储去重后的发现结果。 - 传统的机密扫描器通常将容器镜像视为一个扁平的 blob,或者依赖于本地的 Docker daemon。本项目则是基于 OCI 镜像内部结构设计的。 ## 文档页面 - https://brumbelow.github.io/layerleak/docs 已发布的站点是由 `.github/workflows/pages.yml` 从 `main` 分支上的 `web/` 目录构建的。文档源码和模拟浏览器演示均位于该目录下。 ## 当前功能特性: - 支持来自任何符合 OCI 标准的公共 Registry 的镜像 (Docker Hub、GHCR、Quay、GCR、MCR、Amazon ECR Public、自托管) - 只读扫描 - 无需机密验证 - 无需依赖 Docker daemon - 感知 Manifest 与镜像层的扫描 - 扫描最终的文件系统以及已删除层的残留数据 - 扫描镜像配置元数据、环境变量、标签和历史记录 - 根据机密指纹对发现结果进行去重,并折叠每个 manifest 中重复的相同上下文片段 ## 安装说明 前置条件: - Go 1.25.7+ 使用 Go 安装: ``` go install github.com/brumbelow/layerleak@latest layerleak --help ``` 规范的安装目标是模块根目录。 如需显式固定某个发布版本: ``` go install github.com/brumbelow/layerleak@v1.0.0 ``` 将 `v1.0.0` 替换为你想要的、已发布的 `v1.x.y` 标签。 确保你的 `GOBIN` 或 `GOPATH/bin` 目录已添加到 `PATH` 中。 从源码构建: ``` git clone https://github.com/brumbelow/layerleak.git cd layerleak go build -o layerleak . ./layerleak --help ``` 使用容器镜像运行 API: ``` docker pull ghcr.io/brumbelow/layerleak:latest docker run --rm \ -p 8080:8080 \ -e LAYERLEAK_DATABASE_URL='postgres://:@:5432/layerleak?sslmode=disable' \ ghcr.io/brumbelow/layerleak:latest ``` 该容器镜像默认运行 API,并设置 `LAYERLEAK_API_ADDR=0.0.0.0:8080`。 可选的环境配置: ``` cp .env.example .env ``` 结果和数据库配置: ``` export LAYERLEAK_FINDINGS_DIR=findings export LAYERLEAK_API_ADDR=127.0.0.1:8080 export LAYERLEAK_PERSIST_RAW_SECRETS=0 export LAYERLEAK_TAG_PAGE_SIZE=100 export LAYERLEAK_HTTP_TIMEOUT=30s export LAYERLEAK_MAX_FILE_BYTES=1048576 export LAYERLEAK_MAX_LAYER_BYTES=536870912 export LAYERLEAK_MAX_LAYER_ENTRIES=50000 export LAYERLEAK_MAX_MANIFEST_BYTES=0 export LAYERLEAK_MAX_CONFIG_BYTES=0 export LAYERLEAK_MAX_TAG_RESPONSE_BYTES=8388608 export LAYERLEAK_MAX_REPOSITORY_TAGS=0 export LAYERLEAK_MAX_REPOSITORY_TARGETS=0 export LAYERLEAK_REGISTRY_REQUEST_ATTEMPTS=2 export LAYERLEAK_DATABASE_URL=postgres://postgres:postgres@localhost:5432/layerleak?sslmode=disable ``` 如果未设置 `LAYERLEAK_FINDINGS_DIR`,layerleak 会将 JSON 结果文件写入包含 `go.mod` 的最近父目录(通常是仓库根目录)下的 `findings/` 文件夹中。如果未找到仓库根目录,则会回退到当前工作目录。 保存的结果文件默认只包含检测结果且经过脱敏处理。 仅当你明确希望将原始的检出值和原始的上下文片段写入磁盘和 Postgres 时,才需设置 `LAYERLEAK_PERSIST_RAW_SECRETS=1`。 `LAYERLEAK_TAG_PAGE_SIZE` 用于控制全仓库扫描时的 registry 标签列表分页。 `LAYERLEAK_MAX_LAYER_BYTES` 默认为 `536870912`(即 512 MiB)的单层解压流数据,而 `LAYERLEAK_MAX_LAYER_ENTRIES` 默认为每层 `50000` 个 tar 条目。 `LAYERLEAK_MAX_TAG_RESPONSE_BYTES` 默认为每个 registry 标签列表响应页 `8388608`(即 8 MiB)。 `LAYERLEAK_REGISTRY_BASE_URL` 和 `LAYERLEAK_REGISTRY_AUTH_URL` 是可选的覆盖配置。在正常使用中请保持未设置状态 —— layerleak 会从每个镜像引用中推导出 registry 基础 URL,并通过 registry 的 `WWW-Authenticate` 质询发现 auth realm。仅在需要强制通过代理或备用 endpoint 进行扫描时才设置它们。 当 `LAYERLEAK_MAX_LAYER_BYTES`、`LAYERLEAK_MAX_LAYER_ENTRIES`、`LAYERLEAK_MAX_MANIFEST_BYTES`、`LAYERLEAK_MAX_CONFIG_BYTES`、`LAYERLEAK_MAX_TAG_RESPONSE_BYTES`、`LAYERLEAK_MAX_REPOSITORY_TAGS` 和 `LAYERLEAK_MAX_REPOSITORY_TARGETS` 被设置为 `0` 时,这些限制将被禁用。 如果启用,当超出这些限制时,扫描将因明确的错误而失败,而不是静默截断工作。 `LAYERLEAK_REGISTRY_REQUEST_ATTEMPTS` 控制 registry 请求重试次数,默认为 `2`。 `LAYERLEAK_HTTP_TIMEOUT` 是应用于每次 registry 调用(manifest 获取、blob 下载、标签列表页、auth token 请求)的 HTTP 客户端的单次请求超时时间。接受任何 Go 的 duration 值(例如 `30s`、`2m`、`1h`);默认为 `30s`。 `LAYERLEAK_MAX_FILE_BYTES` 是 layerleak 为层内每个文件缓冲的最大解压字节数;超过此大小的文件将被归类为超大文件并跳过。默认为 `1048576`(1 MiB),且必须大于零。 `LAYERLEAK_API_ADDR` 控制 API 服务器的绑定地址,在本地二进制文件中默认为 `127.0.0.1:8080`。 容器镜像将此覆盖为 `0.0.0.0:8080`。 如果设置了 `LAYERLEAK_DATABASE_URL`,扫描器还会将扫描结果写入 Postgres,如果 Postgres 不可用或保存失败,则命令执行将报错退出。 结果行为: - 可操作的发现结果会保留在 `findings` 中,并驱动扫描返回非零退出状态码。 - 疑似测试/示例/演示用途的占位符会作为被抑制的示例结果单独发出,不计入 `total_findings`。 - 结果记录包含 `disposition`、`disposition_reason` 和 `line_number`,以便于进行分类排查和误报审查。 - 如果超出了配置的运行限制,layerleak 仍然会写入并呈现失败前生成的部分结果,然后以状态码 `1` 退出,因为扫描未完成。 ## Postgres 持久化 Layerleak 在 `migrations/` 目录下附带版本化的 SQL 迁移脚本。 迁移操作设计为需要手动执行。扫描器不会自动创建或升级 schema。 Layerleak 要求 PostgreSQL 服务器版本 `>= 16.13`,以支持基于 DB 的 API 和扫描器持久化。 请使用 `psql` 按顺序应用迁移: ``` psql "$LAYERLEAK_DATABASE_URL" -f migrations/0001_initial.up.sql psql "$LAYERLEAK_DATABASE_URL" -f migrations/0002_finding_occurrence_metadata.up.sql psql "$LAYERLEAK_DATABASE_URL" -f migrations/0003_scan_runs.up.sql ``` 或者使用容器辅助命令应用迁移: ``` docker run --rm \ -e LAYERLEAK_DATABASE_URL="$LAYERLEAK_DATABASE_URL" \ ghcr.io/brumbelow/layerleak:latest \ layerleak-migrate-up ``` 当迁移已经应用时,`layerleak-migrate-up` 可以安全地重复运行。 如果它检测到部分迁移状态,将以非零状态码退出并提示需要人工干预。 该辅助命令还会强制要求服务器版本 `>= 16.13`,并验证内置的 `postgresql-client-16` 使用的是 Ubuntu PGDG `24.04` 打包版本(`.pgdg24.04+`),且版本号 `>= 16.13-1.pgdg24.04+1`。 按相反顺序回滚迁移: ``` psql "$LAYERLEAK_DATABASE_URL" -f migrations/0003_scan_runs.down.sql psql "$LAYERLEAK_DATABASE_URL" -f migrations/0002_finding_occurrence_metadata.down.sql psql "$LAYERLEAK_DATABASE_URL" -f migrations/0001_initial.down.sql ``` 运行默认值: - 预期迁移将保持为增量方式。 - Schema 使用 `first_seen_at` 和 `last_seen_at` 保留当前去重后的状态,并在 `scan_runs` 中存储只追加的扫描历史。 - 被当前扫描触及的标签,其映射关系会被刷新。 - 发现结果通过 `(manifest_digest, fingerprint)` 进行规范去重,并且重复的相同上下文片段会在持久化之前被折叠合并。 - 扫描历史存储的是公共结果 JSON 的脱敏快照,不包含原始值或原始片段。 机密安全提示: - Postgres 持久化默认存储脱敏后的预览数据。 - 如果设置了 `LAYERLEAK_PERSIST_RAW_SECRETS=1`,Postgres 也会存储原始的检出值和原始片段。 - `scan_runs.result_json` 快照保持脱敏状态。 - 请为 layerleak 使用专用的数据库或 schema。 - 对于最安全的清除途径,请直接删除专用的数据库或 schema,而不是试图精确删除单行数据。 ## 如何开始 显示 CLI 帮助信息: ``` layerleak --help layerleak scan --help ``` ![help_output](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/9bf50a89e5201923.png) 针对任何支持 registry 上的公共 OCI 镜像运行扫描: ``` ./layerleak scan ubuntu ./layerleak scan library/nginx:latest --format json ./layerleak scan alpine:latest --platform linux/amd64 ./layerleak scan mongo ./layerleak scan ghcr.io/homebrew/core/hello:latest ./layerleak scan quay.io/prometheus/busybox:latest ./layerleak scan gcr.io/distroless/static:nonroot ./layerleak scan public.ecr.aws/docker/library/alpine:3.20 ./layerleak scan mcr.microsoft.com/hello-world:latest ``` ![cli pic](https://static.pigsec.cn/wp-content/uploads/repos/2026/05/77d4ff8e56201925.png) 每次扫描都会将 JSON 结果文件写入结果输出目录。 如果未设置 `LAYERLEAK_FINDINGS_DIR`,默认输出目录为包含 `go.mod` 的最近父目录(通常是仓库根目录)下的 `findings/`,当找不到仓库根目录时,则回退到当前工作目录。 这些保存的结果文件包含带有 `redacted_value`、已脱敏的 `context_snippet`、确切来源位置、分类元数据以及每个发现的行号的记录。 如果设置了 `LAYERLEAK_PERSIST_RAW_SECRETS=1`,保存的结果文件还将包含原始的 `value` 和 `raw_context_snippet`。 如果启用了 Postgres 持久化,除非设置 `LAYERLEAK_PERSIST_RAW_SECRETS=1`,否则原始的 `findings.value` 和 `finding_occurrences.raw_snippet` 将保持为空。 对于多架构镜像,layerleak 会跳过诸如 `application/vnd.in-toto+json` 的证明和来源 manifest,而不是将它们计入失败的平台扫描中。 如果你传递了一个裸仓库名称(如 `mongo`),layerleak 将枚举该仓库中所有的公共标签,将每个标签解析为摘要,对重复的摘要进行分组,然后扫描不同的目标。如果你只想要单个镜像,请传递显式的标签或摘要,例如 `mongo:latest` 或 `mongo@sha256:...`。 命令语法: ``` layerleak [command] layerleak scan [flags] ``` ## HTTP API Layerleak 还在 `cmd/api` 下提供了一个轻量级的 JSON API。 该 API 由 Postgres 支持并需要配置 `LAYERLEAK_DATABASE_URL`;它不从磁盘上的结果文件中提供数据。 使用以下命令启动: ``` go run ./cmd/api ``` 或运行 API 容器: ``` docker run --rm \ -p 8080:8080 \ -e LAYERLEAK_DATABASE_URL='postgres://:@:5432/layerleak?sslmode=disable' \ ghcr.io/brumbelow/layerleak:latest ``` 当前的 endpoint: - `POST /api/v1/scans` - `GET /api/v1/scans/{id}` - `GET /api/v1/repositories` - `GET /api/v1/repositories/{repository}/scans` - `GET /api/v1/repositories/{repository}/findings` - `GET /api/v1/findings/{id}` `POST /api/v1/scans` 保持同步,并且只要启用了 Postgres 持久化,它现在就会返回 `scan_run_id`。 API 扫描响应复用了与 CLI JSON 输出相同的脱敏结果 schema。 `GET /api/v1/scans/{id}` 返回持久化的运行元数据以及存储的脱敏结果快照。 仓库和发现结果的 endpoint 也保持脱敏:它们返回 `redacted_value` 和已脱敏的 `context_snippet`,绝不会从 Postgres 返回原始的机密值或原始片段。 `GET /api/v1/repositories/{repository}/scans` 和 `GET /api/v1/repositories/{repository}/findings` 接受一个可选的 `registry` 查询参数(例如 `?registry=ghcr.io`)。省略时,出于向后兼容性考虑,registry 默认为 `docker.io`。可使用此参数获取 GHCR、Quay、GCR、MCR、Amazon ECR Public 或任何自托管 registry 上的仓库扫描结果。 该 API 不包含身份验证。 对于组织部署,请将其保留在私有网络中,并在其前面配置你自己的 authn/authz 网关或反向代理策略。 ## Docker Compose 部署 (Dockge / Komodo) 本仓库在 `docker-compose.yml` 中附带了一个包含 `db`、`migrate` 和 `api` 服务的 Compose 堆栈。 `db` 服务基线被固定为 `postgres:16.13-alpine`。 如果你使用不同的 Postgres 镜像,请保持服务器版本为 `16.13` 或更新版本。 设置部署变量(在 shell 中 export,或将其放在与 `docker-compose.yml` 相邻的 `.env` 文件中): ``` export LAYERLEAK_IMAGE=ghcr.io/brumbelow/layerleak:latest export LAYERLEAK_DB_NAME=layerleak export LAYERLEAK_DB_USER=layerleak export LAYERLEAK_DB_PASSWORD=replace-me export LAYERLEAK_API_PORT=8080 ``` 在启动 API 之前运行一次迁移: ``` docker compose --profile manual run --rm migrate ``` 启动 API 服务: ``` docker compose up -d api ``` 在 Dockge 或 Komodo 中,导入相同的 Compose 文件,并在启用长期运行的 `api` 服务之前运行一次 `migrate` 服务。
标签:API安全, DevSecOps, Docker Hub, ECR Public, EVTX分析, GCR, GHCR, Go语言, JSON输出, OCI镜像扫描, Secret Scanner, StruQ, Web截图, 上游代理, 元数据扫描, 只读扫描, 安全助手, 容器安全, 密钥泄露检测, 开源安全工具, 日志审计, 测试用例, 版权保护, 程序破解, 误配置预防, 请求拦截, 逆向工程平台, 镜像层分析