Systemd/SELinux Refresher

1 Agenda

  • Vorbereitung der Lab-Umgebung
  • Systemd-Refresh
  • Namespaces mit systemd-nspawn
  • SELinux Refresh
  • Fehlersuche

3 Vorbereitung der Lab-Umgebung

3.1 Ein einfacher Web-Server

  • Wir benutzen einen einfachen Webserver als Beispiel-Dienst für die Systemd- und SELinux Übungen in diesem Kurs
  • Hier ist der Quellcode (C Programmiersprache) eines (sehr) einfachen Web-Servers. Dieser Webserver liefert nur eine statische Webseite aus (diese Webseite ist fest im Quellcode des Servers eingebaut und wird nicht aus dem Dateisystem geladen):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>

char response[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
"<!DOCTYPE html><html><head><title>Bye-bye baby bye-bye</title>"
"<style>body { background-color: #111 }"
"h1 { font-size:4cm; text-align: center; color: black;"
" text-shadow: 0 0 2mm red}</style></head>"
  "<body><h1>Goodbye, world!</h1></body></html>\r\n";

int main()
{
  int one = 1, client_fd;
  struct sockaddr_in svr_addr, cli_addr;
  socklen_t sin_len = sizeof(cli_addr);

  int sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0)
    err(1, "can't open socket");

  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
  int port = 8080;
  svr_addr.sin_family = AF_INET;
  svr_addr.sin_addr.s_addr = INADDR_ANY;
  svr_addr.sin_port = htons(port);

  if (bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
    close(sock);
    err(1, "Can't bind");
  }

  listen(sock, 5);
  while (1) {
    client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
    printf("got connection\n");

    if (client_fd == -1) {
      perror("Can't accept");
      continue;
    }

    write(client_fd, response, sizeof(response) - 1); /*-1:'\0'*/
    close(client_fd);
  }
}

3.2 Übung

  • Erstelle ein neues Verzeichnis für unser Project und erstellen die Datei mit dem Quellcode des Webserver (oben) mit Hilfe eines Text-Editors (emacs, mg, nano, vim etc)
mkdir ~/src
cd ~/src
$EDITOR simple-server1.c
  • C-Compiler installieren
# dnf install gcc 
  • Übersetze den Quellcode in eine Programm-Datei (simple-server)
gcc -o simple-server simple-server1.c
  • Die Programmdatei in das Verzeichnis /usr/local/bin kopieren
cp simple-server /usr/local/bin
  • Wir starten den Server im Hintergrund und testen die Funktion in dem wir uns mit einem Web-Browser an Port 8080 verbinden. Wir sollten dort eine "Hello^H^H^H^H^HGoodbye World" Meldung sehen. Bei jeder Verbindung gibt der Server den Text Got connection aus.
simple-server &

4 Systemd-Refresh

  • Systemd ist das Systemd zum Verwalten von Diensten auf aktuellen Linux-Systemen.
  • Systemd kann
    • Dienste starten
    • Dienste stoppen
    • Dienste an- und ausschalten
    • Dienste blocken (Start verhindern)
    • Abhängigkeiten zwischen den Diensten definieren
    • Dienste automatisch neu starten
    • Log-Daten in einer Datenbank verwalten (Journal)
    • Hostname, Systemzeit, Netzwerk und Namensauflösung verwalten
  • Systemd Dokumentation und Informationen https://www.freedesktop.org/wiki/Software/systemd/

4.1 Übungen

  • Die nachfolgenden Befehle sollten auf den Lab-Systemen ausprobiert werden. Einige Befehle sollten an das System angepasst werden, so sollte dienst.service durch einen auf dem System laufenden Dienst (z.B. rngd.service) ersetzt werden.
  • Die Übungen müssen als Benutzer root ausgeführt werden (Befehl sudo -i)
  • Die Lab-Systeme dürfen auch neu gestartet werden

4.2 Systemd Architektur

Daeamon Verwaltungs-Tool
systemd systemctl
systemd-hostnamed hostnamectl
systemd-machined machinectl
systemd-journald journalctl
systemd-localed localectl
systemd-logind loginctl
systemd-shutdownd shutdown
systemd-timedated timedatectl

4.3 Systemd Unit Arten

service von Systemd gestartete Dienste und Anwendungen
socket Socket Aktivierung
scopes Gruppierung von gestarteten Anwendungen
slice Resourcen für Prozessgruppen
path Pfad Aktivierung
mount Mountpoints (teilweise aus /etc/fstab)
automount automatische Mountpoints
target Gruppen von Units

4.4 Standard Konfigurationsdateien

Systemd definiert eine Reihe von Standard-Konfigurationsdateien, welche distributions-übergreifend verfügbar sind

