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/*

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

4. Маны — наше все. В дистрибутивах 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" исправляет
это.

Категория: Материалы сайта
Постоянная ссылка