Loading...
Усі статті
Security · 6 min read

Зміцнення безпеки Docker: 10 ключових практик

Вичерпний посібник із захисту Docker-контейнерів у продакшні: сканування образів, runtime-захист, управління секретами тощо.

Чому безпека контейнерів не може бути запізнілою думкою

Контейнери забезпечують ізоляцію, але за замовчуванням вони не є межею безпеки. Неправильно сконфігурований контейнер може відкрити файлові системи хоста, витекти облікові дані або надати атакуючому плацдарм для горизонтального переміщення вашою інфраструктурою. Модель спільного ядра означає, що вихід з контейнера зачіпає кожне навантаження на хості.

У DevOpsVibe ми регулярно проводимо аудит контейнерних середовищ. Ось десять практик, які роблять найбільшу різницю між вразливим розгортанням і зміцненим.

1. Використовуйте мінімальні базові образи

Кожен пакет у вашому образі — це потенційна поверхня атаки. Починайте з найменшого образу, який працює:

До (вразливий):

FROM ubuntu:22.04
RUN apt-get update && apt-get install -y python3 python3-pip curl wget vim
COPY . /app
RUN pip3 install -r /app/requirements.txt
CMD ["python3", "/app/main.py"]

Після (зміцнений):

FROM python:3.12-slim AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir --prefix=/install -r requirements.txt

FROM gcr.io/distroless/python3-debian12
COPY --from=builder /install /usr/local
COPY . /app
WORKDIR /app
CMD ["main.py"]

Distroless-образ не має shell, пакетного менеджера та непотрібних утиліт. Якщо атакуючий отримає виконання коду, працювати буде майже ні з чим.

Порівняння розмірів образів:

  • ubuntu:22.04 із залежностями: ~450MB
  • python:3.12-slim multi-stage з distroless: ~85MB

2. Ніколи не запускайте від root

За замовчуванням Docker запускає процеси як root всередині контейнера. Це небезпечно, бо вихід з контейнера дає атакуючому root на хості:

FROM node:20-slim

# Create a non-root user
RUN groupadd -r appuser && useradd -r -g appuser -d /app -s /sbin/nologin appuser

WORKDIR /app
COPY --chown=appuser:appuser . .
RUN npm ci --only=production

USER appuser
EXPOSE 3000
CMD ["node", "server.js"]

Перевірте під час виконання:

docker run --rm my-app whoami
# Output: appuser

3. Скануйте образи на вразливості

Інтегруйте сканування образів у CI/CD-конвеєр. Ми рекомендуємо Trivy за швидкість та вичерпну базу даних:

# Scan during build
trivy image --severity HIGH,CRITICAL --exit-code 1 my-app:latest

# Example output:
# my-app:latest (debian 12.4)
# Total: 0 (HIGH: 0, CRITICAL: 0)

Автоматизуйте це у вашому конвеєрі:

# .github/workflows/security.yml
- name: Scan image
  uses: aquasecurity/trivy-action@master
  with:
    image-ref: ${{ env.IMAGE }}
    format: sarif
    output: trivy-results.sarif
    severity: HIGH,CRITICAL
    exit-code: "1"

- name: Upload scan results
  uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: trivy-results.sarif

Також скануйте свої Dockerfile на неправильні налаштування:

trivy config --severity HIGH,CRITICAL ./Dockerfile

4. Закріплюйте digest образів, а не лише теги

Теги змінні. Хтось може запушити скомпрометований образ у python:3.12-slim, і кожна збірка, що тягне цей тег, отримує шкідливу версію:

# Risky: tag can be overwritten
FROM python:3.12-slim

# Secure: digest is immutable
FROM python:3.12-slim@sha256:a3e58c29e4a3b692a57d0e68b4b9c4f2e63e547e1b3f0a8e93c2a4fb7d3e1c9a

Отримайте digest за допомогою:

docker inspect --format='{{index .RepoDigests 0}}' python:3.12-slim

5. Впроваджуйте read-only файлові системи

Запобігайте зміні файлової системи контейнера під час виконання. Якщо ваш застосунок повинен писати, монтуйте конкретні writable-томи:

docker run --read-only \
  --tmpfs /tmp:rw,noexec,nosuid,size=64m \
  --volume /app/data:/data:rw \
  my-app:latest

У Kubernetes:

securityContext:
  readOnlyRootFilesystem: true
  allowPrivilegeEscalation: false
  runAsNonRoot: true
  runAsUser: 1000
  capabilities:
    drop:
      - ALL

6. Правильно керуйте секретами

Ніколи не запікайте секрети в образи. Вони залишаються в історії шарів, навіть якщо ви видаляєте їх у пізнішому шарі:

Неправильно:

# NEVER do this
ENV DATABASE_URL=postgres://admin:password@db:5432/myapp
COPY .env /app/.env

