Jak na SELinux: zabezpečujeme Firefox

1. 2. 2008
Doba čtení: 9 minut

Sdílet

V poslední části našeho seriálu o SELinux si ukážeme postup, jak zabezpečit skutečnou část systému, konkrétně internetový prohlížeč Firefox. Od prvotních úvah a pravidel k nim se dostaneme k postupu, podle čeho psát pravidla a případně kde a co přesně hledat, pokud nám něco nefunguje tak, jak potřebujeme.

Podíváme se na:

  • postup při zabezpečení prohlížeče Firefox
  • AVC zprávy a jejich význam

Konfigurace prohlížeče Firefox

Idea je taková: zamezíme Firefoxu veškerý přístup do systému, až na ten, který je nezbytně nutný pro samotnou funkci prohlížeče. Při konfiguraci je použita striktní politika v permissive módu (pro připomenutí: SELinux je vypnutý, ale prováděné operace jsou logovány). Použijeme audit daemona auditd a modul enabledaudit.pp. SELinux politika má nastaveno, aby některé AVC zprávy (převážně dontaudit pravidla) nebyly logovány. S tímto modulem se logy budou velice rychle a opravdu vším prováděným plnit. V logu budeme vyhledávat AVC zprávy (AVC msg) a podle jejich obsahu budeme konfigurovat pravidla.

knihy

Chcete vědět o SELinux víc?

Chcete-li vědět víc, stáhněte si příručku nazvanou Česká dokumentace pro SELinux, kterou naleznete v naší elektronické knihovně na knihy.root.cz.

Začneme tím, že konfigurační soubory v domovských adresářích oddělíme (typem) od ostatních. Vytvoříme tedy nový typ pouze pro Firefox – typ user_firefox_home_t a tímto typem označkujeme ~/.mozilla adresáře. V adresáři /usr/share/se­linux/devel/ vytvoříme adresář local a v něm podadresář firefox, kam umístíme soubory firefox.te a firefox.fc.

 # soubor firefox.fc
 HOME_DIR/\.mozilla(/.*)?  \
     gen_context(user_u:object_r:user_firefox_home_t, s0)

Teď je potřeba vytvořit nový typ (doménu) pro samotný Firefox, se kterým bude moci k těmto souborům přistupovat a označkovat spouštěcí soubor, pomocí kterého Firefox do této domény přejde. Do firefox.fc přidáme:

 # vstupní bod do domény
 /usr/bin/firefox  --  \
     gen_context(system_u:object_r:firefox_exec_t, s0)

Nadále vytvoříme typ user_download_ho­me_t pro adresář ~/download, kde bude mít doména firefox_t povoleno číst a ukládat data.

 # adresář pro ukládání stažených souborů
 HOME_DIR/download(/.*)?  \
     gen_context(user_u:object_r:user_download_home_t, s0)

Více už do souboru firefox.fc připisovat nebudeme. Nyní vytvoříme soubor firefox.te a v něm definujeme nové typy, které budeme potřebovat (všechna následující pravidla už píšeme do firefox.te).

 policy_module(firefox, 1.0.0);
 require{
 attribute domain;    # atribut pro doménu
 attribute exec_type; # atribut pro spustitelný soubor
 attribute file_type; # atribut pro soubory, adresáře
 };
 # část s pravidly

 # typ pro doménu(atribut domain)
 type firefox_t, domain;

 # typ pro spustitelný (exec_type) soubor na \
   disku (file_type), jenž použijeme jako vstupní bod
 type firefox_exec_t, exec_type, file_type;

 # typ pro soubory v domovském adresáři (atribut file_type)
 type user_firefox_home_t, file_type;
 # typ pro soubory v adresáři download
 type user_download_home_t, file_type;

Zkompilujeme, zavedeme do jádra a přeznačkujeme soubory v domovských adresářích a spustitelný soubor:

 [root@noutec ~]# fixfiles relabel /home/ /usr/bin/firefox

Pokud nyní jako běžný uživatel vyzkoušíme vypsat atributy adresáře ~/download nebo ~/.mozilla, dostaneme následující výsledek (v enforcing módu):

 [honza@noutec ~]$ ls -l download
 ?--------- ? ?     ?        ? download

To znamená, že ačkoliv jsou soubory v našem domovském adresáři, nemáme povoleno je číst a vypisovat atributy. Připíšeme následující (podobná pravidla můžeme aplikovat na ~/.mozilla). Poté zkompilujeme, zavedeme do jádra a ověříme správné chování.

 # do části require
 type user_t;
 class file { create_file_perms };
 class dir { rw_dir_perms };

 # část s pravidly
 allow user_t user_download_home_t: dir rw_dir_perms;
 allow user_t user_download_home_t: file create_file_perms;

