Product SiteDocumentation Site

14.5. Einführung in SELinux

14.5.1. Prinzipien

SELinux (Security Enhanced Linux) ist ein System mit Mandatory Access Control, das auf der LSM-Schnittstelle (Linux Security Modules) von Linux aufbaut. In der Praxis befragt der Kernel SELinux vor jedem Systemaufruf, um herauszufinden, ob der Prozess autorisiert ist, den jeweiligen Vorgang auszuführen.
SELinux verwendet einen Satz von Regeln - in ihrer Gesamtheit als Policy bezeichnet - um Vorgänge zu autorisieren oder zu verbieten. Diese Regeln sind schwierig zu erstellen. Glücklicherweise werden zwei Standardregelwerke (targeted und strict) bereitgestellt, die den Großteil der Konfigurierungsarbeit entbehrlich machen.
Mit SELinux ist die Verwaltung der Berechtigungen grundsätzlich verschieden von traditionellen Unix-Systemen. Die Berechtigungen eines Prozesses hängen von seinem Sicherheitskontext ab. Der Kontext wird von der Identität des Benutzers bestimmt, der den Prozess gestartet hat, sowie von der Rolle und der Domain, die dem Benutzer zu dieser Zeit übertragen waren. Die Berechtigungen hängen tatsächlich von der Domain ab, aber die Übergänge zwischen den Domains werden von den Rollen kontrolliert. Und schließlich hängen die möglichen Übergänge zwischen den Rollen von der Identität ab.
Sicherheitskontexte und Unix-Nutzer

Abbildung 14.1. Sicherheitskontexte und Unix-Nutzer

In practice, during login, the user gets assigned a default security context (depending on the roles that they should be able to endorse). This defines the current domain, and thus the domain that all new child processes will carry. If you want to change the current role and its associated domain, you must call newrole -r role_r -t domain_t (there is usually only a single domain allowed for a given role, the -t parameter can thus often be left out). This command authenticates you by asking you to type your password. This feature forbids programs to automatically switch roles. Such changes can only happen if they are explicitly allowed in the SELinux policy.
Offensichtlich gelten die Berechtigungen nicht für alle Objekte (Dateien, Verzeichnisse, Sockets, Geräte usw.). Sie können von Objekt zu Objekt unterschiedlich sein. Um dies zu erreichen, ist jedes Objekt einem Typ zugeordnet (dies wird als Kennzeichnung bezeichnet). Die Rechte einer Domain werden somit durch Sätze von Operationen ausgedrückt, die bei diesen Typen erlaubt sind oder nicht (und indirekt bei allen Objekten, die mit dem jeweiligen Typ gekennzeichnet sind).
Standardmäßig übernimmt ein Programm die Domain des Nutzers, der es gestartet hat, aber die normalen SELinux-Regeln erwarten, dass viele wichtige Programme in speziell für sie vorgesehenen Domains laufen. Um dies zu erreichen, werden diese ausführbaren Dateien mit einem fest zugeordneten Typ gekennzeichnet (zum Beispiel wird ssh mit ssh_exec_t gekennzeichnet, und wenn das Programm startet, wechselt es selbstständig in die Domain ssh_t). Dieser automatische Vorgang des Domainwechsels ermöglicht es, jedem Programm nur die Berechtigungen zu gewähren, die es benötigt. Dies ist ein wesentliches Prinzip von SELinux.
Selbstständige Übergänge zwischen Domains

Abbildung 14.2. Selbstständige Übergänge zwischen Domains

14.5.2. SELinux einrichten