/etc/hostname Hostname
/etc/machine-id Statistisch eindeutige ID des Rechners (UUID)
/etc/os-release Informationen ueber das Betriebssystem (Distribution, Versionsnummer)
/etc/locales.conf Ländereinstellungn
/etc/vconsole.conf Einstellungen der Text-Konsolen
/etc/modules-load.d/*.conf Konfiguration der Kernel-Module
/etc/sysctl.d/*.conf Konfigurationseinstellungen fuer Kernel-Parameter
/etc/tmpfiles.d/*.conf Konfiguration fuer Verzeichnisse mit temporären Dateien
/etc/binfmt.d/*.conf Konfiguration fuer Linux-fremde Programmformate

4.5 Systemd Konfigurationsdateien

  • Der Befehl systemd-delta zeigt die Änderungen an System-Konfigurationsdateien.

4.5.1 System-Konfigurationen

/etc/systemd/... (hoechste System Prio)
/run/systemd/... (zweite Prio)
/usr/lib/systemd/... (dritte System Prio)
/usr/lib/systemd/system-preset... (letzte System Prio)

4.5.2 Benutzer-Konfigurationen

$XDG_CONFIG_HOME/systemd/user/* (hoechste User Prio)
$HOME/.config/systemd/user/*  
/etc/systemd/user/*  
$XDG_RUNTIME_DIR/systemd/user/*  
/run/systemd/user/*  
$XDG_DATA_HOME/systemd/user/*  
$HOME/.local/share/systemd/user/*  
/usr/lib/systemd/user/* (unterste User Prio)

4.6 Systemd-Units anzeigen und editieren, Drop-Ins

  • lesbarer Status eines Systemd-Dienstes inkl. Pfad zu der Unit-Datei und ggf. Drop-Ins
  • Alle Systemd-Einstellungen (inkl. der Defaults) einer Unit anzeigen. Dieser Befehl kann gut in Skripten benutzt werden.
systemctl show <unit>

4.6.1 Systemd-Units editieren

  • Einige Einstellungen einer vom System mitgelieferten Unit überschreiben. Es wird eine Drop-In Datei in /etc/systemd/systemd/<unit-name>.<unit-typ>.d/override.conf erstellt. Beim Ändern von Unit-Dateien per systemctl edit wird der Systemd-Dämon direkt nach dem Beenden des Editors über die Änderungen informiert.
systemctl edit <unit-name>
  • Die vom System gelieferte Unit in /etc/systemd/system/ kopieren und komplett ersetzen
systemctl edit --full <unit-name>
  • Temporäre Änderungen an den vom System mitgelieferten Units
systemctl edit --runtime <unit-name>

4.7 Systemd-Dienste

4.7.1 Dienste starten und eintragen

  • Dienst starten
systemctl start dienstname
  • Alternativ wird der service Befehl auch noch unterstützt
service dienst start
  • den aktuellen Status eines Dienstes anzeigen
systemctl status dienst
  • Dienst anschalten, so das der Dienst bei einem Rechnerneustart mit gestartet wird
systemctl enable dienst
systemctl reenable dienst
  • Dienst anschalten und gleich starten
systemctl enable --now dienst
  • Dienst neu starten. try-restart startet den Dienst nur, wenn der Dienst schon gestartet war
systemctl restart dienst
systemctl try-restart dienst
  • Dienst neu laden (reconfig)
systemctl reload dienst
  • Dienst sofort, einmalig beenden
systemctl stop dienst
  • Alternativ: Beenden eines Dienstes mit service
service dienst stop
  • Dienst beim Booten nicht starten
systemctl disable dienst
  • Dienst "maskieren" und "de-maskieren". Ein maskierter Dienst kann nicht automatisch (als Abhängigkeit) oder manuell gestartet werden
systemctl mask name
systemctl unmask name
  • Dienstkonfiguration erweitern/anpassen (Override-Datei anlegen/editieren)
systemctl edit dienst

4.7.2 Systemd Aufgabe – eigener Dienst

  • date-daemon – schreibt Datum / Uhrzeit alle 60 Sekunden in syslog
  • die Datei /usr/local/bin/dated mit einem Texteditor erstellen
#!/bin/sh
while true; do
   logger -t dated $(date)
   sleep 60
done
  • Datei als ausführbares Programm markieren:
chmod +x /usr/local/bin/dated
  • Ein weiteres Terminal-Fenster öffnen, dort den Systemlog (/var/log/messages) anzeigen mit tail -f <datei> oder less im "Follow" Modus (Taste "F")
  • Das Programm dated auf der Kommandozeile im Terminal ausführen, prüfen das die Uhrzeit jede Minute in das Syslog geschrieben wird
  • Das Programm dated abbrechen
  • Eine Dienstekonfiguration für den neuen "dated" Dienst in der Datei /etc/systemd/system/dated.service erstellen
[Unit]
Description=Logged Datum/Uhrzeit alle 60 Sekunden in Syslog
After=syslog.target

[Service]
ExecStart=/usr/local/bin/dated

[Install]
WantedBy=multi-user.target
  • Dem systemd Init-Prozess den neuen Dienst mitteilen
systemctl daemon-reload
  • den neuen Dienst starten
systemctl start dated
  • Prüfen, ob der "dated" Dienst laüft (Einträge im Syslog /var/log/messages und Systemd-Journal), Prozess prüfen mit ps
  • Dienst-Status prüfen
systemctl status dated
  • Dienst stoppen, prüfen das der Dienst wirklich gestoppt ist (keine neuen Einträge im Syslog), Prozess in der ps Ausgabe verschwunden
  • Dienst anschalten, so das dated bei einem Systemneustart auch gestartet wird
systemctl enable dated
  • Prüfe, ob der dated Dienst bei einem Reboot des Systems wirklich gestartet wurde. Wenn nicht, warum nicht?

4.7.3 Dienste anzeigen

  • alle aktiven Service-Units anzeigen
# systemctl list-units -t service
  • auch inaktiven Service-Units anzeigen
# systemctl list-units -t service --all
  • alle Unit-Dateien zu Diensten (Services) anzeigen
# systemctl list-unit-files -t service
  • Alle Sockets- und Timer-Units auflisten
# systemctl list-sockets
# systemctl list-timers

4.7.4 Abfrage, ob ein Dienst angeschaltet und/oder aktiv ist

# systemctl is-enabled name.service
# systemctl is-active name.service

4.7.5 Dienst-Abhängigkeiten

  • die Abhängigkeiten eines Dienstes anzeigen
systemctl list-dependencies <unit>
  • Units anzeigen, welche von diesem Dienst abhängen
systemctl list-depedencies --reverse <unit>

4.8 Systemd-Targets

4.8.1 Targets auflisten

  • Mittels Systemd können Gruppen von Diensten zu Systemzuständen (Targets) zusammengefasst werden. Targets definieren, welche Dienste gestartet und nicht gestartet sein sollen
# systemctl --type=target --all

4.8.2 Target ansteuern

# systemctl isolate rescue.target

4.8.3 Rescue-/Emergency-Mode (Single-User)

  • Linux in den Rettungs-Modus versetzen. Achtung, in diesem Modus ist kein Netzwerk verfügbar, dieser Modus sollte daher nur bei Zugriff auf eine Admin-Konsole (direkt an der Maschine oder per KVM-Switch) ausgeführt werden. Diese Befehle nicht auf der Lab-Umgebung ausführen!
# systemctl rescue
# systemctl emergency

4.8.4 Rechner anhalten/herunterfahren

# systemctl halt
# systemctl poweroff
# systemctl reboot

4.8.5 Standard-Boot-Target

  • Das Standard-Boot-Target anzeigen
# systemctl get-default
# readlink /etc/systemd/system/default.target
  • Das Standard-Boot-Target ändern
systemctl set-default graphical.target

4.9 Analyse des Systemd-Bootvorgangs

  • Start-Units nach Zeitverbrauch anzeigen
# systemd-analyze blame
  • Den kritischen Pfad des Boot-Prozesses anzeigen
# systemd-analyze critical-chain
  • Boot-Prozesse grafisch anzeigen
  • Übung: wie kann die Datei startup.svg bei einer entfernten Maschine angezeigt werden? Überlege, wie Du die Datei anzeigen kannst.
# systemd-analyze plot > startup.svg

4.10 Systemd Resource-Management und CGroups

  • Man-Page SYSTEMD.RESOURCE-CONTROL(5)

4.10.1 Unit Parameter

Parameter Default Beschreibung
CPUAccounting=true -- CPUAccounting anschalten
CPUQuota=xx% 100%*CPU CPU Verbrauch festlegen
CPUShares=value 1024 CPU Belegung
MemoryAccounting=true -- Speicherverbrauch Ueberwachung anschalten
MemoryLimit=value -- Speicherverbrauch (K,M,G,T)
BlockIOAccounting=true -- BlockIO Accounting anschalten
BlockIOWeight=value 1000 generische IO Prio (100 < Wert < 1000)
BlockIODeviceWeight=device_name value 1000 IO Prio fuer Geraet
BlockIOReadBandwidth=device_name value -- Lese-Bandbreite pro Sekunde (K,M,G,T)
BlockIOWriteBandwidth=device_name value -- Schreib-Bandbreite pro Sekunde (K,M,G,T)
DeviceAllow=device_name options -- Zugriff (r,w,m) auf Geraete-Dateien
DevicePolicy=value -- Zugriffs-Policy fuer Geraete-Dateien (strict, closed, auto)
Slice=slice_name -- Unit einem Slice zuordnen
ControlGroupAttribute=attribute value -- Low-Level Control-Group Parameter setzen

4.10.2 Exec Parameter (im Abschnitt [Service])

Nice= den nice Wert des Prozesses setzen
OOMScoreAdjust= den Score Wert des Kernel Out-of-Memory (OOM) Killers im Kernel für dieses Prozess setzen
IOSchedulingClass=, IOSchedulingPriority= die IO-Priorität des Prozesses setzen
CPUSchedulingPolicy=, CPUSchedulingPriority= die CPU-Priorität des Prozesses setzen
CPUAffinity= den Prozess auf einen oder mehrere CPU-Kerne beschränken

4.10.3 Control-Group Informationen anzeigen

  • Control-Group Parameter der Systemd-Prozesse anzeigen
# systemd-cgls
# systemd-cgtop

4.11 systemd-run

  • mit systemd-run können Prozesse Ad-Hoc unter Kontrolle von Systemd gestartet werden, ohne vorher erst eine Unit-Datei schreiben zu müssen
    • dabei können (fast) alle Parameter aus den Unit-Dateien auf der Kommandozeile angegeben werden
  • Beispiel: ein kleines CPU-Stress Programm /usr/local/bin/my-stress
#!/bin/sh
while true; do
  x=$((123456789/65234))
done
  • Stress-Programm ohne Resource-Control starten
chmod +x /usr/local/bin/my-stress
my-stress &
my-stress &
my-stress &
my-stress &
  • Stress-Programm mit systemd-run starten
# systemd-run -p CPUQuota=10% -p CPUAccounting=true /usr/local/bin/my-stress
Running as unit run-3104.service.
# systemctl status run-3104
-> run-3104.service - /root/./stress.sh
   Loaded: loaded (/run/systemd/system/run-3104.service; static; vendor preset: disabled)
  Drop-In: /run/systemd/system/run-3104.service.d
           * 50-CPUAccounting.conf, 50-CPUQuota.conf, 50-Description.conf, 50-ExecStart.conf
   Active: active (running) since Mon 2017-01-23 10:25:17 CET; 1min 21s ago
 Main PID: 3105 (stress.sh)
   CGroup: /system.slice/run-3104.service
           * 3105 /bin/sh /usr/local/bin/stress

Jan 23 10:25:17 centos7.home.strotmann.de systemd[1]: Started /root/./stress.sh.
Jan 23 10:25:17 centos7.home.strotmann.de systemd[1]: Starting /root/./stress.sh...
  • Systemd-run als Ersatz für at. Siehe man systemd.time für die Zeit-Formate
# systemd-run --on-calendar 10:31 /usr/local/bin/my-stress
Running timer as unit run-3281.timer.
Will run service as unit run-3281.service.

4.12 Dokumentation zu Systemd

4.13 Systemd-Service-Unit erstellen

  • Wir erstellen eine SystemD Service-Unit für den Server Dienst
$EDITOR /etc/systemd/system/simple-server.service
  • Die Unit-Datei
[Unit]
Description=a simple http server
After=syslog.target network.target

[Service]
ExecStart=/usr/local/bin/simple-server

[Install]
WantedBy=multi-user.target
  • Die neue Systemd-Service-Datei muss mit dem richtigen SELinux Label versehen werden
restorecon -R -v /etc/systemd/system/simple-server.service
  • Systemd Service-Units neu laden und den Simple-Server starten
systemctl daemon-reload
systemctl start simple-server
systemctl enable simple-server
systemctl status simple-server

4.14 systemd Sicherheit

4.14.1 Einfache Direktiven

  • Units unter anderer uid/gid laufen lassen
  • Zugriff auf Verzeichnisse beschränken
  • Prozesslimits setzen
$EDITOR /etc/systemd/system/simplehttp.service

[Unit]
Description=HTTP Server

[Service]
Type=simple
Restart=on-failure

#User=karl
#Group=users

#WorkingDirectory=/usr/share/doc
#PrivateTmp=yes
#ReadOnlyDirectories=/var
#InaccessibleDirectories=/home /usr/share/doc
#LimitNPROC=1  #darf nicht forken
#LimitFSIZE=0  #darf keine Files schreiben

ExecStart=/bin/python3 -m http.server 8000

4.14.2 Weitere Directiven und Isolationstechniken

  • PrivateNetwork=yes
  • CapabilityBoundingSet=CAP_CHOWN CAP_KILL
  • CapabilityBoundingSet=~CAP_SYS_PTRACE
  • AmbientCapabilities=CAP_NET_BIND_SERVICE
  • DeviceAllow=/dev/null rw
  • ProtectSystem={ full | strict }
  • ProtectHome=

4.14.3 Selbstanalyse

  • systemd-analyze security
  • systemd-analyze security <unit.service>

4.14.4 Übung:

  • Teste den Dienst simple-server mit systemd-analyze. Erhöhe die Sicherheit des Dienstes mit den angegebenen Empfehlungen. Versuche einen Score von unter 5 zu bekommen. Stelle sicher das der Dienst immer noch funktioniert und die Webseite ausliefert.

4.15 Systemd-Journal

4.15.1 Systemd-Journal Auffrischung

  • Die "Logdateien" im systemd, aka das Journal, ist eine binäre Datenbank mit umfassenden Suchwerkzeugen
  • contra
    • kein KISS Design
    • schlechte post-mortem Analyse
    • nicht mehr kompatibel zu alten Logauswertungen (z.B. logwatch)
  • pro
    • Metainfos nicht mehr fälschbar (weil vom Daemon)
    • Außerhalb des laufenden systemd-journald nicht mehr fälschbar (Verkettung der Log-Einträge per kryptografischen Hashes)
    • wartungsfrei (kein logrotate)
    • kann applikationsspezifische Werte aufnehmen
    • umfangreiche Abfragemöglichkeiten
  1. Journal-Dateien
    • /var/log/journal/<machine-id> ← persistent
    • /run/log/journal/<machine-id> ← dynamisch

    Die machine-id steht in /etc/machine-id und wird automatisch generiert oder mit systemd-machine-id-setup erzeugt. Das Verzeichnis /var/log/journal muss vorhanden sein; systemd-journald loggt andernfalls nur temporär.

  2. Journal abfragen mit journalctl
    • Alle Journalmeldungen anzeigen
    journalclt
    
    • gleich ans Ende springen
    journalctl -e
    
    • Datei verfolgen mit allen Metadaten und Catalog-Meldungen
    journalctl -f -a -x
    
    • Meldungen eines bestimmten Dienstes anzeigen
    journalctl _SYSTEMD_UNIT=ssh.service
    journalctl -u ssh.service
    journalctl /usr/sbin/sshd
    
    • Kernel Meldungen (dmesg)
    journalctl -k
    
    • alle Felder aufschlüsseln
    journalctl -o verbose
    journalctl -o json-pretty
    

    (alle Felder, die mit '_' beginnen, sind interne Felder und werden intern vom journald gesetzt und nicht vom Client. Somit sind sie nicht leicht manipulierbar.)

    • Meldungen seit dem letztem Boot
    journalctl -b
    
    • in einem bestimmten Zeitraum
    journalctl --since "2026-01-10" --until "2026-01-24 12:00"
    
    • ab einem bestimmten Level
    journalctl -p 4
    journalctl -p warning
    
    • Ins Journal schreiben
    ls | systemd-cat
    
    • Größe der Journal-Datenbank beschränken: in der Datei /etc/systemd/journald.conf:
    SystemMaxUse=100M
    SystemKeepFree=1G
    

4.15.2 Journal-Plattenverbrauch

  • Dateisystemverbrauch des Systemd-Journals abfragen
# journalctl --disk-usage
Archived and active journals take up 4.0G in the file system.
  • Journal-Datenbank verkleinern
# journalctl --vacuum-size=500M
# journalctl --disk-usage
Archived and active journals take up 488.1M in the file system.
  1. Dokumentation

5 Namespaces mit systemd-nspawn Refresh

5.1 Namespaces

  • Dieses Kapitel zeigt Namespaces, eine Standard-Technologie des Linux-Kernel
  • Namepspaces 'virtualisieren' die Sicht auf Ressourcen des Linux-Kernels
  • Programme wie Docker, Podman, Chrome, systemd-nspawn, LXC/LXD, Firejail etc. benutzen die Namespaces im Linux-Kernel
  • Verfügbare Namespaces in Linux
Namespace Konstante Isolation
Cgroup CLONE_NEWCGROUP Cgroup root Verzeichnis (Ressourcen wie CPU/RAM)
IPC CLONE_NEWIPC System V IPC, POSIX message queues
Network CLONE_NEWNET Network devices, stacks, ports, Firewall etc.
Mount CLONE_NEWNS Mount points (Dateisysteme)
PID CLONE_NEWPID Process IDs
User CLONE_NEWUSER Benutzer und Gruppen IDs
UTS CLONE_NEWUTS Hostname und NIS Domain Name

5.2 Container/Namespace mit Systemd

  • Jedes Linux mit Systemd bringt mächtige Container-Verwaltungs-Werkzeuge mit
  • systemd-nspawn arbeitet neben Image-Dateien für Container auch mit installieren Linux-Root-Dateien in einem beliebigen Verzeichnis auf dem Container-Host-System
    • Damit ist es sehr einfach, ein Container-System aufzusetzen und Dateien zwischen dem Container-Host und dem Linux im Container auszutauschen

5.2.1 Container Linux manuell installieren

  • In diesem Kapitel werden wir das bestehende Rocky-Linux des Host-Systems der virtuellen Maschine in ein Verzeichnis kopieren und dort innerhalb eines Namespaces mittels systemd-nspawn starten
  • Die Systemd-Container Befehle installieren
    # dnf install systemd-container
    
  • Anlegen des Verzeichnis
    # mkdir -p /srv/container/namespace1
    
  • Root-Dateisystem des Host-Linux anschauen
    # ls -l /
    
  • Kopieren aller Daten aus dem Host-Dateisystem in das neue Verzeichnis. Die Pseudo-Dateisysteme /dev, /proc, /tmp und /run lassen wir aus, diese werden von systemd-nspawn später automatisch mit den korrekten Daten gefüllt. Einige Verzeichnisse wie /afs oder /boot werden im Namespace nicht benötigt.
# cp --reflink -a /bin /etc /home /lib /lib64 /root /sbin /usr /var /srv/container/namespace1/
  • Frage: was machen die Parameter -a und --reflink?
  • Die leeren Verzeichnisse für die Mounts der Pseudo-Dateisysteme erstellen
    # cd /srv/container/namespace1
    # mkdir dev proc run tmp
    # cd /root
    
  • Die Machine-ID Datei im Namespace löschen, damit nicht beide Systeme (Host und Namespace) die gleiche Machine-ID besitzen. Beim Starten des Namespace wird diese Datei neu erstellt
    # rm /srv/container/namespace1/etc/machine-id
    
  • In den Namespace wechseln, um dort änderungen druchzuführen ohne das System im Namespace zu starten. Es wird eine Shell gestartet.
    # systemd-nspawn -D /srv/container/namespace1
    
  • Zeige die laufenden Prozesse im Namespace mittels ps und top an
  • Namespace verlassen durch beenden der Shell
    # exit
    
  • Das Linux im Namespace starten (booten)
    # systemd-nspawn -bD /srv/container/namespace1
    
  1. Übung
    • Starte das Linux-System im Namespace
    • Melde Dich am System an (warum funktionieren Benutzername und Passwort?)
    • Vergleiche den Inhalte vom /etc/machine-id auf dem Host und im Namespace
    • Vergleiche den Netzwerkstatus innerhalb des Namespace mit dem Host
    • Zeige die Prozesse im Namespace an
    • Installiere den NGINX-Webserver im Namespace mit dnf install nginx
    • Benutze systemctl um den NGINX Webserver anzuschalten und zu starten
    • Konfiguriere den Webserver so um, dieser auf den Port 8080 horcht (Datei /etc/nginx/nginx.conf)
    • Restarte den NGINX-Webserver, so das dieser die neue Konfiguration übernimmt
    • Installiere das Programm lsof (List open files) dnf install lsof
    • Teste mit lsof -i das der NGINX nun auf Port 8080 horcht
    • Versuche mit einem Web-Browser den Web-Server im Namespace zu erreichen: http://selinuxNNN.linux-sicherheit.org:8080
    • Gibt es Probleme? Woran können diese liegen? SELinux? Firewall? Namespace-Netzwerk?

5.2.2 Übungen:

  • Während der Namespace gestartet ist
    • Eine weitere Root-Shell auf der virtuelle Maschine öffnen und von dort die Prozesse auf dem Host anschauen: ist der nginx Prozess im Namespace sichtbar?
    • Im Namespace das Programm stress-ng installieren und mit einem CPU-Worker starten: stress-ng -C 1
    • Vom Host aus die Auslastung beobachten mit systemd-cgtop
    • Vom Host aus die Controll-Group Strukturen mit systemd-cgls anschauen
    • Überwache die Systemauslastung auf dem Host mit top. Der Prozess stress-ng sollte knapp 100% der CPU benutzen
    • Finde den Namen des scope des gestarteten Namespaces mit systemd-cgls
    • Beschränke die CPUQuota des Namespace-Scope auf dem Host mit dem Befehl systemctl set-property ... und der Einstellung CPUQuota=20%
    • Der Prozess stress-ng sollten dann nur noch 20% der CPU verbrauchen

5.2.3 Namespace stoppen

  • Ein in einem Namespace gestartetes Linux-System kann mit dem Befehl poweroff gestoppt, und mit dem Befehl reboot neu gestartet werden
    • Vor der Eingabe dieser Befehle sollte geprüft werden, das diese im Namespace und nicht auf dem Host eingegeben werden

5.2.4 Netzwerkanpassungen eines Namespace

  • Container mit privatem Netzwerk-Namespace starten (keine Verbindung zum Host-Netzwerk):
systemd-nspawn -bD  /srv/container/namepspace1 --private-network
  • Container mit privatem Netzwerk-Namespace starten und eine virtuelle Netzwerkverbindung (veth) zwischen dem Host und dem Namespace herstellen
systemd-nspawn -bD  /srv/container/namepspace1 --network-veth
  1. Übung
    • starte den Namespace mit --network-veth
    • finde die virtuellen Netzwerkschnittstellen in Namespace und auf dem Host
    • Konfiguriere beide Netzwerkschnittstellen mit je einer IP-Adresse aus dem gleichen (privatem) IP-Adress-Subnetz. Benutze das ip Kommando.
    • Teste die Netzwerkverbindung zwischen Host und Namespace

5.3 Machinectl

  • Systemd-Befehle um einen Container vom Host aus zu kontrollieren
machinectl list
machinectl status namepspace1
machinectl poweroff namespace1
machinectl terminate namespace1
machinectl kill namespace1
  • Eine Shell vom Host innerhalb eines gestarteten Namespace erstellen
    # machinectl shell namespace1
    

5.4 nsenter

  • Per nsenter können wir uns mit jedem Linux-Namespace verbinden. Dabei kann mit Parametern detailiert bestimmt werden, welche Namespaces für den gestarteten Prozess (hier eine Shell) aktiv werden sollen
    • So kann man z.B.
  • Per nsenter mit dem Container verbinden (es wird eine Prozess-ID eines Container-Prozesses benötigt!)
nsenter -m -u -i -n -p -t <pid-im-container> /bin/bash
  • Der Befehl machinectl status namespace1 zeigt die Prozesshierarchie innerhalb des Namespaces

6 SELinux

6.1 SELinux Dokumentation

  • SELinux-Module werden mit (automatisch generierten) man-Pages ausgeliefert
  • Diese Manpages sind auf einem Red Hat/CentOS-System nicht standardmäßig installiert
  • Sie können aus den SELinux-Richtlinienquellen hinzugefügt werden
    # dnf install -y selinux-policy-devel
    # sepolicy manpage -a -p /usr/share/man/man8
    
  • Während das SELinux-Modul bind genannt wird, heißt die Manpage named_selinux.
    • Diese Manpage dokumentiert die named process types, die neben BIND 9 auch für den Unbound-Resolver verwendet werden:
      # man named_selinux
      

6.2 Audit Subsystem

6.2.1 Beispiele für Audit-Abfragen

  • Alle Audit-Einträge zum Thema "sudo" zeigen
    ausearch -i -x sudo
    
  • Report über fehlgeschlagende Anmeldeversuche
    ausearch -m USER_AUTH,USER_ACCT --success no
    
  • Alle Audit-Meldungen für Benutzer UID 1000
    ausearch -ua 1000 -i
    
  • Fehlgeschlagende Syscalls seit gestern
    ausearch --start yesterday --end now -m SYSCALL -sv no -i
    

6.3 SELinux erkunden

  • SELinux Hilfspakete installieren
dnf install policycoreutils setools libselinux-utils selinux-policy-doc setools-console
dnf install policycoreutils-python3 selinux-policy-devel policycoreutils-newrole

6.3.1 SELinux Label auf Dateien

  • SELinux Label (Context) auf Dateien anzeigen
    ls -lZ <pfad>
    
  • Welche Dateien/Verzeichnisse unter /etc sind vom SELinux Typ system_conf_t?
  • Welchen SELinux Type haben neue Dateien im Heimverzeichnis des Benutzers user (ggf. eine Datei neu anlegen)?
  • Erstelle eine sortierte Liste der SELinux Datei-Typen (3tes Feld im SELinux Label user:role:type) im Verzeichnis /usr/sbin.

6.3.2 SELinux Label auf Prozessen

  • SELinux Label auf Prozessen anzeigen
    ps -auxZ
    
  1. Aufgabe: Label auf Prozessen
    • Wieviel verschiendene SELinux Benutzer, Rollen und Typen gibt es bei den laufenden Prozessen im RedHat 9 System?

6.3.3 SELinux Label auf dem aktuellen Benutzer anzeigen

# id -Z

6.3.4 SELinux Status abfragen

  • Allgemeinen SELinux Status abfragen
sestatus
  • Detaillierten SELinux Status abfragen
sestatus -v
  • SELinux "enforcement" Status anzeigen
getenforce
  • Verfügbare SELinux Module auflisten
semodule -l | less
  • Die kompilierte (binäre) Richtlinien (Policy) Datei
ls -lh /etc/selinux/targeted/policy/
  • Statistiken über den Access-Vector-Cache (AVC)
# avcstat
   lookups       hits        misses     allocs   reclaims      frees
   797921406     797847473   73933      73933    71040         73429

6.4 SELinux Funktions-Beispiel

  • SELinux in den enforcing Modus schalten
setenforce 1
  • Apache Webserver installieren und starten
dnf install httpd
systemctl enable --now httpd
  • Kleine Webseite mit einem Editor anlegen ($EDITOR durch vi, vim, nano, emacs etc ersetzen)
$EDITOR /var/www/html/index.html
  • Inhalt der HTML-Datei /var/www/html/index.html (Vorschlag)
<html>
<body>
<h1>Apache Webserver</h1>
</body>
</html>
  • SELinux Security Context auf der Datei anzeigen
ls -lZ /var/www/html/index.html
  • Port 80 in der Firewall erlauben
# firewall-cmd --zone=public --add-service=http --permanent
# firewall-cmd --reload
  • Die Webseite sollte nun unter Port 80 (http, nicht https) mittels eines Webbrowsers abrufbar sein

6.4.1 Ein SELinux-Problem für den Apache Webserver erzeugen

  • Eine Datei index.html Datei im Heim-Verzeichnis des Benutzers root erstellen und in das Apache-WWW-Verzeichnis verschieben (nicht kopieren):
    rm /var/www/html/index.html
    $EDITOR /root/index.html
    mv /root/index.html /var/www/html/index.html
    ls -lZ /var/www/html/index.html
    
  • Apache sollte nun nicht mehr in der Lage sein, die HTML-Datei auszuliefern (Default-Apache 2 Webseite erscheint)
  • SELinux LSM-Meldungen im Audit-Log zum Prozess httpd anzeigen (Modul avc = Access Vector Cache)
    ausearch -m avc -ts recent -c httpd -i
    
    • SELinux Sicherheits-Kontext der Datei prüfen
    matchpathcon -V /var/www/html/index.html
    
    • Es wird angezeigt das der SELinux Sicherheits-Kontext der Datei nicht korrekt ist. SELinux blockiert daher die Zugriffe vom Apache Webserver auf diese Datei

6.4.2 Das SELinux Problem mit dem Apache Webserver lösen

  • SELinux Security Context für Apache-Dateien anzeigen
    sesearch --allow --source httpd_t --target httpd_sys_content_t --class file
    
  • SELinux Sicherheits-Kontext anpassen (manuell mit dem Befehl chcon (Change Context)
    chcon --type httpd_sys_content_t /var/www/html/index.html
    
  • (Alternativ) SELinux Sicherheits-Kontext aus der SELinux Policy angleichen
    restorecon -v /var/www/html/index.html
    

6.5 SELinux - Policy Development

  • Für dieses Kapitel arbeiten wir auf dem VM Maschinen
  • Andere Web-Server (Apache/NGINX etc) auf der VM stoppen
  • Für die Dauer dieser Übung die Firewall auf der VM deaktivieren
systemctl stop firewalld
systemctl stop httpd

6.5.1 SELinux Policy erstellen

  • In diesem Kapitel werden wir eine SELinux Richtlinie (Policy) für einen Dienst entwickeln, welcher bisher noch nicht durch SELinux geschützt ist
    • Dies kann in der Praxis für Software notwendig werden, welche von externen Entwicklern geliefert wurde oder im eigenen Hause entwickelt wird
    • Unsere Beispiel-Anwendung ist ein sehr einfacher Webserver
  • Für die Entwicklung neuer SELinux Richtlinien benötigen wir die Pakete für die SELinux Policy-Entwicklung (diese sind im Kurs ggf. schon installiert)
dnf install policycoreutils-python3 selinux-policy-devel
  • Nach der Installation dieser Pakete befinden sie die SELinux Policy Quelldateien (der von Red Hat und der Community erstellen Richtlinien) im Verzeichnis /usr/share/selinux/devel/
    ls -l /usr/share/selinux/devel/
    ls -l /usr/share/selinux/devel/include/contrib/
    

6.5.2 Ein einfacher Web-Server

  • Im Kapitel "Vorbereitung der Lab-Umgebung" haben wir den Dienst simple-server erstellt.
  • Wir starten den Dienst simple-server auf dem Host
  • Wenn wir uns die SELinux Label des Dienstes anzeigen lassen sehen wir das dieses Dienst unconfined ist, also nicht durch SELinux abgesichert
ps -eZ | grep simple
ls -lZ /usr/local/bin/simple-server

6.5.3 Initiales SELinux Policy Modul

  • Wir erstellen ein neues Verzeichnis fuer das neue SELinux Policy Modul
mkdir ~/selinux-src
cd ~/selinux-src
  • Der Befehl sepolicy generate erzeugt eine Vorlage für ein SELinux Policy Modul
sepolicy generate -n simple-server --init /usr/local/bin/simple-server
  • Es werden drei SELinux Policy Quelldateien erstell
    • simple-server.te - Type Enforcement Quellcode - auf welche Datei-Typen darf der Prozess zugreifen
    • simple-server.fc - File Context Quellcode - welche Datei-Typen werden benutzt (und in welchen Pfaden liegen diese)
    • simple-server.if - Interface Quellcode - Definiert die Übergänge zwischen den Dateisystem und Prozess Typen, und definiert die Regeln für die Richtlinien
    • simple-server_selinux.spec - Quelldatei für ein RPM Paket
    • simple-server.sh - Shell Skript zum bauen des RPM Pakets des SELinux Policy Moduls
  • Diese Dateien können wir uns anschauen
  • Die Policy ist im permissive Modus!
less simple-server.te
less simple-server.fc
less simple-server.if
  • Wir testen diese SELinux Policy indem wir diese übersetzen und ein RPM-Paket erstellen. Das Paket rpm-build wird benötigt um ein RPM-Paket zu bauen
dnf -y install rpm-build
sh simple-server.sh
ls -l
  • Die neue SELinux Policy befindet sich in der Datei simple-server.pp. Dieses neue SELinux Policy-Modul kann nun geladen und aktiviert werden
semodule -i simple-server.pp
  • Die neue Policy wirkt sich nicht auf schon gestartete Prozesse aus. Daher stoppen wir den vorher gestarteten simple-server Prozess
systemctl stop simple-server
  • Den Simple-Server starten
systemctl start simple-server
  • Nun den Dienst benutzen (Mit dem Web-Browser auf Port 8080 zugreifen). Das SELinux Modul ist noch im Permissive Mode, Verstösse gegen die Policy werden im Audit-Log protokolliert
ausearch -m avc -ts recent -c simple-server
  • Auch das Systemd-Journal liefert Fehlermeldungen über SELinux-Troubleshoot Modul setroubleshoot
journalctl | grep setroubleshoot
  • Erklärungen zu den Policy-Fehlermeldungen ausgeben
ausearch -m avc -ts today -c simple-server | audit2why  | less
  • Mittels des Programms audit2allow lassen sich Policy-Regeln aus den Audit-Meldungen erstellen. Diese Regeln sind selten 100% korrekt und müssen oft nachbearbeitet werden, helfen aber enorm bei der Erstellung eines Regelwerkes
    • Der Befehl sepolgen-ifgen erzeugt aus den SLinux Interface-Dateien des Systems Hilfdateien für die Erstellung der Policy-Dateien
    • Der Befehl audit2allow -R gibt die SELinux Policy-Regeln auf dem Terminal aus. Diese bauen wir per copy-n-paste in die Type-Enforcement-Quelldatei simple-server.te ein. Dabei muss auf die korrekte Reihenfolge der Abschnitte geachtet werden (require Block unter Deklarationen, allow Ausdrücke darunter):
sepolgen-ifgen -v
ausearch -m avc -ts today -c simple-server | audit2allow -R
require {
        type simple-server_t;
        class tcp_socket { bind create setopt accept listen };
}

#============= simple-server_t ==============
allow simple-server_t self:tcp_socket { bind create setopt accept listen };
corenet_tcp_bind_generic_node(simple-server_t)
corenet_tcp_bind_http_cache_port(simple-server_t)
  • Neue Policy-Regeln in die Policy einfügen, Modul entfernen, neu kompilieren und dann neu laden
semodule -r simple-server
sh ./simple-server.sh
semodule -i simple-server.pp
systemctl restart simple-server
  • Die Anwendung benutzen und testen, danach wieder das Audit-Log auf SELinux Fehler des simple-server Prozesses prüfen. GGf. neue Regeln erstellen und Modul und Programm neu laden
  • Diese Schritte wiederholen bis keine SELinux Meldungen mehr im Audit-Log auftauchen
    ausearch -m avc -ts recent -c httpd -i
    <no matches>
    
  • Die Anwendung ggf. für eine gewisse Zeit in Produktion im permissive Modus betreiben und auf SELinux Fehler prüfen
  • Treten keine Fehler mehr auf, dann die Zeile permissive simple-server_t; in der Type-Enforcement Datei auskommentieren und das Modul im enforcing Modus betreiben
  • Schalten wir nun das simple-server Modul in den enforcing Modus, werden wir feststellen das das Programm doch nicht wie gewünscht funktioniert
  • Ein Trace des laufenden Programms mittels strace oder eBPF oder bpftrace zeigt das die Syscalls shutdown und write fehlschlagen. Diese werden von SELinux unterbunden, aber nicht an das Audit-Subsystem gemeldet.
  • Aufgabe: Trage die Syscalls shutdown und write in die Policy ein, übersetze die Policy und teste erneut

6.5.4 Die "DoNotAudit" Regeln ausschalten

  • Entwickler von SELinux-Policies können bestimmte Regeln vom Auditing ausschliessen
    • Um übermässiges Logging im Audit zu vermeiden
    • Verstösse gegen SELinux-Regeln, welche mit donotaudit markiert sind, werden nicht im Audit-Log vermerkt
    • Bei der Entwicklung von neuen SELinux Policies kann dies stören, denn hier möchte man im Audit-Log ein möglichst vollständiges Bild aller Verstösse bekommen
    • Die donotaudit Regeln in der SELinux-Richtline ausschalten
      # semodule -DB
      
    • Um die donotaudit Regel wieder zu aktivieren
      # semodule -B
      

6.5.5 Eine Änderung an einer SELinux Policy

  • Unser "Simple-Server" lernt das Logging in eine Datei. In der Quelldatei-Box sehen wir die Änderungen an dem Quellcode des simple-server.c Programms:
--- simple-server1.c    2022-10-25 09:22:06.684216579 +0000
+++ simple-server2.c    2022-10-26 08:53:33.319698944 +0000
@@ -19,6 +19,13 @@
  int main()
  {
    int one = 1, client_fd;
+   FILE *f = fopen("/var/log/simple-server.log", "a");
+   if (f == NULL)
+   {
+       printf("Error opening file!\n");
+       exit(1);
+   }
+
    struct sockaddr_in svr_addr, cli_addr;
    socklen_t sin_len = sizeof(cli_addr);

@@ -40,7 +47,8 @@
    listen(sock, 5);
    while (1) {
      client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
-     printf("got connection\n");
+     fputs("got connection\n",f);
+     fflush(f);

      if (client_fd == -1) {
        perror("Can't accept");
  • Das gepatchte Programm. Dieses Programm schreibt nun ein einfaches Log in die Datei /var/log/simple-server.log. Den Nachfolgenden Quellcode in die Datei simple-server2.c speichern
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <err.h>

char response[] = "HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=UTF-8\r\n\r\n"
"<!DOCTYPE html><html><head><title>Bye-bye baby bye-bye</title>"
"<style>body { background-color: #111 }"
"h1 { font-size:4cm; text-align: center; color: black;"
" text-shadow: 0 0 2mm red}</style></head>"
  "<body><h1>Goodbye, world!</h1></body></html>\r\n";

int main()
{
  int one = 1, client_fd;
  FILE *f = fopen("/var/log/simple-server.log", "a");
  if (f == NULL)
  {
      printf("Error opening file!\n");
      exit(1);
  }

  struct sockaddr_in svr_addr, cli_addr;
  socklen_t sin_len = sizeof(cli_addr);

  int sock = socket(AF_INET, SOCK_STREAM, 0);
  if (sock < 0)
    err(1, "can't open socket");

  setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int));
  int port = 8080;
  svr_addr.sin_family = AF_INET;
  svr_addr.sin_addr.s_addr = INADDR_ANY;
  svr_addr.sin_port = htons(port);

  if (bind(sock, (struct sockaddr *) &svr_addr, sizeof(svr_addr)) == -1) {
    close(sock);
    err(1, "Can't bind");
  }

  listen(sock, 5);
  while (1) {
    client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
    fprintf(f,"got connection\n");
    fflush(f);

    if (client_fd == -1) {
      perror("Can't accept");
      continue;
    }

    write(client_fd, response, sizeof(response) - 1); /*-1:'\0'*/
    close(client_fd);
  }
}
  • In der SELinux-Policy-Quelldatei simple-server.fc definieren wir den neuen Datei-Kontext var_log_t für die Log-Datei
