Home SYN/ACK SELinux: бронежилет для корпоративного пингвина

SELinux снискала славу сложной в понимании и настройке системы безопасности, которая хоть и делает Linux намного более устойчивой к взлому операционной системой, но создает больше проблем, чем приносит решений. Такая точка зрения в корне не верна, и в этой статье я покажу, что SELinux намного проще и удобнее в использовании, чем это кажется на первый взгляд.

Система SELinux (Security-Enhanced Linux — Linux с улучшенной безопасностью) была разработана Агентством Национальной Безопасности США и всего за несколько лет стала стандартом в области систем контроля прав доступа. Она была включена в ядро Linux версии 2.6.0 и впервые появилась как полностью работающая из коробки система в дистрибутиве Red Hat Enterprise Linux 4. Впоследствии поддержка SELinux была интегрирована в такие дистрибутивы, как Debian, OpenSUSE и Ubuntu, а дополнительные пакеты, активирующие систему, были добавлены во многие другие более или менее популярные дистрибутивы. Именно благодаря SELinux-дистрибутиву RHEL5, работающему на серверах IBM, удалось получить сертификат безопасности EAL4 Augmented with ALC_FLR.3, который в то время имела лишь одна операционная система — Trusted Solaris. SELinux существенно расширяет ядро Linux, делая операционные системы, построенные на его основе, пригодными к использованию в сферах, где доступ к информации должен быть строго разграничен, а приложения, запускаемые пользователями, должны иметь определенный набор прав, который не выходит за рамки минимально необходимого. Это делает систему привлекательной для госучреждений и военных, однако кажется не совсем понятным, зачем она нужна администраторам рядовых серверов, а уж тем более — обычным пользователям (в Fedora SELinux по умолчанию включен). Сейчас я попытаюсь этот момент пояснить.

Зачем это нужно?

Создатели UNIX наделили свою ОС простым, но весьма гибким и красивым механизмом безопасности. В его основе лежат всем нам известные права доступа на файлы, которые определяют круг лиц (пользователей, процессов), способных выполнять какие-либо действия над файлами и каталогами. Например, мы можем создать исполняемый файл и выставить на него права доступа “-rwxr-xr-x”, что будет означать, что мы (то есть владелец файла) можем делать с ним все что захотим, пользователи, входящие в нашу группу, могут его читать и исполнять, а все остальные — только исполнять. Учитывая тот факт, что все устройства в UNIX представлены файлами, такой механизм позволяет не только четко разграничивать доступ пользователей (их приложений) к информации, но и к устройствам и даже к некоторым функциями операционной системы (procfs и sysfs).

Однако, у механизма прав доступа есть два серьезных недостатка. Во-первых, их реализация слишком топорна. Она хорошо подходит для отделения процессов от конфиденциальной информации пользователей, но абсолютно не подходит для гибкого управления их возможностями. Например, чтобы дать какой-либо дисковой утилите (cfdisk, например) возможность модификации хранящейся на диске информации, мы можем либо разрешить полный доступ к диску группе, к которой принадлежит ее владелец, либо запустить ее с правами пользователя root. Как в первом, так и во втором случае мы создадим потенциальную брешь в безопасности: все остальные пользователи/приложения группы получат права доступа к диску, или сама утилита получит наивысшие и безграничные права в системе. А что если нужно дать доступ не к модификации диска, а только к вызову определенных его функций (ioctl)? Здесь права доступа вообще не помогут.

Во-вторых, файлами в Linux представлены далеко не все ресурсы операционной системы. Огромное количество функций ядра скрыто в более чем трех сотнях системных вызовов, большая часть которых доступна для использования абсолютно всем процессам, а если процесс имеет права root, то его возможности вообще никак не ограничиваются. Если, к примеру, мы захотим запустить FTP-сервер на стандартном для него 21 порту, который доступен только процессам с правами root, нам придется всецело довериться его разработчику, поскольку работая от root, FTP-сервер будет способен делать абсолютно все, и ошибка, найденная в его коде, даст взломщику полный контроль над системой. И это из-за простой потребности слушать привилегированный порт!

SELinux решает эти проблемы, позволяя чрезвычайно гибко контролировать отношения процесса и операционной системы. С его помощью можно ограничить процесс в возможности обращения к тем или иным системным вызовам или файлам, контролировать то, как происходит системный вызов, какие при этом используются аргументы, запрещать или разрешать привязку процесса к определенным портам, короче говоря, управлять возможностями процессов на самом низком уровне.

Как это работает?

