По работе разбирался с SELinux под CentOS, написал для себя документацию. Решил поделиться.
Подготовка
Поставить пакеты (CentOS)
yum install setools-console policycoreutils-python
Концепт
Политики
Для всей системы задается policy, которая будет применена ко всем
объектам. По умолчанию действует политика targeted. Политика задается
в файле /etc/sysconfig/selinux
Кратко:
targeted
: всем всё разрешено, за исключением процессов, для которых правила явно заданы (в CentOS их довольно много, в основном сетевые демоны)strict
: то же что и targeted, только доступ ограничен и не только сетевым процессамminimum
(в CentOS ставится отдельно, пакетомselinux-policy-minimum
): targeted, только по умолчанию совсем нет никаких правилMLS
: ?
Режимы SELinux
- disabled: SELinux выключен
enforcing
: включен, правила блокируют, сообщения о блокировке уходят в/var/log/audit/audit.log
permissive
: ничего не блокируется, только уходят сообщения в/var/log/audit/audit.log
Переключиться в permissive:
setenforce 0
Переключиться в enforcing:
setenforce 1
Контексты
Каждый объект мира (файл, процесс и т.д.) имеют свойство context. В SELinux совершенно не важно кто и куда сделал su, fork и т.д. — контекст наследуется до тех пор, пока явно в правилах SELinux не сказано, что надо сменить контекст
Каждый контекст состоит из трех частей:
user
role
type
(у процессов его принято называть domain, но разницы никакой нет)
Для файлов контекст можно посмотреть так:
# ls -dZ /
dr-xr-xr-x. root root system_u:object_r:root_t:s0 /
Здесь:
system_u
: userobject_r
: roles0
: type
Аналогично, можно посмотреть для процессов:
# ps auxwZ|head -2
LABEL USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
system_u:system_r:init_t:s0 root 1 0.0 0.3 19236 1572 ? Ss 10:54 0:00 /sbin/init
То, что у /sbin/init и у корневой директории одинаковый контекст ни о чём не говорит. Права на доступ к корню у init будут только если об этом явно сказано в правилах политики.
Точно так же можно узнать контекст текущего шелла:
# id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
Аудит
В режимах enforcing и permissive система пишет в лог
/var/log/audit/audit.log
обо всех событиях безопасности. Этот лог
полезен как при ручном создании правил, так и с помощью audit2allow.
Если auditd не запущен, сообщения уйдут в /var/log/messages
.
Создание новых правил
Лучше всего это делать через написание модуля. Модуль SELinux можно подгружать, выгружать, распространять. Удобно.
Общий план
- Создать новый type для запускаемого файла
- Создать новый type, который будет domain работающего приложения
- По умолчанию у нового домена нет никаких прав. Поэтому его можно запустить в permissive mode
- С помощью audit2allow создать шаблон правил, отредактировать их.
- Включить режим enforcing.
Создание модуля
Подготовка
mkdir "/root/selinux_my_module"
cd "/root/selinux_my_module"
ln -s "/usr/share/selinux/devel/Makefile"
vim "my_module.te"
Создание минимального модуля
module my_module 1.0;
require {
type unconfined_t;
class process { transition sigchld };
class file { read x_file_perms };
}
type mytype_t;
type mytype_exec_t;
role unconfined_r types mytype_t;
type_transition unconfined_t mytype_exec_t : process mytype_t;
Сборка и загрузка
make && make load
Загрузить заново после пересборки:
make && make reload
Проверка
cp /bin/date /root/test
chcon -t mytype_exec_t /root/test
setenforce 1
/root/test
Сейчас у бинарника нет ни на что прав, поэтому в последней строчке
должны получить /root/test: Permission denied
.
Добавление правил с помощью audit2allow
Утилита предназначена для генерации правил на основе лога аудита.
grep mytype /var/log/audit/audit.log | audit2allow -m mytype >my_module.te
Теперь можно поправить модуль, затем заново его загрузить.
Полезные правила
Возможность менять type обратно
По умолчани
...
require {
...
class file { ... relabelfrom ... }
}
allow unconfined_t mytype_exec_t:file { ... relabelfrom ... }
Базовые правила для запуска файла
# доступ к /
allow mytype_t root_t:dir search;
# разрешить unconfined_t запускать mytype_exec_t
allow unconfined_t mytype_exec_t:file { read getattr open execute };
# разрешить unconfined_t работу с процессом
allow unconfined_t mytype_t:process { siginh rlimitinh transition
noatsecure };
# разрешить unconfined_t менять контекст бинарника и переименовывать его
allow unconfined_t mytype_exec_t:file { rename relabelfrom relabelto };
allow mytype_exec_t fs_t:filesystem associate;
Пример готового модуля
Разрешить бинарнику с type mytype_t доступ только к /tmp
module mytype 1.0;
require {
type unconfined_t;
type tmp_t;
type root_t;
type sshd_t;
type mytype_t;
type mytype_exec_t;
class dir { getattr append read write lock create rename link rmdir open search };
class file { read entrypoint getattr open execute rename relabelfrom relabelto };
class process { siginh transition sigchld noatsecure rlimitinh };
type user_devpts_t;
class chr_file { open read write getattr append ioctl };
class fd use;
class filesystem associate;
}
#============= mytype_t ==============
# access to /
allow mytype_t root_t:dir search;
# access /tmp
allow mytype_t tmp_t:dir { getattr append read write lock create rename link rmdir open search };
# allow general context to run me
allow unconfined_t mytype_exec_t:file { read getattr open execute };
# allow general context to work with my process
allow unconfined_t mytype_t:process { siginh rlimitinh transition noatsecure };
# allow me to write to charcter device
allow mytype_t user_devpts_t:chr_file { read write getattr append ioctl };
# allow general context to switch into my context
type_transition unconfined_t mytype_exec_t : process mytype_t;
allow mytype_t mytype_exec_t:file entrypoint;
# allow me to send SIGCHLD to parent
allow mytype_t unconfined_t:process sigchld;
# allow general context to change to/from my context
allow unconfined_t mytype_exec_t:file { rename relabelfrom relabelto };
## access to pty in SSH session ##
allow mytype_t sshd_t:fd use;
Пример бинарника для тестов:
#include <stdio.h>
#include <dirent.h>
#include <errno.h>
void ls (char *dir) {
printf("\nNow will list %s\n", dir);
DIR *dirp = opendir(dir);
if (NULL == dirp) {
printf("Error opening dir %i\n", errno);
}
struct dirent *dp;
errno = 0;
while (dirp) {
if ((dp = readdir(dirp)) != NULL) {
printf("%s\n", dp->d_name);
} else {
if (errno) {
printf("Error readding dir %i\n", errno);
}
if (closedir(dirp)) {
printf("Error closing dir %i\n", errno);
}
return;
}
}
return;
}
int main() {
ls ("/tmp");
ls ("/etc");
return 0;
}
Собирать конечно надо статиком:
gcc -static a.c
После этого не забыть поменять context:
mv a.out /tmp
chcon -t mytype_t /tmp/a.out
Правило для policy “minimum”
Если установлен minimum, то необходимо всем пользователям роли unconfined_r (а это в политике minumum все пользователи) доступ к типу mytype_t.
role unconfined_r types mytype_t;
Ограничение только одного бинарника
- Поставить пакет
selinux-policy-minimum
(CentOS) - Прописать этот policy в
/etc/selinux/config
- reboot (может быть долгим если начнется relabeling)
- Создать модуль, не забыть про Правило для policy “minimum”
Прочее
Распространение с модулем контекстов файлов
В директории с модулем достаточно создать файл вида:
/usr/lib64/libmagic.* -- system_u:object_r:image_magick_lib_t:s0
...
...
Он точно так же соберется и будет установлен с помощью make && make reload
Обновление контекстов файлов согласно загруженным правилам
Проверить что и как будет обновлено:
fixfiles check /usr/lib64/
Обновить:
fixfiles relabel /usr/lib64/