1.5. Docker socket и группа docker
Цели
После этого материала вы сможете:
- объяснить, что такое
/var/run/docker.sockи как через него работает CLI; - объяснить, почему членство в группе
dockerэквивалентно правамrootна host; - продемонстрировать этот факт на собственной учебной машине;
- осознанно выбрать между
sudo, группойdockerи rootless mode; - объяснить, почему монтирование Docker socket внутрь container опасно;
- добавить и удалить пользователя из группы
docker.
Предварительные знания
- 1.3. Docker daemon — архитектура daemon–client;
- 1.4. Первый container — запуск containers;
- права доступа Linux: владелец, группа, биты
rwx; - понимание, что делает
sudo.
Демонстрации в этом уроке выполняются только на собственной учебной машине. Цель — понять механизм, чтобы принимать осознанные решения о доступе.
Ключевые термины
| Термин | Объяснение |
|---|---|
Unix socket | Файл особого типа для связи между процессами на одной машине. Работает как канал, но с адресацией через путь в файловой системе |
docker.sock | Unix socket Docker daemon, обычно /var/run/docker.sock |
Docker Engine API | HTTP-подобный API daemon. CLI — просто клиент этого API |
эскалация привилегий | Получение прав выше выданных |
bind mount | Монтирование каталога или файла host внутрь container |
rootless mode | Режим, в котором daemon работает от обычного пользователя |
Теория
Что такое Docker socket
Daemon слушает Unix socket — специальный файл, через который процессы обмениваются данными:
ls -l /var/run/docker.sock
srw-rw---- 1 root docker 0 Jul 29 10:12 /var/run/docker.sock
Разбор вывода:
| Часть | Значение |
|---|---|
s в начале | Тип файла — socket |
rw- (владелец) | root может читать и писать |
rw- (группа) | Члены группы docker могут читать и писать |
--- (остальные) | Прочие пользователи не имеют доступа |
root docker | Владелец root, группа docker |
CLI отправляет в этот socket обычные HTTP-запросы. Убедиться можно напрямую:
sudo curl -s --unix-socket /var/run/docker.sock http://localhost/version | head -c 200
{"Platform":{"Name":"Docker Engine - Community"},"Components":[{"Name":"Engine","Version":"29.0.1",...
Это тот же ответ, который CLI форматирует в вывод docker version. Никакой магии: CLI — HTTP-клиент. Подробно API разбирается в разделе 17.
Почему группа docker — это root
Утверждение, которое нужно понять точно:
Возможность отправлять команды Docker daemon эквивалентна правам
rootна этой машине.
Причина не в уязвимости, а в самой природе инструмента. Daemon работает от root и по вашей команде готов:
- запустить container от любого пользователя, включая
root; - смонтировать любой каталог host внутрь container, включая
/; - дать container привилегии через
--privileged; - получить доступ к устройствам host.
Каждая из этих возможностей нужна для законных задач. Вместе они означают: кто управляет daemon, тот управляет машиной.
Ключевая деталь: это не требует sudo. Достаточно доступа к socket.
Демонстрация
Одна команда показывает суть:
docker run --rm -v /:/host alpine cat /host/etc/shadow
Что происходит:
-v /:/host— корневая файловая система host монтируется в container по пути/host;- процесс в container работает от
root(по умолчанию); /etc/shadowсодержит хэши паролей всех пользователей и доступен толькоroot;- container читает его без препятствий.
Файл /etc/shadow — лишь пример. Тем же способом можно записать свой ключ в /root/.ssh/authorized_keys, добавить пользователя в /etc/sudoers или изменить любой системный файл.
Более прямой путь к полноценной оболочке root:
docker run --rm -it --privileged --pid=host alpine nsenter -t 1 -m -u -n -i sh
Что происходит: --pid=host даёт container видимость процессов host; nsenter -t 1 входит в namespaces процесса с PID 1 — то есть в namespaces самой host-системы. Результат — оболочка root на host.
Вывод: пользователь в группе docker — это пользователь с root-доступом, только без записи в журнал sudo.
Три варианта работы
| Вариант | Как работает | Плюсы | Минусы |
|---|---|---|---|
sudo docker | Каждая команда через sudo | Действия попадают в журнал sudo; требуется явное повышение прав; можно ограничить через sudoers | Неудобно: sudo перед каждой командой; проблемы с путями и переменными окружения |
Группа docker | Пользователь добавлен в группу | Удобно; стандартная практика | Постоянный неявный root-доступ; любая скомпрометированная программа, работающая от вашего пользователя, получает root |
| Rootless mode | Daemon работает от вашего пользователя | Компрометация не даёт root на host; настоящее снижение привилегий | Ряд ограничений: порты ниже 1024, часть сетевых сценариев, производительность. Разбирается в уроке 1.6 |
Как выбрать
Решение зависит от того, кто ещё имеет доступ к машине и что на ней есть.
Группа docker приемлема, если это ваша личная рабочая машина, вы единственный пользователь, и вы всё равно имеете sudo без пароля или с ним. В этом случае группа не добавляет новых возможностей — вы и так можете стать root. Она лишь убирает журналирование и явность.
Группа docker неприемлема, если:
- на машине есть другие пользователи, которым не положен
root; - это production-сервер;
- действуют требования аудита, и нужен след в журнале;
- на машине выполняется недоверенный код (например, CI-раннер).
Rootless mode предпочтителен, если нужна реальная изоляция и ограничения не мешают вашим задачам.
Факт против рекомендации. Факт: группа
dockerдаёт права, эквивалентныеroot. Рекомендация: на личной машине это приемлемый компромисс, на общих и production-системах — нет. Курс не запрещает группуdocker; он требует принимать решение осознанно.
Docker socket внутри container
Отдельный случай той же проблемы. Встречается конструкция:
volumes:
- /var/run/docker.sock:/var/run/docker.sock
Её используют, чтобы container мог управлять другими containers: CI-раннеры, инструменты мониторинга, автоматические reverse proxy.
Последствие: container получает права root на host. Любая уязвимость в приложении внутри этого container — уязвимость всей машины. Изоляция становится декоративной.
Альтернативы:
| Задача | Безопасный вариант |
|---|---|
| Сборка образов внутри CI | Отдельный build-контекст без доступа к сокету host: BuildKit в отдельном container, Kaniko, buildah |
| Мониторинг containers | Read-only доступ через прокси API с ограничением набора эндпоинтов |
| Автоматический reverse proxy | Прокси с доступом только к чтению списка containers, через socket-proxy |
| Управление containers из приложения | Пересмотреть архитектуру: обычно это признак того, что задача решается не тем инструментом |
Если доступ к сокету всё же необходим — используйте socket-proxy, ограничивающий набор доступных эндпоинтов только чтением.
Внутренний механизм
Почему изменение группы требует новой сессии
Список групп пользователя определяется при входе в систему и хранится в процессе. Добавление в группу изменяет /etc/group, но уже запущенные процессы (включая вашу оболочку) продолжают работать со старым списком.
Проверить текущий список:
id
uid=1000(evg) gid=1000(evg) groups=1000(evg),4(adm),27(sudo)
Сравнить с содержимым файла:
getent group docker
docker:x:988:evg
Если пользователь есть в getent group docker, но не в выводе id — нужно переоткрыть сессию.
Права на socket создаются при каждом запуске
Socket пересоздаётся при старте docker.socket, и права задаются в его unit-файле:
systemctl cat docker.socket | grep -A3 '\[Socket\]'
[Socket]
ListenStream=/run/docker.sock
SocketMode=0660
SocketUser=root
SocketGroup=docker
Отсюда следует: изменение прав через chmod временно — при следующем перезапуске они вернутся. Правильный способ изменить их — drop-in для docker.socket.
Опасная практика.
sudo chmod 666 /var/run/docker.sockвстречается в интернете как «решение» проблемы с правами. Оно даётroot-эквивалентный доступ всем пользователям и всем процессам системы, включая работающие отnobody. Никогда не применяйте это. Правильные варианты: группаdocker,sudoили rootless mode.
Команды и примеры
Добавление пользователя в группу
sudo usermod -aG docker "$USER"
Разбор:
| Флаг | Значение |
|---|---|
-a | Append — добавить, не заменяя существующие группы |
-G | Список дополнительных групп |
Пропуск -a — известная ошибка: usermod -G docker user заменит все дополнительные группы пользователя на одну. Пользователь потеряет sudo, adm и остальные.
Применение изменений:
newgrp docker
Что делает: запускает новую оболочку с обновлённым списком групп. Действует только в этой оболочке.
Надёжнее — полностью переоткрыть сессию: выйти и войти заново, либо перезагрузиться.
Проверка:
id -nG | tr ' ' '\n' | grep -x docker
docker
Теперь команды работают без sudo:
docker version --format '{{.Server.Version}}'
Удаление пользователя из группы
sudo gpasswd -d "$USER" docker
Затем переоткрыть сессию.
Проверка:
id -nG | tr ' ' '\n' | grep -x docker || echo "не состоит в группе docker"
Настройка ограниченного доступа через sudoers
Компромисс между удобством и контролем: разрешить конкретному пользователю выполнять docker через sudo без пароля, оставив журналирование.
sudo visudo -f /etc/sudoers.d/docker
Содержимое:
evg ALL=(root) NOPASSWD: /usr/bin/docker
Это ограничивает удобство, но не ограничивает привилегии: пользователь всё равно может выполнить
sudo docker run -v /:/host .... Ценность конструкции — в журналировании и явности, а не в безопасности. Реальное ограничение даёт только rootless mode.
Обязательно используйте visudo: он проверяет синтаксис перед сохранением. Ошибка в sudoers, сохранённая обычным редактором, может лишить вас sudo полностью.
Проверка, кто имеет доступ
getent group docker
docker:x:988:evg,ci-runner
После двоеточия — список членов группы. На production-сервере этот список стоит проверять регулярно: каждое имя здесь — пользователь с root-эквивалентными правами.
Проверка прав на сам socket:
stat -c '%A %U:%G %n' /var/run/docker.sock
srw-rw---- root:docker /var/run/docker.sock
Ожидаемое значение — именно srw-rw----. Любые дополнительные права для «остальных» (например, srw-rw-rw-) означают, что кто-то выполнил chmod 666.
Практический пример
Скрипт аудита доступа к Docker. Полезен и на учебной, и на рабочей машине.
docker-check/
└── audit-access.sh
#!/usr/bin/env bash
# audit-access.sh — кто имеет root-эквивалентный доступ через Docker.
set -uo pipefail
SOCK="/var/run/docker.sock"
issues=0
echo "=== Docker socket ==="
if [ -S "$SOCK" ]; then
perms="$(stat -c '%a' "$SOCK")"
owner="$(stat -c '%U:%G' "$SOCK")"
printf ' Путь: %s\n' "$SOCK"
printf ' Права: %s (%s)\n' "$(stat -c '%A' "$SOCK")" "$perms"
printf ' Владелец: %s\n' "$owner"
if [ "$perms" != "660" ]; then
echo " [!] Ожидаются права 660. Текущие: $perms"
if [ "${perms: -1}" != "0" ]; then
echo " Socket доступен ВСЕМ пользователям системы."
echo " Это эквивалентно раздаче root всем. Исправьте немедленно."
fi
issues=$((issues + 1))
else
echo " [+] Права корректны"
fi
else
echo " [!] Socket не найден. Daemon запущен?"
issues=$((issues + 1))
fi
echo
echo "=== Члены группы docker ==="
members="$(getent group docker | cut -d: -f4)"
if [ -z "$members" ]; then
echo " Группа пуста. Доступ только через sudo."
else
echo " Каждый из перечисленных имеет root-эквивалентный доступ:"
echo "$members" | tr ',' '\n' | sed 's/^/ - /'
count="$(echo "$members" | tr ',' '\n' | grep -c .)"
if [ "$count" -gt 2 ]; then
echo " [!] Членов группы: $count. Проверьте, все ли нужны."
issues=$((issues + 1))
fi
fi
echo
echo "=== Текущий пользователь ==="
if id -nG | tr ' ' '\n' | grep -qx docker; then
echo " $USER состоит в группе docker (root-эквивалентный доступ)"
else
echo " $USER не состоит в группе docker"
fi
echo
echo "=== Containers с доступом к socket ==="
if docker info > /dev/null 2>&1; then
found=0
while read -r name; do
[ -z "$name" ] && continue
if docker inspect "$name" --format '{{range .Mounts}}{{.Source}}{{"\n"}}{{end}}' 2>/dev/null \
| grep -q '^/var/run/docker.sock$'; then
echo " [!] $name монтирует Docker socket (root-эквивалент на host)"
found=$((found + 1))
issues=$((issues + 1))
fi
done < <(docker ps --format '{{.Names}}')
[ "$found" -eq 0 ] && echo " [+] Не найдено"
else
echo " Пропущено: нет доступа к daemon"
fi
echo
if [ "$issues" -eq 0 ]; then
echo "Проблем не обнаружено."
else
echo "Обнаружено проблем: $issues"
fi
exit "$issues"
Запуск:
chmod +x audit-access.sh
./audit-access.sh
Ожидаемый вывод на корректно настроенной учебной машине:
=== Docker socket ===
Путь: /var/run/docker.sock
Права: srw-rw---- (660)
Владелец: root:docker
[+] Права корректны
=== Члены группы docker ===
Каждый из перечисленных имеет root-эквивалентный доступ:
- evg
=== Текущий пользователь ===
evg состоит в группе docker (root-эквивалентный доступ)
=== Containers с доступом к socket ===
[+] Не найдено
Проблем не обнаружено.
Проверка результата
Убедитесь, что вы можете ответить на три вопроса про свою систему:
# 1. Кто имеет доступ?
getent group docker
# 2. Корректны ли права на socket?
stat -c '%A' /var/run/docker.sock # ожидается srw-rw----
# 3. Есть ли containers с доступом к socket?
./audit-access.sh
Типичные ошибки
| Ошибка | Причина | Исправление |
|---|---|---|
usermod -G docker user без -a | Заменяет все дополнительные группы пользователя | Всегда использовать -aG. Восстановить группы: sudo usermod -aG sudo,adm,... user |
Группа добавлена, но docker ps требует прав | Список групп процесса не обновился | newgrp docker или переоткрыть сессию |
sudo chmod 666 /var/run/docker.sock | Пытаются починить права быстрым способом | Никогда так не делать. Использовать группу docker, sudo или rootless |
| Монтирование socket в container «для удобства» | Кажется безобидным техническим приёмом | Осознать: это root на host. Использовать socket-proxy или другой подход |
Мнение «группа docker безопасна, ведь это не sudo» | Название группы не отражает уровень привилегий | Проверить демонстрацией из этого урока |
Раздача группы docker всей команде на общем сервере | Удобство разработки | Rootless mode для каждого пользователя либо отдельные машины |
Правка /etc/sudoers обычным редактором | Ошибка синтаксиса ломает sudo целиком | Использовать visudo |
Контрольные вопросы
На понимание:
- Почему возможность отправлять команды Docker daemon равносильна правам
root? - Что означает буква
sв начале выводаls -l /var/run/docker.sock? - Почему после
usermod -aG docker $USERкоманды всё ещё требуютsudoдо переоткрытия сессии? - Чем
sudo dockerотличается от группыdockerс точки зрения фактических привилегий? - Почему монтирование Docker socket в container сводит его изоляцию к нулю?
На применение:
- Как проверить, кто на машине имеет root-эквивалентный доступ через Docker?
- Как безопасно убрать пользователя из группы
docker? - Как найти работающие containers, которым смонтирован Docker socket?
На диагностику:
stat -c '%A' /var/run/docker.sockвыдаётsrw-rw-rw-. Что произошло и что делать?- После добавления пользователя в группу
dockerон потерял возможность выполнятьsudo. Что случилось и как исправить?
Краткое резюме
- CLI общается с daemon через Unix socket
/var/run/docker.sockобычными HTTP-запросами. - Права на socket:
srw-rw----, владелецroot, группаdocker. - Доступ к socket эквивалентен правам
rootна host — это следствие возможностей Docker, а не уязвимость. - Демонстрация:
docker run -v /:/host alpine cat /host/etc/shadow. - Группа
dockerдаётrootбез записи в журналsudo. - На личной машине это приемлемый компромисс; на общих и production-системах — нет.
sudo dockerне ограничивает привилегии, но добавляет явность и журналирование.- Rootless mode — единственный вариант, реально снижающий привилегии.
- Монтирование socket внутрь container делает изоляцию декоративной.
chmod 666на socket недопустим ни при каких обстоятельствах.
Официальные источники
| Источник | Ссылка | Что подтверждает |
|---|---|---|
| Docker daemon attack surface | https://docs.docker.com/engine/security/#docker-daemon-attack-surface | «Only trusted users should be allowed to control your Docker daemon»; возможность монтировать любые каталоги host |
| Post-installation steps for Linux | https://docs.docker.com/engine/install/linux-postinstall/ | Добавление пользователя в группу docker и предупреждение о равносильности root |
| Docker Engine security | https://docs.docker.com/engine/security/ | Модель безопасности, capabilities, требование TLS для API по TCP |
| Rootless mode | https://docs.docker.com/engine/security/rootless/ | Альтернатива с реальным снижением привилегий |
| Protect the Docker daemon socket | https://docs.docker.com/engine/security/protect-access/ | Защита доступа к daemon, требования к TLS |
usermod(8) man page | https://man7.org/linux/man-pages/man8/usermod.8.html | Флаги -a и -G |
Навигация
← Предыдущий материал
Вернуться к разделу
Следующий материал → Rootless Docker
Главное оглавление