How-to по SELinux, базовые принципы

По ра­бо­те раз­би­рал­ся с 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 не ска­за­но, что на­до сме­нить кон­текст

Каж­дый кон­текст со­сто­ит из трех час­тей:

  1. user
  2. role
  3. type (у про­цес­сов его при­ня­то на­зы­вать domain, но раз­ни­цы ни­ка­кой нет)

Для фа­йлов кон­текст мож­но по­смот­реть так:

# ls -dZ /
dr-xr-xr-x. root root system_u:object_r:root_t:s0      /

Здесь:

  • system_u: user
  • object_r: role
  • s0: 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;

Огра­ни­че­ние толь­ко од­но­го би­нар­ни­ка

  1. Поста­вить па­кет selinux-policy-minimum (CentOS)
  2. Про­пи­сать этот policy в /etc/selinux/config
  3. reboot (мо­жет быть дол­гим ес­ли на­чнет­ся relabeling)
  4. Соз­дать мо­дуль, не за­быть про Пра­ви­ло для 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/

Полез­ные ссыл­ки