Главная/Подготовка среды/Урок

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.sockUnix socket Docker daemon, обычно /var/run/docker.sock
Docker Engine APIHTTP-подобный API daemon. CLI — просто клиент этого API
эскалация привилегийПолучение прав выше выданных
bind mountМонтирование каталога или файла host внутрь container
rootless modeРежим, в котором daemon работает от обычного пользователя

Теория

Что такое Docker socket

Daemon слушает Unix socket — специальный файл, через который процессы обмениваются данными:

bash
ls -l /var/run/docker.sock
text
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-запросы. Убедиться можно напрямую:

bash
sudo curl -s --unix-socket /var/run/docker.sock http://localhost/version | head -c 200
text
{"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.

Демонстрация

Одна команда показывает суть:

bash
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:

bash
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 modeDaemon работает от вашего пользователяКомпрометация не даёт root на host; настоящее снижение привилегийРяд ограничений: порты ниже 1024, часть сетевых сценариев, производительность. Разбирается в уроке 1.6

Как выбрать

Решение зависит от того, кто ещё имеет доступ к машине и что на ней есть.

Группа docker приемлема, если это ваша личная рабочая машина, вы единственный пользователь, и вы всё равно имеете sudo без пароля или с ним. В этом случае группа не добавляет новых возможностей — вы и так можете стать root. Она лишь убирает журналирование и явность.

Группа docker неприемлема, если:

  • на машине есть другие пользователи, которым не положен root;
  • это production-сервер;
  • действуют требования аудита, и нужен след в журнале;
  • на машине выполняется недоверенный код (например, CI-раннер).

Rootless mode предпочтителен, если нужна реальная изоляция и ограничения не мешают вашим задачам.

Факт против рекомендации. Факт: группа docker даёт права, эквивалентные root. Рекомендация: на личной машине это приемлемый компромисс, на общих и production-системах — нет. Курс не запрещает группу docker; он требует принимать решение осознанно.

Docker socket внутри container

Отдельный случай той же проблемы. Встречается конструкция:

yaml
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
Мониторинг containersRead-only доступ через прокси API с ограничением набора эндпоинтов
Автоматический reverse proxyПрокси с доступом только к чтению списка containers, через socket-proxy
Управление containers из приложенияПересмотреть архитектуру: обычно это признак того, что задача решается не тем инструментом

Если доступ к сокету всё же необходим — используйте socket-proxy, ограничивающий набор доступных эндпоинтов только чтением.


Внутренний механизм

Почему изменение группы требует новой сессии

Список групп пользователя определяется при входе в систему и хранится в процессе. Добавление в группу изменяет /etc/group, но уже запущенные процессы (включая вашу оболочку) продолжают работать со старым списком.

Проверить текущий список:

bash
id
text
uid=1000(evg) gid=1000(evg) groups=1000(evg),4(adm),27(sudo)

Сравнить с содержимым файла:

bash
getent group docker
text
docker:x:988:evg

Если пользователь есть в getent group docker, но не в выводе id — нужно переоткрыть сессию.

Права на socket создаются при каждом запуске

Socket пересоздаётся при старте docker.socket, и права задаются в его unit-файле:

bash
systemctl cat docker.socket | grep -A3 '\[Socket\]'
text
[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.


Команды и примеры

Добавление пользователя в группу

bash
sudo usermod -aG docker "$USER"

Разбор:

ФлагЗначение
-aAppend — добавить, не заменяя существующие группы
-GСписок дополнительных групп

Пропуск -a — известная ошибка: usermod -G docker user заменит все дополнительные группы пользователя на одну. Пользователь потеряет sudo, adm и остальные.

Применение изменений:

bash
newgrp docker

Что делает: запускает новую оболочку с обновлённым списком групп. Действует только в этой оболочке.

Надёжнее — полностью переоткрыть сессию: выйти и войти заново, либо перезагрузиться.

Проверка:

bash
id -nG | tr ' ' '\n' | grep -x docker
text
docker

Теперь команды работают без sudo:

bash
docker version --format '{{.Server.Version}}'

Удаление пользователя из группы

bash
sudo gpasswd -d "$USER" docker

Затем переоткрыть сессию.

Проверка:

bash
id -nG | tr ' ' '\n' | grep -x docker || echo "не состоит в группе docker"

Настройка ограниченного доступа через sudoers

Компромисс между удобством и контролем: разрешить конкретному пользователю выполнять docker через sudo без пароля, оставив журналирование.

bash
sudo visudo -f /etc/sudoers.d/docker

Содержимое:

text
evg ALL=(root) NOPASSWD: /usr/bin/docker

Это ограничивает удобство, но не ограничивает привилегии: пользователь всё равно может выполнить sudo docker run -v /:/host .... Ценность конструкции — в журналировании и явности, а не в безопасности. Реальное ограничение даёт только rootless mode.

Обязательно используйте visudo: он проверяет синтаксис перед сохранением. Ошибка в sudoers, сохранённая обычным редактором, может лишить вас sudo полностью.

Проверка, кто имеет доступ

bash
getent group docker
text
docker:x:988:evg,ci-runner

После двоеточия — список членов группы. На production-сервере этот список стоит проверять регулярно: каждое имя здесь — пользователь с root-эквивалентными правами.

Проверка прав на сам socket:

bash
stat -c '%A %U:%G %n' /var/run/docker.sock
text
srw-rw---- root:docker /var/run/docker.sock

Ожидаемое значение — именно srw-rw----. Любые дополнительные права для «остальных» (например, srw-rw-rw-) означают, что кто-то выполнил chmod 666.


Практический пример

Скрипт аудита доступа к Docker. Полезен и на учебной, и на рабочей машине.

text
docker-check/
└── audit-access.sh
bash
#!/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"

Запуск:

bash
chmod +x audit-access.sh
./audit-access.sh

Ожидаемый вывод на корректно настроенной учебной машине:

text
=== Docker socket ===
  Путь:    /var/run/docker.sock
  Права:   srw-rw---- (660)
  Владелец: root:docker
  [+] Права корректны

=== Члены группы docker ===
  Каждый из перечисленных имеет root-эквивалентный доступ:
    - evg

=== Текущий пользователь ===
  evg состоит в группе docker (root-эквивалентный доступ)

=== Containers с доступом к socket ===
  [+] Не найдено

Проблем не обнаружено.

Проверка результата

Убедитесь, что вы можете ответить на три вопроса про свою систему:

bash
# 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

Контрольные вопросы

На понимание:

  1. Почему возможность отправлять команды Docker daemon равносильна правам root?
  2. Что означает буква s в начале вывода ls -l /var/run/docker.sock?
  3. Почему после usermod -aG docker $USER команды всё ещё требуют sudo до переоткрытия сессии?
  4. Чем sudo docker отличается от группы docker с точки зрения фактических привилегий?
  5. Почему монтирование Docker socket в container сводит его изоляцию к нулю?

На применение:

  1. Как проверить, кто на машине имеет root-эквивалентный доступ через Docker?
  2. Как безопасно убрать пользователя из группы docker?
  3. Как найти работающие containers, которым смонтирован Docker socket?

На диагностику:

  1. stat -c '%A' /var/run/docker.sock выдаёт srw-rw-rw-. Что произошло и что делать?
  2. После добавления пользователя в группу docker он потерял возможность выполнять sudo. Что случилось и как исправить?

Краткое резюме

  1. CLI общается с daemon через Unix socket /var/run/docker.sock обычными HTTP-запросами.
  2. Права на socket: srw-rw----, владелец root, группа docker.
  3. Доступ к socket эквивалентен правам root на host — это следствие возможностей Docker, а не уязвимость.
  4. Демонстрация: docker run -v /:/host alpine cat /host/etc/shadow.
  5. Группа docker даёт root без записи в журнал sudo.
  6. На личной машине это приемлемый компромисс; на общих и production-системах — нет.
  7. sudo docker не ограничивает привилегии, но добавляет явность и журналирование.
  8. Rootless mode — единственный вариант, реально снижающий привилегии.
  9. Монтирование socket внутрь container делает изоляцию декоративной.
  10. chmod 666 на socket недопустим ни при каких обстоятельствах.

Официальные источники

ИсточникСсылкаЧто подтверждает
Docker daemon attack surfacehttps://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 Linuxhttps://docs.docker.com/engine/install/linux-postinstall/Добавление пользователя в группу docker и предупреждение о равносильности root
Docker Engine securityhttps://docs.docker.com/engine/security/Модель безопасности, capabilities, требование TLS для API по TCP
Rootless modehttps://docs.docker.com/engine/security/rootless/Альтернатива с реальным снижением привилегий
Protect the Docker daemon sockethttps://docs.docker.com/engine/security/protect-access/Защита доступа к daemon, требования к TLS
usermod(8) man pagehttps://man7.org/linux/man-pages/man8/usermod.8.htmlФлаги -a и -G

Навигация

← Предыдущий материал
Вернуться к разделу
Следующий материал → Rootless Docker
Главное оглавление

Markdown на GitHub ↗