second commit

This commit is contained in:
2026-08-19 22:33:19 +02:00
parent 411812e954
commit 199f306993
107 changed files with 5984 additions and 0 deletions

View File

@ -0,0 +1,71 @@
# CIS/STIG-Mapping der `os_hardening`-Rolle
Diese Rolle deckt eine bewusst ausgewählte, für einen dedizierten Jumphost
(Debian/Ubuntu-Familie) relevante Teilmenge der CIS Debian/Ubuntu Linux
Benchmarks sowie einzelner DISA-STIG-typischer Controls ab. Sie ist **kein**
vollständiger, zertifizierter Benchmark-Lauf — dafür fehlt ein automatisiertes
Compliance-Scanning-Tool. Empfehlung: nach dem Rollout zusätzlich mit
`oscap xccdf eval` (OpenSCAP) gegen das jeweils zutreffende Profil prüfen und
diese Tabelle bei Abweichungen als Startpunkt für die Nacharbeit nutzen.
Referenz-Control-IDs folgen der CIS-Nummerierung *sinngemäß* (Benchmark-Version
kann je nach Debian/Ubuntu-Release leicht abweichen); wo passend ist zusätzlich
vermerkt, wenn ein Control STIG-typisch, aber nicht CIS-nummeriert ist.
| Bereich | CIS-Control (sinngemäß) | Umsetzung | Status |
|---|---|---|---|
| Unsichere Legacy-Dienste | 2.1.x | `tasks/packages.yml` | Implementiert |
| Automatische Sicherheitsupdates | 1.9 | `tasks/packages.yml` | Implementiert |
| Deaktivierung seltener Dateisystemtreiber | 1.1.1.11.1.1.8 | `tasks/kernel_modules.yml` | Implementiert |
| Deaktivierung seltener Netzwerkprotokolle | 3.4.13.4.4 | `tasks/kernel_modules.yml` | Implementiert |
| USB-Speichermedien deaktivieren | 1.1.23 | `tasks/kernel_modules.yml` | Implementiert |
| Netzwerk-/Kernel-sysctl-Härtung | 3.x, 1.5.x | `tasks/sysctl.yml` | Implementiert |
| Core Dumps deaktivieren | 1.5.1 | `tasks/sysctl.yml` | Implementiert |
| `/tmp`, `/dev/shm` mit noexec/nosuid/nodev | 1.1.2.x | `tasks/mounts.yml` | **Best-Effort** — nur wirksam, wenn bereits eigene Mountpoints existieren (siehe unten) |
| Passwortqualität (Länge/Komplexität) | 5.4.1 | `tasks/pam_password_policy.yml` | Implementiert (lokale OS-Konten) |
| Account-Lockout (pam_faillock) | 5.3.1 | `tasks/pam_password_policy.yml` | Implementiert, siehe Einschränkung unten |
| Passwort-Historie | 5.4.2 | `tasks/pam_password_policy.yml` | Implementiert |
| Passwort-Ablauf (`login.defs`) | 5.4.1.15.4.1.4 | `tasks/pam_password_policy.yml` | Implementiert |
| Erweiterte auditd-Regeln (Identität, Zeit, Netzwerk, Logins, Kernelmodule) | 4.1.34.1.14 | `tasks/auditd.yml` | Implementiert |
| auditd-Log-Handling (keep_logs, space_left_action) | 4.1.2.3, 4.1.2.4 | `tasks/auditd.yml` | Implementiert |
| auditd unveränderlich (`-e 2`) | 4.1.1.4 (STIG) | `tasks/auditd.yml` | Implementiert, **standardmäßig deaktiviert** (Opt-in `os_hardening_auditd_immutable`, da Reboot zum Ändern nötig) |
| Datei-Integritäts-Monitoring (AIDE) | 1.4.1, 1.4.2 | `tasks/aide.yml` | Implementiert |
| Rootkit-Scanner (rkhunter, ergänzend) | STIG-typisch, nicht CIS-nummeriert | `tasks/rootkit_scan.yml` | Implementiert |
| Anmelde-Banner | STIG-typisch (Legal Notice) | `tasks/banners.yml`, `tasks/sshd.yml` | Implementiert |
| cron/at auf autorisierte Nutzer beschränken | 2.4.1.x | `tasks/cron_at.yml` | Implementiert |
| Berechtigungen kritischer Dateien (`/etc/shadow` etc.) | 6.1.x | `tasks/file_permissions.yml` | Implementiert |
| sudo-Logging (`log_input`, `log_output`, `use_pty`) | STIG-typisch | `tasks/file_permissions.yml` | Implementiert |
| `su` auf Gruppe `sudo` beschränken | 5.6 | `tasks/file_permissions.yml` | Implementiert |
| SSH-Daemon-Härtung (Ciphers/KEX/MACs, kein Root-Login, etc.) | 5.2.x | `tasks/sshd.yml` | Implementiert |
| IPv6 deaktivieren | 3.1.1 | `tasks/sysctl.yml` | Implementiert, **Opt-in** (`os_hardening_disable_ipv6`, Default aus) |
## Bewusst nicht automatisiert (mit Begründung)
| Bereich | CIS-Control (sinngemäß) | Warum nicht automatisiert |
|---|---|---|
| Getrenntes Partitionslayout (`/tmp`, `/var`, `/var/log`, `/var/log/audit`, `/home` als eigene Partitionen) | 1.1.11.1.1.30 | Erfordert eine Neupartitionierung der Festplatte — nur zum Zeitpunkt der OS-Installation sinnvoll setzbar, nicht nachträglich per Ansible auf ein laufendes System ohne Datenverlustrisiko. **Empfehlung:** beim Erstellen des Basis-Images/der VM-Vorlage bereits mit diesem Layout provisionieren. |
| Bootloader-Passwort (GRUB) | 1.4.1 (ältere CIS-Nummerierung) | Erfordert physischen/Konsolen-Zugriff zum Testen und ist bei Cloud-/Hypervisor-Images oft irrelevant oder sogar kontraproduktiv (verhindert automatisierten Neustart). Bewusst dem jeweiligen Betreiber überlassen. |
| Volltext-Festplattenverschlüsselung | Nicht CIS-nummeriert, STIG-typisch | Muss bei der OS-Installation eingerichtet werden (LUKS o.ä.), nicht nachträglich per Ansible. In Cloud-Umgebungen häufig durch Provider-seitige Verschlüsselung (z.B. verschlüsselte Volumes) abgedeckt — separat prüfen. |
| Physische Sicherheit / BIOS-UEFI-Passwort | Nicht CIS-nummeriert | Außerhalb der Reichweite von Ansible; organisatorische/physische Maßnahme. |
| Zentrales Log-Forwarding an SIEM/Syslog-Server | Nicht CIS-nummeriert, aber im Jumphost-Konzept Kap. 6.9 gefordert | Erfordert Kenntnis der Ziel-SIEM-Infrastruktur (Empfänger-Host, Protokoll, TLS-Zertifikate) — als eigener Konfigurationspunkt vorgesehen, aber nicht Teil dieser Rolle (siehe Konzept Kap. 6.9, Erweiterungspunkt). |
| Netzwerksegmentierung / Firewalling *zwischen* Zielsystemen | Außerhalb des Jumphost-Scopes | Betrifft die Netzwerkinfrastruktur rund um den Jumphost, nicht den Jumphost selbst — Bestandteil des übergeordneten Netzwerkkonzepts. |
## Bekannte Einschränkungen
- **`pam_faillock`-Einbindung** (`tasks/pam_password_policy.yml`) fügt einen
Block direkt in `/etc/pam.d/common-auth` ein. Auf Systemen, die
`pam-auth-update --force` als Teil eines anderen Automatisierungsschritts
laufen lassen, kann dieser Block überschrieben werden. Für produktive
Systeme mit häufigen PAM-Änderungen wird empfohlen, stattdessen ein
eigenes `pam-auth-update`-Profil unter `/usr/share/pam-configs/` zu
pflegen (sauberer, upgrade-fest) — hier aus Gründen der Nachvollziehbarkeit
als direkter Block-Insert gehalten.
- **`/tmp`/`/dev/shm`-Remount** wirkt nur, wenn diese Pfade bereits eigene
Mountpoints sind. Ist das nicht der Fall, gibt die Rolle eine Debug-Meldung
aus, ändert aber nichts automatisch am Partitionslayout (siehe Tabelle oben).
- Diese Rolle wurde in dieser Session **nicht** gegen ein reales
Debian/Ubuntu-System ausgeführt (kein Root-/Zielsystem verfügbar) — nur
`ansible-playbook --syntax-check` sowie YAML-/Jinja2-Parsing wurden
verifiziert (siehe README, Abschnitt „Was bewusst noch offen ist"). Ein
`--check`-Lauf (Dry-Run) und ein realer Rollout-Test in einer
Staging-Umgebung stehen vor dem Produktivbetrieb noch aus.

View File

@ -0,0 +1,47 @@
---
# Schalter fuer die vertiefte CIS/STIG-nahe Haertung dieser Rolle.
# Siehe ansible/roles/os_hardening/CIS_STIG_MAPPING.md fuer die Zuordnung
# der einzelnen Tasks zu konkreten Benchmark-Controls sowie fuer alles, was
# hier bewusst NICHT automatisiert wird (mit Begruendung).
# AIDE (Dateiintegritaets-Monitoring) initialisieren + taeglichen Check-Timer
# einrichten. Erzeugt beim ersten Lauf spuerbare I/O-Last (voller Datei-Scan).
os_hardening_aide_enabled: true
# rkhunter als zusaetzlicher, ergaenzender Rootkit-/Anomalie-Scanner.
os_hardening_rkhunter_enabled: true
# /tmp und /dev/shm nach Moeglichkeit mit noexec,nosuid,nodev neu einhaengen
# (nur wenn sie BEREITS eigene Mountpoints sind -- siehe Mapping-Dokument
# Punkt "Partitionslayout" fuer den nicht automatisierbaren Teil).
os_hardening_restrict_tmp_mounts: true
# Der Anmelde-Banner-Text (STIG-typischer Warnhinweis). An lokale
# Rechtslage/Policy anpassen.
os_hardening_login_banner: |
******************************************************************
* Autorisierter Zugriff ausschliesslich fuer befugte Personen. *
* Alle Aktivitaeten auf diesem System werden protokolliert und *
* koennen im Rahmen von Sicherheitsuntersuchungen ausgewertet *
* werden. Mit der Anmeldung stimmen Sie dieser Ueberwachung zu. *
******************************************************************
# IPv6 komplett deaktivieren. Standardmaessig AUS (false), da dies in vielen
# Umgebungen ungewollte Nebenwirkungen hat (z.B. wenn Zielsysteme oder das
# Management-Netz IPv6 nutzen) -- bewusst ein Opt-in, kein stiller Default.
os_hardening_disable_ipv6: false
# auditd-Konfiguration als UNVERAENDERLICH markieren (-e 2 am Ende der
# Regeln). Das ist die STIG-konforme Einstellung, erfordert danach aber
# einen Reboot, um die Audit-Regeln ueberhaupt noch aendern zu koennen --
# daher standardmaessig AUS, damit iterative Playbook-Laeufe waehrend des
# Aufbaus nicht versehentlich aussperren. Vor der finalen Abnahme auf true setzen.
os_hardening_auditd_immutable: false
# Passwortrichtlinie fuer LOKALE OS-Konten auf dem Jumphost selbst (NICHT zu
# verwechseln mit der Argon2id/TOTP-Pflicht der Jumphost-WEBANWENDUNG, die
# unabhaengig davon in app/security/passwords.py + totp.py durchgesetzt wird).
os_hardening_password_min_length: 14
os_hardening_password_remember: 5
os_hardening_faillock_deny: 5
os_hardening_faillock_unlock_time: 900

View File

@ -0,0 +1,32 @@
---
- name: restart auditd
ansible.builtin.service:
name: auditd
state: restarted
- name: restart sshd
ansible.builtin.service:
name: ssh
state: restarted
- name: apply sysctl
ansible.builtin.command: sysctl --system
changed_when: true
- name: update initramfs
ansible.builtin.command: update-initramfs -u
changed_when: true
- name: remount tmp
ansible.builtin.command: mount -o remount /tmp
changed_when: true
failed_when: false # best effort, siehe Kommentar in tasks/mounts.yml
- name: remount shm
ansible.builtin.command: mount -o remount /dev/shm
changed_when: true
failed_when: false
- name: reload systemd (os_hardening)
ansible.builtin.systemd:
daemon_reload: true

View File

@ -0,0 +1,86 @@
---
# CIS 1.4.x: AIDE (Advanced Intrusion Detection Environment) als
# Datei-Integritaets-Monitoring. Ergaenzt das Anwendungs-Audit-Log
# (manipulationssichere Hash-Chain, Konzept 6.1) um eine unabhaengige,
# dateisystemweite Kontrolle -- erkennt z.B. Aenderungen an Systembinaries
# oder eingeschleuste Dateien ausserhalb des Anwendungscodes.
- name: AIDE installieren
ansible.builtin.apt:
name: aide
state: present
update_cache: true
when: os_hardening_aide_enabled
- name: Pruefen, ob bereits eine AIDE-Datenbank existiert
ansible.builtin.stat:
path: /var/lib/aide/aide.db
register: _aide_db
when: os_hardening_aide_enabled
- name: Initiale AIDE-Datenbank aufbauen (kann einige Minuten dauern)
ansible.builtin.command: aideinit -y -f
when: os_hardening_aide_enabled and not _aide_db.stat.exists
async: 1800
poll: 30
- name: AIDE-Wrapper-Skript fuer den taeglichen Check ausrollen
ansible.builtin.copy:
dest: /usr/local/sbin/jumphost-aide-check.sh
owner: root
group: root
mode: "0750"
content: |
#!/usr/bin/env bash
set -euo pipefail
LOGFILE=/var/log/jumphost/aide-check.log
/usr/bin/aide.wrapper --check >> "$LOGFILE" 2>&1 || {
logger -p authpriv.warning "AIDE hat Dateisystem-Abweichungen gemeldet, siehe $LOGFILE"
}
when: os_hardening_aide_enabled
- name: systemd-Service fuer taeglichen AIDE-Check
ansible.builtin.copy:
dest: /etc/systemd/system/jumphost-aide-check.service
owner: root
group: root
mode: "0644"
content: |
[Unit]
Description=Taeglicher AIDE-Dateiintegritaets-Check
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/jumphost-aide-check.sh
Nice=10
IOSchedulingClass=idle
when: os_hardening_aide_enabled
notify: reload systemd (os_hardening)
- name: systemd-Timer fuer taeglichen AIDE-Check
ansible.builtin.copy:
dest: /etc/systemd/system/jumphost-aide-check.timer
owner: root
group: root
mode: "0644"
content: |
[Unit]
Description=Taeglicher AIDE-Dateiintegritaets-Check (Timer)
[Timer]
OnCalendar=daily
RandomizedDelaySec=3600
Persistent=true
[Install]
WantedBy=timers.target
when: os_hardening_aide_enabled
notify: reload systemd (os_hardening)
- name: AIDE-Check-Timer aktivieren
ansible.builtin.systemd:
name: jumphost-aide-check.timer
daemon_reload: true
enabled: true
state: started
when: os_hardening_aide_enabled

View File

@ -0,0 +1,98 @@
---
# CIS 4.1.x / DISA-STIG-aehnliche Audit-Regeln. Ueber die bereits vorhandene
# Ueberwachung des Jumphost-Datenverzeichnisses hinaus wird hier ein
# Standard-Ruleset fuer sicherheitsrelevante OS-Ereignisse ergaenzt:
# Identitaets-/Rechteaenderungen, privilegierte Kommandos, Zeit-/
# Netzwerkkonfigurationsaenderungen, Login-Ereignisse, Modulladen.
- name: auditd + audispd-plugins installieren
ansible.builtin.apt:
name:
- auditd
- audispd-plugins
state: present
update_cache: true
- name: Bestehende auditd-Regeln fuer das Jumphost-Datenverzeichnis
ansible.builtin.copy:
dest: /etc/audit/rules.d/10-jumphost-app.rules
owner: root
group: root
mode: "0640"
content: |
-w {{ jumphost_data_dir }}/jumphost.db -p wa -k jumphost_db
-w {{ jumphost_home }} -p wa -k jumphost_app_files
-w /etc/jumphost -p wa -k jumphost_config
notify: restart auditd
- name: Erweiterte CIS/STIG-Audit-Regeln fuer das Basissystem
ansible.builtin.copy:
dest: /etc/audit/rules.d/20-cis-baseline.rules
owner: root
group: root
mode: "0640"
content: |
# Identitaets-/Rechteaenderungen (CIS 4.1.4)
-w /etc/passwd -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/gshadow -p wa -k identity
-w /etc/sudoers -p wa -k identity
-w /etc/sudoers.d/ -p wa -k identity
# Sudo-Nutzung protokollieren (ergaenzt Defaults logfile in
# sudo_logging.yml um eine auditd-seitige, manipulationsresistentere Spur)
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid!=0 -F auid!=-1 -k privileged_sudo
# Zeitaenderungen (CIS 4.1.3)
-a always,exit -F arch=b64 -S adjtimex,settimeofday,clock_settime -k time_change
-w /etc/localtime -p wa -k time_change
# Netzwerkkonfiguration (CIS 4.1.7)
-w /etc/hosts -p wa -k network_config
-w /etc/network/ -p wa -k network_config
-w /etc/nftables.conf -p wa -k network_config
# Login/Logout-Ereignisse (CIS 4.1.5)
-w /var/log/faillog -p wa -k logins
-w /var/log/lastlog -p wa -k logins
-w /var/run/utmp -p wa -k session
-w /var/log/wtmp -p wa -k session
-w /var/log/btmp -p wa -k session
# Kernel-Modul-Laden/-Entladen (CIS 4.1.13)
-a always,exit -F arch=b64 -S init_module,delete_module -k kernel_modules
# SSH-Konfigurationsaenderungen des Jumphosts selbst
-w /etc/ssh/sshd_config -p wa -k sshd_config
-w /etc/ssh/sshd_config.d/ -p wa -k sshd_config
# Loeschungen durch Nutzer (CIS 4.1.14, exemplarisch fuer den eigenen UID-Bereich)
-a always,exit -F arch=b64 -S unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=-1 -k file_deletion
notify: restart auditd
- name: auditd-Regeln als unveraenderlich markieren (STIG, optional)
ansible.builtin.copy:
dest: /etc/audit/rules.d/99-immutable.rules
owner: root
group: root
mode: "0640"
content: |
# ACHTUNG: nach Aktivierung sind Aenderungen an den Audit-Regeln erst
# nach einem Reboot wieder moeglich (auditctl -e 2 sperrt bis Neustart).
-e 2
when: os_hardening_auditd_immutable
notify: restart auditd
- name: auditd-Log-Rotation auf "keep_logs" setzen statt Ueberschreiben (CIS 4.1.2.3)
ansible.builtin.lineinfile:
path: /etc/audit/auditd.conf
regexp: '^max_log_file_action\s*='
line: "max_log_file_action = keep_logs"
notify: restart auditd
- name: auditd bei vollem Log-Speicher anhalten statt Ereignisse zu verwerfen (CIS 4.1.2.4)
ansible.builtin.lineinfile:
path: /etc/audit/auditd.conf
regexp: '^space_left_action\s*='
line: "space_left_action = email"

View File

@ -0,0 +1,22 @@
---
# STIG-typische Anmelde-Warnhinweise (rechtlich in vielen Organisationen
# vorgeschrieben, bevor Zugriff auf ein System gewaehrt wird).
- name: Anmelde-Banner setzen (/etc/issue, /etc/issue.net, /etc/motd)
ansible.builtin.copy:
dest: "{{ item }}"
owner: root
group: root
mode: "0644"
content: "{{ os_hardening_login_banner }}"
loop:
- /etc/issue
- /etc/issue.net
- /etc/motd
# Kein Serviceneustart noetig: /etc/issue* und /etc/motd werden bei
# jedem neuen Login/jeder neuen Verbindung frisch gelesen.
# Die "Banner /etc/issue.net"-Direktive fuer sshd wird zentral in sshd.yml
# gesetzt (dieselbe Datei /etc/ssh/sshd_config.d/99-jumphost-hardening.conf
# wird dort komplett -- inkl. Banner-Zeile -- verwaltet, um zwei Tasks mit
# widerspruechlichem "wer besitzt diese Datei" zu vermeiden).

View File

@ -0,0 +1,36 @@
---
# CIS 2.4.1.x: cron/at auf autorisierte Nutzer beschraenken.
- name: cron.deny/at.deny entfernen (deny-Listen sind fehleranfaelliger als allow-Listen)
ansible.builtin.file:
path: "{{ item }}"
state: absent
loop:
- /etc/cron.deny
- /etc/at.deny
- name: cron.allow / at.allow auf root und den Jumphost-Service-User beschraenken
ansible.builtin.copy:
dest: "{{ item }}"
owner: root
group: root
mode: "0600"
content: |
root
loop:
- /etc/cron.allow
- /etc/at.allow
- name: Berechtigungen der cron-Verzeichnisse absichern (CIS 2.4.1.7-2.4.1.11)
ansible.builtin.file:
path: "{{ item }}"
owner: root
group: root
mode: "0700"
loop:
- /etc/cron.d
- /etc/cron.daily
- /etc/cron.hourly
- /etc/cron.monthly
- /etc/cron.weekly
ignore_errors: true # nicht jedes Basis-Image legt alle Verzeichnisse an

View File

@ -0,0 +1,38 @@
---
# CIS 6.1.x: Berechtigungen sicherheitskritischer Systemdateien; zusaetzlich
# manipulationssicheres sudo-Logging (ergaenzt die auditd-Regel
# "privileged_sudo" aus auditd.yml um ein menschenlesbares Log).
- name: Berechtigungen sicherheitskritischer Dateien absichern
ansible.builtin.file:
path: "{{ item.path }}"
owner: root
group: "{{ item.group }}"
mode: "{{ item.mode }}"
loop:
- { path: /etc/passwd, group: root, mode: "0644" }
- { path: /etc/group, group: root, mode: "0644" }
- { path: /etc/shadow, group: shadow, mode: "0640" }
- { path: /etc/gshadow, group: shadow, mode: "0640" }
- { path: /etc/ssh/sshd_config, group: root, mode: "0600" }
ignore_errors: true # z.B. wenn die shadow-Gruppe distributionsabhaengig anders heisst
- name: Eigenstaendiges sudo-Logfile aktivieren
ansible.builtin.copy:
dest: /etc/sudoers.d/99-jumphost-logging
owner: root
group: root
mode: "0440"
validate: "visudo -cf %s"
content: |
Defaults logfile="/var/log/sudo.log"
Defaults log_input, log_output
Defaults use_pty
Defaults passwd_tries=3
- name: su-Kommando auf die Gruppe "sudo" beschraenken (CIS 5.6)
ansible.builtin.lineinfile:
path: /etc/pam.d/su
regexp: '^#?\s*auth\s+required\s+pam_wheel\.so'
line: "auth required pam_wheel.so use_uid group=sudo"
insertafter: '^# Uncomment this'

View File

@ -0,0 +1,41 @@
---
# CIS 1.1.1.x: selten benoetigte Dateisysteme und exotische Netzwerk-
# protokolle per modprobe-Blacklist deaktivieren. Ein dedizierter Jumphost
# braucht keines davon; jedes deaktivierte Modul ist Angriffsflaeche weniger
# (u.a. relevant fuer angeschlossene Wechseldatentraeger und historische
# Kernel-CVEs in selten gepflegten Dateisystemtreibern).
- name: Kernelmodule fuer seltene Dateisysteme/Protokolle blacklisten
ansible.builtin.copy:
dest: /etc/modprobe.d/jumphost-blacklist.conf
owner: root
group: root
mode: "0644"
content: |
# CIS 1.1.1.1 - 1.1.1.8 (sinngemaess): unbenoetigte Dateisysteme
install cramfs /bin/false
install freevxfs /bin/false
install jffs2 /bin/false
install hfs /bin/false
install hfsplus /bin/false
install udf /bin/false
install squashfs /bin/false
# CIS 3.4.x (sinngemaess): unbenoetigte/seltene Netzwerkprotokolle
install dccp /bin/false
install sctp /bin/false
install rds /bin/false
install tipc /bin/false
notify: update initramfs
- name: USB-Speichermedien deaktivieren (CIS 1.1.23, sinngemaess)
ansible.builtin.copy:
dest: /etc/modprobe.d/jumphost-usb-storage.conf
owner: root
group: root
mode: "0644"
content: |
# Ein Jumphost sollte keine lokal angeschlossenen USB-Speichermedien
# einbinden muessen -- Dateitransfer laeuft ausschliesslich ueber die
# Anwendung (SFTP/RDP-Laufwerksumleitung), siehe Konzept 4.3.
install usb-storage /bin/false
notify: update initramfs

View File

@ -0,0 +1,47 @@
---
# Betriebssystem-Haertung fuer den Jumphost-Server selbst (Konzept 6.7),
# vertieft auf CIS/STIG-nahes Niveau. Siehe CIS_STIG_MAPPING.md in diesem
# Rollenverzeichnis fuer die Zuordnung der einzelnen Tasks zu konkreten
# Benchmark-Controls sowie fuer alles, was bewusst NICHT automatisiert wird
# (mit Begruendung, z.B. Partitionslayout, Bootloader-Passwort, physische
# Sicherheit).
#
# Kein Ersatz fuer ein vollstaendiges CIS/STIG-Auditwerkzeug (z.B. OpenSCAP)
# -- jeder produktive Rollout sollte zusaetzlich mit `oscap xccdf eval`
# gegen das jeweilige Benchmark-Profil verifiziert werden (siehe README).
- name: Pakete & automatische Updates
ansible.builtin.import_tasks: packages.yml
- name: Kernelmodule (Dateisysteme/Protokolle) einschraenken
ansible.builtin.import_tasks: kernel_modules.yml
- name: sysctl- und Core-Dump-Haertung
ansible.builtin.import_tasks: sysctl.yml
- name: Mount-Optionen (/tmp, /dev/shm)
ansible.builtin.import_tasks: mounts.yml
- name: PAM-/Passwort-Policy fuer lokale OS-Konten
ansible.builtin.import_tasks: pam_password_policy.yml
- name: Erweiterte auditd-Regeln
ansible.builtin.import_tasks: auditd.yml
- name: AIDE-Dateiintegritaets-Monitoring
ansible.builtin.import_tasks: aide.yml
- name: rkhunter-Rootkit-Scanner
ansible.builtin.import_tasks: rootkit_scan.yml
- name: Anmelde-Banner
ansible.builtin.import_tasks: banners.yml
- name: cron/at auf autorisierte Nutzer beschraenken
ansible.builtin.import_tasks: cron_at.yml
- name: Dateirechte & sudo-Logging
ansible.builtin.import_tasks: file_permissions.yml
- name: SSH-Daemon des Jumphosts haerten
ansible.builtin.import_tasks: sshd.yml

View File

@ -0,0 +1,48 @@
---
# CIS 1.1.2.x (nodev/nosuid/noexec auf /tmp, /dev/shm). Best-Effort: nur
# wirksam, wenn diese Pfade BEREITS eigene Mountpoints sind. Ob das der Fall
# ist, haengt vom Partitionslayout des Basis-Images ab -- siehe
# CIS_STIG_MAPPING.md fuer den Hinweis, dass ein vollstaendig CIS-konformes
# Partitionslayout (separate /tmp, /var, /var/log, /var/log/audit, /home)
# eine bewusste Entscheidung bei der OS-Installation ist und nicht nachtraeglich
# per Ansible auf ein bestehendes System aufgepraegt werden kann, ohne die
# Platte neu zu partitionieren.
- name: Pruefen, ob /tmp ein eigener Mountpoint ist
ansible.builtin.command: findmnt --noheadings --output SOURCE /tmp
register: _tmp_mount
changed_when: false
failed_when: false
- name: /tmp mit noexec,nosuid,nodev in /etc/fstab absichern (falls eigener Mountpoint)
ansible.builtin.lineinfile:
path: /etc/fstab
regexp: '^\S+\s+/tmp\s+'
line: "{{ _tmp_mount.stdout }} /tmp tmpfs defaults,noexec,nosuid,nodev 0 0"
backup: true
when: os_hardening_restrict_tmp_mounts and _tmp_mount.rc == 0 and _tmp_mount.stdout != ''
notify: remount tmp
- name: Pruefen, ob /dev/shm ein eigener Mountpoint ist
ansible.builtin.command: findmnt --noheadings --output SOURCE /dev/shm
register: _shm_mount
changed_when: false
failed_when: false
- name: /dev/shm mit noexec,nosuid,nodev in /etc/fstab absichern (falls eigener Mountpoint)
ansible.builtin.lineinfile:
path: /etc/fstab
regexp: '^\S+\s+/dev/shm\s+'
line: "{{ _shm_mount.stdout }} /dev/shm tmpfs defaults,noexec,nosuid,nodev 0 0"
backup: true
when: os_hardening_restrict_tmp_mounts and _shm_mount.rc == 0 and _shm_mount.stdout != ''
notify: remount shm
- name: Hinweis, falls /tmp kein eigener Mountpoint ist
ansible.builtin.debug:
msg: >
/tmp ist kein eigener Mountpoint auf diesem System -- noexec/nosuid/nodev
koennen so nicht erzwungen werden. Fuer volle CIS-Konformitaet muesste
/tmp bei der OS-Installation als eigene Partition/eigenes tmpfs angelegt
werden (siehe CIS_STIG_MAPPING.md).
when: os_hardening_restrict_tmp_mounts and (_tmp_mount.rc != 0 or _tmp_mount.stdout == '')

View File

@ -0,0 +1,33 @@
---
# CIS 2.x (Services) / STIG-aequivalent: unsichere Legacy-Dienste/-Clients
# entfernen, automatische Sicherheitsupdates aktivieren.
- name: Unnoetige/unsichere Pakete entfernen
ansible.builtin.apt:
name: "{{ item }}"
state: absent
purge: true
loop:
- telnet
- rsh-client
- talk
- nis # ypbind etc. -- veraltete, unverschluesselte Netzwerkdienste
- tftpd-hpa
- xinetd
ignore_errors: true
- name: Automatische Sicherheitsupdates installieren
ansible.builtin.apt:
name: unattended-upgrades
state: present
update_cache: true
- name: Unattended-upgrades aktivieren
ansible.builtin.copy:
dest: /etc/apt/apt.conf.d/20auto-upgrades
owner: root
group: root
mode: "0644"
content: |
APT::Periodic::Update-Package-Lists "1";
APT::Periodic::Unattended-Upgrade "1";

View File

@ -0,0 +1,83 @@
---
# CIS 5.3.x / 5.4.x: Passwortqualitaet, Account-Lockout und Ablaufregeln fuer
# LOKALE OS-Konten auf dem Jumphost selbst (Admin-SSH-Zugang zum Jumphost-
# Server, siehe ssh_admin_access_cidr in group_vars/all.yml).
#
# WICHTIG -- Abgrenzung: Dies ist NICHT identisch mit der Argon2id/TOTP-
# Pflicht der Jumphost-WEBANWENDUNG (siehe app/security/passwords.py,
# app/security/totp.py, app/auth/routes.py). Es handelt sich um zwei
# getrennte Konten-/Auth-Systeme: die App verwaltet ihre eigenen Nutzer in
# SQLite, waehrend hier die BS-Konten der Administratoren gehaertet werden,
# die sich per SSH auf den Jumphost-Server selbst einloggen (z.B. fuer
# Wartung, Deployment, Log-Einsicht).
- name: libpam-pwquality installieren
ansible.builtin.apt:
name: libpam-pwquality
state: present
update_cache: true
- name: Passwortqualitaets-Policy setzen (CIS 5.4.1)
# Direkt in pwquality.conf statt eines conf.d-Snippets, da nicht jede
# Distributionsversion von libpam-pwquality ein conf.d-Verzeichnis
# unterstuetzt -- pwquality.conf selbst wird ueberall gelesen.
ansible.builtin.lineinfile:
path: /etc/security/pwquality.conf
regexp: "^#?\\s*{{ item.key }}\\s*="
line: "{{ item.key }} = {{ item.value }}"
create: true
owner: root
group: root
mode: "0644"
loop:
- { key: "minlen", value: "{{ os_hardening_password_min_length }}" }
- { key: "dcredit", value: "-1" }
- { key: "ucredit", value: "-1" }
- { key: "ocredit", value: "-1" }
- { key: "lcredit", value: "-1" }
- { key: "retry", value: "3" }
# Kein Service-Neustart noetig: PAM liest die Datei bei jeder neuen
# Authentifizierung, kein laufender Daemon haelt sie offen.
- name: Passwortqualitaet auch fuer root erzwingen
ansible.builtin.lineinfile:
path: /etc/security/pwquality.conf
regexp: "^#?\\s*enforce_for_root"
line: "enforce_for_root"
create: true
- name: pam_faillock fuer Login-Lockout aktivieren (CIS 5.3.1)
ansible.builtin.blockinfile:
path: /etc/pam.d/common-auth
marker: "# {mark} ANSIBLE MANAGED BLOCK (jumphost pam_faillock)"
insertbefore: "^auth\\s+\\[success=1"
block: |
auth required pam_faillock.so preauth silent deny={{ os_hardening_faillock_deny }} unlock_time={{ os_hardening_faillock_unlock_time }}
auth [success=1 default=ignore] pam_unix.so nullok
auth [default=die] pam_faillock.so authfail deny={{ os_hardening_faillock_deny }} unlock_time={{ os_hardening_faillock_unlock_time }}
auth sufficient pam_faillock.so authsucc deny={{ os_hardening_faillock_deny }} unlock_time={{ os_hardening_faillock_unlock_time }}
# Hinweis: pam-auth-update-verwaltete Systeme (Debian/Ubuntu-Standard)
# ueberschreiben common-auth ggf. bei "pam-auth-update --force". Fuer
# produktive Systeme ist die Nutzung eines eigenen pam-auth-update-Profils
# (/usr/share/pam-configs/jumphost-faillock) die sauberere, upgrade-feste
# Alternative -- hier aus Uebersichtlichkeitsgruenden als direkter Block-
# Insert gehalten und im Mapping-Dokument als bekannte Einschraenkung vermerkt.
- name: Passwort-Ablaufregeln in /etc/login.defs setzen (CIS 5.4.1.1-5.4.1.4)
ansible.builtin.lineinfile:
path: /etc/login.defs
regexp: "^{{ item.key }}\\s"
line: "{{ item.key }} {{ item.value }}"
loop:
- { key: "PASS_MAX_DAYS", value: "90" }
- { key: "PASS_MIN_DAYS", value: "7" }
- { key: "PASS_WARN_AGE", value: "14" }
- { key: "UMASK", value: "027" }
- { key: "ENCRYPT_METHOD", value: "SHA512" }
- name: Passwort-Historie (pam_pwhistory) aktivieren (CIS 5.4.2)
ansible.builtin.lineinfile:
path: /etc/pam.d/common-password
regexp: '^password\s+requisite\s+pam_pwhistory\.so'
insertafter: '^password\s+requisite\s+pam_pwquality\.so'
line: "password requisite pam_pwhistory.so remember={{ os_hardening_password_remember }} use_authtok"

View File

@ -0,0 +1,24 @@
---
# Ergaenzender Rootkit-/Anomalie-Scanner (rkhunter). Kein Ersatz fuer AIDE
# (dateibasierte Integritaet) oder auditd (Ereignisprotokoll), sondern eine
# dritte, unabhaengige Kontrollschicht mit eigener Signaturheuristik.
- name: rkhunter installieren
ansible.builtin.apt:
name: rkhunter
state: present
update_cache: true
when: os_hardening_rkhunter_enabled
- name: rkhunter-Property-Datenbank initialisieren
ansible.builtin.command: rkhunter --propupd
when: os_hardening_rkhunter_enabled
changed_when: true
- name: Woechentlichen rkhunter-Check per systemd-Timer aktivieren (Debian-Paket-Default nutzen)
ansible.builtin.lineinfile:
path: /etc/default/rkhunter
regexp: '^CRON_DAILY_RUN='
line: 'CRON_DAILY_RUN="true"'
create: true
when: os_hardening_rkhunter_enabled

View File

@ -0,0 +1,32 @@
---
# SSH-Daemon des Jumphosts SELBST (administrativer Zugriff auf den Server) --
# nicht zu verwechseln mit der SSH-Proxy-Funktion der Anwendung (app/ssh_proxy),
# die eigene, unabhaengige Verbindungen zu den Zielsystemen aufbaut.
# CIS 5.2.x.
- name: SSH-Daemon des Jumphosts haerten (vollstaendige Konfiguration inkl. Banner)
ansible.builtin.copy:
dest: /etc/ssh/sshd_config.d/99-jumphost-hardening.conf
owner: root
group: root
mode: "0644"
content: |
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no
MaxAuthTries 3
MaxSessions 4
LoginGraceTime 20
ClientAliveInterval 300
ClientAliveCountMax 2
Banner /etc/issue.net
LogLevel VERBOSE
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com
notify: restart sshd

View File

@ -0,0 +1,58 @@
---
# CIS 3.x (Netzwerk) / 1.5.x (Kernel-Haertung). Bewusst ueber
# ansible.builtin.copy + "sysctl --system" statt des ansible.posix.sysctl-
# Moduls (siehe Kommentar in Kap. 7/README) -- keine Zusatz-Collection als
# Voraussetzung fuer den Betrieb dieses Playbooks.
- name: Kernel-/Netzwerk-Haertung (sysctl)
ansible.builtin.copy:
dest: /etc/sysctl.d/99-jumphost-hardening.conf
owner: root
group: root
mode: "0644"
content: |
# --- Netzwerk (CIS 3.x) ---
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
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
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.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.ip_forward = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv6.conf.all.accept_source_route = 0
net.ipv6.conf.default.accept_source_route = 0
{% if os_hardening_disable_ipv6 %}
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1
{% endif %}
# --- Kernel-Haertung (CIS 1.5.x) ---
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.randomize_va_space = 2
kernel.yama.ptrace_scope = 1
fs.suid_dumpable = 0
fs.protected_hardlinks = 1
fs.protected_symlinks = 1
fs.protected_fifos = 2
fs.protected_regular = 2
notify: apply sysctl
- name: Core Dumps zusaetzlich auf PAM-Ebene deaktivieren (CIS 1.5.1)
ansible.builtin.copy:
dest: /etc/security/limits.d/99-jumphost-no-coredumps.conf
owner: root
group: root
mode: "0644"
content: |
* hard core 0