Урок 10. Безопасность Docker
Docker удобен, но он работает от root и сам настраивает сеть сервера. Несколько привычных действий — добавить пользователя в группу, опубликовать порт, подключить сокет — незаметно открывают сервер целиком. В этом уроке — что именно опасно и как делать безопаснее.
Группа docker — это root
Служба Docker работает от root. Кто может отдавать ей команды — через группу docker или sudo docker, — тот может запустить контейнер с подключённым корнем системы / и с правами root изменить любой файл хоста: пароли, ключи SSH, настройки. Никакого пароля Docker при этом не спросит.
Добавлять пользователя в группу docker — то же самое, что дать ему пароль root. Не добавляйте туда служебных пользователей сайтов, ботов и программ, которым вы не доверяете полностью.
Что делать:
- в группе
docker— только администраторы сервера; - если Docker нужен обычным пользователям без прав root, есть режим rootless: служба Docker работает от имени пользователя, и контейнер не может получить root хоста. У режима есть ограничения; описание и установка — в документации Docker (Rootless mode).
Сокет Docker внутри контейнера
Некоторые программы (панели управления контейнерами, обратные прокси, системы мониторинга) просят подключить в контейнер файл /var/run/docker.sock. Через этот файл идут все команды Docker.
Контейнер с подключённым /var/run/docker.sock управляет Docker, а значит — имеет root на хосте. Если такую программу взломают, взломан весь сервер. Подключайте сокет только программам, которым доверяете, и только если без этого нельзя.
Порты в обход ufw
На Linux Docker публикует порты (-p, ports: в Compose) своими правилами iptables (встроенного брандмауэра Linux, которым управляет и ufw), и они срабатывают раньше правил ufw. Порт контейнера, опубликованный как -p 5432:5432, открыт для всей сети, даже если в ufw стоит запрет или ufw разрешает только SSH.
Как правильно:
- не публикуйте порты, которые нужны только другим контейнерам: базы данных, кэши, внутренние API. В общей сети Docker они доступны без
-p(урок Сети контейнеров); - то, что нужно только самому серверу, публикуйте на локальный адрес:
-p 127.0.0.1:8080:80; - наружу — только то, что должно быть видно всем: обычно один обратный прокси на портах 80 и 443.
Проверка снаружи — с другого компьютера: nc -zv адрес-сервера 5432 должен ответить отказом или не дождаться ответа. Подробно, с примерами, — в справке Docker открывает порты в обход ufw. Как работает сам ufw — в теме Ubuntu.
Docker API по сети
Службой Docker можно управлять и по сети — через TCP-порт (обычно 2375 без шифрования и 2376 с TLS). Такой доступ не включён по умолчанию, и включать его без крайней нужды не стоит.
Открытый порт 2375 без TLS — это root на сервере для любого, кто до него достучится. Такие серверы ищут автоматически и захватывают, например, для майнинга. Для удалённой работы используйте SSH: docker -H ssh://ivan@192.168.1.10 ps или контексты docker context.
Образы: откуда и какой версии
- Берите официальные образы Docker Hub и образы от самих разработчиков программ. У неизвестного автора в образе может оказаться что угодно.
- Указывайте теги версий (
postgres:18,nginx:1.30) — так обновления происходят, когда вы решили, а не при случайномpull. - Обновляйте образы регулярно (урок Обновление и очистка места): старые образы содержат известные уязвимости.
- Чем меньше в образе лишнего, тем меньше уязвимостей: варианты
slimиalpineобычно лучше полных.
Контейнер не от root
По умолчанию программа в контейнере работает от root контейнера. Это не root хоста, но при уязвимости в Docker или в ядре такой процесс опаснее. В своих образах создавайте обычного пользователя и переключайтесь на него инструкцией USER:
FROM python:3.13-slim
WORKDIR /app
COPY . .
RUN useradd --create-home appuser
USER appuser
CMD ["python", "app.py"]Всё после USER — и при сборке, и при запуске — выполняется от appuser. Файлы, скопированные до USER, принадлежат root: программа может их читать, но не менять. Если ей нужно писать в свою папку, выдайте права при копировании: COPY --chown=appuser:appuser . . (пользователь должен быть создан раньше этой строки).
Опасные ключи
--privileged— снимает почти все ограничения: контейнер видит устройства хоста и может выйти за пределы изоляции. Нужен редко и только для особых задач.-v /:/hostи подключение системных папок (/etc,/root) — прямой доступ к файлам хоста.--network host— без сетевой изоляции, все порты программы сразу открыты на хосте.--pid host— контейнер видит все процессы хоста.
Если инструкция к программе требует такой ключ, разберитесь, зачем, прежде чем запускать.
Пароли и ключи
- Не пишите пароли в Dockerfile (
ENV PASSWORD=…,ARG) — они останутся в образе, и их покажетdocker historyилиdocker inspectлюбому, у кого есть образ. - Передавайте их при запуске:
-e,--env-file, файл.envдля Compose. Файлы с паролями — с правами600и не в git: добавьте.envв.gitignore(тема Git) и в.dockerignore. - Переменные работающего контейнера видны в
docker inspect, то есть всем участникам группыdocker.
Ограничения ресурсов
Контейнер без ограничений может занять всю память и весь процессор хоста — из-за ошибки или атаки. Лимиты задаются при запуске:
docker run -d --name web --memory 512m --cpus 1 -p 8080:80 nginx:alpine--memory 512m — не больше 512 МБ памяти: если программе станет не хватать, ядро завершит её процесс. --cpus 1 — не больше одного ядра процессора.
Коротко
- Группа
dockerи сокет/var/run/docker.sockравны root на хосте — только для доверенных. - Опубликованные порты обходят ufw: базы не публикуйте, служебное — на
127.0.0.1. - Docker API по TCP без TLS не открывайте; удалённо — через SSH.
- Официальные образы, теги версий, регулярные обновления.
- В своих образах —
USERс обычным пользователем; без нужды не используйте--privilegedи-v /:/…. - Пароли — при запуске и не в git; лимиты
--memoryи--cpusзащищают от «прожорливых» контейнеров.