Безопасный Docker на Windows: как не допустить утечки данных контейнера

Главная проблема Docker на Windows не в том, что контейнер «взломали». Чаще данные утекают из-за привычных вещей: в контейнер смонтировали домашнюю папку, положили пароль в .env, открыли базу наружу или дали доступ к Docker socket. В таком сценарии контейнер перестаёт быть изолированной песочницей и начинает видеть то, что ему видеть не нужно.

Полностью убрать риск нельзя: если приложение внутри контейнера само печатает секреты в логи или получает их для работы, полностью спрятать их от процесса не получится. Но можно сделать так, чтобы обычная ошибка в docker-compose.yml не превращалась в утечку клиентской базы, SSH-ключей или токенов.

Что именно нужно защищать в Docker на Windows

На Windows с Docker Desktop чаще всего используют Linux-контейнеры через WSL2. Это удобно: код лежит в Windows-папке, база крутится в контейнере, IDE видит файлы как обычно. Но именно эта связка создаёт типичные риски.

Данные могут уйти через несколько мест:

  • bind mount — когда папка с Windows-диска подключается внутрь контейнера;
  • переменные окружения — особенно пароли, токены и строки подключения к базе;
  • Docker socket — доступ к /var/run/docker.sock или Windows named pipe фактически даёт управление Docker;
  • образы и слои сборки — секреты, попавшие в Dockerfile или COPY ., могут остаться в истории образа;
  • логи — приложение может записать туда пароль, API-ключ или фрагмент ответа базы;
  • открытые порты — база или админка, опубликованная на 0.0.0.0, может стать доступна не только тебе;
  • бэкапы и дампы.sql, .dump и копии volumes часто лежат там же, где и проект.

Поэтому безопасная работа с Docker на Windows — это не один переключатель, а набор привычек: что монтировать, куда класть секреты, какие порты открывать и какие образы запускать без проверки.

Bind mount: удобство, которое часто ломает изоляцию

Bind mount — это когда ты говоришь Docker: «возьми папку с моего компьютера и покажи её внутри контейнера». Для разработки это нормально. Например, приложение в контейнере читает код из C:\dev\my-app, а ты редактируешь его в VS Code.

Проблема начинается, когда монтируют слишком много:

-v C:\Users\ivan:/home/app

Так делать не нужно. Внутри контейнера окажется не только проект, но и документы, кэши, конфиги, возможно .ssh, .aws, .kube, старые дампы и forgotten .env-файлы. Если приложение скомпрометировано, оно сможет читать всё, что попало в эту смонтированную область.

Безопаснее монтировать только конкретную папку проекта:

-v C:\dev\my-app:/app

А ещё лучше — держать проект в отдельной рабочей папке, где нет личных данных, бэкапов и секретов от других проектов. Для конфигов, которые контейнеру нужно только читать, можно подключать отдельный файл:

-v C:\dev\my-app\config\appsettings.json:/app/appsettings.json:ro

:ro означает read-only. Это защищает от случайной записи, но не от чтения. Если контейнеру нужен файл, он сможет его прочитать. Поэтому read-only не спасает, если внутри папки лежит секрет.

Named volume лучше bind mount для баз данных

Для PostgreSQL, MySQL, Redis и других баз на локальной разработке чаще нужен не bind mount, а named volume. Это хранилище Docker, которое не лежит прямо в папке проекта как обычная директория.

Например, вместо такого варианта:

volumes:
  - C:\dev\pgdata:/var/lib/postgresql/data

лучше использовать так:

volumes:
  - pgdata:/var/lib/postgresql/data

Так данные базы не смешиваются с исходниками, их проще контролировать, и меньше шанс случайно отправить их в архив, репозиторий или чат. Но volume — не шифрование. Если ноутбук украдут, а диск Windows не зашифрован, данные всё равно могут оказаться под угрозой. Для рабочего ноутбука с реальными данными стоит включать шифрование диска, например BitLocker.

Подход Когда использовать Типичный риск Как безопаснее
named volume для базы Локальная PostgreSQL, MySQL, Redis Данные можно потерять при prune --volumes или скопировать без шифрования Использовать volume, не публиковать порт наружу, делать бэкапы в защищённое место
Bind mount проекта Разработка приложения, hot reload Контейнер видит всё, что лежит в папке проекта Монтировать только репозиторий, убрать .env, дампы, ключи и лишние конфиги
Bind mount домашней папки Почти никогда Высокий риск утечки личных и рабочих файлов Не использовать; подключать только конкретные файлы или папки
Docker socket Только для инструментов, которым реально нужно управлять Docker Фактически root-доступ к Docker и возможность монтировать любые разрешённые пути Не монтировать в обычные контейнеры; для сомнительных инструментов использовать отдельную VM
Секреты в docker-compose.yml Быстрый старт, но плохая практика Пароли попадают в историю Git, скриншоты, логи и конфиги Использовать .env.local, secret-файлы или внешнее хранилище секретов

Как правильно хранить секреты

Самая частая ошибка — положить пароль в docker-compose.yml и забыть, что этот файл может попасть в Git, архив, тикет или запись экрана.

Для локальной разработки нормальная схема такая:

  • в репозитории лежит .env.example с пустыми или демонстрационными значениями;
  • реальный файл называется .env.local и добавлен в .gitignore;
  • пароли и токены не хранятся в Dockerfile;
  • секретные файлы лежат вне публичной части проекта или хотя бы не попадают в сборочный контекст;
  • если секрет засветился — его нужно считать скомпрометированным и заменить.

Пример docker-compose.yml для приложения и базы:

services:
  db:
    image: postgres:16
    environment:
      POSTGRES_DB: app
      POSTGRES_USER: app
      POSTGRES_PASSWORD_FILE: /run/secrets/db_password
    volumes:
      - pgdata:/var/lib/postgresql/data
    ports:
      - "127.0.0.1:5432:5432"
    secrets:
      - db_password

  app:
    build: .
    env_file:
      - .env.local
    volumes:
      - .:/app
    ports:
      - "127.0.0.1:3000:3000"
    depends_on:
      - db
    secrets:
      - db_password

volumes:
  pgdata:

secrets:
  db_password:
    file: ./secrets/db_password.txt

Такой файл не делает контейнер «неуязвимым», но убирает главные бытовые утечки: пароль не лежит в compose-файле, база не торчит в локальную сеть, данные БД хранятся в volume, а код монтируется только из папки проекта.

Отдельно: .dockerignore защищает сборку образа, но не runtime bind mount. Если ты смонтировал папку проекта внутрь контейнера, контейнер увидит файлы независимо от .dockerignore. Поэтому .dockerignore и чистая структура проекта нужны оба.

Минимальный .dockerignore:

.git
.env
.env.*
!.env.example
node_modules
dist
build
coverage
*.log
*.dump
*.sql
.ssh
.aws
.kube
Dockerfile
docker-compose*.yml

Список можно менять под проект, но смысл простой: в образ не должно попадать то, что не нужно для работы приложения.

Docker socket: красная зона на Windows

Если контейнер получил доступ к Docker socket, считай, что он получил мощный контроль над Docker. Через этот API можно запускать другие контейнеры, монтировать хост-пути, читать volumes и обходить обычную изоляцию. Для локальной разработки это одна из самых опасных настроек.

Иногда socket монтируют «просто чтобы посмотреть контейнеры», «для Portainer», «для CI

Оцените статью
PEFile — Безопасность и технологии простым языком