Правильно — використовуйте секрети під час збірки (BuildKit):

# syntax=docker/dockerfile:1
RUN --mount=type=secret,id=db_url \
    export DATABASE_URL=$(cat /run/secrets/db_url) && \
    python manage.py collectstatic
docker buildx build --secret id=db_url,src=./db_url.txt -t my-app .

Під час виконання використовуйте управління секретами вашого оркестратора:

# Kubernetes secret injection
env:
  - name: DATABASE_URL
    valueFrom:
      secretKeyRef:
        name: api-secrets
        key: database-url

7. Обмежуйте ресурси контейнера

Необмежений контейнер може спожити всі ресурси хоста, створивши умови для відмови в обслуговуванні:

docker run \
  --memory=512m \
  --memory-swap=512m \
  --cpus=1.0 \
  --pids-limit=256 \
  --ulimit nofile=1024:1024 \
  my-app:latest

У Kubernetes завжди встановлюйте requests і limits ресурсів:

resources:
  requests:
    memory: "256Mi"
    cpu: "250m"
  limits:
    memory: "512Mi"
    cpu: "1000m"

8. Скидайте всі capabilities

Linux capabilities дають точний контроль над тим, що може робити процес. Скиньте їх усі та додайте назад лише те, що вам потрібно:

docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE my-app:latest

Поширені capabilities і коли вони вам справді потрібні:

CapabilityВипадок використання
NET_BIND_SERVICEПрив'язка до портів нижче 1024
CHOWNЗміна власника файлу (рідко потрібна)
SYS_PTRACEЛише для дебагу, ніколи в продакшні
SYS_ADMINМайже ніколи -- це майже еквівалентно root

9. Використовуйте multi-stage збірки, щоб виключити інструменти збірки

Залежності збірки, як-от компілятори, дебагери та вихідний код, ніколи не повинні з'являтися у вашому продакшн-образі:

# Stage 1: Build
FROM golang:1.22 AS builder
WORKDIR /src
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o /app/server ./cmd/server

# Stage 2: Production
FROM scratch
COPY --from=builder /app/server /server
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
EXPOSE 8080
ENTRYPOINT ["/server"]

Кінцевий образ містить лише скомпільований бінарник і TLS-сертифікати. Жодного Go toolchain, жодного вихідного коду, жодного shell.

10. Увімкніть Docker Content Trust і підписуйте образи

Переконайтеся, що у вашому середовищі запускаються лише перевірені образи:

# Enable content trust
export DOCKER_CONTENT_TRUST=1

# Sign and push
docker trust sign myregistry.com/my-app:v1.2.3

# Verify
docker trust inspect myregistry.com/my-app:v1.2.3

Для Kubernetes-середовищ використовуйте admission controllers, як-от Kyverno або OPA Gatekeeper, щоб забезпечувати політики образів:

# Kyverno policy: require signed images
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signatures
spec:
  validationFailureAction: Enforce
  rules:
    - name: verify-signature
      match:
        resources:
          kinds:
            - Pod
      verifyImages:
        - imageReferences:
            - "myregistry.com/*"
          attestors:
            - entries:
                - keys:
                    publicKeys: |-
                      -----BEGIN PUBLIC KEY-----
                      ...
                      -----END PUBLIC KEY-----

Чеклист безпеки

Перед розгортанням будь-якого контейнера в продакшн перевірте:

  • Базовий образ мінімальний (distroless, Alpine або scratch)
  • Образ просканований на CVE з нульовими знахідками HIGH/CRITICAL
  • Контейнер запускається як non-root користувач
  • Файлова система read-only, де це можливо
  • Жодних секретів, запечених в образ
  • Встановлені ліміти ресурсів
  • Усі непотрібні capabilities скинуті
  • Теги образів закріплені до digest
  • Образи підписані та перевірені
  • Network policies обмежують комунікацію контейнерів

Висновок

Безпека контейнерів — це не один інструмент чи одноразовий аудит. Це набір практик, вбудованих у кожен етап вашого конвеєра — від Dockerfile до runtime-конфігурації. Десять практик вище адресують найпоширеніші вектори атаки, які ми бачимо в продакшн-середовищах.

У DevOpsVibe ми допомагаємо командам будувати безпечні контейнерні конвеєри з нуля. Чи потрібен вам аудит безпеки існуючого налаштування, чи ви хочете впровадити DevSecOps-практики у вашій організації, у нашої команди є експертиза, щоб зробити це правильно. Зв'яжіться з нами, щоб розпочати розмову.

у категорії
dockerdockersecuritycontainersdevsecopshardeningkuberneteskubernetes
працювати з нами

Хочете, щоб наша команда допомогла з вашою інфраструктурою?

talk to an engineerFree 30-min discovery callBook
close