Debian-Linux Server Sicherheit Kursnotizen November 2019

1 Tag 1

1.1 Einführung

1.2 Zufallszahlen und Linux

  • starte top in einem anderen Terminal-Fenster, beobachte dort die CPU Benutzung des ssh-keygen Prozesses
  • erstelle einen 10000 bit ssh RSA Schlüssel (hier nur zur Demo von Zufallszahlen und haveged oder rngd)
cat /dev/random # Abbrechen
ssh-keygen -b 10000 -t rsa -f /tmp/test
  • Die Erstellung des Schlüssels dauert extrem lange, da /dev/random benutzt wird.
  • Rngd als Hardware-Zufallszahlengenerator installieren
  • Haveged erzeugt Zufallswerte aus der verteilung von Prozessen auf Prozessorkerne. Homepage http://www.issihosts.com/haveged/ Auch geeignet für virtuelle Umgebungen
  • Haveged installieren
apt install haveged
systemctl status haveged
  • Der Schlüssel sollte nach dem Start von haveged in ein paar Sekunden erstellt sein!

1.2.1 Durchsatz des Zufallszahlengenerators messen

  • rngd installieren
apt install rng-tools5
systemctl status rngd
  • aus /dev/random lesen und der Durchsatz per pv anzeigen lassen
apt install pv
dd if=/dev/random | pv > /dev/null
  • haveged an-/ausschalten (in einem anderen Terminal, die Datenrate im pv betrachten)
  • Dasselbe mit rngd
  • Beide zusammen und keines von Beiden
systemctl stop haveged rngd
systemctl start rngd
systemctl stop rngd
systemctl start haveged
systemctl start rngd

1.3 Aufgabe: Hash und HMAC

  • Lade die Datei https://notes.defaultroutes.de/LinuxSecurityEinfuehrung.pdf
  • Welcher SHA256 Hash-Wert ist korrekt?
    1. 0245bcddf700ddfb4fd37c58bbfc67e19b9aa8827d0242fc9beb40078925f191
    2. f8eee6980c49d8020b10449a0fec6544e266af09
    3. 22833704a424725d5725d977cc733244afe0e38a6e2a987a560c95aa39da2d01
    4. AEGHHA91288ABCCVA005612991ZZ612566189811
openssl dgst -sha256 < LinuxSecurityEinfuehrung.pdf
  • Der folgende Wert sind die HMACs für die PDF-Datei. Welcher PSK (pre-shared-secret) wurde verwendet:
HMAC-SHA256 =
 3989780c03e83f553dca485c38a4d6239de58affbe27f0d7b572be00a5e512ee
  • Auswahl der PSKs:
    1. 'LinuxHotel'
    2. 'EssenHorst'
    3. 'vogelsang'
openssl sha256 -hmac "<psk>" < LinuxSecurityEinfuehrung.pdf

1.4 Symmetrische Verschlüsselung mit OpenSSL

# AES
% echo "mein geheimer text" | openssl enc -e -aes256 -a
enter aes-256-cbc encryption password:
Verifying - enter aes-256-cbc encryption password:
<verschlüsselter text>

echo "<verschlüsselter text>" |  openssl enc -d -aes256 -a

1.5 Asymmetrische Verschlüsselung mit OpenSSL

# einen neuen privaten RSA Schlüssel erstellen
openssl genpkey -algorithm RSA -out key.pem

# den privaten Schlüssel mit AES 256bit verschlüsseln
openssl pkey -in key.pem -aes256 -out keyaes.pem
Enter PEM pass phrase:
Verifying - Enter PEM pass phrase:

# Unverschlüsselten privaten Schlüssel löschen
rm key.pem

# den öffentlichen Schlüssel erzeugen (kann bei RSA aus dem privaten
# Schlüssel erzeugt werden)
openssl pkey -in keyaes.pem -pubout -out pubkey.pem

# Daten mit dem öffentlichen Schlüssel verschlüsseln
echo "Hallo LinuxHotel" > plain.txt
openssl pkeyutl -in plain.txt -out cipher.txt -encrypt -pubin -inkey pubkey.pem

# Verschlüsselten Text als Hex und ASCII anzeigen
cat cipher.txt | od -x -a

# Daten mit dem privaten Schlüssel entschlüsseln
openssl pkeyutl -in cipher.txt -out plain2.txt -decrypt -inkey keyaes.pem
Enter pass phrase for keyaes.pem:

# entschlüsselten Text wieder anzeigen
cat plain2.txt
Hallo LinuxHotel

1.6 Software Downloads prüfen

1.6.1 Debian CD/DVD Download

  • Prüfen, ob eine Debian Installations-CD/DVD "original" ist, d.h. vom Debian-Release Team stammt:
  • CD-Rom ISO Image laden
wget https://debian.inf.tu-dresden.de/debian-cd/10.1.0/amd64/iso-cd/debian-10.1.0-amd64-netinst.iso
  • SHA256 Fingerabdruck der Datei errechnen
openssl dgst -sha256 < debian-10.1.0-amd64-netinst.iso
  • Datei mit den Hash-Prüfsummen laden
wget https://debian.inf.tu-dresden.de/debian-cd/10.1.0/amd64/iso-cd/SHA256SUMS
  • Hash der ISO-Datei mit dem Hash in der Prüfsummendatei vergleichen
  • Nun prüfen wir, ob die Datei mit den Hash-Prüfsummen original ist. Dabei wird die digitale Signatur (mittels GNU-PG) über die Datei mit den SHA256-Hashes geprüft. Wir laden die Signatur der Hash-Prüfsummen vom Download-Server:
wget https://debian.inf.tu-dresden.de/debian-cd/10.1.0/amd64/iso-cd/SHA256SUMS.sign
  • Releases der Debian-Distribution werden mit extra Schlüsseln des Debian-Release-Teams signiert. Wenn wir direkt versuchen, die Signatur auf der Datei zu prüfen, so bekommen wir einen Fehler, da wir den öffentlichen Schlüssel (ID DF9B9C49EAA9298432589D76DA87E80D6294BE9B) des Debian-Release-Teams nicht in unserem GPG-Schlüsselbund haben:
debian$ gpg --verify SHA256SUMS.sign SHA256SUMS
gpg: Signature made Sun Sep  8 17:52:41 2019 CEST
gpg:                using RSA key DF9B9C49EAA9298432589D76DA87E80D6294BE9B

gpg: Can't check signature: No public key
  • Um die Signaturen prüfen zu können, importieren wir den Debian CD Signatur-Schlüssel in den eigenen GPG-Schlüsselring.
# su -
# apt -y install dirmngr
# exit
# gpg  --keyserver keyring.debian.org --recv-keys DF9B9C49EAA9298432589D76DA87E80D6294BE9B
gpg: key DA87E80D6294BE9B: public key "Debian CD signing key <debian-cd@lists.debian.org>" imported
gpg: Total number processed: 1
gpg:               imported: 1
gpg --finger  DF9B9C49EAA9298432589D76DA87E80D6294BE9B
  • Signature auf der Prüfsummendatei prüfen
# gpg --verify SHA256SUMS.sign SHA256SUMS
gpg: Signature made Sun Sep  8 17:52:41 2019 CEST
gpg:                using RSA key DF9B9C49EAA9298432589D76DA87E80D6294BE9B
gpg: Good signature from "Debian CD signing key <debian-cd@lists.debian.org>" [unknown]
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: DF9B 9C49 EAA9 2984 3258  9D76 DA87 E80D 6294 BE9B
  • Um dem CD-ROM Image vertrauen zu können, sollten alle Prüfsummen und Signaturen stimmen.

1.6.2 Debian Release/Repository Schlüssel

  • Die Debian-GPG Schlüssel zum verifizieren von Software-Paketen liegen im Verzeichnis /etc/apt/trusted.gpg.d/

1.7 TMUX - terminal multiplexer

apt install tmux
tmux
Aktion Tastaturkombination
Neues Terminal CTRL+B C
nächstes Terminal CTRL+B N
voheriges Terminal CTRL+B P
Screen Nr. x [0…9] CTRL+B 4
Terminal horizontal teilen CTRL+B "
Terminal vertikal teilen CTRL+B %
zwischen geteilten Terminals wechseln CTRL+B <cursor>
zwischen geteilten Terminals wechseln CTRL+B O
Größe ändern CTRL+B CTRL+<cursor>
Zoomen CTRL+B z
Terminal schliessen CTRL+B x
Tmux abhängen (detach) CTRL+B d
Grafisch Fenster wechseln CTRL+B w
Grafisch Sitzung wechseln CTRL+B s
Tmux anhängen tmux attach
Tmux Kommandozeile CTRL+B :
Tastenkommandos in alle Fenster (Kommandozeile) set synchronize-panes
   

1.8 Sicherheit bei der Unix-Benutzeranmeldung

  • Die Sicherheit der Unix-Benutzeranmeldung hängt u.a. an der Sicherheit der gespeicherten Passwörter. Passwörter werden in einem Linux-System als SHA512-Hash gespeichert. Dabei wird das Passwort 5.000 mal mit dem SHA512 Algorithmus gehashed bevor es in der Datei /etc/shadow gespeichert wird oder gegen den Hash in dieser Datei geprüft wird.
  • 5.000 Runden SHA512 lassen sich heute mit grossen Rechnern "brute-force" berechnen. Die Default-Einstellung von Linux istso gewählt, das es auch auf sehr schwachen Rechnern (z.B. Heim-Routern) noch funktioniert. Moderne Systeme können eine grössere Anzahl SHA512 Runden für die Passwort-Sicherheit verwenden. Nachfolgend stellen wir die SHA512-Runden auf 1.000.000 (1 Million) ein.

1.8.1 Sicherheit der Benutzerpasswörter

  • In der Datei /etc/pam.d/common-password wird die Stärke der Benutzer-Passwörter angepasst. Eine Änderung in dieser Datei wirkt sich nur auf alle neu gesetzten Passwörter aus, alle schon vorher vergebenen Passwörter bleiben mit der vorherigen Einstellung bestehen. D.h. nach einer Änderung dieses Parameters sollten wichtige Passwörter neu vergeben werden.
[...]
password    [success=1 default=ignore]    pam_unix.so obscure sha512 rounds=1000000
[...]

1.8.2 Gruppenpasswörter

  • Die Sicherheit der Gruppenpasswörter werden in der Datei /etc/login.defs festgelegt. Wenn Gruppenpasswörter benutzt werden, wird empfolen diese Werte mit der PAM-Konfiguration gleich zu halten.
ENCRYPT_METHOD SHA512
SHA_CRYPT_MIN_ROUNDS 1000000
SHA_CRYPT_MAX_ROUNDS 1000000

1.8.3 Benutzerdatenbank

  • Die Benutzerinformationen für die Passwortanmeldung unter Unix/Linux werden in der Datei /etc/shadow gespeichert
  1. Felder der Datei /etc/shadow:
    • Benutzername
    • Password-Hash
    • Datum des letzen Passwort-Wechsel
    • Mindestlebensdauer des Passworts
    • Max-Lebensdauer des Passwort
    • Passwort-Ablauf Warn-Zeitraum
    • Zeitdauer der Passwort-Inaktivität (Benutzer kann sich nach Ablauf des Passworts noch einloggen)
    • Ablaufdatum des Passworts
    • Reserviertes Feld
  2. Passwort-Hash-Methoden
    ID Methode
    1 MD5
    2a Blowfish (nicht in der Standard glibc; wurde einigen Distributionen hinzugefügt)
    5 SHA-256 (seit glibc 2.7)
    6 SHA-512 (seit glibc 2.7)

1.9 chage

useradd -m -N fritz
passwd fritz
chage -l fritz
  • Account abgelaufen
chage -E 0 fritz
  • Account ist ab einem bestimmten Datum nicht mehr gültig
chage -E 2019-03-01 fritz
  • Ablaufzeit zurücksetzen
chage -E -1 fritz
  • Passwort darf frühstens nach 3 und muss spätestens nach 9 Tagen geändert werden
chage -m 3 -M 9 -d "1 day ago" fritz
passwd fritz
  • Login innerhalb der Warn-Periode
chage -d "8 days ago" -W 3 fritz
su - fritz
  • Login außerhalb der Warn-Periode aber innerhalb der Gültigkeit
chage -d "12 days ago" -I 10 fritz
su - fritz
  • Login außerhalb der Gültigkeit
chage -d "21 days ago" -I 10 fritz
su - fritz
  • Benutzer muss sein Passwort einmalig beim ersten Login ändern
chage -E -1 -I -1 -m 0 -M 99999 fritz # alles zurücksetzen
passwd fritz                          # Ein Standardpasswort setzen
chage -d 0 fritz                      # Änderung erzwingen

1.10 PAM -Pluggable Authentication Modules

cat /etc/pam.d/common-auth | grep -v "#"

auth    [success=1 default=ignore]      pam_unix.so nullok_secure
auth    requisite                       pam_deny.so
auth    required                        pam_permit.so
auth    optional                        pam_cap.so
  • PAM Dienst-Typen
    • account: Prüfung Berechtigung
    • auth: Authentifizierung
    • password: Passwort-Änderung
    • session: Sitzungsverwaltung
  • PAM Control
    • requisite = muss erfolgreich sein, sonst Kette beenden (notwendige Vorbedingung)
    • required = muss am Ende erfolgreich sein (notwendige Bedingung)
    • sufficient = bei Erfolg wird die Kette beendet (hinreichende Bedingung)
    • optional = Returncode wird nicht verwendet
  • Linux-PAM erweiterte Controls
    • Syntax: [return-value=action …]
    • Actions:
      • OK = Zugriff erlauben
      • ignore = Ignorieren
      • bad = Zugriff verweigern
      • die = Zugriff verweigern und Kette abschliessen
      • done = Zugriff erlauben und Kette abschliessen
      • reset = PAM Variablen zurücksetzen/löschen und weitermachen
      • <n> = die folgenden <n> PAM-Regeln überspringen

1.10.1 Aufgabe: pam_warn.so für Login installieren

  • Installiere das Modul pam_warn.so fuer die Facility auth in PAM-Dienst login
  • Schalte auf einen der Konsolen-Bildschirme des Laptops um "STRG+ALT+F2 … F8"
  • Melde dort den Benutzer nutzerXX an
  • Prüfe die Syslog/Journal Ausgabe

1.10.2 Lösung

  • in Datei /etc/pam.d/login
auth       required   pam_warn.so
  • in Datei und /etc/pam.d/common-session
session       required   pam_warn.so
  • per STRG+ALT+F2 auf eine Text-Konsole wechseln
  • als nutzerXX einloggen
  • die neuen Log-Einträgen in /var/log/auth.log prüfen

1.10.3 Aufgabe: ein einfaches PAM-Modul aktivieren

  • Es gibt ein PAM-Modul, welches allen normalen Benutzern die Anmeldung am System verweigert. Nur der root Benutzer darf sich dann anmelden.
  • Dieses PAM-Modul ist schon für den Dienst login konfiguriert, aber nicht aktiv (Datei /etc/pam.d/login)
  • Lese die Man-Pages der PAM-Module für den Dienst login der auth-Funktionen, finde heraus, welches Modul das Login aller Nicht-Root-Benutzer verweigern kann, und wie es aktiviert wird.
  • Aktiviere diese Funktion und teste diese aus. Wechsle mit STRG+ALT+F2 auf die Text-Konsole und versuche Dich dort mit dem normalen Benutzer anzumelden. Alternativ: Anmeldung per SSH über das Loopback-Interface:
ssh nutzerXX@localhost
  • Wie kann einem normalen Benutzer der Grund für den Fehlschlag des Anmeldeversuches mitgeteilt werden?

1.10.4 Lösung:

  • Modul pam_nologin.so, aktiviert durch die Datei /etc/nologin:
echo 'Heute kein Login möglich! Wartungsarbeiten bis Dienstag!' > /etc/nologin