/var/log/simple-server.log       --             gen_context(system_u:object_r:var_log_t,s0)
  • Die SELinux Type-Enforcment Datei für simple-server auf Permissive stellen und das SELinux-Modul neu übersetzen und laden
  • Den neuen Server-Dienst übersetzen, den alten simple-server Prozess stoppen, die neue Programm-Datei nach /usr/local/bin kopieren, das SELinux Label anpassen und den Dienst neu starten
gcc -o simple-server simple-server2.c
systemctl stop simple-server
cp simple-server /usr/local/bin
restorecon -R -v /usr/local/bin/simple-server
systemctl start simple-server
  • Per Web-Browser die Webseite auf http://selinuxNNN.linux-sicherheit.org:8080/ aufrufen.
  • Neue SELinux Audit-Meldungen tauchen auf
# ausearch -m avc -ts recent -c simple-server
----
time->Wed Aug 24 21:17:34 2016
type=SYSCALL msg=audit(1472073454.717:1390): arch=c000003e syscall=2 success=yes exit=3 a0=400ae2 a1=441 a2=1b6 a3=21000 items=0 ppid=1 pid=7869 auid=4294967295 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=(none) ses=4294967295 comm="simple-server" exe="/usr/local/bin/simple-server" subj=system_u:system_r:simple-server_t:s0 key=(null)
type=AVC msg=audit(1472073454.717:1390): avc:  denied  { open } for  pid=7869 comm="simple-server" path="/var/log/simple-server.log" dev="vda1" ino=268256 scontext=system_u:system_r:simple-server_t:s0 tcontext=system_u:object_r:var_log_t:s0 tclass=file
type=AVC msg=audit(1472073454.717:1390): avc:  denied  { create } for  pid=7869 comm="simple-server" name="simple-server.log" scontext=system_u:system_r:simple-server_t:s0 tcontext=system_u:object_r:var_log_t:s0 tclass=file
type=AVC msg=audit(1472073454.717:1390): avc:  denied  { add_name } for  pid=7869 comm="simple-server" name="simple-server.log" scontext=system_u:system_r:simple-server_t:s0 tcontext=system_u:object_r:var_log_t:s0 tclass=dir
type=AVC msg=audit(1472073454.717:1390): avc:  denied  { write } for  pid=7869 comm="simple-server" name="log" dev="vda1" ino=258603 scontext=system_u:system_r:simple-server_t:s0 tcontext=system_u:object_r:var_log_t:s0 tclass=dir
  • Die Erweiterungen zur SELinux Policy ausgeben, prüfen und in die Type-Enforcement-Datei simple-server.te einfügen:
 # ausearch -m avc -ts recent -c simple-server | audit2allow -R