Poznámka: Pro zpřehlednění již nebudeme v následujících výpisech psát část require. Pokud budeme pracovat s nějakým typem, který jsme ještě nepoužili, tak ho v části require zapíšeme. To stejné platí i pro třídu a množinu operací nad danou třídou. Budeme tedy psát pouze TE pravidla.

Obdobná oprávnění musíme udělit i doméně firefox_t. Aby se doména dostala až k adresáři ~/.mozilla, musí mít oprávnění projít celou cestu k tomuto adresáři. Adresář /home/ má typ home_root_t, doména musí mít povoleno prohledávat tento adresář a získat atributy adresářů. Stejný důvod, pouze v /home/ o úroveň níže, má druhé pravidlo.

 allow firefox_t home_root_t: dir { search getattr };
 allow firefox_t user_home_dir_t: dir { search getattr };

Teď můžeme napsat samotná pravidla pro přístup do ~/.mozilla, kde bude moci doména firefox_t číst, zapisovat, vytvářet nové soubory a adresáře (například pro ukládání do cache, bookmarků a nastavení).

 # doména firefox_t může číst, vytvářet, přejmenovat (rename), \
   uzamknout (lock), zapisovat nebo zrušit odkaz na soubor \
   s typem user_firefox_home_t
 allow firefox_t user_firefox_home_t: file { getattr read write  \
                                      create rename unlink lock };
 # doména firefox_t může prohledávat, číst, přidávat/odebírat
   položky z adresáře s typem user_firefox_home_t
 allow firefox_t user_firefox_home_t: dir { search getattr write \
                                      add_name remove_name read };

Dostáváme se k části, jak bezpečně změnit doménu na firefox_t. Napíšeme pravidla pro přechod z user_t, přes firefox_exec_t do domény firefox_t. Buď použijeme přednastavené makro domain_auto_trans, a nebo si pravidla celé napíšeme sami. Nejprve nadefinujeme samotný přechod (ten ale nic nepovoluje). Pak povolíme typu user_t spustit soubor pro vstup do domény a povolíme přechod z user_t do firefox_t. Čtvrté pravidlo povolí typu firefox_t použít typ firefox_exec_t jako vstupní bod do domény. Pro čtení souboru jsou nutná oprávnění read a getattr. Ve striktní politice se rozhoduje i na základě role, proto musíme navíc povolit roli user_r přístup k typu firefox_t.

 # pokud user_t spustí soubor s typem firefox_exec_t vznikne \
   proces v doméně firefox_t
 type_transition user_t firefox_exec_t: process firefox_t;

 # user_t může spustit soubor s typem firefox_exec_t a použít ho \
   pro přechod do jiné domény
 allow user_t firefox_exec_t: file { execute read getattr \
                                     entrypoint }
 # user_t smí přejít do domény firefox_t
 allow user_t firefox_t: process transition;

 # pokud je spustěn soubor s typem firefox_exec_t, je povolen
   přechod do domény firefox_t
 allow firefox_t firefox_exec_t: file { entrypoint read getattr \
                                        execute };

 # pomoci makra domain_auto_trans by pravidla vypadala následovně
 domain_auto_trans(user_t, firefox_exec_t, firefox_t)
 allow firefox_t firefox_exec_t: file { execute entrypoint read \
                                        getattr };

 # přístup role k typu
 role user_r types { firefox_t };

Předchozí pravidla je možné napsat celkem intuitivně, nyní se dostáváme do části, kde přijdou na řadu logovací soubory. Zavedeme do jádra modul enabledaudit.pp.

 [root@noutec ~]# semodule -b /usr/share/selinux/\
 strict/enableaudit.pp

Zkusíme spustit prohlížeč firefox, běžící již v doméně firefox_t, prohlédneme logovací soubor ve /var/log/audit/ a budeme hledat záznamy jako tento:

 type=AVC msg=audit(1178479954.199:12870):
 avc:  denied { execute_no_trans } for  pid=31239 comm="firefox"
 name="firefox" dev=hda7 ino=1205324
 scontext=user_u:user_r:firefox_t:s0
 tcontext=system_u:object_r:firefox_exec_t:s0 tclass=file

…kde jsou informace o zakázané akci. Nejdůležitějšími částmi jsou pro nás scontext, tcontext, tclass a denied { operace }. AVC zpráva popisuje nepovolený pokus domény firefox_t (scontext) o spuštění bez změny typu (execute_no_trans) souboru (tclass=file) s typem firefox_exec_t (tcontext). Další pravidla se budou odvíjet na základě AVC zpráv.

Upravíme tedy poslední pravidlo na

 allow firefox_t firefox_exec_t: file { entrypoint read getattr \
                                        execute execute_no_trans };

Pravidlem

 allow firefox_t firefox_t: shm { create unix_read unix_write \
                                  read write destroy };

povolíme doméně firefox_t vytvořit, číst, zapisovat a odstranit sdílenou paměť.

