← До програми курсу

Модуль 13 / 18

Безпека: rootless, capabilities, seccomp і LSM

DOCKER

Зміст
01

1. Цілі та модель загроз

Після модуля ви зможете застосовувати least privilege шарами та діагностувати відмову без небезпечного --privileged.

  • non-root зменшує наслідки
  • capabilities дроблять права root
  • seccomp обмежує syscalls
  • AppArmor/SELinux обмежують доступ
  • read-only rootfs і no-new-privileges звужують зміни
02

2. Теорія: межа контейнера

Namespaces і cgroups не роблять container рівним VM: процеси поділяють kernel attack surface. Docker daemon і docker.sock зазвичай дають root-equivalent контроль host.

--privileged видає capabilities, devices і послаблює LSM, тому не виправляє Permission denied.

03

3. Теорія: user та user namespaces

USER або --user задає UID/GID процесу, але root звичайного container лишається UID 0 host у своєму namespace. Bind permissions числові.

userns-remap відображає root у subordinate UID. Rootless запускає daemon без host root, але має окремий context та обмеження network/storage/cgroup.

04

4. Теорія: Linux capabilities

Docker зберігає обмежений default-набір. Починайте з --cap-drop ALL і додавайте одну доведену capability. NET_RAW дозволяє raw sockets, NET_ADMIN змінює network, SYS_ADMIN надто широка.

Capability не скасовує seccomp, LSM і filesystem permissions: діє перетин шарів.

05

5. Теорія: seccomp і no-new-privileges

Default seccomp profile дозволяє сумісні syscalls і повертає EPERM для небезпечних. Не використовуйте unconfined без аналізу syscall.

no-new-privileges забороняє отримати додаткові privileges через execve, setuid або file capabilities.

06

6. Теорія: AppArmor, SELinux і filesystem

Docker застосовує docker-default AppArmor або SELinux labels. Denial шукають в audit, а не обходять вимкненням LSM.

Read-only rootfs потребує точкових writable mounts для /tmp, cache, sockets і даних; noexec/nosuid зменшують ризик.

07

7. Методичні вказівки

  • Почніть з threat model
  • Використовуйте non-root numeric UID
  • Drop ALL, потім мінімальний add
  • Read-only rootfs та точкові mounts
  • Забороняйте privilege escalation
  • Не монтуйте docker.sock і host root
  • Фіксуйте HostConfig
  • Тестуйте негативні сценарії
08

8. Лабораторна робота

Порівняйте default і hardened containers за UID, CapEff, seccomp та HostConfig.

terminal
docker info --format '{{json .SecurityOptions}}' | jq
docker run --name docker-m13-default alpine:3.21 sh -c 'id; grep -E "^(CapEff|CapBnd|NoNewPrivs|Seccomp):" /proc/1/status; cat /proc/1/attr/current 2>/dev/null || true; touch /root/default-write'
docker inspect docker-m13-default --format '{{json .HostConfig}}' | jq '{Privileged,ReadonlyRootfs,CapAdd,CapDrop,SecurityOpt,NetworkMode}'
terminal
docker run --name docker-m13-hardened --user 65532:65532 --read-only --tmpfs /tmp:rw,noexec,nosuid,size=16m --cap-drop ALL --security-opt no-new-privileges --pids-limit 64 --network none alpine:3.21 sh -c 'id; grep -E "^(CapEff|CapBnd|NoNewPrivs|Seccomp):" /proc/1/status; touch /tmp/allowed; if touch /etc/forbidden 2>/dev/null; then exit 1; else echo rootfs_read_only=enforced; fi; test ! -e /proc/net/route || cat /proc/net/route'
docker inspect docker-m13-hardened --format '{{json .HostConfig}}' | jq '{Privileged,ReadonlyRootfs,CapAdd,CapDrop,SecurityOpt,NetworkMode,PidsLimit}'
docker diff docker-m13-hardened

Доведіть дію CHOWN та default seccomp.

terminal
docker run --rm alpine:3.21 sh -c 'touch /tmp/item; chown 1000:1000 /tmp/item; stat -c "uid=%u gid=%g" /tmp/item'
docker run --rm --cap-drop ALL alpine:3.21 sh -c 'touch /tmp/item; if chown 1000:1000 /tmp/item 2>/dev/null; then exit 1; else echo chown_capability_removed; fi'
docker run --rm --cap-drop ALL --cap-add CHOWN alpine:3.21 sh -c 'touch /tmp/item; chown 1000:1000 /tmp/item; stat -c "uid=%u gid=%g" /tmp/item'
terminal
docker run --rm alpine:3.21 sh -c 'grep "^Seccomp:" /proc/1/status; if unshare --user true 2>/dev/null; then echo unexpected; else echo unshare_denied; fi'
docker inspect docker-m13-hardened --format 'apparmor={{.AppArmorProfile}} security={{json .HostConfig.SecurityOpt}}'

Критерій: non-root, CapEff=0, NoNewPrivs=1, read-only, network none; точковий CHOWN дозволяє зміну власника.

09

9. Діагностика Permission denied

Відмову може дати DAC, capability, seccomp, LSM, read-only mount або device cgroup. Визначте шар до зміни policy.

terminal
docker inspect docker-m13-hardened --format '{{json .State}}' | jq
docker inspect docker-m13-hardened --format '{{json .HostConfig}}' | jq
docker logs docker-m13-hardened
docker events --since 10m --until 0s --filter container=docker-m13-hardened
journalctl -k --since '10 minutes ago' --no-pager | grep -Ei 'apparmor|selinux|seccomp|denied' | tail -30 || true
  • id/stat — identity та mode
  • CapEff/CapBnd — capability
  • Seccomp/SecurityOpt — syscall policy
  • AppArmorProfile/audit — LSM
  • Mounts/ReadonlyRootfs — filesystem
  • devices та namespaces — exposure
10

10. Контроль і cleanup

  • Чому container root не безпечний?
  • Rootless чи --user?
  • Чому docker.sock root-equivalent?
  • Навіщо drop ALL?
  • Що робить seccomp?
  • Що забороняє no-new-privileges?
  • Як знайти LSM denial?
  • Чому не --privileged?
terminal
docker rm docker-m13-default docker-m13-hardened
docker ps -a --filter name=docker-m13

Модуль засвоєно, якщо ви видаєте лише доведено потрібні права й локалізуєте denial за шарами.