Die Unterstützung von SELinux ist in den von Debian bereitgestellten Standard-Kerneln enthalten. Die Kernprogramme von Unix unterstützen SELinux ohne Änderungen. Es ist daher recht einfach, SELinux zu aktivieren.
Der Befehl apt install selinux-basics selinux-policy-default installiert selbstständig die zur Konfigurierung eines SELinux-Systems erforderlichen Pakete.
Das Paket selinux-policy-default enthält einen Satz von Standardregeln. Standardmäßig beschränkt dieses Regelwerk nur den Zugang für einige besonders gefährdete Dienste. Die Nutzersitzungen sind nicht eingeschränkt, und es ist daher unwahrscheinlich, dass SELinux legitime Nutzeraktionen blockieren würde. Dieses erhöht jedoch die Sicherheit von Systemdiensten, die auf dem Rechner laufen. Um ein Regelwerk einzurichten, das den alten „strengen“ Regeln entspricht, müssen Sie nur das Modul unconfined deaktivieren (die Modulverwaltung wird in diesem Kapitel ausführlich beschrieben).
Sobald das Regelwerk installiert ist, sollten Sie alle verfügbaren Dateien kennzeichnen (das heißt, sie einem Typ zuzuordnen). Dieser Vorgang muss mit dem Befehl fixfiles relabel von Hand gestartet werden.
Das SELinux-System ist nun einsatzbereit. Um es zu aktivieren, sollten Sie den Parameter selinux=1 security=selinux zum Linux-Kernel hinzufügen. Der Parameter audit=1 aktiviert bei SELinux das Protokollieren, durch das alle unterbundenen Vorgänge aufgezeichnet werden. Schließlich bringt der Parameter enforcing=1 das Regelwerk zur Anwendung: ohne ihn läuft SELinux in seinem standardmäßigen permissive-Modus, bei dem unterbundene Vorgänge zwar protokolliert, aber dennoch ausgeführt werden. Sie sollten daher die Konfigurationsdatei des GRUB-Bootloaders anpassen, indem Sie die gewünschten Parameter anhängen. Ein einfacher Weg, dies zu tun, besteht darin, die Variable GRUB_CMDLINE_LINUX in der Datei /etc/default/grub zu ändern und dann den Befehl update-grub auszuführen. SELinux ist dann nach einem Neustart aktiv.
Es sei darauf hingewiesen, dass das Skript selinux-activate diese Vorgänge automatisiert und das Kennzeichnen der Dateien beim nächsten Rechnerstart erzwingt (wodurch vermieden wird, dass neue nicht gekennzeichnete Dateien erstellt werden, während SELinux noch nicht aktiv ist und das Kennzeichnen noch andauert).

14.5.3. Ein SELinux-System verwalten

Das SELinux-Regelwerk ist ein modularer Satz von Regeln, und mit seiner Installierung werden automatisch alle relevanten Module entsprechend den bereits installierten Diensten erkannt und aktiviert. Das System ist hierdurch sofort funktionsfähig. Wenn jedoch ein Dienst später als das SELinux-Regelwerk installiert wird, müssen Sie in der Lage sein, das entsprechende Modul manuell zu aktivieren. Hierzu dient der Befehl semodule. Darüber hinaus müssen Sie in der Lage sein, die Rollen festzulegen, die jeder Nutzer bestätigen kann. Dies geschieht mit dem Befehl semanage.
Diese beiden Befehle können somit dazu benutzt werden, die aktuelle SELinux-Konfiguration, die in /etc/selinux/default/ gespeichert ist, zu ändern. Im Gegensatz zu anderen Konfigurationsdateien, die Sie in /etc/ finden, dürfen diese Dateien nicht manuell verändert werden. Sie sollten hierzu die für diesen Zweck vorgesehenen Programme verwenden.

14.5.3.1. SELinux-Module verwalten

Verfügbare SELinux-Module sind im Verzeichnis /usr/share/selinux/default/ gespeichert. Um eines dieser Module in der aktuellen Konfiguration zu aktivieren, sollten Sie den Befehl semodule -i Modul.pp.bz2 benutzen. Die Erweiterung pp.bz2 steht für policy package (komprimiert mit bzip2).
Das Entfernen eines Moduls aus der aktuellen Konfiguration erfolgt mit semodule -r module. Letztendlich listet der Befehl semodule -l die aktuell installierten Module auf. Es gibt auch deren Versionsnummern aus. Module können selektiv mit semodul -e aktiviert und mit semodul -d deaktiviert werden.
# semodule -i /usr/share/selinux/default/abrt.pp.bz2
libsemanage.semanage_direct_install_info: abrt module will be disabled after install as there is a disabled instance of this module present in the system.
# semodule -l
accountsd
acct
[...]
# semodule -e abrt
# semodule -d accountsd
# semodule -l
abrt
acct
[...]
# semodule -r abrt
libsemanage.semanage_direct_remove_key: abrt module at priority 100 is now active.semodule -l
semodule lädt die neue Konfiguration unmittelbar, es sei denn, Sie verwenden seine Option -n. Es sei darauf hingewiesen, dass das Programm standardmäßig auf die aktuelle Konfiguration wirkt (die unter der Variablen SELINUXTYPE in der Datei /etc/selinux/config angegeben ist), aber Sie können eine andere ändern, indem Sie sie mit der Option -s vorgeben.