Přístup k síti zajistí následující pravidla

 # povolení vytvořit si, číst, zjistit parametry, připojit se
   a zapisovat do tcp_socketu a udp_socketu
 allow firefox_t firefox_t: tcp_socket { create connect getopt \
                                      read write name_connect };
 allow firefox_t firefox_t: udp_socket { create connect getattr \
                                               read write ioctl};
 # povolení pojmenovat tcp_socket a posílat a přijímat zprávy
 allow firefox_t http_port_t: tcp_socket { name_connect send_msg \
                                           recv_msg};
 # povolení posílat/příjímat udp a tcp pakety síťové kartě
 allow firefox_t netif_t: netif { udp_send udp_recv tcp_send \
                                  tcp_recv };
 allow firefox_t node_t: node { udp_send udp_recv tcp_send \
                                tcp_recv };
 # doména firefox_t smí posílat/přijímat zprávy od udp_socket
   s typem dns_port_t
 allow firefox_t dns_port_t: udp_socket { send_msg recv_msg };
 # čtení nastavení sítě
 allow firefox_t net_conf_t: file { read getattr };
 # self reprezentuje doménu firefox_t
 allow firefox_t self: netlink_route_socket { create bind getattr \
                                            write nlmsg_read read};

Povolíme doméně firefox_t číst a spouštět sdílené knihovny. Typy ld_so_t, shlib_t, textrel_shlib_t a lib_t jsou označkovány sdílené knihovny. Povolíme číst a spouštět soubory s těmito typy, přečíst odkaz a procházet a číst adresář s typem lib_t

 allow firefox_t ld_so_t: file { read execute getattr };
 allow firefox_t ld_so_cache_t: file { read getattr };
 allow firefox_t shlib_t: file { read getattr execute };
 allow firefox_t textrel_shlib_t: file { read getattr execute };
 allow firefox_t lib_t: dir { search read getattr };
 allow firefox_t lib_t: lnk_file { read getattr };
 allow firefox_t lib_t: file { read getattr execute \
                               execute_no_trans };

Firefox_t doména si může zjistit (getsched) a změnit (setsched) svou prioritu procesu a posílat si signály.

 allow firefox_t firefox_t: process { getsched setsched \
                                      sigkill signal };

Přístup domény prohledávat adresář, číst a zapisovat do dočasných souborů okenního správce (xdm_tmp_t), povolení číst nastavení lokalizace (locale_t) a fonty (fonts_t).

 allow firefox_t xdm_tmp_t: dir { search };
 allow firefox_t xdm_tmp_t: file { write read getattr };
 allow firefox_t xdm_tmp_t: sock_file { write };
 allow firefox_t fonts_t: dir { getattr search };
 allow firefox_t fonts_t: file { read getattr };
 allow firefox_t locale_t: dir { search };
 allow firefox_t locale_t: file { read getattr };

Pravidel je celkem hodně a nebudeme je zde všechny vypisovat. Postupnou analýzou AVC zpráv budou pravidla přibývat, není však účelem povolit veškeré zakázané operace, protože by se mezi nimi mohly skrývat i akce, které rozhodně nejsou potřebné nebo žádoucí. Je tedy na administrátorovi se nad konfigurací zamyslet a ne pouze připisovat pravidla a všechno povolit.

Místo pročítání logů, můžeme použít nástroj audit2allow, který projde log a vygeneruje z něj .te soubor, ve kterém povolí všechny zakázané operace a my jen odstraníme pravidla, která nechceme.

Závěr

Tímto jsme dokončili naši cestu světem SELinuxu. Po seznámení s principy a uvedených příkladech bychom měli být schopní si nakonfigurovat SELinux dle svých představ a požadavků. Když se nám vše nepodaří na první pokus, nemusíme si z toho dělat hlavu. Chyba je většinou celkem banální a stačí si prohlédnout dřívější články. Věřím, že náš společný průchod byl poučný a většině pomohl nebo alespoň rozšířil znalosti o SELinuxu.

Použité zdroje:

bitcoin školení listopad 24

Česká dokumentace pro SELinux
MCCARTY Bill. SELinux. 1005 Gravenstein Highway North, Sebastopol, CA 95472: O'Reilly Media, Inc., 2004, 254s. ISBN 0–596–00716–7
Fedora SELinux Project Pages


Configuring the SELinux Policy
LOSCOCCO, Peter. Integrating Flexible Support for Security Policies into the Linux Operating System
Red Hat Enterprise Linux 4: Red Hat SELinux Guide
MORRIS, James. An Overview of Multilevel Security and LSPP under Linux
MORRIS, James. A Brief Introduction to Multi-Category Security (MCS)
CAPLAN, David, MACMILLAN, Karl, MAYER, Frank. SELinux Concepts


COKER, Faye. Writing SE Linux policy HOWTO
WALSH, Dan. danwalsh's Journal