require {
        type simple-server_t;
}

#============= simple-server_t ==============
auth_log_filetrans_login_records(simple-server_t)
logging_manage_generic_logs(simple-server_t)
  • simple-server SELinux Modul entfernen, Policy neu übersetzen, Modul neu laden, testen
  • Ggf. fehlen die Regeln um Log-Dateien anlegen und schreiben zu dürfen. Wenn dies der Fall ist, die donotaudit Funktion in der SELinux-Policy deaktivieren
    • Ein Blick in die bestehenden SELinux Policy Quelldateien von ähnlichen Programmen kann (auch) helfen
  • Wir fügen der Type-Enforcement-Quelldatei den Typ var_log_t und die Klassen file (Syscalls create, open und write) und dir (Verzeichnis mit den Syscalls write und add_name) hinzu
require {
        type simple-server_t;
        type var_log_t;
        class tcp_socket { bind create setopt accept listen shutdown write };
        class file { create open write };
        class dir { write add_name };
}
  • Wir fügen der Type-Enforcement-Quelldatei die Regeln für den Zugriff auf Dateien und Verzeichnisse im /var/log Verzeichnisbaum hinzu:
allow simple-server_t var_log_t:file { create open write };
allow simple-server_t var_log_t:dir { write add_name };
  • Das Makro logging_rw_generic_log_dirs erlaubt das Schreiben von Log-Dateien
    logging_rw_generic_log_dirs(simple-server_t)
    
  • Solange die Policy anpassen, bis keine Permission-Meldungen im Audit-Log erscheinen
  • Prüfen, das die Log-Datei korrekt erstellt wird

