🛠 Docker Level Security
Давай еще поговорим про Docker и посмотрим повнимательнее на namespaces, cgroups, chroot, capabilities, seccomp.
Понимание того, что происходит под капотом и как оно работает, помогает выстраивать нормальную модель безопасности, которая позволяет контролировать сборку и разделять процессы, что решает огромные проблемы при экаплуатации.
Поэтому мы разберем то, на что тебе стоит обратить внимание в первую очередь.
• FS: chroot и mount
Внутри контейнера процессы видят корень как результат mount-операций и chroot. Поэтому следует не монтировать чувствительные директории (/var/run/docker.sock, /etc, /var/lib/docker) внутрь рабочих контейнеров. Тем более для stateful сервисов использовать чётко выделенные volume.
# Смотрим mount-namespace контейнера
pid=$(docker inspect -f '{{.State.Pid}}' my-container)
lsns -t mnt -p "$pid"
• Namespaces: изоляция процессов, сети и хостнейма
Напомню, что контейнер — это набор пространств имён: PID, NET, MNT, UTS, USER. Они отвечают за то, какие процессы ты видишь, какие точки монтирования доступны и т.д. Поэтому никогда не используй --pid=host и --network=host в обычных сервисах, потому что это антипаттерн изоляции. Для отладки следует использовать nsenter, а не пробрасывать сервисы напрямую на хост.
# Сравниваем namespace контейнера с хостом
pid=$(docker inspect -f '{{.State.Pid}}' ns-demo)
lsns -p "$pid"
# Войти в namespace контейнера для отладки
nsenter -t "$pid" -n ip addr
• cgroups лимиты
Они контролируют, сколько CPU/ памяти может потреблять группа процессов. Docker создаёт группы под каждый контейнер автоматически, но лимиты нужно задавать ручками. Поэтому для всех боевых сервисов обязательно задавать лимиты --cpus и -m, иначе один runaway процесс кладёт всю ноду. В мониторинге следить за OOMKilled и CPU throttling и при превышении необходимо пересмотреть лимиты.
# Ограничить контейнер по ресурсам
docker run -d --name web \
--cpus="1.0" \
-m 512m --memory-reservation=256m \
nginx:stable
# Смотреть потребление в реальном времени
docker stats web
• Capabilities
Docker по умолчанию урезает capabilities контейнера и выдает только часть привилегий ядра. Поэтому следует не использовать --privileged в проде ни при каких условиях, а также стартовая формула: --cap-drop=ALL и точечный --cap-add только того, без чего сервис реально не работает, как пример, - NET_BIND_SERVICE для портов 80/ 443.
# Посмотреть набор capabilities
docker run --rm -it alpine sh
apk add libcap && capsh --print | grep cap_
# Дропаем все, добавляем только нужное ручками
docker run --rm -it \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
-p 80:80 nginx:stable
• Seccomp и AppArmor
Даже когда злоумышленник внутри контейнера, - ядро может запретить опасные syscalls или операции с файлами через seccomp/ AppArmor. Docker применяет дефолтный профиль, но для чувствительных сервисов его нужно ужимать под конкретное приложение. Рекомендую включить логирование нарушений профиля, чтобы видеть попытки эскалирований и т.д. до того, как они станут эксплойтом.
# Запустить с кастомным профилем
docker run --rm -it \
--security-opt seccomp=/opt/seccomp/web.json \
alpine sh
# AppArmor
docker run --rm -it \
--security-opt apparmor=docker-default \
alpine sh
• Docker daemon и docker.sock
docker.sock это как root на хосте. Любой, у кого есть доступ к сокету, может запустить привилегированный контейнер и выйти на хост. Поэтому следует:
• не поднимать -H tcp://0.0.0.0:2375 без TLS и mTLS
• не монтировать /var/run/docker.sock в рабочие контейнеры
• для CI-раннеров использовать отдельный нод с отдельными правилами
• доступ к группам docker выдавать только с контролем Segregation-of-Duties
# Кто имеет доступ к сокету
ls -l /var/run/docker.sock
getent group docker
# Минимальный systemd-конфиг: только unix-сокет
ExecStart=/usr/bin/dockerd -H unix:///var/run/docker.sock
#appsec #devsecops #reco #specialty #containersecurity
