capabilities linux что это
В двух словах о привилегиях Linux (capabilities)
Перевод статьи подготовлен специально для студентов курса «Администратор Linux».
Привилегии (capabilities) используются всё больше и больше во многом благодаря SystemD, Docker и оркестраторам, таким как Kubernetes. Но, как мне кажется, документация немного сложна для понимания и некоторые части реализации привилегий для меня оказались несколько запутанными, поэтому я и решил поделиться своими текущими знаниями в этой короткой статье.
Самая важная ссылка по привилегиям — это man-страница capabilities(7). Но она не очень хорошо подходит для первоначального знакомства.
Разрешения процессов (process capabilities)
Права обычных пользователей очень ограничены, в то время как права пользователя “root” очень обширны. Хотя процессам, запущенным под «root», часто не требуются все полномочия root.
Для уменьшения полномочий пользователя “root” разрешения POSIX (POSIX capabilities) предоставляют способ ограничить группы привилегированных системных операций, которые разрешено выполнять процессу и его потомками. По сути, они делят все «root»-права на набор отдельных привилегий. Идея capabilities была описана в 1997 году в черновике POSIX 1003.1e.
Эти числа (здесь показаны в шестнадцатеричной системе счисления) представляют собой битовые карты, в которых представлены наборы разрешений. Вот их полные имена:
При запуске нового процесса через execve(2), полномочия для дочернего процесса преобразуются с использованием формулы, указанной в capabilities(7):
Эти правила описывают действия, выполняемые для каждого бита во всех наборах разрешений (ambient/permitted/effective/inheritable/bounding). Используется стандартный синтаксис языка Си (& — для логического И, | — для логического ИЛИ). P’ — это дочерний процесс. P — текущий процесс, вызывающий execve(2). F — это, так называемые, “файловые разрешения” у файла, запущенного через execve.
Кроме того, процесс может программно изменить свои наследуемые, доступные и эффективные наборы с помощью libcap в любое время в соответствии со следующими правилами:
Разрешения файлов (file capabilities)
Но этот механизм предоставляет слишком много привилегий файлу, поэтому POSIX-разрешения реализовали концепцию, называемую “файловые разрешения”. Они хранятся в виде расширенного атрибута файла, называемого “security.capability”, поэтому вам нужна файловая система с поддержкой расширенных атрибутов (ext*, XFS, Raiserfs, Brtfs, overlay2, …). Для изменения этого атрибута необходимо разрешение CAP_SETFCAP (в доступном наборе разрешений процесса).
Особенные случаи и замечания
Конечно, в реальности все не так просто, и есть несколько особых случаев, описанных в man-странице capabilities(7). Наверное, самыми важными из них являются:
Если официальный контейнер nginx, ingress-nginx или ваш собственный останавливается или перезапускается с ошибкой:
bind() to 0.0.0.0:80 failed (13: Permission denied)
Это все работает до тех пор, пока securityContext/allowPrivilegeEscalation установлен в true и storage-драйвер docker/rkt (см. документацию docker) поддерживает xattrs.
Помимо использования xattr, единственным способом получения cap_net_bind_service в не-root контейнере — это позволить Docker установить внешние разрешения (ambient capabilities). Но по состоянию на апрель 2019, это еще не реализовано.
Примеры кода
Здесь пример кода с использованием libcap для добавления CAP_NET_BIND_SERVICE в эффективный набор разрешений. Он требует наличия разрешения CAP_BIND_SERVICE+p для бинарного файла.
Привилегии как атрибуты процесса Linux
Еще одним важным атрибутом процесса, определяющим его возможности по использованию системных вызовов, являются привилегии процесса cababilities.
Например, обладание привилегией CAP_SYS_PTRACE разрешает процессам трассировщиков strace и ltrace, использующих системный вызов ptrace, трассировать процессы любых пользователей (а не только «свои», EUID которых совладает с EUID трассировщика).
Аналогично, привилегия CAP_SYS_NIСЕ разрешает изменять приоритет, устанавливать привязку к процессорам и назначать алгоритмы планирования процессов и нитей любых пользователей, а привилегия CAP_КILL разрешает посылать сигналы процессам любых пользователей.
Явная привилегия «владельца» CAP_FOWNER позволяет процессам изменять режим и списки доступа, мандатную метку, расширенные атрибуты (см. статью) и флаги любых файлов так, словно процесс выполняется от лица владельца файла.
Привилегия CAP_LINUX_IMMUTABLE разрешает управлять флагами файлов i, immutable и a, append, а привилегия CAP_SETFCAP — устанавливать «файловые» привилегии (см. далее) запускаемых программ.
Необходимо отметить, что именно обладание полным набором привилегий делает пользователя root (UID=0) в Linux — суперпользователем. И наоборот, обычный, непривилегированный пользователь (в смысле UID≠o) не обладает никакими явными привилегиями. Неявно он обладает привилегией владельца для всех своих объектов.
Назначение привилегий процесса происходит при запуске программы при помощи системного вызова exec, исполняемый файл которого помечен «файловыми» привилегиями (здесь допущено намеренное упрощение механизма наследования и назначения привилегий при fork и exec без потери смысла).
В примере из листинга ниже иллюстрируется получение списка привилегий процесса при помощи утилиты getpcaps.
Как и ожидалось, процесс postgres (pid=1427), работающий рт лица обычного (непривилегированного, в смысле UID≠o) псевдопользователя postgres, не имеет никаких.привилегий, а процесс NetworkHanager (pid=1263), работающий от лица суперпользователя root (uid=o), имеет полный набор привилегий.
Однако процесс dhclient (PID=14026) выполняется от лица «суперпользователя», лишенного большинства своих привилегий, т. к. после порождения своего дочернего процесса NetworkManager уменьшил его привилегии до минимально необходимого набора, достаточного для выполнения своих функций, что способствует обеспечению защищенности операционной системы.
Привилегии (capabilities) процесса
postgres 1984 \_ postgres: writer process
postgres 1985 \_ postgres: wal writer process
postgres 1986 \_ postgres: autovacuum launcher process
postgres 1987 \_ postgres: stats collector process
$ getpcaps 1427
Capabilities for ‘1427’: =
$ getpcaps 1263
Capabilities for ‘1263’: =
cap_chown, cap_dac_override, cap_dac_read_search, cap_fowner, cap_fsetid, cap_kill, cap_setgid, capsetuid, cap_setpcap, cap_linux_immutable, cap_net_bind_service, cap_net_broadcast, cap_net_admin, cap_net_raw, cap_ipc_lock, cap_ipc_owner, cap_sys_module, cap_sys_rawio, cap_sys_chroot, cap_sys_ptrace, cap_sys_pacct, cap_sys_admin, cap_sys_boot, cap_sys_nice, cap_sys_resource, cap_sys_time, cap_sys_tty_config, cap_mknod, cap_lease, cap_audit_write, cap_audit_control, cap_setfcap, cap_mac_override, cap _mac_admin, cap_syslog,35,36+ep
$ getpcaps 14026
Capabilities for ‘14026’: =
cap_dac_override,cap_net_bind_service,cap_net_raw,cap_sys_module+ep
В листинге ниже показан типичный пример использования отдельных привилегий там, где классически применяется неявная передача всех полномочий суперпользователя при помощи механизма SUID/SGID.
Например, «обычная» утилита ping для выполнения своей работы должна создать «необработанный» raw сетевой сокет, что является с точки зрения ядра привилегированной операцией.
В большинстве систем программа /bin/ping будет SUID-ной во владении суперпользователем root, чьи права и будут передаваться при ее запуске.
С точки зрения защищенности системы это не соответствует здравому смыслу, подсказывающему наделять программы минимально необходимыми возможностями, достаточными для их функционирования.
Для создания «необработанных» raw и пакетных packet сокетов достаточно только привилегии CAP_NET_RAW, а весь суперпользовательский набор привилегий более чем избыточен.
Делегирование привилегий программе ping
-rwsr-xr-x 1 root root 3474 нояб. 8 2017 /bin/ping
$ ping ubuntu
PING ubuntu (127.0.1.1) 56(84) bytes of data.
64 bytes from ubuntu (127.0.1.1): icmp_req=1 ttl=64 time=0.074 ms
— ubuntu ping statistics —
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt wln/avg/max/mdev = 0.074/0.074/0.074/0.000 ms
$ sudo chmod u-s /bin/ping
$ ping ubuntu
ping: icmp open socket: Operation not permitted
$ sudo setcap cap_net_raw+ep /bin/ping
-rwxr-xr-x 1 root root 34740 нояб. 8 2017 /bin/ping
$ getcap /bin/ping
$ ping ubuntu
PING ubuntu (127.0.1.1) 56(84) bytes of data.
64 bytes from ubuntu (127.0.1.1): icmp_req=1 ttl=64 time=0.142 ms
— ubuntu ping statistics —
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.142/0.142/0.142/0.000 ms
При отключении передачи полномочий программа /bin/ping лишается возможности выполнять свои функции, а при назначении ей при помощи команды setcap «файловой» привилегии CAP_NET_RAW функциональность возвращается в полном объеме, т. к. приводит к установке «процессной» привилегии CAP_NET_RAW при запуске этой программы.
Для просмотра привилегий, делегируемых при запуске программ, используется парная команда getcap.
Аналогично, при использовании анализаторов сетевого трафика tshark и/или wireshark, вызывающих для захвата сетевых пакетов утилиту dumpcap, необходимо открывать как «необработанные» raw, так и пакетные packet сетевые сокеты, что требует той же привилегии CAP_NETJRAW.
Классический способ применения анализаторов пакетов состоит в использовании явной передачи всех полномочий суперпользователя (при помощи su или sudo) при их запуске, что опять не соответствует минимально необходимым и достаточным требованиям к разрешенным возможностям программ.
Делегирование привилегий программе tshark
$ tshark
tshark: There are no interfaces on which a capture can be done
execve(«/usr/bin/tshark», [«tshark»], [/* 23 vars */]) = 0
Process 8951 attached
[pid 8951] execve(«/usr/bin/dunpcap», [«/usr/bin/dumpcap», «-D», «-Z», «none»],
Process 8951 detached
— SIGCHLD (Child exited) @ 0 (0) —
tshark: There are no interfaces on which a capture can be done
$ sudo setcap cap_net_raw+ep /usr/bin/dumpcap
[sudo] password for fitz:
$ getcap /usr/bin/dumpcap
Capturing on wlan0
Для эффективного использования анализаторов трафика непривилегированными пользователями достаточно делегировать их процессам захвата пакетов привилегию CAP_NET_RAW при помощи «файловых» привилегии CAP_NET_RAW для программы захвата /usr/bin/dumpcap, что и проиллюстрировано в листинге выше.
Capabilities linux что это
Для того, чтобы выполнять проверки на соответствие прав, обычные реализации Unix имели две категории процессов: привилегированные процессы (privileged, у них действующий идентификатор пользователя равен 0, соответствуя суперпользователю root) и непривилегированные процессы (unprivileged, у них действующий идентификатор пользователя не равен 0). Привилегированные процессы осуществляют все проверки прав в ядре, в то время как непривилегированные процессы являются объектом для полноценной проверки всех прав на основе параметров процесса (обычно: действующий идентификатор пользователя UID, действующий идентификатор группы GID и список дополнительных групп).
Список возможностей
Возможности процессов
В текущей реализации процессу выдаются все действующие и разрешенные возможности (объекты для операции по ограничению набора возможностей описаны ниже) при исполнении программы set-UID-root, или если процесс с действительным UID равным 0 исполняет новую программу.
Порожденный через fork (2) процесс копирует себе все наборы возможностей своего родителя.
Граница набора возможностей
Только процесс init может устанавливать биты в границе набора возможностей; все другие, кроме суперпользователя, могут только очищать биты в этом наборе.
Текущая и будущие реализации
Если рассмотреть Linux 2.4.20, то в нем выполняются только первые два требования.
В конечном итоге можно ассоциировать эти три набора возможностей с исполняемым файлом, который в соединении с наборами возможностей процесса будет определять возможности процесса после исполнения exec : Allowed (разрешенные): набор логически умножается (AND) с наследованным набором процесса для определения наследуемых возможностей, разрешенных процессу после исполнения exec. Forced (принужденные): возможности автоматически разрешаются процессу, независимо от наследуемых возможностей процесса. Effective (действующие): эти возможности в новом разрешенном наборе также будут установлены в новый действующий набор. (F(effective) будет состоять либо из всех нулей, либо из единиц.)
В то же время, так как текущие реализации не поддерживают наборы возможностей файлов, то во время исполнения exec: 1. Все три набора возможностей файла считаются очищенными. 2. Если исполняется программа set-UID-root, или если действительный идентификатор пользователя процесса равен 0 (root), то файлу разрешены и принудительно устанавливаются все единицы в наборе (то есть полный набор возможностей). 3. Если исполняется программа set-UID-root, то действующий набор файла определен во все единицы.
Во время исполнения exec, ядро рассчитывает новые возможности процесса, используя следующий алгоритм: где: P обозначает набор возможностей процесса до исполнения P’ обозначает значение набора возможностей после исполнения F обозначает набор возможностей файла cap_bset является значением границы набора возможностей.
Capabilities linux что это
Для того, чтобы выполнять проверки на соответствие прав, обычные реализации Unix имели две категории процессов: привилегированные процессы (privileged, у них действующий идентификатор пользователя равен 0, соответствуя суперпользователю root) и непривилегированные процессы (unprivileged, у них действующий идентификатор пользователя не равен 0). Привилегированные процессы осуществляют все проверки прав в ядре, в то время как непривилегированные процессы являются объектом для полноценной проверки всех прав на основе параметров процесса (обычно: действующий идентификатор пользователя UID, действующий идентификатор группы GID и список дополнительных групп).
Список возможностей
Возможности процессов
В текущей реализации процессу выдаются все действующие и разрешенные возможности (объекты для операции по ограничению набора возможностей описаны ниже) при исполнении программы set-UID-root, или если процесс с действительным UID равным 0 исполняет новую программу.
Порожденный через fork (2) процесс копирует себе все наборы возможностей своего родителя.
Граница набора возможностей
Только процесс init может устанавливать биты в границе набора возможностей; все другие, кроме суперпользователя, могут только очищать биты в этом наборе.
Текущая и будущие реализации
Если рассмотреть Linux 2.4.20, то в нем выполняются только первые два требования.
В конечном итоге можно ассоциировать эти три набора возможностей с исполняемым файлом, который в соединении с наборами возможностей процесса будет определять возможности процесса после исполнения exec : Allowed (разрешенные): набор логически умножается (AND) с наследованным набором процесса для определения наследуемых возможностей, разрешенных процессу после исполнения exec. Forced (принужденные): возможности автоматически разрешаются процессу, независимо от наследуемых возможностей процесса. Effective (действующие): эти возможности в новом разрешенном наборе также будут установлены в новый действующий набор. (F(effective) будет состоять либо из всех нулей, либо из единиц.)
В то же время, так как текущие реализации не поддерживают наборы возможностей файлов, то во время исполнения exec: 1. Все три набора возможностей файла считаются очищенными. 2. Если исполняется программа set-UID-root, или если действительный идентификатор пользователя процесса равен 0 (root), то файлу разрешены и принудительно устанавливаются все единицы в наборе (то есть полный набор возможностей). 3. Если исполняется программа set-UID-root, то действующий набор файла определен во все единицы.
Во время исполнения exec, ядро рассчитывает новые возможности процесса, используя следующий алгоритм: где: P обозначает набор возможностей процесса до исполнения P’ обозначает значение набора возможностей после исполнения F обозначает набор возможностей файла cap_bset является значением границы набора возможностей.
rfLinux
Sunday, June 8, 2014
Разрешения процессов в Linux
Терминологический казус
Постановка задачи
Кровавые детали
Начнём с include/linux/sched.h:
И наконец include/linux/capability.h:
SECBIT_KEEP_CAPS | Поток, имеющий UID 0, сохраняет все разрешения при смене идентификатора пользователя (не root) По умолчанию этот флаг очищен и даже если он установлен потоком, он принудительно очищается при вызове exec*() с целью предотвращения утечки пивилегий |
SECBIT_NO_SETUID_FIXUP | Множества разрешеий не будут корректироваться ядром при смене UID между 0 и любым другим значением |
SECBIT_NOROOT | Программа с suid-битом не получает автоматически все привилегии, которые имеет root |
Так в общих чертах выглядит объект нашего интереса. Но встаёт вопрос, а кто и как назначает привилегии конкретным процессам? Когда поддержки файловых разрешений ещё не было, единственным способом использования привилегий был некий вспомогательный процесс, который мог бы раздавать их другим процессам. Этакий сервер безопасности. Либо сама программа, запущенная изначально с правами суперпользователя, могла добровольно отказаться от ненужных ей привилегий. Оба способа явно довольно вычурны. Вероятно это в немалой степени послужило фактором, сдерживающим внедрение использования привилегий.
Файловые разрешения
Проведём небольшой эксперимент:
Что и требовалось продемонстрировать. dumpcap является частью пакета wireshark. Именно этот исполняемый файл «хватает» пакеты с сетевого интерфейса. Для того, чтобы мы могли использовать wireshark из-под обычного пользовательского аккаунта, этому файлу присвоены соответствующие разрешения: операции сетевого администрирования, право на создание символьных («сырых», raw) сокетов во множествах доступных и эффективных привилегий. Каким образом всё это облечено в код ядра? Вся чёрная магия скрыта в файле security/commoncap.c
Инициализация множеств разрешений нового процесса: Данные просто копируются из дескриптора текущей задачи, т.е. вызвавшей exec(), в блок параметров образа, который будет запущен. Теперь весь остальной код может сделать свою работу.
Далее, считывание разрешений с диска и вычисление новых разрешений, там же в fs/exec.c Далее, в security/security.c Структура, содержащая указатели на операции безопасности, инициализируется в security/selinux/hooks.c. Здесь без труда находим наш selinux_bprm_set_creds() Резюмируя, получим такой вот флоучарт:
Надеюсь, это головокружительное путешествие помогло понять, каким образом реализованы привилегии Linux и что делает их привлекательными, надеюсь, вскользь коснувшись кода, вы поймёте, какую роль в реализации поддержки привилегий играет VFS. К сожалению, написано уже и так больше, чем я предполагал изначально, а во все детали заглянуть всё равно не представляется возможным в заметке сколь-нибудь вменяемого объёма. Поэтому напоследок только взглянем краем глаза, как на практике реализуется контроль привилегий и на этом остановимся. Рассмотрим самый простой пример, с которого мы и начали, с настройкой системных часов. Наша программа вызывает stime(). Так как представить себе использование этого системного вызова совсем не сложно, текст самой программы не приведён:
kernel/time.c include/linux/security.h security/commoncap.c kernel/capability.c security/selinux/hooks.c security/commoncap.c
Почти живой пример
PS: в заметке довольно много кода. Изначально я хотел спрятать всё это безобразие под ковриккат, но он работал не совсем так, как мне бы хотелось и я решил, что пусть уж лучше будет так, чем сломанный функционал. Надеюсь, к следующему разу я найду какой-нибудь рабочий вариант.