6.6 Hilfsmittel zur Erstellung von SELinux-Policy Regelwerken

  • strace - Kann die benutzten Systemcalls eines laufenden Prozesses ausgeben
  • autrace - Kann die benutzten Systemcalls eines Prozessaufrufs ausgeben
  • nm - kann die Aufrufe in die C-Bibliothek (GLIBC) ausgeben:
# nm /usr/local/bin/simple-server | grep @GLIBC
                U accept@GLIBC_2.2.5
                U bind@GLIBC_2.2.5
                U close@GLIBC_2.2.5
                U err@GLIBC_2.2.5
                U htons@GLIBC_2.2.5
                U __libc_start_main@GLIBC_2.34
                U listen@GLIBC_2.2.5
                U perror@GLIBC_2.2.5
                U puts@GLIBC_2.2.5
                U setsockopt@GLIBC_2.2.5
                U socket@GLIBC_2.2.5
                U write@GLIBC_2.2.5
  • eBPF und bpftrace sind sehr gute Hilfsmittel, um Prozesse und auch das Verhalten von Prozessen unter SELinux zu analysieren

6.6.1 SELinux Module ausschalten

  • SELinux Module können selektiv deaktiviert/aktiviert werden
  • Um nur das BIND 9 SELinux-Modul zu deaktivieren
    # semodule -d bind
    

