Главная проблема 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