Если бы ты читал толстую книжку без картинок, то здесь тебя должны были поджидать рассуждения о таких штуках, как дискреционный и мандатный контроль доступа, RBAC, MCS и других околонаучных вещах. К счастью, ты читаешь ][, поэтому тебе придется прослушать более простое объяснение. Понять принцип работы SELinux и заложенные в него идеи проще всего разобравшись с тем, что происходит во время его активации. Что делает SELinux для того, чтобы контролировать обращения процессов к ресурсам ОС? Концептуально можно выделить четыре шага:

  1. Все субъекты (процессы) и объекты (файлы, системные вызовы и т.д.) взаимодействия помечаются с помощью специальных меток, называемых контекстом безопасности (процессы во время запуска, файлы — во время создания или установки ОС, их метки хранятся в расширенных атрибутах ФС, системные вызовы — при компиляции модуля SELinux).

  2. Когда субъект пытается произвести какое-либо действие в отношении объекта, информация об этом действии поступает к обработчику SELinux.

  3. Обработчик смотрит на контексты безопасности субъекта и объекта и, сверяясь с написанными ранее правилами (так называемая политика), принимает решение о дозволенности действия.

  4. Если действие оказывается правомочным (политика его разрешает), объект (программа) продолжает работать в обычном режиме, в противном случае — она либо принудительно завершается, либо получает вежливый отказ.

В реальной ситуации все это выглядит примерно так: один из скриптов инициализации дистрибутива, имеющий метку initrc_t (на самом деле эту метку имеют порождаемые им процессы, но сейчас это не важно), запускает веб-сервер Apache с помощью файла /usr/sbin/httpd, который имеет метку httpd_exec_t. Как и все остальные действия, запрос на эту операцию поступает в SELinux, в правилах (политике) которого указано, что initrc_t не только имеет право запускать файл с меткой httpd_exec_t, но и то, что при запуске этого файла процесс и его потомки должны получить метку httpd_t. Теперь в системе появляется один или несколько процессов Apache, помеченных как httpd_t. Каждый из них будет подчиняться правилам SELinux, относящимся к этой метке, поэтому, если в политике указано, что httpd_t может получать доступ только к файлам, помеченным как httpd_sys_content_t и 80 порту, Apache не сможет нарушить эти правила (перевесив его на другой порт мы попросту получим ошибку запуска).

Резонный вопрос: откуда берутся правила? Их пишут люди, а точнее — мантейнеры дистрибутивов, что не мешает системному администратору исправить их соответственно своим потребностям. Обычно правила загружаются на первом этапе инициализации ОС, но с помощью особых приемов их можно подсунуть и в уже работающую систему (не перезагружать же сервер с аптаймом 1,5 года только для того, чтобы разрешить FTP-серверу ходить по симлинкам). Типичный объем файла политик SELinux для основных приложений и сервисов дистрибутива может содержать до нескольких сотен тысяч строк, поэтому, чтобы не обременять админов рутинной работой, были созданы инструменты для их автоматической генерации с помощью обучения (например, утилита audit2allow позволяет составлять правила SELinux на основе лог-файлов подсистемы audit).

В следующих разделах мы подробнее остановимся на этом вопросе, однако для начала нам следует разобраться с “политикой управления доступом на основе ролей”.

Подсистема SELinux отрабатывает после классического механизма управления доступом UNIX, поэтому с его помощью нельзя разрешить то, что уже запрещено с помощью традиционных прав доступа.

Управление доступом на основе ролей или RBAC

Само собой разумеется, что SELinux не смог бы так радовать военных, если бы в его основе лежали только метки, определяющие контекст безопасности, и механизм разделения доступа на их основе (кстати, это называется “мандатным управлением доступом”, и многие ошибочно приписывают его SELinux). Из-за своей низкоуровневости такая система безопасности была бы слишком сложной в управлении и негибкой, поэтому создатели SELinux снабдили ее еще одним уровнем доступа, именуемым “ролями”.

Когда мы говорили о метках SELinux, я намеренно упустил из виду одну важную деталь. На самом деле контекст безопасности (метка) состоит не из одного, а из трех компонентов (есть еще и четвертый компонент, но об этом пока не стоит задумываться): имени пользователя, роли и типа субъекта (тот самый компонент, который оканчивается на “_t”). Каждый пользователь SELinux (который может быть связан с учетной записью пользователя Linux, но обычно все Linux-пользователи отображаются в SELinux-пользователя unconfined_u) может выполнять несколько ролей, в рамках каждой из которых, ему доступны несколько типов субъектов, а каждый субъект, в свою очередь, может иметь доступ к некоторому количеству объектов.

Как и группы пользователей в классической модели управления доступом UNIX, роли используются для наделения процессов пользователя разными видами полномочий. Однако, фактически они нужны только для того, чтобы тонко регулировать доступ пользователей к данным, то есть при сопровождении компьютерной инфраструктуры больших предприятий, сотрудники которых могут иметь разные уровни доступа к секретной информации (то есть, те самые военные и госучреждения).

При использовании SELinux на обычных серверах, роли не играют большой роли (парадокс, да и только). Любой Linux-дистрибутив, из коробки оснащенный SELinux, по умолчанию использует так называемую “целевую политику”, которая предполагает наличие всего нескольких общих SELinux-пользователей и ролей. Например, целевая политика Fedora и RHEL определяет только двух дефолтовых пользователей (на самом деле, есть еще несколько специальных пользователей, но обычно они не используются) и две роли: пользователь system_u, роль system_r и пользователь unconfined_u, роль unconfined_r. Первые используются для запуска системных процессов во время инициализации системы, и в политике четко указано, что запускаемые пользователем system_u (который имеет роль system_r) приложения должны получать конкретный тип субъекта (например, httpd_t), определяемый типом объекта (файла), из которого они запускаются (например, httpd_exec_t). В их отношении действуют строгие правила ограничения, о которых мы говорили в предыдущем разделе. Пользователь unconfined_u и роль unconfined_r предназначены для обычных Linux-пользователей. На последнем этапе инициализации системы запускается менеджер входа в систему (он работает в домене system_u:system_r:login_t), который принимает имя пользователя и его пароль и запускает шелл/графическую оболочку. Однако вместо того, чтобы сохранить текущий контекст безопасности, либо изменить его на system_u:system_r:shell_t в соответствии с правилами политики SELinux (если бы такие были), он обращается к PAM-модулю pam_selinux, который сообщает SELinux, что запущенный менеджером входа процесс (шелл или оболочка) должен получить контекст unconfine d_u:unconfined_r:unconfined_t.

Смысл этого перехода в том, что целевая политика SELinux имеет правила, которые разрешают приложениям, работающим в этом контексте, делать что угодно, в том числе — запускать другие приложения. Однако, если пользователь запустит приложение, в контексте безопасности которого указан SELinux-пользователь system_u и тип, для которого в политике есть правила, процессы этого приложения будут ограничены в правах точно так же, как если бы они были запущены во время инициализации. Если говорить о контексте безопасности файлов, то здесь роли не используются в принципе, и второе поле всегда равно object_r, а первое будет либо system_u, либо unconfined_u, если это файл, созданный пользователем.

Таким образом, можно сказать, что при использовании целевой политики значимым полем контекста безопасности остается только тип субъекта/объекта, тогда как поля пользователь и роль просто определяют отношение SELinux к процессу (либо он находится под контролем правил, либо нет).

Ограниченные пользователи

Кроме SELinux-пользователя unconfined_u, который по дефолту присваивается всем юзерам системы, в целевой политике также описано несколько непривилегированных пользователей, которых можно использовать для создания гостевых учетных записей, процессы которых будут очень ограничены в правах (возможности и различия этих пользователей смотри в таблице “Дефолтовые пользователи SELinux”). Чтобы создать такого пользователя, достаточно выполнить следующую команду:

# useradd -Z xguest_u имя_юзера

Кроме этого можно сделать его дефолтовым, так что ограниченными будут все вновь созданные Linux-юзеры:

# semanage login -m -S targeted -s "xguest_u" -r s0 __default__

Посмотреть список текущих SELinux-юзеров можно так:

# /usr/sbin/semanage login -l

Работаем с системой

Главное достоинство SELinux в том, что он абсолютно незаметен для многих администраторов и обычных пользователей. Приложения, для которых есть правила в политике, будут автоматически ограничены в правах, остальные программы смогут функционировать как ни в чем не бывало. Однако, время от времени сисадмины натыкаются на некоторые проблемы в работе системы, которые вынуждают их отключить SELinux. В этом разделе мы разберемся как решать эти проблемы в рамках возможностей SELinux, но сначала я дам несколько важных советов.

  1. SELinux должен быть включен. Глупо отключать SELinux только потому, что так рекомендуют делать многие сисадмины и пользователи. Работу штатных сервисов он нарушить не может, а если тебе нужно расширить возможности какого-либо приложения, это всегда можно сделать с помощью изменения мета-настроек или отключения проверок для определенного типа субъекта (позже я скажу, как это сделать).

  2. “-Z” — твой друг. Да, вся эта возня с контекстами безопасности может вывести из себя. Но есть множество инструментов, которые позволяют выяснить текущий контекст безопасности процессов и файлов:

$ id -Z
$ ps auxZ
$ ls -Z

Найти файлы с нужным контекстом:

$ fi nd /etc -context '*net_conf_t'

Восстановить правильный контекст файла:

# restorecon -v /usr/sbin/httpd

И даже узнать, каким должен быть контекст файла и сравнить его с текущим:

matchpathcon -V /var/www/html/*

  1. Скажи mv – нет! Во время установки дистрибутива все файлы получают определенный контекст безопасности, а все файлы, созданные в процессе работы, — контекст безопасности, определяемый правилами на основе родительского каталога (например, если создать файл внутри каталога /etc, его тип автоматически станет etc_t, а для файлов каталога /var/www/html — httpd_sys_content_t). Однако это работает только в отношении вновь созданных файлов. Во время перемещения файла с помощью mv его контекст сохраняется, что может привести к отказу в доступе (например, Apache не сможет получить доступ к файлу, если его тип не httpd_sys_content_t).

  2. Маны — наше все. В дистрибутивах Fedora и RHEL есть большое количество man-страниц, которые разъясняют все ограничения SELinux в отношении сервисов и демонов. Например, на странице httpd_selinux(8) описано, в каком контексте безопасности работает Apache, какие у него есть полномочия, какие контексты должны иметь доступные ему файлы.

Теперь разберемся с тем, что нужно делать, когда SELinux начинает мешать. Обычно это происходит вследствие того, что админ пытается включить определенную функцию сервиса или как-то перенастроить его, а SELinux начинает сыпать на экран предупреждающие сообщения. Первое, что нужно сделать в этой ситуации, это проверить лог-файлы. Главный журнальный файл SELinux носит имя /var/log/audit/audit.log. Туда поступают “необработанные” сообщения, которые могут быть полезны другим приложениям, но трудны для чтения человеком. Поэтому существует второе место, куда поступают те же сообщения, но в гораздо более понятном формате. Это файл /var/log/messages, в нем сообщения могут выглядеть так:

# grep "SELinux is preventing" /var/log/messages
May 7 18:55:56 localhost setroubleshoot: SELinux is preventing httpd (httpd_t) "getattr" to /var/www/html/index.html (home_dir_t). For complete SELinux messages. run sealert -l de7e30d6-5488-466d-a606-92c9f40d316d

Здесь все должно быть понятно: SELinux запретил субъекту httpd_t (веб-сервер Apache) доступ к файлу /var/www/html/index.html на том основании, что последний имеет неправильный тип объекта (home_dir_t присваивается файлам, созданным в домашнем каталоге пользователя). Для получения более детальной информации SELinux рекомендует выполнить команду sealert -l бла-бла-бла. Эта команда выведет очень информативное сообщение, где будут описаны все обстоятельства произошедшего, причины, по которым они могли возникнуть, а также пути решения проблемы.

В нашем случае причина проста: админ переместил файл index.html из своего домашнего каталога с помощью mv, выставил на него нужного владельца и права, но забыл изменить контекст. Выхода из ситуации два. Либо присвоить файлу правильный контекст с помощью команды chcon:

# chcon -t httpd_sys_content_t /var/www/html/index.html

Либо заставить систему “сбросить” контекст всех файлов каталога:

# restorecon -v /var/www/html

После перезапуска Apache все должно быть ok. Однако этот метод не сработает, если мы захотим переместить корневой каталог Apache в другое место (например, в /www). Дело в том, что chcon изменяет контекст только до следующей переиндексации, с помощью restorecon, которая сбросит контекст файлов каталога /www до default_t (по правилам политики все каталоги, не имеющие родительского каталога, и их файлы получают такой тип). Поэтому мы должны изменить саму политику:

# semanage fcontext -a -t httpd_sys_content_t /www

restorecon -v /www

Первая команда изменит политику так, чтобы дефлтовым типом для каталога /www и его содержимого был httpd_sys_content_t, а вторая сбросит контекст его файлов, так что они получат тот же тип (правила SELinux допускают, чтобы дефолтовые метки каталогов и их содержимого отличались, но для удобства semanage делает их одинаковыми).

Также semanage может быть использована для просмотра списка мета-правил:

# semanage boolean -l

С помощью мета-правил можно контролировать такие аспекты работы приложений, как, например, возможность веб-сервера подключаться к удаленной базе данных (httpd_can_network_connect_db) или разрешение ftp-сервера читать файлы домашнего каталога пользователей (ftp_home_dir) и т.д. Такие правила группируют в себе большое количество низкоуровневых правил, что существенно упрощает жизнь сисадмина.

Чтобы включить/отключить то или иное правило, можно использовать команду setsebool:

# setsebool httpd_can_network_connect_db on

setsebool httpd_can_network_connect_db off

Указав флаг “-P”, можно сделать эти правила постоянными между перезагрузками. В самом крайнем случае semanage можно использовать для полного отключения проверок SELinux в отношении определенного субъекта:

# semanage permissive -a httpd_t

Включение производится с помощью похожей команды:

# semanage permissive -d httpd_t

Выводы

SELinux далеко не так страшен, как о нем говорят. Система хоть и сложна в понимании, но невероятно логична и удобна в сопровождении, а имеющиеся средства управления позволяют очень точно диагностировать проблемы и легко их устранять.

Warning

По умолчанию tar не сохраняет контексты безопасности файлов, что может привести к проблемам при последующей распаковке. Флаг “–selinux” исправляет это.