6.6.2 SEModule anschalten

  • Um das BIND 9 SELinux-Modul zu aktivieren
    # semodule -ve bind
    Attempting to enable module 'bind':
    Ok: return value of 0.
    Committing changes:
    Ok: transaction number 6.
    

6.7 SELinux Fehlkonfigurationen finden

6.7.1 Reports des Linux Audit Subsystem

  • Verstöße gegen SELinux-Richtlinien werden mit dem Linux Audit Subsystem protokolliert
  • Mit dem Kommando ausearch können Sie die Richtlinienverletzungen eines bestimmten Prozesses auflisten
    • -m avc listet LSM-Richtlinienverstöße auf
    • -x /usr/sbin/named filtert nach Verstößen dieses Prozesses
    • -i (interpretieren) gibt die Daten in lesbarer Form aus

6.7.2 Nicht übereinstimmende Dateitypkennzeichnung

  • Das SELinux-System verweigert Prozessen den Zugriff auf Zonen- oder Konfigurationsdateien, wenn die Dateilabel nicht korrekt sind
  • Gründe für falsche oder fehlende Dateilabel
    • Das Linux-System wurde mit deaktiviertem SELinux betrieben
    • Die Dateien befinden sich in einem nicht standardmäßigen Verzeichnis (z.B. nicht in /etc oder /var/named)
    • Die Dateien wurden in einem nicht standardmäßigen Verzeichnis erstellt und dann in das richtige Verzeichnis verschoben. Die Datei-Bezeichnungen werden bei der Erstellung einer Datei zugewiesen und ändern sich nicht, wenn sie innerhalb des gleichen Dateisystem verschoben werden.

6.7.3 SELinux Label anzeigen (1/2)

  • SELinux Sicherheits-Kontext (Label) auf einer Datei anzeigen
# secon --file /etc/shadow
user: system_u
role: object_r
type: shadow_t
sensitivity: s0
clearance: s0
mls-range: s0

6.7.4 SELinux Label anzeigen (2/2)

  • Sicherheitskontext auf einem Prozess anzeigen
# secon --pid $(pgrep dbus)
user: system_u
role: system_r
type: system_dbusd_t
sensitivity: s0
clearance: s0:c0.c1023
mls-range: s0-s0:c0.c1023