1.10.5 Aufgabe: 2-Faktor Authentisierung - OATH Open Authentication Event Token (HOTP)

  • RFC 4226 HOTP: An HMAC-Based One-Time Password Algorithm (https://tools.ietf.org/html/rfc4226)
  • Alternative: RFC 6238 "TOTP: Time-Based One-Time Password Algorithm"
  • Token-Software als App für viele Mobiltelefone verfügbar
  • Pakete installieren
apt install libpam-oath oathtool
  • pam_oath in die PAM Konfiguration aufnehmen (hier für den su Befehl). window=5 gibt ein Fenster von 5 Passwörtern aus der Liste an, welche akzeptiert werden.
$EDITOR /etc/pam.d/su
-----
#%PAM-1.0
auth            sufficient      pam_rootok.so
auth            requisite       pam_oath.so usersfile=/etc/oath/users.oath window=5
-----
  • OATH Benutzerdatei anlegen
mkdir /etc/oath
$EDITOR /etc/oath/users.oath
-----
HOTP nutzerXX - <hex-secret>
HOTP nutzerYY - 0102030405
  • Beispiel: Passwort in HEX-Zahl umrechnen:
echo "villa" | od -x
0000000 6976 6c6c 0a61
0000006
  • Benutzerrechte setzen
chmod 000 /etc/oath/users.oath
chown root: /etc/oath/users.oath
  • Eine Reihe (5 Stück) von Passwörter zum Test erstellen
oathtool -w 5 <hex-secret>
  • Anmeldung ausprobieren als "nutzerXX", eines der Passwörter aus der Liste probieren
su - nutzerXX
  • Wer ein Smart-Phone hat, mal im App-Store nach "OATH" suchen, eines OATH-Token Programm installieren und konfigurieren

1.10.6 Aufgabe: Einmal Passwörter mit OTPW

  • OTPW = One-Time-Password – ähnlich einer TAN-Liste
  • OTPW Pakete installieren
apt install libpam-otpw otpw-bin
  • das Modul pam_otpw in die PAM-Konfiguration vom su aufnehmen, die Konfiguration von OATH und common-auth auskommentieren
...
auth            required        pam_otpw.so
...
session         optional        pam_otpw.so
#@include common-auth
...
  • Das Programm otpw-gen benutzt die Ausgabe von netstat, um den eigenen Zufallszahlengenerator zu initialisieren (ist das sicher?). netstat ist jedoch auf modernen Linux-Systemen wie Debian 9 nicht vorhanden und muss manuell installiert werden:
apt install net-tools
  • Als Benutzer nutzerXX eine Passwortliste erstellen (dabei das Prefix-Passwort angeben) und ggf. ausdrucken. Ausdrucke finden sich im Kyocera-Drucker in der Vorhalle
su - nutzerXX
otpw-gen > otpwlist.txt
less otpwlist.txt
lp otpwlist.txt
  • OTPW legt Hashes der Einmalpasswörter in der Datei ~/.otpw ab
less ~/.otpw
  • OTPW ausprobieren (als NutzerXX nochmals per su anmelden). Das bei der Erstellung der Passwort-Liste angegebene Passwort muss vor dem Einmal-Passwort eingegeben werden
su - nutzerXX
  • das Passwort ist nun verbraucht und aus der Liste gestrichen
less ~/.otpw

1.11 su

  • mit dem Programm su (Switch User eigentlich Substitute User) kann ein Benutzer ein einen anderen Benutzerkontext wechseln
  • Unterschiede bei den Umgebungsvariablen zwischen su und su - oder su -l
    • bei su werden die Umgebung des aufrufenden Benutzer übernommen (kann Sicherheitsprobleme erzeugen)
    • die Variablen $IFS, $HOME, $SHELL, $USER, $LOGNAME, $PATH werden bei su - oder su -l zurückgesetzt
  • Such-Pfade für Benutzer und root werden in /etc/login.defs über die Optionen ENV_PATH und ENV_SUPATH konfiguriert

1.12 sudo

  • sudo ist ein moderner Ersatz für su. Gegenüber su hat sudo mehrere Vorteile:
    • es wird das eigene Benutzerpasswort abgefragt, nicht das Passwort dies Ziel-Benutzers
    • Das Ergebniss der Passwort-Prüfung des Benutzers kann für eine gewisse Zeit gespeichert werden, so das der Benutzer nicht für jeden Befehl das Passwort eingeben muss
    • Bessere Protokollierung
    • Umfangreiche Konfigurationsmöglichkeiten
  • sudo Installation
apt install sudo
  • sudo Basis-Konfiguration ausgeben
sudo -V
  • sudo Konfigurationsdatei sicher editieren (ggf. Variable $EDITOR setzen, sonst wird vi verwendet)
EDITOR=emacs visudo
  • aliase (host, user, command)
  • sudo und das Environment
  • sudo -i vs. sudo su -

1.12.1 Beispiel:

  • Befehl cat auf die Datei /var/log/apt/term.log für Benutzer nutzer02
vi /etc/sudoers.d/apt-term-cat
----
nutzer02 ALL=(root) /bin/cat /var/log/apt/term.log
----
nutzerXX$ sudo -l
  • sudo -l Zeigt die sudo-Konfiguration und die erlaubten Befehle eines sudo-Benutzers an.

1.12.2 Aufgabe:

  • Erstelle eine sudo Konfiguration, um es dem Benutzer nutzerXX zu erlauben, die Logdateien /var/log/messages und /var/log/daemon.log unter den Benutzerberechtigungen des Benutzers root mit den Programmen more und less anzuschauen

1.12.3 Lösung

vi /etc/sudoers.d/log-message-view
------
...
nutzerXX  ALL=(root) /usr/bin/more /var/log/messages
nutzerXX  ALL=(root) /usr/bin/more /var/log/daemon.log
nutzerXX  ALL=(root) /usr/bin/less /var/log/messages
nutzerXX  ALL=(root) /usr/bin/less /var/log/daemon.log
...
  • in einem anderen Terminal/Tmux-Fenster, teste sudo als nutzerXX
nutzerXX$ sudo more /var/log/messages
nutzerXX$ sudo less /var/log/daemon.log
nutzerXX$ sudo less /etc/shadow       # <--- darf nicht gehen
nutzerXX$ sudo more /var/log/auth.log # <--- darf nicht gehen

1.12.4 Frage:

  • gibt es mit dieser Konfiguration ein Sicherheitsproblem?

1.12.5 Antwort:

  • Ja - less und viele andere Programme können Unterprogramme aufrufen (z.B. eine Shell), welche dann mit root-Rechten läuft
  • Lösung: NOEXEC:
nutzerXX  ALL=(root) NOEXEC: /usr/bin/more /var/log/messages
nutzerXX  ALL=(root) NOEXEC: /usr/bin/more /var/log/boot.log

1.12.6 sudo Aliases

     # User alias specification
     User_Alias      FULLTIMERS = millert, mikef, dowdy
     User_Alias      PARTTIMERS = bostley, jwfox, crawl
     User_Alias      WEBMASTERS = will, wendy, wim

     # Runas alias specification
     Runas_Alias     OP = root, operator
     Runas_Alias     DB = oracle, sybase
     Runas_Alias     ADMINGRP = adm, oper

     # Host alias specification
     Host_Alias      SPARC = bigtime, eclipse, moet, anchor :\
                     LINUX = grolsch, dandelion, black :\
                     LINUX_ARM = widget, thalamus, foobar :\
                     LINUX_PPC64 = boa, nag, python
     Host_Alias      CUNETS = 128.138.0.0/255.255.0.0
     Host_Alias      CSNETS = 128.138.243.0, 128.138.204.0/24, 128.138.242.0
     Host_Alias      SERVERS = master, mail, www, ns
     Host_Alias      CDROM = orion, perseus, hercules

     # Cmnd alias specification
     Cmnd_Alias      KILL = /usr/bin/kill, /usr/bin/pkill
     Cmnd_Alias      PRINTING = /usr/sbin/lpc, /usr/bin/lprm
     Cmnd_Alias      SHUTDOWN = /usr/sbin/shutdown
     Cmnd_Alias      HALT = /usr/sbin/halt
     Cmnd_Alias      REBOOT = /usr/sbin/reboot
     Cmnd_Alias      SHELLS = /usr/bin/sh, /usr/bin/zsh, /bin/bash

FULLTIMERS  SPARC=(OP) KILL
WEBMASTERS  LINUX=(:ADMINGRP) SHELLS

1.12.7 sudoedit

  • Benutzer nutzerXX soll die Datei /etc/rsyslog.conf editieren dürfen
  • in der Datei /etc/sudoers
visudo
----
nutzerXX ALL= sudoedit /etc/rsyslog.conf
----
sudoedit /etc/rsyslog.conf

1.12.8 sudo replay

  • Füge die folgenden Konfigurationszeilen in die /etc/sudoers Datei ein
Defaults log_output
Defaults!/usr/bin/sudoreplay !log_output
Defaults!/sbin/reboot !log_output
  • nutzerXX darf root werden
nutzerXX ALL=(root) ALL
  • Benutze sudo als nutzerXX um eine interaktive Shell zu bekommen
nutzerXX$ sudo -s
  • ein paar Befehle ausführen, die Ausgaben produzieren
  • die sudo Root-Shell wieder verlassen
  • (im Terminal als Benutzer root) Aufgezeichnete Sitzungen auflisten
sudoreplay -l
  • aufgezeichnete sudo Sitzung abspielen
sudoreplay <TSID>
  • Verzeichnis der sudo Aufzeichnungen:
ls -l /var/log/sudo-io/

1.12.9 einfaches Intrusion Detection mit sudo (ab sudo 1.8.7)

openssl dgst -sha256 /usr/bin/passwd
SHA256(/usr/bin/passwd)= a92b1b6fb52549ed23b12b32356c6a424d77bcf21bfcfbd32d48e12615785270
visudo
----
nutzerXX ALL= sha256:a92b1b6fb52... /usr/bin/passwd
----

1.12.10 sudo Konfiguration (Passwort Cache Beispiele)

Defaults passwd_tries=5, passwd_timeout=2
Defaults timestamp_timeout=0 # Disable password caching
Defaults timestamp_timeout=5 # 5 Minuten password caching (default)

1.12.11 Befehle ohne Passwort

nutzer ALL=(dba) NOPASSWD: /opt/oracle/bin/befehl

1.12.12 Passwort des Zielaccount angeben (SUSE Linux Default)

Defaults targetpw

2 Tag 2

2.1 Dateisystemberechtigungen

2.1.1 normale Unix-Rechte

  1. die klassischen Unix-Dateisystemberechtigungen (RWX)
  2. das sgid-bit
    chmod g+s <verzeichnis>
    

    neue Dateien bekommen die Gruppe des Verzeichnisses anstatt der (primäre) Gruppe des Benutzers. Beim anlegen von Unterverzeichnissen bekommen diese auch ein sgid bit gesetzt

  3. Unerwartet: Schreibrechte auf Verzeichnis vs. Schreibrechte auf Datei
    useradd nutzer1
    useradd nutzer2
    groupadd projekt
    usermod -a -G projekt nutzer1
    usermod -a -G projekt nutzer2
    mkdir /home/projekt
    chown :projekt /home/projekt
    chmod g+w /home/projekt
    ls -ld /home/projekt/
    su - nutzer1
    cd /home/projekt/
    cat > unveraenderbarer.txt
    Dies ist ein unveraenderbarer text
    CTRL+D
    chmod u=rw,g=r,o=r unveraenderbarer.txt
    chgrp projekt unveraenderbarer.txt
    ls -l unveraenderbarer.txt
    -rw-r--r--. 1 cas projekt 35 Nov 23 21:57 unveraenderbarer.txt
    logout
    su - nutzer2
    cd /home/projekt
    ls -l unveraenderbarer.txt
    vi unveraenderbarer.txt
    
  4. Frage:
    • kann nutzer2 mit diesen Berechtigungen die Datei verändern?
  5. Antwort:
    • ja, indirekt. Der Benutzer hat Schreibrechte auf dem Verzeichnis. Daher kann er neue Dateien anlegen und bestehende Löschen. Er kann auch Dateien löschen, auf denen er keine Schreibrechte besitzt! Der vi Editor legt bei Bearbeiten einer Datei eine lokale Kopie an, diese Kopie wird editiert und beim Speichern wird die Ausgangsdatei gelöscht und durch die temporäre Datei ersetzt.
  6. Sticky-Bit auf Verzeichnis - Nur der Besitzer der Datei darf die Datei löschen
    chmod o+t <verzeichnis>
    

2.1.2 Posix ACLs

  1. ACLs setzen (modify)
    setfacl -m u:<nutzer>:rwx file/dir # ACL setzen
    setfacl -x u:<nutzer>              # ACL löschen
    setfacl -d -m g:<gruppe>:rwx <dir> # Default ACL auf Verzeichnisse setzen
    setfacl -m "d:u::rwx,d::g:rwx,d:o::-" <dir> # alternative Syntax. Generic default mask
    
  2. ACLs auslesen
    getfacl <datei/verzeichnis>
    
  3. ACL mask
    • chmod gruppe ändert nur noch die Maske
    • die Maske filtert die ACLs
    • Berechtigungs-Änderungen können nur mit setfacl durchgeführt werden
    • setfacl -k – default ACLs löschen
    • setfacl -b – ACLs löschen

2.1.3 Erweiterte Attribute

  • Nicht alle Dateisysteme unterstützen alle erweiterten Attribute (EA). Unter Linux unterstützen die ext2/ext3/ext4-Dateisysteme und das XFS-Dateisystem einige der EAs. Die Man-Page zum Dateisystemformat gibt Auskunft über die EA Unterstützung (z.B. man ext4).
  • Anzeigen der erweiterten Attribute: lsattr
  • Ändern der erweiterten Attribute: chattr
  • Sicherheit von erweiterten Attribute (EA) unter Linux im Vergleich zu BSD: unter BSD können die Sicherheitsrelevanten EA (append, immutable) vom Benutzer root nur im Single-User Module (ohne Netzwerk) gelöscht werden. Unter Linux kann root die EA zu jedem Zeitpunkt entfernen!
  1. erweiterte Attribute vom Benutzer verwaltbar
    • d – Datei nicht bei "dump" mitsichern
    • s – Datei wird beim Loeschen mit Nullen ueberschrieben
    • A – bei der Datei wird atime nicht aktualisiert
  2. erweiterte Attribute welche nur vom Benutzer root verwaltet werden
    • a – append - Datei kann nur erweitert werden
    • i – immutable - Datei ist unveraenderbar
  3. Beispiele
    # Erweiterte Attribute Auslesen
    lsattr /pfad/zur/datei
    # Erweiterte Attribute setzen
    chattr +d /pfad/zur/datei
    # Erweiterte Attribute löschen
    chattr -d /pfad/zur/datei
    

2.2 Linux Capabilities

  • Linux Capabilities erlaubes es, Teile der Rechte des root Benutzers an Prozesse und Dateien zu binden
  • die verfügbaren Capabilities sind unter man capabilities nachlesbar
  • Installiere ein paar Zusatztools für Linux-Capabilities
apt install libcap-ng-utils libpam-cap

2.2.1 Beispiel: Als Ersatz für SUID

Als User

cat /usr/bin/ping > PING
chmod a+x PING
./PING 9.9.9.9
sudo -i
setcap cap_net_raw+p .../PING
^d
PING 9.9.9.9

Mit pstree und unter dem proc Dateisystem CapPrm prüfen

2.2.2 Beispiel: root CAP_CHOWN entziehen

capsh --drop=cap_chown --
chown fritz /home/prj

2.2.3 Beispiel: CAP_CHOWN an einen normalen Benutzer delegieren

  • wir möchten die Capabilities CAP_CHOWN (den Besitzer einer Datei/Verzeichnis ändern) und CAP_DAC_OVERRIDE (Dateiberechtigungen ignorieren) an einen normalen Benutzer delegieren
  • PAM Konfiguration prüfen
$EDITOR /etc/pam.d/common-auth
----
[...]
auth        optional      pam_cap.so
[...]
  • Die Capability Benutzerkonfiguration erstellen. der Befehl man capabilities listed die Namen und Beschreibungen der verfügbaren Capabilities.
$EDITOR /etc/security/capability.conf
----
cap_chown,cap_dac_override          nutzerXX
----
  • Welche Capabilities hat das chown Programm? (sollte derzeit keine haben)
getcap $(which chown)
  • Wir setzen die cap_chown Capability auf dem chown Programm
setcap cap_chown=ei $(which chown)
getcap $(which chown)
/bin/chown = cap_chown+ei
  • neu als Benutzer nutzerXX anmelden
su - nutzerXX
su - nutzerXX
  • nun sollte der aktuelle Prozess die Capability cap_chown besitzen
/sbin/getpcaps $$
Capabilities for `15412': = cap_chown+i
  • nun kann der Benutzer nutzerXX Rechte auf Dateien ändern, die ihm nicht gehören:
ls -ld /root
chown nutzerXX /root
  • Für alle Benutzer unabhängig von der PAM-Konfiguration
setcap cap_chown=pe $(which chown)

2.3 Zugriff auf Ressourcen beschränken

2.3.1 Resourcenbeschränkungen über PAM (Modul pam_limits)

  • Konfigurationsdatei der PAM-Limits ist /etc/security/limits.conf. Liste der Resourcen die kontrolliert werden können:
    core Grösse einer Core-Dump Datei (KB)
    data maximale Grösse des zu ladenen Programms (KB)
    fsize maximale Dateigrösse (KB)
    memlock maximale Grösse des Prozesspeichers, welcher nicht ausgelagert werden darf (KB)
    nofile maximale Anzahl der offenen Dateien
    rss maximale Grösse des "resident set size" (Speicherverbrauch eines Prozesses im Hauptspeicher) (KB)
    stack maximale Grösse des Stacks eines Prozesses (KB)
    cpu maximale CPU Zeit (MIN)
    nproc maximale Anzahl von Prozessen
    as Limit des Addressbereiches (KB)
    maxlogins maximale Anzahl der Logins pro Benutzername
    maxsyslogins maximale Anzahl der Logins am System (aller Benutzer)
    priority Priorität von Benutzerprozessen
    locks maximale Anzahl von Datei-Locks
    sigpending maximale Anzahl von wartenden Signalen für Prozesse
    msgqueue maximaler Speicherverbrauch von POSIX message queues (bytes)
    nice maximaler "nice" Wert: [-20, 19]
    rtprio maximale Echtzeit Priorität von Prozessen
    chroot Benutzer darf den "chroot" Syscall ausführen (Debian spezifisch)
  1. Format der limits.conf Datei
    # Benutzer/Gruppe  Type    Ressource       Wert
    *                  soft    core            0
    root               hard    core            100000
    *                  hard    rss             10000
    @student           hard    nproc           20
    @faculty           soft    nproc           20
    @faculty           hard    nproc           50
    ftp                hard    nproc           0
    ftp                -       chroot          /ftp
    @student           -       maxlogins       4
    
  2. Aktuelle Limits der aktuellen Session anzeigen
    ulimit -a
    
  3. limit setzen und testen
    ulimit -S -t 2
    dd if=/dev/urandom | gzip > /dev/null
    
    apt install stress
    ulimit -S -t 2
    prlimit --cpu=10:20 stress -c 1
    
  4. Anzahl Logins beschränken

    wir passen die Anzahl der erlaubten Logins pro Benutzer an

    nutzerXX             -       maxlogins       2
    
  5. testen der neuen Konfiguration
    ssh nutzerXX@localhost
    

    3 x wiederholen oder auf mehr als 3 Konsolen anmelden

  6. CPU Zeit, RAM und offene Dateien beschränken

    durch eine Forkbombe (oder durch einen Sicherheitsfehler in einem Programm) kann ein Benutzer ein Linux/Unix System zum erliegen bringen. Eine Forkbombe erzeugt exponential viele Prozesse. Wenn die Erzeugung von Prozessen nicht beschränkt wurde, kommt das System in einen Zustand in dem es nicht mehr produktiv benutzt werden kann:

    forkbomb(){ forkbomb | forkbomb & }; forkbomb
    
    • oder Kurzversion einer Forkbombe
    :(){ :|:& };:
    
    nutzerXX             -       cpu             1
    nutzerXX             soft    nproc           800
    nutzerXX             hard    nproc           1000
    nutzerXX             -       priority        5
    
  7. testen
    ps -p $$ -o pid,user,nice,cmd
    ulimit -a
    forkbomb(){ forkbomb | forkbomb & }; forkbomb
    

2.3.2 nice, renice und cpulimit

apt install cpulimit
stress -c 8 # CPU Kerne * 2
renice -n <nice-wert> <pid>
cpulimit -l 50 -p <pidid> # nicht mehr empfohlen

2.3.3 ionice

  1. Disk IO per ionice beschränken
    • per ionice kann die IO-Priorität eines Prozesses kontrolliert werden ( ab Kernel 2.6.13 mit CFQ I/O-Scheduler ). Dokumentation zu ionice findet sich in der Linux-Kernel Dokumentation /usr/src/linux/Documentation/block/ioprio.txt.
    1. ionice Klassen
      Nummer Klasse
      0 keine Klasse zugewiesen
      1 Realtime (RT)
      2 Best-Efford (BE)
      3 Idle
      • Die ionice Klassen Realtime und Best-Efford unterscheiden die Prioritätsstufen 0-7 (Default 4)
      • einen Prozess als in der Klasse Idle laufen lassen
      ionice -c 3 dd if=/dev/zero of=/tmp/ionice-test
      
      • Prozess in die Best Effort Klasse (mit Prio "0") hochstufen
      ionice -c 2 -n 0 -p <pid-des-prozesses>
      
  2. iotop

    iotop zeigt die IO-Auslastung des Systems nach Prozessen an. In der Spalte "PRIO" wird die ionice Klasse und die Priorität angezeigt.

    apt install iotop
    stress -d 2
    iotop
    

2.4 Linux CGroups (Controll-Groups)

  • CGroup Tools installieren
apt install libpam-cgroup cgroup-tools libcgroup1
  • CGroups Informationen anzeigen
cat /proc/cgroups
ps xawf -eo pid,user,cgroup,args
systemd-cgls
systemd-cgtop
  • Eine neue Controll-Group erzeugen
cgcreate -g cpu,memory,blkio,devices,freezer:/resourcebox
  • CPU Auslastung auf 10% pro CPU-Kern begrenzen
cgset -r cpu.cfs_period_us=100000 \
      -r cpu.cfs_quota_us=$[ 10000 * $(getconf _NPROCESSORS_ONLN) ] \
           resourcebox
  • Speicher in der Controll-Group auf 256 MB begrenzen
cgset -r memory.limit_in_bytes=256M resourcebox
  • Plattenzugriffe auf 1 MB/s beschraenken
for dev in 253:0 252:0 252:16 8:0 8:16 1:0; do
  cgset -r blkio.throttle.read_bps_device="${dev}  1048576" resourcebox
  cgset -r blkio.throttle.write_bps_device="${dev} 1048576" resourcebox
done
  • Zugriff auf Geraetedateien verbieten
cgset -r devices.deny=a resourcebox
  • Zugriff auf "console", "null", "zero", "random" und "urandom"
for d in "c 5:1" "c 1:3" "c 1:5" "c 1:8" "c 1:9"; do
  cgset -r devices.allow="$d rw" resourcebox
done
  • Programm (Bash Shell als Beispiel) in der CGroup ausführen
cgexec -g cpu,memory,blkio,devices,freezer:/resourcebox \
  prlimit --nofile=256 --nproc=512 --locks=32 /bin/bash
  • Programm (Bash Shell als Beispiel) in der CGroup ausführen
cgexec -g cpu,memory,blkio,devices,freezer:/resourcebox \
  prlimit --nofile=256 --nproc=512 --locks=32 /bin/bash
  • Controll-Group löschen
cgdelete -g cpu,memory,blkio,devices,freezer:/resourcebox

2.4.1 Systemresourcen beschränken mit systemd-run

systemd-run stress -c 3
systemd-run stress -c 3
systemctl show run-r<UUID>.service
systemctl set-property run-r<UUID>.service CPUShares=100
systemctl set-property run-r<UUID>.service CPUQuota=100

2.5 systemd Sicherheit

2.5.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/python -m SimpleHTTPServer 8000

2.5.2 weitere Directiven und Isolationstechniken

  • PrivateNetwork=yes
  • CapabilityBoundingSet=CAP_CHOWN CAP_KILL
  • CapabilityBoundingSet=~CAP_SYS_PTRACE
  • DeviceAllow=/dev/null rw

2.6 SSH

2.6.1 SSH Known Hosts hashen

ssh-keygen -H -f .ssh/known_hosts
$EDITOR .ssh/config
----------
HashKnownHosts yes
----------
chmod -Rc go= .ssh

2.6.2 SSH Passphrase ändern

ssh-keygen -p -f .ssh/id_ed25519.pub

2.6.3 Auf dem SSH-Server: Passwort-Authentisierung abschalten

Die Benutzung von Passwörter für die SSH-Authentisierung ist einfach aber liefert nur eine minimale Sicherheit. Passwörter sind oft zu erraten oder per Brute-Force-Angriff ermittelbar. Besser sind kryptografische Schlüssel. SSH bietet die Authentisierung per Schlüssel, jedoch wird die Passwort-Anmeldung als Rückfall-Mechanismus benutzt. Um SSH richtig abzusichern sollte daher die Passwort-Anmeldung in der Konfiguration des SSH-Servers abgeschaltet werden.

$EDITOR /etc/ssh/sshd_config
----
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
----

2.6.4 Übung: SSH mit Schlüsseln statt mit Passwort (ca. 20 Minuten)

  • in dieser Übung arbeiten wir als Benutzer und als Administrator. Lege mit dem Übungs-Partner die Rolle fest (Benutzer oder Administrator) und führe die Übung durch. Danach wechselt die Rolle.
  • Administrator: lege einen Benutzeraccount für den Kollegen/Kollegin vor/hinter Dir im Kurs an (Passwort und Benutzername absprechen)
  • Administrator: OpenSSH Server installieren (wenn nicht schon geschehen)
sudo apt install openssh-server
  • Benutzer: Teste aus, dass Du Dich auf dem System des Übungs-Partners einloggen kannst. Bei der ersten Kontaktaufnahme wird der Fingerprint des Servers angezeigt. Dieser Fingerprint kann über den Administrator des Servers angefragt werden.
nutzerXX: ssh nutzerXX@notebookYY
  • Benutzer: nach dem Test wieder aus dem fernen Rechner ausloggen
  • Benutzer: als normaler Benutzer (nutzerXX, nicht root) einen neuen SSH-Schlüssel erstellen (4096 RSA). Eine Passphrase vergeben.
nutzerXX$ ssh-keygen -b 4096
  • Benutzer: den öffentlichen Schlüssel per SSH auf den fernen Rechner übertragen:
nutzerXX$ ssh-copy-id nutzerXX@notebookYY
  • Benutzer: Alternativ die Datei ~/.ssh/id_rsa.pub vom eigenen Rechner auf den fernen Rechner übertragen (z.B. per E-Mail senden) und den Administrator bitten, den eigenen Schlüssel in die Datei .ssh/authoritzed_keys einzutragen.
  • Administrator: Nun sind die Schlüssel ausgetauscht, und wir können Anmeldung per Passwort auf dem Server-System abschalten. Als root Benutzer:
$EDITOR /etc/ssh/sshd_config
------
PermitRootLogin no
PubkeyAuthentication yes
PasswordAuthentication no
------
  • Administrator: SSH-Dienst neu starten
systemctl restart sshd
  • Benutzer: Anmeldung am fernen Rechner testen, dies sollte nun nach Eingabe der Passphrase funktionieren
nutzerXX$ ssh nutzerXX@notebookYY
  • Benutzer: Wenn ein Benutzer ohne Schlüssel versucht, sich am fernen Rechner anzumelden, wird er gleich abgewiesen
ssh root@notebookYY
  • Info Benutzer: die Fingerprints der Server werden beim Client unter ~/.ssh/known_hosts gespeichert
  • Info Administrator: die öffentlichen Schlüssel werden auf dem fernen Rechner in der Datei ~/.ssh/authorized_keys gespeichert

2.6.5 SSH-Agent

  • SSH-Agent starten (wird normalerweise in der Benutzer-Sitzung gestartet, z.B. über ".profile")
eval $(ssh-agent)
  • die privaten SSH-Schlüssel dem SSH-Agent hinzufügen
ssh-add
ssh-add <schlüssel>
  • Fingerprints aller Schlüssel im Agent anzeigen
ssh-add -l
  • alle Schlüssel im Agent anzeigen
ssh-add -L
  • Agent sperren/entsperren
ssh-add -x
ssh-add -X
  • Schlüssel im SSH-Agent löschen
ssh-agent -D

3 Tag 3

3.1 SSH Teil 2

3.1.1 SSH Fingerprint eines Servers prüfen

  • SSH Fingerprint eines Schlüssels auf einem Server anzeigen (zur Prüfung eines ersten Logins)
ssh-keygen -l -f /etc/ssh/ssh_host_<algorithm>_key.pub

3.1.2 lokale SSH-Konfigurationsdatei

  • Bei komplexen SSH-Konfigurationen ist es hilfreich, die SSH-Parameter pro Ziel-System in der lokalen SSH-Konfigurationsdatei für den Benutzer abzulegen. Fast alle Kommandozeilen-Optionen des SSH-Clients können in dieser Konfigurationsdatei festgelegt werden (siehe man ssh).
  • Beispiel einer lokalen SSH-Konfigurationsdatei für einen Benutzer. Datei ~/.ssh/config
Host zielhost                                          # symbolischer Name
        Hostname zielhost.example.com                  # echter DNS-Name
	User nutzerXX                                  # Standard Benutzer
	Compression yes                                # gzip Kompression an
	VerifyHostKeyDNS yes                           # Host-Key per DNS prüfen
	Port 65123                                     # SSH läuft auf diesem Port
	ProxyJump jumphost.example.com                 # Verbindung über Jumphost
  1. Übung: lokale SSH-Client-Konfigurationsdatei
    • Zeit: 15 Minuten
    • erstelle eine lokale SSH-Konfigurationsdatei ~/.ssh/config für die Verbindung auf die IP-Adresse 192.168.1.241 (Laptop des Trainers)
      • vergebe den symbolischen Namen trainer für den Server
      • SSH-Server läuft auf Port 8422
      • Benutzername nutzer, Passwort villa
      • benutze die IP-Adresse des Servers als Hostnamen in der Konfiguraionsdatei
    • die SSH Konfigurationsdatei darf nun für den Benutzer lesbar sein (ggf. müssen die Dateiberechtigungen angepasst werden)
    • teste die Verbindung per symbolischen Namen
    ssh trainer
    
    • Beispiellösung
    Host trainer
      Hostname 192.168.1.241
      User nutzer
      Port 8422
    
  2. Weitere Informationen und Tipps: Simplify Your Life With an SSH Config File

3.1.3 Agent-Forwarding

  • Agent-Funktion an einem Jumphost weitergeben. Nun kann sich der Benutzer vom Jumphost mit Schlüssel an weiteren SSH-Servern anmelden
ssh -A <jumphost>
  1. Übung: SSH-Forwarding
    • der Nachbar sollte uns einen Benutzer auf seinem System mit Deinem Benutzernamen erstellen
    • Für diese Übung müssen wir unseren öffentlichen Schlüssel auf den Laptop eines Nachbarn installieren (dies wurde ggf. schon am Tag 2 gemacht: Übung Anmeldung am SSH mit Schlüssel
    ssh-copy-id nutzerXX@notebookYY
    
    • nachdem die Schlüssel kopiert wurden kann die Passwort-Authentisierung in der Datei /etc/ssh/sshd_config ausgeschaltet werden
    [...]
    PasswordAuthentication no
    [...]
    
    • und den SSH-Dienst neu starten
    systemctl restart sshd
    
    • nun sollte auf dem Laptop nur noch eine Anmeldung per Schlüssel möglich sein
    • stelle sicher, das der SSH-Agent für die aktuelle Shell-Sitzung aktiv ist und lade die eigenen Schlüssel in den SSH-Agent
    ssh-add -l
    
    • Teste die Anmeldung an den Trainer-Laptop (hier Anmeldung per Passwort villa)
    ssh trainer
    
    • Teste, das eine Anmeldung mit dem eigenen Benutzer (nutzerXX) von Laptop des Trainers auf den Laptop des Übungs-Partners (notebookYY) nicht möglich ist
    trainer-laptop$ ssh nutzerXX@notebookYY
    
    • den Laptop des Trainers wieder verlassen
    trainer-laptop$ exit
    
    • wir nehmen an, dass eine direkte Netzwerkverbindung zwischen dem eigenen Laptop und dem Laptop des benachbarten Teilnehmers nicht möglich ist, ein Login auf diesem Laptop muss über den Umweg des Trainer-Laptops geschehen (Jump-Host)
    • Teste die SSH-Agent-Forwarding Funktion mit einer Anmeldung an den Jump-Host (Trainer-Laptop per Passwort villa) und von dort aus einer Anmeldung an den Laptop des benachbarten Teilnehmers. Die zweite Anmeldung solle bei aktiven SSH-Agent ohne Passwortabfrage funktionieren
    notebookXX$ ssh -A trainer
    trainer-laptop$ ssh nutzerXX@notebookYY
    

3.1.4 SSH Agent-Forwarding vs. ProxyCommand

  1. AgentForwarding Alternative ProxyCommand
    • vor OpenSSH 7.3
    Host jumphost
     	User nutzer
     	Hostname 192.168.1.241 # Trainer Laptop
    	Port 8422
    
    Host internalserver
     	User nutzerXX
     	Hostname notebookYY # Laptop des anderen Teilnehmers
     	Port 22
     	ProxyCommand ssh -q -W %h:%p jumphost
    
    • seit OpenSSH 7.3
    Host jumphost
     	User nutzer
     	Hostname 192.168.1.241 # Trainer Laptop
    	Port 8422
    
    Host internalserver
     	User nutzerXX
     	Hostname notebookYY # Laptop des anderen Teilnehmers
     	Port 22
     	ProxyJump jumphost
    
    • ProxyJump auf der Kommandzeile (seit OpenSSH 7.3)
    ssh -J jumphost benutzer@internalserver
    
  2. Übung:
    • erstelle eine lokale Konfigurationsdatei ~/.ssh/config mit der ProxyCommand Konfiguration (siehe oben)
    • SSH-Verbindung durch den JumpHost (Trainer-Laptop) zum Laptop eines anderen Teilnehmers testen
    ssh internalserver
    

3.1.5 VisualHostKey

  • der Konfigurationsparameter VisualHostKey in der Datei /.ssh/config zeigt eine visuelle Darstellung des Schlüssel-Hash jedes Ziel- und Jump-Host Servers an
VisualHostKey yes

3.1.6 SSH-PAM-Module

  • pam_ssh.so – per SSH Key Passphrase anmelden und Schlüssel in den Agent laden
  • pam_sshauth.so – lokale Anmeldung durch erfolgreiche SSH-Anmeldung an einen fernen SSH Server

3.1.7 SSH Tunnel

  • Über SSH Tunnel können beliebige Ports über die SSH Verbindung geleitet werden
  • Tunnel von Port 80 auf dem Server zu Port 8080 auf dem Client
ssh -L 8080:notebookXX:80 nutzerXX@notebookXX
ssh -L 8080:datenbank:80 nutzerXX@notebookXX
  • Rückwärts-Tunnel: vom Ziel-System zurück auf den Client. Dieses Beispiel öffnet einen Tunnel von localhost:8000 auf dem Zielsystem auf Port 8000 des SSH-Clients
ssh -R 8000:notebookXX:8000 nutzerXX@notebookXX
  • Socks-Proxy Forwarding (Web-Browsen durch den Tunnel), im Browser die Socks-Proxy Konfiguration auf 127.0.0.1 Port 8080 setzen
ssh -CD 127.0.0.1:8080 nutzer@server
  1. Übung: SSH Tunnel
    • erstelle einen SSH-Tunnel von Port 8080 (lokal) auf die IPv4-Loopback-Adresse (127.0.0.XX; x=Teilnehmernummer, z.B. 42) des Trainer-Laptops, Port 8000:
    ssh -L 8080:127.0.0.XX:8000 trainer
    
    • auf dem Trainer-Laptop erstellen wir eine minimale HTML-Datei
    mkdir ~/webseiteXX
    cd ~/webseiteXX
    ~/webseiteXX% echo "dies ist der Webserver von NutzerXX" > index.html
    
    • auf dem Trainer-Laptop den Python3 Webserver auf der Loopback-Adresse, Port 8000, starten
    ~/webseiteXX% python3 -m http.server --bind 127.0.0.XX
    
    • Starte Firefox-Browser, verbinde Dich mit der eigenen Loopback-Adresse Port 8080 (URL http://127.0.0.1:8080/). Es sollte die minimale Webseite aus dem Heimverzeichnis auf dem Trainer-Laptop erscheinen

3.1.8 SSH-Socks-Proxy

  • Socks-Proxy Forwarding (Web-Browsen durch den Tunnel), im Browser die Socks-Proxy Konfiguration auf 127.0.0.1 Port 8080 setzen
    • Option -C schaltet Kompression der Daten im Tunnel ein
    • Option -D schaltet den Socks-Proxy für den SSH-Tunnel ein
ssh -C -D 127.0.0.1:8080 nutzer@server
  1. Übung:
    • gehe mit dem Browser (Firefox) auf die Seite https://ripe.net. Oben rechts steht die Verbindungs-IP-Adresse der HTTPS-Verbindung. Notiere die Adresse.
    • Öffne einen Socks-Tunnel per SSH auf den Server notes.defaultroutes.de, Port 65020, Benutzer nutzer und Passwort villa
    ssh -CD 2222 -p 65020 nutzer@notes.defaultroutes.de
    
    • Konfiguriere einen Socks-Proxy im Firefox für Port 2222 (Bearbeiten -> Einstellungen -> Erweitert -> Netzwerk -> Verbindungs-Einstellungen -> Socks-Host 127.0.0.1 Port 2222)
    • gehe mit dem Browser (Firefox) auf die Seite https://ripe.net. Oben rechts steht die Verbindungs-IP-Adresse der HTTPS-Verbindung. Vergleiche die Adresse mit der Adresse vom ersten Verbindungsaufbau. Bonus: mache eine whois-Abfrage auf beide Adressen (wenn der Befehl whois nicht installiert ist, diesem mit apt install whois installieren, dann Abfragen der IP-Adresse mit whois <IP-Adresse>
    • nach der Übung den Server notes.defaultroutes.de verlassen und den Socks-Proxy aus der Firefox-Konfiguration entfernen

3.1.9 SSH-Escape

  • A tilde (~) after an Enter is the escape sequence of SSH
  • ~~ Tilde senden
  • ~. SSH-Verbindung sofort stoppen
  • ~<Ctrl-Z> SSH Sitzung in den Hintergrund senden (mit fg wieder in den Vordergrund holen)
  • ~& SSH als 'daemon' in den Hintergrund senden und vom Terminal ablösen
  • ~? Hilfe anzeigen.
  • ~R neuen Sitzungschlüssel aushandeln
  • ~C SSH Komandozeile anzeigen (kann benutzt werden um neue Tunnel in schon bestehende Verbindungen einzubauen)
  • ~# bestehende SSH-Tunnel anzeigen

3.1.10 SSH-Schluessel Fingerprints in DNS

  • SSH Fingerprint im DNS hinterlegen (DNSSEC Absicherung empohlen!). SSHFP Records erstellen (auf dem Server, je ein Aufruf pro Schlüssel-Type (RSA, ECDSA, ED25519 …):
ssh-keygen -r <hostname> -f /etc/ssh/ssh_host_<algorithm>_key.pub
  • SSH mit Prüfung des SSH-Fingerabdrucks in DNS
ssh -o "VerifyHostKeyDNS ask" <hostname>
  • in SSH-Config Datei übernehmen
$EDITOR ~/.ssh/config
----
HOST *
  VerifyHostKeyDNS ask
  • Test
ssh host.example.com
The authenticity of host 'host.example.com (2001:db8:2b6:0:ba27:ebff:fe12:30db)' can't be established.
ECDSA key fingerprint is SHA256:ue1FlRUMmkPNhgFE55yqnnFl58FOlMnDGheCxQn2tWg.
Matching host key fingerprint found in DNS.
ECDSA key fingerprint is MD5:2e:a1:3c:94:d0:cb:ed:85:67:2e:7a:85:1c:bc:f3:70.
Matching host key fingerprint found in DNS.
Are you sure you want to continue connecting (yes/no)?
  • mit VerifyHostKeyDNS yes
ssh -v <host>
[...]
debug1: found 4 secure fingerprints in DNS
debug1: matching host key fingerprint found in DNS
[...]
  • Wenn OpenSSH Probleme hat, die DNSSEC Authentizität der SSHFP zu prüfen, muss ggf. EDNS in der Resolver-Konfiguration des Linux eingeschaltet werden
$EDITOR /etc/resolv.conf
----
[...]
options edns0

3.1.11 ausführbare Befehle über die authorized_keys beschränken

command="foobar" ssh-rsa AAAAB3Nza… calls "foobar" and only "foobar" upon every login with this key
from="computer" ssh-rsa AAAAB3Nza… allows access with this key only from the host "computer".

3.1.12 Verwaltung von SSH authorized_keys auf dem Server

3.1.14 Alerting or notifying on SSH logins / PAM

  • Original von Jan-Piet Mens mit Benachrichtigung via MQTT: https://jpmens.net/2018/03/25/alerting-on-ssh-logins/
  • zentralisierte Benachrichtigung bei SSH-Logins auf einem Rechner der eigenen IT-Infrastruktur. Kommunikation zwischen SSH-Server und zentraler Monitoring-Instatnz über leichtgewichtiges UDP, E-Mail Kommunikationsparameter werden zentral verwaltet.
  • Quellcode des hare Programms (PAM-Modul für die SSH-Server)
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <memory.h>
#include <sys/socket.h>

/*
 * hare.c (C)2018 by Jan-Piet Mens <jp@mens.de>
 */

#define PORTNO  8035

#define env(K)  ( getenv(K) ? getenv(K) : "<unknown>" )

int main(int argc, char **argv)
{
        struct sockaddr_in servaddr;
        unsigned long addr;
        int sockfd;
        char *host = argv[1], buf[BUFSIZ], myhostname[BUFSIZ];
        char *pamtype = env("PAM_TYPE");        /* Linux */
        char *pamsm = env("PAM_SM_FUNC");       /* FreeBSD https://www.freebsd.org/cgi/man.cgi?query=pam_exec */

        if (strcmp(pamtype, "open_session") != 0 && strcmp(pamsm, "pam_sm_open_session") != 0) {
                fprintf(stderr, "Neither PAM open_session nor pam_sm_open_session detected\n");
                return 0;
        }

        if (argc != 2) {
                fprintf(stderr, "Usage: %s address\n", *argv);
                exit(2);
        }

        if (gethostname(myhostname, sizeof(myhostname)) != 0)
                strcpy(myhostname, "?");

        snprintf(buf, sizeof(buf), "%s login to %s from %s via %s",
                env("PAM_USER"),
                myhostname,
                env("PAM_RHOST"),
                env("PAM_SERVICE"));

        addr = inet_addr(host);

        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sin_family = AF_INET;
        servaddr.sin_port = htons(PORTNO);
        memcpy((void *)&servaddr.sin_addr, (void *)&addr, sizeof(addr));

        sockfd = socket(AF_INET, SOCK_DGRAM, 0);

        if (sendto(sockfd, (void *)buf, strlen(buf), 0, (struct sockaddr *) &servaddr, sizeof(servaddr)) < 0) {
                perror("sendto");
        }
        return (0);
}
  • Übersetzen und Installieren des hare Programms (ggf. das Paket gcc installieren)
gcc -O2 -o hare hare.c
sudo cp hare /usr/local/sbin
  • das hare Programm in die PAM-Konfiguration /etc/pam.d/sshd eintragen. Ziel-IP ist der Laptop des Trainers (192.168.1.241)
[...]
session    optional     pam_exec.so /usr/local/sbin/hare <ip-des-hare-servers>
[...]
  • der zentrale Python-Server /usr/local/bin/hared.py, mit der Funktion pro SSH-Login eine Benachrichtigungs-Mail zu versenden (läuft auf dem Laptop des Trainers)
#!/usr/bin/env python
# -*- coding: utf-8 -*-

import socket
import json
import smtplib, ssl
import random
import string
import datetime

__author__    = 'Jan-Piet Mens <jp()mens.de> / Carsten Strotmann <carsten()strotmann.de>'

myhostname     = "debian.example.com" # Domain-Name des E-Mail Senders (diese Maschine)
smtp_server    = "mail.example.com" # Name of the Mail-Server
smtp_port      = "587"  # SMTP with STARTTLS
sender_email   = "servernotification@example.com"
receiver_email = "admin@example.com"
smtp_user      = "admin"
smtp_password  = "secret"

server_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
server_socket.bind(('', 8035))

while True:
    message, address = server_socket.recvfrom(1024)
    host, port = address

    data = {
        'host' : host,
        'msg'  : message,
    }

    js = json.dumps(str(data))
    # print(js)


    message = "To: <{}>\n".format(receiver_email)
    message = message + "From: <{}>\n".format(sender_email)
    message = message + "Message-Id: <{}@example.com>\n".format(''.join(random.choices(string.ascii_uppercase + string.digits, k=10)))
    time = datetime.datetime.now()
    message = message + "Date: {} \n".format(time.strftime('%d %b %Y  %H:%M:%S %z'))
    message = message + "Subject: SSH Login to {}\n\n".format(data["host"])
    message = message + "SSH Login into {} detected, Message: {}".format(data["host"], data["msg"])

    # Create a secure SSL context
    context = ssl.create_default_context()
    # if the TLS certificate of the mail server does not match
    # the mail servers hostname or domain-name, disable hostname checking
    # context.check_hostname = False
    server = smtplib.SMTP()
    # Try to log in to server and send email
    try:
        server.connect(smtp_server,smtp_port)      # Connect to the server
        server.ehlo(myhostname)                    # Send EHLO
        server.starttls(context=context)           # TLS Secure the connection
        server.ehlo(myhostname)                    # Another EHLO, now TLS secured
        server.login(smtp_user, smtp_password)     # Login to the server

        server.sendmail(sender_email, receiver_email, message) # send the mail
    except Exception as e:
        # Print any error messages to stdout
        print("Error: ", e)
    finally:
        if (server):
            server.quit()

3.1.15 eine sichere SSH-Server Konfiguration

$EDITOR /etc/ssh/sshd_config
  • Nur Protokoll Version 2 erlauben
Protocol 2
  • Sichere Schlüssel-Austausch-Verfahren
KexAlgorithms curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256
  • neue DH Moduli für "diffie-hellman-group-exchange-sha256" (Achtung, dauert lange!) Hintergründe unter http://weakdh.org
ssh-keygen -G /etc/ssh/moduli.all -b 4096
ssh-keygen -T /etc/ssh/moduli.safe -f /etc/ssh/moduli.all
mv /etc/ssh/moduli.safe /etc/ssh/moduli
rm /etc/ssh/moduli.all
chcon -v --type=etc_t /etc/ssh/moduli
  • EC-ED25519-Kurve Schlüssel bevorzugen
HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
  • Sichere symmetrische Verschlüsselungs-Algorithmen
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
  • Sichere Message-Authenication-Codes (MAC)
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
  • Host-Keys neu erzeugen (Achtung: Clients werden Warnungen bekommen, Kunden und Kollegen informieren!)
cd /etc/ssh
rm ssh_host_*key*
ssh-keygen -t ed25519 -f ssh_host_ed25519_key < /dev/null
ssh-keygen -t rsa -b 4096 -f ssh_host_rsa_key < /dev/null
  • SSH Server neu starten
systemctl restart sshd
  • SSH Client Parameter testen
ssh -Q cipher
ssh -Q cipher-auth
ssh -Q mac
ssh -Q kex
ssh -Q key
  • Jetzt nochmal per SSH auf dem fernen Rechner einloggen, -v Zeigt die Kommunikationsparameter an
nutzerXX$ ssh -v nutzer@notebookYY
  • Hier sollte jetzt eine Warnung ausgegeben worden sein, da die Host-Schlüssel nicht mehr mit den Fingerprints in der known_hosts Datei übereinstimmen. Den Admin des fernen Servers fragen, ob er die Schlüssel auf dem Rechner neu erstellt hat. Wenn der Admin dies bestätigt, dann er Texteditor den alten Fingerprint aus der ~/.ssh/known_hosts Datei löschen.
  • SSH Client ~/.ssh/config
VerifyHostKeyDNS yes
VisualHostKey yes

3.2 Linux-Distributionen (Sicherheitsbewertung)

3.3 Software-Sicherheitsfunktionen unter Linux

3.3.1 Kernel-Konfigurationsoptionen

Der Linux-Kernel bietet einige Sicherheitfunktionen, welche zur Zeit der Übersetzung des Kernels aus den Quellen angeschaltet werden können. Einige dieser Funktionen haben negative Auswirkungen auf die Ausführungsgeschwindigkeit des Systems, so das diese Funktionen nicht in allen Distributionen gesetzt sind. In den Klammern sind die jeweiligen Konfigurationsnamen aufgeführt. In einem Linux-System können diese Namen in der Kernel-Konfigurationsdatei geprüft werden. Beispiel:

zcat /proc/config.gz | grep CONFIG_SECURITY_DMESG_RESTRICT

oder, wenn /proc/config.gz nicht vorhanden.

cat  /boot/config-$(uname -r) | grep CONFIG_SECURITY_DMESG_RESTRICT
  1. Die Funktionen (Kernel 4.15+):
    • Zugriff auf Kernel-Syslogmeldungen via dmesg unterbinden (CONFIG_SECURITY_DMESG_RESTRICT)
    • Speicher-Kopierroutinen zwischen Kernel- und Userspace härten (CONFIG_HARDEN_USERCOPY)
    • String- und Speicher-Funktionen gegen Buffer-Overflows härten (CONFIG_FORTIFY_SOURCE)
    • Lade alle Kernel-Dateien aus einem Dateisystem (Module, Firmware, LSM-Dateien) (CONFIG_SECURITY_LOADPIN)
  2. Kernel-Schnittstelle für Hardware- und CPU Sicherheitsprobleme
    • Seit Kernel 4.15 besitzt der Linux-Kernel eine Schnittstelle zum Abfragen von bekannten Hardware- und CPU-Sicherheitsproblemen, und Informationen ob der laufende Linux-Kernel gegen die Sicherheitsprobleme schützt.
    grep . /sys/devices/system/cpu/vulnerabilities/*
    

3.3.2 Userspace Software-Sicherheitsfunktionen unter Linux

  1. Programm zum Prüfen der Sicherheitsfunktionen
    git clone https://github.com/slimm609/checksec.sh
    cd checksec.sh/
    bash ./checksec --proc-all
    bash ./checksec --kernel
    bash ./checksec --dir=/usr/sbin --verbose
    

3.4 Linux Sicherheitsmeldungen

Quelle Link
Linux Weekly News Sicherheitsmeldungen https://lwn.net/Security/
Red Hat Sicherheitsmeldungen https://access.redhat.com/security/security-updates/#/security-advisories
Debian Security Tracker https://security-tracker.debian.org/tracker/
Debian "stable" Sicherheitsmeldungen https://security-tracker.debian.org/tracker/status/release/stable
SUSE Sicherheitsmeldungen https://www.suse.com/de-de/support/update/
Ubuntu Sicherheitsmeldungen https://www.ubuntu.com/usn/
Gentoo Sicherheitsmeldungen https://security.gentoo.org/glsa

3.5 Audit Subsystem

  • Das Audit-Subsystem überwacht Dateien und Systemaufrufe (Systemcalls) und schreibt Log-Informationen in das Audit-Log
  • Audit Subsystem Homepage https://people.redhat.com/sgrubb/audit/
  • Audit-Subsystem Programme installieren
apt install auditd audispd-plugins
  • Audit-Subsystem Trace eines Prozesses (hier 4 x ICMP Echo per ping)
autrace /bin/ping -c 4 8.8.8.8
  • Audit Log für einen speziellen autrace Aufruf anschauen
ausearch -i -p <audit-id>
  • Alle System-Calls des autrace Aufruf auflisten und nach Häufigkeit sortiert ausgeben
ausearch -i -p <audit-id> | grep type=SYSCALL | cut -f 6 -d ' ' | sort | uniq -c
  • Audit-Daemon Konfiguration anpassen (Grössen-Werte in MB)
$EDITOR /etc/audit/auditd.conf
----
space_left=4000
admin_space_left=2000
num_logs=10
max_log_file=<max-groesse-der-log-datei>
max_log_file_action=rotate
  • Audit-Dämon anschalten (so dass er bei einem Restart neu gestartet wird) und prüfen das der Prozess läuft
systemctl enable --now auditd
systemctl status auditd
  • eine Audit-Policy erstellen
$EDITOR /etc/audit/rules.d/audit.rules
  • hier ist eine Beispiel-Policy:
## First rule - delete all
-D

## Increase the buffers to survive stress events.
## Make this bigger for busy systems
-b 8192

## This determine how long to wait in burst of events
--backlog_wait_time 0

## Set failure mode to syslog
-f 1

# audit_time_rules - Record attempts to alter time through adjtime
-a always,exit -F arch=b64 -S adjtimex -k audit_time_rules

# audit_time_rules - Record attempts to alter time through settimeofday
-a always,exit -F arch=b64 -S settimeofday -k audit_time_rules

# audit_time_rules - Record Attempts to Alter Time Through stime
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -S clock_settime -k audit_time_rules

# audit_time_rules - Record Attempts to Alter Time Through clock_settime
-a always,exit -F arch=b64 -S clock_settime -k audit_time_rules

# Record Attempts to Alter the localtime File
-w /etc/localtime -p wa -k audit_time_rules

# Record Events that Modify User/Group Information
# audit_account_changes
-w /etc/group -p wa -k audit_account_changes
-w /etc/passwd -p wa -k audit_account_changes
-w /etc/gshadow -p wa -k audit_account_changes
-w /etc/shadow -p wa -k audit_account_changes
-w /etc/security/opasswd -p wa -k audit_account_changes

# Record Events that Modify the System's Network Environment
# audit_network_modifications
-a always,exit -F arch=b64 -S sethostname -S setdomainname -k audit_network_modifications
-w /etc/issue -p wa -k audit_network_modifications
-w /etc/issue.net -p wa -k audit_network_modifications
-w /etc/hosts -p wa -k audit_network_modifications

#Record Events that Modify the System's Mandatory Access Controls
-w /etc/selinux/ -p wa -k MAC-policy

#Record Events that Modify the System's Discretionary Access Controls - chmod
-a always,exit -F arch=b32 -S chmod -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S chmod  -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - chown
-a always,exit -F arch=b32 -S chown -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S chown -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - fchmod
-a always,exit -F arch=b32 -S fchmod -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S fchmod -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - fchmodat
-a always,exit -F arch=b32 -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - fchown
-a always,exit -F arch=b32 -S fchown -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S fchown -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - fchownat
-a always,exit -F arch=b32 -S fchownat -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S fchownat -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - fremovexattr
-a always,exit -F arch=b32 -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - fsetxattr
-a always,exit -F arch=b32 -S fsetxattr -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S fsetxattr -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - lchown
-a always,exit -F arch=b32 -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - lremovexattr
-a always,exit -F arch=b32 -S lremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S lremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - lsetxattr
-a always,exit -F arch=b32 -S lsetxattr -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S lsetxattr -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - removexattr
-a always,exit -F arch=b32 -S removexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S removexattr -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Events that Modify the System's Discretionary Access Controls - setxattr
-a always,exit -F arch=b32 -S setxattr -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S setxattr -F auid>=500 -F auid!=4294967295 -k perm_mod

#Record Attempts to Alter Logon and Logout Events
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins

#Record Attempts to Alter Process and Session Initiation Information
-w /var/run/utmp -p wa -k session
-w /var/log/btmp -p wa -k session
-w /var/log/wtmp -p wa -k session

#Ensure auditd Collects Unauthorized Access Attempts to Files (unsuccessful)
-a always,exit -F arch=b32 -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -k access
-a always,exit -F arch=b32 -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -k access
-a always,exit -F arch=b64 -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EACCES -F auid>=500 -F auid!=4294967295 -k access
-a always,exit -F arch=b64 -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EPERM -F auid>=500 -F auid!=4294967295 -k access

#Ensure auditd Collects Information on the Use of Privileged Commands
#
#  Find setuid / setgid programs then modify and uncomment the line below.
#
##  sudo find / -xdev -type f -perm -4000 -o -perm -2000 2>/dev/null
#
# -a always,exit -F path=SETUID_PROG_PATH -F perm=x -F auid>=500 -F auid!=4294967295 -k privileged

#Ensure auditd Collects Information on Exporting to Media (successful)
-a always,exit -F arch=b64 -S mount -F auid>=500 -F auid!=4294967295 -k export

#Ensure auditd Collects File Deletion Events by User
-a always,exit -F arch=b64 -S rmdir -S unlink -S unlinkat -S rename -S renameat -F auid>=500 -F auid!=4294967295 -k delete

#Ensure auditd Collects System Administrator Actions
-w /etc/sudoers -p wa -k actions

#Ensure auditd Collects Information on Kernel Module Loading and Unloading
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-a always,exit -F arch=b64 -S init_module -S delete_module -k modules

#Make the auditd Configuration Immutable
-e 2
  • die Policy kompilieren und prüfen
augenrules --check
  • die neue Policydatei in den Kernel laden
augenrules --load
  • aktive Regeln auflisten
auditctl -l
  • Status des Audit-Subsystems
# auditctl -s
enabled 2
failure 1
pid 12901
rate_limit 0
backlog_limit 8192
lost 0
backlog 0
backlog_wait_time 0
loginuid_immutable 0 unlocked
  • 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
  1. Aufgabe:
    • das Audit-Subsystem um neue Regel(n) zu Systemd erweitern:
      • alle Dateien mit der Endung *.conf unter /etc/systemd sollen auf Schreibzugriffe überwacht werden
      • die neue Richtlinie kompilieren und aktivieren (ggf. Reboot notwendig)
      • die neue Richtlinie testen, in dem manuell an einer der überwachten Dateien eine Änderung vorgenommen wird
      • die Audit-Meldungen per ausearch anzeigen und analysieren
  2. Beispiel-Lösung
    [...]
    # Systemd Konfiguration
    -w /etc/systemd/journald.conf -p wa -k systemd
    -w /etc/systemd/logind.conf -p wa -k systemd
    -w /etc/systemd/networkd.conf -p wa -k systemd
    -w /etc/systemd/resolved.conf -p wa -k systemd
    -w /etc/systemd/sleep.conf -p wa -k systemd
    -w /etc/systemd/system.conf -p wa -k systemd
    -w /etc/systemd/timesyncd.conf -p wa -k systemd
    -w /etc/systemd/user.conf -p wa -k systemd
    [...]
    

3.5.1 Audit-Hilfsprogramme

  • die letzten Logins auf dem System anzeigen
aulast
  • Wann haben sich Benutzer zum letzten Mal eingelogged?
aulastlog
  • Zusammenfassung des Audit-Log
aureport
  • grafische Auswertung von Audit-Logs
$ sudo apt install graphviz gnuplot git
$ git clone https://github.com/cstrotm/audit-visualize
$ cd audit-visualize
$ chmod +x ./mkgraph
$ chmod +x ./mkbar
  • grafische Reports erstellen
$ sudo aureport -s -i --summary  | bash ./mkbar syscall
$ sudo aureport -f -i --summary --failed | bash ./mkbar failed-access
$ sudo aureport -e -i --summary | egrep -vi '(syscall|change)'
$ sudo aureport -e -i --summary | egrep -vi '(syscall|change)' | bash ./mkbar events2
  • Syscall Benutzung von Programmen
$ sudo aureport -s -i | awk '/^[0-9]/ { printf "%s %s\n", $6, $4 }' | sort | uniq | bash ./mkgraph
Gzipping graph...
Graph was written to gr.png
  • welcher Benutzer führt welche Programme aus?
sudo aureport -u -i | awk '/^[0-9]/ { printf "%s %s\n", $4, $7 }' | sort | uniq | bash ./mkgraph
  • wer greift auf welche Dateien zu?
sudo aureport -f -i | awk '/^[0-9]/ { printf "%s %s\n", $8, $4 }' | sort | uniq | bash ./mkgraph

3.5.2 CIS Benchmarks mit Vorlagen für Audit-Regeln

3.6 Namespaces und Seccomp-bpf

  • Dieses Kapitel zeigt, das Namespaces (und damit Container) eine Standard-Technologie des Linux-Kernel ist und keiner weiteren Software benötigt. systemd-nspawn und auch Docker sind nur Verwaltungsprogramme für die Namespaces und Controll-Groups des Linux-Kernels.
  • Namepspaces 'virtualisieren' die Sicht auf Ressourcen des Linux-Kernels
  • Programme wie Docker, 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
IPC CLONE_NEWIPC System V IPC, POSIX message queues
Network CLONE_NEWNET Network devices, stacks, ports, etc.
Mount CLONE_NEWNS Mount points
PID CLONE_NEWPID Process IDs
User CLONE_NEWUSER Benutzer und Gruppen IDs
UTS CLONE_NEWUTS Hostname und NIS Domain Name

3.6.1 Namespace Beispielprogramm

apt install build-essential
  • Unser Ausgangs-C-Programm: Leeres Verzeichnis anlegen und die leere Quelltextdatei im Editor öffnen
cd /usr/src
mkdir namespaces
cd namespaces
$EDITOR namespaces.c
----
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h>

#define STACK_SIZE (1024 * 1024)

static char child_stack[STACK_SIZE];
char* const child_args[] = {
  "/bin/bash",
  NULL
};

int child_main(void* arg)
{
  printf("Namespace aktiv\n");
  execv(child_args[0], child_args);
  printf("Ooops\n");
  return 1;
}

int main()
{
  printf("Namespace wird erzeugt\n");
  int child_pid = clone(child_main, child_stack+STACK_SIZE, \
   CLONE_NEWUTS | CLONE_NEWIPC | SIGCHLD, NULL);
  waitpid(child_pid, NULL, 0);
  printf("Namespace beendet\n");
  return 0;
}

----
  • Programm übersetzen
gcc -o namespaces namespaces.c
  • Programm ausführen
./namespaces
  • den Namespace mit exit verlassen, dann einen Process-Namespace zum Programm hinzufügen
...
 int child_pid = clone(child_main, child_stack+STACK_SIZE, \
   CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID | SIGCHLD, NULL);
...
  • neu übersetzen und ausführen
gcc -o namespaces namespaces.c
  • Einen Netzwerk-Namespace hinzufügen
...
  int child_pid = clone(child_main, child_stack+STACK_SIZE, \
   CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWPID |  CLONE_NEWNET | SIGCHLD, NULL);
...

3.7 Netzwerk Namespaces per ip Kommando

  • Netzwerk Namespace netns1 erzeugen
# ip netns add netns1
# ip netns list
  • Befehl im Netzwerk-Namespace ausführen
# ip netns exec netns1 ip link list
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
  • Loopback ist "down", ein ping geht nicht
# ip netns exec netns1 ping 127.0.0.1
connect: Network is unreachable
  • Nun bringen wir das Loopback-Interface "up"
# ip netns exec netns1 ip link set dev lo up
# ip netns exec netns1 ping 127.0.0.1
  • Wir erstellen nun virtuelle Netzwerkschnittstellen
# ip link add veth0 type veth peer name veth1
# ip link
  • Netzwerkschnittstelle veth1 wird in den Netzwerk-Namespace netns1 verschoben
# ip link set veth1 netns netns1  # auch physische NICs koennen in Namespaces verschoben werden
# ip link
  • Wir konfigurieren ein IP-Adresse auf veth1 im Namespace
# ip netns exec netns1 ifconfig veth1 10.1.1.1/24 up
# ip netns exec netns1 ip a
# ping 10.1.1.1  # ping in den Namespace geht jetzt noch nicht!
  • Eine IP-Adresse auf dem veth0 auf dem Linux-Host konfigurieren
ifconfig veth0 10.1.1.2/24 up
  • Ping von aussen in den Namespace geht jetzt!
# ping 10.1.1.1
  • Ping aus den Namespace nach draussen sollte auch gehen
ip netns exec netns1 ping 10.1.1.2
  • der Namespace hat eine eigene Routing-Tabelle und eine eigene Netfilter-Firewall
ip netns exec netns1 route
ip netns exec netns1 ip route show
ip netns exec netns1 iptables -L
  • Namespace wieder löschen
ip netns delete netns1

3.8 Einfache Container mit unshare

  • mit dem Programm unshare können die einzelnen Namespaces separat erstellt und eingeschaltet werden. Wird kein zu startendes Programm angegeben, so wird die Standard-Shell des Systems mit den neuen Namespaces gestartet. Beispiel: ein Namespace mit privatem Netzwerk, Mount- und Prozess-ID- Namespace:
    # unshare -n -p -f --mount-proc
    
  • Container mit Benutzer root, welcher nicht root auf dem Host ist
    sudo unshare --map-root-user --user sh
    

3.9 Container/Namespace mit Systemd

  • Ab Debian 9 müssen die Systemd-Container-Tools extra installiert werden:
apt install systemd-container
  • wir erstellen ein Verzeichnis für den neuen Container
mkdir -p /srv/container/centos7
  • initialisieren eine neue RPM-Instanz im Container-Verzeichnis (bei Debian mit debboostrap, bei SUSE mit RPM und zypper)
apt -y install rpm yum
rpm --root /srv/container/centos7 --initdb
  • Das Basis-Paket für CentOS 7 laden
# Im Internet
wget http://mirror.centos.org/centos/7/os/x86_64/Packages/centos-release-7-7.1908.0.el7.centos.x86_64.rpm
# Im Linuxhotel
wget http://centos.linuxhotel.de/7/os/x86_64/Packages/centos-release-7-7.1908.0.el7.centos.x86_64.rpm
rpm --nodeps --root /srv/container/centos7 -ivh  centos-release-7-7.1908.0.el7.centos.x86_64.rpm
  • das CentOS Basis-System installieren
yum -y --nogpg --releasever=7 --installroot=/srv/container/centos7 \
  install systemd passwd yum procps-ng iproute tmux
echo 7 > /srv/container/centos7/etc/yum/vars/releasever
  • eine Shell im Container starten
systemd-nspawn -D  /srv/container/centos7
  • Root-Passwort setzen und neuen Benutzer anlegen
-bash-4.2$ passwd
-bash-4.2$ adduser nutzerXX
-bash-4.2$ passwd nutzerXX
  • Shell im Container verlassen
-bash-4.2# exit
  • Container booten
systemd-nspawn -bD  /srv/container/centos7
  • Container stoppen (im Container eingeben)
conainter# poweroff
  • Container mit privatem Netzwerk-Namespace:
systemd-nspawn -bD  /srv/container/centos7 --private-network

Container mit eigener (virtuellen) Netzwerkkarte (neue MAC-Adresse)

systemd-nspawn --network-macvlan=enp1s0f1 -M centos01 \
  -bD /srv/container/centos7
container# ip links
container# dhclient mv-enp1s0f1
container# ip address show
container# ping 1.1.1.1
  • Container vom Host kontrollieren
machinectl list
machinectl status centos
machinectl terminate centos
machinectl kill centos
  • 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

3.10 Firejail

  • Firejail installieren
apt install firejail
  • Firefox im Jail starten
firejail firefox
  • Firejail Wrapper-Skripte installieren. Die Wrapper Skripte überlagern die Programm-Namen von schützenswerten Linux-Anwendungen.
firecfg
  • Firejail Programme überwachen (in einem separaten Terminal starten und dann z.B. Firefox starten)
firemon
  • Firejail Profile auflisten
ls /etc/firejail

4 Tag 4

4.1 Docker

4.1.1 Docker als Sicherheits-Werkzeug

  • Docker bietet gegenüber dem Betrieb von klassischen Linux-Installationen (auf „bare Metal“ oder virtualisiert) einige Vorteile:
    • Anwendungen werden voneinander isoliert. Selbst die Bestandteile einer Anwendung (z.B. LAMP - Linux/Apache/MySQL/PHP) können in eigene Docker-Container ausgelagert und voneinander isoliert werden.
    • die Rechte einer Anwendung im Docker-Conatiner kann per Seccomp-bpf und Linux-Capabilities beschränkt werden
    • die Sicht auf den Netzwerkstack kann für Anwendungen eingeschränkt werden (Admin-Netzwerk für Anwendungen nicht sichtbar)
    • Docker unterstützt ein Implementations-Design, bei dem Anwendungs-Programme und Daten getrennt gehalten werden (Anwendungen in Container-Images, Daten in Speicher-Volumes ausserhalb der Container)
    • Über Dockerfiles sind die Container jederzeit reproduzierbar. Bei einem Einbruch in einen Docker-Container kann dieser zu Beweiszwecken eingefroren werden und durch ein neues, sauberes Container-Image ersetzt werden
    • Docker-Container werden nicht per Software-Update aktualisiert, sondern durch ein neues Container-Image mit aktueller Software ersetzt. Hierdurch kann sich ein (auch bisher unbemerkter) Einbrecher nicht im System festsetzen
  • Nachteile:
    • die Isolierung von Anwendungen durch Linux-Container-Virtualisierung ist nicht so stark und vollständig wie bei einer Voll-Virtualisierung (VMWare, HyperV, VirtualBox). Linux-Container können jedoch innerhalb einer Voll-Virtualisierung eingesetzt werden und somit werden die Vorteile beider Systeme genutzt
    • Anwendungs-Installationen müssen angepasst werden, um Programme und Daten sauber zu trennen
    • Docker erhöht die Komplexität der Installation
    • Docker-Verwaltungswerkzeuge (Kubernetes etc) erhöhen die Komplexität und haben ggf. eigene Sicherheitsprobleme

4.1.2 Docker Installieren

  • Docker installieren und starten
apt install docker.io
  • Docker testen
docker run hello-world

4.1.3 erste Schritte mit Docker

  • Docker Image herunterladen
# docker pull centos
# docker images
  • Ein Centos mit Bash starten
# docker run --name=application1 -it centos /bin/bash
docker# yum update -y
docker# yum install -y procps httpd
docker# yum clean all
docker# exit
# docker ps -a
  • mit laufendem Docker Container verbinden
docker attach application1
  • Docker Container Statistiken
# docker top <container>
# docker stats <container1> <container2> ...
  • einen weiteren Prozess im Docker Container ausfuehren
# docker exec -d <container> touch /etc/new-config
# docker exec -t -i <container> /bin/sh
  • Docker Container stoppen
# docker stop application1
  • Docker bei Applikations-Ende automatisch neu starten
docker run --restart=always
docker run --restart=on-failure:5
docker inspect <container>
docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container>
  • Docker Container im Hintergrund (-d) starten
docker run --name <name> -d <image> <command>
  • Ausgaben der Anwendung im Docker Container anschauen
docker logs <container>
docker logs -f <container>
docker logs --tail -f <container>
docker logs --tail 50 <container>
  • Docker log drivers (json-file, syslog, none, …)
docker run --log-driver="syslog" ...
  • Docker image bauen
mkdir -p docker-work
cd docker-work
vi Dockerfile
-----
# Debian with nginx
# Version 1
FROM debian:latest

MAINTAINER Linuxhotel Trainer

# Update image
RUN apt update
# Add httpd package. procps and iproute are only added to investigate the image later.
RUN apt -y install nginx
RUN echo container.example.com > /etc/hostname

# Create an index.html file
RUN bash -c 'echo "Your Web server test on Debian Docker Container is successful." >> /var/www/html/index.html'
# create a nginx start-command
CMD ["nginx", "-g", "daemon off;"]
------
docker build -t debian-nginx --no-cache .
docker run -d -t --name=deb-nginx1 -p 8080:80 debian-nginx

4.1.4 Docker Container verbinden

  • Volumes
docker run -v <local-dir>:<container-dir>[:ro] ...
  • Volumes im Dockerfile definieren. Hierdurch werden Volumes beim Erstellen des Containers ausserhalb des Containers erstellt (unter /var/lib/docker/... auf dem Linux-Host) und in den Container eingebunden
# Dockerfile
[...]
VOLUME /data
VOLUME /var/www/html

4.1.5 Docker Übung: Caddy-Webserver:

  • installiere ein Image mit dem 'Caddy' Webserver (https://caddyserver.com) aus der Docker Hub Registry (abiosoft/caddy). Caddy ist ein in der Sprache go geschriebener Webserver, welcher automatisch TLS/x509 Zertifikate von Let's Encrypt besorgt und aktualisiert.
    • starte einen Container aus dem Image, mappe Port 2015 vom Container auf einen freien Port auf dem Host
    • teste die Caddy-Webseite mit dem Firefox auf dem Laptop http://localhost:<port>
    • starte eine Shell im Caddy-Webserver Container per docker exec. Suche das Verzeichnis mit den HTML-Dateien.
    • stoppe den Container, lösche den Container (docker rm)
    • erstelle ein leeres Verzeichnis auf dem Container-Host (Laptop) unter /srv/websites/html. Erstelle eine einfache HTML-Datei index.html in diesem Verzeichnis.
    • starte einen neuen Container, verbinde das Verzeichnis /srv/websites/html vom Container-Host in den Container (Parameter -v, siehe oben).
    • Teste den Webserver mit dem Firefox Browser
    • Ändere die Datei index.html unter /srv/websites/html, teste das die Änderungen im Browser sichtbar sind
    • Lösche den NGINX-Container
    • erstelle einen neuen NGINX Container, bei dem das Verzeichnis /srv/websites/html in den Webroot des NGINX-Webservers gemappt wird (Achtung: anderen Port als für den Caddy-Webserver benutzen. Beide Webserver-Container sollten zur gleichen Zeit aktiv sind)
    • Einige Befehle zum Probieren (als Benutzer root auf dem Host)
docker logs <container>   # Anzeige der Logausgaben des Hauptprozesses
docker top <container>    # Anzeige der Prozesse im Container
docker stats <container>  # Anzeige der vom Container benutzten Resourcen
  1. Beispiellösung
    • Caddy-Webserver Image aus dem Docker Repository
    docker run --name=caddyweb -d -p 8088:2015 abiosoft/caddy
    
    • Im Docker-Container nach den HTML-Dateien suchen
    docker exec caddyweb find / -name index.html
    
    • Caddy Container stoppen und löschen
    docker stop caddyweb
    docker rm caddyweb
    
    • Verzeichnis für die Webseiten anlegen und eine index.html Datei erstellen
    mkdir -p /srv/websites/html
    echo "Dies ist meine Webseite" > /srv/websites/html/index.html
    
    • Caddy Container mit dem externen Webseiten-Verzeichnis starten
    docker run --name=caddyweb -v /srv/websites/html:/srv -d -p 8088:2015 abiosoft/caddy
    
    • NGINX Container stoppen und löschen
    docker stop debian-nginx
    docker rm debian-nginx
    
    • NGINX Container mit dem Web-Verzeichnis starten
    docker run --name=debian-nginx -v /srv/websites/html:/var/www/html -p 8080:80 debian-nginx
    

4.1.6 mit Docker Images arbeiten

  • Docker Image History anschauen
docker images
docker history <image-name>
  • Docker Image(s) in eine Datei sichern. Metadaten und Schichten bleiben erhalten
docker save -o mydockerapp.tar <image-name> [<image-name2>]
  • Docker-Image aus einer Datei laden (welche mit docker save erstellt wurde)
docker load -i mydockerapp.tar
  • Docker Container exportieren (nur das Dateisystem wird exportiert, die Schichten/Layer verschwinden)
docker ps -a
docker export -o mydockerapp.tar <container-name>
xz -v mydockerapp.tar
  • Docker Container vom Tarball importieren
xzcat mydockerapp.tar.xz | docker import - <image-name>
  • Docker Container in ein Image umwandeln (z.B. für forensische Analyse)
docker commit <container-ID> <image-name>

4.1.7 lokale Docker Registry

  • Docker registry als Docker-Container starten. Die Docker Registry benutzt Port 5000/tcp.
docker run -d -p 5000:5000 registry
  • Docker image „taggen“
docker images
docker tag <images-id> localhost:5000/debian-nginx:01
  • Image in die Registry laden (push)
docker push localhost:5000/debian-nginx:01
  • Docker Image lokal löschen (Achtung: Docker Images können erst gelöscht werden, wenn keine aktiven under inaktiven Container dieses Image benuzten. Ggf. müssen erst die auf diese Images basierenden Container per docker rm <container> gelöscht werden)
docker rmi localhost:5000/debian-nginx:01
docker rmi debian:latest
docker images
  • Image aus der lokalen Registy (neu) laden
docker pull localhost:5000/debian-nginx:01
  • wenn die Kommunikation zwischen Registry und Docker-Client nicht über die Loopback-Schnittstellen läuft (nicht localhost), dann muss diese Kommunikation mit TLS/SSL abgesichert sein. Dazu muss auf der Registry ein x509 Zertifikat installiert werden. Optionen:
    • von Let's Encrypt ein x509 Zertifikat besorgen und einbauen. Da Let's Encrypt Zertifkate nur 3 Monate gültig sind, sollte dieser Prozess automatisiert und überwacht sein
    • ein x509 Zertifikat von einer Zertifizierungsstelle (CA) kaufen
    • Option ohne TLS: per SSH einen Tunnel von Port 5000 des Clients auf Port 5000 des Registry-Hosts aufbauen und den Docker-Client gegen den „localhost“ Port laufen lassen. Bonus: per Systemd-Socket-Activation auf dem Client kann der SSH-Tunnel automatisch aufgebaut werden.
  • Liste der Images in einer lokalen Registry laden und anzeigen
curl -X GET http://localhost:5000/v2/_catalog | python -m json.tool

4.2 Docker Sicherheit

4.2.1 Docker Images

4.2.2 Container Sicherheit

  • keine SUID Programme im Container
  • keinen "echten" Benutzer "root" (UID0) im Container (Capabilities benutzen)
  • Anwendungen im Container nicht unter einem Root-Account betreiben
  • Capabilities sparsam einsetzen (kein CAP_SYS_ADMIN)
  • Dateien in /tmp sind keine Daten, nicht von extern mounten
  • Programme (im Container) und Daten (ausserhalb des Containers) trennen
  • sensitive Programme read-only in den Container mappen/mounten
  • Container auf bekannte Sicherheitslücken der im Container benutzen Software überwachen
  • Container oft und regelmässig auffrischen

4.2.3 Audit-Regeln fuer Docker einrichten

  • Beispiel-Regelwerk (generisch, muss ggf. für die eigene Repository angepasst werden)
 -w /etc/docker -k docker
-w /etc/docker/key.json -k docker
-w /etc/docker/daemon.json -k docker
-w /etc/docker/certs.d -k docker
-w /etc/sysconfig/docker -k docker
-w /etc/sysconfig/docker-network -k docker
-w /etc/sysconfig/docker-storage -k docker
-w /etc/sysconfig/docker-storage-setup -k docker
-w /usr/lib/systemd/system/docker.service -k docker
-w /usr/bin/docker-containerd -k docker
-w /usr/bin/docker-containerd-current -k docker
-w /usr/bin/docker-containerd-shim -k docker
-w /usr/bin/docker-containerd-shim-current -k docker
-w /usr/bin/docker -k docker
-w /usr/bin/docker-current -k docker
-w /usr/bin/docker-ctr-current -k docker
-w /usr/bin/dockerd -k docker
-w /usr/bin/dockerd-current -k docker
-w /usr/bin/container-storage-setup -k docker
-w /var/lib/docker -k docker
-w /var/run/docker.sock -k docker

4.2.4 Docker-Engine nicht auf einem (ungeschützten) TCP Port betreiben

4.2.5 Docker Repository mit TLS absichern (x509 Zertifikate benötigt)

  • in der Datei /etc/sysconfig/docker
dockerd --tlsverify ...

4.2.6 User-Namespaces verwenden

  • in der Datei /etc/sysconfig/docker
dockerd --userns-remap=default ...

4.2.7 Legacy-Registry abschalten

  • in der Datei /etc/sysconfig/docker
dockerd --disable-legacy-registry ...

4.2.8 Ulimits auf Container setzen

  • in der Datei /etc/sysconfig/docker, Format --default-ulimit <ulimit-spec>=<soft-limit>:<hard-limit>
  • Information ueber ULIMIT Schluesselwoerter in /etc/security/limits.conf
  • Default-Ulimit setzen
dockerd --default-ulimit nofile=50:150 --default-ulimit nproc=10:20
  • Ulimit fuer einzelnen Container
docker run --ulimit nofile=20:50 ...

4.2.9 Docker Container nicht als Benutzer root betreiben

docker run -u dockeruser -d ...

4.2.10 Capabilities von Container-Prozessen beschraenken

  • Docker Container mit beschraenkten Capabilities starten
docker run --cap-drop=all --cap-add [notwendige capabilities] ...
  • Capabilities eines Prozesses in einem Docker Container herausfinden am Beispiel des nginx Webservers
 # Prozess-ID des nginx Prozesses herausfinden
docker exec $(docker ps -q) pgrep -a nginx
# Capabilities der Prozess-ID 1 (nginx) abfragen
docker exec $(docker ps -q) cat /proc/1/status | grep CapEff | awk '{print $NF}'
00000000a80425fb
# Capabilties dekodieren und Docker-Parameter ausgeben
capsh --decode=00000000a80425fb | echo "--cap-drop=all --cap-add={$(sed -e 's/.*=//' -e 's/cap_//g')}"
--cap-drop=all --cap-add={chown,dac_override,fowner,fsetid,kill,setgid,setuid,setpcap,net_bind_service,net_raw,sys_chroot,mknod,audit_write,setfcap}
  • Die Capabilities unter –cap-add pruefen und ggf. einschraenken

4.2.11 /etc/localtime im Container Read-only

  • die Datei /etc/localtime im Container Read-Only setzen, verhindert, das die Zeitzone im Container veraendert wird
docker run -v /etc/localtime:/etc/localtime:ro

4.2.12 Speicher- und CPU-Resources des Containers einschraenken

Docker Parameter Beschreibung
–cpu-period Laenge der CPU CFS (Completely Fair Scheduler) Zeitscheibe
–cpu-quota CPU CFS (Completely Fair Scheduler) quota
–cpuset-cpus Container an CPUs binden (0-3, 0,1).
–cpuset-mems Container an Speicher-Nodes binden (0-3, 0,1) nur fuer NUMA Maschinen
–kernel-memory Kernel Speicher-Limit
-m, –memory Speicher Limit
–memory-reservation Limit fuer Speicherreservierungen (Virtueller noch nicht benutzer Speicher)
–memory-swap Vollstaendiges Speicherlimit (memory + swap), '-1' schaltet virtuellen Speicher (SWAP) aus

4.2.13 Dateisysteme "nur-lesbar" einbinden

  • Das Root-Dateisystem im Container „nur-lesend“ einbinden
docker run --read-only ...
  • Container Volume nur-lesend einbinden
docker run -v /volume:ro ...
  • Pfade des Hosts nur-lesend in den Container einbinden
docker run -v /srv/container/name:/srv:ro ...

4.2.14 Fähigkeiten eines Docker Containers per SELinux einschränken

Für Linux-Sicherheits-Module (LSM) auf Label Basis (Role Based Access Control, RBAC) kann der Benutzer, die Rolle, der Tyoe und der Level für einen Container festgelegt werden. LSM funktionieren nicht innerhalb von Containers, sondern nur für einen gesamten Container.

docker run --security-opt=[label=user:USER]

4.2.15 Fähigkeiten eines Docker Containers per AppArmor einschränken

4.2.16 Syscalls einschränken mit Seccomp

4.2.17 Vorsicht bei dem Einsatz von KSM (Kernel-Same-Pages-Merging)

# systemctl status ksm
● ksm.service - Kernel Samepage Merging
   Loaded: loaded (/usr/lib/systemd/system/ksm.service; enabled; vendor preset: enabled)
   Active: active (exited) since Mi 2017-10-25 19:20:35 CEST; 13h ago
  Process: 777 ExecStart=/usr/libexec/ksmctl start (code=exited, status=0/SUCCESS)
 Main PID: 777 (code=exited, status=0/SUCCESS)
    Tasks: 0
   Memory: 0B
   CGroup: /system.slice/ksm.service

Okt 25 19:20:35 notebook51 systemd[1]: Starting Kernel Samepage Merging...
Okt 25 19:20:35 notebook51 systemd[1]: Started Kernel Samepage Merging.
# /usr/libexec/ksmctl stop
# echo 0 > /sys/kernel/mm/ksm/run
# systemctl disable ksm
# systemctl disable ksmtuned
# echo 2 > /sys/kernel/mm/ksm/run

4.3 Incidence Response

4.3.1 Vorbereitungen auf den Notfall

  • Forensik Live-Linux-CD-ROM bereithalten und regelmässig üben (1/2 jährlich)
  • Kontaktadressen von Forensikern/Strafverfolgungsbehörden bereithalten
  • das Erstellen von Platten-Images üben
  • PGP/GPG Schlüssel erstellen und aktuell halten. PGP/GPG-E-Mail regelmässig benutzen, um in Übung zu bleiben
  • Ersatz-Hardware vorhalten (Festplatten, SSD), Speicherplatz für Platten-Images
  • Beutel für manipulationssichere Beweissicherung bestellen und "vor Ort" bereithalten
  • Hardware-RAID meiden (Alternativen: Software RAID - DM-RAID, btrfs, zfs)
  • alternative Kommunikationswege (Twitter, externes E-Mail-System, externer Web-Server) vorbereiten. Kunden über diese alternativen Kommunikationswege informieren!
  • Minimale-Installationen benutzen
  • Datensparsamkeit - nur Daten, die nicht gespeichert werden, können nicht mißbraucht werden -> Datenhaltung kostet
  • Incident-Modus für die IT-Systeme definieren –> welche Teile der IT können im Fall eines Angriffs ohne starke Auswirkungen auf den Firmenbetrieb ausgeschaltet oder abgeschottet werden
  • Incident-Modus automatisiert per Configuration-Management System anschalten –> Testen

4.3.2 Kunden/Benutzer im Falle eines Sicherheitsvorfalls informieren

  • wenn die eigene Kommunikationsstruktur (z.B. E-Mail) betroffen ist, die alterntaiven Kommunikationswege benutzen
  • dokumentierte Notfall-Prozedur zur Kunden-Kommunikation vorhalten und verteilen
  • bei erkannten Angriffen 4 Augen-Prinzip beachten, nicht alleine arbeiten!

4.3.3 Spurensicherung

  • VM Snapshots erstellen (per GPG signieren)
  • von einem Forensik-Linux booten (Kali-Linux, Deft o.ä.)
  • revisionssichere Images (dcfldd, aff4) erstellen http://www.osforensics.com/tools/create-disk-images.html
    apt install dcfldd
    dcfldd if=/dev/sda1 hash=sha256 hashwindow=100M sha256log=sha256.txt \
      hashconv=after bs=512 conv=noerror,sync split=100M splitformat=aa of=sda1.dd
    
  • Images sofort nach der Erstellung per GPG signieren (mit externer Signaturdatei)
  • erst Platten-Images erstellen, dann Log-Dateien etc. anschauen
  • Festplatten ausbauen und durch fabrikneue Platten ersetzen, Festplatten mit kompromittiertem System versiegeln (lassen)
  • Analyse des Einbruches auf den Read-Only-Dateisystem-Images (nicht auf den echten Platten, die echten Platten sind ein Beweismittel und dürfen nach der Versiegelung nicht angefasst werden)

4.3.4 Backup/Rebuild eines kompromitierten Systems

  • Server komplett neu aufsetzen (Docker dockerfile, Foreman/Ansible/Salt etc, VM Templates)
  • Festplatte(n) austauschen
  • nur Daten ohne ausführbare Teile (Maschinencode, Java, .NET, Python pyc, PHP, Scripte etc) zurückspielen
    • bei der Einrichtung der Server schon auf eine Trennung von System, Anwendungen und Daten achten!
  • Scripte neu erstellen, von einem bekannt sauberen Backup zurückspielen oder einzeln per Pair-Review (2 Personen Review) prüfen

4.4 Rootkits aufspüren

  • Root-Kit-Hunter installieren
apt install rkhunter
  • WEB_CMD in der Datei /etc/rkhunter.conf setzen
WEB_CMD=/usr/bin/curl
  • Root-Kit tests updaten
rkhunter --update
  • RKHunter Baseline Datenbank erstellen
rkhunter --propupd
  • nach Root-Kits suchen
rkhunter --check
less /var/log/rkhunter.log

4.5 Intrusion Detection

4.5.1 rhash

  • rhash Installation
apt install rhash
  • Datenbank mit den Hash-Werten erstellen (Hash=SHA3/Keccak mit 224 bit, Datenbankformat wie bei BSD)
rhash -r --sha3-224 --bsd /etc -o hash.lst
  • Datenbank anschauen
less hash.lst
  • Datenbank sichern (in Produktionsumgebung)
scp hash.lst benutzer@sicherer-server.example.com
  • Dateien gegen die Datenbank prüfen
rhash -r -c --bsd hash.lst
  • Die Benutzer-Authentisierungsdateien durch Anlegen eines neuen Benutzers ändern
adduser intruder
  • nochmal Dateien gegen die Datenbank prüfen
rhash -r -c --bsd hash.lst
  • Nur die "nicht-OK" Dateien anzeigen
rhash --skip-ok -r -c --bsd hash.lst

4.5.2 AIDE

  • Installation
apt install aide
  • Konfiguration anschauen und ggf. ändern
$EDITOR /etc/aide/aide.conf
update-aide.conf
less /var/lib/aide/aide.conf.autogenerated
  • AIDE Datenbank erstellen (Dauer ca. 3-6 Minuten auf den Linuxhotel Laptops)
aideinit
  • Ausgabe von aideinit
Start timestamp: 2019-11-13 07:58:08 +0100 (AIDE 0.16.1)
AIDE initialized database at /var/lib/aide/aide.db.new
Verbose level: 6

Number of entries:      178250

---------------------------------------------------
The attributes of the (uncompressed) database(s):
---------------------------------------------------

/var/lib/aide/aide.db.new
  RMD160   : w/xQIwY8je09TrZAMFOFN2JqAY4=
  TIGER    : MRqyf0hq+8N+Kv/wILF7v0X3HEFcvhdv
  SHA256   : 8Jvgtqq0amatTf+Tj0o42W2HYne6Jf2g
             i5PClJmWv04=
  SHA512   : OWGC4zRYKyBTEg9fBBDgUCLVDoERZjve
             GI5EdEPCoGXwRnTR6tBP67tAa9tj7llh
             bd2O2CbTZ08pVI9AQ22Q2g==
  CRC32    : B9czIg==
  HAVAL    : 9GLjw3cDS/k2qpnZhoiZHmKYoSMZ1I3h
             mL2pUnyMoCQ=
  GOST     : mfBemqOHFSzWHf49h33R7gysjoLA4JiH
             oA+P3T9v6xU=

End timestamp: 2019-11-13 08:01:19 +0100 (run time: 3m 11s)
  • Datenbank sichern
scp  /var/lib/aide/aide.db.new.gz user@secure-machine
  • eine unbefugte Änderung am System simulieren
groupadd intruders
  • AIDE Check laufen lassen
aide --check -c /var/lib/aide/aide.conf.autogenerated

4.5.3 Samhain

  • Installation
apt install samhain
  • Samhain internen Schlüssel ergänzen (<key> sollte durch eine lange, zufällige Zahl ersetzt werden)
systemctl stop samhain
samhain --add-key=<key>@/usr/sbin/samhain
cp /usr/sbin/samhain.out /usr/sbin/samhain
rm -f /var/lib/samhain/samhain_file
systemctl start samhain
  • Samhain Konfigurationsdatei anschauen/anpassen. Die Samhain Konfigurationsdatei ist noch nicht auf Debian 10 angepasst und sollte, um Fehlmeldungen zu vermeiden, an einigen Stellen (z.B. /bin, /sbin, /lib) geändert werden
$EDITOR /etc/samhain/samhainrc
  • Datenbank auf einen sicheren Server kopieren (in Produktionsumgebungen)
scp /var/lib/samhain/samhain_file nutzer@secure-host:.
  • Samhain ist als Service-Dämon gestartet
systemctl status samhain
  • Test Passwort-Dateien ändern
# test, Datei (/etc/shadow und /etc/passwd) ändern
passwd nutzerXX
  • Check mit Ausgaben nur Level "crit"
samhain -t check --foreground -l none -p crit
  • Samhain Datenbank aktualisieren
samhain -t update --foreground

4.6 Lynis

  • Lynis ist ein Sicherheitsscanner welcher veraltete Software-Versionen und bekannt unsichere Konfigurationen in Linux-Installationen aufspürt
  • Homepage: https://cisofy.com/lynis/
  • Lynis installieren (die lynis Version in den Debian Paket-Repositories ist veraltet)
sudo apt install dirmngr
sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys C80E383C3DE9F082E01391A0366C67DE91CA5D5F
sudo apt install apt-transport-https
echo "deb https://packages.cisofy.com/community/lynis/deb/ stable main" | sudo tee /etc/apt/sources.list.d/cisofy-lynis.list
apt update
apt install lynis
  • Lynis Version Information abfragen
lynis update info
  • Lynis starten
lynis audit system
  • Lynis Informationen anzeigen
lynis show help
lynis show commands
lynis show settings
lynis show profiles
lynis --man-page
  • In der Produktion: Lynis als Cron-Job (oder Systemd-Timer-Unit) eingerichten und periodisch ausführen, Ausgaben/Reports per E-Mail versenden

4.7 LSM - Linux-Securty-Modules

4.7.1 YAMA

  • YAMA sammelt Sicherheitsfunktionen aus Linux-Kernel-Sicherheitspatches, welche nicht in eine der anderen Linux-Sicherheits-Module passen
  • in aktuellen Linux-Kerneln bietet mit YAMA einen Schutz gegen ptrace (ptrace_scope), d.h. das ein Prozess den Speicher und die Ausführung eines anderen Prozesses überwachen kann (diese Funktion wird für die Benutzung von Debuggern bei der Programmentwicklung benötigt). Auf Produktions-Serversystemen ist diese Funktion oft nicht benötigt.
  • Werte für ptrace_scope
    Wert Beschreibung
    0 normales Verhalten, jeder Prozess und Benutzer kann andere Prozesse überwachen
    1 ein Prozess kann nur einen eigenen Kind-Prozess überwachen
    2 nur ein Systemadministrator mit CAP_SYS_PTRACE kann Prozesse überwachen
    3 keine Überwachen von Prozessen mittels ptrace möglich, diese Einstellung kann im laufenden System nicht überschrieben werden
  1. Beispiel (Firefox als nutzerXX gestartet):
    root$ apt install strace
    nutzerXX$ strace -p $(pgrep firefox)
    root$ echo "1" > /proc/sys/kernel/yama/ptrace_scope
    nutzerXX$ strace -p $(pgrep firefox)
    strace: attach: ptrace(PTRACE_ATTACH, 3135): Operation not permitted
    

4.7.2 AppArmor

apt install apparmor apparmor-profiles apparmor-utils
  • AppArmor Status prüfen
aa-status
less  /etc/apparmor.d/bin.ping
  • Änderungen am Profil für ping, das ping Programm sollte danach keine ICMP(v6) Pakete versenden können
$EDITOR /etc/apparmor.d/bin.ping
---
[...]
  #include <abstractions/nameservice>

#  capability net_raw,
#  capability setuid,
  network inet raw,
[...]
  • Profil neu laden
apparmor_parser -r /etc/apparmor.d/bin.ping
  • Enforce-Modus für ein Programm setzen
# aa-enforce  /etc/apparmor.d/bin.ping
Setting /etc/apparmor.d/bin.ping to enforce mode.
  • Test des ping Programms:
# ping 8.8.8.8
ping: socket: Die Operation ist nicht erlaubt
# ping fe80::3e97:eff:feeb:a358%enp0s25
ping: socket: Die Operation ist nicht erlaubt
  • Complain-Modus für ein Programm setzen
aa-complain  /etc/apparmor.d/bin.ping
Setting /etc/apparmor.d/bin.ping to complain mode.
  • gestartete Programme ohne AppArmor Profil listen
# aa-unconfined
622 /usr/sbin/NetworkManager not confined
623 /usr/sbin/cupsd confined by '/usr/sbin/cupsd (enforce)'
630 /usr/sbin/avahi-daemon confined by '/usr/sbin/avahi-daemon (complain)'
650 /usr/sbin/cups-browsed confined by '/usr/sbin/cups-browsed (enforce)'
698 /usr/sbin/sshd not confined
1250 /usr/sbin/minissdpd not confined
1504 /usr/sbin/exim4 not confined
2013 /sbin/dhclient not confined
  • AppArmor Profil-Vorlage automatisiert erstellen. Die Vorlage muss immer manuell nachgearbeitet werden. (Aufgrund des Debian-Fehlers https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=928160 funktioniert aa-genprof nicht wenn das Paket apparmor-profiles installiert ist. Um aa-genprof zu benutzen erst dieses Paket deinstallieren apt remove apparmor-profiles und dann aa-genprof benutzen).
aa-genprof <programm>

4.7.3 SELinux

  • wir arbeiten auf einer VM im Internet. Benutzername nutzer und Passwort villa, Benutzer root hat das Passwort vogelsang.
Hostname IP-Adresse Teilnehmer
VM01 167.172.176.65 Moritz
VM02 165.22.94.139 Christian
VM03 104.248.44.206 Ulf
VM04 104.248.16.216 Andy
VM05 167.172.176.135 Svenja
VM06 167.172.176.60 Björn
VM07 104.248.134.210 Stefan
VM08 167.172.176.233 Nico
VM09 165.22.24.31 Carsten
  • SELinux Hilfspakete installieren
yum install policycoreutils setools libselinux-utils selinux-policy-doc setools-console
  \ policycoreutils-python selinux-policy-devel policycoreutils-newrole
  • SELinux Label (Context) auf Dateien/Prozesse/Benutzer anzeigen
ls -lZ <pfad>
ps -auxZ
id -Z
  • SELinux Status abfragen
getenforce
sestatus
sestatus -v
  • SELinux Module auflisten
semodule -l | less
  • Modul-Dateien (binär) und die fertige Policy
ls -l /etc/selinux/targeted/active/modules/100/
ls -lh /etc/selinux/targeted/policy/
  • SELinux Policy Quelldateien
ls -l /usr/share/selinux/devel/
  • Apache Webserver installieren und starten
yum install httpd
systemctl enable --now httpd
  • Kleine Webseite anlegen
$EDITOR /var/www/html/index.html
  • Inhalt der HTML-Datei
<html>
<body>
<h1>Apache Webserver</h1>
</body>
</html>
  • SELinux Security Context auf der Datei
ls -lZ /var/www/html/index.html
  1. SELinux und Benutzer
    • Benutzer nutzerXX in die Benutzerklasse user_u einfügen
    semanage login -l
    semanage user -l
    semanage login -a -s user_u nutzer
    # su oder sudo sollten nun für den Benutzer nicht mehr moeglich sein
    cat /etc/selinux/targeted/seusers
    semanage login -a -s guest_u nutzer
    getsebool allow_guest_exec_content
    setsebool allow_guest_exec_content off
    # scripts sind nun nicht mehr direkt ausführbar (indirekt über BASH oder SH trotzdem)
    
    • Fehlersuche
    ausearch -m avc -ts recent
    
  2. Aufgabe: Apache auf einem anderen Port als 80 oder 443
    • Schritte auf der VM ausführen
    • Ändere die Apache Konfiguration unter /etc/httpd/conf/httpd.conf so das der Apache auf Port 1234/TCP auf HTTP-Anfragen horcht (Konfigurationsparameter Listen: 1234)
    • versuche den Apache Webserver mittels systemd neu zu starten (systemctl restart httpd). Dies wird fehlschlagen, da httpd sich nicht auf Port 1234 binden kann.
    • schaue in per ausearch in die Audit-Logdatei nach SELinux Fehlern des Prozesses httpd
    • Benutze den Befehl semanage port um den Port 1234 für den Prozess httpd im SELinux freizuschalten und starte den Apache Webserver neu.
    • Teste, on der Firefox-Browser unter der URL http://vmXX.defaultroutes.de:1234/ die Webseite des Webservers sehen kann.
  3. Beispiellösung
    • Apache Konfiguration bearbeiten
    $EDITOR /etc/httpd/conf/httpd.conf
    
    • Apache Prozess neu starten. Dies soll Fehlschlagen, da SELinux die Benutzung von Port 1234 für den Prozess httpd verbietet
    • SELinux Fehler für httpd im Audit-Log suchen
    ausearch -m avc -c httpd -ts recent -i
    
    • Port 1234 für Apache in der SELinux Richtlinie freischalten
    semanage port -m -t http_port_t -p tcp 1234
    
    • Apache Webserver neu starten
    systemctl restart httpd
    
    • Änderungen mit semanage sind persistent, die Port-Änderung ist unter /etc/selinux/targeted/active/ports.local abgespeichert:
    # This file is auto-generated by libsemanage
    # Do not edit directly.
    
    portcon tcp 1234 system_u:object_r:http_port_t:s0
    

4.8 BIOS/UEFI Sicherheit

4.8.1 Intel Management Engine (ME)

4.8.2 Coreboot/Libreboot

Coreboot ist eine freie (open source) Firmware für Intel und ARM Rechner. Coreboot hiess früher LinuxBIOS.

4.9 Kernel Parameter

  • Kernel Parameter können auf der Kernel-Kommando-Zeile im Bootloader angegeben werden, um Kernel-Funktionen zu steuern

4.9.1 Audit-Subsystem

  • Audit-Subsystem anschalten
audit=[0|1]
  • wenn audit nicht gesetzt ist, dann wir das Audit-Subsystem erst mit dem Starten des Audit-Daemon auditd aktiv
  • Wert 0 – Audit-Subsystem ist ausgeschaltet
  • Wert 1 – Audit-Subsystem wird sofort aktiv, der Kernel sammelt Audit Informationen und übergibt diese an den Audit-Daemon, sobald dieser gestartet ist

4.9.2 AppArmor

apparmor=[0|1]
  • AppArmor an/ausschalten

4.9.3 SELinux

  • SELinux an/ausschalten
selinux=[0|1]
  • SELinux Regeln durchsetzen (enforcing)
enforcing=[0|1]
  • Wert 0 = SELinux im permissive Modus
  • Wert 1 = SELinux im enforcing Modus

4.9.4 Signierte Module

module.sig_enforce
  • wenn gesetzt, können nur Kernel-Module mit einer gültigen Signatur geladen werden

4.9.5 NOEXEC

noexec=[on|off]
  • bei 32bit PAE Kerneln, schaltet Schreibschutz auf Daten-Speicherseiten an

4.9.6 Datei-Capabilities

no_file_caps
  • Schaltet die Datei-Capabilities aus

4.9.7 Keine Module nachladen

nomodule
  • es können keine Module nachgeladen werden

4.9.8 Kernel-Panic

panic=<timeout>
  • Wert > 0 - Sekunden bis zum Reboot
  • Wert = 0 - kein Reboot (* aus Sicherheitsgründen empfohlen)
  • Wert < 0 - sofortiger Reboot

4.9.9 sysctl

sysctl -a
  • Permanente sysctl Einstellungen in /etc/sysctl.conf
kernel.randomize_va_space = 2

# Restrict core dumps
fs.suid_dumpable = 0

# Hide exposed kernel pointers
kernel.kptr_restrict = 1

#Prevent SYN attack, enable SYNcookies (they will kick-in when the max_syn_backlog reached)
# use iptables synproxy
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_max_syn_backlog = 4096

# Disables packet forwarding
net.ipv4.ip_forward = 0
net.ipv4.conf.all.forwarding = 0
net.ipv4.conf.default.forwarding = 0
net.ipv6.conf.all.forwarding = 0
net.ipv6.conf.default.forwarding = 0

# Disables IP source routing
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0

# Enable IP spoofing protection, turn on source route verification
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1

# Disable ICMP Redirect Acceptance
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0

# Enable Log Spoofed Packets, Source Routed Packets, Redirect Packets
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1

# Don't relay bootp
net.ipv4.conf.all.bootp_relay = 0

# Don't proxy arp for anyone
net.ipv4.conf.all.proxy_arp = 0

# Enable ignoring broadcasts request
net.ipv4.icmp_echo_ignore_broadcasts = 1

# Enable bad error message Protection
net.ipv4.icmp_ignore_bogus_error_responses = 1

5 Tag 5

5.1 CVE (Common Vulnerabilities and Exposures)

5.1.1 Common Vulnerability Scoring System (CVSS)

  1. CVSS V2
    • Access Vector (AV) - wie kann der Angreifer die Lücke ausnutzen
      • AV:L lokaler Angriff möglich
      • AV:A Angriff aus den gleichen Subnetz möglich
      • AV:N Remote über das Netzwerk
    • Access Complexity (AC) - wie schwierig ist es für einen Angreifer die Lücke auszunutzen, sobald er im System ist
      • AC:H Hoch
      • AC:M Medium
      • AC:L Niedrig
    • Authentication (Au) - wie oft muss sich der Angreifer am System anmelden/authentisieren muss
      • Au:M mehrfache Authentisierung notwendig (Benutzer -> sudo Root)
      • Au:S einmalige Anmeldung genügt (normaler Benutzer)
      • Au:N garnicht
    • Confidentiality Impact (C) - Auswirkung auf die Datensicherheit
      • C:N keine
      • C:P Teil-Daten
      • C:C alle Daten
    • Integrity Impact (I) - Auswirkung auf die Integrität des Systems
      • I:N keine Auswirkung
      • I:P System teilweise nicht mehr vertrauenswürdig
      • I:C System nicht mehr vertrauenswürdig
    • Availability Impact (A) - Auswirkung auf die Verfügbarkeit des Systems
      • A:N keine Auswirkung
      • A:P teilweise beeinträchtigung der Verfügbarkeit
      • A:C Komplettausfall des Systems
  2. CVSS V3
    • Angriffs Vektor (AV)
      • Network (N) - Angriff über Netzwerk möglich
      • Adjacent (A) - Angreifer muss im lokalen Netz sein
      • Local (L) - Angreifer muss lokal auf dem System sein
      • Physical (P) - Angreifer muss physischen Zugang zu dem System haben
    • Angriffs Komplexität (AC)
      • Low (L) - keine besondern Kentnisse beim Angreifer erforderlich
      • High (H) - der Angreifer braucht spezielle Kenntnisse
    • benötigte Privilegien (PR)
      • None (N) - keine besonderen Privilegien notwendig
      • Low (L) - Normale Benutzeranmeldung notwendig
      • High (H) - Angreifer benötigt spezielle Rechte
    • Unterstützung duch einen Benutzer (UI)
      • None (N) - der Angriff kann ohne Aktion eines lokalen Benutzers erforderlich sein
      • Required (R) - der Angriff benötigt die Unterstützung eines lokalen Benutzers
    • Scope (Reichweite) (S)
      • Unchanged (U) - die Sicherheitslücke beeinträchtigt nur das angegriffene System
      • Changed (C) - über die Sicherheitslücke werden auch andere Systeme als das angegriffene beeinträchtigt
    • Auswirkungen auf den Schutz von Daten (Confidentiality Impact) (C)
      • High (H) - Datenschutz kann nicht mehr sichergestellt werden
      • Low (L) - einige Daten können in die Hände des Angreifers fallen
      • None (N) - die Sicherheitslücke hat keine Auswirkungen auf den Schutz der Daten
    • Auswirkungen auf die Integrität der Daten (I)
      • High (H) - die Daten können vom Angreifer verändert werden und der Angreifer kann damit direkt Schaden verursachen
      • Low (L) - der Angreifer kann Daten verändern, hat aber keine Kontrolle über die Auswirkungen
      • None (N) - der Angreifer kann keine Daten verändern
    • Auswirkungen auf die Verfügbarkeit des Systems (A)
      • High (H) - das System fällt aus
      • Low (L) - es gibt Beeinträchtigungen der Verfügbarkeit
      • None (N) - die Sicherheitslücke hat keine Auswirkungen auf die Verfügbarkeit
    • Verfügbarkeit von Angriffscode
      • Undefiniert (X) - es liegen keine Informationen über die Verfügbarkeit von Schadcode vor
      • High (H) - es gibt automatisierte Angriffs-Programme
      • Functional (F) - es existiert Schadcode der manuell ausgeführt werden kann und in den meisten Fällen erfolgreich ist
      • Proof-of-Concept (P) - es gibt Beispiele für Schadcode, dieser ist jedoch auf dem meisten Systemen nicht einsetzbar
      • Unproven (U) - es gibt noch keinen Code oder die Sicherheitslücke ist nur theoretisch
    • Abhilfe
      • Undefiniert (X) - es liegen noch keine Informationen über Patche vor
      • Unavailable (U) - es gibt noch keinen Schutz gegen die Sicherheitslücke
      • Workaround (W) - es gibt keinen Fix, aber eine Anleitung die Ausnutzung der Sicherheitslücke zu verhindern
      • temporary fix (T) - es gibt einen vorläufigen Patch
      • official fix (O) - es gibt einen offiziellen Patch
    • Glaubwürdigkeit
      • Undefiniert (X) - es können keine Aussagen zur Glaubwürdigkeit der Meldung gemacht werden
      • Confirmed (C) - die Sicherheitslücke wurde bestätigt
      • Reasonable (R) - die Sicherheitslücke ist glaubhaft
      • Unknown (U) - es gibt Berichte über Sicherheitsprobleme, aber eine Verbindung zur Sicherheitslücke konnte noch nicht bestätigt werden

5.2 TOR - The Onion Router

5.2.1 TOR installieren und aktivieren

sudo apt install tor

5.2.2 einen Onion-Dienst konfigurieren

  • Tor-Konfigurationsdatei editieren
sudo /etc/tor/torrc
  • in der Datei die Sektion für Hidden Services suchen und einen Service definieren
HiddenServiceDir /var/lib/tor/ssh/
HiddenServicePort 22 127.0.0.1:22
  • der Konfigurationsbefehl HiddenServiceDir gibt an, in welchem Verzeichnis die Tor-Software die Informationen zum Dienst ablegen soll (privater Schlüssel und Hostname/öffentlicher Schlüssel). Dieses Verzeichnis sollte noch nicht existieren und wird von der Tor-Software automatisch mit den korrekten Rechten angelegt.
  • der Konfigurationsbehehl HiddenServicePort gibt an, welcher Port im Tor-Netzwerk sichtbar sein soll, und auf welchem Rechner (IP-Adresse) und Port die Pakete weitergeleitet werden sollen. Dabei kann auch eine IP-Adresse eines anderen Rechners im Netz angegeben werden, auf dem dann die Tor-Software nicht laufen muss, welcher aber als Onion-Service erreichbar ist.
  • Nun die Tor-Software neu starten
# modernes Linux (systemd)
sudo systemctl restart tor
  • in der Datei hostname des für den Onion-Dienst angegebenen Verzeichnis befindet sich der Onion-Name des Dienstes. Mit diesem Namen kann der Dienst erreicht werden. Der Client muss dabei auch die Tor-Software installiert haben und das Tor-Netz benutzen.
cat /var/lib/tor/ssh/hostname                                                    
rhbmhbl3n5iljqkj.onion

5.2.3 Tor auf Seite des Client

  • Tor-Software installieren und starten (keine besondere Konfiguration erforderlich)
  • für Web-Anwendungen den Tor-Browser installieren und benutzen: https://www.torproject.org/projects/torbrowser.html.en
  • oder im Browser (Firefox, Seamonkey, Chromium etc) in den Netzwerkeinstellungen als Proxy einen Socks5 Proxy auf Port 9050 eintragen. Einstellen, das DNS-Anfragen auch über den Socks5-Proxy gesendet werden.
  • Dienste, welche keinen Socks5-Proxy unterstützen, können über den Befehl torify das Tor-Netzwerk benutzen. Dabei fängt torify DNS-Anfragen und Netzwerkverbindungen an das Internet ab und leitet diese Anfragen an das Tor-Netzwerk weiter. Beispiel SSH (Secure Shell):
$ torify ssh rhbmhbl3n5iljqkj.onion
The authenticity of host 'rhbmhbl3n5iljqkj.onion (127.42.42.0)' can't be established.
ECDSA key fingerprint is SHA256:UDnhiUeJdxYVlYJHM/cOmP4gh+a6jKO+dfYwH0J8fDc.
ECDSA key fingerprint is MD5:8e:cc:bf:07:ee:bb:a7:9e:5f:77:f7:10:12:01:01:fa.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'rhbmhbl3n5iljqkj.onion' (ECDSA) to the list of known hosts.
root@rhbmhbl3n5iljqkj.onion's password: 
box$

5.2.4 Sicherheit

  • Einige Server-Dienste schalten spezielle Admin-Funktionen frei, wenn der Server über eine Loopback-Adresse (127.0.0.1) angesprochen wird (z.B. der Apache Webserver –> https://www.heise.de/security/meldung/Apache-verpetzt-moeglicherweise-Tor-Hidden-Services-3090218.html). Bei Bennutzung als Tor-Dienst auf einer Loopback-Adresse sind diese Admin-Funktionen von aussen über das Tor-Netzwerk erreichbar. Lösung: den Dienst auf einer nicht-Loopback Adresse konfigurieren, oder die speziellen Admin-Funktionen in der Server-Konfiguration ausschalten.

5.2.5 Onion-Dienst mit Client-Authentifizierung

Der Onion-Dienst, wie oben beschrieben, ist erreichbar für jeden Benutzer der den Onion-Namen kennt. Ein Onion-Name besteht derzeit aus 16 Zeichen (80 bit) und kann u.U. erraten werden. Um die Sicherheit des Onion-Dienstes noch weiter zu erhöhen, kann der Dienst mit einer Authentisierung versehen werden. Bei der Authentisierung bekommt jeder Client einen eigenen kryptografischen Schlüssel zugewiesen, und nur wenn dieser Schlüssel beim Verbindungsaufbau mit angegeben wird, ist der Onion-Dienst erreichbar (und sichtbar). Der Schlüssel ist ein Authentisierungs-Cookie aus 16 Bytes (Base64 kodiert) welcher zusätzlich zum Onion-Namen bekannt sein muss.

5.2.6 Authentisierung auf der Seite des Onion-Dienstes (Server)

Die Dienste-Authentisierung wird auf der Server-Seite mit der Konfigurationsdirektive HiddenServiceAuthorizeClient angeschaltet. Diese Direktive nimmt als Parameter den Authentisierungs-Modus (basic oder stealth) und eine per Komma getrennte Liste an Client-Namen. Diese Client-Namen sind nur beschreibener Natur und müssen nicht mit Hostnamen oder Domain-Namen übereinstimmen. Die Direktive gilt für den jeweils voher definierten Onion-Dienst:

HiddenServiceDir /var/lib/tor/ssh/
HiddenServicePort 22 127.0.0.1:22
HiddenServiceAuthorizeClient stealth client1,client2,client3

Nach einem Neustart des Tor-Dienstes findet sich im Verzeichnis des Onion-Dienstes eine neue Datei mit dem Dateinamen client_keys. Diese Datei enthält die Authentisierungs-Cookies und privaten Schlüssel für jeden angegebenen Client-Rechner:

client-name client1
descriptor-cookie QJVF38CnraTrg8FaAHuvFw==
client-key
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDTcZOJO3Eh4GWva8L9I4MlabqAvujx6oCL3xso00NTX6zWNWZn
[...]
6rEwyC92BYs2rShFsGadG0ET6N6+j7uWNf18Ya+qikCl
-----END RSA PRIVATE KEY-----
client-name client2
descriptor-cookie m6Isl8df2gPV+MBDoYFNLw==
client-key
-----BEGIN RSA PRIVATE KEY-----
MIICWwIBAAKBgQC/OSyIWcTGD9oZK3vUqeTpSIJkpkJc670/c57iXo6a6L94VW/0
[...]
NFd2QdCnL87ay3gxjTx9APvGdDRf6VaMNaSurciq2Q==
-----END RSA PRIVATE KEY-----

In der Datei hostname im Verzeichnis des Onion-Dienstes befinden sich nun unterschiedliche Onion-Namen für die jeweiligen Clients, zusammen mit den Authentisierungs-Cookies:

iaopwbjmex2splr2.onion QJVF38CnraTrg8FaAHuvFx # client: client1
bdc5ujizach44dwm.onion m6Isl8df2gPV+MBDoYFNLx # client: client2
kura5sy7vsmi6ws6.onion wyThiRFLyqtsppBS0TDoHB # client: client3

Aus dieser Datei muss nun für jeden Client der Onion-Namen und der dazugehörige Cookie in die Konfiguration der Tor-Software auf den jeweiligen Clients kopiert werden.

5.2.7 Authentisierung auf der Client-Seite

Onion-Namen und Cookie werden auf Client-Seite mit der Direktive HidServAuth in die Konfigurationsdatei /etc/tor/torrc eingetragen:

HidServAuth  kura5sy7vsmi6ws6.onion wyThiRFLyqtsppBS0TDoHB # client: client3

Nach einem Neustart der Tor-Software auf dem Client sollte nun der Onion-Dienst von diesem Client aus sichtbar und benutzbar sein.

Ein Verbindungsaufbau zum Dienst ohne den Authentisierungs-Cookie bedingt einen Timeout:

# torify ssh -v kura5sy7vsmi6ws6.onion
OpenSSH_7.5p1, OpenSSL 1.1.0f-fips  25 May 2017
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Reading configuration data /etc/ssh/ssh_config.d/05-redhat.conf
debug1: Reading configuration data /etc/crypto-policies/back-ends/openssh.config
debug1: /etc/ssh/ssh_config.d/05-redhat.conf line 8: Applying options for *
debug1: Connecting to kura5sy7vsmi6ws6.onion [127.42.42.0] port 22.
[Aug 03 22:26:45] ERROR torsocks[9052]: Connection timed out (in socks5_recv_connect_reply() at socks5.c:536)
debug1: connect to address 127.42.42.0 port 22: Connection timed out
ssh: connect to host kura5sy7vsmi6ws6.onion port 22: Connection timed out

Mit dem korrektem Cookie funktioniert der Verbindungsaufbau:

# torify ssh -v kura5sy7vsmi6ws6.onion
OpenSSH_7.5p1, OpenSSL 1.1.0f-fips  25 May 2017
debug1: Reading configuration data /etc/ssh/ssh_config
debug1: Reading configuration data /etc/ssh/ssh_config.d/05-redhat.conf
debug1: Reading configuration data /etc/crypto-policies/back-ends/openssh.config
debug1: /etc/ssh/ssh_config.d/05-redhat.conf line 8: Applying options for *
debug1: Connecting to kura5sy7vsmi6ws6.onion [127.42.42.0] port 22.
debug1: Connection established.
[...]
debug1: SSH2_MSG_KEXINIT sent
debug1: SSH2_MSG_KEXINIT received
debug1: kex: algorithm: curve25519-sha256@libssh.org
debug1: kex: host key algorithm: ecdsa-sha2-nistp256
debug1: kex: server->client cipher: aes256-gcm@openssh.com MAC: <implicit> compression: none
debug1: kex: client->server cipher: aes256-gcm@openssh.com MAC: <implicit> compression: none
debug1: kex: curve25519-sha256@libssh.org need=32 dh_need=32
debug1: kex: curve25519-sha256@libssh.org need=32 dh_need=32
debug1: expecting SSH2_MSG_KEX_ECDH_REPLY
debug1: Server host key: ecdsa-sha2-nistp256 SHA256:UDnhiUeJdxYVlYJHM/cOmP4gh+a6jKO+dfYwH0J8fDc
The authenticity of host 'kura5sy7vsmi6ws6.onion (127.42.42.0)' can't be established.
ECDSA key fingerprint is SHA256:UDnhiUeJdxYVlYJHM/cOmP4gh+a6jKO+dfYwH0J8fDc.
ECDSA key fingerprint is MD5:8e:cc:bf:07:ee:bb:a7:9e:5f:77:f7:10:12:01:01:fa.
Are you sure you want to continue connecting (yes/no)? 

5.2.8 Vor- und Nachteile von Tor-Onion-Diensten

5.2.9 Vorteile

  • keine Änderungen an der (Heim-)Firewall notwendig. Keine Port-Forwardings etc.
  • kein DynDNS notwendig, um eine wechselne IP-Adresse mit einen Namen zu verbinden. Der Onion-Name wird immer im Tor-Netz gefunden
  • Datenverkehr ist (einfach) verschlüsselt (1024bit RSA)
  • Datenverkehr ist anonymisiert

5.2.10 Nachteile

  • Client muss die Tor-Software als Proxy benutzen
  • nur IPv4, kein IPv6 (daran wird gearbeitet)
  • hohe Latenz (Verzögerung) bei den Paketlaufzeiten
  • Tor-Verschlüsselung von 1024bit RSA nicht mehr zeitgemäß (auch daran wird im Tor-Projekt gearbeitet)

5.3 Logdateien auswerten

  • Artificial Ignorance: bei diesem von Marcus Ranum beschriebenen Konzept werden Filter aufgebaut durch welche die Log-Ausgaben gefiltert werden. Log-Ausgaben werden in zwei Kategorien eingeteilt:
    • Meldung beschreibt eine kritische Fehlfunktion im System. Die Ursache der Fehlfunktion muss beseitigt werden, damit die Log-Ausgaben nicht mehr angezeigt werden
    • Meldung beschreibt eine unkritische Situation im System. Die Filter werden erweitert, um die Log-Meldung in Zukunft zu unterdrücken

    Ziel von AI-Logauswertung ist es, das die Log-Dateien nach dem Filtern leer sind. Alle neuen Log-Meldungen, welche in den Log-Dateien auftauchen, müssen entweder bereinigt werden oder durch einen Filter unterdrückt werden: http://www.ranum.com/security/computer_security/papers/ai/

5.3.1 NSB - Never Before Seen

  • Berkeley-DB Development-Dateien laden
apt install libdb-dev build-essential
  • NBS Quellcode laden und auspacken
cd ~/src
wget http://www.ranum.com/security/computer_security/code/nbs.tar
tar xvf nbs.tar
cd nbs
  • NBS Quellcode patchen
sed -i 's/.*extern\tint\terrno;.*/#include <errno.h>/' nbspreen.c
  • NBS Quellcode bauen und Programm-Dateien installieren
make
cp nbs nbsmk nbspreen nbsdump /usr/local/bin
  • Verzeichnis für NBS Dateien anlegen
mkdir /var/lib/nbs
  • Datenbank für NBS anlegen
cd /var/lib/nbs
nbsmk -d messages.nbs
  • Datenbank mit Log-Daten anlernen
nbs -d messages.nbs -i /var/log/messages -s
[...]
NBS completed run (0 sec)
9567 never before seen entries
13630 lines of input processed (70.19% NBS)
9567 total entries in database
  • Neuen Log-Eintrag simulieren
logger -t nbstest "Hallo, dies ist ein Test"
  • NBS Lauf
nbs -d messages.nbs -i /var/log/messages -s
  • Ausgabe
nbs -d messages.nbs -i /var/log/messages -s
Aug 30 08:17:48 notebook32 nbstest: Hallo, dies ist ein Test
NBS completed run (0 sec)
1 never before seen entries
13631 lines of input processed (0.01% NBS)
9568 total entries in database
  • die NBS Ausgabe kann auch in eine Datei geschrieben oder angehangen werden
nbs -d /var/lib/nbs/messages.nbs -i /var/log/messages -a /var/log/nbs.log

5.3.2 Log-Templater

mkdir ~/src
cd ~/src
apt -y install git build-essential automake autoconf
git clone https://github.com/rondilley/tmpltr.git
cd tmpltr
./bootstrap
autoreconf -i
./configure
make
make install
  • Systemd-Journal für den Dienst sshd mit tmpltr auswerten
journalctl -u ssh | tmpltr - | sort -n |  sed -e 's/%s.*||//'
  • Templates abspeichern, dann ignore Datei bearbeiten
journalctl -u ssh | tmpltr -w /var/log/ssh.ignore -
  • Log mit ignore Datei auswerten
journalctl -u ssh | tmpltr -t /var/log/ssh.ignore - | sort -n |  sed -e 's/%s.*||//'
  • SSH-Fehler produzieren (z.B. Anmeldeversuch mit falschem Passwort) und Log-Datei neu auswerten
ssh bar@localhost
bar@localhost's password:
Permission denied, please try again.
bar@localhost's password:
  • Log-Auswerten
[bar@notebook32 tmpltr]# journalctl -u sshd | tmpltr -t /var/log/sshd.ignore - | sort -n |  sed -e 's/%s.*||//'
Opening [-] for read
           1 Aug 31 12:29:25 notebook32 sshd[24691]: Connection closed by ::1 port 40372 [preauth]
           1 Aug 31 12:29:25 notebook32 sshd[24691]: Failed password for bar from ::1 port 40372 ssh2
           1 Aug 31 12:29:23 notebook32 sshd[24691]: pam_succeed_if(sshd:auth): requirement "uid >= 1000" not met by user "bar"
           1 Aug 31 12:29:23 notebook32 sshd[24691]: pam_unix(sshd:auth): authentication failure; logname= uid=0 euid=0 tty=ssh ruser= rhost=localhost  user=bar
  • Neue Log-Einträge in die Ignore-Datei aufnehmen
journalctl -u sshd | tmpltr -t /var/log/sshd.ignore -w /var/log/sshd.ignore.new - && \
  cat /var/log/sshd.ignore.new >> /var/log/sshd.ignore

5.4 die neue Firewall nftables

  • nftables Programme installieren
apt install nftables
  • prüfen das keine iptables Regeln mehr aktiv sind
iptables -L
ip6tables -L
  • Beispiel: Lesen der Firewall-Regeln aus einer Datei
# nft -f /etc/nftables.conf

5.4.1 Beispiel: Eine Regel (IPv4, Tabelle "fw", Kette "input") hinzufügen

# nft "add rule ip fw input drop"

5.4.2 Beispiel: eine interaktive nft Sitzung

# nft -ia
nft> list ruleset
table inet workstation-fw {
    chain input {
             type filter hook input priority 0;
    }
}

5.4.3 Beispiel: eine interaktive nft Sitzung

nft> add rule inet workstation-fw input tcp dport {ssh, http} accept
nft> list ruleset
table inet workstation-fw {
    chain input {
             type filter hook input priority 0;
             tcp dport { ssh, http} accept # handle 3
    }
}
nft> quit

5.4.4 Regelnsatz löschen

  • Der Befehl flush löscht Objekte aus der nftables Kernel-Firewall.
  • Ein flush ruleset löscht die gesamten Firewall-Regeln.
  • Ein flush ruleset sollte am Anfang eines nftables-Regelsatzes stehen:
# nft flush ruleset
  • Über flush chain und flush table können Ketten und Tabellen gelöscht werden.

5.4.5 Regeln hinzufügen

  • Der Befehl add fügt die Regel immer am Ende der Kette an
  • mit insert kann eine Regel an einer beliebigen Stelle der Kette eingefügt werden.

5.4.6 Regeln löschen

  • Der Befehl delete löscht eine Regel aus einer Kette. Zum Löschen einer Regel wird das Handle der Regel benötigt, das Handle wird über den Kommandozeilenparameter -a ausgegeben:
# nft -a list ruleset
...
ip6 saddr 2001:db8::/64 # handle 15
...
# nft delete rule inet filter input handle 15

5.4.7 Monitor

Mittels nft monitor können Änderungen in der nftables-Firewall überwacht werden.

Der Befehl schreibt Modifikationen im Regelsatz der Firewall als nft-Befehlszeilen (Alternativ als XML oder JSON) auf die Konsole:

# nft monitor
add table inet filter
add chain inet filter input { type filter hook input priority 0; }
add rule inet filter input iif lo accept
add rule inet filter input ct state established,related accept
add rule inet filter input ip6 daddr ff02::1010 udp dport ntp accept
add rule inet filter input ip6 daddr ff02::fb udp dport mdns accept
add rule inet filter input ip daddr 224.0.0.251 udp dport mdns accept
add rule inet filter input ip6 saddr & :: == :: udp dport dhcpv6-client accept
add rule inet filter input udp dport ipp accept
add rule inet filter input ip6 saddr & :: == :: icmpv6 
      type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert} accept
add rule inet filter input ip6 saddr & :: == :: icmpv6 
      type { echo-reply, echo-request, packet-too-big, destination-unreachable, 
             time-exceeded, param-problem} accept
add rule inet filter input counter packets 0 bytes 0 log prefix "nftables drop: " drop
  • die aus iptables bekannten Konzepte von Tabellen (Tables) und Ketten (Chains) finden sich auch in nftables wieder
  • vordefinierten Tabellen (in iptables 'filter', 'nat' und 'mangle') gibt es im nftables nicht
  • Auch die in iptables vorhandenen Ketten ('input', 'output', 'forward') sucht man vergebens
  • nftables kommt ohne vordefinierte Tabellen und Ketten.
  • Die vordefinierten Ketten in iptables sind ein Performance-Problem bei grossen Datenmengen, da Netzwerkpakete auch durch unbenutze, leere Ketten geschleusst werden.
    • Unter nftables sind nur Tabellen und Ketten aktiv, welche der Administrator definiert hat.
  • In nftables sind Tabellen einfache Container für beliebige Ketten.
  • Eine Tabelle muss einem Protokoll zugewiesen werden (ip = IPv4, arp,/ip6/ = IPv6, bridge).
  • Die Tabellen können beliebige Namen haben, auch wenn in vielen Beispielen im Internet die aus iptables bekannten Namen verwendet werden.
    • Bei den Ketten (Chains) unterscheidet man "base-chains" und "auxiliary chains".
    • Bei den "base-chains" handelt es sich um Ketten, welche in den Paketfluss des Netfilter-Framework eingeklinkt werden.
    • Diese Ketten erhalten Netzwerkpakete aus den Zugriffspunkten (Hooks) des Linux-Kernels.
    • Diese Zugriffspunkte für eine Kette vom Typ "filter" heissen "input", "output" und "forward".
    • Die Konzepte sind von iptables übernommen worden, der Firewall-Administrator hat nun aber mehr Freiraum, die Ketten zu organisieren.

5.4.8 Beispiel-Tabelle

  • Beispiel einer Tabelle mit zwei (leeren) "filter" Ketten, für "input" und "output":
table inet filter01 {
        chain input01 {
                type filter hook input priority 0;
        }
        chain output01 {
                type filter hook output priority 0;
        }
}

5.4.9 Base- und Auxiliary-Ketten

  • Mit dem Schlüsselwort hook wird eine Kette mit den Linux-Kernel-Internen Schnittstellen des Netfilter-Frameworks verbunden.
  • Eine Kette mit Verbindung zum den Kernel-Schnittstellen ist eine "base-chain".
  • Zusätzlich zu den "base-chains" kann der Administrator noch beliebig weitere "auxiliary chains" erzeugen.
  • Diese Chains erhalten nur Netzwerkpakete, wenn diese aus einer "base-chain" per goto oder jump Direktive an die "auxiliary chain" verteilt werden:

5.4.10 Beispiel Base- und Auxiliary-Ketten

table inet filter01 {
        # auxiliary chain 
        chain link-local-aux01 {
                 counter packets 0 bytes 0 log prefix "link-local: " accept
                 # weitere Regeln für link-local IPv6 
                 [...]
        }
        # base chain
        chain input01 {
                type filter hook input priority 0;
                ip6 daddr fe80::/64 jump link-local-aux01
        }
        # base chain
        chain output01 {
                type filter hook output priority 0;
        }
}

5.4.11 Filter-Regeln

  • Eine Hauptaufgabe einer Firewall ist das Filtern von Netzwerkverkehr.
  • Die nftables-Firewall bietet umfangreiche Möglichkeiten, Netzwerkpakete in Filterregeln auszuwählen:
    • nach Quell- und Ziel-Adressen
    • UDP- und TCP-Ports
    • Meta-Informationen wie
      • Netzwerk-Interfaces
      • Protokolle
      • Hardware-MAC-Adressen
      • VLAN-Informationen
      • Status einer IP-Verbindung (Connection-Tracking)

5.4.12 Beispiel einer Filter-Regel

  • In diesem Beispiel wird eine Regel der Firewall hinzugefügt. Gefiltert wird nach der IPv6-Zieladdresse daddr ff02::fb und dem UDP-Port dport mdns (Multicast-DNS):
# nft "add rule inet filter input ip6 daddr ff02::fb udp dport mdns accept"
  • Eine Liste der Filter-Optionen findet sich in der man-Page zu nft.

5.4.13 Verdict Statements

  • Am Ende einer Firewall-Regel wird das Urteil (Verdict) über eine Netzwerk-Verbindung getroffen, das sogennante Verdict-Statement.
  • Es gibt entgültige Urteile, welche die Bearbeitung eines Netzwerkpaketes sofort beenden.
  • Dazu zählen * accept (Paket passieren lassen) * drop (Paket ohne Rückmeldung an den Sender verwerfen) und * reject (mit Rückmeldung verwerfen).
  • Bei reject kann optional die ICMP-Meldung spezifiziert werden, welche an den Sender des Paketes zurückgeliefert wird:
# nft "add rule inet filter input ip6 saddr 2001:db8::/64" \
      "reject with icmpv6 type admin-prohibited"

Neben den entgültigen Urteilen kann eine Regel die Bearbeitung eines Paketes auch an anderen Stelle fortsetzen:

  • continue Bearbeitung des Paketes mit die nächsten Regel in der Kette
  • jump <chain> verzweigt die Bearbeitung wie ein Unterprogrammaufruf in eine andere Kette. Am Ende der neuen Kette wird die Bearbeitung in der ursprünglichen Kette weitergeführt.
  • goto <chain> wird direkt in eine andere Kette gesprungen, ohne das die Bearbeitung jemals wieder zurückkehrt.
  • queue verzweigt die Verarbeitung an ein externes Programm im Userspace.
  • return beendet die Bearbeitung der aktuellen Kette an dieser Stelle. Wird return in einer Kette der obersten Bearbeitungsebene verwendet, so wird das Paket durch die Firewall gelassen (identisch zu einem accept).

5.4.14 Logging und Zähler

  • Über das Befehlswort log werden Informationen zu dem gerade bearbeiteten Netzwerk-Paket in das Kernel-Log (und damit in den meisten Systemen auch in das System-Log) geschrieben.
  • Der Befehl counter erzeugt einen Zähler für diese Firewall-Regel.
  • Anders als bei iptables müssen Counter für Regeln explizit erzeugt werden. Dies hat Performance-Gründe, die Zähler werden nur dann mitgeführt, wenn der Administrator diese im Regelsatz angefordert hat.
  • In dem Zähler werden die Anzahl der gefilterten Pakete und die Datenmenge in Bytes gezählt.
  • Der Zähler wird beim Auflisten des Regelwerkes, z.B. mit list ruleset, ausgegeben.
  • Die Position des Befehls log in der Regel wichtig:
    • Steht der Befehl vor der Filter-Bedingung, so wird ein Log-Eintrag für jedes Paket geschrieben, welches von dieser Regel gesehen wird, unabhängig ob die Regel danach aktiv wird oder nicht.
    • Steht jedoch der Befehl log nach der Filterbedingung, so wird der Log-Eintrag nur geschrieben, wenn die Filterbedingung zutrifft.
  • Gleiches gilt für die Position des Befehls counter.
# schreibe einen Log-Eintrag für jedes Paket 
# welches von dieser Regel gesehen wird
log tcp dport { 22, 80, 443 } ct state new accept
# schreibe einen Log-Eintrag nur wenn die Regel zutrifft
tcp dport { 22, 80, 443 } ct state new log counter accept
  • Dem Befehl log kann mit dem Parameter prefix eine beliebige Zeichenkette mitgegeben werden.
  • Diese Zeichenkette erscheint vor jeder Log-Meldung und wird benutzt, um Log-Einträge zu finden und zu filtern.

5.4.15 IPv4 und IPv6 kombinieren

  • Neben den Protokollen ip für IPv4 und ip6 für IPv6 unterstützt nftables auch das Pseudo-Protokoll inet für kombinierte IPv4- und IPv6-Filter.
  • Anstatt z.B. Port 80 für einen Webserver in zwei getrennten Regeln für je IPv4 und IPv6 freizugeben, kann dies in nftables in nur einer Regel geschehen.
  • Bei einem Server mit Dual-Stack und vielen Diensten kann dies das Regelwerk vereinfachen.
  • Für das Protokoll inet muss eine eigene Tabelle mit Chains erstellt werden.
table inet filter {
    chain input { 
       ...
    }
}
  • Die Tabelle mit dem Pseudo-Protokoll inet existiert paralell zu Tabellen mit IPv4- und IPv6-Regeln.
  • Werden Tabellen mit dedizierten IPv4 und IPv6 Regeln genutzt, so müssen die Pakete sowohl in der inet Tabelle als auch in der jeweiligen Tabelle für das IP-Protokoll erlaubt werden.
  • Um Fehler zu vermeiden, sollte entweder kombinierte inet Tabellen, oder protokoll-spezifische Tabellen benutzt werden, aber nicht beides.

5.4.16 Datenstrukturen

  • nftables bietet verschiedene Datenstrukturen, welche die Erstellung eines Regelwerkes vereinfachen:
    • Variablen
    • Intervalle
    • Sets
    • benannte und anonyme Maps
    • benannte und anonyme Dictionaries/Verdict-Maps

5.4.17 Variablen

  • Variablen erlauben symbolische Namen für Werte und IP-Adressen in einem nftables-Regelwerk.
  • Variablen sind jeweils in der Umgebung sichtbar, in der sie definiert wurden (Table, Chain).
  • Variablen werden über das Schlüsselwort define deklariert und mit einem Wert versehen. Im Regelwerk wird dann der Variablenname mit einem vorangestellem Dollar-Zeichen "$" verwendet, vergleichbar mit Variablen der Unix-Shell.
define lan_if = eth0
define DMZ-Dienste = { ssh, smtp, dns, http, https }
add rule inet filter input iif $lan_if tcp dport $DMZ-Dienste accept

5.4.18 Intervalle

  • Intervalle in nftables sind Wertefolgen mit einem Start- und End-Wert.
  • Intervalle werden z.B. für IP-Adresse-Bereiche und TCP/UDP-Port-Bereiche verwendet.
nft "add rule inet filter input ip6 daddr 2001:db8::0-2001:db8::1000 drop"
nft "add rule inet filter input tcp dport 0-1023 drop"

5.4.19 Sets

  • Sets definieren eine Sammlung von Werten eines gleichen Typs. Ein Set wird durch geschweifte Klammern eingeleitet und die Elemente des Sets werden mit Komma getrennt.
  • Ein anonymes Set mit Port-Nummern (Microsoft SMB/CIFS-Protokoll):
nft "add rule inet filter input udp dport { 137, 138, 139, 445 } reject" 
  • Bei der Anlage eines benannten Sets muss nftables der Werte-Typ mitgeteilt werden.
  • Eine (leider noch unvollständige) Liste der verfügbaren Werte-Typen ist in der nft man-Page aufgelistet:
nft "add set inet filter bogus { type ipv4_addr; }"
nft "add element inet filter bogus { 203.0.113.0; }"
nft "add element inet filter bogus { 203.0.113.1; }"
nft "add rule inet filter input ip saddr @bogus drop"
nft "list set inet filter bogus"
  • Der letzte Befehl des Beispiel gibt den aktuellen Inhalt des benannten Sets bogus aus.
  • Ein benanntes Set kann in einer Regel mit vorangestelltem "@" eingefügt werden.
  • Eine Filterregel mit benanntem Set wird vom der nftables Virtuellen-Maschine schneller bearbeitet als eine Reihe von Einzel-Regeln.

5.4.20 Anonyme und benannte Maps und Dictionaries

  • Maps und Dictionaries existieren in anonymen (statischen) und benannten Versionen.
  • Die anonymen Versionen werden direkt in die Regel geschrieben und sind damit fester Bestandteil der Regel.
  • Die Inhalte von anonymen Datenstrukturen können nicht zu einem späeteren Zeipunk geändert werden.
  • Bei den benannten Maps und Dictionaries können die Inhalte nach dem Laden des Firewall-Regelwerkes noch angepasst werden.
  • So können z.B. Programme wie fail2ban oder denyhost direkt mit der Firewall interagieren und IP-Adressen von Angreifern sperren.
  • Oder eine Anti-Spam-Dienst kann das Einliefern von Spam-E-Mail über die IP-Adressen von identifizierten Spammern mittels einer Rate-Limit-Regel drosseln.

5.4.21 Maps

  • Maps oder Data-Maps verbinden zwei Werte miteinander.
  • Der Eingangswert wird in der Liste der Index-Werte der Map gesucht und der dort gespeicherte Wert wird an die Regel zurückgegeben.
  • Eine Beispiel-Anwendung ist das Verzweigen auf verschiedene interne Server mit privaten Adressen per NAT, basierend auf der Port-Nummer des Dienstes:
# nft "add rule ip nat prerouting dnat" \
      "tcp dport map { 80 : 192.168.1.100, 8888 : 192.168.1.101 }"

5.4.22 Verdict-Maps/Dictionaries

  • Verdict-Maps, auch Dictionaries genannt, verbinden einen Eingangswert des zu betrachtenen Pakets mit der Filter-Entscheidung einer Regel.
  • Somit lassen sich für die verschiedenen Werte einers IP-Paketes automatisch unterschiedliche Aktionen auslösen.
  • In diesem Beispiel werden die Ergebnisse einer Regel an die Quell-IPv4-Adresse des Netzwerk gebunden:
nft "add map inet filter aktion { type ipv4_addr : verdict; }"

nft "add element inet filter aktion" \
    "{ 192.0.2.80 : accept, 192.0.2.88 : drop, 192.0.2.99 : drop }"

nft "add rule inet filter input ip saddr vmap @aktion"

5.4.23 Beispiel: ein Regelwerk für eine Host-Firewall

  • Das nachfolgende nftable-Regelwerk kann als Grundlage für eine Host-Firewall für eine Linux-Workstation benutzt werden.
  • Gefiltert werden die eingehenden IPv4- und IPv6-Verbindungen.
  • Gängige Dienste auf einer Linux-Workstation wie Internet Printing Protocol (IPP/Cups) und Multicast-DNS werden erlaubt:
flush ruleset

define any = 0::0/0

table inet filter {
        chain input {
                type filter hook input priority 0;

                # accept any localhost traffic
                iif lo accept

                # accept traffic originated from us
                ct state established,related accept

                # activate the following line to accept common local services
                #tcp dport { 22, 80, 443 } ct state new accept

                # NTP multicast
                ip6 daddr ff02::1010 udp dport 123 accept

                # mDNS (avahi)
                ip6 daddr ff02::fb udp dport 5353 accept
                ip  daddr 224.0.0.251 udp dport 5353 accept

                # DHCPv6
                ip6 saddr $any udp dport 546 accept

                # IPP (CUPS)
                udp dport 631 accept

                # Accept neighbour discovery otherwise IPv6 connectivity breaks.
                ip6 saddr $any icmpv6 type { nd-neighbor-solicit,  nd-router-advert, nd-neighbor-advert }  accept

                # Accept essential icmpv6
                # nft cannot parse "param-problem" (icmpv6 type 4)
                ip6 saddr $any icmpv6 type { echo-reply, echo-request, packet-too-big, destination-unreachable, time-exceeded, 4 } accept

                # count and drop any other traffic
                counter log prefix "nftables drop: " drop
        }
}

5.4.24 Beispiel: ein Regelwerk für einen Gateway-Router mit IPv4-NAT

  • Nachfolgend ein nftables-Regelwerk für eine einfache Firewall mit DMZ-, WAN- und LAN-Schittstelle. In der DMZ befinden sich die Server mit Web und E-Mail.
  • In der Postrouting-Kette werden alle Pakete aus dem LAN-Segment, welche die Firewall in Richtung Internet (WAN) verlassen, per Masquerading-NAT auf die IPv4-Adresse der WAN-Netzwerkschnittelle umgeschrieben.
#!/usr/bin/nft -f
flush ruleset
define mgt_if = eth0
define wan_if = eth1
define lan_if = eth3
define dmz_if = eth2
define any_v6  = 0::0/0

table inet gateway-fw {
        chain forward {
                type filter hook forward priority 0
                # accept established traffic
                ct state established,related accept
                # allow all outgoing traffic from LAN
                iif $lan_if accept
                # access to services in the DMZ
                oif $dmz_if tcp dport { http, https, smtp, imap, imaps } counter accept
                # Accept essential icmpv6
                # nft cannot parse "param-problem" (icmpv6 type 4)
                ip6 saddr $any_v6 icmpv6 type { echo-reply, echo-request, packet-too-big, destination-unreachable, time-exceeded, 4 } accept
                # reject everything else
                counter log reject
       }
       chain input {
                type filter hook input priority 0
                iif lo accept
                iif $mgt_if tcp dport ssh accept
                ip6 saddr $any_v6 icmpv6 type { nd-neighbor-solicit,  nd-router-advert, nd-neighbor-advert }  accept
                ip6 saddr $any_v6 icmpv6 type { echo-reply, echo-request, packet-too-big, destination-unreachable, time-exceeded, 4 } accept
                counter log reject
       }
}
table ip nat {
        chain prerouting {
                type nat hook prerouting priority 0
        }
        chain postrouting {
                type nat hook postrouting priority 0
                oif $wan_if log masquerade
        }
}

5.4.25 Vergleich iptables und nftables

  • Derzeit gibt es für die Firewall-Infrastruktur netfilter im Linux-Kernel zwei Schnittstellen
    • das neue nftables
    • und das bekannte iptables.
  • In der Zukunft, nachdem nftables zum Funktionsumfang von iptables aufgeschlossen hat, möchten die Kernel-Entwickler iptables aus dem Kernel entfernen. Dies wird aber erst in ein paar Jahren der Fall sein.
  • Solange können beide Firewall-Systeme nebeneinander aktiv sein.
  • Dies kann zu ungewollten Sicherheitsproblemen führen, wenn beide Firewall-Systeme aktiviert und mit wiedersprechenden Regelsätzen konfiguriert sind.
  • So kann iptables Netzwerkpakete durchlassen, die nftables blocken soll.

5.4.26 Migration von iptables Regeln

  • Das nftables Team arbeiten an einer Kompatibilitätsschicht für bestehende iptables-Scripte.
  • Die Version 1.6 der iptables Kommandozeilenprogramme beinhaltet die Befehle iptables-nft, ip6tables-nft, arptables-nft und ebtables-nft.
  • Diese Kommandos sind kompatibel zu den bisherigen iptables Befehlen, doch anstatt der iptables-Schnittstelle wird nftables benutzt.
  • Die iptables Befehle werden dabei in den Bytecode für die nftables VM übersetzt.
  • Bei Debian 10 werden bei der Installation von nftables die iptables Befehle durch die nftables Versionen ersetzt. Die original iptables Befehle sind unter dem Namen iptables-legacy zu erreichen

5.4.27 Aufgabe: IPv4 Host-Firewall

  • Lösche mögliche alte IPTables Regeln (NAT Tabelle und Filter Tabelle)
iptables -F -t nat
iptables -F
  • Installiere nftables auf der Laptop
  • erstelle eine Host-Firewall auf dem Laptop in der Datei include "/etc/nftables.conf", welche nur folgende Zugriffe erlaubt
    • ICMP Echo-Request/Echo-Reply
    • Port 80 (HTTP) und 22 (SSH)
  • starte einen Python-Webserver als Benutzer root auf Port 80
python -m SimpleHTTPServer 80
  • Installiere nmap auf dem Laptop
apt install nmap
  • Scanne per nmap nach offenen Ports vom Laptop den Laptop-Rechner Deines Nachbarn einmal mit nftables Firewall aktiv, einmal ohne eingeschaltete nftables Firewall auf dem Rechner des Nachbarn. Gibt es Unterschiede in der Ausgabe?
nmap -v -sT 192.168.1.XXX
  • Teste bei eingeschalteter Firewall auf dem Rechner des Nachbarn, ob die Website/Verzeichnlis-Listing auf dem Webserver des Nachbarn vom Firefox erreichbar ist.

5.4.28 Erweiterte Aufgabe:

  • starte den Python Webserver auf Port 8000
python -m SimpleHTTPServer 8000
  • erstelle eine redirect oder dnat Destination-NAT Regel (Tabelle nat, Chain prerouting), welche Zugriffe auf Port 80 des Rechners auf Port 8000 umleitet: Nftables Dokumentation: man nftables
  • Teste das der Firefox vom einem anderen Rechner aus auf Port 80 den Webserver erreichen kann

5.4.29 Beispiel-Lösung

#!/usr/sbin/nft -f

flush ruleset

table ip filter {
        chain input {
                type filter hook input priority 0; 
		policy accept;

                iif lo accept
		
		icmp type { echo-reply, echo-request } accept
                ct state established,related accept
                tcp dport { ssh, http } ct state new accept

		counter log prefix "nftables drop: " drop
        }
}
  • Regelwerk nach Änderungen neu laden
systemctl restart nftables
  • aktives Regelwerk inkl. der Zähler anzeigen
nft list ruleset
  • Log-Meldungen erscheinen im Syslog (/var/log/messages) und im Journal
journalctl | grep nftables
grep nftables /var/log/messages

5.4.30 Beispiel-Lösung (Erweitert)

#!/usr/sbin/nft -f

flush ruleset

table ip nat {
	chain prerouting {
		type nat hook prerouting priority 0;
		tcp dport 80 redirect to 8000
	}
	chain postrouting {
		type nat hook postrouting priority 0;
	}

}

table ip filter {
	chain input {
		type filter hook input priority 0;
		policy accept;

		iif lo accept

		icmp type { echo-reply, echo-request } accept
		ct state established,related accept
		tcp dport { ssh, http, 8000 } ct state new accept

		counter log prefix "nftables drop: " drop
	}
}

5.5 SELinux Policy Entwicklung

  • wir arbeiten auf einer VM im Internet. Benutzername nutzer und Passwort villa, Benutzer root hat das Passwort vogelsang.
Hostname IP-Adresse Teilnehmer
VM01 46.101.162.149 Moritz
VM02 46.101.177.110 Christian
VM03 46.101.183.174 Ulf
VM04 46.101.183.179 Svenja
VM05 46.101.192.81 Björn
VM06 134.209.249.227 Stefan
VM07 165.22.31.233 Nico
VM08 167.71.48.36 Carsten
  • SELinux Hilfspakete installieren
yum install policycoreutils setools libselinux-utils selinux-policy-doc setools-console \
  policycoreutils-python selinux-policy-devel policycoreutils-newrole

5.5.1 SELinux Policy erweitern - Ein simpler HTTP-Server

  • C-Compiler installieren
yum install gcc
  • der Quellcode
#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);
  }
}

  • Quellcode vorbereiten
mkdir ~/src
cd ~/src
$EDITOR simple-server1.c
  • Übersetzen
gcc -o simple-server simple-server1.c
  • Installieren
cp simple-server /usr/local/bin
  • Server starten, Testen, mit einem Web-Browser an Port 8080 verbinden
simple-server &
  • SELinux Label des Dienstes anzeigen
ps -eZ | grep simple
ls -lZ /usr/local/bin/simple-server
  • SELinux Policy Modul erstellen
mkdir ~/selinux-src
cd ~/selinux-src
  • ein Basis SELinux Policy Modul erstellen
sepolicy generate -n simple-server --init /usr/local/bin/simple-server
  • die erstellen Policy-Dateien anschauen
less simple-server.te
less simple-server.fc
less simple-server.if
  • SELinux Policy compilieren und ein RPM-Paket erstellen
yum -y install rpm-build
sh simple-server.sh
ls -l
  • neues Modul aktivieren
semodule -i simple-server.pp
  • den vorher gestarteten simple-server Prozess stoppen
pkill simple-server
  • eine SystemD Service-Unit für den Server Dienst erstellen
$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
  • neue Systemd-Service-Datei mit dem richtigen SELinux Label versehen
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
  • Nun den Dienst benutzen. Das Modul ist noch im Permissive Mode, Verstösse gegen die Policy werden im Audit-Log protokolliert
ausearch -m avc -ts recent -c simple-server
  • Erklärungen zu den Policy-Fehlermeldungen
ausearch -m avc -ts today -c simple-server | audit2why  | less
  • Policy-Regeln aus den Audit-Meldungen erstellen
sepolgen-ifgen
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 (Datei simple-server.te), 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

5.5.2 Eine Änderung an einer SELinux Policy

  • Unser Server lernt Logging. Den Inhalt in eine Datei simple-server.patch in das Verzeichnis mit dem Quellcode des simple-server speichern:
--- simple-server1.c    2016-08-24 20:12:56.379000000 +0000
+++ simple-server2.c    2016-08-24 21:13:31.648000000 +0000
@@ -15,13 +15,20 @@
 "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");
@@ -41,7 +48,7 @@
   listen(sock, 5);
   while (1) {
     client_fd = accept(sock, (struct sockaddr *) &cli_addr, &sin_len);
-    printf("got connection\n");
+    fprintf(f,"got connection\n");

     if (client_fd == -1) {
       perror("Can't accept");
  • Patch einspielen
patch -p1 < simple-server.patch
  • das gepatchte Programm:
#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");
  }

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

  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'*/
    shutdown(client_fd,2);
    close(client_fd);
  }
}


  • den neuen Server-Dienst übersetzen, alten simple-server stoppen, neue Programm-Datei nach /usr/local/bin kopieren, 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://localhost:8080/ aufrufen
  • neue SELinux Log-Meldungen tauchen auf
# ausearch -m avc -ts recent
----
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
  • Policy-Erweiterung 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 var_log_t;
        type simple-server_t;
        class file { create open };
}

#============= simple-server_t ==============
allow simple-server_t var_log_t:file { create open };
  • simple-server SELinux Modul entfernen, Policy neu übersetzen, Modul neu laden, testen
  • Solange die Policy anpassen, bis keine Permission-Meldungen im Audit-Log erscheinen
  • "Permissive" aus der finalen Policy herausnehmen
# less simple-server.te
policy_module(simple-server, 1.0.0)

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

type simple-server_t;
type simple-server_exec_t;
init_daemon_domain(simple-server_t, simple-server_exec_t)

#permissive simple-server_t;

########################################
#
# simple-server local policy
#
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 };
}

allow simple-server_t self:fifo_file rw_fifo_file_perms;
allow simple-server_t self:unix_stream_socket create_stream_socket_perms;
allow simple-server_t self:tcp_socket { bind create setopt accept listen shutdown write };
allow simple-server_t var_log_t:file { create open write };
allow simple-server_t var_log_t:dir { write add_name };

corenet_tcp_bind_generic_node(simple-server_t)
corenet_tcp_bind_http_cache_port(simple-server_t)
domain_use_interactive_fds(simple-server_t)
logging_manage_generic_logs(simple-server_t)
logging_rw_generic_log_dirs(simple-server_t)
  • simple-server SELinux Modul laden, simple-server Prozess per SystemD neu starten, Programm testen

5.6 Unter Debian Dienste nicht sofort nach der Installation starten

  • Datei /etc/systemd/system-preset/00-disable-all.preset anlegen mit dem Inhalt
disable *
  • Unter /usr/sbin/policyd-rc ein Shell Script erzeugen, welches Fehler #101 zurückliefert
# echo -e '#!/bin/bash\nexit 101' > /usr/sbin/policy-rc.d
# chmod +x /usr/sbin/policy-rc.d
# /usr/sbin/policy-rc.d
# echo $?
101

5.7 Secret Sharing

apt install ssss
  • Ein Geheimniss (Passwort) auf 5 Shares aufteilen, bei denen 3 Shares reichen um das Geheimnis wiederherzustellen
ssss-split -t 3 -n 5
  • Geheimnis aus 3 Shares wiederherstellen
ssss-combine -t 3

5.8 DNSSEC DNS-Resolver

  • Installation des Unbound DNS-Resolvers und des DNS-Kommandozeilen Werkzeuge dig unter Debian
apt install unbound dnsutils
  • DNSSEC Auflösung und Validierung testen
dig @localhost dnssec.works a
  • DNSSEC Signaturen anzeigen (+dnssec) und Ausgabe auf 80-Zeichen formatieren (+multi)
dig @localhost dnssec.works a +dnssec +multi
  • DNSSEC defekte Domains geben den Status SERVFAIL zurück. SERVFAIL kann aber auch andere Gründe (Serverfehler, Konfigurationsfehler) haben
dig @localhost fail01.dnssec.works a +dnssec +multi
  • der Schalter +cd (Checking Disabled) setzt die DNSSEC Validierung des DNS-Resovlers für eine Anfrage ausser Kraft; die fehlerhaften Daten werden angezeigt
dig @localhost fail01.dnssec.works a +dnssec +multi +cd

5.9 Dateisystem-Verschlüsselung

  • DNS Konfiguration wieder zurückstellen
echo "nameserver 192.168.1.5" > /etc/resolv.conf

5.9.1 ext4 mit Verschlüsselung

  • ACHTUNG: unter Debian 9 nicht die Boot-Partition mit ext4-Verschlüsselung betreiben. Der von Debian 9 benutzte Boot-Loader GRUB2 kann noch nicht von ext4 Dateisystemen mit Verschlüsselung booten
    • https://lwn.net/Articles/639427/
    • Installation der Linux Key-Utilities:
      apt install keyutils
      
    • Platz schaffen: die Swap-Partition abhängen, löschen, im verbleibenen Platz 2 neue Partitionen mit je 1 GB erstellen. Den verbleibenden Rest als Partition 5 für den Swap-Space einrichten. UUID in der Datei /etc/fstab anpassen und dann wieder als Swap-Partition einhängen
      swapoff /dev/sda3
      parted
      parted> p
      parted> rm <id-der-swap-partition>
      parted> mkpart part1 ext4 <start> <end>
      parted> mkpart part2 ext4 <start> <end>
      parted> mkpart swap ext4 <start> 100%
      parted> quit
      mkswap /dev/sda5
      swapon /dev/sda5
      
    • Die Partition /dev/sda3 mit ext4 formatieren (mit 4096 Byte Blöcken!)
      mkfs.ext4 -b 4096 /dev/sda3
      
    • Die ext4 Funktion "Verschlüsselung" im Dateisystem auf /dev/sda3 anschalten
      tune2fs -O encrypt /dev/sda3
      
    • Partition /dev/sda3 unter /encrypted einhängen
      mkdir /encrypted
      mount /dev/sda3 /encrypted
      
    • einen neuen Schlüssel dem Linux-Session-Keyring hinzufügen und dem Verzeichnis /encrypted zuordnen
      mkdir /encrypted/test
      e4crypt add_key /encrypted/test
      keyctl show
      e4crypt set_policy <key-id> /encrypted/test
      
    • Daten in das verschlüsselte Verzeichnis kopieren
      cp /etc/passwd /encrypted/test
      lsattr /encrypted/test
      
    • Verschieben auf dem gleichen Dateisystem in ein verschlüsseltes Verzeichnis geht nicht, die Dateien müssen neu angelegt werden!
      cp /etc/hosts.deny /encrypted
      mv /encrypted/hosts.deny /encrypted/test
      

5.9.2 dmcrypt/LUKS

  • LUKS Tools und LVM installieren
apt install cryptsetup lvm2
  • neues Physisches Volume in /dev/sda4 erstellen
pvcreate /dev/sda4
vgcreate crypt_vg /dev/sda4
  • ein neues logisches Volume im LVM erstellen (in der Volumegroup crypt_vg):
lvcreate --size 500M --name crypt crypt_vg
  • ein verschlüsseltes Laufwerk auf dem LV erstellen
cryptsetup --verbose --verify-passphrase luksFormat \
    /dev/mapper/crypt_vg-crypt
  • das verschlüsselte Laufwerk im Linux-Device-Mapper anmelden
cryptsetup luksOpen /dev/mapper/crypt_vg-crypt crypt
  • an dieser Stelle muss das neue Laufwerk nun im Device-Mapper auftauchen
ls -l /dev/mapper/crypt
  • Das 'crypt' Laufwerk formatieren (hier ext4, andere Dateisysteme und SWAP sind möglich)
mkfs.ext4 /dev/mapper/crypt
  • leeres Verzeichnis anlegen und das neue Dateisystem dort mounten:
mkdir /crypt
mount /dev/mapper/crypt /crypt
  • Konfigurationsdatei für verschlüsselte Laufwerke anlegen
$EDITOR /etc/crypttab
  • Eintrag für unser verschlüsseltes Laufwerk
crypt /dev/mapper/crypt_vg-crypt  none luks,timeout=180
  • Eintrag in der /etc/fstab
/dev/mapper/crypt     /crypt    ext4    defaults  0 0
  • Partition in der /etc/fstab eintragen, Reboot testen

5.10 Antivirus

5.10.1 ClamAV

  • ClamAV installieren
apt install clamav clamav-daemon clamav-base clamav-freshclam
  • ist SELinux aktiviert (z.B. CentOS/Red Hat), muss einen Antivirus-Scan im SELinux zulassen werden
setsebool -P antivirus_can_scan_system 1
setsebool -P clamd_use_jit on
  • FreshClam Konfiguration pruefen, Option DatabaseMirror von db.local.clamav.net auf db.de.clamav.net ändern, dann den FreshClam Dienst neu starten
$EDITOR /etc/clamav/freshclam.conf
systemctl restart clamav-freshclam
  • Im Journal nach Meldungen schauen
journalctl -u clamav-freshclam
  • Wurde FreshClam ohne Fehler gestartet?
systemctl status clamav-freshclam
  • Beispiel: Virensignaturen erfolgreich geladen
[root@notebook33 src]# systemctl status clamav-freshclam
freshclam.service - freshclam update ClamAV databases
   Loaded: loaded (/etc/systemd/system/freshclam.service; enabled)
   Active: inactive (dead) since Wed 2015-11-25 14:59:35 CET; 1min 2s ago
  Process: 28088 ExecStart=/usr/bin/freshclam -c 12 (code=exited, status=0/SUCCESS)
 Main PID: 28088 (code=exited, status=0/SUCCESS)

Nov 25 14:59:20 notebook33 freshclam[28088]: Downloading daily.cvd [100%]
Nov 25 14:59:21 notebook33 freshclam[28088]: [440B blob data]
Nov 25 14:59:29 notebook33 freshclam[28088]: daily.cvd updated (version: 21096, sigs: 1700789, f-level: 63, builder: neo)
Nov 25 14:59:29 notebook33 freshclam[28088]: daily.cvd updated (version: 21096, sigs: 1700789, f-level: 63, builder: neo)
Nov 25 14:59:29 notebook33 freshclam[28088]: [293B blob data]
Nov 25 14:59:29 notebook33 freshclam[28088]: Downloading bytecode.cvd [100%]
Nov 25 14:59:30 notebook33 freshclam[28088]: bytecode.cvd updated (version: 270, sigs: 46, f-level: 63, builder: shurley)
Nov 25 14:59:30 notebook33 freshclam[28088]: bytecode.cvd updated (version: 270, sigs: 46, f-level: 63, builder: shurley)
Nov 25 14:59:35 notebook33 freshclam[28088]: Database updated (4125060 signatures) from database.clamav.net (IP: 62.201.161.84)
Nov 25 14:59:35 notebook33 freshclam[28088]: Database updated (4125060 signatures) from database.clamav.net (IP: 62.201.161.84)
  • ClamAV Konfiguration prüfen
    clamconf | less
    
  • Manueller Scan (im Vordergrund)
clamscan -vr /usr
  • EICAR Test-Virus in einem Heimverzeichnis erstellen, Heimverzeichnisse scannen (per Clam-Daemon)
echo -n 'X5O!P%@AP[4\PZX54(P^)7CC)7}$EICAR-STANDARD-ANTIVIRUS-TEST-FILE!$H+H*' \
        > /home/nutzerXX/eicar.com
clamdscan -vr /home
  • ClamAV Daemon starten
    systemctl start clamav-daemon
    
  • Clam-Daemon Auslastung überwachen
    clamdtop
    
  • auf Virenfunde reagieren, in der Datei /etc/clamav/virusevent.d/ ein Shellskript anlegen
    #!/bin/sh
    logger -t virus "Signature detected: $CLAM_VIRUSEVENT_VIRUSNAME in $CLAM_VIRUSEVENT_FILENAME"
    
  • Shell Skript ausführbar machen
  • und in der Datei /etc/clamav/clamd.conf eintragen
    VirusEvent /etc/clamav/virusevent.d/log-virus
    

6 Anhang

6.1 Literaturliste

6.2 Linkliste

6.2.1 Crypto Wettbewerbe

6.2.2 Linux Zufallszahlen