Условие
Друг хочет разместить веб-сервис в Docker-контейнерах. Какие рекомендации по безопасному деплою вы дадите?
Решение
1. Минимальный базовый образ
- Distroless (
gcr.io/distroless/...) или scratch для Go-бинарников. - Если нужен runtime —
alpineилиdebian-slim. Больше пакетов = больше CVE. - Конкретный тег, не
:latest. Ещё лучше — digest-pin:image@sha256:....
2. Multi-stage build
# Этап 1: сборка
FROM golang:1.22 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /out/app ./cmd/app
# Этап 2: runtime
FROM gcr.io/distroless/static-debian12:nonroot
COPY --from=builder /out/app /app
USER nonroot:nonroot
ENTRYPOINT ["/app"]В итоге — крошечный образ без bash, wget, curl, build-tools.
3. Не запускать от root
RUN addgroup -S app && adduser -S app -G app
USER appили образы *-nonroot. Если приложение слушает <1024 порт — сменить на 8080+ или использовать setcap cap_net_bind_service=+ep.
4. Read-only filesystem
# docker-compose
services:
app:
read_only: true
tmpfs: [/tmp]Если приложение пишет на диск — выносим в volumes отдельно.
5. Capabilities и seccomp
- Сбросить ВСЕ capabilities, добавить только нужные:
cap_drop: [ALL] cap_add: [NET_BIND_SERVICE] # если нужен 80/443 - Default seccomp profile Docker уже хорош; кастомный — только при необходимости.
--security-opt=no-new-privileges— обязательно.
6. Секреты
- Никогда не в
Dockerfile(ENV PASSWORD=...) — попадёт в layer. - Не в build-args для prod — видно в
docker history. - Использовать Docker Secrets, Kubernetes Secrets, HashiCorp Vault, AWS Secrets Manager.
- В коде — читать из ENV или mounted file, не commit.
.dockerignoreобязателен (исключаем.env,.git,node_modules).
7. Образы и реестры
- Подпись и проверка образов:
cosign sign/cosign verify, Docker Content Trust. - Сканирование на CVE в CI:
trivy,grype,snyk container test. - Свой приватный registry (Harbor, ECR, GCR) с auth, не публичный Docker Hub.
8. Сеть
- Внутри docker-compose — отдельные сети для backend/frontend/db.
- БД не публиковать наружу:
expose:(только внутри сети), а неports:. - TLS терминируется в reverse-proxy (nginx/Traefik), а не в каждом контейнере.
9. Лимиты ресурсов
deploy:
resources:
limits:
cpus: "1.0"
memory: 512MЗащита от DoS: один контейнер не сожрёт всю машину.
10. Healthcheck
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -q --spider http://localhost:8080/health || exit 1Орекстратор перезапустит «зависшие» контейнеры.
11. Логи и аудит
docker logsподключить к централизованной системе (Loki/Elastic).- Аудит Docker daemon на хосте (auditd на
/var/lib/docker,/etc/docker).
12. Хост (host hardening)
- Свежие ядро и Docker (CVE-2019-5736 — известный escape).
- AppArmor/SELinux включён.
- Никаких
--privilegedконтейнеров. Если очень нужно (docker-in-docker, GPU) — полностью изолированный VM. - Не монтировать
/var/run/docker.sockбез необходимости — это полный root на хосте.
13. Обновления
- CI пересобирает образы на каждый PR + ночью на свежей базе.
- Регулярно
docker pullбазовых тегов и пересборка → пропатченные CVE.
Подводные камни
USERвDockerfileпослеEXPOSEможет не сработать, если вENTRYPOINTесть скрипт, требующий root. Решение —gosu/su-execдля понижения уровня в entrypoint.docker historyвыдаёт build-args. Не передавайте секреты черезARG. Используйте BuildKit secret-mounts:RUN --mount=type=secret,id=mytoken ….COPY . .копирует.env/.git. Без.dockerignoreвсё это попадает в образ.latestломает воспроизводимость. Сегодняpython:3-alpine= 3.12, завтра 3.13.--privileged= полный root. Никогда не используйте в продакшне.- Docker socket = эскалация.
-v /var/run/docker.sock:/var/run/docker.sockвнутри контейнера = root на хосте. Используйте rootless docker илиsocket-proxy. alpineне всегда меньше уязвим. musl-libc даёт другие CVE, и некоторые тулы там нестабильны.HEALTHCHECKничего не делает в Kubernetes. Там —livenessProbe/readinessProbe.- Read-only без
tmpfsдля/tmpломает приложения, пишущие во временные файлы.
Эталонный ответ
Distroless / minimal base + multi-stage build + non-root user + cap_drop: ALL + read_only + no-new-privileges + сканер CVE в CI (trivy) + секреты через Vault/Secrets Manager (не в env/Dockerfile) + лимиты ресурсов + сегментация сетей + регулярные пересборки.