6.7.5 Das richtige SELinux Label finden

  • Der Befehl matchpathcon (Match Path Context) meldet Dateien, bei denen das Dateilabel nicht mit der SELinux-Richtlinie übereinstimmen
    • Der Befehl meldet auch die erwarteten Dateilabel-Typen
    # matchpathcon -V /var/named/named.localhost
    /var/named/named.localhost has context system_u:object_r:etc_t:s0,
                               should be system_u:object_r:named_zonefile
    

6.7.6 Ändern des Dateilabels

  • Der Befehl chcon (change SELinux context) kann verwendet werden, um den Typ des Dateilabel zu ändern:
    # chcon --type named_cache_t /var/named/zonefile.db
    

6.7.7 Anwenden des korrekten Label aus der Richtlinie

  • Der Befehl restorecon passt das Label einer Datei so an, dass es mit dem von der SELinux-Richtlinie erwarteten Label übereinstimmt
    # restorecon -v /var/named/named.localhost
    Relabeled /var/named/named.localhost ...
           from system_u:object_r:etc_t:s0 ...
           to system_u:object_r:named_zone_t:s0
    

6.7.8 Anpassen des erwarteten Dateikontextes für eine einzelne Datei

  • Wenn Konfigurations- oder Datendateien an einem nicht standardmäßigen Speicherort gespeichert sind, sollte die SELinux-Richtlinie angepasst werden, um das richtige Kontextlabel zuzuordnen
  • Der Befehl semanage fcontext -a fügt einen Dateikontext Label zur SELinux-Richtlinie hinzu.

6.7.9 Anpassen des erwarteten Dateikontextes für eine einzelne Datei

  • Die Dateien werden nicht automatisch neu gekennzeichnet. Verwenden Sie restrorecon, um die Dateien neu zu kennzeichnen.
# semanage fcontext -a -t named_zone_t /srv/bind/zones/primary/example.com.db
# restorecon -vr /srv/bind/zones
Relabeled /srv/bind/zones/primary/example.com.db
        from unconfined_u:object_r:var_t:s0
        to unconfined_u:object_r:named_zone_t:s0

6.7.10 Rekursives Anpassen des Dateikontextes für alle Dateien und Verzeichnisse

  • Ein neuer SELinux-Dateikontext kann rekursiv zu einem Verzeichnis hinzugefügt werden
    • Alle neuen Dateien, die in den angegebenen Verzeichnissen erstellt werden, erhalten automatisch das richtige SELinux-Dateilabel
# semanage fcontext -a -t named_zone_t --ftype f "/srv/bind/zones(/.*)?"
# semanage fcontext -a -t named_zone_t --ftype d "/srv/bind/zones(/.*)?"
# semanage fcontext -a -t named_cont_t --ftype f "/srv/bind/conf(/.*)?"
# semanage fcontext -a -t named_conf_t --ftype d "/srv/bind/conf(/.*)?"

6.7.11 Dateikontext automatisch anpassen

  • Der Hintergrundprozess restorecond kann optional installiert und gestartet werden (Paket policycoreutils-restorecond), um Dateilabel von neu angelegten Dateien automatisch an die SELinux-Policy anzupassen
    • Je nach Einsatzbereich kann restorecond die Sicherheit beeinträchtigen, da SELinux-Label auf Dateien automatisch korrigiert werden
    • Die Datei /etc/selinux/restorecond.conf listed die Dateien und Verzeichnisse auf, welche von restorecond automatisch überwacht und berichtigt werden sollen

6.8 SELinux Richtlinien Konfigurieren

  • Viele SELinux Module bieten Konfigurations-Optionen an
  • Über SELinux Booleans (Schalter) können Funktionen von Richtlinien-Modulen an- bzw. ausgeschaltet werden
  • Eine Liste alle SELinux Boolean-Schalter kann durch semanage boolean -l abgerufen werden
    SELinux boolean                State  Default Description
    abrt_anon_write                (off  ,  off)  Allow ABRT to modify public files used ...
    abrt_handle_event              (off  ,  off)  Determine whether ABRT can run in the ...
    antivirus_can_scan_system      (off  ,  off)  Allow antivirus programs to read non ...
    antivirus_use_jit              (off  ,  off)  Determine whether antivirus programs ...
    
  • Der Befehl getsebool ist eine alternative Schnittstelle zu den SELinux Schaltern
# getsebool -a
abrt_anon_write --> off
abrt_handle_event --> off
[...]
named_tcp_bind_http_port --> off
named_write_master_zones --> on
[...]
  • Mit dem Befehl semanage boolean -l --locallist wird eine übersicht der lokalen Schalter-Anpassungen ausgegeben
    # semanage boolean -l --locallist
    SELinux boolean                State  Default Description
    named_write_master_zones       (on   ,   on)  Determine whether Bind can write [...]
    
  • Beispiel des Ausschalten eines SELinux Schalters
# semanage boolean --modify --off named_write_master_zones

6.8.1 Alternativ: setsebool

  • Als Alternative zu semanage boolean kann der Befehl setsebool verwendet werden
    setsebool named_write_master_zones off
    
  • Um eine Änderung dauerhaft (persistent) im System zu ändern (Reboot-Fest), muss der Schalter -P angegeben werden
    setsebool -P named_write_master_zones off
    

6.8.2 SELinux Schalter

  • Bei der Neu-Installation von Software auf einem SELinux System ist es sinnvoll sich mit den SELinux Schaltern für diese Software vertraut zu machen

6.9 SELinux Netzwerk-Ports

  • Die SELinux Policy erlaubt Anwendungen (oder SELinux Type-Label) die Benutzung bestimmter UDP/TCP Netzwerkports
    • Versucht die Anwendung, einen anderen Port zu öffnen, so wird dies durch SELinux unterbunden
    • Beispiele: Webserver, SSH-Server, DNS-Server auf Nicht-Standard-Ports
  • Der Befehl semanage port -l listet alle Port-Definitionen pro SELinux Type-Label auf
    • Diese Liste ist lang und umfasst alle Module, nicht nur die aktiven SELinux Module. Benutze grep um die Port-Konfiguration für einen SELinux-Typ zu sehen
# semanage port -l | grep ssh
ssh_port_t                     tcp      22
  • Um einen Netzwerk-Dienst auf einem nicht-standard Port unter SELinux betreiben zu können, muss dieser Port dem SELinux-Type hinzugefügt werden:
    # semanage port -a -t ssh_port_t -p tcp 4422
    # semanage port -l | grep ssh
    ssh_port_t                     tcp      22,4422
    

6.10 Durchsetzung der Richtlinie für Module ausschalten

  • Es ist möglich einzelne SELinux Module in einen permissive Modus zu versetzten
    • In diesem Modus wird die SELinux Policy für dieses Modul nicht mehr vom Kernel durchgesetzt
    • Verstösse gegen die Policy werden jedoch weiterhin im Audit-Log protokolliert
  • Ein Modul (hier httpd_t für Apache oder NGINX Webserver) in den permissive Modus setzen
semanage permissive -a httpd_t
  • Alle Module auflisten, welche im permissive Modus laufen
# semanage permissive -l
  • Permissive Modus von einem Modul entfernen
semanage permissive -d httpd_t
  • Permissive Modus von allen Modulen entfernen
semanage permissive -D

7 Fehlersuche

7.1 Systemd

7.1.1 Systemd-status

  • Fehlerhafte Dienste auflisten
    # systemctl list-units --state=failed
      UNIT             LOAD   ACTIVE SUB    DESCRIPTION
     * gssproxy.service loaded failed failed GSSAPI Proxy Daemon
    
     Legend: LOAD   > Reflects whether the unit definition was properly loaded.
             ACTIVE > The high-level unit activation state, i.e. generalization of SUB.
             SUB    > The low-level unit activation state, values depend on unit type.
    
  • Details zu einem Dienst auflisten
    # systemctl status gssproxy
    × gssproxy.service - GSSAPI Proxy Daemon
         Loaded: loaded (/usr/lib/systemd/system/gssproxy.service; disabled; preset: disabled)
        Drop-In: /usr/lib/systemd/system/service.d
                 └─10-timeout-abort.conf
         Active: failed (Result: signal) since Mon 2026-03-16 07:18:48 CET; 2min 5s ago
       Duration: 3d 23h 14min 24.878s
     Invocation: ab00596fa3ac4504b1272f3a0c4fc158
        Process: 1113 ExecStart=/usr/bin/gssproxy -i (code=killed, signal=KILL)
       Main PID: 1113 (code=killed, signal=KILL)
         Status: "Running, 2 service(s) configured"
       Mem peak: 4.1M
            CPU: 2.923s
    
    Mar 12 08:04:23 srv systemd[1]: Starting gssproxy.service - GSSAPI Proxy Daemon...
    Mar 12 08:04:23 srv systemd[1]: Started gssproxy.service - GSSAPI Proxy Daemon.
    Mar 16 07:18:48 srv systemd[1]: gssproxy.service: Main process exited, code=killed, status=9/KILL
    Mar 16 07:18:48 srv systemd[1]: gssproxy.service: Failed with result 'signal'.
    Mar 16 07:18:48 srv systemd[1]: gssproxy.service: Consumed 2.923s CPU time, 4.1M memory peak.
    