14.5.3.2. Identitäten verwalten

Jedes Mal, wenn sich ein Benutzer anmeldet, wird ihm eine SELinux-Identität zugewiesen. Diese bestimmt die Rollen, die er bestätigen kann. Diese beiden Zuordnungen (des Benutzers zur Identität und der Identität zu den Rollen) können mit dem Befehl semanage konfiguriert werden.
You should definitely read the semanage(8) manual page. All the managed concepts have their own manual page; for instance, semanage-login(8). Even if the command's syntax tends to be similar for all the concepts which are managed, it is recommended to read its manual page. You will find common options to most sub-commands: -a to add, -d to delete, -m to modify, -l to list, and -t to indicate a type (or domain).
semanage login -l führt die aktuellen Zuordnungen zwischen Benutzerkennungen und SELinux-Identitäten auf. Benutzer, die keinen ausdrücklichen Eintrag haben, erhalten die Identität, die im Eintrag __default__ angegeben ist. Der Befehl semanage login -a -s user_u benutzer ordnet die Identität user_u dem angegebenen Benutzer zu. Schließlich entfernt der Befehl semanage login -d benutzer den Zuordnungseintrag, der an diesen Benutzer vergeben war.
# semanage login -a -s user_u rhertzog
# semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0:c0.c1023       *
rhertzog             user_u               s0                   *
root                 unconfined_u         s0-s0:c0.c1023       *
# semanage login -d rhertzog
semanage user -l führt die Zuordnungen zwischen den SELinux-Benutzeridentitäten und den erlaubten Rollen auf. Um eine neue Identität hinzuzufügen, ist es erforderlich, sowohl die entsprechenden Rollen als auch ein kennzeichnendes Präfix festzulegen, das dazu benutzt wird, einem Typ persönliche Dateien (/home/benutzer/*) zuzuordnen. Als Präfix muss user, staff oder sysadm gewählt werden. Das Präfix „staff“ ergibt Dateien des Typs „staff_home_dir_t“. Das Erstellen einer neuen SELinux-Benutzeridentität geschieht mit dem Befehl semanage user -a -R rollen -P präfix identität. Schließlich kann eine SELinux-Benutzeridentität mit dem Befehl semanage user -d identität entfernt werden.
# semanage user -a -R 'staff_r user_r' -P staff test_u
# semanage user -l

                Labeling   MLS/       MLS/                          
SELinux User    Prefix     MCS Level  MCS Range                      SELinux Roles

root            sysadm     s0         s0-s0:c0.c1023                 staff_r sysadm_r system_r
staff_u         staff      s0         s0-s0:c0.c1023                 staff_r sysadm_r
sysadm_u        sysadm     s0         s0-s0:c0.c1023                 sysadm_r
system_u        user       s0         s0-s0:c0.c1023                 system_r
test_u          staff      s0         s0                             staff_r user_r
unconfined_u    unconfined s0         s0-s0:c0.c1023                 system_r unconfined_r
user_u          user       s0         s0                             user_r
# semanage user -d test_u

14.5.3.3. Dateikontexte, Ports und Boolesche Optionen verwalten

Jedes SELinux-Modul stellt einen Satz von Dateibezeichnungsregeln zur Verfügung, aber es ist auch möglich, eigene Bezeichnungsregeln hinzuzufügen, um einen speziellen Fall abzudecken. Wenn Sie zum Beispiel möchten, dass der Webserver in der Lage ist, Dateien innerhalb der /srv/www/-Dateihierarchie zu lesen, könnten Sie semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?" gefolgt von restorecon -R /srv/www/ ausführen. Der erste Befehl registriert die neue Bezeichnungsregel, und der zweite gleicht die Dateitypen gemäß den derzeitigen Bezeichnungsregeln an.
Ebenso sind die TCP/UDP-Ports in einer Weise gekennzeichnet, die sicherstellt, dass nur die entsprechenden Daemons an ihnen Verbindungen annehmen können. Wenn Sie zum Beispiel möchten, dass der Web-Server am Port 8080 Verbindungen annehmen kann, sollten Sie den Befehl semanage port -m -t http_port_t -p tcp 8080 ausführen.
Einige SELinux-Module exportieren Boolesche Optionen, die Sie justieren können, um das Verhalten der Standardregeln zu ändern. Das Dienstprogramm getsebool kann dazu verwendet werden, diese Optionen anzusehen (getsebool boolesche_option zeigt eine Option an und getsebool -a alle). Der Befehl setsebool boolesche_option wert ändert den aktuellen Wert einer Booleschen Option. Die Option -P macht die Änderung dauerhaft, was bedeutet, dass der neue Wert zum Standard wird und über Neustarts hinaus erhalten bleibt. Das unten stehende Beispiel gewährt Web-Servern Zugriff auf Home-Verzeichnisse (dies ist nützlich, wenn Benutzer persönliche Websites in ~/public_html/ haben).
# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off
# setsebool -P httpd_enable_homedirs on
# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> on

14.5.4. Die Regeln anpassen

Da das SELinux-Regelwerk modular ist, könnte es interessant sein, neue Module für (möglicherweise maßgefertigte) Anwendungen zu entwickeln, für die es diese noch nicht gibt. Diese neuen Module würden dann die Referenzrichtlinien ergänzen.
Zur Erstellung neuer Module werden die Pakete selinux-policy-dev und selinux-policy-doc benötigt. Letzteres enthält die Dokumentation der Standardregeln (/usr/share/doc/selinux-policy-doc/html/) und Beispieldateien, die als Vorlagen für die Erstellung neuer Module verwendet werden können. Installieren Sie diese Dateien und untersuchen Sie sie genauer:
$ cp /usr/share/doc/selinux-policy-doc/Makefile.example Makefile
$ cp /usr/share/doc/selinux-policy-doc/example.fc ./
$ cp /usr/share/doc/selinux-policy-doc/example.if ./
$ cp /usr/share/doc/selinux-policy-doc/example.te ./
Die Datei .te ist die wichtigste. Sie legt die Regeln fest. Die Datei .fc bestimmt die „Dateikontexte“, das heißt, die Typen, die den auf diese Module bezogenen Dateien zugeordnet sind. Die in der Datei .fc befindlichen Daten werden während des Dateikennzeichnungsschrittes benutzt. Schließlich legt die Datei .if die Schnittstelle der Module fest: es ist ein Satz „öffentlicher Funktionen“, die andere Module verwenden können, um ordnungsgemäß mit dem Modul, das Sie erstellen, zu interagieren.

14.5.4.1. Eine .fc-Datei schreiben

Das Lesen des unten stehenden Beispiels sollte genügen, um die Struktur einer derartigen Datei zu verstehen. Sie können reguläre Ausdrücke verwenden, um denselben Sicherheitskontext mehreren Dateien zuzuordnen oder auch einem ganzen Verzeichnisbaum.

Beispiel 14.2. beispiel.fc-Datei

# myapp executable will have:
# label: system_u:object_r:myapp_exec_t
# MLS sensitivity: s0
# MCS categories: <none>

/usr/sbin/myapp         --      gen_context(system_u:object_r:myapp_exec_t,s0)

14.5.4.2. Eine .if-Datei schreiben

In unten stehendem Beispiel kontrolliert die erste Schnittstelle („myapp_domtrans“), wer die Anwendung ausführen kann. Die zweite („myapp_read_log“) gewährt Schreibzugriff auf die Protokolldateien der Anwendung.
Jede Schnittstelle muss einen gültigen Regelsatz erzeugen, der in eine .te-Datei eingegliedert werden kann. Sie sollten daher alle Typen, die Sie verwenden, festlegen (mit dem Makro gen_require) und Standardanweisungen benutzen, um Berechtigungen zu vergeben. Beachten Sie jedoch, dass Sie auch Schnittstellen benutzen können, die von anderen Modulen bereitgestellt werden. Der nächste Abschnitt gibt weitere Erläuterungen darüber, wie diese Berechtigungen ausgedrückt werden können.

Beispiel 14.3. beispiel.if-Datei

## <summary>Myapp example policy</summary>
## <desc>
##      <p>
##              More descriptive text about myapp.  The <desc>
##              tag can also use <p>, <ul>, and <ol>
##              html tags for formatting.
##      </p>
##      <p>
##              This policy supports the following myapp features:
##              <ul>
##              <li>Feature A</li>
##              <li>Feature B</li>
##              <li>Feature C</li>
##              </ul>
##      </p>
## </desc>
#

########################################
## <summary>
##      Execute a domain transition to run myapp.
## </summary>
## <param name="domain">
##      Domain allowed to transition.
## </param>
#
interface(`myapp_domtrans',`
        gen_require(`
                type myapp_t, myapp_exec_t;
        ')

        domtrans_pattern($1,myapp_exec_t,myapp_t)
')

########################################
## <summary>
##      Read myapp log files.
## </summary>
## <param name="domain">
##      Domain allowed to read the log files.
## </param>
#
interface(`myapp_read_log',`
        gen_require(`
                type myapp_log_t;
        ')

        logging_search_logs($1)
        allow $1 myapp_log_t:file r_file_perms;
')

14.5.4.3. Eine .te-Datei schreiben

Sehen Sie sich die beispiel.te-Datei an:
policy_module(myapp,1.0.0) 1

########################################
#
# Declarations
#

type myapp_t; 2
type myapp_exec_t;
domain_type(myapp_t)
domain_entry_file(myapp_t, myapp_exec_t) 3

type myapp_log_t;
logging_log_file(myapp_log_t) 4

type myapp_tmp_t;
files_tmp_file(myapp_tmp_t)

########################################
#
# Myapp local policy
#

allow myapp_t myapp_log_t:file { read_file_perms append_file_perms }; 5

allow myapp_t myapp_tmp_t:file manage_file_perms;
files_tmp_filetrans(myapp_t,myapp_tmp_t,file)

1

Das Modul muss mit seinem Namen und seiner Versionsnummer gekennzeichnet sein. Diese Anweisung ist obligatorisch.

2

Falls das Modul neue Typen einführt, muss es sie mit Anweisungen wie dieser festlegen. Zögern Sie nicht, so viele Typen zu erstellen, wie erforderlich sind, anstatt zu viele nutzlose Berechtigungen zu erteilen.

3

Diese Schnittstellen legen den Typ myapp_t als Prozess-Domain fest, die von jeder mit myapp_exec_t gekennzeichneten ausführbaren Datei benutzt werden sollte. Dies fügt diesen Objekten stillschweigend auch ein exec_type-Attribut hinzu, das seinerseits anderen Modulen ermöglicht, Berechtigungen zur Ausführung dieser Programme zu gewähren: zum Beispiel erlaubt das userdomain-Modul Prozessen mit den Domains user_t, staff_t und sysadm_t, sie auszuführen. Die Domains anderer eingeschränkter Anwendungen sind nicht berechtigt, sie auszuführen, es sei denn, die Regeln gewähren ihnen ähnliche Berechtigungen (dies trifft zum Beispiel auf dpkg mit seiner dpkg_t-Domain zu).

4

logging_log_file is an interface provided by the reference policy. It indicates that files labeled with the given type are log files which ought to benefit from the associated rules (for example, granting rights to logrotate so that it can manipulate them).

5

Die allow-Anweisung ist die grundlegende Anweisung zur Genehmigung eines Vorgangs. Der erste Parameter ist die Prozess-Domain, der es erlaubt ist, den Vorgang auszuführen. Der zweite legt das Objekt fest, das ein Prozess der zuvor genannten Domain handhaben darf. Dieser Parameter hat die Form „type:class“, wobei type sein SELinux-Typ ist und class die Art des Objekts beschreibt (Datei, Verzeichnis, Socket, FIFO usw.). Schließlich beschreibt der letzte Parameter die Berechtigungen (die erlaubten Vorgänge).
Berechtigungen sind als Satz erlaubter Vorgänge festgelegt und entsprechen diesem Schema: { vorgang1 vorgang2 }. Jedoch können Sie auch Makros verwenden, die die nützlichsten Berechtigungen darstellen. Die Datei /usr/share/selinux/devel/include/support/obj_perm_sets.spt listet sie auf.
The following web page provides a relatively exhaustive list of object classes, and permissions that can be granted.
Jetzt müssen Sie lediglich den kleinsten Regelsatz finden, der erforderlich ist, damit die Anwendung oder der Dienst, auf die er abzielt, ordnungsgemäß funktionieren. Um dies zu erreichen, sollten Sie sich gut damit auskennen, wie die Anwendung funktioniert, und welche Art von Daten sie verarbeitet oder erzeugt.
Jedoch ist auch eine auf Erfahrung beruhende Vorgehensweise möglich. Nachdem die relevanten Objekte richtig gekennzeichnet sind, können Sie die Anwendung im permissive-Modus benutzen: die Vorgänge, die verboten würden, werden protokolliert, werden aber weiterhin ausgeführt. Durch eine Analyse der Protokolle können Sie nun die Vorgänge identifizieren, die erlaubt werden sollen. Hier ist ein Beispiel eines derartigen Protokolleintrags:
avc:  denied  { read write } for  pid=1876 comm="syslogd" name="xconsole" dev=tmpfs ino=5510 scontext=system_u:system_r:syslogd_t:s0 tcontext=system_u:object_r:device_t:s0 tclass=fifo_file permissive=1
Um diese Mitteilung besser verstehen zu können, gehen wir sie Schritt für Schritt durch.

Tabelle 14.1. Analyse eines SELinux-Ablaufs

MeldungBeschreibung
avc: deniedEin Vorgang wurde abgelehnt.
{ read write }Dieser Vorgang erforderte die Berechtigungen read und write.
pid=1876Der Prozess mit der PID 1876 hat den Vorgang ausgeführt (oder hat versucht, ihn auszuführen).
comm="syslogd"Der Prozess war eine Ausführung des Programms syslogd.
name="xconsole"Das Zielobjekt wurde xconsole genannt. Manchmal kann man stattdessen eine "path"-Variable - mit dem vollen Pfad - haben.
dev=tmpfsThe device hosting the target object is a tmpfs (an in-memory filesystem). For a real disk, you could see the partition hosting the object (for example, “sda3”).
ino=5510Das Objekt ist mit der Inode-Nummer 5510 bezeichnet.
scontext=system_u:system_r:syslogd_t:s0Dies ist der Sicherheitskontext des Prozesses, der den Vorgang ausgeführt hat.
tcontext=system_u:object_r:device_t:s0Dies ist der Sicherheitskontext des Zielobjekts.
tclass=fifo_fileDas Zielobjekt ist eine FIFO-Datei.
By observing this log entry, it is possible to build a rule that would allow this operation. For example, allow syslogd_t device_t:fifo_file { read write }. This process can be automated, and it is exactly what the audit2allow command (of the policycoreutils package) offers. This approach is only useful if the various objects are already correctly labeled according to what must be confined. In any case, you will have to carefully review the generated rules and validate them according to your knowledge of the application. Effectively, this approach tends to grant more rights than are really required. The proper solution is often to create new types and to grant rights on those types only. It also happens that a denied operation isn't fatal to the application, in which case it might be better to just add a “dontaudit” rule to avoid the log entry despite the effective denial.

14.5.4.4. Die Dateien kompilieren

Once the 3 files (example.if, example.fc, and example.te) match your expectations for the new rules, rename them to myapp.extension and run make NAME=devel to generate a module in the myapp.pp file (you can immediately load it with semodule -i myapp.pp). If several modules are defined, make will create all the corresponding .pp files.