Зачем
Исторически: root всемогущий, юзер ничего не может. Это всё-или-ничего. Хотел бы пинговать → нужен raw socket → нужен root → но тогда программа может ВСЁ.
Capabilities делят это «всемогущество» на ~40 битов, каждый - конкретное
право. Программе нужен только CAP_NET_RAW (создать ICMP-сокет), а не
все остальные 39 бит. На контейнерах это критично - --cap-drop=ALL
--cap-add=NET_BIND_SERVICEдаёт практически безопасный root.
Самые ходовые capabilities
| CAP | Что разрешает |
|---|---|
| NET_ADMIN | ip, tc, iptables, nft, sysctl сети |
| NET_RAW | raw sockets (ping, tcpdump) |
| NET_BIND_SERVICE | bind на порты <1024 (80, 443, 22) |
| SYS_ADMIN | mount, umount, swapon, и куча всего - почти как root |
| SYS_PTRACE | ptrace чужие процессы (gdb, strace других юзеров) |
| SYS_TIME | менять системное время |
| SYS_NICE | priority, real-time scheduling |
| SYS_RESOURCE | поднимать ulimit-ы |
| CHOWN | менять владельца файлов |
| DAC_OVERRIDE | обходить file-permissions чтения/записи |
| DAC_READ_SEARCH | то же только для чтения |
| SETUID / SETGID | менять UID/GID процесса |
| KILL | посылать signals любому процессу |
| MKNOD | создавать device-nodes |
| AUDIT_WRITE | писать в audit log (нужен для login) |
| BPF | загружать eBPF-программы (новее) |
| PERFMON | perf без root (с CONFIG_BPF) |
Полный список - man 7 capabilities или capsh --print.
Где capabilities у процесса
Процесс держит 5 наборов capabilities (через bitmasks в task_struct):
- Permitted (P) - что МОЖНО получить
- Effective (E) - что АКТИВНО прямо сейчас
- Inheritable (I) - что унаследует exec'нутая программа
- Bounding (B) - потолок: за этот set не выйти даже эскалацией
- Ambient (A) - наследуется при exec без setuid (новее, для не-root юзеров)
cat /proc/self/status | grep ^Cap
# CapInh: 0000000000000000
# CapPrm: 0000003fffffffff ← все 40 бит = root
# CapEff: 0000003fffffffff
# CapBnd: 0000003fffffffff
# CapAmb: 0000000000000000
capsh --print # читаемый формат
capsh --decode=0000003fffffffff
Обычный юзер: все нули. Root: все единицы. Контейнер с --cap-drop=ALL
плюс несколько --cap-add: видишь конкретный bitmask с включёнными.
File capabilities
Бинарь может иметь capabilities в xattr - тогда при exec он их получает, без setuid:
# Дать /usr/bin/ping право CAP_NET_RAW (так в современных дистро)
sudo setcap cap_net_raw+ep /usr/bin/ping
getcap /usr/bin/ping
# /usr/bin/ping = cap_net_raw+ep
# Удалить
sudo setcap -r /usr/bin/ping
Это безопаснее SUID-root: бинарь получает только нужный бит, а не полные root-привилегии.
В Docker / containers
Docker по умолчанию даёт ограниченный default-set caps:
CAP_AUDIT_WRITE, CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_FOWNER, CAP_FSETID,
CAP_KILL, CAP_MKNOD, CAP_NET_BIND_SERVICE, CAP_NET_RAW, CAP_SETFCAP,
CAP_SETGID, CAP_SETPCAP, CAP_SETUID, CAP_SYS_CHROOT
Управление:
docker run --cap-drop=ALL --cap-add=NET_BIND_SERVICE nginx # минимум
docker run --cap-add=NET_ADMIN ubuntu # для tc/iptables
docker run --cap-add=SYS_PTRACE ubuntu # для strace
docker run --privileged ubuntu # ВСЕ caps + ещё всякое (НЕ безопасно)
В Kubernetes - securityContext.capabilities:
securityContext:
capabilities:
drop: ["ALL"]
add: ["NET_BIND_SERVICE"]
Дебаг «Operation not permitted»
Когда программа падает с EPERM:
cat /proc/<pid>/status | grep ^CapEff- что есть у процессаcapsh --decode=<значение>- расшифровать- Сравнить с тем что нужно для операции (
man 7 capabilities) - Если внутри контейнера - добавить через
--cap-add
CAP_SYS_ADMIN - «новый root»
Из-за legacy-причин, очень многие операции требуют именно SYS_ADMIN
(mount, namespaces, BPF, MAC labels). Это де-факто = root.
Контейнер с CAP_SYS_ADMIN - почти небезопасный по дизайну.
Это известная проблема, поэтому и появился CAP_BPF, CAP_PERFMON -
выпиливают конкретные права из SYS_ADMIN в свои биты.