
layerleak — сканер секретов Docker Hub
Ознакомьтесь с CONTRIBUTING.md для руководства по участию.
Опубликованный сайт собирается из web/ на ветке main с помощью .github/workflows/pages.yml. Исходный код документации и симулированная демонстрация браузера находятся в этой директории.
Предварительные требования:
Установка с помощью Go:
go install github.com/brumbelow/layerleak@latest
layerleak --help
Каноническая цель установки — корень модуля. Чтобы явно зафиксировать релиз:
go install github.com/brumbelow/[email protected]
Замените v1.0.0 на опубликованный тег v1.x.y, который вам нужен.
Убедитесь, что ваш GOBIN или GOPATH/bin находится в PATH.
Путь модуля — github.com/brumbelow/layerleak, поэтому go install @latest разрешается в самый высокий опубликованный тег v1.x.y. Релиз модуля v2.x.y потребовал бы изменения пути модуля на github.com/brumbelow/layerleak/v2. Установленные из модуля бинарники сообщают разрешённую версию модуля через layerleak --version; сборки из локальной копии сообщают версию, встроенную Go для этой копии, с понижением до dev, если версия модуля недоступна.
Сборка из исходного кода:
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://<user>:<password>@<host>:5432/layerleak?sslmode=disable' \
ghcr.io/brumbelow/layerleak:latest
Контейнерный образ по умолчанию запускает API и устанавливает LAYERLEAK_API_ADDR=0.0.0.0:8080.
Необязательная конфигурация окружения:
cp .env.example .env
Конфигурация результатов и базы данных:
export LAYERLEAK_LOG_LEVEL=info
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_REGISTRY_BASE_URL=
export LAYERLEAK_REGISTRY_AUTH_URL=
export LAYERLEAK_DATABASE_URL=postgres://postgres:postgres@localhost:5432/layerleak?sslmode=disable
Те же переменные и их значения по умолчанию находятся в .env.example, который является источником истины для значений по умолчанию.
| Переменная | По умолчанию | Назначение |
|---|---|---|
LAYERLEAK_LOG_LEVEL | info | Уровень логирования: debug, info, warn или error. |
LAYERLEAK_FINDINGS_DIR | не задана | Куда записывать JSON-файлы с результатами. Если не задана, по умолчанию используется findings/ в ближайшей родительской директории, содержащей go.mod, или текущая рабочая директория, если корень репозитория не найден. |
LAYERLEAK_API_ADDR | 127.0.0.1:8080 | Адрес привязки для API-сервера. Контейнерный образ переопределяет это значение на 0.0.0.0:8080. |
LAYERLEAK_PERSIST_RAW_SECRETS | 0 | Установите в 1, чтобы записывать сырые значения секретов и сырые фрагменты контекста на диск и в Postgres. По умолчанию результаты остаются скрытыми. |
LAYERLEAK_HTTP_TIMEOUT | 30s | Тайм-аут на один запрос для каждого вызова реестра (манифесты, блобы, страницы тегов, токены аутентификации). Принимает любую длительность Go (30s, 2m, 1h). |
LAYERLEAK_MAX_FILE_BYTES | 1048576 (1 MiB) | Максимальное количество распакованных байт, буферизованных на один файл внутри слоя. Файлы больше этого размера пропускаются как слишком большие. Должно быть больше нуля. |
LAYERLEAK_MAX_LAYER_BYTES | 536870912 (512 MiB) | Максимальное количество распакованных байт потока слоя на один слой. 0 отключает ограничение. |
LAYERLEAK_MAX_LAYER_ENTRIES | 50000 | Максимальное количество tar-записей на слой. 0 отключает ограничение. |
LAYERLEAK_MAX_MANIFEST_BYTES | 0 | Максимальное количество байт тела манифеста. 0 отключает ограничение. |
Когда любой из лимитов MAX_* установлен в положительное значение, его превышение приводит к сбою сканирования с явной ошибкой, а не к молчаливому усечению работы.
Поведение результатов:
findings и формируют ненулевой статус выхода сканирования.total_findings.disposition, disposition_reason и line_number для упрощения триажа и проверки ложных срабатываний.1, так как сканирование неполное.Layerleak поставляется с версионированными SQL-миграциями в migrations/.
Миграции намеренно ручные. Сканер не создаёт и не обновляет схему автоматически.
Layerleak требует PostgreSQL сервер >= 16.13 для работы 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
Операционные значения по умолчанию:
first_seen_at и last_seen_at, а также хранит историю сканирования в режиме "только добавление" в scan_runs.(manifest_digest, fingerprint), и повторяющиеся идентичные фрагменты контекста сворачиваются перед сохранением.Примечание по безопасности секретов:
LAYERLEAK_PERSIST_RAW_SECRETS=1, Postgres также хранит сырые значения результатов и сырые фрагменты.scan_runs.result_json остаётся скрытым.Показать справку CLI:
layerleak --help
layerleak scan --help