7.1.2 Journal

  • Log-Meldungen zu einem Dienst anschauen
       # journalctl -u gssproxy --since today
    Mar 16 07:18:48 srv systemd[1]: gssproxy.service: Main process exited, code=killed, status=9/KILL
    Mar 16 07:18:48 srv systemd[1]: gssproxy.service: Failed with result 'signal'.
    Mar 16 07:18:48 srv systemd[1]: gssproxy.service: Consumed 2.923s CPU time, 4.1M memory peak.
    Mar 16 07:23:18 srv systemd[1]: Starting gssproxy.service - GSSAPI Proxy Daemon...
    Mar 16 07:23:18 srv systemd[1]: Started gssproxy.service - GSSAPI Proxy Daemon.
    
  • Logmeldungen eines Dienstes verfolgen (wie tail -f /var/log/logdatei.log)
# journalctl -fu gssproxy
  • Im Journal nach einem Wort suchen
 # journalctl -u gssproxy --grep kill
-- Boot c2bdc564a8dc44d4b3ddce21527094b5 --
Mar 16 07:18:48 srv systemd[1]: gssproxy.service: Main process exited, code=killed, status=9/KILL

7.2 Namespace

7.2.1 Machinectl

  • Liste aller von machinectl unterstützten virtualisierten Systeme
# machinectl
  • Status eines Namespaces/Containers/virtuellen Maschine abfragen
    # machinectl status namespace1
    
  • Eine Shell in einem Namespace erstellen
    # machinectl shell namespace1
    
  • Alternativ, falls machinectl shell fehltschlägt: Per nsenter mit dem Container verbinden (es wird eine Prozess-ID eines Container-Prozesses benötigt!)
nsenter -m -u -i -n -p -t <pid-im-container> /bin/bash
  • Der Befehl machinectl status namespace1 zeigt die Prozesshierarchie innerhalb des Namespaces
  • Namespace herunterfahren
    # machinectl poweroff namespace1
    
  • Namespace hart beenden (kann zu Datenverlust führen!)
    # machinectl terminate namespace1
    
  • Prozesse des Namespace per kill Signal beenden
    # machinectl kill namespace1 # sendet "TERM" signal
    # machinectl --signal=kill kill namespace1 # sendet "KILL" signal
    
  • Journal innerhalb des Namespace anschauen
    # journalctl -M namespace1 ...
    
  • Systemctl von Host innerhalb eines Namespace ausführen
    # systemctl -M namespace1 ...
    

7.2.2 CGroups

  • Hierarchie der CGroups anzeigen
    # systemd-cgls
    
  • Cgroups eines Namespace anzeigen
    # systemd-cgls -M namespace1
    
  • Auslastung der CGroups anzeigen
    # systemd-cgtop
    
  • Auslastung der CGroups nur eines Namespace anzeigen
    # systemd-cgtop -M namespace1
    

7.2.3 ATOP

  • atop ist ein Anzeigeprogramm für Systemressourcen. atop kann die Pressure Stall Information des Linux-Kernel anzeigen (siehe https://andrestc.com/post/pressure-stall-information/)
  • atop zeigt Prozesse den jeweiligen (Prozess-)Namespaces zugeordnet (Spalte CID/POD)
  • atop zeigt mehr Daten an, je breiter das Terminalfenster ist
  • atop installieren (RedHat Linux)
    # dnf install epel-release
    # dnf install atop
    
  • Text-grafische Darstellung der Systemauslastung
    # atop -B
    
  1. Speicher
    • Speicherverbrauch-Kennzahlen anzeigen
    atop -m
    
  2. CPU / Scheduler
    • Prozess/Scheduler-Kennzahlen anzeigen
    atop -s
    
  3. Historische ATOP-Daten
    • atop kann Daten zur Systemauslastung in eine komprimierte binäre Log-Datei schreiben. Diese Datei kann später gelesen und analysiert werden. Einige Linux-Systeme bringen einen Systemd-Dienst mit, um atop als Dienst im Hintergrund zu starten
      # systemctl enable --now atop
      
    • Die Logdaten werden nach /var/log/atop/ geschrieben
    • atop Logs lesen (Tasten: t - nächste Daten, T - vorherige Daten, r - Anfang der Datei, Z - Ende der Datei, b - Zeitpunkt eingeben
      # atop -r /var/log/atop/atop_<datum>
      

7.3 SELinux

7.3.1 Audit-Subsystem

  • SELinux Policy-Verstösse auflisten
    # ausearch -m avc
    
  • SELinux Policy-Meldungen der letzten 10 Minuten (weitere Zeit-Modifikatoren: this-hour, boot, today, yesterday, this-week, week-ago, this-month, this-year)
    # ausearch -m avc -ts recent
    

7.3.2 Do-Not-Edit-Flag

  • Die donotaudit Regeln in der SELinux-Richtline ausschalten
    # semodule -DB
    
  • Um die donotaudit Regel wieder zu aktivieren
    # semodule -B
    

7.4 Dateisystem

# df -Th

7.4.1 BTRFS Dateisystem

  • BTRFS ist ein "copy-on-write" Dateisystem: kopierte Daten verbrauchen nach dem Kopieren erst einmal keinen Speicher auf dem Storage-Medium.
  • Erst nach einer änderung der Daten wird Speicherplatz für das Delta der Daten verbraucht. Daten auf einem BTRFS Dateisystem können transparent komprimiert werden, daher kann je nach Inhalt der Daten mehr der oder weniger Daten auf einem BTRFS Dateisystem gespeichert werden.
    • aus diesem Grund zeigen klassische Unix-Programme wie du (Disk Usage) oder df (disk free) auf 'copy-on-write' Dateisystemen nicht die korrekten Werte an und sollte nicht benutzt werden
    • Für 'copy-on-write' Dateisysteme gibt es spezialisierte Programme, welche die korrekten Daten anzeigen
  • Die BTRFS Programme installieren
# dnf install btrfs-progs
  • Anzeige der Subvolumes eines BTRFS Dateisystems
# btrfs subvol list /
ID 256 gen 1157782 top level 5 path home
ID 257 gen 1157785 top level 5 path root
ID 258 gen 1150812 top level 257 path var/lib/machines
ID 259 gen 1148976 top level 257 path swap
  • BTRFS "Disk-Free" Kommando
# btrfs filesys df /
Data, single: total=309.01GiB, used=296.94GiB
System, DUP: total=8.00MiB, used=64.00KiB
Metadata, DUP: total=4.00GiB, used=2.08GiB
GlobalReserve, single: total=512.00MiB, used=0.00B
  • BTRFS "Disk-Use" Kommando
# btrfs filesys du /home
     Total   Exclusive  Set shared  Filename
     0.00B       0.00B           -  /home/cas/.mozilla/extensions
     0.00B       0.00B           -  /home/cas/.mozilla/plugins
     0.00B       0.00B           -  /home/cas/.mozilla
   1.25MiB     1.25MiB           -  /home/cas/.cache/mesa_shader_cache/index
     0.00B       0.00B           -  /home/cas/.cache/mesa_shader_cache/34/4791756204dd6c84700e5bdf2a07b2269d87b7
     0.00B       0.00B           -  /home/cas/.cache/mesa_shader_cache/34
     0.00B       0.00B           -  /home/cas/.cache/mesa_shader_cache/e8/e4aab5a6deaa6b3197db45a8049e076dab45b7
   8.00KiB     8.00KiB           -  /home/cas/.cache/mesa_shader_cache/e8/60a3262ad25dd9c8b66cf5d1fe30d5c11f1cfe
   4.00KiB     4.00KiB           -  /home/cas/.cache/mesa_shader_cache/e8/ba73c5504d4da5403d49ff95525360c15e2fee
   [...]
  • BTRFS Statistiken eines Dateisystems
# btrfs filesys usage /home
Overall:
    Device size:		 952.28GiB
    Device allocated:		 317.02GiB
    Device unallocated:		 635.26GiB
    Device missing:		     0.00B
    Device slack:		     0.00B
    Used:			 301.10GiB
    Free (estimated):		 647.32GiB	(min: 329.69GiB)
    Free (statfs, df):		 647.32GiB
    Data ratio:			      1.00
    Metadata ratio:		      2.00
    Global reserve:		 512.00MiB	(used: 0.00B)
    Multiple profiles:		        no

Data,single: Size:309.01GiB, Used:296.95GiB (96.10%)
   /dev/nvme0n1p3	 309.01GiB

Metadata,DUP: Size:4.00GiB, Used:2.08GiB (51.89%)
   /dev/nvme0n1p3	   8.00GiB

System,DUP: Size:8.00MiB, Used:64.00KiB (0.78%)
   /dev/nvme0n1p3	  16.00MiB

Unallocated:
   /dev/nvme0n1p3	 635.26GiB