Глиф Учебники

Урок 10. Безопасность Docker

Курс «Docker» · урок 10 из 10 · 6 минут чтения

Docker удобен, но он работает от root и сам настраивает сеть сервера. Несколько привычных действий — добавить пользователя в группу, опубликовать порт, подключить сокет — незаметно открывают сервер целиком. В этом уроке — что именно опасно и как делать безопаснее.

Группа docker — это root

Служба Docker работает от root. Кто может отдавать ей команды — через группу docker или sudo docker, — тот может запустить контейнер с подключённым корнем системы / и с правами root изменить любой файл хоста: пароли, ключи SSH, настройки. Никакого пароля Docker при этом не спросит.

Добавлять пользователя в группу docker — то же самое, что дать ему пароль root. Не добавляйте туда служебных пользователей сайтов, ботов и программ, которым вы не доверяете полностью.

Что делать:

Сокет Docker внутри контейнера

Некоторые программы (панели управления контейнерами, обратные прокси, системы мониторинга) просят подключить в контейнер файл /var/run/docker.sock. Через этот файл идут все команды Docker.

Контейнер с подключённым /var/run/docker.sock управляет Docker, а значит — имеет root на хосте. Если такую программу взломают, взломан весь сервер. Подключайте сокет только программам, которым доверяете, и только если без этого нельзя.

пользователь в группе docker или с правом на sudo docker программа в контейнере с подключённым /var/run/docker.sock служба dockerd работает от root запускает любой контейнер без пароля контейнер с подключённым / хоста меняет пароли ключи SSH любые файлы хоста
Участник группы docker и программа с подключённым docker.sock управляют службой, работающей от root, а через неё — любым файлом хоста.

Порты в обход ufw

На Linux Docker публикует порты (-p, ports: в Compose) своими правилами iptables (встроенного брандмауэра Linux, которым управляет и ufw), и они срабатывают раньше правил ufw. Порт контейнера, опубликованный как -p 5432:5432, открыт для всей сети, даже если в ufw стоит запрет или ufw разрешает только SSH.

Как правильно:

Порт опубликован: -p 5432:5432 другой компьютер сервер правила Docker срабатывают первыми база db:5432 открыта всем запрет 5432 в ufw не срабатывает сразу в контейнер Как безопаснее другой компьютер сервер обратный прокси порты 80 и 443 панель 127.0.0.1:8080 база db без -p
Опубликованный порт Docker открывает своими правилами раньше ufw, поэтому наружу публикуют только прокси, служебное — на 127.0.0.1, а базы — без -p.

Проверка снаружи — с другого компьютера: 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.

Образы: откуда и какой версии

Контейнер не от root

По умолчанию программа в контейнере работает от root контейнера. Это не root хоста, но при уязвимости в Docker или в ядре такой процесс опаснее. В своих образах создавайте обычного пользователя и переключайтесь на него инструкцией USER:

Dockerfile
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 . . (пользователь должен быть создан раньше этой строки).

Опасные ключи

Если инструкция к программе требует такой ключ, разберитесь, зачем, прежде чем запускать.

Пароли и ключи

пароль в образе ENV PASSWORD=… в Dockerfile образ пароль записан в него навсегда любой, у кого есть образ, прочтёт: docker history пароль при запуске образ без паролей .env 600, не в git -e, --env-file работающий контейнер в образ пароль не попадает виден в docker inspect — всем участникам группы docker
Пароль из ENV в Dockerfile навсегда остаётся в образе, а переданный при запуске через -e или .env есть только в работающем контейнере.

Ограничения ресурсов

Контейнер без ограничений может занять всю память и весь процессор хоста — из-за ошибки или атаки. Лимиты задаются при запуске:

Bash
docker run -d --name web --memory 512m --cpus 1 -p 8080:80 nginx:alpine

--memory 512m — не больше 512 МБ памяти: если программе станет не хватать, ядро завершит её процесс. --cpus 1 — не больше одного ядра процессора.

Коротко

Проверьте себя

1. Почему участник группы docker фактически имеет права root?
2. В ufw закрыт порт 3306, а контейнер с MariaDB запущен с -p 3306:3306. Доступна ли база из сети?
3. Чем опасно подключить /var/run/docker.sock в контейнер?
4. Где безопаснее хранить пароль базы для своего образа?