Запустить сканирование публичного 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

Каждое сканирование записывает JSON-файл с результатами в выходную директорию findings.
Если LAYERLEAK_FINDINGS_DIR не установлена, выходная директория по умолчанию — findings/ в ближайшей родительской директории, содержащей go.mod (обычно корень репозитория), с понижением до текущей рабочей директории, если корень репозитория не найден.
Сохранённые файлы результатов содержат записи с redacted_value, скрытый context_snippet, точное местоположение источника, метаданные disposition и номер строки для каждого результата.
Если LAYERLEAK_PERSIST_RAW_SECRETS=1, сохранённые файлы результатов также включают сырое value и raw_context_snippet.
Если включено постоянное хранение в Postgres, сырые findings.value и finding_occurrences.raw_snippet остаются пустыми, если не установлен LAYERLEAK_PERSIST_RAW_SECRETS=1.
Для multi-arch образов layerleak пропускает манифесты аттестации и происхождения, такие как application/vnd.in-toto+json, вместо того чтобы считать их неудачными сканированиями платформы.
Зачистки репозитория (sweeps):
mongo, перечисляет все публичные теги в этом репозитории, разрешает каждый тег в дайджест, группирует дубликаты дайджестов и сканирует различные цели.mongo:latest или mongo@sha256:....Синтаксис команд:
layerleak [command]
layerleak scan <image-ref> [flags]
Флаги области для зачисток репозитория (каждый переопределяет соответствующую переменную окружения для одной команды):
| Флаг | Назначение |
|---|---|
--tag-page-size | Размер страницы списка тегов реестра для зачисток репозитория. Должен быть больше нуля. Переопределяет LAYERLEAK_TAG_PAGE_SIZE. |
--max-repository-tags | Максимальное количество тегов, перечисляемых за зачистку репозитория. 0 отключает ограничение. Переопределяет LAYERLEAK_MAX_REPOSITORY_TAGS. |
--max-repository-targets | Максимальное количество различных целей, разрешённых за зачистку репозитория. 0 отключает ограничение. Переопределяет LAYERLEAK_MAX_REPOSITORY_TARGETS. |
Layerleak также включает минимальный JSON API в cmd/api.
API использует Postgres и требует LAYERLEAK_DATABASE_URL; он не обслуживает данные из файлов результатов на диске.
Запустите его с помощью:
go run ./cmd/api
Или запустите контейнер API:
docker run --rm \
-p 8080:8080 \
-e LAYERLEAK_DATABASE_URL='postgres://<user>:<password>@<host>:5432/layerleak?sslmode=disable' \
ghcr.io/brumbelow/layerleak:latest
Текущие конечные точки:
GET /healthPOST /api/v1/scansGET /api/v1/scans/{id}GET /api/v1/repositoriesGET /api/v1/repositories/{repository}/scansGET /api/v1/repositories/{repository}/findingsGET /api/v1/findings/{id}GET /health возвращает {"status":"ok"} и не требует настроенного хранилища или сканера.
Он подходит для зондов готовности Kubernetes и целей healthcheck Docker Compose.
POST /api/v1/scans остаётся синхронным. Он принимает JSON-тело с reference и опциональным platform, и возвращает scan_run_id, если включено постоянное хранение в Postgres.
Ответы API на сканирования используют ту же скрытую схему результата, что и вывод JSON CLI.
GET /api/v1/scans/{id} возвращает сохранённые метаданные запуска плюс сохранённый скрытый снимок результата.
Конечные точки репозитория и результатов также остаются скрытыми: они возвращают redacted_value и скрытый context_snippet, никогда не сырые значения секретов или сырые фрагменты из Postgres.
GET /api/v1/repositories/{repository}/scans и GET /api/v1/repositories/{repository}/findings принимают опциональный параметр запроса registry (например, ?registry=ghcr.io). Если он опущен, по умолчанию используется docker.io для обратной совместимости. Используйте его для получения сканирований репозиториев на GHCR, Quay, GCR, MCR, Amazon ECR Public или любом собственном реестре.
Конечные точки списков (/repositories, /repositories/{repository}/scans, /repositories/{repository}/findings) принимают ?limit= и ?offset= для пагинации. limit по умолчанию равен 50 и ограничен 200. /repositories/{repository}/findings также принимает ?disposition=actionable|suppressed|all; если параметр опущен, ответ включает только действенные результаты.
API не включает аутентификацию. Для организационных развёртываний держите его в частной сети и разместите перед ним собственный шлюз authn/authz или политику реверс-прокси.
Этот репозиторий включает стек Compose в docker-compose.yml с сервисами db, migrate и api.
Сервис db по умолчанию использует postgres:16.13-alpine.
Если вы используете другой образ Postgres, поддерживайте версию сервера на уровне 16.13 или новее.
Установите переменные развёртывания (экспортируйте в оболочке или поместите в файл .env рядом с docker-compose.yml):
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
Проверьте отрендеренную конфигурацию Compose перед развёртыванием:
docker compose config
Запустите миграции один раз перед запуском API:
docker compose --profile manual run --rm migrate
Запустите сервис API:
docker compose up -d api
В Dockge или Komodo импортируйте тот же файл Compose и запустите сервис migrate один раз перед включением долго работающего сервиса api.
Выпущено под лицензией MIT — см. LICENSE.
Если этот репозиторий сэкономил вам время или помог, вы можете поддержать будущие обновления здесь:
Спасибо :) это действительно помогает поддерживать проект.
LAYERLEAK_MAX_CONFIG_BYTES | 0 | Максимальное количество байт тела конфигурации образа. 0 отключает ограничение. |
LAYERLEAK_MAX_TAG_RESPONSE_BYTES | 8388608 (8 MiB) | Максимальное количество байт на страницу ответа списка тегов реестра. 0 отключает ограничение. |
LAYERLEAK_TAG_PAGE_SIZE | 100 | Размер страницы списка тегов реестра для сканирования всего репозитория. |
LAYERLEAK_MAX_REPOSITORY_TAGS | 0 | Максимальное количество тегов, перечисляемых при сканировании репозитория. 0 отключает ограничение. |
LAYERLEAK_MAX_REPOSITORY_TARGETS | 0 | Максимальное количество различных целей, разрешённых при сканировании репозитория. 0 отключает ограничение. |
LAYERLEAK_REGISTRY_REQUEST_ATTEMPTS | 2 | Количество попыток (включая первую) для каждого запроса к реестру. |
LAYERLEAK_REGISTRY_BASE_URL | не задана | Необязательное переопределение. Обычно layerleak определяет его из ссылки на образ; устанавливайте только для принудительного сканирования через прокси или альтернативную конечную точку. |
LAYERLEAK_REGISTRY_AUTH_URL | не задана | Необязательное переопределение. Обычно определяется из запроса WWW-Authenticate реестра. |
LAYERLEAK_DATABASE_URL | не задана | Если задана, layerleak записывает результаты сканирования в Postgres и завершает команду с ошибкой, если постоянное хранение не удалось. |