Linux

Linux - ZFS

Linux - ZFS

Rollback

1 | Snapshots anzeigen

Ein Snapshot ist eine schreibgeschützte Momentaufnahme Ihres Dateisystems.
Liste aller vorhandenen Snapshots anzeigen:

zfs list -t snapshot

Beispielausgabe (mit ausgedachten Namen & Größen):

NAME                                 USED  AVAIL  REFER  MOUNTPOINT
poolalpha/data@2025-07-14_06-04-31   248M      -   1.3T  -
poolalpha/data@2025-07-16_00-39-41   253M      -   1.4T  -
tank/media@2025-07-02_23-13-47        25G      -    13T  -
tank/media@2025-07-16_00-39-40        1T      -    19T  -

2 | Rollback (zu einem Snapshot zurückkehren)

  1. Kopieren Sie den vollständigen Snapshot-Namen aus der Liste (pool/dataset@datum_uhrzeit).
  2. Führen Sie den Rollback-Befehl aus:
    zfs rollback poolalpha/data@2025-07-14_06-04-31
  3. Tipp für Fortgeschrittene:
    Mit -r (recursive) werden automatisch alle neueren Snapshots entfernt, bevor der Rollback ausgeführt wird.
    zfs rollback -r poolalpha/data@2025-07-14_06-04-31

3 | Snapshot manuell erstellen (heutiges Datum)

Einfach im Terminal einen Einzeiler eingeben:

# Zeitstempel-Variable im Format YYYY-MM-DD_HH-MM-SS
TS=$(date +"%Y-%m-%d_%H-%M-%S")

# Snapshot für ein einzelnes Dataset
zfs snapshot poolalpha/data@$TS

# Rekursiv für einen gesamten Dataset-Baum
zfs snapshot -r tank@$TS

4 | Häufige Fragen & praktische Tipps

Was passiert beim Rollback mit meinen neueren Daten ?

Alle Blöcke, die nach dem gewählten Snapshot erzeugt wurden, gehen verloren. Sichern Sie daher wichtige Dateien zuerst oder testen den Snapshot mit einem Clone:

zfs clone poolalpha/data@2025-07-14_06-04-31 poolalpha/data-test
Kann ich einen Snapshot gegen Löschen schützen ?

Ja – mit einem sogenannten Hold:

zfs hold keep poolalpha/data@2025-07-14_06-04-31
# Aufheben:
zfs release keep poolalpha/data@2025-07-14_06-04-31
Wie schiebe ich einen Snapshot auf einen Backup-Server ?
zfs send -vP poolalpha/data@$TS | \
ssh backuphost zfs recv -uF backuppool/data
Linux - ZFS

ZFS modules cannot be auto-loaded

ZFS-DKMS – Schritt-für-Schritt-Anleitung für Debian 13 (Trixie)

Diese Anleitung fasst die notwendigen Schritte zusammen, um OpenZFS als DKMS-Modul auf Debian 13 zu installieren, nach Kernel-Updates neu zu bauen und typische Fehler zu beheben. Besonders wichtig ist der Fall, dass ZFS zwar erfolgreich für einen neuen Kernel gebaut wurde, das System aber noch mit einem alten Kernel läuft.


0  Wichtig: Laufender Kernel und DKMS-Modul müssen zusammenpassen

DKMS baut Kernelmodule immer für bestimmte Kernelversionen. modprobe lädt Module aber nur für den aktuell laufenden Kernel. Der aktuell laufende Kernel wird so angezeigt:

uname -r

Der DKMS-Status wird so angezeigt:

dkms status
Wichtig: Wenn uname -r einen anderen Kernel zeigt als dkms status, dann ist ZFS nicht für den aktuell laufenden Kernel installiert. In diesem Fall hilft oft kein weiterer Build, sondern ein Neustart in den Kernel, für den DKMS das Modul bereits gebaut hat.

Beispiel:

uname -r
6.12.74+deb13+1-amd64
dkms status
zfs/2.3.2, 6.12.90+deb13.1-amd64, x86_64: installed

In diesem Beispiel läuft das System noch mit Kernel 6.12.74+deb13+1-amd64, ZFS wurde aber für 6.12.90+deb13.1-amd64 gebaut. modprobe zfs muss dann fehlschlagen, weil es im falschen Modulverzeichnis sucht.


1  Voraussetzungen


2  Repositories einrichten

ZFS liegt in Debian in contrib. Stelle daher sicher, dass contrib aktiv ist. Optional können non-free und non-free-firmware ergänzt werden, wenn das System entsprechende Firmware benötigt.

Beispiel für /etc/apt/sources.list oder eine passende Datei unter /etc/apt/sources.list.d/:

deb http://deb.debian.org/debian trixie main contrib non-free non-free-firmware
deb http://security.debian.org/debian-security trixie-security main contrib non-free non-free-firmware
deb http://deb.debian.org/debian trixie-updates main contrib non-free non-free-firmware

Optional für Backports:

deb http://deb.debian.org/debian trixie-backports main contrib non-free non-free-firmware

Danach die Paketlisten aktualisieren:

apt update

3  Benötigte Pakete installieren

Empfohlen ist die Installation der Kernel- und Header-Metapakete. Dadurch werden Kernel und Header gemeinsam aktuell gehalten, statt dass man irgendwann mit einem Kernel ohne passende Header herumsteht. Das ist ungefähr so sinnvoll wie ein Fahrrad ohne Vorderrad, nur mit mehr Fehlermeldungen.

apt install linux-image-amd64 linux-headers-amd64

Danach ZFS und die Build-Werkzeuge installieren:

apt install build-essential dkms zfs-dkms zfsutils-linux

Falls DKMS wegen pahole oder BTF-Erzeugung fehlschlägt:

apt install dwarves
Hinweis: Wenn kein Debian-Standardkernel verwendet wird, müssen die passenden Header oder Build-Verzeichnisse vorhanden sein: /lib/modules/$(uname -r)/build und /lib/modules/$(uname -r)/source.

4  Nach Kernel- oder Header-Update neu starten

Wenn ein neuer Kernel installiert wurde, läuft das System nicht automatisch mit diesem Kernel. Der neue Kernel wird erst nach einem Neustart verwendet.

Vor dem Neustart kann man prüfen, welche Kernelmodul-Verzeichnisse vorhanden sind:

ls /lib/modules

Außerdem den DKMS-Status prüfen:

dkms status

Wenn ZFS für einen neuen Kernel gebaut wurde, aber uname -r noch den alten Kernel zeigt:

reboot

Nach dem Neustart erneut prüfen:

uname -r
dkms status

Der laufende Kernel aus uname -r sollte jetzt zu einem ZFS-Eintrag aus dkms status passen.


5  ZFS-Modul laden und prüfen

Wenn der laufende Kernel und der DKMS-Eintrag zusammenpassen, kann das ZFS-Modul geladen werden:

modprobe zfs

Danach prüfen:

lsmod | sed -n '/^zfs/p'
zpool status -v
Hinweis: Wenn noch kein ZFS-Pool existiert, kann zpool status melden, dass keine Pools vorhanden sind. Das ist dann kein Fehler des Kernelmoduls.

6  ZFS gezielt für den laufenden Kernel bauen

Diesen Schritt nur verwenden, wenn dkms status keinen installierten ZFS-Eintrag für den aktuell laufenden Kernel zeigt.

ZFS-Version aus dem installierten Paket ermitteln:

ZFS_VER="$(dpkg-query -W -f='${Version}\n' zfs-dkms | sed 's/-.*//')"
echo "$ZFS_VER"

ZFS für den aktuell laufenden Kernel bauen und installieren:

dkms install -m zfs -v "$ZFS_VER" -k "$(uname -r)"

Danach laden:

modprobe zfs

Wenn der Build fehlschlägt, das DKMS-Log ansehen:

less /var/lib/dkms/zfs/"$ZFS_VER"/build/make.log

7  Häufiger Fehler: Header für den aktuell laufenden Kernel fehlen

Typisches Fehlerbild:

apt install linux-headers-$(uname -r)
Fehler: Paket linux-headers-6.12.74+deb13+1-amd64 kann nicht gefunden werden.
Fehler: Mittels des Musters »linux-headers-6.12.74+deb13+1-amd64« konnte kein Paket gefunden werden.

Das bedeutet meistens: Der aktuell laufende Kernel ist nicht mehr der Kernel, für den die Repositories aktuell Header anbieten. Statt ewig nach alten Headern zu suchen, sollte man die Kernel- und Header-Metapakete installieren:

apt install linux-image-amd64 linux-headers-amd64

Wenn DKMS dabei ZFS erfolgreich für den neuen Kernel baut, danach neu starten:

reboot

Nach dem Neustart:

uname -r
dkms status
modprobe zfs

8  Backports nur bei Bedarf verwenden

Backports sind sinnvoll, wenn ein neuerer Kernel verwendet wird oder wenn die ZFS-Version aus Stable zu alt für den verwendeten Kernel ist.

ZFS aus Backports installieren:

apt install -t trixie-backports zfs-dkms zfsutils-linux

Wenn auch der Kernel aus Backports verwendet werden soll:

apt install -t trixie-backports linux-image-amd64 linux-headers-amd64

Danach neu starten:

reboot

Nach dem Neustart prüfen:

uname -r
dkms status
modprobe zfs
zpool status -v
Hinweis: Nicht Stable-Kernel, Backports-Header und Stable-ZFS wild mischen, außer der Grund ist klar. APT ist kein Smoothie-Mixer.

9  Secure Boot prüfen

Wenn Secure Boot aktiv ist, können nicht korrekt signierte oder nicht akzeptierte Module blockiert werden.

Typische Fehlermeldungen:

modprobe: ERROR: could not insert 'zfs': Key was rejected by service

Required key not available

Secure-Boot-Status prüfen:

mokutil --sb-state

Wenn Secure Boot aktiv ist, gibt es zwei saubere Möglichkeiten:

Beim DKMS-Build sieht man häufig Hinweise wie:

Signing key: /var/lib/dkms/mok.key
Public certificate (MOK): /var/lib/dkms/mok.pub

Dann kann der öffentliche Schlüssel importiert werden:

mokutil --import /var/lib/dkms/mok.pub

Danach neu starten und im MOK-Menü bestätigen.


10  Alte Kernel nicht vorschnell entfernen

Alte Kernel sollten erst entfernt werden, wenn ZFS mit dem neuen Kernel erfolgreich geladen wurde.

uname -r
dkms status
modprobe zfs
zpool status -v

Wenn alles funktioniert, können alte nicht mehr benötigte Pakete später bereinigt werden:

apt autoremove

11  Problemlösung und häufige Stolperfallen

Symptom Wahrscheinliche Ursache Lösung
modprobe: FATAL: Module zfs not found in directory /lib/modules/<kernel> ZFS wurde nicht für den aktuell laufenden Kernel gebaut. uname -r und dkms status vergleichen.
linux-headers-$(uname -r) kann nicht gefunden werden. Der laufende Kernel ist älter oder stammt nicht mehr aus den aktuell verfügbaren Repositories. linux-image-amd64 und linux-headers-amd64 installieren, danach neu starten.
DKMS zeigt ZFS für einen neueren Kernel als uname -r. Der neue Kernel wurde installiert, aber noch nicht gebootet. System neu starten und danach erneut prüfen.
DKMS-Build schlägt wegen pahole fehl. Das Paket dwarves fehlt. apt install dwarves
Modul wird wegen Schlüssel abgelehnt. Secure Boot blockiert das Modul. Secure Boot deaktivieren oder MOK-Schlüssel enrollen.
Build bricht mit Kernel-API-Fehlern ab. Die installierte ZFS-Version passt nicht zum verwendeten Kernel. ZFS aus trixie-backports nutzen oder auf den Debian-Stable-Kernel wechseln.

12  Schnellreferenz

Standardfall Debian 13 Stable

apt update
apt install linux-image-amd64 linux-headers-amd64
apt install build-essential dkms zfs-dkms zfsutils-linux
reboot

Nach dem Neustart:

uname -r
dkms status
modprobe zfs
zpool status -v

Wenn ZFS für den laufenden Kernel fehlt

ZFS_VER="$(dpkg-query -W -f='${Version}\n' zfs-dkms | sed 's/-.*//')"
dkms install -m zfs -v "$ZFS_VER" -k "$(uname -r)"
modprobe zfs

Wenn ein neuerer Backports-Kernel genutzt wird

apt install -t trixie-backports linux-image-amd64 linux-headers-amd64
apt install -t trixie-backports zfs-dkms zfsutils-linux
reboot

Danach:

uname -r
dkms status
modprobe zfs
zpool status -v

13  Praxisbeispiel: ZFS wurde gebaut, aber der alte Kernel läuft noch

Ausgangslage:

uname -r
6.12.74+deb13+1-amd64

Die Header für diesen Kernel sind nicht installierbar:

apt install linux-headers-6.12.74+deb13+1-amd64
Fehler: Paket linux-headers-6.12.74+deb13+1-amd64 kann nicht gefunden werden.
Fehler: Mittels des Musters »linux-headers-6.12.74+deb13+1-amd64« konnte kein Paket gefunden werden.

Danach werden die Metapakete installiert:

apt install linux-image-amd64 linux-headers-amd64

DKMS baut ZFS erfolgreich für den neuen Kernel:

Autoinstall of module zfs/2.3.2 for kernel 6.12.90+deb13.1-amd64 (x86_64)
Building module(s)................................................. done.
Installing /lib/modules/6.12.90+deb13.1-amd64/updates/dkms/zfs.ko.xz
Autoinstall on 6.12.90+deb13.1-amd64 succeeded for module(s) zfs.

Trotzdem schlägt modprobe zfs fehl:

modprobe: FATAL: Module zfs not found in directory /lib/modules/6.12.74+deb13+1-amd64

Der Grund ist simpel: Das System läuft noch mit 6.12.74+deb13+1-amd64. ZFS wurde aber für 6.12.90+deb13.1-amd64 gebaut. Der Build ist also erfolgreich, aber der falsche Kernel ist aktiv.

Korrekte Lösung:

reboot

Nach dem Neustart:

uname -r
dkms status
modprobe zfs

Wenn uname -r jetzt 6.12.90+deb13.1-amd64 oder einen anderen Kernel mit passendem ZFS-DKMS-Eintrag zeigt, sollte modprobe zfs funktionieren.

Hinweis: Wenn nach dem Neustart weiterhin der alte Kernel läuft, muss die Bootloader-Auswahl geprüft werden. Dann ist nicht ZFS das Problem, sondern das System bootet schlicht nicht den Kernel, für den das Modul gebaut wurde.

Apache Guacamole - Docker

Fehler

Fehler

Festplatte mit 0 Bytes - Unsupported sector size 520

Ich hatte SAS Server Festplatten auf Ebay gekauft, diese hatten eine Sector Größe von 520 statt 512, damit konnte mein Linux Server nicht arbeiten.
Ausgabe von lsblk:

root@thinkstation-d30:/home/jj# lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
loop0    7:0    0 111,9M  1 loop /snap/lxd/24322
loop1    7:1    0  63,3M  1 loop /snap/core20/1822
loop2    7:2    0  49,8M  1 loop /snap/snapd/18357
sda      8:0    0 232,9G  0 disk 
├─sda1   8:1    0     1G  0 part /boot/efi
└─sda2   8:2    0 231,8G  0 part /
sdb      8:80   1     0B  0 disk 
sdc      8:80   1     0B  0 disk 

Wenn man dann in /var/log/syslog guckt:

Mar  2 14:35:49 thinkstation-d30 kernel: [842811.011313] sd 6:0:21:0: [sdb] Unsupported sector size 520.
Mar  2 14:35:49 thinkstation-d30 kernel: [842811.011789] sd 6:0:21:0: [sdb] 0 512-byte logical blocks: (0 B/0 B)
Mar  2 14:35:49 thinkstation-d30 kernel: [842811.011793] sd 6:0:21:0: [sdb] 520-byte physical blocks
Mar  2 14:35:49 thinkstation-d30 kernel: [842811.012135] sd 6:0:21:0: [sdb] Test WP failed, assume Write Enabled
Mar  2 14:35:49 thinkstation-d30 kernel: [842811.012243] sd 6:0:21:0: [sdb] Asking for cache data failed
Mar  2 14:35:49 thinkstation-d30 kernel: [842811.012269] sd 6:0:21:0: [sdb] Assuming drive cache: write through
Mar  2 14:35:49 thinkstation-d30 kernel: [842811.015632] sd 6:0:21:0: [sdb] Spinning up disk...
Mar  2 14:35:49 thinkstation-d30 systemd-udevd[230768]: sdx: Process '/usr/bin/unshare -m /usr/bin/snap auto-import --mount=/dev/sdb' failed with exit code 1.


Mar  2 14:36:19 thinkstation-d30 kernel: [842840.707363] sd 6:0:21:0: [sdb] Unsupported sector size 520.
Mar  2 14:36:19 thinkstation-d30 kernel: [842840.708694] sd 6:0:21:0: [sdb] Write cache: disabled, read cache: enabled, supports DPO and FUA
Mar  2 14:36:19 thinkstation-d30 kernel: [842840.709171] sd 6:0:21:0: [sdb] Attached SCSI disk
Mar  2 14:37:08 thinkstation-d30 kernel: [842841.350666] ................................................
Mar  2 14:37:08 thinkstation-d30 kernel: [842889.668429] ata5: SATA link down (SStatus 0 SControl 300)
Mar  2 14:37:25 thinkstation-d30 kernel: [842890.498005] .................not responding...
Mar  2 14:37:25 thinkstation-d30 kernel: [842906.882025] sd 6:0:22:0: [sdb] Unsupported sector size 520.
Mar  2 14:37:25 thinkstation-d30 kernel: [842906.882508] sd 6:0:22:0: [sdb] 0 512-byte logical blocks: (0 B/0 B)
Mar  2 14:37:25 thinkstation-d30 kernel: [842906.882511] sd 6:0:22:0: [sdb] 520-byte physical blocks
Mar  2 14:37:25 thinkstation-d30 kernel: [842906.882865] sd 6:0:22:0: [sdb] Test WP failed, assume Write Enabled
Mar  2 14:37:25 thinkstation-d30 kernel: [842906.882975] sd 6:0:22:0: [sdb] Asking for cache data failed
Mar  2 14:37:25 thinkstation-d30 kernel: [842906.882998] sd 6:0:22:0: [sdb] Assuming drive cache: write through
Mar  2 14:37:25 thinkstation-d30 kernel: [842906.884748] sd 6:0:22:0: [sdb] Spinning up disk...
Mar  2 14:37:25 thinkstation-d30 systemd-udevd[234836]: sdb: Process '/usr/bin/unshare -m /usr/bin/snap auto-import --mount=/dev/sdb' failed with exit code 1.
Mar  2 14:37:34 thinkstation-d30 kernel: [842907.905767] .........ready
Mar  2 14:37:34 thinkstation-d30 kernel: [842916.098275] sd 6:0:22:0: [sdb] Unsupported sector size 520.
Mar  2 14:37:34 thinkstation-d30 kernel: [842916.099587] sd 6:0:22:0: [sdb] Write cache: disabled, read cache: enabled, supports DPO and FUA
Mar  2 14:37:34 thinkstation-d30 kernel: [842916.100070] sd 6:0:22:0: [sdb] Attached SCSI disk
Mar  2 14:38:39 thinkstation-d30 kernel: [842981.024812] sd 7:0:0:4: [sdj] Media removed, stopped polling
Mar  2 14:39:41 thinkstation-d30 kernel: [843042.682033] sd 6:0:22:0: device_block, handle(0x001b)
Mar  2 14:39:51 thinkstation-d30 kernel: [843052.431519] sd 6:0:22:0: device_unblock and setting to running, handle(0x001b)
Mar  2 14:39:51 thinkstation-d30 kernel: [843052.793769] mpt2sas_cm0: mpt3sas_transport_port_remove: removed: sas_addr(0x5000c5006338f78e)
Mar  2 14:39:51 thinkstation-d30 kernel: [843052.793778] mpt2sas_cm0: removing handle(0x001b), sas_addr(0x5000c5006338f78e)
Mar  2 14:39:51 thinkstation-d30 kernel: [843052.793781] mpt2sas_cm0: enclosure logical id(0x5d4ae524852be97e), slot(14)
Mar  2 14:41:03 thinkstation-d30 kernel: [843124.431788] mpt2sas_cm0: handle(0x1b) sas_address(0x5000c5006338f78e) port_type(0x1)
Mar  2 14:41:03 thinkstation-d30 kernel: [843124.493757] scsi 6:0:23:0: Direct-Access     SEAGATE  ST910006CLAR1000 AS0C PQ: 0 ANSI: 5
Mar  2 14:41:03 thinkstation-d30 kernel: [843124.493781] scsi 6:0:23:0: SSP: handle(0x001b), sas_addr(0x5000c5006338f78e), phy(14), device_name(0x00c500508ef73863)
Mar  2 14:41:03 thinkstation-d30 kernel: [843124.493784] scsi 6:0:23:0: enclosure logical id (0x5d4ae524852be97e), slot(14) 
Mar  2 14:41:03 thinkstation-d30 kernel: [843124.493793] scsi 6:0:23:0: qdepth(254), tagged(1), scsi_level(6), cmd_que(1)
Mar  2 14:41:03 thinkstation-d30 kernel: [843124.493897] scsi 6:0:23:0: Power-on or device reset occurred
Mar  2 14:41:03 thinkstation-d30 kernel: [843124.495280] sd 6:0:23:0: [sdb] Spinning up disk...
Mar  2 14:41:03 thinkstation-d30 kernel: [843124.495417] sd 6:0:23:0: Attached scsi generic sg26 type 0
Mar  2 14:41:03 thinkstation-d30 kernel: [843124.495748]  end_device-6:0:22: add: handle(0x001b), sas_addr(0x5000c5006338f78e)

Also passt hier die Sector Größe nicht, diese kann man aber mit den Befehl "sg_format" anpassen:

sg_format /dev/disk/by-id/scsi-SSEAGATE_ST910006CLAR1000_9XG86B5W_0000C526KTTB -v --format --size=512

Ich habe hier lieber by-id genutzt, da ich mir nicht sicher bin, wie gut sdb da funktioniert hätte und ich eh die Seriennummer auf der Festplatte lesen kann.
Je nach Größe dauert das schon seine paar Stunden, er fragt anfangs noch ein paar mal, ob die Festplatte wirklich gelöscht werden soll.
Wenn man nichts macht, führt er es einfach aus.

Am Ende kommt dann sowas:

Format unit in progress, 99.01% done
Format unit in progress, 99.20% done
Format unit in progress, 99.38% done
Format unit in progress, 99.56% done
Format unit in progress, 99.74% done
Format unit in progress, 99.92% done
FORMAT UNIT Complete
root@thinkstation-d30:/home/jj#

 In lsblk:

root@thinkstation-d30:/home/jj# lsblk
NAME   MAJ:MIN RM   SIZE RO TYPE MOUNTPOINTS
loop0    7:0    0 111,9M  1 loop /snap/lxd/24322
loop1    7:1    0  63,3M  1 loop /snap/core20/1822
loop2    7:2    0  49,8M  1 loop /snap/snapd/18357
sda      8:0    0 232,9G  0 disk 
├─sda1   8:1    0     1G  0 part /boot/efi
└─sda2   8:2    0 231,8G  0 part /
sdb     65:112  0 931,5G  0 disk 
sdc     65:128  0 931,5G  0 disk 

Danach kann die Festplatte ganz normal genutzt werden...

Fehler

Nextcloud - Terminal Updater - Fehler

Fehler:

root@nextcloud:/var/www/html/nextcloud# sudo -u www-data php updater/updater.phar
Nextcloud Updater - version: v26.0.0beta2-9-g64e2e4c dirty

Current version is 27.1.1.
PHP Fatal error:  Uncaught Error: Call to undefined function NC\Updater\curl_init() in phar:///var/www/html/nextcloud/updater/updater.phar/lib/Updater.php:468
Stack trace:
#0 phar:///var/www/html/nextcloud/updater/updater.phar/lib/Updater.php(132): NC\Updater\Updater->getUpdateServerResponse()
#1 phar:///var/www/html/nextcloud/updater/updater.phar/lib/UpdateCommand.php(155): NC\Updater\Updater->checkForUpdate()
#2 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Command/Command.php(255): NC\Updater\UpdateCommand->execute()
#3 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Application.php(1009): Symfony\Component\Console\Command\Command->run()
#4 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Application.php(273): Symfony\Component\Console\Application->doRunCommand()
#5 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Application.php(149): Symfony\Component\Console\Application->doRun()
#6 phar:///var/www/html/nextcloud/updater/updater.phar/updater.php(10): Symfony\Component\Console\Application->run()
#7 /var/www/html/nextcloud/updater/updater.phar(14): require('...')
#8 {main}
  thrown in phar:///var/www/html/nextcloud/updater/updater.phar/lib/Updater.php on line 468
root@nextcloud:/var/www/html/nextcloud# 
root@nextcloud:/var/www/html/nextcloud# sudo -u www-data php updater/updater.phar
Nextcloud Updater - version: v26.0.0beta2-9-g64e2e4c dirty

Current version is 27.1.1.
PHP Fatal error:  Uncaught Error: Call to undefined function NC\Updater\simplexml_load_string() in phar:///var/www/html/nextcloud/updater/updater.phar/lib/Updater.php:495
Stack trace:
#0 phar:///var/www/html/nextcloud/updater/updater.phar/lib/Updater.php(132): NC\Updater\Updater->getUpdateServerResponse()
#1 phar:///var/www/html/nextcloud/updater/updater.phar/lib/UpdateCommand.php(155): NC\Updater\Updater->checkForUpdate()
#2 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Command/Command.php(255): NC\Updater\UpdateCommand->execute()
#3 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Application.php(1009): Symfony\Component\Console\Command\Command->run()
#4 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Application.php(273): Symfony\Component\Console\Application->doRunCommand()
#5 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Application.php(149): Symfony\Component\Console\Application->doRun()
#6 phar:///var/www/html/nextcloud/updater/updater.phar/updater.php(10): Symfony\Component\Console\Application->run()
#7 /var/www/html/nextcloud/updater/updater.phar(14): require('...')
#8 {main}
  thrown in phar:///var/www/html/nextcloud/updater/updater.phar/lib/Updater.php on line 495
root@nextcloud:/var/www/html/nextcloud# 

Bei mir fehlte php-curl und php-xml, also musste ich das nach installieren:

sudo apt-get install php-curl php-xml

 Danach lief er bei mir:

root@nextcloud:/var/www/html/nextcloud# sudo -u www-data php updater/updater.phar
Nextcloud Updater - version: v26.0.0beta2-9-g64e2e4c dirty

Current version is 27.1.1.

Update to Nextcloud 27.1.7 available. (channel: "stable")
Following file will be downloaded automatically: https://download.nextcloud.com/server/releases/nextcloud-27.1.7.zip
Open changelog ↗

Steps that will be executed:
[ ] Check for expected files
[ ] Check for write permissions
[ ] Create backup
[ ] Downloading
[ ] Verify integrity
[ ] Extracting
[ ] Enable maintenance mode
[ ] Replace entry points
[ ] Delete old files
[ ] Move new files in place
[ ] Done

Start update? [y/N] Y

Info: Pressing Ctrl-C will finish the currently running step and then stops the updater.

[✔] Check for expected files
[✔] Check for write permissions
[ ] Create backup ...

 

Fehler zwei, es fehlte hier die Zip Erweiterung:

Info: Pressing Ctrl-C will finish the currently running step and then stops the updater.

[✔] Check for expected files
[✔] Check for write permissions
[✔] Create backup
[✔] Downloading
[✔] Verify integrity
[ ] Extracting ...PHP Fatal error:  Uncaught Error: Class "ZipArchive" not found in phar:///var/www/html/nextcloud/updater/updater.phar/lib/Updater.php:693
Stack trace:
#0 phar:///var/www/html/nextcloud/updater/updater.phar/lib/UpdateCommand.php(383): NC\Updater\Updater->extractDownload()
#1 phar:///var/www/html/nextcloud/updater/updater.phar/lib/UpdateCommand.php(240): NC\Updater\UpdateCommand->executeStep()
#2 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Command/Command.php(255): NC\Updater\UpdateCommand->execute()
#3 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Application.php(1009): Symfony\Component\Console\Command\Command->run()
#4 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Application.php(273): Symfony\Component\Console\Application->doRunCommand()
#5 phar:///var/www/html/nextcloud/updater/updater.phar/vendor/symfony/console/Application.php(149): Symfony\Component\Console\Application->doRun()
#6 phar:///var/www/html/nextcloud/updater/updater.phar/updater.php(10): Symfony\Component\Console\Application->run()
#7 /var/www/html/nextcloud/updater/updater.phar(14): require('...')
#8 {main}
  thrown in phar:///var/www/html/nextcloud/updater/updater.phar/lib/Updater.php on line 693
root@nextcloud:/var/www/html/nextcloud# 

Also muss hier zip für php noch installiert werden:

sudo apt-get install php-zip

 

Fehler

Unifi Controller startet nicht mehr

Fehler - /var/log/unifi/server.log :

[2024-03-07 03:17:19,025] <db-server> INFO  db     - Checking if database needs to be shut down
[2024-03-07 03:17:20,026] <db-server> INFO  db     - Database was not running
[2024-03-07 03:17:20,026] <db-server> INFO  db     - Database configuration, dir=/usr/lib/unifi/data/db, port=27117
[2024-03-07 03:17:20,114] <db-server> ERROR system - unable to exec
java.io.IOException: Cannot run program "bin/mongod" (in directory "/usr/lib/unifi"): error=0, Failed to exec spawn helper: pid: 755101, signal: 11
        at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1143)
        at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1073)
        at com.ubnt.ace.B.Ôo0000(Unknown Source)
        at com.ubnt.ace.B.Öô0000(Unknown Source)
        at com.ubnt.ace.B.oÖ0000(Unknown Source)
        at com.ubnt.service.system.z.Òô0000(Unknown Source)
        at com.ubnt.service.system.z$1.run(Unknown Source)
        at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.io.IOException: error=0, Failed to exec spawn helper: pid: 755101, signal: 11
        at java.base/java.lang.ProcessImpl.forkAndExec(Native Method)
        at java.base/java.lang.ProcessImpl.<init>(ProcessImpl.java:314)
        at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:244)
        at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1110)
        ... 7 common frames omitted
[2024-03-07 03:17:20,114] <db-server> INFO  db     - Starting database process...
[2024-03-07 03:17:20,205] <db-server> ERROR system - unable to exec
java.io.IOException: Cannot run program "bin/mongod" (in directory "/usr/lib/unifi"): error=0, Failed to exec spawn helper: pid: 755109, signal: 11
        at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1143)
        at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1073)
        at com.ubnt.ace.B.ô00000(Unknown Source)
        at com.ubnt.ace.B.oÔ0000(Unknown Source)
        at com.ubnt.service.system.z.Òô0000(Unknown Source)
        at com.ubnt.service.system.z$1.run(Unknown Source)
        at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: java.io.IOException: error=0, Failed to exec spawn helper: pid: 755109, signal: 11
        at java.base/java.lang.ProcessImpl.forkAndExec(Native Method)
        at java.base/java.lang.ProcessImpl.<init>(ProcessImpl.java:314)
        at java.base/java.lang.ProcessImpl.start(ProcessImpl.java:244)
        at java.base/java.lang.ProcessBuilder.start(ProcessBuilder.java:1110)
        ... 6 common frames omitted
[2024-03-07 03:17:20,205] <db-server> INFO  db     - Database process stopped...
[2024-03-07 03:17:21,904] <stamgr> WARN  cache  - unable to retrieve settings in site 6432ba9476f22131549ac718
com.google.common.util.concurrent.UncheckedExecutionException: com.mongodb.MongoTimeoutException: Timed out after 10000 ms while waiting to connect. Client view of cluster state is {type=UNKNOWN, servers=[{address=localhost:27117, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketOpenException: Exc>
        at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2055)
        at com.google.common.cache.LocalCache.get(LocalCache.java:3966)
        at com.google.common.cache.LocalCache.getOrLoad(LocalCache.java:3989)
        at com.google.common.cache.LocalCache$LocalLoadingCache.get(LocalCache.java:4950)
        at com.ubnt.service.system.OooO.ôöØ000(Unknown Source)
        at com.ubnt.service.system.OooO.oøØ000(Unknown Source)
        at com.ubnt.service.event.A.newreturnnew(Unknown Source)
        at com.ubnt.service.event.A.oöÓ000(Unknown Source)
        at com.ubnt.service.event.A.ÔõÓ000(Unknown Source)
        at com.ubnt.service.devmgr.classObject.StringdoObject(Unknown Source)
        at com.ubnt.service.system.newnew.ÖO0000(Unknown Source)
        at com.ubnt.ace.B$_OOo.run(Unknown Source)
        at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
        at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
        at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305)
        at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
        at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
        at java.base/java.lang.Thread.run(Thread.java:833)
Caused by: com.mongodb.MongoTimeoutException: Timed out after 10000 ms while waiting to connect. Client view of cluster state is {type=UNKNOWN, servers=[{address=localhost:27117, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketOpenException: Exception opening socket}, caused by {java.net.ConnectE>
        at com.mongodb.internal.connection.BaseCluster.getDescription(BaseCluster.java:180)
        at com.mongodb.internal.connection.SingleServerCluster.getDescription(SingleServerCluster.java:44)
        at com.mongodb.client.internal.MongoClientDelegate.getConnectedClusterDescription(MongoClientDelegate.java:144)
        at com.mongodb.client.internal.MongoClientDelegate.createClientSession(MongoClientDelegate.java:101)
        at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.getClientSession(MongoClientDelegate.java:291)
        at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.execute(MongoClientDelegate.java:183)
        at com.mongodb.client.internal.FindIterableImpl.first(FindIterableImpl.java:213)
        at com.ubnt.service.system.z.Stringnull(Unknown Source)
        at com.ubnt.service.system.z.Ó00000(Unknown Source)
        at com.ubnt.service.system.floatsuper.ØÕ0000(Unknown Source)
        at com.ubnt.service.system.OooO$2.o00000(Unknown Source)
        at com.ubnt.service.system.OooO$2.load(Unknown Source)
        at com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3533)
        at com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2282)
        at com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2159)
        at com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2049)
        ... 17 common frames omitted
[2024-03-07 03:17:21,904] <stamgr> INFO  db     - waiting for db connection...
[2024-03-07 03:17:22,011] <discover> INFO  discover - [Internal] incomplete device information received
com.mongodb.MongoTimeoutException: Timed out after 10000 ms while waiting to connect. Client view of cluster state is {type=UNKNOWN, servers=[{address=localhost:27117, type=UNKNOWN, state=CONNECTING, exception={com.mongodb.MongoSocketOpenException: Exception opening socket}, caused by {java.net.ConnectException: C>
        at com.mongodb.internal.connection.BaseCluster.getDescription(BaseCluster.java:180)
        at com.mongodb.internal.connection.SingleServerCluster.getDescription(SingleServerCluster.java:44)
        at com.mongodb.client.internal.MongoClientDelegate.getConnectedClusterDescription(MongoClientDelegate.java:144)
        at com.mongodb.client.internal.MongoClientDelegate.createClientSession(MongoClientDelegate.java:101)
        at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.getClientSession(MongoClientDelegate.java:291)
        at com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor.execute(MongoClientDelegate.java:183)
        at com.mongodb.client.internal.FindIterableImpl.first(FindIterableImpl.java:213)
        at com.ubnt.service.system.z.Stringnull(Unknown Source)
        at com.ubnt.service.system.z.Ó00000(Unknown Source)
        at com.ubnt.service.devmgr.o0O0o.Õôö000(Unknown Source)
        at com.ubnt.service.devmgr.o0O0o.thisinterfaceString(Unknown Source)
        at com.ubnt.net.float.ÔÒø000(Unknown Source)
        at com.A.A.oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.Object.ô00000(Unknown Source)
        at com.A.A.oOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO.oOoO$1.run(Unknown Source)
        at java.base/java.lang.Thread.run(Thread.java:833)
[2024-03-07 03:17:22,011] <discover> INFO  db     - waiting for db connection...

Der Fehler sagt aus, das er Probleme mit der mongo db also der Datenbank gibt.

Zuerst muss der Dienst gestoppt werden:

service unifi stop

In einigen Fällen, in denen das Journal beschädigt ist, ist es hilfreich, den folgenden Befehl auszuführen, bevor die Datenbank repariert wird, um sie an einen anderen Ort zu verschieben

mv -vi /usr/lib/unifi/data/db/journal /usr/lib/unifi/data/db/journal-$(date -I)

Reparieren Sie die Datenbank mit diesem Befehl:

mongod --dbpath /usr/lib/unifi/data/db --logpath /usr/lib/unifi/logs/mongod.log --repair

Starten Sie schließlich den UniFi-Dienst neu

service unifi start

Hinweis: Wenn dieser Prozess als Root durchgeführt wurde, hat sich der Dateibesitz möglicherweise geändert. Dies kann geändert werden mit:

chown -R unifi:unifi /usr/lib/unifi/data/db/
chown -R unifi:unifi /usr/lib/unifi/logs/

Quelle:
https://help.ui.com/hc/en-us/articles/360006634094-UniFi-Repairing-Database-Issues-on-the-UniFi-Network-Application

Linux - Zeit setzen - timedatectl

Manchmal gibt es Situationen, wo eine andere Uhrzeit Simuliert werden muss, dabei kann dir diese Seite helfen.

Uhrzeit abfragen

timedatectl

NTP deaktivieren

timedatectl set-ntp false

Kurzform - Zeit ändern

Um nur die Stunden zu ändern, reicht es, die Stunde und Minute einzutragen.

timedatectl set-time 12:00


Falls du aber nur den Tag, Jahr, usw. ändern willst, reicht das hier auch aus.

timedatectl set-time 2025-03-10

Langform - Zeit ändern

timedatectl set-time "2023-01-01 12:00"

NTP aktivieren - Normalbetrieb

timedatectl set-ntp true

Postfix

Postfix

User anlegen

Ganz einfach im Terminal adduser und den Namen eingeben und danach das Passwd für den Account hinterlegen

adduser [name]
Also:
root@postfix:/home/jj# adduser nextcloud
info: Adding user `nextcloud' ...
info: Selecting UID/GID from range 1000 to 59999 ...
info: Adding new group `nextcloud' (1006) ...
info: Adding new user `nextcloud' (1006) with group `nextcloud (1006)' ...
info: Creating home directory `/home/nextcloud' ...
info: Copying files from `/etc/skel' ...
New password:
Retype new password:
passwd: password updated successfully
Changing the user information for nextcloud
Enter the new value, or press ENTER for the default
        Full Name []:
        Room Number []:
        Work Phone []:
        Home Phone []:
        Other []:
Is the information correct? [Y/n]
info: Adding new user `nextcloud' to supplemental / extra groups `users' ...
info: Adding user `nextcloud' to group `users' ...
root@postfix:/home/jj# 

 

 

 

 

 

Danach den Postfix neu einlesen lassen, bin mir nicht sicher ob das wirklich notwendig ist, habe ich noch nicht ausgiebig getestet.

Korrigiere, ist nicht notwendig, geht auch so.....

systemctl reload postfix

Webserver

Webserver

Fonts

Gute Fonts

https://www.freefonts.io/download/cambria/

https://www.freefonts.io/free-ageo-geometric-sans-font-family/

https://www.freefonts.io/download/ageo-geometric-sans/

 

MariaDB

MariaDB

MariaDB Installation - Linux


sudo apt update
sudo apt install mariadb-server -y
sudo systemctl start mariadb.service
sudo mysql_secure_installation

 

 

 

Temp:

Die Fehlermeldung deutet darauf hin, dass es ein Problem mit der Struktur der Systemtabellen in Ihrer MariaDB-Datenbank gibt. Die Tabelle mysql.column_stats hat eine inkorrekte Definition, die nicht mit dem von Ihrer MariaDB-Version erwarteten Schema übereinstimmt.

Diese Art von Fehler kann nach einem Upgrade von MariaDB auftreten, wenn die Systemtabellen nicht korrekt aktualisiert wurden.
Lösungsschritte zur Behebung:
1. Überprüfen der MariaDB-Version

Zuerst sollten Sie die aktuelle Version Ihrer MariaDB-Installation überprüfen, um sicherzustellen, dass sie mit den erwarteten Systemtabellen übereinstimmt:

bash

mariadb --version

2. Aktualisieren der Systemtabellen

Um die Systemtabellen an das aktuelle Schema Ihrer MariaDB-Version anzupassen, können Sie den Befehl mysql_upgrade ausführen. Dieser Befehl überprüft und repariert die Systemtabellen.

Führen Sie den folgenden Befehl als root-Benutzer oder mit ausreichenden Berechtigungen aus:

bash

sudo mysql_upgrade

Starten Sie den MariaDB-Dienst nach dem Upgrade neu:

bash

sudo systemctl restart mariadb

3. Überprüfung der Tabelle mysql.column_stats

Falls der Fehler weiterhin besteht, sollten Sie die Tabelle mysql.column_stats manuell überprüfen und anpassen.

    Melden Sie sich bei MariaDB an:

    bash

sudo mysql -u root -p

Überprüfen Sie die Struktur der Tabelle mysql.column_stats:

sql

DESCRIBE mysql.column_stats;

Vergleichen Sie die Ausgabe mit der erwarteten Struktur laut Fehlermeldung. Es sollte wie folgt aussehen:

    Spalte hist_type sollte vom Typ enum('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON_HB') sein.
    Spalte histogram sollte vom Typ longblob sein.

Anpassen der Tabelle (falls nötig):

Falls die Struktur nicht korrekt ist, können Sie die Tabelle manuell anpassen:

sql

    ALTER TABLE mysql.column_stats MODIFY hist_type ENUM('SINGLE_PREC_HB','DOUBLE_PREC_HB','JSON_HB');
    ALTER TABLE mysql.column_stats MODIFY histogram LONGBLOB;

4. Überprüfen Sie die MariaDB-Logs

Nach den Änderungen und dem Neustart von MariaDB können Sie die MariaDB-Logs überprüfen, um sicherzustellen, dass keine weiteren Fehler auftreten:

bash

sudo tail -f /var/log/mysql/error.log

Zusammenfassung:

    Führen Sie mysql_upgrade aus, um die Systemtabellen zu aktualisieren.
    Überprüfen Sie die Struktur der Tabelle mysql.column_stats.
    Starten Sie den MariaDB-Dienst neu und prüfen Sie die Logs.

Wenn die Fehler weiterhin bestehen oder neue Fragen aufkommen, lassen Sie es mich wissen!

UUID

UUID von Partitionen:

sudo blkid
root@Turbo:/home/jj# sudo blkid
/dev/mmcblk0p1: LABEL_FATBOOT="bootfs" LABEL="bootfs" UUID="A7E6-0C92" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="5e89687f-01"
/dev/mmcblk0p2: LABEL="rootfs" UUID="f6cc7216-ff28-4ec1-86d6-b391f5f31405" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="5e89687f-02"
root@Turbo:/home/jj# 

 

Linux System UUID:

cat /etc/machine-id


Links

Ordner wo anders mounten, unter einem anderen Pfad:

mount --bind /pfad/zum/ursprungsordner /pfad/zum/neuen/ort

Geräte ohne Internetverbindung / Keine Updates

Geräte ohne Internetverbindung / Keine Updates

debian / Ubuntu

syslog Ausgabe (Snapd Auto-Update):

Dec 29 00:12:21 000000005acde0e1 snapd[671]: autorefresh.go:643: Cannot prepare auto-refresh change due to a permanent network error: persistent network error: Post "https://api.snapcraft.io/v2/snaps/refresh": dial tcp: lookup api.snapcraft.io: Temporary failure in name resolution

Update-Timer auf eine ungenutzte Zeit setzen:

sudo snap set system refresh.timer=00:00-24:00

Dies legt einen Zeitbereich fest, der effektiv keine automatischen Updates ermöglicht.

Automatische Aktualisierungen vollständig deaktivieren:

Es gibt keine direkte Methode, um automatische Updates komplett zu deaktivieren, aber Sie können den Update-Intervall verlängern:

sudo snap set system refresh.hold="$(date --date='100 years' +%Y-%m-%dT%H:%M:%S)"

Dadurch werden Updates für einen sehr langen Zeitraum zurückgehalten.

Snapd-Dienst komplett deaktivieren:

Wenn Sie Snapd nicht benötigen, können Sie den Dienst komplett deaktivieren und entfernen
Snapd-Dienst stoppen und deaktivieren:

sudo systemctl stop snapd.service
sudo systemctl disable snapd.service
sudo systemctl mask snapd.service

stop: Stoppt den laufenden Snapd-Dienst.
disable: Verhindert, dass Snapd beim Systemstart automatisch startet.
mask: Blockiert die Möglichkeit, den Dienst manuell oder automatisch zu starten.

Snapd entfernen:

Wenn Sie sicher sind, dass Sie Snapd nicht mehr benötigen, können Sie es deinstallieren:

sudo apt purge snapd
sudo rm -rf /var/cache/snapd/

Hinweis: Stellen Sie sicher, dass keine installierten Anwendungen von Snapd abhängen, bevor Sie es entfernen.

Optional: Entfernen von Snap-Paketen und Verzeichnissen:

Um alle Snap-Pakete und zugehörigen Verzeichnisse zu entfernen:

rm -rf ~/snap
sudo rm -rf /snap
sudo rm -rf /var/snap
sudo rm -rf /var/lib/snapd

 

syslog Ausgabe (Linux Auto-Update):

Dec 30 11:41:00 000000005acde0e1 python3[8175]: ["2024-12-30T11:41:00.605", "WARNING", "ubuntupro.apt", "fail", 907, "Failed to fetch ESM Apt Cache item: https://esm.ubuntu.com/apps/ubuntu/dists/jammy-apps-security/InRelease", {}]

Automatische Update-Dienste deaktivieren

Ubuntu verwendet systemd-Timer, um regelmäßig nach Updates zu suchen und diese zu installieren. Sie können diese Timer deaktivieren:

sudo systemctl stop apt-daily.timer
sudo systemctl disable apt-daily.timer

sudo systemctl stop apt-daily-upgrade.timer
sudo systemctl disable apt-daily-upgrade.timer

Automatische Update-Dienste maskieren

Um sicherzustellen, dass die Dienste nicht versehentlich wieder aktiviert werden, können Sie sie maskieren:

sudo systemctl mask apt-daily.service
sudo systemctl mask apt-daily-upgrade.service

Unattended Upgrades deaktivieren

Falls das Paket unattended-upgrades installiert ist, sollten Sie es deaktivieren:

sudo systemctl stop unattended-upgrades
sudo systemctl disable unattended-upgrades

 

 

syslog Ausgabe (Linux Auto-News):

Jan  1 03:43:46 000000005acde0e1 systemd[1]: Starting Update APT News...

Deaktivieren des apt-news Dienstes

Der apt-news Dienst ist dafür verantwortlich, Nachrichten von den APT-Repositories abzurufen. Um diesen Dienst zu deaktivieren:

sudo systemctl stop apt-news.service
sudo systemctl disable apt-news.service

Überprüfen Sie, ob ein Timer für apt-news existiert, und deaktivieren Sie diesen ebenfalls:

sudo systemctl stop apt-news.timer
sudo systemctl disable apt-news.timer

Debian

Debian

Display nie ausgehen lassen

nano /etc/lightdm/lightdm.conf
xserver-command=X -s 0 -dpms

 

 

 

 

 

Debian

Docker installation

Hier findest du eine Schritt‑für‑Schritt-Anleitung, wie du Docker unter Debian bzw. Kali Linux installierst:

1. Voraussetzungen aktualisieren und notwendige Pakete installieren

Öffne ein Terminal und führe folgende Befehle aus:

apt-get update
apt-get install ca-certificates curl

2. Docker’s GPG-Schlüssel hinzufügen

Erstelle zunächst das Verzeichnis für die Schlüssel und lade dann den offiziellen Docker-GPG-Schlüssel herunter:

install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

3. Docker-Repository in die APT-Quellen aufnehmen

Füge das Repository zu deinen APT-Quellen hinzu. Unter Debian verwendest du dazu in der Regel den Codename deiner Distribution, den du mit $(. /etc/os-release && echo "$VERSION_CODENAME") auslesen kannst:

# Add Docker's official GPG key:
apt-get update
apt-get install ca-certificates curl
install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg -o /etc/apt/keyrings/docker.asc
chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to Apt sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  tee /etc/apt/sources.list.d/docker.list > /dev/null
apt-get update

Hinweis für Kali Linux:
Da Kali eine abgeleitete Distribution ist, kann es sein, dass der automatisch ermittelte Codename (über $(. /etc/os-release && echo "$VERSION_CODENAME")) nicht korrekt ist.
In diesem Fall ersetze diesen Ausdruck manuell durch den entsprechenden Debian-Codename, z. B.:

echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/debian \
  bookworm stable" | \
  tee /etc/apt/sources.list.d/docker.list > /dev/null

Aktualisiere danach erneut die Paketliste:

apt-get update

4. Docker-Pakete installieren

Installiere die neuesten Versionen der Docker-Pakete mit:

apt-get install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

Debian

Docker Container erstellen

Container erstellen

1. Dockerfile erstellen

Ein Dockerfile definiert, wie dein Container aufgebaut sein soll. Erstelle ein neues Verzeichnis und eine Datei Dockerfile:

# Basis-Image wählen (z. B. Debian, Ubuntu oder Alpine)
FROM ubuntu:latest

# Update und Installation von Paketen
RUN apt update && apt install -y nano curl htop

# Standard-Shell setzen
CMD ["/bin/bash"]
# Basis-Image wählen (z. B. Debian, Ubuntu oder Alpine)
FROM ubuntu:latest

# Update und Installation von Paketen
RUN apt update && apt install -y nano curl htop iputils-ping iproute2 ethstatus vnstat btop htop sshfs wget sudo cifs-utils python3 python3-venv python3-pip git

# Standard-Shell setzen
CMD ["/bin/bash"]

2. Container bauen

Navigiere in das Verzeichnis, in dem dein Dockerfile liegt, und baue das Image:

docker build -t mein-linux-container .

3. Container starten

Starte den Container im interaktiven Modus:

docker run -it mein-linux-container

Container CPU beschränken

Option 1 - Begrenzung mit --cpus (empfohlen)

Da dein System 12 CPU-Kerne hat und du dem Docker-Container 95% der Gesamtleistung zuweisen möchtest, hast du zwei Möglichkeiten:

Du kannst den Container mit --cpus so starten:

docker run -it --cpus="11.4" mein-linux-container

🔹 Erklärung:

Falls der Container bereits läuft, kannst du die Einstellung nachträglich setzen:

docker update --cpus="11.4" mein-linux-container

Option 2 - Begrenzung mit --cpu-quota und --cpu-period

Falls du eine genauere Kontrolle möchtest:

docker run -it --cpu-quota=950000 --cpu-period=1000000 mein-linux-container

🔹 Erklärung:

Falls dein Container bereits läuft:

docker update --cpu-quota=950000 --cpu-period=1000000 mein-linux-container

Option 3 - Begrenzung auf bestimmte Kerne mit --cpuset-cpus

Falls du den Container auf 11 von 12 Kernen begrenzen möchtest:

docker run -it --cpuset-cpus="0-10" mein-linux-container

🔹 Erklärung:

Fazit: Welche Methode solltest du nehmen?

✅ Für eine einfache Begrenzung:
Nutze --cpus="11.4" → Leicht verständlich und flexibel.

✅ Für eine exakte Kontrolle über CPU-Zeit:
Nutze --cpu-quota=950000 --cpu-period=1000000.

✅ Falls du nur bestimmte Kerne zuweisen willst:
Nutze --cpuset-cpus="0-10" (≈91.6% Leistung).

Container CPU beschränken ohne Datenverlust

Option 1 - Container mit --rm vermeiden

Falls du den Container mit --rm gestartet hast, wird er nach dem Beenden gelöscht.

Starte den Container stattdessen ohne --rm, damit er bestehen bleibt:

docker run -it --cpus="11.4" --name mein-linux-container mein-linux-container

🔹 Erklärung:

Starte ihn später wieder mit:

docker start -ai mein-linux-container

Falls du ihn nicht interaktiv starten willst:

docker start mein-linux-container
docker attach mein-linux-container

Option 2 - Den Container committen (Änderungen dauerhaft speichern)

Falls du sysbench bereits installiert hast und den Container beenden möchtest, kannst du die Änderungen mit docker commit in ein neues Image speichern:

docker commit mein-linux-container mein-linux-container

🔹 Erklärung:

docker run -it --cpus="11.4" mein-linux-container

Falls du möchtest, dass der Container diese Änderungen auch nach dem Schließen speichert, kannst du stattdessen docker start verwenden:

docker start -ai mein-linux-container

Option 3 - Ein Dockerfile nutzen (Empfohlen für reproduzierbare Container)

Falls du sysbench immer in diesem Container haben willst, solltest du ein Dockerfile erstellen:

Erstelle eine Datei Dockerfile:

FROM ubuntu:latest
RUN apt update && apt install -y sysbench
CMD ["/bin/bash"]

 Baue das Image mit:

docker build -t mein-linux-container .

Starte den Container:

docker run -it --cpus="11.4" mein-linux-container

Vorteile:
✅ Jedes Mal, wenn du das Image nutzt, ist sysbench bereits installiert.
✅ Reproduzierbar – kannst du auf anderen Systemen wiederverwenden.

Nützliche Pakete

Ping (ping 1.1.1.1):

apt-get install iputils-ping -y

ip tools (ip a):

apt install iproute2 -y

Debian

Shortcuts - FN Tasten

Überprüfen von ACPI-Ereignissen

Installiere und starte den ACPI-Dienst:

sudo apt-get update
sudo apt-get install acpid
sudo systemctl enable acpid
sudo systemctl start acpid

Verwende acpi_listen, um zu sehen, ob die Fn-Tasten ACPI-Ereignisse auslösen:

acpi_listen

Drücke nun nacheinander die Fn-Tasten F9, F10 und F11.
Beobachte die Ausgabe im Terminal.


ACPI

ACPI arbeitet über ein Ereignissystem, das in deinem System so konfiguriert ist, dass es bei bestimmten Hardware-Ereignissen (wie z. B. Fn-Tastendrücken) automatisch Skripte oder Befehle ausführt. Hier eine Erklärung, wie das im Detail funktioniert:

  1. ACPI-Ereignisdateien:
    Im Verzeichnis /etc/acpi/events/ liegen Konfigurationsdateien wie media_prev. Jede dieser Dateien enthält zwei wichtige Teile:

    • event=...
      Hier wird ein Muster definiert, das einem bestimmten ACPI-Ereignis entspricht. Zum Beispiel könnte event=ibm/hotkey LEN0268:00 00000080 00001317 genau dem Signal entsprechen, das gesendet wird, wenn du F9 drückst.
    • action=...
      Hier wird angegeben, welcher Befehl oder welches Skript ausgeführt werden soll, wenn ein Ereignis eintritt, das dem Muster entspricht. Im Fall von media_prev wäre das beispielsweise ein Skript, das den Befehl playerctl previous ausführt, um zum vorherigen Track zu wechseln.
  2. Der ACPI-Daemon (acpid):
    Beim Systemstart lädt der ACPI-Daemon (acpid) alle Dateien aus /etc/acpi/events/ ein. Er „kennt“ dadurch alle Muster und die zugehörigen Aktionen.
    Sobald ein ACPI-Ereignis (z. B. das Drücken von F9) vom Kernel erzeugt wird, vergleicht acpid dieses Ereignis mit den Mustern in den Konfigurationsdateien.

    • Passender Treffer: Findet acpid ein Muster, das zu deinem Tastendruck passt, wird automatisch die in der Datei definierte Aktion ausgeführt.
  3. Kein manuelles „Verlinken“ nötig:
    Es gibt keine separate Konfigurationsdatei, in der du explizit die Fn-Tasten mit den Skripten verknüpfst.
    Stattdessen durchforstet acpid beim Start das Verzeichnis /etc/acpi/events/ und „registriert“ alle darin enthaltenen Ereignismuster.
    Wenn du also F9 drückst und das ACPI-Ereignis exakt mit dem in der Datei media_prev definierten Muster übereinstimmt, weiß acpid automatisch, dass es das entsprechende Skript (bzw. den Befehl) ausführen soll.

Zusammengefasst:

So „weiß“ das System also automatisch, dass bei einem F9-Druck der vorherige Track abgespielt werden soll – alles basiert auf den in den ACPI-Ereignisdateien definierten Mustern und Aktionen.

Next Song - Spotify

/etc/acpi/events/media_next
event=ibm/hotkey LEN0268:00 00000080 00001319
action=/usr/bin/sudo -u jj /etc/acpi/local/media_next.sh

/etc/acpi/local/media_next.sh
#!/bin/bash

export DISPLAY=:0.0
export XAUTHORITY=/home/jj/.Xauthority
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

playerctl -p spotify next

Previous Song - Spotfy

/etc/acpi/events/media_prev
event=ibm/hotkey LEN0268:00 00000080 00001317
action=/usr/bin/sudo -u jj /etc/acpi/local/media_prev.sh

/etc/acpi/local/media_prev.sh
#!/bin/bash

export DISPLAY=:0.0
export XAUTHORITY=/home/jj/.Xauthority
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

playerctl -p spotify previous

Play and Pause - Spotify

/etc/acpi/events/media_play_pause
event=ibm/hotkey LEN0268:00 00000080 00001318
action=/usr/bin/sudo -u jj /etc/acpi/local/media_play_pause.sh

/etc/acpi/local/media_play_pause.sh
#!/bin/bash

export DISPLAY=:0.0
export XAUTHORITY=/home/jj/.Xauthority
export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/1000/bus

playerctl -p spotify play-pause

Syslog Server

Release Key hinzufügen:

wget -qO - https://ose-repo.syslog-ng.com/apt/syslog-ng-ose-pub.asc | sudo apt-key add -

Reposetory vorbereiten

echo "deb https://ose-repo.syslog-ng.com/apt/ stable ubuntu-noble" | sudo tee -a /etc/apt/sources.list.d/syslog-ng-ose.list

Listen Updaten:

apt-get update

Installieren:

apt-get install syslog-ng-core syslog-ng-scl

Syslog einrichten

/etc/syslog-ng/syslog-ng.conf:

Füge das ganz unten dazu:

source s_network {
    # Empfang über UDP an Port 514
    udp(ip("0.0.0.0") port(514));

    # Falls auch TCP benötigt wird, kannst Du diesen Block hinzufügen:
    tcp(ip("0.0.0.0") port(514));
};

destination d_remote {
    file("/var/log/syslog-ng/remote.log"
         owner("root") group("adm")
         perm(0640)
         create-dirs(yes));
};

log {
    source(s_network);
    destination(d_remote);
};

 

Testen:

sudo syslog-ng -s

 

Neustarten:

sudo systemctl restart syslog-ng

Netzwerk

Netzwerk

Ubuntu - Statische IP Setzen

Um unter Ubuntu dauerhaft eine feste IP-Addresse zu setzen, musst du die Datei von netplan bearbeiten.

 

Ubuntu:

Um zu starten musst du folgende Datei bearbeiten.

Ubuntu Server 24.01 LTS:

nano /etc/netplan/50-cloud-init.yaml

Ubuntu Server 22.01 LTS:

nano /etc/netplan/00-installer-config.yaml

 

Die Datei sollte in etwa diesen Inhalt haben:

# This file is generated from information provided by the datasource.  Changes
# to it will not persist across an instance reboot.  To disable cloud-init's
# network configuration capabilities, write a file
# /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following:
# network: {config: disabled}
network:
  ethernets:
    ens160:
#      dhcp4: true
      dhcp4: no
      addresses:
        - 10.0.0.10/24
      gateway4: 10.0.0.1
      nameservers:
          addresses: [1.1.1.1, 8.8.8.8]
  version: 2

ens160 is dabei mein Netzwerk Interface, das ist von System zu System unterschiedlich.
Um dein Interface zu erkennen, kannst du im Terminal ip a eingeben, danach werden dir alle erkannten Netzwerk Interfaces angezeigt.
dhcp4: no muss gesetzt sein, da du sonst per DHCP auch nochmal eine Addresse bekommst.
addresses: da kannst du entweder eine oder mehere IP Addressen eintragen, vergiss nicht dahinter die passende Subnetmaske zu setzen.
gateway4: ist dann in dem falle dein Router/Firewall, halt das Gateway
nameservers: hier kannst du deine DNS Server eintragen die genutzt werden sollen

 

Je nachdem was für ne Rakete du bist, kannst du das ganze direkt anwenden, mit dem Risiko, das du dich von von deinem System verabschiedest und nicht mehr per SSH rauf kommst oder du testest das ganze erstmal.

Testen:

netplan try -timeout 180

Mach die Rakete oder wenn's funktioniert, dauerhaft anwenden:

netplan apply

 

 

 

 

 

Netzwerk

Debian - Statische IP Setzen

Statische IP

Um unter Debian dauerhaft eine feste IP-Adresse zu setzen, musst du die Datei /etc/network/interfaces bearbeiten.

Debian:

Öffne die Datei mit nano /etc/network/interfaces und ersetze ihren Inhalt beispielsweise durch Folgendes:

auto lo
iface lo inet loopback

auto eth0
iface eth0 inet static
    address 10.0.0.10
    netmask 255.255.255.0
    gateway 10.0.0.1
    dns-nameservers 1.1.1.1 8.8.8.8
    dns-search example.local
    up echo "supersede ntp-servers 10.0.0.1;" >> /etc/dhcp/dhclient.conf
    up echo "supersede bootfile-name 'pxelinux.0';" >> /etc/dhcp/dhclient.conf
    up echo "supersede next-server 10.0.0.1;" >> /etc/dhcp/dhclient.conf

eth0 ist hier dein Netzwerk‑Interface (Name kann abweichen – prüfe mit ip a).

Testen:

netplan try -timeout 180

Dauerhaft anwenden:

netplan apply

DNS Server

Falls du die DNS‑Server nicht über DHCP verteilen möchtest oder sie sich häufig ändern, kannst du /etc/resolv.conf direkt anpassen (und optional schreibschützen):

nameserver 1.1.1.1
nameserver 8.8.8.8

Schreibschutz aktivieren:

chattr +i /etc/resolv.conf

Schreibschutz entfernen:

chattr -i /etc/resolv.conf
Netzwerk

NTP Server abfragen und Netzwerk prüfen

NTP nutzt UDP Port 123. Ein normaler TCP-Portcheck ist daher nicht ausreichend. Wenn bestimmte NTP-Server per IP-Adresse getestet werden sollen, muss eine echte NTP-Anfrage an diese IP gesendet werden.


Keine Zusatzpakete notwendig

Der folgende Test benötigt keine zusätzlichen NTP-Tools wie ntpdig, sntp oder ntpdate. Er verwendet nur python3, falls es auf dem System vorhanden ist.

Auf normalen Debian-Systemen ist python3 häufig vorhanden. Auf sehr minimalen Systemen ist das aber nicht garantiert. Ohne python3 oder ein NTP-Tool ist ein sauberer direkter NTP-Test gegen beliebige IP-Adressen nicht sinnvoll möglich.


1. Einzelne NTP-IP testen

Beispiel-IP anpassen:

python3 - 192.168.10.10 <<'PY'
import datetime
import ipaddress
import socket
import struct
import sys

if len(sys.argv) != 2:
    print("FEHLER: Bitte genau eine IP-Adresse angeben.")
    sys.exit(2)

host = sys.argv[1].strip()

try:
    ipaddress.ip_address(host)
except ValueError:
    print(f"FEHLER {host}: Keine gültige IP-Adresse.")
    sys.exit(2)

packet = bytearray(48)
packet[0] = 0x23

try:
    infos = socket.getaddrinfo(host, 123, type=socket.SOCK_DGRAM)
    family, socktype, proto, _, sockaddr = infos[0]

    with socket.socket(family, socktype, proto) as sock:
        sock.settimeout(3)
        sock.sendto(packet, sockaddr)
        data, addr = sock.recvfrom(512)

    if len(data) < 48:
        print(f"FEHLER {host}: Antwort zu kurz, keine gültige NTP-Antwort.")
        sys.exit(1)

    stratum = data[1]
    tx_seconds, tx_fraction = struct.unpack("!II", data[40:48])
    unix_time = tx_seconds - 2208988800 + tx_fraction / 2**32
    server_time = datetime.datetime.fromtimestamp(unix_time, datetime.timezone.utc)

    if stratum == 0:
        print(f"WARNUNG {host}: Antwort erhalten, aber Stratum 0. Server lehnt die Anfrage möglicherweise ab.")
    else:
        print(f"OK {host}: NTP-Antwort erhalten, Stratum {stratum}, Serverzeit {server_time.isoformat()}")

except socket.timeout:
    print(f"FEHLER {host}: Timeout. Keine NTP-Antwort über UDP Port 123.")
    sys.exit(1)
except OSError as e:
    print(f"FEHLER {host}: Netzwerkfehler: {e}")
    sys.exit(1)
PY

2. Mehrere NTP-IPs testen

Die IP-Adressen in der ersten Zeile anpassen:

for ip in 192.168.10.10 192.168.10.11 192.168.10.12; do
  python3 - "$ip" <<'PY'
import datetime
import ipaddress
import socket
import struct
import sys

host = sys.argv[1].strip()

try:
    ipaddress.ip_address(host)
except ValueError:
    print(f"FEHLER {host}: Keine gültige IP-Adresse.")
    sys.exit(2)

packet = bytearray(48)
packet[0] = 0x23

try:
    infos = socket.getaddrinfo(host, 123, type=socket.SOCK_DGRAM)
    family, socktype, proto, _, sockaddr = infos[0]

    with socket.socket(family, socktype, proto) as sock:
        sock.settimeout(3)
        sock.sendto(packet, sockaddr)
        data, addr = sock.recvfrom(512)

    if len(data) < 48:
        print(f"FEHLER {host}: Antwort zu kurz, keine gültige NTP-Antwort.")
        sys.exit(1)

    stratum = data[1]
    tx_seconds, tx_fraction = struct.unpack("!II", data[40:48])
    unix_time = tx_seconds - 2208988800 + tx_fraction / 2**32
    server_time = datetime.datetime.fromtimestamp(unix_time, datetime.timezone.utc)

    if stratum == 0:
        print(f"WARNUNG {host}: Antwort erhalten, aber Stratum 0. Server lehnt die Anfrage möglicherweise ab.")
    else:
        print(f"OK {host}: NTP-Antwort erhalten, Stratum {stratum}, Serverzeit {server_time.isoformat()}")

except socket.timeout:
    print(f"FEHLER {host}: Timeout. Keine NTP-Antwort über UDP Port 123.")
except OSError as e:
    print(f"FEHLER {host}: Netzwerkfehler: {e}")
PY
done

3. Prüfen, ob Python vorhanden ist

python3 --version

Wenn eine Versionsnummer angezeigt wird, kann der Test verwendet werden. Wenn python3: command not found erscheint, ist Python auf diesem System nicht installiert.


Warum nicht curl oder wget?

curl und wget können keine echte NTP-Abfrage machen. NTP läuft über UDP Port 123, nicht über HTTP oder HTTPS.

Mit curl oder wget kann man nur prüfen, ob HTTP/HTTPS funktioniert:

curl -I https://www.debian.org
wget --spider https://www.debian.org

Das sagt aber nichts Sicheres darüber aus, ob NTP über UDP Port 123 erlaubt ist. Denn natürlich kann Netzwerkdiagnose nicht einfach mal logisch sein.


Warum nicht nur Bash mit /dev/udp?

Bash kann zwar Daten an /dev/udp/IP/PORT senden, aber UDP hat keinen Verbindungsaufbau wie TCP. Nur weil ein UDP-Paket gesendet wurde, weiß man noch nicht, ob ein NTP-Server geantwortet hat.

Für einen brauchbaren Test muss eine echte NTP-Anfrage gesendet und die Antwort ausgewertet werden. Genau das macht der Python-Test oben.


Ergebnis bewerten

Ergebnis Bedeutung
OK ... NTP-Antwort erhalten Die IP-Adresse antwortet auf NTP über UDP Port 123.
WARNUNG ... Stratum 0 Der Server hat geantwortet, lehnt die Anfrage aber möglicherweise ab oder liefert keine nutzbare Zeit.
Timeout Keine Antwort erhalten. UDP Port 123 wird möglicherweise blockiert oder der Server antwortet nicht.
Netzwerkfehler Es gibt ein lokales Netzwerkproblem, Routingproblem oder ein Problem mit der Adresse.
Keine gültige IP-Adresse Der angegebene Wert ist keine gültige IPv4- oder IPv6-Adresse.

Kurzfazit

Wenn bestimmte IP-Adressen getestet werden sollen, reicht timedatectl nicht aus. Für einen direkten Test braucht man entweder ein NTP-Tool wie ntpdig oder einen kleinen UDP-NTP-Test wie oben mit python3.

Ohne python3, ntpdig, sntp oder ntpdate ist ein sauberer direkter NTP-Test gegen beliebige IP-Adressen nicht realistisch möglich.

Internet teilen (NAT) und Loggen

Interface identifizieren

root@Turbo:/home/jj# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host noprefixroute 
       valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
    link/ether e4:5f:01:36:43:ce brd ff:ff:ff:ff:ff:ff
    inet 192.168.6.112/24 brd 192.168.6.255 scope global dynamic noprefixroute eth0
       valid_lft 75086sec preferred_lft 75086sec
    inet6 fe80::782e:63d3:252f:a8d8/64 scope link noprefixroute 
       valid_lft forever preferred_lft forever
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
    link/ether FF:5f:FF:36:FF:cf brd ff:ff:ff:ff:ff:ff
    inet 172.200.0.1/24 brd 172.200.0.255 scope global wlan0
       valid_lft forever preferred_lft forever
    inet6 fe80::e65f:1ff:fe36:43cf/64 scope link 
       valid_lft forever preferred_lft forever
4: wlan1: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default qlen 1000
    link/ether d0:37:45:36:17:ef brd ff:ff:ff:ff:ff:ff
    inet 10.1.143.2/30 brd 10.1.143.3 scope global dynamic wlan1
       valid_lft 75098sec preferred_lft 75098sec
root@Turbo:/home/jj# 

In dem falle haben bei mir jetzt eth0 und wlan1 Internetzugriff und wlan0 macht einfach nur nen Hotspot.

SMB

SMB

SAMBA Befehle

http://lug.krems.cc/docu/samba/ch08_06.html

Influx

Influx

Daten in einem Measurement Löschen - Influx

🧹 InfluxDB 2.x – Alle Daten eines Measurements löschen

Ziel: Vollständiges Löschen aller Daten eines bestimmten Measurements aus einem Bucket in InfluxDB 2.x

📌 Voraussetzungen

🖥️ Methode 1: Löschen per Influx CLI

influx delete \
  --org "MEINE_ORG" \
  --bucket "MEIN_BUCKET" \
  --start 1970-01-01T00:00:00Z \
  --stop 2100-01-01T00:00:00Z \
  --predicate '_measurement="MEIN_MEASUREMENT"'

📝 Erklärung

🔐 Authentifizierung

export INFLUX_TOKEN="DEIN_TOKEN_HIER"

🌐 Methode 2: Löschen über HTTP API

📩 Request

POST /api/v2/delete?org=MEINE_ORG&bucket=MEIN_BUCKET
Authorization: Token DEIN_TOKEN
Content-Type: application/json

{
  "start": "1970-01-01T00:00:00Z",
  "stop": "2100-01-01T00:00:00Z",
  "predicate": "_measurement=\"MEIN_MEASUREMENT\""
}

🔗 Beispiel mit curl

curl --request POST \
  "http://localhost:8086/api/v2/delete?org=MEINE_ORG&bucket=MEIN_BUCKET" \
  --header "Authorization: Token DEIN_TOKEN" \
  --header "Content-Type: application/json" \
  --data '{
    "start": "1970-01-01T00:00:00Z",
    "stop": "2100-01-01T00:00:00Z",
    "predicate": "_measurement=\"MEIN_MEASUREMENT\""
  }'

⚠️ Hinweise

✅ Beispiel

influx delete \
  --org "my-org" \
  --bucket "iot-data" \
  --start 1970-01-01T00:00:00Z \
  --stop 2100-01-01T00:00:00Z \
  --predicate '_measurement="temperatur_sensor"'
Influx

Influx - Datenbank anlegen

0 | Vorbereitungen

apt-get update
apt-get install -y \
curl gnupg ca-certificates lsb-release coreutils

1 | Repository & Paketinstallation

curl --silent --location -O https://repos.influxdata.com/influxdata-archive.key
echo "943666881a1b8d9b849b74caebf02d3465d6beb716510d86a39f6c8e8dac7515  influxdata-archive.key" \
| sha256sum --check -

cat influxdata-archive.key | gpg --dearmor \
| tee /etc/apt/trusted.gpg.d/influxdata-archive.gpg >/dev/null

echo 'deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive.gpg] https://repos.influxdata.com/debian stable main' \
| tee /etc/apt/sources.list.d/influxdata.list

apt-get update
apt-get install -y influxdb2
systemctl enable --now influxdb

2 | Selbst-signiertes Zertifikat erzeugen (100 Jahre)

cat > /root/san.cnf <<'EOF'
[req]
distinguished_name = dn
req_extensions     = v3_req
prompt             = no
[dn]
CN = influxdb.local
[v3_req]
keyUsage         = digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName   = @alt
[alt]
DNS.1 = influxdb.local
IP.1  = 127.0.0.1
EOF

openssl req -new -newkey rsa:4096 -nodes \
  -keyout /etc/ssl/influxdb.key \
  -out    /etc/ssl/influxdb.csr \
  -config /root/san.cnf

openssl x509 -req -in  /etc/ssl/influxdb.csr \
  -signkey          /etc/ssl/influxdb.key \
  -out              /etc/ssl/influxdb.crt \
  -days 36500 -extensions v3_req -extfile /root/san.cnf

rm /etc/ssl/influxdb.csr /root/san.cnf
chown influxdb:influxdb /etc/ssl/influxdb.{crt,key}
chmod 644 /etc/ssl/influxdb.crt
chmod 600 /etc/ssl/influxdb.key

3 | HTTPS in config.toml aktivieren

# Default-Konfig erzeugen, falls nicht vorhanden
test -f /etc/influxdb/config.toml || influxd print-config > /etc/influxdb/config.toml

# Port sicherstellen & alte TLS-Zeilen entfernen
sed -i \
  -e 's|^[# ]*http-bind-address .*|http-bind-address = ":8086"|' \
  -e '/^[# ]*tls-cert /d' \
  -e '/^[# ]*tls-key  /d' \
  /etc/influxdb/config.toml

# TLS-Pfad-Direktiven anhängen
cat >> /etc/influxdb/config.toml <<'EOT'
tls-cert = "/etc/ssl/influxdb.crt"
tls-key  = "/etc/ssl/influxdb.key"
EOT

# Influx-Startskript auf diese Konfig verweisen
echo 'INFLUXD_CONFIG_PATH=/etc/influxdb/config.toml' > /etc/default/influxdb2

systemctl daemon-reload
systemctl restart influxdb

4 | Funktionstest

curl --insecure https://localhost:8086/api/v2/ping -I
# Erwartung: HTTP/1.1 204 No Content

Hinweise


5 | Alles-in-einem-Skript (Copy & Paste)

#!/usr/bin/env bash
# install_influxdb_https.sh – Einmal ausführen, um InfluxDB 2 + HTTPS einzurichten
set -euo pipefail

### 0 | Pakete
apt-get update
apt-get install -y curl gnupg ca-certificates lsb-release coreutils

### 1 | Repo & Paket
curl --silent --location -O https://repos.influxdata.com/influxdata-archive.key
echo "943666881a1b8d9b849b74caebf02d3465d6beb716510d86a39f6c8e8dac7515  influxdata-archive.key" | sha256sum --check -
cat influxdata-archive.key | gpg --dearmor | tee /etc/apt/trusted.gpg.d/influxdata-archive.gpg >/dev/null
echo 'deb [signed-by=/etc/apt/trusted.gpg.d/influxdata-archive.gpg] https://repos.influxdata.com/debian stable main' \
  | tee /etc/apt/sources.list.d/influxdata.list
apt-get update
apt-get install -y influxdb2
systemctl enable --now influxdb

### 2 | TLS-Zertifikat (100 Jahre)
cat >/tmp/san.cnf <<'EOF'
[req]
distinguished_name = dn
req_extensions     = v3_req
prompt             = no
[dn]
CN = influxdb.local
[v3_req]
keyUsage         = digitalSignature, keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName   = @alt
[alt]
DNS.1 = influxdb.local
IP.1  = 127.0.0.1
EOF

openssl req -new -newkey rsa:4096 -nodes \
  -keyout /etc/ssl/influxdb.key \
  -out    /etc/ssl/influxdb.csr \
  -config /tmp/san.cnf
openssl x509 -req -in /etc/ssl/influxdb.csr -signkey /etc/ssl/influxdb.key \
  -out /etc/ssl/influxdb.crt -days 36500 -extensions v3_req -extfile /tmp/san.cnf
rm /etc/ssl/influxdb.csr /tmp/san.cnf
chown influxdb:influxdb /etc/ssl/influxdb.{crt,key}
chmod 644 /etc/ssl/influxdb.crt
chmod 600 /etc/ssl/influxdb.key

### 3 | config.toml anpassen
test -f /etc/influxdb/config.toml || influxd print-config > /etc/influxdb/config.toml
sed -i -e 's|^[# ]*http-bind-address .*|http-bind-address = ":8086"|' \
       -e '/^[# ]*tls-cert /d' -e '/^[# ]*tls-key  /d' \
       /etc/influxdb/config.toml
printf "\ntls-cert = \"/etc/ssl/influxdb.crt\"\ntls-key  = \"/etc/ssl/influxdb.key\"\n" \
  >> /etc/influxdb/config.toml
echo 'INFLUXD_CONFIG_PATH=/etc/influxdb/config.toml' > /etc/default/influxdb2

### 4 | Neustart & Test
systemctl daemon-reload
systemctl restart influxdb
curl --insecure https://localhost:8086/api/v2/ping -I
echo "✔ InfluxDB läuft jetzt per HTTPS auf Port 8086"


Vim

Änderungen rückgängig machen

In Vim kannst du Änderungen mit u (undo) rückgängig machen und mit Strg + R (redo) wiederherstellen:

  1. In den Normal Mode wechseln (drücke Esc, um sicherzugehen, dass du im Normal Mode bist).
  2. u drücken, um den letzten Befehl bzw. die letzte Änderung rückgängig zu machen.
  3. Mehrmals u drücken, um mehrere Schritte rückgängig zu machen.
  4. Strg + R drücken, um rückgängig gemachte Schritte wiederherzustellen (Redo).

Berechtigungen - chmod

🔐 Rechteverwaltung unter Linux – chmod erklärt

Linux verwendet ein einfaches, aber mächtiges System zur Rechtevergabe für Dateien und Ordner. Mit dem Befehl chmod kannst du steuern, wer lesen, schreiben oder ausführen darf – mit Zahlen (z. B. chmod 770) oder Buchstaben (z. B. chmod g+s).


📁 Die 3 Ebenen der Dateiberechtigungen

Für jede dieser Ebenen kannst du Berechtigungen vergeben:


🔢 chmod mit Zahlen

Die häufigste Form ist die Zahlendarstellung, z. B. chmod 755 datei

Jede Ziffer steht für einen Teil der Berechtigung:

Zahl Binär Bedeutung Erlaubt
0 000 --- keine Rechte
1 001 --x nur ausführen
2 010 -w- nur schreiben
3 011 -wx schreiben + ausführen
4 100 r-- nur lesen
5 101 r-x lesen + ausführen
6 110 rw- lesen + schreiben
7 111 rwx alles erlaubt

Beispiel:

chmod 750 meinskript.sh
# ergibt: rwxr-x--- (Owner: alles, Group: lesen + ausführen, Others: nichts)

🔤 chmod mit Buchstaben (symbolische Schreibweise)

Du kannst Berechtigungen auch gezielt setzen oder entfernen:

Die Buchstaben stehen für:


⚙️ Spezielle Modi: Setuid, Setgid, Sticky

Bit Zahl Symbol Beschreibung
Setuid 4 s Prozesse starten mit den Rechten des Dateibesitzers
Setgid 2 s Neue Dateien im Ordner erben die Gruppenzugehörigkeit
Sticky Bit 1 t Nur der Besitzer darf Dateien löschen (z. B. in /tmp)

Beispiel: Gruppenvererbung aktivieren

chmod 2770 /gemeinsamer/ordner
# Ergebnis: rwxrws--- (Setgid aktiv → Gruppe wird vererbt!)

Alternativ:

chmod g+s /pfad

🧠 Beispiele aus der Praxis

# Nur Besitzer darf lesen + schreiben, Gruppe darf nur lesen:
chmod 640 datei.txt

# Alles für Besitzer und Gruppe, andere dürfen durch Verzeichnis springen:
chmod 2771 /mnt/shared

# Alle dürfen alles:
chmod 777 /mnt/offen

# Gruppe "admin" soll dauerhaft Zugriff behalten:
chown root:admin /mnt/projekt
chmod 2770 /mnt/projekt

🔍 Rechte prüfen mit ls -l

Mit ls -l siehst du die Rechte:

-rwxrws--- 1 root admin  ...   ← s = Setgid aktiv
drwxrwxrwt 2 root root   ...   ← t = Sticky Bit aktiv (z. B. /tmp)

✅ Zusammenfassung

Nutze Setgid für gemeinsame Team-Ordner, Sticky für geteilte Schreibbereiche und plane Rechte frühzeitig – spart dir später Ärger 😉

Bluetooth Autoconnect

Bluetooth-Geräte verbinden sich ständig von selbst? – Fix für Kali Linux (2024 / 2025)

Betroffene Systeme

Kali Linux Rolling ab etwa 2024-04 bis heute (Debian testing / sid).
Entscheidend ist die neue Kombination aus pipewire 1.0 +, wireplumber 0.5 + und blueman 2.3 +.

Das Symptom

Nach einem Dist-Upgrade verbindet sich der Laptop alle 30-60 s wieder mit bereits gekoppelten Bluetooth-Geräten (Headsets, Lautsprecher …), auch wenn diese gerade an einem anderen Gerät (z. B. Handy) genutzt werden.

Warum passiert das?

Was bewirkt dconf reset -f /org/blueman/ ?

dconf ist die Datenbank, in der GTK-Programme ihre Benutzer-Einstellungen speichern (ähnlich der Windows-Registry). Der Befehl dconf reset -f /org/blueman/ löscht alle Blueman-Schlüssel **für den aktuellen Benutzer** – also auch die Liste services des Auto-Connect-Plugins.

Schritt-für-Schritt-Fix

  1. Öffne ein Terminal als Dein normaler Benutzer (nicht root).
  2. Blueman beenden:

    killall blueman-applet 2>/dev/null
  3. Blueman-Einstellungen zurücksetzen:

    dconf reset -f /org/blueman/
    → die Liste services wird auf [] gesetzt, alle Plugins stehen wieder auf ihren Default-Werten (Auto-Connect = aus).
  4. Applet neu starten (leise im Hintergrund):

    nohup blueman-applet >/dev/null 2>&1 &
  5. Test:

    • Headset am Laptop trennen.
    • Headset am Handy verbinden.
    • Eine Minute warten – der Laptop soll jetzt nicht mehr stören.

Was, wenn ich Auto-Connect später wieder brauche?

Einfach Blueman-Manager → Plugins → Häkchen bei “Auto Connect” erneut setzen (oder mit
gsettings set org.blueman.plugins.autoconnect services "['audio', 'input']" die Dienste einzeln hinzufügen).

Zuletzt aktualisiert: 9 Mai 2025 · getestet auf Kali 2025.2 (rolling)

vnstat - Interface adden

vnStat: Netzwerk-Traffic-Monitoring für mehrere Interfaces

vnStat ist ein ressourcenschonendes Tool zur Überwachung von Netzwerkdatenverkehr unter Linux. Diese Anleitung erklärt, wie du vnStat so einrichtest, dass es alle verfügbaren Interfaces überwacht, inklusive VPN- oder virtuellen Interfaces wie wg0 (WireGuard), tailscale0, usw.


1. Voraussetzungen


2. Verfügbare Netzwerkschnittstellen anzeigen

Zeige alle verfügbaren Interfaces an:

Oder alternativ:

ls /sys/class/net/

Beispielausgabe:

lo  eth0  wg0  tailscale0

3. Schnittstellen zu vnStat hinzufügen

Füge jedes gewünschte Interface manuell zur vnStat-Datenbank hinzu:

sudo vnstat --add -i <interface-name>

Beispiel:


sudo vnstat --add -i wg0
sudo vnstat --add -i tailscale0

Wiederhole den Befehl für jedes Interface, das du überwachen möchtest.


4. Dienst neu starten

Damit vnStat mit den neuen Einstellungen arbeitet, starte den Dienst neu:

sudo systemctl restart vnstat

5. Überwachte Interfaces anzeigen

Überprüfe, welche Interfaces derzeit überwacht werden:

vnstat --iflist

Oder zeige Statistiken für ein bestimmtes Interface:

vnstat -i wg0

6. Optional: Automatisches Hinzufügen neuer Interfaces

Falls du häufig neue oder virtuelle Interfaces nutzt (z. B. durch VPNs), kannst du folgendes Skript verwenden, um automatisch alle Schnittstellen hinzuzufügen, die noch nicht überwacht werden:

#!/bin/bash
for IFACE in $(ls /sys/class/net/); do
    if ! vnstat --iflist | grep -q "$IFACE"; then
        sudo vnstat --add -i "$IFACE"
    fi
done

Speichern z. B. als /usr/local/bin/vnstat-interface-check.sh und ausführbar machen:

chmod +x /usr/local/bin/vnstat-interface-check.sh

Dann via cron regelmäßig ausführen lassen:

crontab -e
0 * * * * /usr/local/bin/vnstat-interface-check.sh

→ Das Skript läuft stündlich und fügt neue Interfaces bei Bedarf hinzu.


7. Hinweise


Fertig! Du hast nun eine vollständige vnStat-Konfiguration für mehrere Interfaces eingerichtet.

Nachricht an alle angemeldeten User senden

Nachrichten an angemeldete Benutzer im Linux-Terminal senden

In Linux-Systemen gibt es einfache Möglichkeiten, Nachrichten an aktuell angemeldete Benutzer zu senden. Dies ist besonders nützlich für Systemadministratoren, um wichtige Informationen schnell zu verbreiten.

1. Aktuell angemeldete Benutzer anzeigen

Verwende den Befehl who, um eine Liste aller derzeit angemeldeten Benutzer zu erhalten:

who

Die Ausgabe zeigt:

2. Nachricht an alle Benutzer senden

Mit dem Befehl wall kannst du eine Nachricht an alle angemeldeten Benutzer senden:

wall Hallo zusammen!

Alle Benutzer sehen die Nachricht in ihrem Terminal.

3. Nachricht an einen bestimmten Benutzer senden

Verwende den Befehl write, um einem bestimmten Benutzer eine Nachricht zu senden:

write benutzername
Bitte kommen Sie in mein Büro

Beispiel:

write robert
Bitte kommen Sie in mein Büro

Falls der Benutzer mehrfach angemeldet ist, wird die Nachricht an das erste gefundene Terminal gesendet:

write: robert is logged in more than once; writing to pts/0

4. Nachricht an eine Benutzergruppe senden

Um Nachrichten an eine bestimmte Gruppe von Benutzern zu senden, kannst du wall in Kombination mit grep verwenden. Beispiel:

who | grep 'benutzergruppe' | awk '{print $1}' | xargs -I {} write {} Nachrichtentext

Ersetze benutzergruppe durch ein eindeutiges Merkmal der Benutzer (z. B. Gruppennamen) und Nachrichtentext durch die gewünschte Nachricht.

Fazit

Die Befehle who, wall und write ermöglichen eine effektive Kommunikation mit Benutzern direkt über das Terminal, ohne zusätzliche Software installieren zu müssen.

Thunderbird

Thunderbird

Vivaldi anstatt Firefox als Standard - Thunderbird Linux

Diese Anleitung zeigt Schritt für Schritt, wie du Thunderbird so konfigurierst, dass Links in Vivaldi (und nicht Firefox) geöffnet werden. Sie behandelt außerdem ein häufiges Absturzproblem nach dem Link-Klick unter Kali/Debian.

1. Vivaldi als Standardbrowser im System festlegen

xdg-settings set default-web-browser vivaldi-stable.desktop
update-alternatives --set x-www-browser /usr/bin/vivaldi-stable

Prüfen mit:

xdg-settings get default-web-browser
xdg-mime query default x-scheme-handler/http
xdg-mime query default x-scheme-handler/https

Falls nötig, setzen mit:

xdg-mime default vivaldi-stable.desktop x-scheme-handler/http
xdg-mime default vivaldi-stable.desktop x-scheme-handler/https

2. Thunderbird anpassen (Konfiguration über about:config)

  1. Thunderbird starten
  2. Menü → Einstellungen → Allgemein → ganz unten „Konfiguration bearbeiten“
  3. Suche nach:
    network.protocol-handler.app.http
    network.protocol-handler.app.https
    → Falls vorhanden: Zurücksetzen oder auf folgendes setzen:
/usr/bin/vivaldi-stable

Zusätzlich sicherstellen:

network.protocol-handler.external.http  = true
network.protocol-handler.external.https = true

→ Falls nicht vorhanden, neu hinzufügen als Boolean.

3. Absturzproblem beheben (Vivaldi detached starten)

Bei manchen Systemen stürzt Thunderbird ab, wenn er Vivaldi direkt startet. Lösung:

  1. Starte Vivaldi detached im Terminal zum Test:
nohup /usr/bin/vivaldi-stable https://example.com >/dev/null 2>&1 &
  1. Erstelle ein Wrapper-Skript:
nano ~/vivaldi-detached.sh

Inhalt:

#!/bin/bash
nohup /usr/bin/vivaldi-stable "$@" >/dev/null 2>&1 &
  1. Speichern und ausführbar machen:
chmod +x ~/vivaldi-detached.sh
  1. In Thunderbird (about:config):
network.protocol-handler.app.http  = /home/USERNAME/vivaldi-detached.sh
network.protocol-handler.app.https = /home/USERNAME/vivaldi-detached.sh

Jetzt öffnet Thunderbird Vivaldi detached und stürzt nicht mehr ab.

4. Falls Thunderbird trotzdem crasht: Neues Profil testen

Thunderbird-Profil kann beschädigt sein.

  1. Alle Fenster schließen
  2. Terminal öffnen:
thunderbird -P
  1. Neues Profil erstellen (z. B. „Testprofil“)
  2. Starten und Vivaldi wie oben konfigurieren

5. Optional: handlers.json zurücksetzen

rm ~/.thunderbird/*.default*/handlers.json

Fertig! Thunderbird nutzt nun systemweit Vivaldi, stürzt nicht mehr ab, und dein Setup ist sauber konfiguriert.

RDP

RDP

Version_0.5 xfreerdp - Skript

RDP-Launcher mit xfreerdp3 – Schnellstart & Nutzung

Quickstart: Schritt für Schritt (wenn venv schon existiert)
  1. Terminal öffnen
    Ctrl+Alt+T
  2. In Projektordner wechseln
    cd ~/rdp-launcher
  3. Virtuelle Umgebung aktivieren
    source .venv/bin/activate
    (Bei jedem neuen Terminal nötig!)
  4. Launcher starten
    ./xfreerdp_launcher.py
    (Falls nicht ausführbar: chmod +x xfreerdp_launcher.py nur 1x nötig)
    Oder per Python:
    python3 xfreerdp_launcher.py
  5. Menü nutzen, Standort & Server auswählen
  6. Erste Passwortabfrage – wird sicher im Keyring gespeichert

Diese Anleitung richtet sich an absolute Einsteiger und führt dich in wenigen Schritten zur ersten RDP-Sitzung.
Bitte arbeite jeden Schritt der Reihe nach ab.


1. Voraussetzungen prüfen (nur 1x nötig)

  1. Python & Pip:
    python3 --version
    pip3 --version
    Fehlt was?
    sudo apt update sudo apt install python3 python3-pip python3-venv
  2. xfreerdp3 installieren:
    sudo apt install freerdp3-x11
  3. Keyring-Backend? (Gnome, KDE etc: schon dabei)

2. Projektordner & venv erstellen (nur 1x nötig)

  1. Ordner anlegen & wechseln:
    mkdir -p ~/rdp-launcher
    cd ~/rdp-launcher
  2. Virtuelle Umgebung erstellen:
    python3 -m venv .venv
  3. venv aktivieren:
    source .venv/bin/activate
  4. Abhängigkeit installieren:
    pip install keyring
Tipp: Immer zuerst source .venv/bin/activate ausführen, bevor du arbeitest!

3. Python-Skript anlegen

Speichere folgendes als xfreerdp_launcher.py im Projektordner:

▶ Python-Code anzeigen / ausklappen
#!/usr/bin/env python3
"""
xfreerdp_launcher.py – RDP-Launcher mit TUI und Keyring-Support

Features:
- Übersichtliche, farbige Menüführung (TUI)
- Passwörter sicher im System-Keyring gespeichert
- Automatisches Neuladen der Konfiguration bei Änderungen
- Voll kompatibel mit xfreerdp3-Parametern

Benötigt:
- Python ≥ 3.9
- keyring (pip install keyring)
- config.json im selben Ordner
"""

import argparse
import json
import subprocess
import sys
import os
from pathlib import Path
from getpass import getpass
import keyring

CONFIG_FILE = Path("./config.json").resolve()
SERVICE = "xfreerdp_launcher"

def farbig(text, farbe="reset"):
    farben = {
        "reset": "\033[0m", "bold": "\033[1m", "red": "\033[31m",
        "green": "\033[32m", "yellow": "\033[33m", "blue": "\033[34m",
        "magenta": "\033[35m", "cyan": "\033[36m", "gray": "\033[90m",
        "bright_red": "\033[91m", "bright_green": "\033[92m",
        "bright_yellow": "\033[93m", "bright_blue": "\033[94m",
        "bright_magenta": "\033[95m", "bright_cyan": "\033[96m",
        "bright_white": "\033[97m"
    }
    return f"{farben.get(farbe, '')}{text}{farben['reset']}"

def lade_config():
    try:
        return json.loads(CONFIG_FILE.read_text())
    except Exception as e:
        print(farbig(f"⚠️  Fehler in config.json: {e}", "red"))
        return {}

def passwort_holen(cred_alias, domain, user):
    cred_id = f"{cred_alias}|{domain}|{user}"
    pw = keyring.get_password(SERVICE, cred_id)
    if pw is None:
        pw = getpass(f"Passwort für {user}@{domain}: ")
        if not pw:
            sys.exit("Abbruch – kein Passwort eingegeben.")
        keyring.set_password(SERVICE, cred_id, pw)
        print(farbig("Passwort gespeichert.", "green"))
    return pw

def rdp_starten(ip, domain, user, pw, extra_args):
    cmd = [
        "xfreerdp3",
        f"/u:{user}", f"/d:{domain}", f"/v:{ip}",
        "/dynamic-resolution", "/clipboard", "/cert:ignore",
        f"/p:{pw}"
    ] + extra_args
    try:
        subprocess.Popen(cmd)
    except Exception as e:
        print(farbig(f"Fehler beim Start: {e}", "red"))

def host_verbinden(entry, creds, default_cred, extra_args):
    cred = entry.get("cred", default_cred)
    profile = creds.get(cred)
    if not profile:
        print(farbig(f"Credential '{cred}' fehlt!", "red"))
        input("⏎ Weiter...")
        return
    pw = passwort_holen(cred, profile["domain"], profile["user"])
    rdp_starten(entry["host"], profile["domain"], profile["user"], pw, extra_args)
    print(farbig(f"Sitzung zu {entry['alias']} gestartet.", "cyan"))
    input("⏎ Weiter...")

def menue(section, creds, default_cred, extra_args, path="", offene=set()):
    is_root = (path == "")
    while True:
        config = lade_config()
        groups = config.get("groups", {})
        credentials = config.get("credentials", {})
        default = config.get("default_cred", default_cred)
        if is_root:
            section = {"groups": groups}
        creds = credentials
        default_cred = default

        os.system("clear")
        print(farbig(f"=== RDP-Menü {path} ===\n", "bold"))
        groups = section.get("groups", {})
        hosts = section.get("hosts", [])
        index = {}
        i = 1
        if is_root:
            for name, content in groups.items():
                print(f" {i:2}) {farbig(name, content.get('color', 'reset'))} →")
                index[str(i)] = ("group", content, f"{path}/{name}")
                i += 1
        else:
            for h in hosts:
                alias = h["alias"]
                mark = farbig("✅", "green") if alias in offene else "  "
                print(f" {i:2}) {farbig(alias, h.get('color', 'reset'))} – {h['host']} {mark}")
                index[str(i)] = ("host", h)
                i += 1

        print("\n  b) Zurück")
        print("  q) Beenden")
        sel = input(farbig("Auswahl: ", "bold")).strip()
        if sel == "q":
            sys.exit("Beende Launcher.")
        elif sel == "b":
            return
        elif sel in index:
            typ, eintrag, *rest = index[sel]
            if typ == "group":
                menue({"hosts": eintrag.get("hosts", [])}, creds, default_cred, extra_args, rest[0], offene)
            elif typ == "host":
                offene.add(eintrag["alias"])
                host_verbinden(eintrag, creds, default_cred, extra_args)
        else:
            print(farbig("Ungültige Auswahl.", "red"))
            input("⏎ Weiter...")

def main():
    parser = argparse.ArgumentParser(description="xfreerdp Launcher mit Keyring")
    parser.add_argument("freerdp_args", nargs=argparse.REMAINDER, help="zusätzliche xfreerdp3-Parameter")
    args = parser.parse_args()
    offene = set()
    while True:
        config = lade_config()
        creds = config.get("credentials", {})
        default = config.get("default_cred", "")
        groups = config.get("groups")
        if not groups:
            print(farbig("⚠️  Keine Gruppen in config.json.", "yellow"))
            input("⏎ Weiter...")
            continue
        menue({"groups": groups}, creds, default, args.freerdp_args, "", offene)

if __name__ == "__main__":
    try:
        main()
    except KeyboardInterrupt:
        print("\n[Abbruch]")

4. Beispiel config.json

Erstelle im selben Ordner eine Datei config.json mit folgendem Inhalt:

▶ Beispiel-Konfiguration anzeigen / ausklappen
{
  "default_cred": "user1",
  "credentials": {
    "user1": { "domain": "FIRMENNETZ", "user": "max.mustermann" },
    "user2": { "domain": "INTRANET",   "user": "maria.musterfrau" }
  },
  "groups": {
    "Standort_A": {
      "color": "bright_blue",
      "hosts": [
        { "alias": "Server-A1", "host": "10.0.10.11", "color": "bright_green",  "cred": "user1" },
        { "alias": "Server-A2", "host": "10.0.10.12", "color": "bright_magenta","cred": "user2" }
      ]
    },
    "Standort_B": {
      "color": "bright_yellow",
      "hosts": [
        { "alias": "Server-B1", "host": "10.0.20.21", "color": "bright_cyan",   "cred": "user1" },
        { "alias": "Server-B2", "host": "10.0.20.22", "color": "bright_red",    "cred": "user2" }
      ]
    }
  }
}

IP-Adressen, Farben etc. kannst du jederzeit anpassen!


5. Alles ausführen – Zusammengefasst

Mini-Quickstart:
Ctrl+Alt+T  cd ~/rdp-launcher  source .venv/bin/activate  ./xfreerdp_launcher.py

Linux - SSH

Linux - SSH

SSH Authentifizierung

Debian SSH: Passwort für normale User, SSH-Key für root

Diese Anleitung zeigt, wie du OpenSSH so konfigurierst, dass sich normale Benutzer mit Passwort anmelden können, während sich root ausschließlich mit SSH-Schlüssel (Zertifikat/Key) anmelden darf.

Voraussetzungen

1) SSH-Key für root vorbereiten

  1. Auf deinem Admin-Client (nicht auf dem Server) einen Key erzeugen (falls noch nicht vorhanden):
    ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519
    ssh-keygen -t ed25519 -a 256 -f ~/.ssh/id_ed25519 -C "jj@little-snitch"
    
  2. Öffentlichen Schlüssel auf den Server in die root-Authorized Keys kopieren:
    ssh-copy-id -i ~/.ssh/id_ed25519.pub root@server.example.com

    Alternativ manuell:

    cat ~/.ssh/id_ed25519.pub | ssh root@server.example.com 'mkdir -p /root/.ssh && chmod 700 /root/.ssh && cat >> /root/.ssh/authorized_keys && chmod 600 /root/.ssh/authorized_keys'

2) Bestehende SSH-Konfiguration sichern

cp -a /etc/ssh/sshd_config /etc/ssh/sshd_config.backup.$(date +%F-%H%M%S)

3) Kerneinstellungen

Die zentrale Idee:

4) Beispiel: vollständige /etc/ssh/sshd_config

Folgende Konfiguration entspricht deinem Beispiel und erzwingt für root Key-Login, lässt für master771 sowohl Passwort als auch Key zu. Andere Benutzer folgen den globalen Einstellungen (Passwort erlaubt, Key erlaubt):

root@mqtt:~# cat /etc/ssh/sshd_config

# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.

# This sshd was compiled with PATH=/usr/local/bin:/usr/bin:/bin:/usr/games

# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented.  Uncommented options override the
# default value.

Include /etc/ssh/sshd_config.d/*.conf

#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key

# Ciphers and keying
#RekeyLimit default none

# Logging
#SyslogFacility AUTH
#LogLevel INFO

# Authentication:

#LoginGraceTime 2m
#PermitRootLogin prohibit-password
PermitRootLogin prohibit-password
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

#PubkeyAuthentication yes
PubkeyAuthentication yes

# Expect .ssh/authorized_keys2 to be disregarded by default in future.
#AuthorizedKeysFile     .ssh/authorized_keys .ssh/authorized_keys2

#AuthorizedPrincipalsFile none

#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody

# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes

# To disable tunneled clear text passwords, change to "no" here!
#PasswordAuthentication yes
PasswordAuthentication yes
#PermitEmptyPasswords no

# Change to "yes" to enable keyboard-interactive authentication.  Depending on
# the system's configuration, this may involve passwords, challenge-response,
# one-time passwords or some combination of these and other methods.
# Beware issues with some PAM modules and threads.
KbdInteractiveAuthentication no

# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no

# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the KbdInteractiveAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via KbdInteractiveAuthentication may bypass
# the setting of "PermitRootLogin prohibit-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and KbdInteractiveAuthentication to 'no'.
UsePAM yes

#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
X11Forwarding yes
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
PrintMotd no
#PrintLastLog yes
#TCPKeepAlive yes
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
#PidFile /run/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none

# no default banner path
#Banner none

# Allow client to pass locale and color environment variables
AcceptEnv LANG LC_* COLORTERM NO_COLOR

# override default of no subsystems
Subsystem       sftp    /usr/lib/openssh/sftp-server

# Example of overriding settings on a per-user basis
#Match User anoncvs
#       X11Forwarding no
#       AllowTcpForwarding no
#       PermitTTY no
#       ForceCommand cvs server

Match User root
    PasswordAuthentication no
    PubkeyAuthentication yes

Match User master771
    PasswordAuthentication yes
    PubkeyAuthentication yes

5) Syntax prüfen und SSH neu laden

  1. Syntaxcheck:
    sshd -t

    Wenn keine Ausgabe erfolgt, ist die Syntax OK.

  2. Dienst neu laden (ohne laufende Sessions zu kappen):
    systemctl reload ssh

    Falls reload nicht verfügbar, dann:

    systemctl restart ssh

     

6) Sicher testen (zweite Session!)

  1. Neue Terminal-Session öffnen, bestehende Sitzung nicht schließen.
  2. Test root per Key:
    ssh -i ~/.ssh/id_ed25519 root@server.example.com
  3. Test root per Passwort (soll scheitern):
    ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no root@server.example.com
  4. Test normaler Benutzer per Passwort (soll funktionieren):
    ssh -o PreferredAuthentications=password -o PubkeyAuthentication=no master771@server.example.com

Optionen & Varianten

Rollback

Falls etwas schiefgeht, via Konsole (z. B. VM-Konsole) die Sicherung zurückspielen:

mv /etc/ssh/sshd_config.backup.YYYY-MM-DD-HHMMSS /etc/ssh/sshd_config
systemctl restart ssh

Hinweise zur Sicherheit


Stand: aktualisiere diese Seite, wenn sich deine Policy ändert oder neue Benutzer hinzukommen.

Linux - SSH

SSH - only key

Ziel: Auf dem Client ein Ed25519-Schlüsselpaar mit Passphrase erzeugen und in ~/.ssh/config hinterlegen. Auf dem Server Logins für root und jj auf Public-Key-Authentifizierung beschränken.

Platzhalter


1) Client: SSH-Key mit Passphrase erzeugen

Befehl (kopierbar)

ssh-keygen \
  -t ed25519 \
  -a 1024 \
  -o \
  -Z aes256-ctr \
  -C "$Dieser_Client" \
  -f "/root/.ssh/$org/$device_name/id_ed25519"

Beispielausgabe

Generating public/private ed25519 key pair.
Enter passphrase for "/root/.ssh/JSc/little-ghost/id_ed25519" (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/JSc/little-ghost/id_ed25519
Your public key has been saved in /root/.ssh/JSc/little-ghost/id_ed25519.pub
The key fingerprint is:
SHA256:**************************** little-ghost
The key's randomart image is:
+--[ED25519 256]--+
|*****************|
|*****************|
|*****************|
|*****************|
|*****************|
|*****************|
|*****************|
|*****************|
|*****************|
+----[SHA256]-----+

Optional: Rechte setzen

chmod 700 /root/.ssh /root/.ssh/JSc /root/.ssh/JSc/little-ghost
chmod 600 /root/.ssh/JSc/little-ghost/id_ed25519
chmod 644 /root/.ssh/JSc/little-ghost/id_ed25519.pub

Beispielausgabe

# (keine Ausgabe bei Erfolg)
# Kontrolle:
ls -ld /root/.ssh /root/.ssh/JSc /root/.ssh/JSc/little-ghost
ls -l /root/.ssh/JSc/little-ghost/

2) Client: SSH-Konfiguration für Zielhost

In /root/.ssh/config eintragen/ergänzen:

Dateiinhalt (kopierbar)

Host SERVER_IP
  HostName SERVER_IP
  IdentitiesOnly yes
  IdentityFile /root/.ssh/JSc/little-ghost/id_ed25519
  ForwardAgent no
  ForwardX11 no
  StrictHostKeyChecking ask
  UserKnownHostsFile /root/.ssh/known_hosts

Beispielausgabe

# Kontrolle:
cat /root/.ssh/config

3) Client: Public Key anzeigen/kopieren

Befehl (kopierbar)

cat /root/.ssh/JSc/little-ghost/id_ed25519.pub

Beispielausgabe

ssh-ed25519 AAAAAAAAAABBBBBBBBBB/CCCCCCCCCC little-ghost

Diesen Einzeiler auf dem Server in die Datei ~<user>/.ssh/authorized_keys einfügen (eine Zeile pro Key, keine Umbrüche).


4) Server: root nur per Public Key

Datei /etc/ssh/sshd_config.d/10-root-only-key.conf anlegen/ändern.

Dateiinhalt (kopierbar)

# Root: nur noch Public-Key, keine Passwörter/Keyboard-Interactive
Match User root
    PubkeyAuthentication yes
    PasswordAuthentication no
    KbdInteractiveAuthentication no
    AuthenticationMethods publickey
    PermitRootLogin prohibit-password

Beispielausgabe

# Kontrolle:
cat /etc/ssh/sshd_config.d/10-root-only-key.conf

5) Server: jj nur per Public Key

Datei /etc/ssh/sshd_config.d/20-jj-only-key.conf anlegen/ändern.

Dateiinhalt (kopierbar)

# jj: nur noch Public-Key, keine Passwort-/Keyboard-Interactive-Logins
Match User jj
    PubkeyAuthentication yes
    PasswordAuthentication no
    KbdInteractiveAuthentication no
    AuthenticationMethods publickey

Beispielausgabe

# Kontrolle:
cat /etc/ssh/sshd_config.d/20-jj-only-key.conf

6) Server: Syntax prüfen & Dienst neu laden

Befehl (kopierbar)

sshd -t
sleep 30
systemctl reload ssh   # Debian-Standarddienstname

Beispielausgabe

# Bei korrekter Konfiguration keine Ausgabe.
# Fehler würden hier mit Zeile/Datei gemeldet werden.

7) Client: Login zum Server testen

Befehl (kopierbar)

ssh SERVER_IP

Beispielausgabe

The authenticity of host 'SERVER_IP (SERVER_IP)' can't be established.
ED25519 key fingerprint is SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'SERVER_IP' (ED25519) to the list of known hosts.
Enter passphrase for key '/root/.ssh/JSc/little-ghost/id_ed25519':
Last login: Wed Sep 24 12:34:56 2025 from 203.0.113.42
root@server:~#

Hinweise

Stand: 25.09.2025

Linux - SSH

SSHFS - Storage

🔐 SSHFS-Mount Anleitung für Linux + Nextcloud

Beispiele nutzen Fantasie-Namen und RFC-5737-Dummy-IP (192.0.2.0/24):
Client: orion-client • Server: atlas-storage • Server-IP: 192.0.2.55
Zielpfad auf dem Server: /srv/projects/ai/labs/local-sdxl-cpu


0. Voraussetzungen (Server)

# Gruppe für SSHFS-Nutzer anlegen (falls noch nicht vorhanden)
groupadd sshfsusers

# Verzeichnis für hinterlegte Public Keys anlegen und absichern
mkdir -p /etc/sshfs_keys
chown root:root /etc/sshfs_keys
chmod 0755 /etc/sshfs_keys

1. SSH-Schlüssel erstellen (Client)

# moderner Schlüssel (ed25519)
ssh-keygen -t ed25519 -a 2048 -o -Z aes256-ctr -f ~/.ssh/orion_to_atlas -C "sshfs orion-client → atlas-storage"

2. Öffentlichen Schlüssel auf den Server übertragen

scp ~/.ssh/orion_to_atlas.pub root@192.0.2.55:/etc/sshfs_keys/sshfs_orion.pub
# auf dem Server:
chown root:root /etc/sshfs_keys/sshfs_orion.pub
chmod 0644 /etc/sshfs_keys/sshfs_orion.pub

3. SSHFS-Benutzer auf dem Server anlegen

# Benutzer ohne Home (kein Risiko durch dotfiles), in Gruppe sshfsusers
useradd -M -g sshfsusers -s /usr/sbin/nologin sshfs_orion

# Ziel-Verzeichnisstruktur (nur Beispielpfad)
mkdir -p /srv/projects/ai/labs/local-sdxl-cpu

# Eigentümer/Gruppe setzen, setgid auf dem Zielordner
chown -R root:sshfsusers /srv/projects
chmod 2750 /srv/projects/ai/labs/local-sdxl-cpu

Wichtig (Traversal nur für die Gruppe): Damit der Benutzer sshfs_orion den Zielordner erreicht, müssen alle Elternordner das x-Recht für die Gruppe haben, ohne Leserechte (kein Listing). Das hält den Pfad geschlossen und erlaubt nur Durchgang.

# Elternordner: Gruppe setzen + nur Durchgang (710)
chgrp sshfsusers /srv
chmod 710       /srv

chgrp sshfsusers /srv/projects
chmod 710        /srv/projects

chgrp sshfsusers /srv/projects/ai
chmod 710        /srv/projects/ai

chgrp sshfsusers /srv/projects/ai/labs
chmod 710        /srv/projects/ai/labs

# Zielordner: nur Besitzer+Gruppe, setgid damit neue Dateien in der Gruppe bleiben
chgrp sshfsusers /srv/projects/ai/labs/local-sdxl-cpu
chmod 2770       /srv/projects/ai/labs/local-sdxl-cpu

4. SSH-Konfiguration auf dem Server

Datei /etc/ssh/sshd_config bearbeiten und am Ende hinzufügen:

Match User sshfs_orion
    AuthorizedKeysFile /etc/sshfs_keys/%u.pub
    PubkeyAuthentication yes
    KbdInteractiveAuthentication no
    PasswordAuthentication no
    AllowTcpForwarding no
    X11Forwarding no
    PermitTTY no
    PermitTunnel no
    ForceCommand internal-sftp
# Syntax prüfen und Dienst neu laden
sshd -t
systemctl reload ssh

5. FUSE erlauben (Client & ggf. Server)

Datei /etc/fuse.conf öffnen und sicherstellen, dass die Zeile ohne Kommentar vorhanden ist:

user_allow_other

6. Bequemer Host-Alias auf dem Client (empfohlen)

Datei ~/.ssh/config (Client) ergänzen:

Host atlas
    HostName 192.0.2.55
    User sshfs_orion
    IdentityFile ~/.ssh/orion_to_atlas
    IdentitiesOnly yes
    ServerAliveInterval 15
    ServerAliveCountMax 3
    StrictHostKeyChecking yes

7. SSHFS-Mount auf dem Client

# lokaler Mountpunkt
mkdir -p /mnt/atlas_labs

# Mount (streng: others=0 → umask=007)
sshfs -o IdentityFile=~/.ssh/orion_to_atlas \
      -o reconnect \
      -o ServerAliveInterval=15 \
      -o ServerAliveCountMax=3 \
      -o allow_other \
      -o default_permissions \
      -o uid=1000 \
      -o gid=1000 \
      -o umask=007 \
      -o idmap=user \
      atlas:/srv/projects/ai/labs/local-sdxl-cpu /mnt/atlas_labs

Hinweise:


8. Optional: Dauerbetrieb per /etc/fstab (Client)

sshfs#sshfs_orion@192.0.2.55:/srv/projects/ai/labs/local-sdxl-cpu  /mnt/atlas_labs  fuse.sshfs  \
IdentityFile=/root/.ssh/orion_to_atlas,allow_other,default_permissions,uid=1000,gid=1000,umask=007, \
reconnect,ServerAliveInterval=15,ServerAliveCountMax=3,idmap=user,_netdev  0  0

🎁 Bonus: Nutzung mit Nextcloud

Mount für www-data (uid/gid 33):

mkdir -p /mnt/nextcloud_data
sshfs -o IdentityFile=/root/.ssh/nextcloud_key \
      -o reconnect -o ServerAliveInterval=15 -o ServerAliveCountMax=3 \
      -o allow_other -o default_permissions \
      -o uid=33 -o gid=33 -o umask=007 -o idmap=user \
      nextcloud_svc@192.0.2.55:/srv/nextcloud/export /mnt/nextcloud_data

9. Troubleshooting (kurz & sicher)

Linux - Mounts (SMB, SSH....)

Linux - Mounts (SMB, SSH....)

SSHFS mount hängt

Wenn der Befehl df -h hängt oder ein Mount nicht mehr reagiert, kannst du folgende Schritte testen.

1. Prüfen, welche sshfs-Verbindungen aktiv sind

ps aux | grep sshfs

Bei mir wurde dabei deutlich, dass noch mehrere sshfs-Prozesse aktiv waren – einer davon schon sehr lange:

root     3026932  1.5  0.0 1708128 12612 ?   Ssl  Jul31 298:43 sshfs -o IdentityFile=...

2. Hängenden sshfs-Prozess beenden

Den betroffenen Prozess mit seiner PID (hier 3026932) beenden:

sudo kill -9 3026932

3. Ergebnis

Nach dem Beenden des Prozesses war mein df -h-Problem sofort gelöst.

4. Ursache

Das Problem lag daran, dass df -h auch Informationen zu FUSE-Dateisystemen wie sshfs abfragt. Wenn eine solche Verbindung hängt oder der sshfs-Daemon nicht mehr antwortet, bleibt der df-Prozess im sogenannten D-State (uninterruptible sleep) stecken und kann nicht normal beendet werden. Das Beenden des zugehörigen sshfs-Prozesses löst die blockierte Anfrage im Kernel, sodass df -h wieder funktioniert.

Linux - Komprimierung

Linux - Komprimierung

TAR

📦 Linux: Tar-Archive erstellen, komprimieren und entpacken

Mit dem tar-Befehl kannst du unter Linux einfach komplette Ordner archivieren und optional komprimieren. Hier ist eine vollständige, übersichtliche Anleitung mit allen wichtigen Optionen und Beispielen.


1️⃣ Einfaches Tar-Archiv erstellen

tar -cvf archivname.tar /pfad/zum/ordner

Beispiel:

tar -cvf meinArchiv.tar meinOrdner/

2️⃣ Archiv mit Kompression erstellen

a) Mit gzip (.tar.gz / .tgz)

tar -czvf meinArchiv.tar.gz meinOrdner/

-z → gzip-Kompression

b) Mit bzip2 (.tar.bz2)

tar -cjvf meinArchiv.tar.bz2 meinOrdner/

-j → bzip2-Kompression

c) Mit xz (.tar.xz)

tar -cJvf meinArchiv.tar.xz meinOrdner/

-J → xz-Kompression (stärkste Kompression, aber langsamer)


3️⃣ Nützliche Optionen

a) Verzeichniswechsel mit -C

tar -czvf meinArchiv.tar.gz -C /pfad/zum meinOrdner/

→ Nimmt nur meinOrdner, nicht den gesamten Pfad ins Archiv auf

b) Dateien oder Verzeichnisse ausschließen

tar -czvf meinArchiv.tar.gz --exclude='*.log' meinOrdner/

→ Schließt alle .log-Dateien vom Archiv aus

c) Archivgröße vor und nach Kompression prüfen

du -sh meinArchiv.tar.gz

4️⃣ Inhalt eines Archivs anzeigen (ohne entpacken)

a) Unkomprimiertes Archiv:

tar -tvf meinArchiv.tar

b) Komprimiertes Archiv (gzip):

tar -tzvf meinArchiv.tar.gz

5️⃣ Archiv entpacken

a) Unkomprimiertes Archiv:

tar -xvf meinArchiv.tar

b) Mit gzip:

tar -xzvf meinArchiv.tar.gz

c) Mit bzip2:

tar -xjvf meinArchiv.tar.bz2

d) Mit xz:

tar -xJvf meinArchiv.tar.xz

6️⃣ Komplettes Beispiel

Archivieren und komprimieren:

tar -czvf projekt.tar.gz projekt/

Wiederherstellen / Entpacken:

tar -xzvf projekt.tar.gz

💡 Tipps

tar -czvf backup.tar.gz /etc /var/log /home/user1

🧪 Test-Modus: Was würde tar tun?

tar -cvf meinArchiv.tar --dry-run meinOrdner/

Simuliert die Archiv-Erstellung, ohne eine Datei zu schreiben (nur mit --dry-run in Kombination mit GNU tar)


📚 Weiterführende Hilfe

man tar

Oder online: https://man7.org/linux/man-pages/man1/tar.1.html

Linux - GPG

Linux - GPG

GPG - Yubikey "not found"

YubiKey wird von GPG nicht erkannt – Lösung mit pcscd Neustart

Wenn der YubiKey bereits eingesteckt ist, aber gpg oder gpg2 ihn nicht erkennt (z. B. mit dem Fehler gpg: selecting card failed: No such device), liegt das häufig an einem Problem mit dem Smartcard-Dienst pcscd.

Fehlermeldung

gpg: selecting card failed: No such device
gpg: OpenPGP card not available: No such device

Lösung: pcscd Dienst neu starten

  1. Öffne ein Terminal mit Root-Rechten oder verwende sudo.
  2. Führe den folgenden Befehl aus, um den Dienst neu zu starten:
sudo systemctl restart pcscd

Alternativ bei älteren Systemen:

sudo service pcscd restart
  1. Danach kannst du gpg2 --card-edit erneut ausführen:
gpg2 --card-edit

Nun sollte der YubiKey korrekt erkannt werden und Informationen wie Seriennummer, Hersteller und Kartentyp anzeigen.

Hinweis

sudo systemctl enable pcscd.socket

Dies stellt sicher, dass der Dienst automatisch gestartet wird, sobald ein Smartcard-Gerät verwendet wird.

Linux - GPG

GPG Keys exportieren

Linux - GPG

GPG - Sicheren Schlüssel erstellen und Ordner verschlüsseln

GPG maximal hart: Schlüssel erstellen & kompletten Ordner sicher verschlüsseln (mit Erklärungen)

Zielbild: Wir erzeugen einen modernen ECC-Schlüssel (Curve25519), härten GnuPGs Auswahl (AES-256, SHA-512), nutzen – wo möglich – AEAD/OCB für manipulationssichere Verschlüsselung und packen den Ordner zuerst in ein Tar-Archiv, um Dateinamen und Struktur zu verbergen. So bekommst du robuste Sicherheit mit wenig Angriffsfläche.

0) Vorbereiten & Umgebung schützen – warum überhaupt?

Eine aktuelle GnuPG-Version bringt neue Sicherheitsfeatures (z. B. AEAD/OCB). GPG_TTY sorgt für saubere Passphrase-Prompts. umask 077 und strikte Rechte verhindern, dass Konfigs/Backups aus Versehen für andere lesbar sind.

Nun musst du folgenden Befehl eingeben:
# Version prüfen (2.4+ ideal):
gpg --version

# Terminal-Integration:
export GPG_TTY=$(tty)

# Verzeichnis und Rechte:
umask 077
mkdir -p ~/.gnupg && chmod 700 ~/.gnupg

1) Sehr starken OpenPGP-Schlüssel anlegen

ECC/Curve25519 liefert heute eine hervorragende Kombination aus Sicherheit, Geschwindigkeit und Kompatibilität: du erhältst einen Ed25519-Signierschlüssel plus X25519-(cv25519)-Verschlüsselungs-Subkey. Das ist „state of the art“ für die meisten Anwendungsfälle.

Nun musst du folgenden Befehl eingeben:
gpg --expert --full-generate-key
# Im Dialog:
#   Typ: (9) ECC und ECC  →  Kurve: Curve25519
#   Ablaufdatum: z.B. 2y (verlängerbar)
#   Name/E-Mail setzen
#   sehr starke Passphrase vergeben
Wann RSA? Nur wenn du mit sehr alten Gegenstellen sprechen musst, die ECC nicht können; sonst bleib bei Curve25519.
1.1) Fingerprint & Notfall: Widerrufszertifikat

Der Fingerprint identifiziert deinen Schlüssel zweifelsfrei. Ein Widerrufszertifikat ist deine „Notbremse“, falls der private Schlüssel verloren/kompromittiert ist.

Nun musst du folgenden Befehl eingeben:
gpg --list-keys
gpg --fingerprint "Dein Name"
gpg --output revoke.asc --gen-revoke "Dein Name"

3) Einen kompletten Ordner verschlüsseln – Dateinamen & Struktur verbergen

Ein simples „alle Dateien einzeln verschlüsseln“ leakt Dateinamen und Ordnerstruktur. Besser: zuerst deterministisch zu tar bündeln (glättet Metadaten), dann gpg drüber. Ergebnis: .tar.gpg – sauber, dicht, auditierbar.

3.0) Pipeline: tar → gpg (maximale Härte)
Nun musst du folgenden Befehl eingeben:
# Variablen
ORDNER="mein_geheimer_ordner"
EMPFAENGER="Dein Name <you@example.com>"   # oder: 40-stelliger Fingerprint
OUT="mein_geheimer_ordner.tar.gpg"

# Deterministisch packen & sofort verschlüsseln (mit AEAD/OCB):
tar --sort=name \
    --mtime='UTC 1970-01-01' \
    --owner=0 --group=0 --numeric-owner \
    -C "$(dirname "$ORDNER")" -cf - "$(basename "$ORDNER")" \
| gpg --encrypt \
      --recipient "$EMPFAENGER" \
      --cipher-algo AES256 \
      --force-ocb \
      --output "$OUT"

# Kompatibilitätsmodus (ohne OCB):
# ... | gpg --encrypt --recipient "$EMPFAENGER" --cipher-algo AES256 -o "$OUT"
Warum diese tar-Optionen? --sort=name, --mtime='UTC 1970-01-01' und --owner/--group/--numeric-owner reduzieren Metadaten-Leaks und machen das Archiv reproduzierbar/auditierbar.
3.1) Alternative „aus einem Guss“: gpgtar

gpgtar kapselt Archivieren + Verschlüsseln in einem Tool – komfortabel und sicher, Dateinamen bleiben verborgen.

Nun musst du folgenden Befehl eingeben:
# Erstellen:
gpgtar --encrypt --recipient "$EMPFAENGER" --output geheim.gpg "$ORDNER"
# Entpacken:
gpgtar --decrypt --output entpackt geheim.gpg
3.2) Erfolg prüfen (ohne zu entschlüsseln)

Zur Kontrolle, welche Paket-/Algo-Infos tatsächlich drinstecken (nützlich für Audits):

gpg --list-packets "$OUT" | sed -n '1,40p'
3.3) Originaldaten entfernen – HDD vs. SSD

Auf klassischen HDDs kann shred sinnvoll sein. Auf SSDs/Copy-on-Write-FS (btrfs, zfs) sind Überschriften nicht zuverlässig – setze dort lieber auf Voll-Disk-Verschlüsselung (LUKS) und sichere Löschprozesse auf Geräteebene.

Nun musst du folgenden Befehl eingeben (HDD-Fall):
shred -vzun 3 -r -- "$ORDNER"   # ACHTUNG: für SSDs ungeeignet!
SSD-Hinweis: Nutze LUKS/Full-Disk-Encryption; für komplettes Gerät ggf. „Secure Erase“/blkdiscard je nach Hardware/Setup.

4) Entschlüsseln & auspacken – bewusst zweistufig

Erst entschlüsseln (fragt nach Passphrase/Token), dann das Tar entpacken – so bleibt der Ablauf transparent und nachvollziehbar.

Nun musst du folgenden Befehl eingeben:
gpg --output ordner.tar --decrypt mein_geheimer_ordner.tar.gpg
tar -xf ordner.tar
Wie GPG intern arbeitet: Hybrid-Krypto – ein zufälliger Session-Key (z. B. AES-256) verschlüsselt die Daten; dieser Session-Key wird für deinen Public Key eingepackt.

5) Schlüssel sichern & Vertrauen setzen – was braucht’s wirklich?

Teile deinen öffentlichen Schlüssel, sichere den privaten offline, und setze die Vertrauensstufe bewusst, damit dein System Signaturen korrekt bewertet.

Nun musst du folgenden Befehl eingeben:
# Public Key exportieren:
gpg --armor --export "$EMPFAENGER" > publickey.asc

# Private Keys sichern (offline aufbewahren!):
gpg --armor --export-secret-keys "$EMPFAENGER" > privatekey-backup.asc

# Vertrauen setzen (optional):
gpg --edit-key "$EMPFAENGER"
gpg> trust   # 5 = ultimate
gpg> save
Wichtig: privatekey-backup.asc nie in die Cloud/VC, sondern offline (z. B. USB im Tresor) + starke Passphrase.

6) Zusätzliche Härtung – wann lohnt sich der Extra-Aufwand?

7) Troubleshooting (kurz & praxisnah)
8) Schnellreferenz
# Schlüssel (Curve25519):
gpg --expert --full-generate-key

# Ordner → deterministisches Tar → GPG (mit OCB):
tar --sort=name --mtime='UTC 1970-01-01' --owner=0 --group=0 --numeric-owner \
  -C "$(dirname "$ORDNER")" -cf - "$(basename "$ORDNER")" \
| gpg --encrypt --recipient "$EMPFAENGER" --cipher-algo AES256 --force-ocb -o "$OUT"

# Entschlüsseln:
gpg -o ordner.tar -d "$OUT" && tar -xf ordner.tar

# gpgtar-Variante:
gpgtar --encrypt -r "$EMPFAENGER" -o geheim.gpg "$ORDNER"
gpgtar --decrypt -o entpackt geheim.gpg
Kompatibilitätsnotiz: Für alte Gegenstellen ohne --force-ocb arbeiten. Für lokale Backups und moderne GnuPG-Umgebungen OCB beibehalten.

Linux - Server erstellen

Linux - Server erstellen

Software installieren

Debian:

Hilfreiche Software installieren:

apt update \
  && \
  apt install -y \
  openssh-server \
  nano \
  curl \
  htop \
  btop \
  iputils-ping \
  iproute2 \
  iptables \
  ipset \
  ethstatus \
  vnstat \
  sshfs \
  wget \
  cifs-utils \
  python3 \
  python3-venv \
  python3-pip \
  tmux \
  tree \
  rsync \
  tcpdump \
  isc-dhcp-client \
  gpg \
  ntfs-3g \
  util-linux \
  parted \
  fuse \
  dosfstools \

tmux für crontab vorbereiten und auto update vorbereiten:

echo 'set-option -g default-shell /bin/bash' >> ~/.tmux.conf
mkdir manual
echo -e 'apt-get update\napt-get full-upgrade -y\n/sbin/reboot now' >> manual/update_and_reboot.sh
echo -e 'apt-get update\napt-get full-upgrade -y' >> manual/just_update.sh

mini Skript für neu installierte Debian Systeme:

# 1) /etc/apt/sources.list komplett ersetzen
cat > /etc/apt/sources.list <<'EOF'
deb http://deb.debian.org/debian/ trixie main contrib non-free-firmware non-free
deb http://security.debian.org/debian-security trixie-security main contrib non-free-firmware non-free
deb http://deb.debian.org/debian/ trixie-updates main contrib non-free-firmware non-free
deb http://deb.debian.org/debian trixie-backports main contrib non-free-firmware non-free
EOF

# 2) /etc/resolv.conf komplett ersetzen
cat > /etc/resolv.conf <<'EOF'
nameserver 1.1.1.1
EOF
chattr +i /etc/resolv.conf

apt-get update
apt-get install tmux -y

# 3) tmux Sessions tm0 bis tm12 erstellen (falls nicht vorhanden)
for i in $(seq 0 12); do
  if ! tmux has-session -t "tm${i}" 2>/dev/null; then
    tmux new-session -d -s "tm${i}"
  fi
done

tmux send-keys -t tm0 "cd /root/" C-m
tmux send-keys -t tm0 "apt update \
  && \
  apt install -y \
  openssh-server \
  nano \
  curl \
  htop \
  btop \
  iputils-ping \
  iproute2 \
  iptables \
  ipset \
  ethstatus \
  vnstat \
  sshfs \
  wget \
  cifs-utils \
  python3 \
  python3-venv \
  python3-pip \
  tmux \
  tree \
  rsync \
  tcpdump \
  isc-dhcp-client \
  gpg \
  ntfs-3g \
  util-linux \
  parted \
  fuse \
  dosfstools" C-m

Linux - Nützliche Software

Linux - Nützliche Software

Audio

Amberol

Amberol finde ich sehr cool, da er deine Playlist auch nach dem schließen noch kennt

https://apps.gnome.org/de/Amberol/

apt-get install amberol -y

image.png

VLC

Klassiker, is immer gut den zu haben

https://www.vlc.de

apt-get install vlc -y

image.png

 

 

 

 

Linux - Nützliche Software

Programmierung / Entwicklung

Atom

Sehr sehr alt, wurde damals von Microsoft's Visual Studio Code abgelöst und wird heute nicht mehr weiter geupdatet, aber ich liebe Atom, da mir Visual Studio Code viel zu überladen is und ich noch nicht super in VIM bin.

https://atom-editor.cc

image.png

 

Arduino IDE

Das is nur was für Leute die mit Mikrocontrollern arbeiten, ansonsten bringt es einen wenig.
Ich bevorzuge die alte Legacy IDE, welche auf der Download Seite etwas tiefer zu finden is, da man dort den seriellen Monitor noch in einem separaten Fenster hat.

https://www.arduino.cc/

https://docs.arduino.cc/software/ide-v1/tutorials/Linux/

image.png

 

 

 

 

 

Linux - Festplatten

Linux - Festplatten

NetApp-Shelf - 520-Byte Sektoren

Zielgruppe: Admins mit gemischten SAS-Shelfs (z. B. NetApp) an Linux-Servern.
Diese Anleitung erklärt warum 520-Byte-Sektoren existieren, wie man betroffene Platten identifiziert und welche Wege es gibt, sie nutzbar zu machen (oder bewusst auszublenden), ohne produktive Datenträger anzutasten.

Inhalt

  1. Warum haben manche NetApp-Platten 520 B/Sektor?
  2. Typische Symptome & Kernel-Logs
  3. Voraussetzungen & Pakete
  4. Betroffene Platten sicher finden (ohne Risiko für Daten)
  5. Entscheidung: Reformatieren, Übersetzen oder Ausblenden
  6. Reformatieren auf 512 B/4 Kn (falls erlaubt)
  7. Fortschritt & Status überwachen
  8. Nachbereitung: Rescan, Multipath, Partition/FS
  9. Wenn Reformatierung verweigert wird (Vendor-Lock)
  10. Optional: Multipath-Spam vermeiden (Blacklist)
  11. Troubleshooting-Tabelle
  12. Anhang: hilfreiche One-Liner/Skripte

1) Warum haben manche NetApp-Platten 520 Byte pro Sektor?

Merke: 4160 B physische Blockgröße in Logs = 8 × 520 B.

2) Typische Symptome & Kernel-Logs

3) Voraussetzungen & Pakete

# Debian/Ubuntu
apt-get update
apt-get install -y sg3-utils lsscsi multipath-tools smartmontools

# RHEL/CentOS/Rocky/Alma
yum install -y sg3_utils lsscsi device-mapper-multipath smartmontools
Wichtig: Formatieren (sg_format) zerstört alle Daten auf der betroffenen Platte. Diese Anleitung zeigt, wie du nur die neuen/unerwünschten 520 B-Platten anfasst und produktive Disks sicher ignorierst.

4) Betroffene Platten sicher finden (ohne Risiko)

4.1 Geräte & Pfade auflisten

lsscsi -g | grep -E "NETAPP|HGST|Seagate|TOSHIBA"    # Beispielhaft; Hersteller ggf. anpassen
# Ausgabe zeigt je Zeile: ...  /dev/sdX   /dev/sgY

4.2 Prüfen, ob 520 B gemeldet werden

Die sg*-Geräte (SCSI generic) für Detailabfragen verwenden:

sg_readcap -l /dev/sg108
# Wichtige Zeilen:
#   Logical block length=520 bytes
#   Logical blocks per physical block exponent=3 [phys = 4160 bytes]
#   Device size: ... 5.99 TB

4.3 Sicherstellen, dass es derselbe physische Datenträger ist (Multipath)

Bei mehreren Pfaden hat jedes einen eigenen /dev/sg*. Die LU name (LUN-WWN) ist identisch:

sg_inq -i /dev/sg108 | grep -E "Unit serial number|LU name"
sg_inq -i /dev/sg109 | grep -E "Unit serial number|LU name"
# Sind LU name und/oder Unit serial identisch → gleicher Datenträger über zwei Pfade.
Regel: Wenn du formatierst, dann immer nur genau einen Pfad (z. B. /dev/sg108) zum selben Laufwerk verwenden!

5) Entscheidung: Reformatieren, Übersetzen oder Ausblenden

Weg Wann? Pro Contra
Reformatieren auf 512 B/4096 B Nur bei neuen/leerem Laufwerk, wenn Firmware es erlaubt Standard-Linux nutzbar, keine Spezial-HW Vernichtet Daten; 2–4 h/6 TB (oft), manche NetApp-OEMs verbieten es
On-the-fly Übersetzung (HBA/RAID mit DIF-Translation) Wenn Reformat blockiert ist, aber Controller PI/DIF-Translation beherrscht OS sieht 512 B, Laufwerk bleibt 520 B Abhängig von Hardware-Modell/Firmware; nicht jeder HBA kann das
Ausblenden/Blacklisten Wenn Platte nicht benötigt/kompatibel ist Ruhe im syslog/multipath Platte bleibt ungenutzt

6) Reformatieren auf 512 B/4 Kn (falls erlaubt)

Gefahr: Vergewissere dich zu 100 %, dass du eine neue/leere Platte erwischt. Prüfe LU name und Unit serial number. Niemals produktive Disks anfassen.

6.1 Multipath kurz pausieren (verhindert Verwirrung)

systemctl stop multipathd
multipath -F   # alle Maps flushen

6.2 Probe-Format (Dry-Run)

Testet nur, ob die Firmware 512/4096 akzeptiert – es passiert noch nichts:

# 512-Byte testen
sg_format --format --size=512 --six --fmtpinfo=0 --dry-run /dev/sg108

# 4Kn (4096) als Alternative testen
sg_format --format --size=4096 --six --fmtpinfo=0 --dry-run /dev/sg108

# Typische Fehler bei Vendor-Lock:
#   "INVALID FIELD IN CDB" oder "Device not ready" → Reformat nicht erlaubt.

6.3 Schnellstmögliches Format anstoßen

Wenn der Dry-Run OK ist, starte das Format. Manche Firmwares unterstützen „fast“/„complete list“ – andere ignorieren es.

# Schnellvariante versuchen (falls Firmware unterstützt):
sg_format --format --size=512 --six --fmtpinfo=0 --cmplst=1 --early /dev/sg108

# Fällt Firmware auf Full-Format zurück, dauert es entsprechend (2–4h/6TB).
# Alternative "fast format":
sg_format --format --size=512 --six --fmtpinfo=0 --ffmt=1 --early /dev/sg108

Hinweis: --early kehrt sofort zurück, das Laufwerk formatiert im Hintergrund weiter. Auf Systemen ohne --early beende das Userspace-Tool notfalls mit Ctrl+C; der Vorgang im Laufwerk läuft weiter.

7) Fortschritt & Status überwachen

# Regelmäßig Sense-Daten abfragen (zeigt Fortschritt, wenn verfügbar)
sg_requests /dev/sg108
# Beispielausgabe:
#  Sense key: Not Ready
#  Additional sense: Logical unit not ready, format in progress
#    Progress indication: 37.50%

# Alternativ (je nach Build):
sg_turs /dev/sg108      # "Unit ready" wenn fertig; manche Builds können nicht anzeigen
# Live-Monitoring:
watch -n 60 sg_requests /dev/sg108
Abbrechen? Ein laufendes FORMAT UNIT lässt sich nicht sauber abbrechen. Stromlos/Slot ziehen ist riskant und endet oft damit, dass du erneut formatieren musst.

8) Nachbereitung: Rescan, Multipath, Partition & Filesystem

# Nach Abschluss erneut einlesen
rescan-scsi-bus.sh -a    # ggf. aus 'sg3-utils' Zusatzpaket
# prüfen:
sg_readcap -l /dev/sg108
# Erwartet: "Logical block length=512 bytes" (oder 4096)

# Blockgeräte-Ansicht
lsblk -o NAME,HCTL,WWN,MODEL,LOG-SEC,PHY-SEC,SIZE

# Multipath wieder starten
systemctl start multipathd
multipath -ll

# (Optional) Partition & Filesystem
# GPT anlegen und 1 große Partition:
parted -s /dev/sdX mklabel gpt mkpart primary 1MiB 100%

# z. B. XFS:
mkfs.xfs -f /dev/sdX1
# oder ext4:
mkfs.ext4 -F -L data01 /dev/sdX1

# Mountpunkt
mkdir -p /mnt/data01
echo '/dev/sdX1  /mnt/data01  xfs  defaults,noatime  0 2' >> /etc/fstab
mount -a

9) Wenn Reformatierung verweigert wird (Vendor-Lock)

10) Optional: Multipath-Spam vermeiden (Blacklist)

Wenn du 520 B-Platten nicht nutzen kannst/willst, blackliste sie (z. B. nach Vendor/Product):

# /etc/multipath/conf.d/blacklist.conf
blacklist {
    vendor  "NETAPP"
    product "X316_SMKRE06TA07"
}
systemctl reload multipathd

11) Troubleshooting-Tabelle

Symptom Ursache Maßnahme
Unsupported sector size 520 Linux erwartet 512n/4Kn Reformat (falls erlaubt) oder HBA-Übersetzung oder Blacklist
Preferred minimum I/O size 4096 ... not a multiple of 4160 Physischer Block = 4160 B (8×520) Wie oben; Platte ist 520 B-formatiert
multipathd: failed to setup map ... Gerät mit 520 B; kein nutzbares Blockdevice Nach Reformat OK; sonst Blacklist
sg_format ... Device not ready (MODE SENSE) Firmware blockiert Reformat / bereits Format läuft Mit sg_requests prüfen; ggf. später erneut versuchen
INVALID FIELD IN CDB Reformat auf 512/4096 verboten (Vendor-Lock) Nur NetApp/HBA-Translation/tauschen
Mehrere /dev/sg* für dieselbe Platte Mehrpfad (zwei Controller/Ports) Nur einen Pfad formatieren; Gleichheit via LU name prüfen

12) Anhang: hilfreiche One-Liner & Schnipsel

12.1 Alle 520 B-Kandidaten listen (mit sd-Mapping & LU-WWN)

#!/usr/bin/env bash
# list-520.sh — listet alle SCSI-Geräte mit 520-Byte-Sektoren
for sg in /dev/sg*; do
  cap=$(sg_readcap -l "$sg" 2>/dev/null | tr -d '\r')
  [[ -z "$cap" ]] && continue
  if echo "$cap" | grep -q "Logical block length=520 bytes"; then
    sd=$(lsscsi -g | awk -v s="$sg" '$NF==s{print $(NF-1)}')
    inq=$(sg_inq -i "$sg" 2>/dev/null)
    serial=$(echo "$inq" | awk -F': ' '/Unit serial number/{print $2}')
    lun=$(echo "$inq" | awk -F': ' '/LU name/{print $2}')
    size=$(echo "$cap" | awk -F': ' '/Device size/{print $2}')
    printf "%-8s %-8s %-20s %-20s %s\n" "$sg" "${sd:--}" "${serial:--}" "${lun:--}" "$size"
  fi
done

12.2 Nur einen Pfad pro physischer Disk wählen (per LU-Name deduplizieren)

#!/usr/bin/env bash
# pick-one-path.sh — gibt pro LU (LUN WWN) genau EIN /dev/sg* aus
declare -A seen
for sg in /dev/sg*; do
  lun=$(sg_inq -i "$sg" 2>/dev/null | awk -F': ' '/LU name/{print $2}')
  [[ -z "$lun" ]] && continue
  if [[ -z "${seen[$lun]}" ]]; then
    echo "$sg"
    seen[$lun]=1
  fi
done

12.3 Format sicher anstoßen (schnellstmöglich, wenn erlaubt)

# Beispiel: genau EINEN Pfad der Zielplatte verwenden!
systemctl stop multipathd
multipath -F

# Dry-Run (prüfen, ob 512 akzeptiert wird)
sg_format --format --size=512 --six --fmtpinfo=0 --dry-run /dev/sgXYZ

# Start (Firmware versucht "fast", fällt ggf. auf "full" zurück)
sg_format --format --size=512 --six --fmtpinfo=0 --cmplst=1 --early /dev/sgXYZ

# Fortschritt
watch -n 60 sg_requests /dev/sgXYZ

# Nach Abschluss
rescan-scsi-bus.sh -a
sg_readcap -l /dev/sgXYZ
systemctl start multipathd
multipath -ll

Kurzfazit: 520 B-Sektoren sind bei NetApp wegen T10 PI/DIF ein Feature für Datenintegrität. Unter generischem Linux sind solche Platten ohne Reformat/Übersetzung nicht nutzbar. Wenn die Firmware Reformat erlaubt: sicher auf 512 B/4 Kn umstellen (nur einen Pfad!), Fortschritt mit sg_requests prüfen, danach wie gewohnt einbinden. Wenn nicht: NetApp-Controller oder HBA mit DIF-Translation verwenden – ansonsten die Geräte sauber blacklisten.

NVIDIA

NVIDIA

nvidia-smi

„nvidia-smi: command not found“ – so fixt du das (Debian/Kali)

Diese Anleitung hilft dir, nvidia-smi wieder lauffähig zu machen und erklärt kurz, warum der Fehler passiert. Sie ist bewusst einfach gehalten – Schritt für Schritt, mit Kopierbefehlen.

TL;DR – Schnelllösung

  1. Stelle sicher, dass das NVIDIA/CUDA-Repository aktiv ist (für Treiber 580.x):
install -d /etc/apt/keyrings
curl -fsSL https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64/3bf863cc.pub \
  | gpg --dearmor -o /etc/apt/keyrings/cuda-archive-keyring.gpg

cat >/etc/apt/sources.list.d/cuda-debian12-x86_64.list <<'EOF'
deb [arch=amd64 signed-by=/etc/apt/keyrings/cuda-archive-keyring.gpg] https://developer.download.nvidia.com/compute/cuda/repos/debian12/x86_64/ /
EOF

apt-get update
  1. Reihen konsistent halten: gleiche Treiberlinie verwenden (hier 580.x). Prüfe die installierten Versionen:
apt-cache policy nvidia-driver nvidia-alternative libnvidia-ml1 | sed -n '1,200p'
  1. Fehlendes nvidia-alternative in 580 installieren (sofern nicht schon da):
apt-get install -y nvidia-alternative=580.82.07-1
  1. Falls das 580er Repo keine echte nvidia-smi-Binary liefert: nur die Binary aus 560er-Paket extrahieren (kein Downgrade!):
apt-get download nvidia-smi=560.35.05-1
dpkg-deb -x nvidia-smi_560.35.05-1_amd64.deb /tmp/nsmi
install -D -m 0755 /tmp/nsmi/usr/lib/nvidia/current/nvidia-smi /usr/local/bin/nvidia-smi
ln -sf /usr/local/bin/nvidia-smi /usr/bin/nvidia-smi
hash -r
nvidia-smi -h
nvidia-smi -q | head -n 20
Hinweis: Dieser Weg belässt dein System auf Treiberlinie 580, nutzt aber die 560er nvidia-smi-Binary. In der Praxis ist das mit libnvidia-ml1 580 kompatibel und genügt für Monitoring.

Warum passiert der Fehler?

Sauberer Weg (empfohlen): konsistente Repo-Linie

Entscheide dich für eine Linie: entweder komplett 580.x (NVIDIA/CUDA) oder komplett 550.x (Kali). Mischen führt zu Konflikten.

Variante A – 580.x (NVIDIA/CUDA)

  1. CUDA-Repo eintragen (siehe TL;DR) und doppelte Zeilen entfernen:
awk '!seen[$0]++' /etc/apt/sources.list.d/cuda-debian12-x86_64.list \
  > /etc/apt/sources.list.d/cuda-debian12_x86_64.fixed && \
mv /etc/apt/sources.list.d/cuda-debian12_x86_64.fixed /etc/apt/sources.list.d/cuda-debian12-x86_64.list
apt-get update
  1. Sicherstellen, dass alle Kernpakete 580 sind:
apt-cache policy nvidia-driver nvidia-alternative libnvidia-ml1
  1. nvidia-alternative ggf. nachziehen:
apt-get install -y nvidia-alternative=580.82.07-1

Wenn keine 580er nvidia-smi-Binary verfügbar ist: siehe Abschnitt „Binary pragmatisch bereitstellen“.

Variante B – 550.x (Kali)

Nur falls du auf Kali-Stand bleiben willst.

  1. CUDA-Repo deaktivieren (um Versionen nicht zu mischen).
  2. Kali-Pakete sauber auf 550 ausrichten:
apt-get update
apt-get install -y \
  nvidia-driver=550.163.01-3 \
  nvidia-driver-bin=550.163.01-3 \
  nvidia-alternative=550.163.01-3 \
  libnvidia-ml1=550.163.01-3 \
  --allow-downgrades

Danach ist /usr/bin/nvidia-smi normalerweise direkt vorhanden.

Binary pragmatisch bereitstellen (ohne System-Downgrade)

Wenn die 580er Repo-Linie keine nvidia-smi-Binary liefert, extrahieren wir nur die Binary aus dem 560er Paket:

apt-get download nvidia-smi=560.35.05-1
dpkg-deb -x nvidia-smi_560.35.05-1_amd64.deb /tmp/nsmi
install -D -m 0755 /tmp/nsmi/usr/lib/nvidia/current/nvidia-smi /usr/local/bin/nvidia-smi
ln -sf /usr/local/bin/nvidia-smi /usr/bin/nvidia-smi
hash -r
nvidia-smi -q | head -n 20
Aufräumen (optional):
rm -f /usr/bin/nvidia-smi /usr/local/bin/nvidia-smi
rm -rf /tmp/nsmi

Prüfen, ob der Treiber wirklich läuft

nvidia-smi startet auch ohne aktive GPU, zeigt dann aber z. B. „No devices were found“. Treiberstatus prüfen:

lsmod | grep -i nvidia || modprobe nvidia || true
dmesg | grep -i nvidia | tail -n 60
uname -r

Wenn das Kernelmodul fehlt oder nicht baut:

apt-get install -y dkms linux-headers-$(uname -r)
reboot

Alternative: Im Container testen

Mit nvidia-docker2/nvidia-container-toolkit kannst du nvidia-smi im Container ausführen (ohne Host-Binary):

docker run --rm --gpus all nvidia/cuda:12.4.1-base-ubuntu24.04 nvidia-smi

Fehlersuche (Kurzcheckliste)

FAQ

Ist die 560er Binary mit 580 kompatibel?
Für die reine Abfrage über NVML (nvidia-smi) funktioniert das üblicherweise problemlos. Wir installieren keine 560er Systempakete, sondern nutzen nur die einzelne Binary.

Warum nicht einfach „apt install nvidia-smi“?
Weil dieses Paket in einigen Repositories nur ein Platzhalter/Dummy ist (liefert keine Binary). Die echte nvidia-smi kann in anderen Paketen stecken.

Wie mache ich das rückgängig?
Symlinks/Binary entfernen:

rm -f /usr/bin/nvidia-smi /usr/local/bin/nvidia-smi

Repository-Zeilen ggf. wieder anpassen und apt-get update.

 


Stand: Beispielbefehle für Debian 12/Kali, Treiberlinie 580.x. Versionsnummern bitte bei Bedarf mit apt-cache policy prüfen.

TMUX

TMUX

tmux - crontab

Default‑Shell schnell auf /bin/bash festlegen

Oft bleibt eine neue tmux‑Session schwarz, wenn keine gültige Standard‑Shell definiert ist. Mit der folgenden Ein‑Zeiler‑ oder Schritt‑für‑Schritt‑Anleitung schreibst du den notwendigen Eintrag in ~/.tmux.conf.

Ein‑Zeiler (sofort ausführen)

echo 'set-option -g default-shell /bin/bash' >> ~/.tmux.conf
echo 'set-option -g default-shell /bin/bash' >> ~/.tmux.conf
mkdir manual
echo -e 'apt-get update\napt-get full-upgrade -y\n/sbin/reboot now' >> manual/update_and_reboot.sh
echo -e 'apt-get update\napt-get full-upgrade -y' >> manual/just_update.sh

Hinweis: /bin/bash ist auf fast allen Linux‑Systemen vorhanden. Wer plattformunabhängig arbeiten möchte, setzt lieber /usr/bin/env bash.

Schritt‑für‑Schritt‑Variante

  1. Datei öffnen:
    nano ~/.tmux.conf
  2. Zeile hinzufügen – ganz egal, wo:
    set-option -g default-shell /bin/bash
  3. Speichern & schließen:
    Ctrl+O, Enter, Ctrl+X
  4. tmux‑Konfiguration neu laden (ohne Neustart):
    tmux source-file ~/.tmux.conf

tmux hängt im toten Netzlaufwerk – Notfall‑Cheatsheet

Wenn dein tmux‑Fenster in einem Pfad steht, der auf einem abgestürzten NFS‑/CIFS‑Mount liegt, wirkt das Terminal wie eingefroren: jeder I/O‑Aufruf wartet ewig auf eine Antwort, die nie kommt. Diese Anleitung zeigt dir, wie du die blockierte Shell verlässt, die Pane reparierst – oder den kaputten Mount brutal löst – ohne deine ganze tmux‑Sitzung zu verlieren.


1 · Schnelle Hilfe direkt in tmux

Aktion Tastenfolge (Prefix = Ctrl+b) Ergebnis
Neues Window Ctrl+b c Frische Login‑Shell in $HOME oder /.
Neue Pane (horizontal) Ctrl+b " Zweite Shell, unabhängig vom blockierten Verzeichnis.
Hängende Pane killen Ctrl+b x → y Nur die kaputte Pane wird beendet.
Pane neu starten Ctrl+b : → respawn-pane -k Kills + sofortiger Neustart der Shell darin.
Ganzes Window killen Ctrl+b & → y Alle Panes dieses Fensters schließen.

Tipp: Meist reicht es, Ctrl+b c zu drücken, sich in der neuen Shell per pwd den aktuellen Pfad anzeigen zu lassen und anschließend den defekten Mount zu lösen (→ Abschnitt 3).


2 · Fest­hängende Prozesse gezielt töten

# 1 · TTY der Pane herausfinden
who         # Spalte "PTS" merken (z. B. pts/4)

# 2 · laufende Prozesse auf diesem TTY zeigen
ps -o pid,stat,cmd -t pts/4

# 3 · sanft beenden (SIGTERM), notfalls hart (SIGKILL)
kill <PID>          # SIGTERM
kill -9 <PID>       # SIGKILL

Sobald der blockierende Prozess endet, reagiert die Shell sofort wieder.


3 · Abgestürzten Netz‑Mount „faul“ abhängen

sudo umount -l -f /pfad/zum/netzlaufwerk

Danach funktionieren alle Befehle wieder, die nicht nochmals auf dieses Verzeichnis zugreifen.


4 · Letzter Ausweg – komplette Session schließen

# innerhalb von tmux
Ctrl-b d             # detach
# auf der Login-Shell
tmux kill-session -t <sessionname>

Das beendet alles in einem Rutsch. Nützlich, wenn mehrere Windows abhängig sind oder sich die Lage anders nicht bereinigen lässt.


5 · Dauerhafte Prävention


6 · „Neue tmux‑Session bleibt leer“ – Konfiguration fixen

Manche Distributionen starten tmux ohne explizite Login‑Shell. Ist die voreingestellte Shell nicht ausführbar (z. B. durch einen defekten Symlink oder ein fehlendes Home‑Verzeichnis), erscheint nur ein schwarzer Bildschirm. Setze deshalb eine valide Standard‑Shell in deiner ~/.tmux.conf:

nano ~/.tmux.conf
# ------------------------------
# Saubere Login‑Shell erzwingen:
set-option -g default-shell /usr/bin/env bash
# Optional: gleich als Login‑Shell starten
set-option -g default-command "bash -l"
# ------------------------------

Verbesserung gegenüber deinem ursprünglichen Trick: /usr/bin/env bash findet bash auch dann, wenn sie nicht unter /bin liegt, und funktioniert daher auf nahezu allen Linux‑Varianten (inkl. macOS / BSD). Mit default-command "bash -l" bekommst du zusätzlich ein vollwertiges Login‑Environment (u. a. /etc/profile wird geladen).


7 · Kurzanleitung für die Hosentasche

# Pane hängt im toten Mount?
Ctrl-b c                # neues Window
sudo umount -l -f …     # Mount loswerden
Ctrl-b x                # alte Pane killen

Fertig! Deine tmux‑Sitzung lebt weiter, und du sparst dir den Logout.

Datenspeicher

Datenspeicher

Festplatte vergrößern / resize

Linux: Root-Partition mit Bordmitteln vergrößern (fdisk + resize2fs)

Ziel: Die bestehende Root-Partition /dev/sda2 wird ohne Zusatzpakete vergrößert. Wir ändern nur die Partitionstabelle (nicht das Dateisystem selbst) und lassen anschließend das ext4-Dateisystem online wachsen.

Kernprinzip für Sicherheit: Beim Neu-Anlegen der vergrößerten Partition muss der Startsektor von /dev/sda2 exakt gleich bleiben. Dadurch zeigen die Partitions-Metadaten weiterhin auf die gleiche Datenposition – das Dateisystem bleibt unbeschädigt. Wir schreiben erst am Ende mit w; bis dahin sind alle Änderungen nur im Speicher.


1) Voraussetzungen & Vorab-Checks

# Dateisystemtyp von / ermitteln (wichtig für den Grow-Befehl)
findmnt -no FSTYPE /

# Partitionslayout anzeigen und Startsektor von sda2 notieren
fdisk -l /dev/sda

Hinweis: Wenn sich freier Platz rechts (am Ende) von /dev/sda2 befindet, kann sie erweitert werden. In einem MBR-Layout mit einer kleinen erweiterten Partition vor sda2 (wie im Beispiel unten) blockiert diese das Wachstum nicht.


2) Umsetzung nur mit Bordmitteln (fdisk → resize2fs)

2.1 Swap (wenn nötig) temporär deaktivieren

Ist Swap rechts hinter /dev/sda2, muss er aus dem Weg. Im gezeigten Layout liegt /dev/sda5 vor /dev/sda2, daher war keine Aktion nötig. Allgemein:

swapoff -a    # Swap überall deaktivieren (nur temporär)

Warum: Aktiver Swap kann das Ändern/Löschen seiner Partition verhindern.

2.2 Partitionstabelle mit fdisk anpassen

fdisk /dev/sda

Kommandos in fdisk (Beispiel-Sitzung passend zu deinem Fall):

p               # p = print: aktuelle Tabelle zur Kontrolle anzeigen
d               # d = delete: die alte sda2 löschen (nur Tabelleneintrag!)
  2             # Partitionsnummer 2
n               # n = new: neue Partition anlegen
  p             # p = primary
  2             # Partitionsnummer 2 wiederverwenden
  <Startsektor> # exakt den zuvor notierten Start von sda2 eingeben (z.B. 1953792)
  <Ende>        # Enter für "bis zum Ende" (oder +<Größe> lassen, falls man Platz für Swap-Partition reservieren will)
p               # zur Kontrolle (Start muss identisch, Größe nun größer)
w               # w = write: Änderungen auf die Disk schreiben
Optional: Boot-Flag (BIOS/MBR)

In manchen Setups war die Root-Partition bootable (Sternchen in der Spalte Boot). Falls nötig, kann man den Boot-Flag wieder setzen:

a   # a = toggle bootable flag
2   # für Partition 2
w   # schreiben

Viele GRUB-Installationen booten auch ohne gesetztes Flag, aber wenn es zuvor gesetzt war, empfiehlt es sich, es wieder zu setzen.

2.3 Reboot (nur falls erforderlich)

Bei Root-Disks kann der Kernel die neue Geometrie nicht immer „live“ einlesen. In deinem Fall hat das Online-Resizing direkt funktioniert. Allgemein ist ein Neustart empfehlenswert:

reboot

3) Dateisystem online vergrößern

Für ext4 geht das im laufenden Betrieb:

# optionaler Konsistenzcheck im Online-Modus
e2fsck -f /dev/sda2

# ext4 bis zum Partitionsende vergrößern
resize2fs /dev/sda2

Was macht das? resize2fs liest die neue (größere) Partitionslänge aus und erweitert die internen Strukturen des ext4-Dateisystems, sodass der zusätzliche Platz nutzbar wird.

Ergebnis prüfen:

df -h /
lsblk

4) Deine konkrete Sitzung (vorher → nachher)

# Vorher (auszug)
Disklabel type: dos
Device     Boot   Start       End   Sectors   Size Id Type
/dev/sda1          2046   1953791   1951746   953M  5 Extended
/dev/sda2  *    1953792 314570751 312616960 149.1G 83 Linux
/dev/sda5          2048   1953791   1951744   953M 82 Linux swap / Solaris

# Aktion in fdisk:
d (2) → n (p, 2, Start=1953792, Ende=629145599) → w

# Nachher
Device     Boot   Start       End   Sectors   Size Id Type
/dev/sda1          2046   1953791   1951746   953M  5 Extended
/dev/sda2       1953792 629145599 627191808 299.1G 83 Linux
/dev/sda5          2048   1953791   1951744   953M 82 Linux swap / Solaris

# Dateisystem vergrößert:
resize2fs /dev/sda2
→ The filesystem on /dev/sda2 is now 78398976 (4k) blocks long.

# Platzgewinn sichtbar:
df -h /
→ /dev/sda2  294G  used 97G  free 184G  (Beispielwerte)

Besonderheit deines Layouts: Die kleine erweiterte Partition (/dev/sda1 mit /dev/sda5 als Swap) liegt am Anfang der Platte – vor /dev/sda2. Da wir /dev/sda2 nach rechts bis ans Plattenende vergrößert haben, störte der Swap nicht und musste nicht verschoben/gelöscht werden.


5) Warum das Verfahren sicher ist (wenn man es korrekt macht)

Wenn es schief geht? Typische Fehler entstehen nur, wenn der Startsektor falsch eingegeben wird (→ dann zeigt der Eintrag nicht mehr auf das Dateisystem). Deshalb: Vor dem w mit p gegentesten und den Startwert doppelt prüfen.


6) Typische Stolpersteine & Checks


7) Alternative: Swap als Datei (empfohlen)

Wer künftig flexibel bleiben will, legt Swap als Datei an und spart sich MBR/erweiterte-Partition-Themen:

# 2 GiB Swapfile (Größe anpassen)
fallocate -l 2G /swapfile || dd if=/dev/zero of=/swapfile bs=1M count=2048
chmod 600 /swapfile
mkswap /swapfile
swapon /swapfile
printf "\n/swapfile none swap sw 0 0\n" >> /etc/fstab

8) Kurzzusammenfassung (Cheat Sheet)

# 1) Startsektor notieren
fdisk -l /dev/sda

# 2) fdisk: sda2 löschen > neu anlegen (gleicher Start, größeres Ende)
fdisk /dev/sda
# p, d (2), n (p, 2), Start=<alter Start>, Ende=<Enter für Ende>, p, w

# 3) (ggf.) reboot

# 4) Dateisystem vergrößern (ext4)
resize2fs /dev/sda2

# 5) prüfen
df -h /
lsblk

Damit ist die Root-Partition vergrößert, ohne zusätzliche Tools zu installieren, minimal-invasiv und sicher – sofern der Startsektor gleich bleibt und die Kontrolle mit p vor dem w sorgfältig erfolgt.

Linux - SoCat

Linux - SoCat

SoCat - Fast start

1. Was ist socat?

Socat (SOcket CAT) ist ein sehr flexibles Kommandozeilen-Tool zum Weiterleiten von Datenströmen zwischen Netzwerk-Ports, Dateien, Pipes, UNIX Sockets oder SSL/TLS-Verbindungen.

Tipp: Für produktiven Dauerbetrieb sind Reverse-Proxys, VPNs oder Firewalls die bessere Wahl – socat ist ideal als temporärer Workaround!

2. Installation unter Debian/Linux

apt-get update
apt-get install socat

3. Einfaches Port-Forwarding: 192.168.0.1 → localhost

Beispiel: Du möchtest einen Dienst auf 192.168.0.1:8080 lokal als localhost:8000 bereitstellen.

socat TCP-LISTEN:8000,bind=localhost,reuseaddr,fork TCP:192.168.0.1:8080

4. Mehrere Ports/Services von 192.168.0.1 zu localhost weiterleiten

socat TCP-LISTEN:8001,bind=localhost,reuseaddr,fork TCP:192.168.0.1:8081

5. Optionen erklärt

6. Verbindung nach Abbruch automatisch wiederherstellen (persist)

socat -d -d TCP:192.168.0.1:8080,persist STDOUT

7. SSL/TLS: Verbindung von altem 192.168.0.1-SSL auf lokalen Dienst

Beispiel: Dienst auf 192.168.0.1:8443 nutzt nur TLS 1.0, du möchtest lokal localhost:8000 bereitstellen.

openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -nodes -subj "/CN=localhost"
socat openssl-connect:192.168.0.1:8443,method=TLS1 TCP-LISTEN:8000,bind=localhost,reuseaddr,fork

8. Umgekehrter Weg: Lokaler Dienst auf localhost nach 192.168.0.1 im anderen Netz bereitstellen

socat TCP-LISTEN:8080,bind=192.168.0.1,reuseaddr,fork TCP:localhost:8000

9. Hinweise & Tipps

10. Ressourcen

Sudo deinstallieren

1) Ziel & Hintergrund

Auf Systemen wie Kali/Debian existiert immer ein Root-Account. sudo ist ein Komfort- und Kontrollwerkzeug, um Root-Rechte selektiv an normale Benutzer zu delegieren. Diese Anleitung stellt um auf das klassische Modell: kein sudo, Adminzugriff nur über su - mit Root-Passwort.

2) Auswirkungen & Trade-offs

Aspekt Mit sudo Mit su - (ohne sudo)
Berechtigungen Feingranular delegierbar Alles oder nichts über Root-Passwort
Nachvollziehbarkeit Pro-User-Logging möglich Weniger granular; Fokus auf Auth-Logs
Komfort Befehlsweise Elevation Bewusster Wechsel in Root-Shell
Angriffsfläche Zusätzliche Binary & Policy Weniger Komponenten, ein Geheimnis (Root-PW)
Merke: Wer bereits Root-Rechte hat, kann immer das Root-Passwort ändern – unabhängig davon, ob sudo installiert ist. Sicherheit entsteht hier primär durch Zugriffskontrolle, Härtung und Auditing.

3) Vorbereitung (Root & Backup)

  1. Sicherstellen, dass Root aktiviert ist und ein Passwort hat:
    # falls aktuell noch sudo vorhanden ist:
    sudo passwd root
    # ab dann:
    su -
            
  2. Backups der sudo-Konfiguration:
    mkdir -p /root/backup-sudo-files
    cp -a /etc/sudoers /root/backup-sudo-files/ 2>/dev/null || true
    cp -a /etc/sudoers.d /root/backup-sudo-files/ 2>/dev/null || true
            

4) sudo entfernen (Paket oder manuell)

4.1 Paketzuordnung prüfen

which sudo
ls -l /usr/bin/sudo
dpkg -S /usr/bin/sudo 2>/dev/null || echo "Kein dpkg-Eintrag (manuell installiert)"

4.2 Falls als Paket installiert

apt update
apt purge -y sudo sudo:arm64 || true
apt autoremove -y

4.3 Falls nicht paketverwaltet (manuell installiert)

Vorsicht: Nur löschen, wenn sichergestellt ist, dass sudo nicht von dpkg verwaltet wird.
cp -a /usr/bin/sudo /root/backup-sudo      # Sicherung
rm -v /usr/bin/sudo                         # Entfernen
# optional: visudo entfernen, falls vorhanden
[ -e /usr/bin/visudo ] && rm -v /usr/bin/visudo

5) Bereinigen von Gruppen & Resten

# Benutzer aus sudo-Gruppe entfernen (Beispiel: jj)
deluser jj sudo

# sudo-Gruppe ggf. ganz entfernen
getent group sudo && delgroup sudo || true
Optional: su auf bestimmte Gruppe beschränken (pam_wheel)

Damit nur Mitglieder einer bestimmten Gruppe (z. B. wheel) su benutzen dürfen:

groupadd wheel 2>/dev/null || true
gpasswd -a deinadmin wheel
# PAM anpassen (Zeile aktivieren):
sed -i 's|^#\s*auth\s\+required\s\+pam_wheel.so\s\+use_uid|auth required pam_wheel.so use_uid|' /etc/pam.d/su

Damit ist nicht jeder lokale User zu su berechtigt.

6) Verifikation & Funktionstests

# Existenz prüfen
command -v sudo || echo "sudo nicht gefunden (erwartet)"

# als normaler Benutzer: sudo sollte scheitern
sudo id || echo "OK: sudo nicht verfügbar"

# Rootwechsel via su -
su - -c 'id && whoami'
Tipp: Prüfe die Logs: /var/log/auth.log (oder Journal) sollte su-Events zeigen.

7) Warum das der Sicherheit hilft

  • Reduzierte Angriffsfläche: Entfernt eine zusätzliche, setuid-fähige Binary (/usr/bin/sudo) und deren Policy/Parser.
  • Einheitliches Geheimnis: Nur das Root-Passwort erlaubt Privilege Escalation. Kein Nebeneingang über falsch konfigurierte sudoers-Regeln.
  • Bewusster Admin-Kontext: Klare Trennung: normale Arbeit als User, gezielter Wechsel in eine Root-Login-Shell.
Trade-off: Du verlierst feingranulare Delegation und pro-User-Audit auf Befehlsebene. Das kompensierst du mit Auth- und su-Logging und ggf. pam_wheel (siehe nächster Abschnitt).

8) Zusätzliche Härtung (Best Practices)

8.1 SSH

# /etc/ssh/sshd_config anpassen:
PermitRootLogin prohibit-password   # Root nur per Key; oder 'no' wenn du Root-SSH ganz verbietest
PasswordAuthentication no           # globale Passwort-Logins aus (nur Keys)
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes
# danach:
systemctl restart ssh
Wenn du Root-SSH doch per Passwort brauchst

Dann setze PermitRootLogin yes und erzwinge starke Passwörter + Fail2Ban. Besser ist dennoch: SSH-Keys + ggf. 2FA (PAM/Google Authenticator).

8.2 Passwortrichtlinien

# Beispiel: libpam-pwquality nutzen
apt install -y libpam-pwquality
# /etc/security/pwquality.conf anpassen (Minimallänge, Klassen, etc.)

8.3 Auditing & Log-Monitoring

apt install -y auditd
auditctl -l | cat
# su/Passwort-Events erscheinen in /var/log/auth.log bzw. journalctl -u ssh -u systemd-logind

8.4 Brute-Force-Schutz

apt install -y fail2ban
# jail.local anpassen, insbesondere für sshd; dann:
systemctl enable --now fail2ban

8.5 su einschränken (PAM Wheel)

Siehe Abschnitt 5: Nur Mitglieder von wheel dürfen su verwenden.

9) Rollback (sudo wiederherstellen)

  1. Neuinstallation:
    apt update
    apt install -y sudo
  2. Benutzer berechtigen:
    usermod -aG sudo 
    # neue Gruppenmitgliedschaft gilt nach neuem Login
  3. Konfiguration prüfen:
    visudo   # Syntaxcheck & Policy anpassen

10) Anhang: Ein-Befehl-Variante (vorsichtig!)

Führt Backup, Entfernung (Paket oder manuell) und Gruppenbereinigung in einem Rutsch aus. Vorher lesen!

bash -c '
set -euo pipefail
echo ">>> Backup"; mkdir -p /root/backup-sudo-files;
cp -a /etc/sudoers /root/backup-sudo-files/ 2>/dev/null || true;
cp -a /etc/sudoers.d /root/backup-sudo-files/ 2>/dev/null || true;
SUDO_BIN="$(command -v sudo || true)";
if [ -n "$SUDO_BIN" ]; then
  echo ">>> Gefunden: $SUDO_BIN";
  dpkg -S "$SUDO_BIN" &>/dev/null && {
    echo ">>> Paketverwaltet: apt purge";
    apt update;
    apt purge -y sudo sudo:arm64 || true;
    apt autoremove -y;
  } || {
    echo ">>> Nicht paketverwaltet: manuell entfernen";
    cp -a "$SUDO_BIN" /root/backup-sudo-files/;
    rm -v "$SUDO_BIN";
    [ -e /usr/bin/visudo ] && rm -v /usr/bin/visudo || true;
  };
else
  echo ">>> sudo nicht im PATH";
fi
# Beispielbenutzer anpassen:
if getent group sudo &>/dev/null; then
  deluser jj sudo || true;
  delgroup sudo || true;
fi
echo ">>> Fertig."
'
Achtung: Passe den Benutzernamen jj im Script an deine Umgebung an.

Linux - MQTT

Linux - MQTT

MQTT - Retained Message

Diese Anleitung erklärt kurz, was retained messages sind, wie du eine retained Nachricht gezielt löschen kannst, und was es mit der LWT (Last Will and Testament) auf sich hat.


Was ist eine retained message?

Eine retained message ist die zuletzt veröffentlichte Nachricht einer Topic, die der Broker speichert und sofort an neue Subscriber ausliefert, sobald sie das Topic abonnieren. Das ist praktisch für Statuswerte (z. B. „online“), kann aber zu unerwünschtem Verhalten führen, wenn versehentlich ein Steuerbefehl mit retain veröffentlicht wurde.

Symptom: Du abonnierst ein Topic und ein alter Befehl wird sofort wieder angewendet – obwohl niemand gerade etwas sendet. Das ist fast immer eine retained message.


Retained message löschen

Löschen geht, indem du auf genau dieselbe Topic eine leere Payload mit gesetztem Retain-Flag veröffentlichst.

Beispiel (Mosquitto-Tools)

# -r = retained publish, -n = empty payload
mosquitto_pub -h <broker> -t 'dein/topic' -r -n

# mit Benutzer/Passwort:
mosquitto_pub -h <broker> -u <user> -P <pass> -t 'dein/topic' -r -n

# mit TLS (falls benötigt), Zertifikat prüfen:
mosquitto_pub -h <broker> -p 8883 --capath /etc/ssl/certs -t 'dein/topic' -r -n

Retained Nachrichten prüfen / sichtbar machen

Beim Abonnieren sendet der Broker retained Nachrichten sofort. So siehst du, ob noch eine da ist:

# zeigt auch retained beim Start
mosquitto_sub -v -h <broker> -t 'dein/topic'

# retained unterdrücken (nur Live-Nachrichten)
mosquitto_sub -v -h <broker> -t 'dein/topic' -R

Vermeiden, dass es wieder passiert


Unterscheidung: Retained vs. LWT (Last Will and Testament)

Die LWT ist eine Nachricht, die der Broker automatisch sendet, wenn ein Client unerwartet die Verbindung verliert. Sie kann optional ebenfalls retained sein.


Häufige Fehler


Kurze Checkliste

  1. mosquitto_sub starten und prüfen, ob beim Abo sofort eine Nachricht kommt.
  2. Mit mosquitto_pub -r -n auf die exakte Topic leere Payload senden.
  3. Nochmals abonnieren und verifizieren, dass nichts retained kommt.
  4. In Sender-Clients Retain-Flag für Steuer-Themen deaktivieren.

Beispiele mit Platzhaltern

# Platzhalter ersetzen:
# <broker> = Hostname/IP des Brokers
# <user> / <pass> = Zugangsdaten (falls gesetzt)
# 'dein/topic' = exakte Topic der alten retained Nachricht

mosquitto_pub -h mqtt.example.lan -u haus -P geheim -t 'haus/wohnzimmer/licht/set' -r -n
mosquitto_sub -v -h mqtt.example.lan -t 'haus/wohnzimmer/licht/set'

Sicherheit: Nutze nach Möglichkeit TLS am Broker (Port 8883) und Accounts mit eng zugeschnittenen Berechtigungen (ACLs), damit nicht versehentlich auf falsche Topics geschrieben wird.

Lenovo - Thinkpad

Lenovo - Thinkpad

Fn+F8 (WLAN-rfkill) deaktivieren – Lenovo ThinkPad

Ziel: Das WLAN darf durch die Fn+F8-Taste nicht mehr per rfkill blockiert werden.

Umgebung

Lösung (Hotkey-Interface abschalten)

Deaktiviert das Hotkey-Input-Interface von thinkpad_acpi, sodass Fn+F8 kein rfkill mehr auslöst.

sudo tee /etc/modprobe.d/thinkpad-hotkey-off.conf >/dev/null <<'EOF'
options thinkpad_acpi hotkey=0
EOF
sudo modprobe -r thinkpad_acpi
sudo modprobe thinkpad_acpi

Optional: systemd-rfkill neutralisieren

Verhindert, dass beim Booten alte rfkill-Zustände wiederhergestellt werden.

sudo systemctl stop systemd-rfkill.service systemd-rfkill.socket
sudo systemctl mask systemd-rfkill.service systemd-rfkill.socket
sudo rm -f /var/lib/systemd/rfkill/*

Verifikation

  1. Alle Soft-Blocks aufheben:
rfkill unblock all
rfkill list
  1. Fn+F8 drücken und erneut prüfen:
rfkill list
Erwartung: Vor und nach Fn+F8 bleibt WLAN unblocked (kein Soft-Block).

Rollback

Hotkey-Interface wieder aktivieren:

sudo rm -f /etc/modprobe.d/thinkpad-hotkey-off.conf
sudo modprobe -r thinkpad_acpi
sudo modprobe thinkpad_acpi

Optional: systemd-rfkill wieder aktivieren:

sudo systemctl unmask systemd-rfkill.service systemd-rfkill.socket
sudo systemctl start systemd-rfkill.service systemd-rfkill.socket

Hinweise

Linux - Tmux

Linux - Tmux

tmux autostart (boot + Display)

tmux Orchestrator (boot + Display) – Installation auf neuem Server

Diese Anleitung richtet pro Benutzer einen systemd-Userdienst ein, der:

Der Orchestrator startet Kommandos in der bestehenden zsh, sodass Ctrl+C nur das Programm beendet. Ein cd … im Command wirkt persistent.


0) Voraussetzungen

sudo apt update
sudo apt install -y tmux zsh xauth x11-xserver-utils
# (optional) setxkbmap ist in x11-xserver-utils enthalten

Falls der Benutzer nicht jj heißt, ersetze ihn in den folgenden Befehlen entsprechend. Die Inhalte der Dateien bleiben gleich.


1) Benutzer jj einrichten

1.1 Linger aktivieren (User-Manager auch ohne Login)

sudo loginctl enable-linger jj

1.2 Verzeichnisse anlegen

sudo -u jj mkdir -p /home/jj/.config/systemd/user /home/jj/.config/tmux

1.3 Service-Datei (User-Unit)

/home/jj/.config/systemd/user/tmux-autostart.service

[Unit]
Description=Start/refresh tmux for jj (boot + wait for display, fixed socket)

[Service]
Type=simple
# Delay, bis /run/user/<uid> steht
ExecStartPre=/bin/sleep 10
# Sockets mit sicheren Rechten vorbereiten
ExecStartPre=/usr/bin/install -d -m 0700 -o %u -g %u %t
ExecStartPre=/usr/bin/install -d -m 0700 -o %u -g %u %t/tmux-%u
# alten Default-Socket ggf. entfernen (sauberer Start)
ExecStartPre=/usr/bin/env bash -c 'test -S "%t/tmux-%u/default" && rm -f "%t/tmux-%u/default" || true'

Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Environment=TMUX_TMPDIR=%t
Environment=TMUX=
UMask=0077
KillMode=process

ExecStart=%h/.config/tmux/orchestrator.sh

[Install]
WantedBy=default.target

1.4 Orchestrator-Skript (fixer Socket, persistentes cd, :0 nutzen)

/home/jj/.config/tmux/orchestrator.sh

#!/usr/bin/env bash
# ~/.config/tmux/orchestrator.sh (jj) – fixed socket /run/user/<uid>/tmux-<uid>/default
set -euo pipefail
unset TMUX

CONF="$HOME/.config/tmux/autostart.conf"
TMUX_BIN="$(command -v tmux)"
ZSH_BIN="$(command -v zsh || echo /usr/bin/zsh)"
UIDNUM="$(id -u)"
RUNDIR="${XDG_RUNTIME_DIR:-/run/user/${UIDNUM}}"
SOCKDIR="$RUNDIR/tmux-${UIDNUM}"
SOCK="$SOCKDIR/default"

umask 077
install -d -m 0700 -o "$UIDNUM" -g "$UIDNUM" "$RUNDIR"  >/dev/null 2>&1 || true
install -d -m 0700 -o "$UIDNUM" -g "$UIDNUM" "$SOCKDIR" >/dev/null 2>&1 || true

export TMUX_TMPDIR="$RUNDIR"
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
TMUXS=(-S "$SOCK")

log() { printf '[tmux-auto] %s\n' "$*" >&2; }
command -v "$TMUX_BIN" >/dev/null 2>&1 || { log "missing tmux"; exit 127; }

ensure_session() {
  local s="$1"
  if ! "$TMUX_BIN" "${TMUXS[@]}" has-session -t "$s" 2>/dev/null; then
    "$TMUX_BIN" "${TMUXS[@]}" new-session -d -s "$s" "$ZSH_BIN -l"
    sleep 0.1
    "$TMUX_BIN" "${TMUXS[@]}" send-keys -t "$s" "cd $HOME" C-m
    log "created session $s"
  fi
}

ensure_window() {
  local s="$1" w="$2"
  if ! "$TMUX_BIN" "${TMUXS[@]}" list-windows -t "$s" -F '#W' 2>/dev/null | grep -Fxq -- "$w"; then
    "$TMUX_BIN" "${TMUXS[@]}" new-window -t "$s" -n "$w" "$ZSH_BIN -l"
    sleep 0.05
    "$TMUX_BIN" "${TMUXS[@]}" send-keys -t "$s:$w" "cd $HOME" C-m
    log "created window $s:$w"
  fi
}

run_in_window_shell() {
  local tgt="$1" cmd="$2"
  if printf '%s' "$cmd" | grep -Eq '^[[:space:]]*cd[[:space:]]+'; then
    "$TMUX_BIN" "${TMUXS[@]}" send-keys -t "$tgt" "$cmd" C-m
  else
    "$TMUX_BIN" "${TMUXS[@]}" send-keys -t "$tgt" "bash -lc '$cmd'" C-m
  fi
  log "started $tgt -> $cmd"
}

harden_tmux_server() {
  "$TMUX_BIN" "${TMUXS[@]}" set -g remain-on-exit on >/dev/null
  "$TMUX_BIN" "${TMUXS[@]}" set -g exit-empty off >/dev/null
  "$TMUX_BIN" "${TMUXS[@]}" set -g detach-on-destroy off >/dev/null
}

run_phase_boot() {
  ensure_session tm0
  harden_tmux_server
  [[ -f "$CONF" ]] || return 0
  awk -F '|' '/^[[:space:]]*#/ || /^[[:space:]]*$/ {next} $1=="boot"{print}' "$CONF" |
  while IFS='|' read -r _p s w cmd; do
    s="${s:-tm0}"; ensure_session "$s"
    if [ -z "${cmd:-}" ]; then [ -n "${w:-}" ] && ensure_window "$s" "$w"; continue; fi
    if [ "$s" = "tm0" ]; then
      [ -n "${w:-}" ] && "$TMUX_BIN" "${TMUXS[@]}" rename-window -t tm0:0 "$w" 2>/dev/null || true
      run_in_window_shell "tm0:0" "$cmd"
    else
      if [ -n "${w:-}" ]; then
        ensure_window "$s" "$w"; run_in_window_shell "$s:$w" "$cmd"
      else
        run_in_window_shell "$s:0" "$cmd"  # :0 wiederverwenden
      fi
    fi
  done
}

wait_for_display_and_inject() {
  local xauth disp
  log "waiting for display (xauth cookie in $RUNDIR)…"
  while true; do
    xauth="$(ls -t "$RUNDIR"/xauth_* 2>/dev/null | head -n1 || true)"
    if [ -n "${xauth:-}" ] && [ -s "$xauth" ]; then
      disp="$(xauth -f "$xauth" list 2>/dev/null | awk '{ if (match($0,/unix:([0-9]+)/)) {n=substr($0,RSTART+5,RLENGTH-5); print ":" n; exit} if (match($0,/:([0-9]+)/)) {n=substr($0,RSTART+1,RLENGTH-1); print ":" n; exit} }')"
      if [ -n "${disp:-}" ]; then
        "$TMUX_BIN" "${TMUXS[@]}" set-environment -g XAUTHORITY "$xauth"
        "$TMUX_BIN" "${TMUXS[@]}" set-environment -g DISPLAY "$disp"
        "$TMUX_BIN" "${TMUXS[@]}" set-environment -g XDG_RUNTIME_DIR "$RUNDIR"
        export XAUTHORITY="$xauth" DISPLAY="$disp" XDG_RUNTIME_DIR="$RUNDIR"
        log "injected DISPLAY=$disp, XAUTHORITY=$xauth"
        return 0
      fi
    fi
    sleep 2
  done
}

set_xkb_for_xwayland() {
  command -v setxkbmap >/dev/null 2>&1 || return 0
  local cur_layout cur_variant
  cur_layout="$(setxkbmap -query 2>/dev/null | awk '/^layout:/ {print $2}')"
  cur_variant="$(setxkbmap -query 2>/dev/null | awk '/^variant:/ {print $2}')"
  if [ "$cur_layout" != "de" ] || [ "${cur_variant:-}" != "nodeadkeys" ]; then
    setxkbmap -layout de -variant nodeadkeys 2>/dev/null || true
    log "setxkbmap de nodeadkeys for Xwayland clients"
  fi
}

run_phase_display() {
  [[ -f "$CONF" ]] || return 0
  awk -F '|' '/^[[:space:]]*#/ || /^[[:space:]]*$/ {next} $1=="display"{print}' "$CONF" |
  while IFS='|' read -r _p s w cmd; do
    s="${s:-tm0}"; ensure_session "$s"
    if [ -z "${w:-}" ]; then w=""; fi
    if [ -z "${cmd:-}" ]; then
      if [ -n "$w" ]; then ensure_window "$s" "$w"; fi
      continue
    fi
    if [ -n "$w" ]; then
      ensure_window "$s" "$w"; run_in_window_shell "$s:$w" "$cmd"
    else
      run_in_window_shell "$s:0" "$cmd"
    fi
  done
}

log "start (boot phase)"; run_phase_boot
log "wait/display phase"; wait_for_display_and_inject
set_xkb_for_xwayland
run_phase_display
log "done"; exit 0

1.5 Beispiel-Config

/home/jj/.config/tmux/autostart.conf

# phase|session|window|command

# BOOT: sofort in tm0:0 ausführen
boot|tm0|AI|cd /home/jj/Test_Lab/Camera/; source /home/jj/Test_Lab/Camera/.venv/bin/activate && python3 /home/jj/Test_Lab/Camera/Version_0.8__capture.py

# DISPLAY: Sessions vorbereiten/Ordner setzen
display|tm12||cd /home/jj/OpenVPN/Schneider.land
display|tm5||

1.6 Rechte & Start

sudo -u jj chmod 0755 /home/jj/.config/tmux/orchestrator.sh
sudo -u jj systemctl --user daemon-reload
sudo -u jj systemctl --user enable --now tmux-autostart.service

1.7 Test

# direkt nach Aktivierung
sudo -u jj tmux -S /run/user/1000/tmux-1000/default ls
# nach Login (GUI): display-Phase sollte gelaufen sein
sudo -u jj tmux ls

2) Optional: root identisch als User-Unit

2.1 Linger & Verzeichnisse

sudo loginctl enable-linger root
sudo mkdir -p /root/.config/systemd/user /root/.config/tmux

2.2 Service-Datei

/root/.config/systemd/user/tmux-autostart.service

[Unit]
Description=Start/refresh tmux for root (boot + wait for display, fixed socket)

[Service]
Type=simple
ExecStartPre=/bin/sleep 10
ExecStartPre=/usr/bin/install -d -m 0700 -o root -g root /run/user/0
ExecStartPre=/usr/bin/install -d -m 0700 -o root -g root /run/user/0/tmux-0
ExecStartPre=/usr/bin/env bash -c 'test -S "/run/user/0/tmux-0/default" && rm -f "/run/user/0/tmux-0/default" || true'

Environment=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
Environment=TMUX_TMPDIR=%t
Environment=TMUX=
UMask=0077
KillMode=process

ExecStart=%h/.config/tmux/orchestrator.sh

[Install]
WantedBy=default.target

2.3 Orchestrator-Skript (root, fester Socket + Boot-:0-Fix)

/root/.config/tmux/orchestrator.sh

#!/usr/bin/env bash
# /root/.config/tmux/orchestrator.sh – fixed socket /run/user/0/tmux-0/default
set -euo pipefail
unset TMUX

CONF="/root/.config/tmux/autostart.conf"
TMUX_BIN="$(command -v tmux)"
ZSH_BIN="$(command -v zsh || echo /usr/bin/zsh)"
RUNDIR="/run/user/0"
SOCKDIR="$RUNDIR/tmux-0"
SOCK="$SOCKDIR/default"

umask 077
install -d -m 0700 -o root -g root "$RUNDIR"  >/dev/null 2>&1 || true
install -d -m 0700 -o root -g root "$SOCKDIR" >/dev/null 2>&1 || true

export TMUX_TMPDIR="$RUNDIR"
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:$PATH"
TMUXS=(-S "$SOCK")

log() { printf '[tmux-root] %s\n' "$*" >&2; }
command -v "$TMUX_BIN" >/dev/null 2>&1 || { log "missing tmux"; exit 127; }

ensure_session() {
  local s="$1"
  if ! "$TMUX_BIN" "${TMUXS[@]}" has-session -t "$s" 2>/dev/null; then
    "$TMUX_BIN" "${TMUXS[@]}" new-session -d -s "$s" "$ZSH_BIN -l"
    sleep 0.1
    "$TMUX_BIN" "${TMUXS[@]}" send-keys -t "$s" "cd /root" C-m
    log "created session $s"
  fi
}

ensure_window() {
  local s="$1" w="$2"
  if ! "$TMUX_BIN" "${TMUXS[@]}" list-windows -t "$s" -F '#W' 2>/dev/null | grep -Fxq -- "$w"; then
    "$TMUX_BIN" "${TMUXS[@]}" new-window -t "$s" -n "$w" "$ZSH_BIN -l"
    sleep 0.05
    "$TMUX_BIN" "${TMUXS[@]}" send-keys -t "$s:$w" "cd /root" C-m
    log "created window $s:$w"
  fi
}

run_in_window_shell() {
  local tgt="$1" cmd="$2"
  if printf '%s' "$cmd" | grep -Eq '^[[:space:]]*cd[[:space:]]+'; then
    "$TMUX_BIN" "${TMUXS[@]}" send-keys -t "$tgt" "$cmd" C-m
  else
    "$TMUX_BIN" "${TMUXS[@]}" send-keys -t "$tgt" "bash -lc '$cmd'" C-m
  fi
  log "started $tgt -> $cmd"
}

harden_tmux() {
  "$TMUX_BIN" "${TMUXS[@]}" set -g remain-on-exit on >/dev/null
  "$TMUX_BIN" "${TMUXS[@]}" set -g exit-empty off >/dev/null
  "$TMUX_BIN" "${TMUXS[@]}" set -g detach-on-destroy off >/dev/null
}

run_phase_boot() {
  ensure_session tm0
  harden_tmux
  [[ -f "$CONF" ]] || return 0
  awk -F '|' '/^[[:space:]]*#/ || /^[[:space:]]*$/ {next} $1=="boot"{print}' "$CONF" |
  while IFS='|' read -r _p s w cmd; do
    s="${s:-tm0}"; ensure_session "$s"
    if [ -z "${cmd:-}" ]; then [ -n "${w:-}" ] && ensure_window "$s" "$w"; continue; fi
    if [ "$s" = "tm0" ]; then
      [ -n "${w:-}" ] && "$TMUX_BIN" "${TMUXS[@]}" rename-window -t tm0:0 "$w" 2>/dev/null || true
      run_in_window_shell "tm0:0" "$cmd"
    else
      if [ -n "${w:-}" ]; then
        ensure_window "$s" "$w"; run_in_window_shell "$s:$w" "$cmd"
      else
        run_in_window_shell "$s:0" "$cmd"   # :0 wiederverwenden (kein "auto")
      fi
    fi
  done
}

find_latest_xauth() { ls -t /run/user/*/xauth_* 2>/dev/null | head -n1 || true; }

wait_for_display_and_inject() {
  local xauth disp cookie_dir
  log "waiting for display (any /run/user/*/xauth_*)…"
  while true; do
    xauth="$(find_latest_xauth)"
    if [ -n "${xauth:-}" ] && [ -s "$xauth" ]; then
      disp="$(xauth -f "$xauth" list 2>/dev/null | awk '{ if (match($0,/unix:([0-9]+)/)) {n=substr($0,RSTART+5,RLENGTH-5); print ":" n; exit} if (match($0,/:([0-9]+)/)) {n=substr($0,RSTART+1,RLENGTH-1); print ":" n; exit} }')"
      if [ -n "${disp:-}" ]; then
        cookie_dir="$(dirname "$xauth")"
        "$TMUX_BIN" "${TMUXS[@]}" set-environment -g XAUTHORITY "$xauth"
        "$TMUX_BIN" "${TMUXS[@]}" set-environment -g DISPLAY "$disp"
        "$TMUX_BIN" "${TMUXS[@]}" set-environment -g XDG_RUNTIME_DIR "$cookie_dir"
        export XAUTHORITY="$xauth" DISPLAY="$disp" XDG_RUNTIME_DIR="$cookie_dir"
        log "injected DISPLAY=$disp, XAUTHORITY=$xauth, XDG_RUNTIME_DIR=$cookie_dir"
        return 0
      fi
    fi
    sleep 2
  done
}

set_xkb_for_xwayland() {
  command -v setxkbmap >/dev/null 2>&1 || return 0
  local cur_layout cur_variant
  cur_layout="$(setxkbmap -query 2>/dev/null | awk '/^layout:/ {print $2}')"
  cur_variant="$(setxkbmap -query 2>/dev/null | awk '/^variant:/ {print $2}')"
  if [ "$cur_layout" != "de" ] || [ "${cur_variant:-}" != "nodeadkeys" ]; then
    setxkbmap -layout de -variant nodeadkeys 2>/dev/null || true
    log "setxkbmap de nodeadkeys"
  fi
}

run_phase_display() {
  [[ -f "$CONF" ]] || return 0
  awk -F '|' '/^[[:space:]]*#/ || /^[[:space:]]*$/ {next} $1=="display"{print}' "$CONF" |
  while IFS='|' read -r _p s w cmd; do
    s="${s:-tm0}"; ensure_session "$s"
    if [ -z "${w:-}" ]; then w=""; fi
    if [ -z "${cmd:-}" ]; then
      if [ -n "$w" ]; then ensure_window "$s" "$w"; fi
      continue
    fi
    if [ -n "$w" ]; then
      ensure_window "$s" "$w"; run_in_window_shell "$s:$w" "$cmd"
    else
      run_in_window_shell "$s:0" "$cmd"
    fi
  done
}

log "start (boot phase)"; run_phase_boot
log "wait/display phase"; wait_for_display_and_inject
set_xkb_for_xwayland
run_phase_display
log "done"; exit 0

2.4 Beispiel-Config (root)

/root/.config/tmux/autostart.conf

# phase|session|window|command
boot|tm11||cd /etc/JJ_SystemMetric/; source /etc/JJ_SystemMetric/.venv/bin/activate && python3 /etc/JJ_SystemMetric/Version_0.19__Start_SystemMetric.py
boot|tm12||cd /home/jj/OpenVPN/Schneider.land
boot|tm13||cd /home/jj/Cronjob/ntfy/; python3 /home/jj/Cronjob/ntfy/Version_0.13__ntfy.py

2.5 Rechte & Start

sudo chmod 0755 /root/.config/tmux/orchestrator.sh
sudo systemctl --user daemon-reload
sudo systemctl --user enable --now tmux-autostart.service
# prüfen:
sudo tmux -S /run/user/0/tmux-0/default ls

3) Nutzung & Beispiele


4) Troubleshooting


5) Sicherheitshinweis (root → User-Display)

Root nutzt das Xauth-Cookie der eingeloggten Nutzersitzung, um X/Wayland-Apps an diese Session anzubinden. Das ist üblich, aber beachte die Sicherheitsimplikationen (root erhält Display-Zugriff). Kein xhost + nötig.


6) Deinstallation

# jj
sudo -u jj systemctl --user disable --now tmux-autostart.service
sudo rm -f /home/jj/.config/systemd/user/tmux-autostart.service /home/jj/.config/tmux/orchestrator.sh
# root
sudo systemctl --user disable --now tmux-autostart.service
sudo rm -f /root/.config/systemd/user/tmux-autostart.service /root/.config/tmux/orchestrator.sh

Linux - Angemeldete User

Diese Anleitung zeigt, wie man auf einem Debian-Server:

Wichtig: Die meisten Schritte müssen als root ausgeführt werden (z. B. über direkte Root-Anmeldung oder su -).

1. Aktive Benutzer anzeigen

1.1 Schnellübersicht mit who

Der Befehl who zeigt, wer aktuell eingeloggt ist.

who

Typische Ausgabe:

user1    pts/0        2025-12-02 10:15 (192.168.1.10)
user2    pts/1        2025-12-02 10:20 (10.0.0.5)

1.2 Detailansicht mit w

w liefert zusätzlich, was die Benutzer gerade tun.

w

Typische Ausgabe (Beispiel gekürzt):

 10:25:01 up 10 days,  3:12,  2 users,  load average: 0.00, 0.01, 0.05
USER     TTY      FROM             LOGIN@   IDLE   JCPU   PCPU WHAT
user1    pts/0    192.168.1.10     10:15    2:00   0.01s  0.01s -bash
user2    pts/1    10.0.0.5         10:20    0.00s  0.02s  0.00s sshd: user2@pts/1
Hinweis: Die Spalte FROM zeigt die Quelle (IP/Hostname), WHAT zeigt den aktuell laufenden Prozess bzw. Befehl.

1.3 Nur Benutzernamen mit users

Wenn nur eine schnelle Liste der angemeldeten Benutzer benötigt wird:

users

2. Anmelde-Historie anzeigen

2.1 Letzte Logins mit last

last liest /var/log/wtmp und zeigt vergangene Logins und Logouts.

last

Beispielausgabe:

user1   pts/0    192.168.1.10   Tue Dec  2 10:15   still logged in
user2   pts/1    10.0.0.5       Tue Dec  2 10:20 - 10:30  (00:10)
reboot  system boot  5.10.0-xx  Mon Dec  1 09:00   still running

Mit zusätzlicher Anzeige der IP/Hostname am Ende:

last -a

2.2 Übersicht über alle Benutzer mit lastlog

Alle Benutzer und deren letzten Login:

lastlog

Damit lassen sich z. B. inaktive Accounts erkennen.


3. SSH-Logins über Logdateien prüfen

SSH-Anmeldungen werden auf Debian typischerweise in /var/log/auth.log protokolliert.

3.1 Aktuelle SSH-Sessions und Logins anzeigen

Nur die Zeilen mit geöffneten Sessions anzeigen:

grep "session opened for user" /var/log/auth.log

Nur fehlgeschlagene SSH-Logins anzeigen:

grep "Failed password" /var/log/auth.log

3.2 Live-Monitoring von SSH-Anmeldungen

Neue SSH-Einträge fortlaufend beobachten:

tail -f /var/log/auth.log

Oder mit journalctl (bei systemd):

journalctl -u ssh -f

4. Benutzer-Sessions gezielt trennen („kicken“)

Vorsicht: Das Beenden einer Session kann laufende Prozesse oder ungespeicherte Arbeiten des Benutzers zerstören. Nur dort beenden, wo es wirklich nötig ist.

4.1 Session über Terminal (TTY/PTS) beenden

1. Zuerst das Terminal des Benutzers ermitteln, z. B. mit who:

who

Beispiel:

user1    pts/0        2025-12-02 10:15 (192.168.1.10)

Hier ist das Terminal pts/0.

2. Session über dieses Terminal beenden:

pkill -KILL -t pts/0

Nach diesem Befehl wird der Benutzer user1 von diesem Terminal abgemeldet.

4.2 Session über loginctl beenden (systemd)

Auf systemd-basierten Systemen (aktuelles Debian) können Sessions mit loginctl verwaltet werden.

4.2.1 Sessions anzeigen

loginctl list-sessions

Beispielausgabe:

SESSION  UID  USER   SEAT  TTY
     2 1000  user1        pts/0
     3 1001  user2        pts/1

4.2.2 Einzelne Session beenden

Um z. B. Session 2 zu beenden:

loginctl terminate-session 2

4.2.3 Alle Sessions eines Benutzers beenden

Um alle Sessions des Benutzers user1 zu beenden:

loginctl kill-user user1

5. Welche Art von Anmeldung liegt vor?

Über die Ausgabe von w, who und die Logs lässt sich die Art der Anmeldung erkennen:

Über die Logdatei /var/log/auth.log sieht man zudem genau:


6. Praxis-Beispiele

6.1 Alle aktuell eingeloggten Benutzer anzeigen

who
w

6.2 Historie der Logins eines bestimmten Benutzers

Beispielsweise für den Benutzer user1:

last user1

6.3 Alle fehlgeschlagenen SSH-Logins anzeigen

grep "Failed password" /var/log/auth.log

6.4 Bestimmten SSH-Benutzer sofort trennen

Beispiel: user1 ist auf pts/0 eingeloggt.

who
pkill -KILL -t pts/0

7. Zusammenfassung

Mit diesen Befehlen lassen sich Benutzeraktivitäten auf einem Debian-Server nachvollziehen und bei Bedarf Sessions gezielt trennen.

Linux - VNC/RDP

Linux - VNC/RDP

Debian 13 + XRDP + KDE Plasma (X11) – Basic Setup

Überblick: Diese Seite besteht aus zwei getrennten Anleitungen:
  1. KDE Plasma mit Wayland für die lokale Anmeldung auf der VM installieren.
  2. XRDP einrichten und sich anschließend per xfreerdp3 mit der VM verbinden.
Wichtig: KDE Plasma Wayland und XRDP sind zwei verschiedene Dinge. Die lokale KDE-Sitzung kann Wayland nutzen. Eine XRDP-Sitzung mit KDE läuft dagegen zuverlässig als X11, nicht als Wayland.

Anleitung 1 ausklappen: KDE Plasma Wayland auf Debian 13 installieren

Anleitung 1: KDE Plasma Wayland auf Debian 13 installieren

Diese Anleitung installiert KDE Plasma mit Wayland-Unterstützung für die lokale Anmeldung an der VM.

Voraussetzung: Diese Anleitung geht davon aus, dass du als root arbeitest. Es wird kein sudo verwendet.

1. Paketquellen aktualisieren

apt update

2. KDE Plasma mit Wayland-Unterstützung und Display-Manager installieren

apt install -y plasma-desktop plasma-workspace sddm dbus-x11 kwin-x11
Hinweis: In Debian 13/Trixie wird plasma-workspace-wayland nicht mehr separat verwendet. Die benötigten Wayland-Komponenten sind über plasma-workspace abgedeckt.
Schlanke Installation: plasma-desktop installiert eine relativ schlanke KDE-Plasma-Umgebung. Wer später ein größeres KDE-Setup möchte, kann zusätzlich kde-standard oder kde-full installieren.

3. SDDM aktivieren

SDDM ist der Login-Manager für die grafische Anmeldung.

systemctl enable sddm

4. Grafischen Startmodus aktivieren

systemctl set-default graphical.target

5. System neu starten

reboot

6. Bei KDE Plasma Wayland anmelden

Nach dem Neustart erscheint der grafische Login-Bildschirm.

Wähle dort vor dem Login als Sitzung:

Plasma (Wayland)
Ergebnis: Die lokale Anmeldung an der VM nutzt jetzt KDE Plasma mit Wayland.

Anleitung 2 ausklappen: Per XRDP und xfreerdp3 auf die VM zugreifen

Anleitung 2: Per XRDP und xfreerdp3 auf die VM zugreifen

Diese Anleitung richtet XRDP auf der VM ein und zeigt danach die Verbindung mit xfreerdp3 vom Client aus.

Wichtig: Die XRDP-Sitzung startet KDE Plasma als X11-Session. Wayland wird für XRDP hier bewusst nicht verwendet, weil KDE über XRDP als X11-Sitzung deutlich zuverlässiger läuft.

1. XRDP und Xorg-Backend installieren

Auf der VM als root ausführen:

apt update
apt install -y xrdp xorgxrdp dbus-x11
Hinweis: Wenn KDE Plasma bereits aus Anleitung 1 installiert wurde, müssen plasma-desktop und plasma-workspace hier nicht erneut installiert werden.

2. XRDP-Startscript für KDE Plasma X11 setzen

Bearbeite die Datei:

/etc/xrdp/startwm.sh

Ersetze den kompletten Inhalt durch:

#!/bin/sh

unset DBUS_SESSION_BUS_ADDRESS
unset XDG_RUNTIME_DIR

export XDG_SESSION_TYPE=x11
export KDE_FULL_SESSION=true
export QT_QPA_PLATFORM=xcb

export QT_QUICK_BACKEND=software
export LIBGL_ALWAYS_SOFTWARE=1

export KWIN_COMPOSE=N
export KWIN_NO_COMPOSITING=1

exec dbus-run-session -- startplasma-x11

3. Startscript ausführbar machen

chmod +x /etc/xrdp/startwm.sh

4. XRDP-Dienste aktivieren und starten

systemctl enable --now xrdp xrdp-sesman

5. IP-Adresse der VM anzeigen

Auf der VM ausführen:

hostname -I

Merke dir die passende IP-Adresse der VM. Diese wird später bei xfreerdp3 als SERVER-IP verwendet.

6. Benutzer für die RDP-Anmeldung verwenden

Sicherheit: Melde dich nicht als root per RDP an. Verwende einen normalen Benutzer, zum Beispiel jj.

Falls noch kein normaler Benutzer vorhanden ist, kann einer angelegt werden:

adduser jj

7. FreeRDP-Client installieren

Auf dem Linux-Client ausführen, von dem aus du dich zur VM verbinden möchtest:

apt update
apt install -y freerdp3-x11

8. Verbindung zur VM herstellen

Auf dem Client ausführen:

xfreerdp3 /v:SERVER-IP:3389 /u:jj /d:. /dynamic-resolution /clipboard /cert:tofu

Beispiel:

xfreerdp3 /v:192.168.178.50:3389 /u:jj /d:. /dynamic-resolution /clipboard /cert:tofu

9. Bedeutung der wichtigsten Optionen

10. Security-Basics

RDP nicht offen ins Internet stellen. Port 3389 sollte nur im lokalen Netzwerk, über VPN oder über einen sicheren Tunnel erreichbar sein.

11. Typische Stolpersteine

XRDP zeigt keinen KDE-Desktop

Prüfe, ob die Dienste laufen:

systemctl status xrdp
systemctl status xrdp-sesman

Keine Fensterleisten oder Fenster lassen sich nicht verschieben

Dann läuft KWin wahrscheinlich nicht sauber oder Compositing stört die XRDP-Sitzung.

Das Startscript aus dieser Anleitung deaktiviert Compositing bereits für die XRDP-Sitzung:

export KWIN_COMPOSE=N
export KWIN_NO_COMPOSITING=1

Danach XRDP neu starten:

systemctl restart xrdp xrdp-sesman

Panel ist schwarz oder Transparenzen sehen kaputt aus

Das kann durch deaktiviertes Compositing passieren. Für XRDP ist das meist der stabilere Weg.

Mögliche Lösung:


Zusammenfassung


Linux Server

Linux - ZRAM

Linux - ZRAM

Linux - ZRAM Config

Ziel: ZRAM als schnellen Swap einrichten (komprimierter RAM statt SSD/HDD), passende Größe für 2 GB bis 128 GB RAM wählen, Prioritäten korrekt setzen, Latenz/CPU-Last sauber austarieren, Verifikation & Monitoring verstehen.

Voraussetzung: Die Installations-/Systemänderungsbefehle erfordern Root-Rechte (z. B. via Root-Shell). Reine Anzeige-/Monitoring-Befehle funktionieren auch als normaler Nutzer.


Inhalt

  1. Grundidee: Was ist ZRAM und wofür nutzt man es?
  2. Quick-Check: Ist ZRAM aktiv und sinnvoll priorisiert?
  3. Sizing: Empfehlungen von 2 GB bis 128 GB RAM
  4. Kompressions-Algorithmen: lz4 vs. zstd (und warum)
  5. Installation (Debian/Ubuntu)
  6. Konfiguration: systemd-zram-generator (empfohlen)
  7. Konfiguration: zram-tools (Alternative)
  8. VM-/Kernel-Tuning: warum diese sysctl-Werte?
  9. Disk-Swap: wann, warum, Priorität
  10. Verifizieren & Monitoring (inkl. PSI)
  11. Rollback: wie man es sauber rückgängig macht

Grundidee: Was ist ZRAM und wofür nutzt man es?

Merke: ZRAM ist ein Puffer gegen Speicherpeaks und OOM, kein Ersatz für realistischen RAM-Bedarf.


Quick-Check: Ist ZRAM aktiv und sinnvoll priorisiert?

Kommandos
swapon --show
free -h
zramctl
cat /sys/block/zram0/mm_stat
cat /proc/pressure/memory

Warum genau diese Befehle?

 

swapon --show
Zeigt aktive Swap-Geräte (ZRAM und ggf. Disk-Swap), inkl. Größe, Nutzung und Priorität. Wichtigster Punkt: ZRAM sollte eine höhere Priorität haben als Disk-Swap, damit zuerst der schnelle komprimierte RAM genutzt wird.
free -h
Gibt eine grobe Übersicht über RAM/Swap. Besonders relevant ist verfügbar: das ist die Menge RAM, die ohne massiven Stress freigemacht werden kann (Cache wird dabei mitgedacht). Unterschied zu swapon: free zeigt Gesamtbild, swapon zeigt Swap-Details.
zramctl
Zeigt ZRAM-spezifische Infos: Algorithmus, virtuelle Disksize, sowie wieviel unkomprimierte Daten im ZRAM liegen und wieviel realer RAM dafür belegt wird. Unterschied zu free: free sieht nur „Swap belegt“, zramctl zeigt die Kompressionswirkung.
cat /sys/block/zram0/mm_stat
Detail-Statistiken aus dem Kernel: u. a. wieviel Daten im ZRAM liegen und wie effektiv komprimiert wird. Gut, um einzuschätzen, ob z. B. zstd wirklich einen Vorteil bringt oder nur CPU verbrennt.
cat /proc/pressure/memory
PSI (Pressure Stall Information) zeigt, ob Prozesse messbar auf Speicher warten mussten. Warum das wichtig ist: Ruckler, Audio-Aussetzer und „alles zäh“ korrelieren oft eher mit PSI als mit „Swap ist voll“.

 


Sizing: Empfehlungen von 2 GB bis 128 GB RAM

Regel: Wenig RAM → aggressiver (mehr ZRAM), viel RAM → moderater (ZRAM als Sicherheitsnetz). Ziel ist weniger OOM und weniger Disk-Swap, nicht „unendlich Tabs durch Kompression“.

RAM ZRAM-Größe (Richtwert) Warum gerade so? Typische Nutzung
2 GB 75–100% (1.5–2 GB) Ohne Puffer kommt es schnell zu OOM. ZRAM schafft Luft, ohne Disk-I/O. Minimal-Desktop, kleine Server, VMs
4 GB 75–100% (3–4 GB) Hier bringt ZRAM oft den größten „Feeling“-Gewinn: weniger harte Memory-Kanten. Browser/Office/kleine Tools
8 GB ~75% (6 GB) Genug, um Peaks abzufedern, ohne zu viel in Swap zu driften. Allround-Desktop
16 GB 60–75% (10–12 GB) Guter Sweet Spot: ZRAM ist Reserve, aber RAM bleibt Hauptarbeitsfläche. IDE/Browser/leichte VMs
32 GB 50–75% (16–24 GB) Bei Tab/IDE/Meetings hilft mehr ZRAM, Disk-Swap zu vermeiden. Heavy Desktop/Workstation
64 GB 40–50% (24–32 GB) Mehr RAM → ZRAM wird seltener gebraucht, sollte aber Peaks fangen. Builds, VMs, Containers
128 GB 25–40% (32–48 GB) ZRAM als Sicherheitsnetz. Zu groß kann „Swap-Gewohnheiten“ fördern. Große Workstations/Server

Merke: ZRAM „Disksize“ ist virtuell. Realer RAM wird erst belegt, wenn Seiten wirklich im ZRAM landen. Trotzdem beeinflusst großer ZRAM die Swap-Strategie des Systems, deshalb bei sehr viel RAM moderater bleiben.


Kompressions-Algorithmen: lz4 vs. zstd

Algorithmus Stärke Schwäche Wann nehmen?
lz4 Sehr niedrige CPU-Last, geringe Latenz Weniger Kompression (mehr RAM-Verbrauch pro Swap-Seite) Meetings/Audio/Low-Latency wichtig, schwächere CPU, Desktop „smooth“
zstd Sehr gute Kompression (mehr „virtueller RAM“) Höhere CPU-Last als lz4 Viele Daten/Tabs/IDE/VMs und CPU ist nicht der Engpass

Merke: Wenn PSI/Audio-Ruckler dein Problem sind, ist lz4 oft die bessere Wahl. Wenn OOM/disk-swap dein Problem sind, bringt zstd oft mehr Nutzwert.


Installation (Debian & Ubuntu)

Option A (empfohlen): systemd-zram-generator
apt update
apt install -y systemd-zram-generator

 

apt update
Aktualisiert Paketlisten. Unterschied zu install: ohne frische Listen kann apt alte Versionen/Abhängigkeiten sehen oder Pakete nicht finden.
apt install -y systemd-zram-generator
Installiert den Generator, der beim Booten ZRAM-Devices nach Konfig erzeugt. Warum das bevorzugt ist: saubere systemd-Integration, klare Konfigdatei, gut wartbar. Das -y beantwortet die Rückfrage automatisch (nützlich für Copy/Paste, weniger nützlich für Menschen, die gern überraschende Dinge abbrechen).

 

Option B (Alternative): zram-tools
apt update
apt install -y zram-tools

 

apt install -y zram-tools
Installiert ein klassisches Setup-Skript/Service für ZRAM-Swap. Unterschied zu Option A: funktioniert oft okay, aber weniger flexibel und weniger „systemd-nativ“.

 


Konfiguration: systemd-zram-generator (empfohlen)

Datei: /etc/systemd/zram-generator.conf

Konfig-Snippets nach RAM-Klasse

2–4 GB RAM (maximaler Puffer, geringe CPU-Last)

[zram0]
zram-size = ram * 1.0
compression-algorithm = lz4
swap-priority = 100

Warum: Bei Low-RAM ist OOM die größte Gefahr. lz4 hält CPU-Latenz klein, während 100% ZRAM die Überlebenschancen erhöht.

8–16 GB RAM (Allround)

[zram0]
zram-size = ram * 0.75
compression-algorithm = zstd
swap-priority = 100

Warum: 75% ist ein guter Kompromiss. zstd holt mehr Effekt aus dem ZRAM heraus, weil hier oft viele ähnliche Seiten anfallen (Browser/IDE).

32 GB RAM (Heavy Desktop/Workstation)

[zram0]
zram-size = ram * 0.60
compression-algorithm = zstd
swap-priority = 100

Warum: Genug ZRAM als Reserve, aber nicht so viel, dass das System „Swap ist ja da“ als Einladung versteht.

64–128 GB RAM (Sicherheitsnetz, Latenz wichtiger)

[zram0]
zram-size = ram * 0.35
compression-algorithm = lz4
swap-priority = 100

Warum: Bei viel RAM sind harte Peaks seltener. Wenn doch, willst du glatte Latenz statt maximale Ratio. Daher moderater Size + lz4.

Konfig anwenden
systemctl daemon-reload
systemctl restart systemd-zram-setup@zram0.service

 

systemctl daemon-reload
Lädt systemd-Konfiguration neu ein. Warum: systemd merkt sich Units/Generator-Ergebnisse; ohne Reload kann eine Änderung wirkungslos bleiben. Unterschied zu restart: Reload aktualisiert systemd selbst, Restart startet nur den Dienst neu.
systemctl restart systemd-zram-setup@zram0.service
Initialisiert/konfiguriert zram0 neu. Warum: Wendet neue ZRAM-Größe/Algorithmus an, ohne Neustart. Wenn die Unit anders heißt: systemctl list-units --type=service --all nutzen und im Pager nach „zram“ suchen.

 


Konfiguration: zram-tools (Alternative)

Datei: /etc/default/zramswap

Beispiele (mit Begründung)
# 2 GB RAM
ALGO=lz4
PERCENT=100
PRIORITY=100

# 8 GB RAM
# ALGO=zstd
# PERCENT=75
# PRIORITY=100

# 32 GB RAM
# ALGO=zstd
# PERCENT=60
# PRIORITY=100

# 128 GB RAM
# ALGO=lz4
# PERCENT=35
# PRIORITY=100

 

ALGO
Legt den Kompressionsalgorithmus fest. lz4 ist latenzfreundlicher, zstd komprimiert besser. Unterschied: CPU vs. Kompressionsrate (siehe Tabelle oben).
PERCENT
Prozentualer Anteil des RAM, der als ZRAM-Disksize verwendet wird. Warum: Steuert, wie viel „Reserve“ du hast. Unterschied zu systemd-zram-generator: Dort nutzt man meist Formeln (ram * 0.75) statt Prozent-Integer.
PRIORITY
Swap-Priorität für ZRAM. Warum: ZRAM soll vor Disk-Swap drankommen. Priorität ist der zentrale Mechanismus, um die Reihenfolge zu erzwingen.

 

Konfig anwenden
systemctl restart zramswap.service

 

systemctl restart zramswap.service
Startet den ZRAM-Swap-Dienst neu, damit ALGO/PERCENT/PRIORITY neu gesetzt werden. Unterschied zu daemon-reload: hier wird nur der Service neu gestartet, nicht systemd selbst.

 


VM-/Kernel-Tuning: warum diese sysctl-Werte?

Datei: /etc/sysctl.d/99-zram-tuning.conf

Empfohlene Werte (Desktop/Allround)
vm.swappiness=100
vm.page-cluster=0
vm.vfs_cache_pressure=50
vm.dirty_background_ratio=5
vm.dirty_ratio=15

Warum genau diese Werte?

 

vm.swappiness=100
Erlaubt dem Kernel, eher zu swappen. Warum sinnvoll mit ZRAM: Swap ist hier schnell, weil er im RAM liegt. Unterschied zu klassischem Disk-Swap: Bei SSD/HDD wäre ein hoher Wert oft fühlbar langsamer.
vm.page-cluster=0
Beeinflusst, wie viele Swap-Seiten auf einmal read-ahead geladen werden. Warum 0: Bei ZRAM ist Read-ahead oft kein Gewinn und kann Latenzspitzen erzeugen, weil unnötig viel dekomprimiert wird.
vm.vfs_cache_pressure=50
Steuert, wie aggressiv der Kernel Dentry/Inode-Caches verwirft. Warum niedriger: Desktop-Workloads profitieren davon, wenn Metadaten-Caches länger bleiben (Dateibrowser, IDE, viele kleine Files).
vm.dirty_background_ratio=5 und vm.dirty_ratio=15
Steuert, wie viel „dirty“ (noch nicht auf Disk geschriebener) Cache erlaubt ist, bevor Writeback startet bzw. blockiert. Warum niedriger: verhindert große, plötzliche Schreib-Spikes, die Audio/Video und Interaktivität stören können. Unterschied: background startet früher sanften Writeback, dirty_ratio ist der harte Deckel.

 

Aktivieren
sysctl --system

 

sysctl --system
Lädt alle sysctl-Konfigurationen neu (u. a. aus /etc/sysctl.d/). Unterschied zu Neustart: wirkt sofort, ohne Boot.

 


Disk-Swap: wann, warum, Priorität

Priorität für Disk-Swap via /etc/fstab (Beispiel)
# Beispiel-Zeile (Device/UUID an dein System anpassen):
/dev/dm-2 none swap sw,pri=-2 0 0

Warum: pri=-2 sorgt dafür, dass Disk-Swap erst genutzt wird, wenn ZRAM nicht reicht. Unterschied zu Default: ohne explizite Prio kann die Reihenfolge je nach Setup weniger klar sein.


Verifizieren & Monitoring (inkl. PSI)

Kommandos
swapon --show
zramctl
free -h
cat /proc/pressure/memory

Wie interpretiert man das?


Rollback: wie man es sauber rückgängig macht

systemd-zram-generator
# Konfig entfernen oder auskommentieren:
# /etc/systemd/zram-generator.conf

systemctl daemon-reload
# Danach Neustart oder erneutes Initialisieren, je nach Setup/Unit-Namen.

Warum: Entfernst du die Generator-Konfig, wird beim nächsten Setup kein ZRAM mehr erstellt. Unterschied zu „swapoff“: hier wird die Ursache (Auto-Setup) entfernt, nicht nur temporär deaktiviert.

zram-tools
systemctl disable --now zramswap.service

 

systemctl disable --now zramswap.service
--now stoppt sofort, disable verhindert Autostart beim Boot. Unterschied zu stop: stop ist nur temporär, disable ist dauerhaft.

 

WireGuard

WireGuard

WireGuard auf Server und Client einrichten

Diese Anleitung beschreibt die Installation und Grundkonfiguration von WireGuard auf einem Debian-Server und einem Debian-Client. Das Beispiel verwendet einen WireGuard-Server auf UDP Port 443 und das interne VPN-Netz 172.31.255.224/27.

Verwendete Beispielwerte


1. WireGuard auf dem Server installieren

Auf dem Server werden die benötigten Pakete installiert und das Verzeichnis für die Konfiguration vorbereitet.

apt update
apt install -y wireguard-tools

install -d -m 700 /etc/wireguard
cd /etc/wireguard
umask 077

2. Schlüssel auf dem Server erzeugen

Der Server benötigt ein Schlüsselpaar. Zusätzlich wird für jeden Client ein eigener PresharedKey erzeugt.

wg genkey | tee server.key | wg pubkey | tee server.pub >/dev/null
wg genpsk | tee client1.psk >/dev/null

chmod 600 /etc/wireguard/server.key /etc/wireguard/client1.psk
chmod 644 /etc/wireguard/server.pub

3. Server-Konfiguration erstellen

Die Datei /etc/wireguard/wg0.conf wird auf dem Server angelegt.

nano /etc/wireguard/wg0.conf

Beispielinhalt:

[Interface]
Address = 172.31.255.254/27
ListenPort = 443
PrivateKey = SERVER_PRIVATE_KEY
SaveConfig = false

# Client 1
[Peer]
PublicKey = CLIENT1_PUBLIC_KEY
PresharedKey = CLIENT1_PRESHARED_KEY
AllowedIPs = 172.31.255.253/32

Dabei müssen die Platzhalter ersetzt werden:

Hinweis:

Für jeden weiteren Client wird ein zusätzlicher [Peer]-Block mit eigener Client-IP, eigenem Public Key und eigenem Preshared Key ergänzt. Beispiel:

# Client 2
[Peer]
PublicKey = CLIENT2_PUBLIC_KEY
PresharedKey = CLIENT2_PRESHARED_KEY
AllowedIPs = 172.31.255.252/32

4. WireGuard-Port in der Firewall freigeben

Damit Clients den Server erreichen können, muss der verwendete UDP-Port offen sein. In diesem Beispiel ist das 443/UDP.

5. WireGuard auf dem Server starten

Nach dem Speichern der Konfiguration wird das Interface gestartet und beim Booten aktiviert.

systemctl enable --now wg-quick@wg0
systemctl status wg-quick@wg0 --no-pager
wg show

Falls die Konfigurationsdatei später geändert wird, muss der Dienst neu geladen werden:

systemctl restart wg-quick@wg0
wg show

6. WireGuard auf dem Client installieren

Auch auf dem Client wird zunächst WireGuard installiert und das Konfigurationsverzeichnis angelegt.

apt update
apt install -y wireguard-tools

install -d -m 700 /etc/wireguard
cd /etc/wireguard
umask 077

7. Schlüssel auf dem Client erzeugen

Der Client erzeugt sein eigenes Schlüsselpaar lokal. Der Private Key bleibt nur auf dem Client.

wg genkey | tee client1.key | wg pubkey | tee client1.pub >/dev/null
chmod 600 /etc/wireguard/client1.key
chmod 644 /etc/wireguard/client1.pub

Der Public Key des Clients kann angezeigt werden mit:

cat /etc/wireguard/client1.pub

Dieser Public Key wird anschließend in die Server-Konfiguration im passenden [Peer]-Block eingetragen.

8. Client-Konfiguration erstellen

Die Datei /etc/wireguard/wg0.conf auf dem Client anlegen:

nano /etc/wireguard/wg0.conf

Beispielinhalt für Client 1:

[Interface]
Address = 172.31.255.253/32
PrivateKey = CLIENT1_PRIVATE_KEY

[Peer]
PublicKey = SERVER_PUBLIC_KEY
PresharedKey = CLIENT1_PRESHARED_KEY
Endpoint = DEINE_VPS_IP:443
AllowedIPs = 172.31.255.254/32
PersistentKeepalive = 25

Platzhalter ersetzen:

Wichtig:

In diesem Beispiel erlaubt AllowedIPs = 172.31.255.254/32 nur den Zugriff auf die Tunnel-IP des Servers. Der gesamte Internetverkehr des Clients wird dadurch nicht automatisch über WireGuard geleitet.

9. Verbindung auf dem Client starten

Zum Testen wird das Interface zunächst manuell gestartet.

wg-quick up wg0

Status prüfen:

wg show
ip addr show wg0
ping -c 3 172.31.255.254

Verbindung wieder beenden:

wg-quick down wg0

Wenn alles funktioniert, kann WireGuard dauerhaft aktiviert werden:

systemctl enable --now wg-quick@wg0
systemctl status wg-quick@wg0 --no-pager
wg show

10. Weitere Clients hinzufügen

Für jeden weiteren Client werden dieselben Schritte wiederholt:

  1. Auf dem Client eigenes Schlüsselpaar erzeugen
  2. Auf dem Server einen eigenen PresharedKey erzeugen
  3. Auf dem Server einen neuen [Peer]-Block anlegen
  4. Dem Client eine eigene feste Tunnel-IP geben
  5. Server-Dienst nach Änderungen neu starten

Beispiel für Client 2:


11. Typische Fehlerquellen

12. Kurzfassung

  1. Server installieren und Schlüssel erzeugen
  2. Server-Config mit [Interface] und [Peer] erstellen
  3. 443/UDP freigeben
  4. Server mit wg-quick@wg0 starten
  5. Client installieren und eigenes Schlüsselpaar erzeugen
  6. Client-Config mit Server-Public-Key, Preshared Key und Endpoint erstellen
  7. Client mit wg-quick up wg0 starten und Verbindung testen

ATOM (Alter Code-Editor)

ATOM (Alter Code-Editor)

Atom: Kotlin-Dateien (.kt) mit Syntax-Highlighting öffnen

Diese Anleitung zeigt, wie Atom Kotlin-Dateien mit der Endung .kt und .kts als Kotlin erkennt und Syntax-Highlighting aktiviert.


Problem

Die Paket-Suche in Atom funktioniert nicht mehr zuverlässig. Beim Suchen nach language-kotlin erscheint zum Beispiel:

Unexpected token < in JSON at position 0

Das liegt daran, dass Atom keine Paketdaten mehr bekommt, sondern eine HTML-Seite. Atom wurde eingestellt, deshalb funktioniert die Paketverwaltung nicht mehr sauber.


Voraussetzung

Für diese Anleitung muss git bereits installiert sein.

Das kann mit folgendem Befehl geprüft werden:

git --version

Wenn eine Versionsnummer angezeigt wird, ist git vorhanden.


Lösung

Das Kotlin-Sprachpaket wird manuell in den Atom-Paketordner des eigenen Benutzers installiert.

Atom-Paketordner anlegen

mkdir -p "$HOME/.atom/packages"

Kotlin-Sprachpaket herunterladen

git clone --depth 1 https://github.com/alexmt/atom-kotlin-language.git "$HOME/.atom/packages/language-kotlin"

Falls die Meldung erscheint, dass der Zielordner bereits existiert, wurde das Paket vermutlich schon heruntergeladen.

Der Paketordner sollte danach hier liegen:

$HOME/.atom/packages/language-kotlin

Dateizuordnung in Atom setzen

Damit Atom Dateien mit .kt und .kts sicher als Kotlin erkennt, muss die Atom-Konfiguration angepasst werden.

Die Datei befindet sich hier:

$HOME/.atom/config.cson

Im vorhandenen Bereich core wird customFileTypes ergänzt.

Beispiel

"*":
  core:
    closeDeletedFileTabs: true
    telemetryConsent: "no"
    customFileTypes:
      "source.kotlin": [
        "kt"
        "kts"
      ]
  editor:
    fontSize: 15
    showIndentGuide: true
    showInvisibles: true
  "exception-reporting":
    userId: "22758505-7925-41c0-a70b-cb47b5f1678a"
  welcome:
    showOnStartup: false

Wichtig: Es darf kein zweiter core:-Block angelegt werden. Die Zeilen müssen unter dem bereits vorhandenen core:-Block eingefügt werden.


Atom neu starten

Nach der Änderung Atom vollständig schließen und neu öffnen.

Danach eine .kt-Datei öffnen. Unten rechts in Atom sollte dann als Sprache Kotlin angezeigt werden.


Kontrolle

Folgende Dinge sollten vorhanden sein:


Hinweis

Atom wird nicht mehr aktiv weiterentwickelt. Für neue Projekte ist ein gepflegter Editor wie Visual Studio Code, Kate oder IntelliJ IDEA sinnvoller. Für bestehende Systeme kann diese Lösung aber weiterhin ausreichen.

Linux - Proxmox

Linux - Proxmox

Proxmox VE 9 auf verschlüsseltem Debian 13 mit Dropbear-Remote-Unlock installieren

Diese Anleitung beschreibt die Installation von Proxmox VE 9 auf einem bereits installierten Debian 13 System mit verschlüsseltem Root-Dateisystem via LUKS. Das System wird vor dem Boot per dropbear-initramfs über SSH entsperrt. Zusätzlich wird ein zweiter verschlüsselter lokaler Storage für VM-Disks und ISO-Dateien eingerichtet.

Ausgangslage in diesem Beispiel:


1. Bestehende Partitionierung prüfen

lsblk
df -h
uname -r

Beispielhafte Zielstruktur für das Systemlaufwerk:

nvme0n1
├─nvme0n1p1        /boot/efi
├─nvme0n1p2        /boot
└─nvme0n1p3        crypto_LUKS
  └─nvme0n1p3_crypt
    ├─broken--vg-root   /
    └─broken--vg-swap_1 [SWAP]

Wichtig: /boot ist bei verschlüsselten Debian-Installationen oft klein. Proxmox-Kernel und Initramfs-Dateien können sehr groß werden. Vor der Installation sollte ausreichend Platz geschaffen werden.


2. Alten Debian-Kernel bereinigen

Aktuell laufenden Kernel prüfen:

uname -r

Installierte Kerneldateien prüfen:

ls -lh /boot
dpkg -l 'linux-image*' 'linux-headers*' | awk '/^ii|^rc/ {print $1, $2, $3}'

Wenn mehrere alte Kernel vorhanden sind, zuerst in den neuesten Debian-Kernel booten und danach alte Kernel entfernen.

Beispiel:

apt purge linux-image-6.12.85+deb13-amd64
apt autoremove
update-grub
df -h /boot

Falls das Paket anders heißt, den Besitzer der Kerneldatei ermitteln:

dpkg -S /boot/vmlinuz-6.12.85+deb13-amd64

Dann das exakt passende Paket entfernen, zum Beispiel:

apt purge linux-image-6.12.85+deb13-amd64-unsigned

3. Proxmox Repository einrichten

Benötigte Werkzeuge installieren:

apt install -y wget ca-certificates

Proxmox-Keyring für Debian 13 / Trixie herunterladen:

wget https://enterprise.proxmox.com/debian/proxmox-archive-keyring-trixie.gpg -O /usr/share/keyrings/proxmox-archive-keyring.gpg

Hash prüfen:

sha256sum /usr/share/keyrings/proxmox-archive-keyring.gpg

Erwarteter Hash:

136673be77aba35dcce385b28737689ad64fd785a797e57897589aed08db6e45  /usr/share/keyrings/proxmox-archive-keyring.gpg

Repository-Datei anlegen:

cat > /etc/apt/sources.list.d/proxmox.sources <<'EOF'
Types: deb
URIs: http://download.proxmox.com/debian/pve
Suites: trixie
Components: pve-no-subscription
Signed-By: /usr/share/keyrings/proxmox-archive-keyring.gpg
EOF

Paketlisten aktualisieren:

apt update

4. Proxmox-Kernel installieren

Zuerst nur den Proxmox-Kernel installieren, noch nicht das komplette Proxmox-Paket:

apt install proxmox-default-kernel

Danach GRUB prüfen:

update-grub

Erwartet wird ein Eintrag ähnlich zu:

Found linux image: /boot/vmlinuz-7.0.2-2-pve
Found initrd image: /boot/initrd.img-7.0.2-2-pve

5. Initramfs-Größe bei kleinem /boot reduzieren

Bei kleinem /boot kann der Proxmox-Initramfs zu groß werden. In diesem Setup wurde die Kompression von zstd auf xz geändert.

Datei öffnen:

nano /etc/initramfs-tools/initramfs.conf

Diese Zeile ändern:

COMPRESS=zstd

zu:

COMPRESS=xz

MODULES=most wurde bewusst beibehalten, damit Netzwerk- und Storage-Treiber für Dropbear/LUKS möglichst robust im Initramfs enthalten bleiben.

Proxmox-Initramfs neu bauen:

update-initramfs -c -k 7.0.2-2-pve

Prüfen:

df -h /boot
ls -lh /boot/initrd.img-7.0.2-2-pve

6. In den Proxmox-Kernel booten

reboot

Nach dem Reboot wie gewohnt per Dropbear entsperren.

Danach prüfen:

uname -r

Erwartet:

7.0.2-2-pve

7. Debian-Kernel entfernen

Nachdem der Proxmox-Kernel erfolgreich gebootet wurde, können die Debian-Kernel entfernt werden. Das schafft Platz in /boot und verhindert spätere Initramfs-Probleme.

Paketnamen ermitteln:

dpkg -S /boot/vmlinuz-6.12.86+deb13-amd64

Falls Debian-Headers den Kernel wieder installieren wollen, Kernel und Headers zusammen entfernen:

apt purge linux-image-6.12.86+deb13-amd64-unsigned linux-headers-6.12.86+deb13-amd64 linux-headers-6.12.86+deb13-common linux-headers-amd64

Danach Proxmox-Initramfs erneut bauen:

update-initramfs -c -k 7.0.2-2-pve
update-grub
df -h /boot

8. ZFS-DKMS entfernen und Proxmox-ZFS verwenden

Falls Debian-ZFS bereits installiert war, insbesondere zfs-dkms, sollte zfs-dkms entfernt werden. Proxmox nutzt eigene Kernel und passende ZFS-Pakete.

ZFS-Pakete prüfen:

dpkg -l 'zfs*' 'spl*' | awk '/^ii|^rc/ {print $1, $2, $3}'

zfs-dkms entfernen:

apt purge zfs-dkms

Falls dabei zfsutils-linux und zfs-zed entfernt wurden, diese aus dem Proxmox-Repository wieder installieren:

apt install zfsutils-linux zfs-zed

Prüfen:

dpkg -l 'zfs*' 'proxmox*' 'pve*' | awk '/^ii|^rc/ {print $1, $2, $3}'

Gewünscht:

ii proxmox-default-kernel ...
ii proxmox-kernel-7.0 ...
ii pve-firmware ...
ii zfs-zed ...
ii zfsutils-linux ...

Nicht gewünscht:

ii zfs-dkms ...

9. Proxmox VE installieren

apt install proxmox-ve postfix open-iscsi chrony

Bei der Postfix-Abfrage kann für einen normalen Proxmox-Host gewählt werden:

Local only

Entfernungen während der Installation sind normal:


10. Hostname-Auflösung für Proxmox korrigieren

Proxmox benötigt, dass der Hostname auf eine echte Nicht-Loopback-IP zeigt. Wenn pve-cluster nicht startet und im Journal steht:

Unable to resolve node name 'byte-me' to a non-loopback IP address

dann muss /etc/hosts angepasst werden.

Datei öffnen:

nano /etc/hosts

Beispiel:

127.0.0.1       localhost
10.100.3.13     byte-me.localdomain byte-me

# The following lines are desirable for IPv6 capable hosts
::1     localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Wichtig: Der Hostname byte-me darf nicht auf 127.0.0.1 oder 127.0.1.1 zeigen.

Prüfen:

hostname
hostname -f
getent hosts byte-me

Erwartung:

byte-me
byte-me.localdomain
10.100.3.13 byte-me.localdomain byte-me

Proxmox-Dienste neu starten:

systemctl reset-failed pve-cluster
systemctl restart pve-cluster
systemctl restart pvedaemon pveproxy pvestatd

Status prüfen:

systemctl status pve-cluster pvedaemon pveproxy pvestatd --no-pager

Erwartung:

pve-cluster   active
pvedaemon     active
pveproxy      active
pvestatd      active

Weboberfläche:

https://10.100.3.13:8006/

Verschlüsselten lokalen VM-Storage einrichten

Zusätzlich wurde eine zweite NVMe als verschlüsselter Storage für VMs und ISO-Dateien eingerichtet. In diesem Beispiel:

/dev/nvme1n1

Achtung: Die folgenden Schritte löschen die Zielplatte. Nicht auf dem Systemlaufwerk ausführen.


11. Zielplatte prüfen

lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL
wipefs -n /dev/nvme1n1

Beispiel:

nvme1n1 953,9G disk SKHynix_HFS001TEJ9X162N

12. Partition erstellen

fdisk /dev/nvme1n1

In fdisk:

g
n
1


y
w

Danach prüfen:

lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS,MODEL /dev/nvme1n1

Erwartung:

nvme1n1       953,9G disk
└─nvme1n1p1   953,9G part

13. LUKS2 sicher formatieren

cryptsetup luksFormat \
  --type luks2 \
  --label crypt-vmstore \
  --cipher aes-xts-plain64 \
  --key-size 512 \
  --hash sha512 \
  --pbkdf argon2id \
  --iter-time 5000 \
  --verify-passphrase \
  /dev/nvme1n1p1

Eine lange Passphrase verwenden. Keine kurzen Passwörter, keine Projektnamen, keine Tastaturmuster.


14. LUKS öffnen

In diesem Beispiel wurde der Mapper exploding_disk genannt.

cryptsetup open /dev/nvme1n1p1 exploding_disk

Prüfen:

lsblk /dev/nvme1n1p1

Erwartung:

nvme1n1p1
└─exploding_disk

15. LUKS-Header sichern

Ein LUKS-Header-Backup ist wichtig. Wenn der Header beschädigt wird, sind die Daten sonst praktisch verloren.

mkdir -p /root/luks-header-backups
chmod 700 /root/luks-header-backups

cryptsetup luksHeaderBackup /dev/nvme1n1p1 \
  --header-backup-file /root/luks-header-backups/nvme1n1p1-crypt-vmstore-header.img

chmod 400 /root/luks-header-backups/nvme1n1p1-crypt-vmstore-header.img

Dieses Header-Backup sollte zusätzlich extern und sicher verschlüsselt gesichert werden. Ohne passenden Header und ohne Passphrase sind die Daten nicht wiederherstellbar.


16. LVM auf dem entschlüsselten Device erstellen

pvcreate /dev/mapper/exploding_disk
vgcreate vg_vmstore /dev/mapper/exploding_disk

Prüfen:

vgs
pvs

Erwartung:

VG           VSize    VFree
vg_vmstore   953,85g  953,85g

17. LVM-thin für VM-Disks erstellen

lvcreate -L 850G -T vg_vmstore/vmdata

Prüfen:

lvs

Beispiel:

LV       VG          Attr       LSize
vmdata   vg_vmstore  twi-a-tz-- 850,00g

18. ISO-Storage erstellen

lvcreate -L 60G -n iso vg_vmstore
mkfs.ext4 -L pve-iso /dev/vg_vmstore/iso
mkdir -p /mnt/pve/iso-store

UUID anzeigen:

blkid /dev/vg_vmstore/iso

Beispiel:

/dev/vg_vmstore/iso: LABEL="pve-iso" UUID="0f1496f7-d7ad-4e97-9243-cff799d44e5e" BLOCK_SIZE="4096" TYPE="ext4"

19. ISO-Storage in /etc/fstab eintragen

nano /etc/fstab

Eintrag ergänzen:

UUID=0f1496f7-d7ad-4e97-9243-cff799d44e5e /mnt/pve/iso-store ext4 defaults,noatime 0 2

Systemd neu laden und Mount testen:

systemctl daemon-reload
mount -a
df -h /mnt/pve/iso-store

20. Storage in Proxmox eintragen

LVM-thin für VM- und Container-Disks:

pvesm add lvmthin vmstore \
  --vgname vg_vmstore \
  --thinpool vmdata \
  --content images,rootdir

Directory-Storage für ISOs und Container-Templates:

pvesm add dir iso-store \
  --path /mnt/pve/iso-store \
  --content iso,vztmpl

Status prüfen:

pvesm status

Beispiel:

Name             Type     Status     Total (KiB)      Used (KiB) Available (KiB)        %
iso-store         dir     active        61611820            2084        58447624    0.00%
local             dir     active       477545328       154494456       298719404   32.35%
vmstore       lvmthin     active       891289600               0       891289600    0.00%

Optional: Automatisches Entsperren des zweiten LUKS-Storage

Ein Keyfile auf dem verschlüsselten Root-Dateisystem ist komfortabel, reduziert aber die Trennung der Sicherheitsstufen. Ohne Keyfile braucht ein Angreifer bei Diebstahl beider Platten Root-Passphrase und Storage-Passphrase. Mit Keyfile reicht die Root-Passphrase, weil danach das Keyfile verfügbar ist.

Variante A: Maximale Trennung

Kein Keyfile verwenden. Nach jedem Boot manuell entsperren:

cryptsetup open /dev/nvme1n1p1 exploding_disk
vgchange -ay vg_vmstore
mount -a

VM-Autostart für VMs auf diesem Storage sollte dann deaktiviert werden, weil der Storage nach dem Boot zunächst fehlt.

Variante B: Komfortabler Auto-Unlock nach Root-Unlock

Keyfile auf verschlüsseltem Root-Dateisystem erstellen:

mkdir -p /root/luks-keys
chmod 700 /root/luks-keys

dd if=/dev/random of=/root/luks-keys/exploding_disk.key bs=64 count=1
chmod 400 /root/luks-keys/exploding_disk.key

Keyfile zu LUKS hinzufügen:

cryptsetup luksAddKey /dev/nvme1n1p1 /root/luks-keys/exploding_disk.key

LUKS-UUID anzeigen:

blkid /dev/nvme1n1p1

/etc/crypttab bearbeiten:

nano /etc/crypttab

Eintrag ergänzen:

exploding_disk UUID=DEINE-LUKS-UUID-HIER /root/luks-keys/exploding_disk.key luks

Systemd neu laden:

systemctl daemon-reload

Ceph-Zukunftsplanung

Für später geplante drei Proxmox-Nodes mit Ceph gilt:

Geplanter späterer Ablauf:

1. PVE 2 und PVE 3 installieren
2. Cluster erstellen
3. Dediziertes 10G-Netz für Ceph konfigurieren
4. Ceph installieren
5. Auf allen Nodes freie NVMe als verschlüsselte Ceph-OSD einrichten
6. Ceph-Pool erstellen
7. VM-Storage auf Ceph RBD anlegen
8. VMs vom lokalen vmstore auf Ceph migrieren
9. Lokalen LUKS/LVM-Storage auf nvme1n1 entfernen
10. nvme1n1 als Ceph-OSD neu verwenden

Mit nur einem Node ist Ceph nicht sinnvoll redundant. Für produktive Ceph-Nutzung sollten mindestens drei Proxmox-Nodes vorhanden sein.


Wichtige Checks nach der Einrichtung

dpkg --audit
df -h /boot
uname -r
pvesm status
lsblk -o NAME,SIZE,TYPE,FSTYPE,MOUNTPOINTS
vgs
lvs
systemctl status pve-cluster pvedaemon pveproxy pvestatd --no-pager

Erwartung:

Kernel: 7.0.2-2-pve
/boot nicht voll
pve-cluster active
pvedaemon active
pveproxy active
pvestatd active
vmstore active
iso-store active

Zusammenfassung

Linux - Proxmox

Neue Seite

Proxmox iSCSI SAN mit tgt und LVM

Diese Dokumentation beschreibt die bisher eingerichtete iSCSI-SAN-Konfiguration mit tgt auf der SAN-Seite und dem ersten Proxmox-Node pve_1.


1. Ziel der Konfiguration

Es wurde ein iSCSI-Target auf der SAN-Seite bereitgestellt und auf dem ersten Proxmox-Node als Shared-LVM-Storage eingebunden.

Der Aufbau ist:

tgt SAN
  └─ iSCSI Target: iqn.2026-02-25.linux.proxmox.pve:cleopatra
       └─ LUN: /dev/mapper/cleopatra
            └─ pve_1 sieht LUN als /dev/sda
                 └─ /dev/sda1 als LVM Physical Volume
                      └─ VG: vg_vmstore
                           └─ Proxmox Storage: lvm-cleopatra

2. Namensschema

Objekt Name Beschreibung
SAN-Host big-bunda Server mit tgt als iSCSI-Target-Dienst
Erster Proxmox-Node pve_1 / byte-me Erster Proxmox-VE-Node, der das iSCSI-LUN nutzt
iSCSI Portal 10.99.255.1:3260 IP-Adresse und Port des iSCSI-Targets
iSCSI Target IQN iqn.2026-02-25.linux.proxmox.pve:cleopatra Eindeutiger iSCSI-Targetname für den Storage Cleopatra
tgt Backing Store /dev/mapper/cleopatra Blockdevice, das vom tgt-Target als LUN exportiert wird
Proxmox iSCSI Storage-ID iscsi-cleopatra Proxmox-Eintrag für die reine iSCSI-Verbindung
LVM Volume Group vg_vmstore LVM-VG auf der iSCSI-LUN
Proxmox LVM Storage-ID lvm-cleopatra Nutzbarer VM-Storage in Proxmox

3. Wichtige Entscheidung: Warum zwei Proxmox-Storage-Einträge?

In Proxmox gibt es zwei Einträge, weil iSCSI und LVM zwei verschiedene Schichten sind.

iscsi-cleopatra = Verbindung zum iSCSI-Target
lvm-cleopatra   = eigentlicher VM-Storage auf der LVM Volume Group

Der iSCSI-Eintrag selbst hat content none, weil Proxmox dort keine VM-Disks direkt ablegt. Die eigentlichen VM-Disks werden später auf lvm-cleopatra erstellt.


4. tgt SAN-Konfiguration

Auf dem SAN-Host big-bunda wird tgt als iSCSI-Target-Daemon genutzt. Das exportierte Blockdevice ist:

/dev/mapper/cleopatra

Beispiel für die tgt-Konfiguration:

<target iqn.2026-02-25.linux.proxmox.pve:cleopatra>
    driver iscsi

    <backing-store /dev/mapper/cleopatra>
        lun 1
        device-type disk
        vendor_id "linux"
        product_id "iscsi disk"
        product_rev "0001"
        scsi_sn "cleopatra-lun1"
        write-cache off
    </backing-store>

    # zugriff nur für erlaubte proxmox-nodes
    initiator-address 10.99.255.X

    # initiator-iqn von pve_1 / byte-me
    # prüfen auf dem pve-node mit:
    # cat /etc/iscsi/initiatorname.iscsi
    initiator-name iqn.1993-08.org.debian:01:1ebae4d041e

    # mutual chap
    # incominguser = initiator authentifiziert sich beim target
    # outgoinguser = target authentifiziert sich beim initiator
    incominguser pve-byteme "SECRET_NICHT_IN_DOKU_EINTRAGEN"
    outgoinguser tgt-cleopatra "ANDERES_SECRET_NICHT_IN_DOKU_EINTRAGEN"
</target>

Wichtig: Die CHAP-Secrets dürfen nicht identisch sein. Für incominguser und outgoinguser müssen unterschiedliche Passwörter verwendet werden.

Für dieses Setup wurden kurze, kompatible CHAP-Secrets verwendet, da längere Secrets im konkreten tgt/Open-iSCSI-Stack Probleme verursachen können. Empfohlen sind zufällige Secrets mit 12 bis 16 Zeichen.

openssl rand -hex 8

5. Proxmox pve_1 / byte-me: iSCSI-Verbindung

Auf dem ersten Proxmox-Node wurde das Target über das Portal 10.99.255.1 eingebunden.

Proxmox-Storage-Eintrag:

iscsi: iscsi-cleopatra
        portal 10.99.255.1
        target iqn.2026-02-25.linux.proxmox.pve:cleopatra
        content none

Das iSCSI-LUN ist auf pve_1 sichtbar als:

/dev/sda

Der stabile Gerätepfad lautet:

/dev/disk/by-path/ip-10.99.255.1:3260-iscsi-iqn.2026-02-25.linux.proxmox.pve:cleopatra-lun-1

6. Entfernen des alten VMFS-Datastores

Auf dem iSCSI-LUN befand sich vorher ein VMware VMFS6-Dateisystem. Dieses wurde entfernt, da die Daten nicht mehr benötigt wurden.

Vorheriger Zustand:

sda
└─sda1 VMFS_volume_member 6

Verwendete Befehle:

lun="/dev/disk/by-path/ip-10.99.255.1:3260-iscsi-iqn.2026-02-25.linux.proxmox.pve:cleopatra-lun-1"

findmnt "$lun"
findmnt "${lun}-part1"

wipefs -a "${lun}-part1"
wipefs -a "$lun"
partprobe "$lun"
udevadm settle

Achtung: Diese Befehle löschen Dateisystem- und Partitionssignaturen. Sie dürfen nur verwendet werden, wenn die Daten auf der LUN wirklich gelöscht werden dürfen.


7. Neue GPT-Partition für LVM

Nach dem Entfernen der VMFS-Signaturen wurde eine neue GPT-Partitionstabelle mit einer LVM-Partition erstellt.

parted -s "$lun" mklabel gpt
parted -s "$lun" mkpart primary 1MiB 100%
parted -s "$lun" set 1 lvm on
partprobe "$lun"
udevadm settle

Danach war die LUN sauber vorbereitet:

NAME   FSTYPE FSVER LABEL UUID FSAVAIL FSUSE% MOUNTPOINTS
sda
└─sda1

8. LVM auf iSCSI erstellen

Auf der neuen Partition wurde ein LVM Physical Volume und danach eine Volume Group erstellt.

pvcreate "${lun}-part1"
vgcreate vg_vmstore "${lun}-part1"

Ergebnis:

PV                 VG          Fmt  Attr PSize    PFree
/dev/sda1          vg_vmstore  lvm2 a--  <6,99t   <6,99t

VG          #PV #LV #SN Attr   VSize    VFree
vg_vmstore   1   0   0 wz--n-  <6,99t   <6,99t

Wichtig für weitere Nodes: pvcreate und vgcreate wurden nur einmal auf pve_1 ausgeführt. Auf weiteren Proxmox-Nodes darf das nicht erneut ausgeführt werden. Weitere Nodes sollen die bestehende VG nur erkennen.


9. Proxmox LVM Storage

Der Proxmox-LVM-Storage wurde als shared Storage angelegt.

pvesm add lvm lvm-cleopatra \
  --vgname vg_vmstore \
  --content images,rootdir \
  --shared 1

Finaler Proxmox-Storage-Eintrag:

lvm: lvm-cleopatra
        vgname vg_vmstore
        content images,rootdir
        shared 1

Damit ist lvm-cleopatra der nutzbare Storage für VM-Disks und Container-Rootdisks.


10. Aktueller Proxmox-Status

Aktueller Status auf pve_1:

Name                   Type     Status     Total (KiB)      Used (KiB) Available (KiB)        %
iscsi-cleopatra       iscsi     active               0               0               0    0.00%
lvm-cleopatra           lvm     active      7503060992               0      7503060992    0.00%

Dass iscsi-cleopatra eine Größe von 0 zeigt, ist normal. Dieser Eintrag ist nur die Verbindung zum iSCSI-Target. Die nutzbare Kapazität wird bei lvm-cleopatra angezeigt.


11. Finaler Aufbau in Proxmox

iscsi: iscsi-cleopatra
        portal 10.99.255.1
        target iqn.2026-02-25.linux.proxmox.pve:cleopatra
        content none

lvm: lvm-cleopatra
        vgname vg_vmstore
        content images,rootdir
        shared 1

12. Hinweise für spätere Proxmox-Nodes

Bei weiteren Proxmox-Nodes muss nur die iSCSI-Verbindung eingerichtet werden. Die LVM-Struktur existiert bereits.

Auf jedem weiteren Node prüfen:

cat /etc/iscsi/initiatorname.iscsi
pvesm status
pvs
vgs

Jeder Node braucht einen eigenen eindeutigen Initiator-IQN. Dieser muss auf der SAN-Seite in der tgt-Konfiguration als initiator-name erlaubt werden.

Nicht erneut ausführen:

pvcreate
vgcreate
wipefs
parted mklabel

Diese Befehle würden die bestehende LVM-Struktur beziehungsweise die Daten auf dem Shared Storage beschädigen. Storage verzeiht so etwas nicht, es merkt sich das nur in Form von Datenverlust.


13. Sicherheitsregeln


14. Optionales späteres Cleanup

Aktuell heißt die LVM Volume Group noch:

vg_vmstore

Für ein konsequentes Namensschema könnte sie später optional umbenannt werden:

vgrename vg_vmstore vg_cleopatra
pvesm set lvm-cleopatra --vgname vg_cleopatra

Danach wäre das Schema vollständig konsistent:

iscsi-cleopatra
lvm-cleopatra
vg_cleopatra

Dieser Schritt ist optional. Der Storage funktioniert bereits mit vg_vmstore.


15. Kurzfassung

SAN big-bunda:
  Target: iqn.2026-02-25.linux.proxmox.pve:cleopatra
  Backing Store: /dev/mapper/cleopatra
  Portal: 10.99.255.1:3260
  Auth: Mutual CHAP
  ACL: initiator-address + initiator-name

Proxmox pve_1 / byte-me:
  iSCSI Storage: iscsi-cleopatra
  LVM VG: vg_vmstore
  Proxmox VM Storage: lvm-cleopatra
  Shared: ja
  Content: images,rootdir
Linux - Proxmox

Proxmox - VMs von einem ausgefallenen Proxmox-Host übernehmen

Wenn ein Proxmox-Host im Cluster ausgefallen bzw. offline ist, können die darauf registrierten VMs einem anderen Host zugeordnet und dort gestartet werden, sofern deren Storage weiterhin erreichbar ist.

Wichtig: Der ausgefallene Host muss wirklich ausgeschaltet bzw. vom Cluster getrennt sein. Er darf nicht gleichzeitig wieder starten, während seine VMs bereits auf einem anderen Host laufen. Andernfalls besteht die Gefahr von gleichzeitig laufenden VMs und beschädigten Dateisystemen.

Storage beachten: Liegen die VM-Festplatten ausschließlich auf dem lokalen Storage des ausgefallenen Hosts, können die VMs nicht einfach auf einem anderen Host gestartet werden. Die folgende Vorgehensweise setzt voraus, dass die benötigten VM-Datenträger vom Ziel-Host erreichbar sind, beispielsweise über Ceph, NFS oder anderes Shared Storage.

VMs des ausgefallenen Hosts anzeigen

Die VM-Konfigurationen des ausgefallenen Hosts befinden sich im Proxmox Cluster Filesystem und können direkt angezeigt werden:

ls -1 /etc/pve/nodes/<AUSGEFALLENER-HOST>/qemu-server/

Beispiel:

ls -1 /etc/pve/nodes/pve01/qemu-server/

Die Ausgabe enthält die VM-Konfigurationen anhand ihrer VM-ID:

101.conf
102.conf
103.conf
104.conf

Alle VMs auf einen anderen Host übernehmen

Um alle VMs des ausgefallenen Hosts dem Ziel-Host zuzuordnen, werden deren Konfigurationsdateien in das Verzeichnis des Ziel-Hosts verschoben:

mv /etc/pve/nodes/<AUSGEFALLENER-HOST>/qemu-server/*.conf /etc/pve/nodes/<ZIEL-HOST>/qemu-server/

Beispiel:

mv /etc/pve/nodes/pve01/qemu-server/*.conf /etc/pve/nodes/pve02/qemu-server/

Die VMs erscheinen anschließend in Proxmox unter dem neuen Host. Dabei werden nur die VM-Konfigurationen verschoben. Die eigentlichen VM-Datenträger bleiben auf dem vorhandenen Storage.

Einzelne VM übernehmen

Soll nur eine bestimmte VM übernommen werden, kann deren Konfigurationsdatei einzeln verschoben werden:

mv /etc/pve/nodes/<AUSGEFALLENER-HOST>/qemu-server/<VMID>.conf /etc/pve/nodes/<ZIEL-HOST>/qemu-server/

Beispiel:

mv /etc/pve/nodes/pve01/qemu-server/101.conf /etc/pve/nodes/pve02/qemu-server/

VMs starten

Nach der Übernahme können die benötigten VMs über die Proxmox-Weboberfläche oder über die Shell gestartet werden:

qm start <VMID>

Beispiel:

qm start 101

Bei HA-verwalteten VMs

VMs, die durch Proxmox HA verwaltet werden, sollten nicht manuell über das Cluster-Dateisystem verschoben werden. In diesem Fall übernimmt Proxmox HA die Wiederherstellung und den Neustart der VM auf einem verfügbaren Cluster-Node.

Nach der Übernahme prüfen

Vor dem Start größerer VM-Mengen sollte geprüft werden, ob Storage und Netzwerk auf dem Ziel-Host korrekt verfügbar sind.

qm status <VMID>

Zusätzlich sollte kontrolliert werden, ob alle in der VM-Konfiguration verwendeten Storages auf dem Ziel-Host verfügbar sind.

Linux - Proxmox

Proxmox VE ohne Subscription: kostenlose Repositories nutzen und Subscription-Hinweis ausblenden

Proxmox VE 9: No-Subscription-Popup deaktivieren

Fix

Betroffene Datei:

/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js

Datei öffnen:

nano /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js

Mit Strg + W nach folgendem Text suchen:

checked_command: function (orig_cmd)

Den vollständigen checked_command-Block löschen und an exakt derselben Position durch diesen Block ersetzen. Die vorhandene Einrückung beibehalten:

        checked_command: function (orig_cmd) {
            orig_cmd();
        },

Wichtig: Der komplette bisherige Inhalt der Funktion wird entfernt. Es bleibt kein alter Subscription-Code unterhalb von orig_cmd() stehen.

Danach speichern, Nano schließen und pveproxy neu starten:

systemctl restart pveproxy

Anschließend die Proxmox-Weboberfläche mit Strg + F5 neu laden.

Quick-Befehl

Wenn du die Datei nicht manuell bearbeiten willst, als root direkt in der Terminal-Session ausführen:

python3 -c '
from pathlib import Path
import re, shutil

p = Path("/usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js")
s = p.read_text()

m = re.search(r"^([ \t]*)checked_command:\s*function\s*\(orig_cmd\)\s*\{", s, re.M)
if not m:
    raise SystemExit("FEHLER: checked_command wurde nicht gefunden")

indent = m.group(1)
start = m.start()
brace = s.find("{", m.start())
depth = 0
end = None

for i in range(brace, len(s)):
    if s[i] == "{":
        depth += 1
    elif s[i] == "}":
        depth -= 1
        if depth == 0:
            end = i + 1
            break

if end is None:
    raise SystemExit("FEHLER: Ende von checked_command wurde nicht gefunden")

if end >= len(s) or s[end] != ",":
    raise SystemExit("FEHLER: Abschließendes Komma von checked_command wurde nicht gefunden")
end += 1

replacement = (
    f"{indent}checked_command: function (orig_cmd) {{\n"
    f"{indent}    orig_cmd();\n"
    f"{indent}}},"
)

if s[start:end] == replacement:
    print("Patch ist bereits sauber gesetzt")
else:
    shutil.copy2(p, str(p) + ".bak")
    p.write_text(s[:start] + replacement + s[end:])
    print("Patch angewendet; Backup: " + str(p) + ".bak")
' && systemctl restart pveproxy && systemctl is-active pveproxy

Der Befehl übernimmt die vorhandene Einrückung der checked_command-Zeile und ersetzt den vollständigen Funktionsblock. Danach steht der Block an derselben Position wie vorher und ist sauber wie folgt eingerückt:

        checked_command: function (orig_cmd) {
            orig_cmd();
        },

Der alte Subscription-Code wird vollständig entfernt. Wenn tatsächlich eine Änderung vorgenommen wird, wird die vorherige Datei als proxmoxlib.js.bak gesichert.

Wenn am Ende active ausgegeben wird, läuft pveproxy wieder.

Änderung rückgängig machen

cp -a /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js.bak /usr/share/javascript/proxmox-widget-toolkit/proxmoxlib.js && systemctl restart pveproxy && systemctl is-active pveproxy

Nach Updates

Ein Update von proxmox-widget-toolkit kann proxmoxlib.js ersetzen. Wenn das Popup danach wieder erscheint, den Quick-Befehl erneut ausführen. Vor einem erneuten tatsächlichen Patch wird die dann aktuelle Paketversion wieder als .bak gesichert.

Dienst installation

OpenVPN - Server

Dienst installation

OpenVPN Site-to-Site Server für Phoenix-Contact-Router: zwei sichere Serverprofile

Stand: 27. Mai 2026

Ziel dieses Dokuments: Es beschreibt zwei getrennte OpenVPN-Serverinstanzen auf einem Linux-Server.

  1. modern.conf: maximal gehärtet für aktuelle OpenVPN-Clients.
  2. compat.conf: weiterhin sehr sicher, aber bewusst kompatibler für ältere Industrie-/Phoenix-Contact-Geräte.

Beide Varianten sind für geroutete Site-to-Site-Tunnel gedacht. Das ist für dein Szenario richtig: Ein Phoenix-Contact-Router verbindet sich als OpenVPN-Client, teilt sein lokales LAN über den Tunnel und erreicht umgekehrt das zentrale LAN.

1. Quellen und Grundannahmen

Dieses Dokument orientiert sich vor allem an der offiziellen OpenVPN-2.6-Dokumentation, der OpenVPN-Hardening-Dokumentation, der OpenVPN-Dokumentation zu client-spezifischen Regeln und Easy-RSA. Wikipedia wird nur für die allgemeine Einordnung von OpenVPN als TLS-basiertes VPN über UDP/TCP erwähnt. Produktiv relevante Optionen werden aus der OpenVPN-Dokumentation abgeleitet, weil Wikipedia nicht deine Firewall administriert. Zum Glück.

Quelle Wofür sie hier verwendet wird
OpenVPN 2.6 Manual Direktiven wie server, data-ciphers, tls-groups, tls-crypt-v2, ccd-exclusive, iroute, keepalive, explicit-exit-notify.
OpenVPN Hardening Wiki Begründung für tls-version-min 1.2 und allgemeine TLS-Härtung.
OpenVPN Client-Specific Rules Nutzung von client-config-dir, festen Client-IP-Adressen und Firewall-Policy je Client.
OpenVPN Compression Wiki Begründung, warum Kompression deaktiviert bleibt.
Easy-RSA Dokumentation PKI-, CA-, Zertifikats- und CRL-Erstellung.
Wikipedia: OpenVPN Allgemeine Einordnung: OpenVPN ist ein freies VPN über verschlüsselte TLS-Verbindungen und kann UDP oder TCP verwenden.

2. Zielbild

Wir bauen kein Layer-2-Bridging mit TAP. Wir bauen geroutete Layer-3-Tunnel mit TUN. Das ist stabiler, besser kontrollierbar und für Site-to-Site-Netze sauberer.

Zentrales LAN:
  10.10.0.0/24

OpenVPN modern:
  Instanz:          modern.conf
  Port:             UDP/1194
  TUN-Interface:    tun0
  VPN-Netz:         10.255.0.0/24
  Beispielclient:   phoenix-modern-werk1
  Client-VPN-IP:    10.255.0.10
  LAN hinter Client:192.168.101.0/24

OpenVPN kompatibel:
  Instanz:          compat.conf
  Port:             UDP/1195
  TUN-Interface:    tun1
  VPN-Netz:         10.254.0.0/24
  Beispielclient:   phoenix-legacy-werk1
  Client-VPN-IP:    10.254.0.10
  LAN hinter Client:192.168.201.0/24

Wichtig: Die LAN-Netze hinter den Phoenix-Geräten müssen eindeutig sein. Zwei Standorte mit demselben Netz, etwa zweimal 192.168.0.0/24, machen sauberes Routing unmöglich. Dann brauchst du 1:1-NAT oder eine Umnummerierung. Umnummerierung ist meistens die bessere Lösung, auch wenn Menschen dabei gern so tun, als wäre ein IP-Plan eine Naturkatastrophe.

3. Warum zwei getrennte OpenVPN-Instanzen?

Eine einzelne OpenVPN-Instanz kann theoretisch moderne und ältere Clients bedienen. Praktisch verwässerst du damit die starke Konfiguration, weil du Legacy-Fallbacks zulässt. Besser ist die Trennung:

Instanz Ziel Sicherheitsentscheidung
modern.conf Neue Clients mit aktueller OpenVPN-/TLS-Unterstützung. Starke Kurvengruppen, AEAD-Cipher, tls-crypt-v2, keine CBC-Fallbacks.
compat.conf Ältere Phoenix-/Industrieclients, die moderne Optionen nicht zuverlässig können. Weiterhin TLS 1.2+, Zertifikate, CRL, tls-auth, keine Kompression, aber zusätzlicher CBC-Fallback.

Wenn du zwei öffentliche IP-Adressen hast, kannst du beide Instanzen auf Port 1194 betreiben und jeweils an eine andere IP binden. Wenn du nur eine öffentliche IP hast, müssen die Ports verschieden sein. Dieses Dokument nutzt daher 1194 für modern und 1195 für kompatibel.

4. Sicherheitsprinzipien

  1. Ein Zertifikat pro Standort. Kein Zertifikat wird mehrfach verwendet.
  2. Ein Common Name pro Standort. Der Dateiname in ccd entspricht genau dem Common Name des Zertifikats.
  3. Ein Standortnetz pro Zertifikat. Das wird mit iroute gebunden.
  4. ccd-exclusive. Clients ohne CCD-Datei werden abgewiesen.
  5. Keine Kompression. Kompression in verschlüsselten Tunneln ist ein historischer Fußtritt in Richtung Sicherheitsproblem.
  6. CRL aktiv. Verlorene Geräte werden über Zertifikatssperrlisten entfernt.
  7. Keine überlappenden Netze. Routing braucht Eindeutigkeit. Überraschend, ich weiß.
  8. Firewall erzwingt die Policy. OpenVPN routet, die Firewall entscheidet.
  9. CA-Key nicht dauerhaft auf dem VPN-Server. Idealerweise liegt die CA auf einer separaten Admin-Maschine.

5. Installation auf Debian/Ubuntu

Die folgenden Befehle sind für Debian/Ubuntu-artige Systeme geschrieben. Bei RHEL/Rocky/Alma sind Paketmanager und Gruppennamen anders. Das Konzept bleibt gleich.

sudo apt update
sudo apt install -y openvpn easy-rsa nftables
sudo systemctl enable --now nftables

5.1 Befehl-für-Befehl-Erklärung

Befehl Was macht er? Warum so? Alternativen
sudo apt update Aktualisiert die lokale Paketliste des Systems. Ohne aktuelle Paketliste installiert apt eventuell alte oder nicht auffindbare Pakete. apt-get update ist die ältere, skriptfreundliche Variante. Auf RHEL-Systemen wäre es z. B. dnf makecache.
sudo apt install -y openvpn easy-rsa nftables Installiert OpenVPN, Easy-RSA und nftables. openvpn ist der VPN-Dienst, easy-rsa erzeugt Zertifikate und CRLs, nftables stellt die Firewall. Statt nftables könntest du iptables, ufw oder eine externe Firewall verwenden. Für Server ist nftables heute die sauberere Wahl.
sudo systemctl enable --now nftables Aktiviert nftables dauerhaft und startet es sofort. enable sorgt für Start beim Booten, --now startet den Dienst direkt. Man kann Firewallregeln auch über ein zentrales Firewall-System setzen. Lokal ist trotzdem sinnvoll, weil ein VPN-Server niemals blind vertrauen sollte.

6. Verzeichnisstruktur anlegen

sudo install -d -m 750 /etc/openvpn/server/modern
sudo install -d -m 750 /etc/openvpn/server/modern/ccd
sudo install -d -m 750 /etc/openvpn/server/compat
sudo install -d -m 750 /etc/openvpn/server/compat/ccd
Befehl Was macht er? Warum so? Alternativen
sudo install -d -m 750 /etc/openvpn/server/modern Legt das Verzeichnis für die moderne Instanz an. install -d erstellt ein Verzeichnis. -m 750 setzt Rechte: Besitzer darf alles, Gruppe darf lesen/betreten, andere dürfen nichts. mkdir -p plus chmod geht auch, ist aber ein Befehl mehr. Weil wir hier angeblich Ordnung wollen, nutzen wir install.
sudo install -d -m 750 /etc/openvpn/server/modern/ccd Legt das CCD-Verzeichnis der modernen Instanz an. Hier liegen später Dateien wie phoenix-modern-werk1. OpenVPN liest daraus feste IPs und iroute-Einträge. Man könnte ccd auch woanders speichern, aber unterhalb der Instanz bleibt es verständlich.
sudo install -d -m 750 /etc/openvpn/server/compat Legt das Verzeichnis für die kompatible Instanz an. Trennung verhindert, dass Schlüssel, CRLs oder CCD-Dateien versehentlich zwischen modern und legacy vermischt werden. Eine gemeinsame Struktur wäre möglich, aber schlechter auditierbar.
sudo install -d -m 750 /etc/openvpn/server/compat/ccd Legt das CCD-Verzeichnis der kompatiblen Instanz an. Auch ältere Geräte bekommen feste Zuordnungen über CN, feste VPN-IP und entferntes LAN. Keine gute Alternative, wenn du pro Zertifikat ein Netz sauber binden willst.

7. PKI-Strategie

Für die sauberste Trennung nutzt jede Instanz ihre eigene PKI:

Das ist administrativ etwas mehr Arbeit, aber sicherer. Wenn ein Legacy-Bereich kompromittiert wird, ist der moderne Bereich nicht automatisch dieselbe Vertrauensdomäne. Man könnte eine gemeinsame Root-CA mit getrennten Intermediate-CAs bauen. Für viele kleinere Betriebsumgebungen sind zwei getrennte Easy-RSA-PKIs einfacher.

8. Moderne PKI erstellen

make-cadir ~/easy-rsa-modern
cd ~/easy-rsa-modern

./easyrsa init-pki
./easyrsa build-ca
./easyrsa build-server-full modern-server nopass
./easyrsa build-client-full phoenix-modern-werk1 nopass
./easyrsa gen-crl

8.1 Erklärung der Befehle

Befehl Was macht er? Warum so? Optionen / Alternativen
make-cadir ~/easy-rsa-modern Erstellt ein Arbeitsverzeichnis mit Easy-RSA-Dateien. Die PKI wird getrennt von der Systemkonfiguration vorbereitet. Das ist übersichtlicher und reduziert versehentliches Überschreiben. Du kannst auch ein anderes Verzeichnis nehmen, z. B. /root/easy-rsa-modern. Idealerweise liegt die CA aber nicht dauerhaft auf dem VPN-Server.
cd ~/easy-rsa-modern Wechselt in das neue Easy-RSA-Verzeichnis. Die nächsten Befehle müssen im Easy-RSA-Verzeichnis ausgeführt werden. Mit absoluten Pfaden geht es auch, ist aber unleserlicher.
./easyrsa init-pki Initialisiert die PKI-Struktur unter pki/. Erzeugt Verzeichnisse und Datenbanken für Zertifikate, Schlüssel und Seriennummern. init-pki löscht/initialisiert die PKI. Auf produktiven CAs also nicht gedankenlos wiederholen. Menschen lieben destruktive Befehle mit harmlosen Namen.
./easyrsa build-ca Erzeugt die Certificate Authority. Die CA signiert später Server- und Client-Zertifikate. Der private CA-Key ist das wichtigste Geheimnis. build-ca nopass wäre möglich, ist aber für eine CA nicht empfehlenswert. Eine CA sollte mit Passwort geschützt sein.
./easyrsa build-server-full modern-server nopass Erzeugt und signiert ein Serverzertifikat mit Namen modern-server. nopass sorgt dafür, dass OpenVPN beim Dienststart kein Passwort interaktiv abfragen muss. Ohne nopass wird der private Schlüssel verschlüsselt. Das ist sicherer, aber für automatischen Serverstart unpraktisch. Alternativ kann man mit askpass arbeiten.
./easyrsa build-client-full phoenix-modern-werk1 nopass Erzeugt ein Clientzertifikat für den Standort phoenix-modern-werk1. Der Name wird zum Common Name und muss später exakt als CCD-Dateiname verwendet werden. Für Benutzerlaptops wäre ein passwortgeschützter Key besser. Für Phoenix-Router ist nopass oft nötig, weil das Gerät beim Booten kein Passwort eintippen kann.
./easyrsa gen-crl Erzeugt die Zertifikatssperrliste crl.pem. OpenVPN prüft damit, ob ein Clientzertifikat gesperrt wurde. Nach jedem revoke musst du gen-crl erneut ausführen und die neue CRL auf den Server kopieren.

9. Moderne PKI-Dateien auf den Server kopieren

sudo install -m 644 pki/ca.crt /etc/openvpn/server/modern/pki-ca.crt
sudo install -m 644 pki/issued/modern-server.crt /etc/openvpn/server/modern/modern-server.crt
sudo install -m 600 pki/private/modern-server.key /etc/openvpn/server/modern/modern-server.key
sudo install -m 644 pki/crl.pem /etc/openvpn/server/modern/crl.pem

sudo install -d -m 750 /etc/openvpn/server/modern/pki
sudo install -d -m 750 /etc/openvpn/server/modern/pki/issued
sudo install -d -m 700 /etc/openvpn/server/modern/pki/private

sudo cp pki/ca.crt /etc/openvpn/server/modern/pki/ca.crt
sudo cp pki/issued/modern-server.crt /etc/openvpn/server/modern/pki/issued/modern-server.crt
sudo cp pki/private/modern-server.key /etc/openvpn/server/modern/pki/private/modern-server.key
sudo cp pki/crl.pem /etc/openvpn/server/modern/crl.pem

sudo chown -R root:root /etc/openvpn/server/modern
sudo chmod 600 /etc/openvpn/server/modern/pki/private/modern-server.key
sudo chmod 644 /etc/openvpn/server/modern/crl.pem

Hinweis: In der Praxis brauchst du die ersten vier install-Zeilen nicht, wenn du direkt die strukturierte Variante darunter verwendest. Ich zeige sie trotzdem, weil install -m verständlich macht, dass Dateirechte kein Deko-Element sind. Für die eigentliche Dokumentation ist die strukturierte Variante mit pki/ca.crt, pki/issued und pki/private die relevante.

9.1 Erklärung der wichtigsten Kopierbefehle

Befehl Was macht er? Warum so? Optionen / Alternativen
sudo install -d -m 750 .../pki Erstellt ein PKI-Unterverzeichnis für die Instanz. Die Pfade in modern.conf sind dadurch klar und getrennt. Du könntest die Dateien direkt unter /etc/openvpn/server/modern ablegen, aber Unterordner sind sauberer.
sudo install -d -m 700 .../pki/private Erstellt das private Schlüsselverzeichnis mit strengeren Rechten. Private Schlüssel sollen nicht von normalen Benutzern lesbar sein. 750 wäre möglich, aber 700 ist konservativer.
sudo cp pki/ca.crt ... Kopiert das öffentliche CA-Zertifikat. Der Server braucht die CA, um Clientzertifikate zu prüfen. Die CA-Datei ist öffentlich. Sie ist nicht geheim. Der CA-Key ist geheim und gehört nicht hierher.
sudo cp pki/issued/modern-server.crt ... Kopiert das Serverzertifikat. OpenVPN präsentiert dieses Zertifikat den Clients. Der Name ist frei wählbar, muss aber zur Config passen.
sudo cp pki/private/modern-server.key ... Kopiert den privaten Server-Key. Ohne privaten Schlüssel kann der Server sein Zertifikat nicht nutzen. Der Key muss geschützt werden. Verlust oder Diebstahl bedeutet: Serverzertifikat neu erzeugen und altes sperren.
sudo cp pki/crl.pem ... Kopiert die CRL. OpenVPN kann gesperrte Clientzertifikate ablehnen. Die CRL muss erneuert werden, nachdem Zertifikate widerrufen wurden.
sudo chown -R root:root ... Setzt root als Besitzer. Systemkonfiguration soll nicht normalen Benutzern gehören. Je nach Distribution kann eine OpenVPN-Gruppe genutzt werden. Das sollte bewusst passieren, nicht zufällig.
sudo chmod 600 ...modern-server.key Nur root darf den privaten Schlüssel lesen/schreiben. Minimiert Risiko durch lokale Benutzer. Wenn OpenVPN nach dem Start Rechte abgibt, liest es den Key vorher. 600 ist daher meistens korrekt.
sudo chmod 644 ...crl.pem Macht die CRL für den OpenVPN-Prozess lesbar. Nach Rechteabgabe an nobody muss OpenVPN die CRL weiterhin lesen können. 640 root:nogroup ist restriktiver, setzt aber korrekte Gruppe voraus.

10. Moderne TLS-Crypt-v2-Schlüssel erzeugen

sudo openvpn --genkey tls-crypt-v2-server /etc/openvpn/server/modern/tc2-server.key

sudo openvpn   --tls-crypt-v2 /etc/openvpn/server/modern/tc2-server.key   --genkey tls-crypt-v2-client /etc/openvpn/server/modern/phoenix-modern-werk1-tc2.key

sudo chmod 600 /etc/openvpn/server/modern/tc2-server.key
sudo chmod 600 /etc/openvpn/server/modern/phoenix-modern-werk1-tc2.key
Befehl Was macht er? Warum so? Optionen / Alternativen
openvpn --genkey tls-crypt-v2-server ... Erzeugt den serverseitigen Schlüssel für tls-crypt-v2. tls-crypt-v2 schützt den TLS-Kontrollkanal und erlaubt client-spezifische Schlüssel. tls-crypt ist kompatibler, nutzt aber einen gemeinsamen statischen Schlüssel. tls-auth ist noch kompatibler, schützt aber nicht die Vertraulichkeit des Kontrollkanals.
openvpn --tls-crypt-v2 ... --genkey tls-crypt-v2-client ... Erzeugt einen client-spezifischen tls-crypt-v2-Schlüssel. Jeder Client bekommt einen eigenen Zusatzschlüssel. Das passt zur Idee: ein Zertifikat, ein Standort, ein Schlüssel. Du kannst optionale Metadaten in Clientkeys einbetten. Für eine klare Betriebsdokumentation reicht der einfache Weg.
chmod 600 ... Schränkt den Zugriff auf die Schlüsseldateien ein. TLS-Crypt-Schlüssel sind geheim. Wer sie hat, kommt zwar ohne Zertifikat nicht rein, aber er reduziert deine Schutzschicht. 640 mit dedizierter Gruppe ist möglich, wenn du sauber mit Gruppenrechten arbeitest.

11. Moderne OpenVPN-Serverkonfiguration

Datei:

/etc/openvpn/server/modern.conf
port 1194
proto udp4
dev tun0
topology subnet

server 10.255.0.0 255.255.255.0

client-config-dir /etc/openvpn/server/modern/ccd
ccd-exclusive

route 192.168.101.0 255.255.255.0

ca /etc/openvpn/server/modern/pki/ca.crt
cert /etc/openvpn/server/modern/pki/issued/modern-server.crt
key /etc/openvpn/server/modern/pki/private/modern-server.key

dh none
tls-groups X25519:secp256r1:X448:secp384r1

tls-server
remote-cert-tls client
verify-client-cert require

tls-version-min 1.2
data-ciphers AES-256-GCM:CHACHA20-POLY1305:AES-128-GCM
auth SHA256
tls-cert-profile preferred

tls-crypt-v2 /etc/openvpn/server/modern/tc2-server.key

allow-compression no
crl-verify /etc/openvpn/server/modern/crl.pem

keepalive 10 60
explicit-exit-notify 1
persist-key
persist-tun

user nobody
group nogroup

verb 3

11.1 Jede Zeile der modernen Konfiguration erklärt

Zeile Was macht sie? Warum diese Wahl? Alternativen / Hinweise
port 1194 OpenVPN lauscht auf UDP-Port 1194. 1194 ist der offiziell/typisch verwendete OpenVPN-Port. Im betrieblichen Umfeld ist der Default sinnvoll, weil Firewalls, Doku und Monitoring dadurch klar bleiben. Du könntest den Port ändern, etwa auf 443 oder 11940, um den Dienst etwas zu verschleiern. Das ist aber keine echte Sicherheit und führt im Betrieb gern zu Verwirrung.
proto udp4 Nutzt UDP über IPv4 als Transportprotokoll. UDP ist für VPN-Tunnel meist besser als TCP, weil TCP-over-TCP bei Paketverlust schlecht performt. udp4 erzwingt IPv4. Alternativen: udp für IPv4/IPv6 je nach System, tcp-server für TCP, tcp4-server für TCP nur IPv4. TCP nur verwenden, wenn UDP nicht möglich ist.
dev tun0 Erstellt oder nutzt das TUN-Interface tun0. TUN ist Layer 3 und ideal für Routing zwischen Netzen. Der feste Name erleichtert Firewallregeln. Alternative: dev tun lässt OpenVPN automatisch wählen. dev tap wäre Layer-2-Bridging und für dieses Site-to-Site-Konzept nicht empfehlenswert.
topology subnet Vergibt Client-IP-Adressen wie in einem normalen Subnetz. Damit kann ein Client z. B. 10.255.0.10/24 bekommen. Das ist verständlicher als alte /30-Paarungen. Alternative: topology net30 für sehr alte Clients. Für moderne und die meisten industriellen Geräte ist subnet sinnvoller.
server 10.255.0.0 255.255.255.0 Aktiviert Servermodus und definiert das VPN-Transfernetz. Der Server bekommt üblicherweise 10.255.0.1, Clients erhalten Adressen aus 10.255.0.0/24. Dieses Netz ist nur für den Tunnel. Du kannst jedes private, nicht überlappende Netz verwenden, z. B. 10.8.0.0/24. Wichtig ist: keine Überschneidung mit LANs.
client-config-dir /etc/openvpn/server/modern/ccd Aktiviert client-spezifische Konfigurationsdateien. Jeder Standort bekommt eine Datei mit seinem Common Name. Darin stehen feste VPN-IP und iroute. Der Pfad ist frei wählbar. Trennung je Instanz verhindert Fehlzuordnungen.
ccd-exclusive Nur Clients mit vorhandener CCD-Datei dürfen verbinden. Das ist für Site-to-Site wichtig: Kein Zertifikat soll ohne explizite Netzzuordnung online gehen. Ohne diese Option könnten gültige Zertifikate ohne definierte Policy verbinden. Das ist für Betriebsnetze unsauber.
route 192.168.101.0 255.255.255.0 Fügt auf Serverseite eine Route zum entfernten Standortnetz hinzu. Der Linux-Kernel gibt Pakete zu diesem Netz an OpenVPN. OpenVPN braucht zusätzlich das passende iroute in der CCD-Datei. Für jeden Standort kommt eine weitere route-Zeile dazu. Bei vielen Standorten kann Routenmanagement über Scripts oder Konfigurationsmanagement erfolgen.
ca .../ca.crt Pfad zum CA-Zertifikat. Damit prüft der Server, ob Clientzertifikate von der richtigen CA signiert wurden. Die CA-Datei ist öffentlich. Der private CA-Key darf nicht auf den VPN-Server.
cert .../modern-server.crt Pfad zum Serverzertifikat. Der Server weist sich damit gegenüber Clients aus. Der Name ist frei, muss aber mit der Datei existieren.
key .../modern-server.key Pfad zum privaten Server-Schlüssel. Der private Schlüssel gehört zum Serverzertifikat und muss geheim bleiben. Rechte: üblicherweise 600 root:root.
dh none Deaktiviert klassische statische DH-Parameter. Wir nutzen moderne elliptische Gruppen über tls-groups. Das ist für neue Clients zeitgemäßer. Für Legacy-Clients kann stattdessen dh /pfad/dh.pem nötig sein. Genau darum gibt es die zweite Instanz.
tls-groups X25519:secp256r1:X448:secp384r1 Legt erlaubte TLS-Gruppen/Kurven in Prioritätsreihenfolge fest. X25519 ist eine moderne Standardwahl. secp256r1 ist sehr kompatibel. X448 und secp384r1 bieten weitere starke Optionen. Alternative: Option weglassen und Bibliotheksdefaults nutzen. Oder ecdh-curve prime256v1 für ältere Clients, aber das ist weniger modern.
tls-server Setzt diese Instanz explizit in die TLS-Serverrolle. Im Servermodus meist implizit, aber lesbar und eindeutig. Normalerweise reicht server .... Explizit schadet hier nicht.
remote-cert-tls client Prüft, ob die Gegenstelle ein Client-Zertifikat verwendet. Verhindert, dass Zertifikate mit falscher Rolle akzeptiert werden. Nicht weglassen. Alternativ gibt es spezifischere Prüfungen über verify-x509-name, die zusätzlich genutzt werden können.
verify-client-cert require Erzwingt ein gültiges Clientzertifikat. Ohne gültiges Zertifikat keine Verbindung. Das ist zentral für Site-to-Site. Der Default ist in vielen Fällen bereits require, aber explizit dokumentiert ist besser.
tls-version-min 1.2 Erlaubt nur TLS 1.2 oder höher. TLS 1.0/1.1 gehören nicht mehr in ein neues betriebliches VPN. Noch härter wäre TLS 1.3-only, aber das bricht viele Clients. Für OpenVPN-Umgebungen ist TLS 1.2+ der realistische harte Mindeststandard.
data-ciphers AES-256-GCM:CHACHA20-POLY1305:AES-128-GCM Liste erlaubter Datenkanal-Cipher. Nur moderne AEAD-Cipher. Kein CBC-Fallback in der modernen Instanz. Du könntest AES-128-GCM vor CHACHA20-POLY1305 setzen, wenn AES-Hardwarebeschleunigung erwartet wird. Wichtig: kein BF-CBC.
auth SHA256 Setzt SHA-256 als HMAC-Digest für relevante Kontroll-/Nicht-AEAD-Fälle. SHA-256 ist ein robuster Standard. Bei AEAD-Ciphers ist der Datenkanal bereits integriert authentifiziert. Alternativen: SHA384 oder SHA512. SHA1 nicht neu verwenden.
tls-cert-profile preferred Verlangt brauchbare Zertifikats-/Signaturalgorithmen. Schiebt alte SHA1-/zu schwache Zertifikate aus dem Weg. Bei sehr alten Geräten kann legacy nötig sein. In diesem Dokument bleiben auch ältere Geräte bei neu erzeugten Zertifikaten, daher preferred.
tls-crypt-v2 .../tc2-server.key Aktiviert TLS-Crypt-v2 für den Kontrollkanal. Beste Wahl für moderne Setups: client-spezifische Zusatzschlüssel, Schutz vor Scans und Müllpaketen. Alternativen: tls-crypt oder tls-auth. Für Legacy nutzen wir bewusst tls-auth.
allow-compression no Verbietet Kompression. Kompression in verschlüsselten Tunneln erhöht Angriffsfläche und ist für Site-to-Site meist unnötig. Nicht compress setzen. Auch nicht „nur testweise“, diese berühmten letzten Worte der IT.
crl-verify .../crl.pem Aktiviert Prüfung der Zertifikatssperrliste. Verlorene oder kompromittierte Clients können gesperrt werden. Ohne CRL musst du im Ernstfall CA oder Serverpolicy gröber ändern. CRL ist Pflicht für sauberen Betrieb.
keepalive 10 60 Sendet Keepalive-Pings und erkennt tote Verbindungen. Gerade Mobilfunk-/Industrieverbindungen profitieren davon. Alternative: manuell ping und ping-restart setzen. keepalive ist die einfache Form.
explicit-exit-notify 1 Informiert UDP-Clients beim Serverstopp/Restart. Clients reconnecten sauberer und schneller. Nur für UDP sinnvoll. Bei TCP nicht verwenden.
persist-key Liest Schlüssel bei internen Restarts nicht neu. Hilft, wenn OpenVPN nach Start Rechte reduziert. Weglassen ist möglich, aber weniger robust.
persist-tun Hält das TUN-Interface bei Restarts offen. Verhindert unnötiges Interface-Flapping und reduziert Routingstörungen. Weglassen führt zu mehr Bewegung im Netzwerkstack. Betrieb mag keine Bewegung.
user nobody OpenVPN läuft nach Initialisierung als Benutzer nobody. Reduziert Schaden bei Prozesskompromittierung. Manche Distributionen nutzen einen dedizierten Benutzer wie openvpn. Das ist sogar besser, wenn vorhanden.
group nogroup OpenVPN wechselt in die Gruppe nogroup. Passend zu nobody auf Debian/Ubuntu. Auf RHEL heißt die Gruppe oft nobody. Mit getent group nogroup prüfen.
verb 3 Setzt das Log-Level auf normalen Betriebswert. Genug Informationen für Betrieb, nicht zu viel Rauschen. Für Fehlersuche kurzzeitig verb 4 oder verb 5. Nicht dauerhaft extrem hoch loggen.

12. Moderne CCD-Datei für einen Phoenix-Standort

Datei:

/etc/openvpn/server/modern/ccd/phoenix-modern-werk1
ifconfig-push 10.255.0.10 255.255.255.0
iroute 192.168.101.0 255.255.255.0
push "route 10.10.0.0 255.255.255.0"
Zeile Was macht sie? Warum diese Wahl? Alternativen / Hinweise
ifconfig-push 10.255.0.10 255.255.255.0 Weist diesem Client immer die feste VPN-IP 10.255.0.10 zu. Feste Tunnel-IP macht Firewallregeln, Logs und Betrieb nachvollziehbar. Bei topology net30 wäre die Syntax anders: dann braucht man ein IP-Paar. Mit topology subnet ist diese Form korrekt.
iroute 192.168.101.0 255.255.255.0 Sagt OpenVPN intern: Dieses entfernte LAN gehört zu genau diesem Client. Das ist der Kern deines „ein Zertifikat = ein Netzwerk“-Modells. Ohne iroute kennt der Linux-Kernel vielleicht die Route, aber OpenVPN weiß nicht, zu welchem Client das Paket soll.
push "route 10.10.0.0 255.255.255.0" Sendet dem Client eine Route zum zentralen LAN. Der Phoenix-Router weiß dadurch, dass 10.10.0.0/24 über den VPN-Tunnel erreichbar ist. Viele Industrieclients ignorieren Push-Routen oder konfigurieren Remote Networks im Webinterface. Dann diese Route dort manuell eintragen.

13. Moderne Instanz aktivieren

sudo install -m 640 modern.conf /etc/openvpn/server/modern.conf
sudo openvpn --config /etc/openvpn/server/modern.conf --test-crypto
sudo systemctl enable --now openvpn-server@modern
sudo systemctl status openvpn-server@modern
Befehl Was macht er? Warum so? Optionen / Alternativen
sudo install -m 640 modern.conf /etc/openvpn/server/modern.conf Kopiert die Konfigurationsdatei an den OpenVPN-Serverpfad. 640 verhindert unnötige Lesbarkeit durch normale Benutzer. Du kannst auch direkt mit einem Editor in /etc/openvpn/server/modern.conf schreiben.
sudo openvpn --config ... --test-crypto Testet kryptografische Optionen der Konfiguration. Findet Fehler bei Ciphers, TLS-Gruppen oder Pfaden früher. Zusätzlich sudo openvpn --config ... im Vordergrund starten, wenn du detaillierte Fehlermeldungen brauchst.
sudo systemctl enable --now openvpn-server@modern Aktiviert und startet die systemd-Instanz modern. OpenVPN lädt dabei /etc/openvpn/server/modern.conf. Nur starten: systemctl start .... Nur Boot-Aktivierung: systemctl enable ....
sudo systemctl status openvpn-server@modern Zeigt Dienststatus und letzte Logs. Erste Prüfung, ob der Dienst läuft. Detaillierter: journalctl -u openvpn-server@modern -f.

14. Kompatible PKI erstellen

make-cadir ~/easy-rsa-compat
cd ~/easy-rsa-compat

./easyrsa init-pki
./easyrsa build-ca
./easyrsa build-server-full compat-server nopass
./easyrsa build-client-full phoenix-legacy-werk1 nopass
./easyrsa gen-crl

Die Befehle entsprechen der modernen PKI. Der wichtige Unterschied ist nicht Easy-RSA selbst, sondern die spätere OpenVPN-Konfiguration. Auch ältere Geräte sollen neue, saubere Zertifikate bekommen. Altgerät heißt nicht: altes SHA1-Zertifikat aus irgendeinem Export von 2014 behalten, nur weil es noch blinkt.

15. Kompatible PKI-Dateien kopieren

sudo install -d -m 750 /etc/openvpn/server/compat/pki
sudo install -d -m 750 /etc/openvpn/server/compat/pki/issued
sudo install -d -m 700 /etc/openvpn/server/compat/pki/private

sudo cp pki/ca.crt /etc/openvpn/server/compat/pki/ca.crt
sudo cp pki/issued/compat-server.crt /etc/openvpn/server/compat/pki/issued/compat-server.crt
sudo cp pki/private/compat-server.key /etc/openvpn/server/compat/pki/private/compat-server.key
sudo cp pki/crl.pem /etc/openvpn/server/compat/crl.pem

sudo chown -R root:root /etc/openvpn/server/compat
sudo chmod 600 /etc/openvpn/server/compat/pki/private/compat-server.key
sudo chmod 644 /etc/openvpn/server/compat/crl.pem

16. Kompatible TLS-Auth- und DH-Dateien erzeugen

sudo openvpn --genkey secret /etc/openvpn/server/compat/ta.key
sudo chmod 600 /etc/openvpn/server/compat/ta.key

sudo openssl dhparam -out /etc/openvpn/server/compat/dh.pem 3072
sudo chmod 644 /etc/openvpn/server/compat/dh.pem
Befehl Was macht er? Warum so? Optionen / Alternativen
sudo openvpn --genkey secret .../ta.key Erzeugt einen statischen Schlüssel für tls-auth. tls-auth ist älter und breiter unterstützt als tls-crypt-v2. Es schützt den Kontrollkanal per HMAC gegen fremde Pakete. tls-crypt wäre besser, wenn das Altgerät es unterstützt. tls-crypt-v2 ist modern, aber auf älteren Appliances oft nicht verfügbar.
sudo chmod 600 .../ta.key Beschränkt Zugriff auf den statischen Schlüssel. Der Key ist geheim und wird auch auf dem Client gebraucht. Bei Verteilung an Phoenix-Geräte sicher übertragen, z. B. per SFTP, nicht per E-Mail im Klartext, weil wir keine Museumsführung durch Sicherheitsfehler brauchen.
sudo openssl dhparam -out .../dh.pem 3072 Erzeugt klassische Diffie-Hellman-Parameter mit 3072 Bit. Das ist kompatibler als moderne ECDH-only-Konfigurationen und bleibt trotzdem stark genug für eine Legacy-Instanz. 4096 ist möglich, aber langsamer. 2048 ist kompatibler/schneller, aber weniger großzügig bei der Sicherheitsmarge.
sudo chmod 644 .../dh.pem Macht die DH-Parameter lesbar. DH-Parameter sind nicht geheim; OpenVPN muss sie lesen können. 640 mit passender Gruppe ist möglich.

17. Kompatible OpenVPN-Serverkonfiguration

Datei:

/etc/openvpn/server/compat.conf
port 1195
proto udp4
dev tun1
topology subnet

server 10.254.0.0 255.255.255.0

client-config-dir /etc/openvpn/server/compat/ccd
ccd-exclusive

route 192.168.201.0 255.255.255.0

ca /etc/openvpn/server/compat/pki/ca.crt
cert /etc/openvpn/server/compat/pki/issued/compat-server.crt
key /etc/openvpn/server/compat/pki/private/compat-server.key

dh /etc/openvpn/server/compat/dh.pem

tls-server
remote-cert-tls client
verify-client-cert require

tls-version-min 1.2
data-ciphers AES-256-GCM:AES-128-GCM:AES-256-CBC
data-ciphers-fallback AES-256-CBC
cipher AES-256-CBC
auth SHA256
tls-cert-profile preferred

tls-auth /etc/openvpn/server/compat/ta.key 0

allow-compression no
crl-verify /etc/openvpn/server/compat/crl.pem

keepalive 10 60
explicit-exit-notify 1
persist-key
persist-tun

user nobody
group nogroup

verb 3

17.1 Jede Zeile der kompatiblen Konfiguration erklärt

Zeile Was macht sie? Warum diese Wahl? Alternativen / Hinweise
port 1195 OpenVPN lauscht für die kompatible Instanz auf UDP-Port 1195. Da 1194 bereits von modern.conf genutzt wird, braucht diese Instanz einen zweiten Port. Mit zwei öffentlichen IPs könnte auch diese Instanz Port 1194 nutzen, gebunden an eine andere IP. Auf einer IP müssen Ports verschieden sein.
proto udp4 Nutzt UDP über IPv4. Auch für Altgeräte ist UDP normalerweise zuverlässiger für VPN als TCP. TCP ist möglich, wenn Firewalls UDP blockieren. Dann proto tcp4-server, aber nur als Notlösung.
dev tun1 Nutzt ein eigenes TUN-Interface für die kompatible Instanz. Getrennt von tun0, damit Firewallregeln klar zwischen modern und legacy unterscheiden können. Automatische Benennung mit dev tun ist bequemer, aber schlechter für eindeutige Firewallregeln.
topology subnet Verwendet normales Subnetzmodell für Client-IP-Zuweisung. Auch viele ältere Geräte kommen damit klar. Es macht CCDs lesbarer. Wenn ein sehr alter Client Probleme macht, kann topology net30 nötig sein. Dann müssen ifconfig-push-Zeilen angepasst werden.
server 10.254.0.0 255.255.255.0 Definiert das VPN-Transfernetz der kompatiblen Instanz. Getrennt vom modernen Netz, damit Logs, Routen und Firewallregeln eindeutig bleiben. Jedes private, nicht überlappende Netz ist möglich. Nicht dasselbe Netz wie bei modern verwenden.
client-config-dir /etc/openvpn/server/compat/ccd Aktiviert CCD-Dateien für Legacy-Clients. Auch ältere Geräte werden pro Common Name fest gebunden. Pfad frei wählbar, aber pro Instanz getrennt halten.
ccd-exclusive Nur Clients mit CCD-Datei dürfen verbinden. Gerade im Legacy-Bereich willst du keine Überraschungsclients. Weglassen wäre bequemer und schlechter. Bequemlichkeit ist der natürliche Feind des Audits.
route 192.168.201.0 255.255.255.0 Serverroute zum Legacy-Standortnetz. Der Kernel gibt Pakete zu diesem Netz an die OpenVPN-Instanz. Für jeden Legacy-Standort eigene Route hinzufügen.
ca .../ca.crt CA der kompatiblen PKI. Getrennte Vertrauensdomäne für Legacy-Clients. Du könntest eine gemeinsame CA nutzen, aber getrennt ist sauberer.
cert .../compat-server.crt Serverzertifikat der kompatiblen Instanz. Separates Zertifikat verhindert Vermischung mit modern. Name frei wählbar, Datei muss existieren.
key .../compat-server.key Privater Serverkey der kompatiblen Instanz. Muss geheim bleiben. Rechte restriktiv setzen.
dh /etc/openvpn/server/compat/dh.pem Nutzt klassische Diffie-Hellman-Parameter. Das ist für ältere Clients zuverlässiger als ECDH-only mit dh none. Moderner wäre dh none plus ecdh-curve oder tls-groups. Für Legacy ist klassische DH robuster.
tls-server Setzt TLS-Serverrolle. Eindeutige Rollenverteilung. Meist durch server implizit.
remote-cert-tls client Prüft Client-Zertifikatrolle. Auch alte Geräte sollen korrekte Clientzertifikate nutzen. Nicht weglassen.
verify-client-cert require Erzwingt Clientzertifikate. Ohne Zertifikat keine Verbindung. Benutzer/Passwort zusätzlich möglich, aber für Router-Site-to-Site normalerweise nicht nötig.
tls-version-min 1.2 Erlaubt TLS 1.2 oder höher. Das ist die harte Untergrenze. TLS 1.0/1.1 wird nicht zugelassen. Wenn ein Gerät nur TLS 1.0 kann, sollte es ersetzt oder isoliert werden. Eine produktive Betriebsverbindung mit TLS 1.0 ist keine „Kompatibilität“, sondern eine Altlast mit Netzwerkkabel.
data-ciphers AES-256-GCM:AES-128-GCM:AES-256-CBC Erlaubt moderne GCM-Cipher und als letzten Fallback AES-256-CBC. GCM bleibt bevorzugt. CBC wird nur zugelassen, damit ältere Clients ohne moderne Cipher-Aushandlung funktionieren. Härter wäre ohne CBC. Genau das macht modern.conf. Schwächer wäre Blowfish/Camellia/3DES. Nicht verwenden.
data-ciphers-fallback AES-256-CBC Fallback für Clients ohne moderne Data-Cipher-Negotiation. Manche ältere OpenVPN-Clients können die neue Cipher-Aushandlung nicht sauber. Dieser Fallback hält sie kompatibel. Nur setzen, wenn du solche Geräte wirklich brauchst. Nicht in der modernen Instanz verwenden.
cipher AES-256-CBC Legacy-Hinweis für ältere Clients/Kompatibilität. Ältere Clientkonfigurationen erwarten oft explizit cipher AES-256-CBC. In neueren OpenVPN-Versionen ist data-ciphers maßgeblich. Auf modernen Clients besser GCM nutzen. Diese Zeile ist bewusst Legacy-Futter.
auth SHA256 HMAC-Digest SHA-256. Für CBC und tls-auth relevant und sicherer als SHA1. SHA384/SHA512 möglich, aber SHA256 ist breit kompatibel und sicher.
tls-cert-profile preferred Erzwingt moderne Zertifikatsqualität. Wir erzeugen neue Zertifikate, also gibt es keinen Grund für schwache Zertifikate. Falls ein Altgerät alte SHA1-Zertifikate zwingend braucht, wäre legacy möglich. Das ist hier bewusst nicht empfohlen.
tls-auth .../ta.key 0 Aktiviert HMAC-Schutz für den TLS-Kontrollkanal. 0 ist die Server-Key-Direction. Sehr kompatibel und trotzdem nützlich gegen fremde Pakete/Scans. Besser wäre tls-crypt, falls der Client es kann. tls-crypt-v2 ist die moderne Variante, aber weniger kompatibel.
allow-compression no Verbietet Kompression. Auch für Legacy bleibt Kompression aus. Kompatibilität ist kein Freifahrtschein für bekannte Angriffsflächen. Nicht compress setzen. Wenn ein Altclient Kompression erzwingt, muss dessen Konfig angepasst werden.
crl-verify .../crl.pem Prüft gesperrte Zertifikate. Geräte können verloren gehen oder ausgemustert werden. CRL macht Sperrung sauber möglich. CRL regelmäßig nach Widerruf erneuern.
keepalive 10 60 Prüft Tunnel-Lebendigkeit. Gerade Mobilfunkrouter brauchen robuste Reconnect-Erkennung. Werte können erhöht werden, wenn instabile Mobilfunkstrecken sonst zu hektisch reconnecten.
explicit-exit-notify 1 Informiert UDP-Clients beim Serverstopp. Sauberere Reconnects. Bei TCP nicht verwenden.
persist-key Hält Key-Material über interne Restarts. Hilft bei Dienste-Reconnects nach Rechteabgabe. Weglassen selten sinnvoll.
persist-tun Hält TUN-Interface offen. Stabilere Routen und weniger Flapping. Weglassen erzeugt mehr Interface-Bewegung.
user nobody Reduziert Prozessrechte. Besser als dauerhaft root. Dedizierter OpenVPN-Benutzer ist noch sauberer, wenn Distribution ihn anbietet.
group nogroup Reduziert Gruppenzugehörigkeit. Debian/Ubuntu-kompatibel. Auf anderen Distributionen prüfen.
verb 3 Normales Log-Level. Geeignet für Dauerbetrieb. verb 4 zur Fehlersuche, danach zurück.

18. Kompatible CCD-Datei für einen Phoenix-Standort

Datei:

/etc/openvpn/server/compat/ccd/phoenix-legacy-werk1
ifconfig-push 10.254.0.10 255.255.255.0
iroute 192.168.201.0 255.255.255.0
push "route 10.10.0.0 255.255.255.0"
Zeile Was macht sie? Warum diese Wahl? Alternativen / Hinweise
ifconfig-push 10.254.0.10 255.255.255.0 Feste VPN-IP für den Legacy-Phoenix. Logs und Firewallregeln bleiben eindeutig. Bei net30 andere Syntax nötig.
iroute 192.168.201.0 255.255.255.0 Bindet das entfernte Legacy-LAN an genau diesen Client. Kern des Site-to-Site-Routings. Pro Client nur die Netze eintragen, die wirklich hinter diesem Router liegen.
push "route 10.10.0.0 255.255.255.0" Teilt dem Client die Route zum zentralen LAN mit. Damit Rückverkehr sauber über den Tunnel geht. Wenn das Phoenix-Gerät Push-Routen ignoriert, Route im Phoenix-Webinterface als Remote Network setzen.

19. Kompatible Instanz aktivieren

sudo install -m 640 compat.conf /etc/openvpn/server/compat.conf
sudo openvpn --config /etc/openvpn/server/compat.conf --test-crypto
sudo systemctl enable --now openvpn-server@compat
sudo systemctl status openvpn-server@compat

20. IP-Forwarding aktivieren

echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-openvpn-routing.conf
sudo sysctl --system
Befehl Was macht er? Warum so? Optionen / Alternativen
echo ... | sudo tee ... Schreibt die Kernel-Option für IPv4-Forwarding in eine persistente sysctl-Datei. Der Server muss Pakete zwischen LAN und VPN weiterleiten dürfen. Temporär ginge sudo sysctl -w net.ipv4.ip_forward=1, ist aber nach Reboot weg.
sudo sysctl --system Lädt alle sysctl-Konfigurationen neu. Die Änderung wird sofort aktiv, ohne Reboot. sudo sysctl -p /etc/sysctl.d/99-openvpn-routing.conf lädt nur diese Datei.

21. Firewall mit nftables

Datei:

/etc/nftables.conf
table inet filter {
  chain input {
    type filter hook input priority filter;
    policy drop;

    iif lo accept
    ct state established,related accept

    udp dport 1194 accept
    udp dport 1195 accept

    ip saddr 10.10.0.0/24 tcp dport 22 accept
  }

  chain forward {
    type filter hook forward priority filter;
    policy drop;

    ct state established,related accept

    iif "tun0" ip saddr 192.168.101.0/24 ip daddr 10.10.0.0/24 accept
    oif "tun0" ip saddr 10.10.0.0/24 ip daddr 192.168.101.0/24 accept

    iif "tun1" ip saddr 192.168.201.0/24 ip daddr 10.10.0.0/24 accept
    oif "tun1" ip saddr 10.10.0.0/24 ip daddr 192.168.201.0/24 accept
  }
}

21.1 Erklärung der Firewall-Regeln

Regel Was macht sie? Warum so? Optionen / Alternativen
table inet filter Erstellt eine Tabelle für IPv4 und IPv6. Auch wenn wir hier IPv4 routen, ist inet ein moderner Standard. table ip filter nur für IPv4 wäre auch möglich.
chain input Regeln für Verkehr zum VPN-Server selbst. Schützt den Serverdienst und Adminzugänge. Input ist nicht Forwarding. Das wird gern verwechselt, weil Netzwerkbegriffe offenbar absichtlich Menschen ärgern.
policy drop Standard: alles verwerfen, was nicht erlaubt ist. Whitelist-Prinzip. Sicherer als nachträglich Dinge zu verbieten. Für Testbetrieb kann accept helfen, produktiv ist drop besser.
iif lo accept Erlaubt Loopback-Verkehr. Lokale Prozesse brauchen Loopback. Nicht entfernen. Viele Dienste erwarten das.
ct state established,related accept Erlaubt Antworten zu bestehenden Verbindungen. Ohne diese Regel würde selbst erlaubter Verkehr keine Rückantwort bekommen. Connection Tracking ist Standard bei zustandsbehafteten Firewalls.
udp dport 1194 accept Erlaubt moderne OpenVPN-Instanz. Notwendig für modern.conf. Wenn du Port änderst, hier mitändern.
udp dport 1195 accept Erlaubt kompatible OpenVPN-Instanz. Notwendig für compat.conf. Bei zwei öffentlichen IPs und gleichem Port anders lösen.
ip saddr 10.10.0.0/24 tcp dport 22 accept Erlaubt SSH nur aus dem zentralen LAN. Adminzugang sollte nicht aus dem Internet offen sein. Besser: dediziertes Adminnetz, Jump Host oder Out-of-band-Management.
chain forward Regeln für weitergeleitete Pakete zwischen VPN und LAN. Site-to-Site-Verkehr läuft durch Forwarding, nicht Input. Hier liegt die eigentliche Zugriffskontrolle.
iif "tun0" ip saddr 192.168.101.0/24 ip daddr 10.10.0.0/24 accept Erlaubt Verkehr vom modernen Phoenix-LAN ins zentrale LAN. Nur das erwartete Quellnetz darf über tun0 kommen. Noch restriktiver: nur einzelne Zielhosts und Ports erlauben.
oif "tun0" ip saddr 10.10.0.0/24 ip daddr 192.168.101.0/24 accept Erlaubt Rück-/Hinverkehr vom zentralen LAN zum modernen Phoenix-LAN. Bidirektionaler Site-to-Site-Zugriff. Für reine Fernwartung kann man Richtung und Ports stärker einschränken.
iif "tun1" ... 192.168.201.0/24 ... Erlaubt Verkehr vom kompatiblen Phoenix-LAN ins zentrale LAN. Legacy-Clients werden getrennt von modern behandelt. Bei höherem Risiko Legacy nur zu einzelnen Wartungsservern zulassen.
oif "tun1" ... 192.168.201.0/24 ... Erlaubt Verkehr vom zentralen LAN zum kompatiblen Phoenix-LAN. Notwendig für bidirektionale Kommunikation. Ports einschränken, wenn möglich.

Regeln laden:

sudo nft -c -f /etc/nftables.conf
sudo systemctl reload nftables
sudo nft list ruleset
Befehl Was macht er? Warum so? Optionen / Alternativen
sudo nft -c -f /etc/nftables.conf Prüft die Syntax, ohne Regeln zu laden. Verhindert, dass du dich mit einer kaputten Firewall selbst aussperrst. Immer vor Reload ausführen. Wirklich.
sudo systemctl reload nftables Lädt die Firewallregeln neu. Aktiviert die neue Policy. restart geht auch, reload ist meist sauberer.
sudo nft list ruleset Zeigt aktive nftables-Regeln. Kontrolle, ob wirklich geladen wurde, was dokumentiert ist. Für gezielte Analyse: nft list table inet filter.

22. Rückrouten im zentralen LAN

Alle Systeme im zentralen LAN müssen wissen, wie sie die Phoenix-Netze erreichen. Am besten setzt du die Routen auf dem zentralen LAN-Gateway.

ip route add 192.168.101.0/24 via 10.10.0.5
ip route add 192.168.201.0/24 via 10.10.0.5

10.10.0.5 ist hier die LAN-IP des OpenVPN-Servers.

Befehl Was macht er? Warum so? Optionen / Alternativen
ip route add 192.168.101.0/24 via 10.10.0.5 Sagt dem LAN-Gateway: Das moderne Phoenix-LAN erreichst du über den OpenVPN-Server. Ohne Rückroute gehen Antworten ins Default-Gateway und verschwinden. Wenn der OpenVPN-Server selbst das Default-Gateway des LANs ist, brauchst du diese Zusatzroute eventuell nicht.
ip route add 192.168.201.0/24 via 10.10.0.5 Dasselbe für das kompatible Phoenix-LAN. Jedes entfernte LAN braucht eine Route. Bei vielen Standorten statische Routen zentral dokumentieren oder per Routingprotokoll lösen.

23. Clientdateien für Phoenix-Geräte

23.1 Moderne Variante

Auf das Phoenix-Gerät bzw. in dessen OpenVPN-Client-Konfiguration gehören:

23.2 Kompatible Variante

Kein NAT auf dem Phoenix aktivieren, wenn echte bidirektionale Site-to-Site-Kommunikation gewünscht ist. NAT nur verwenden, wenn Netze überlappen oder ein Gerät technisch nicht sauber routen kann. NAT macht Fehler nicht weg, es zieht ihnen nur eine Jacke an.

24. Zertifikat widerrufen

Wenn ein Gerät verloren geht, ausgetauscht oder kompromittiert wird:

cd ~/easy-rsa-modern
./easyrsa revoke phoenix-modern-werk1
./easyrsa gen-crl
sudo cp pki/crl.pem /etc/openvpn/server/modern/crl.pem
sudo systemctl reload openvpn-server@modern

Für die kompatible Instanz entsprechend:

cd ~/easy-rsa-compat
./easyrsa revoke phoenix-legacy-werk1
./easyrsa gen-crl
sudo cp pki/crl.pem /etc/openvpn/server/compat/crl.pem
sudo systemctl reload openvpn-server@compat
Befehl Was macht er? Warum so? Optionen / Alternativen
./easyrsa revoke NAME Markiert das Zertifikat als widerrufen. Das Zertifikat soll nicht mehr verbinden dürfen. Name muss dem Zertifikatsnamen/Common Name entsprechen.
./easyrsa gen-crl Erzeugt eine aktualisierte CRL. OpenVPN liest nicht die Easy-RSA-Datenbank, sondern die CRL-Datei. Nach jedem Widerruf erforderlich.
sudo cp pki/crl.pem ... Kopiert die neue CRL in den OpenVPN-Pfad. Nur dann nutzt der Server den neuen Sperrstand. Mit Konfigurationsmanagement automatisieren, wenn viele Standorte existieren.
sudo systemctl reload ... Lädt den OpenVPN-Dienst neu. Damit die neue CRL verwendet wird. Wenn Reload nicht reicht, restart. Kurze Unterbrechung beachten.

25. Wichtige Tests

sudo openvpn --version
sudo openvpn --show-groups
sudo openvpn --show-ciphers
sudo openvpn --show-digests

journalctl -u openvpn-server@modern -f
journalctl -u openvpn-server@compat -f

ip addr show tun0
ip addr show tun1
ip route

ping 10.255.0.10
ping 192.168.101.1
ping 10.254.0.10
ping 192.168.201.1
Befehl Was prüft er? Warum wichtig?
openvpn --version OpenVPN-Version und verwendete Crypto-Bibliothek. Optionen wie tls-groups hängen von Version und Bibliothek ab.
openvpn --show-groups Verfügbare TLS-Gruppen/Kurven. Wichtig für die moderne Instanz.
openvpn --show-ciphers Verfügbare Datenkanal-Cipher. Prüft, ob GCM/ChaCha20/CBC verfügbar sind.
openvpn --show-digests Verfügbare HMAC-Digests. Prüft SHA256-Unterstützung.
journalctl -u ... -f Live-Logs der Instanz. Beste erste Diagnose bei Verbindungsproblemen.
ip addr show tun0/tun1 Existenz und IPs der Tunnelinterfaces. Zeigt, ob OpenVPN Interface und Server-IP gesetzt hat.
ip route Kernel-Routingtabelle. Prüft, ob Routen zu entfernten LANs existieren.
ping ... Basis-Erreichbarkeit. Ping ist kein vollständiger Test, aber ein guter Anfang. Danach Porttests machen.

26. Typische Fehlerbilder

Symptom Wahrscheinliche Ursache Prüfung Lösung
Client verbindet, LAN dahinter ist nicht erreichbar. iroute fehlt oder falscher CCD-Dateiname. journalctl, CCD-Datei, Common Name prüfen. CCD-Datei exakt nach CN benennen und iroute setzen.
Server-LAN erreicht Phoenix, aber Antwort kommt nicht zurück. Rückroute im zentralen Gateway oder Phoenix-LAN fehlt. ip route auf Gateway/Phoenix prüfen. Route zum Gegennetz über OpenVPN/Phoenix setzen.
TLS-Handshake schlägt bei modern fehl. Client unterstützt tls-crypt-v2, tls-groups oder Cipher nicht. Logs mit verb 4, Clientfähigkeiten prüfen. Gerät in kompatible Instanz verschieben.
Kompatibler Client verbindet nicht wegen Cipher. Client nutzt anderen Legacy-Cipher. Clientconfig prüfen: cipher. Client auf AES-256-CBC stellen. Keine Blowfish-Fallbacks neu einführen.
Clientzertifikat wird abgelehnt. Falsche CA, CRL, Zertifikatsrolle oder Ablaufdatum. openssl x509 -in client.crt -text -noout. Neues Zertifikat erzeugen und korrekt verteilen.
OpenVPN startet nicht nach user nobody. Dateien nach Rechteabgabe nicht lesbar, oft CRL. journalctl prüfen. CRL lesbar machen, z. B. chmod 644 crl.pem oder passende Gruppe setzen.

27. Optionen, die bewusst nicht verwendet werden

Option Warum nicht?
duplicate-cn Würde erlauben, dass mehrere Clients dasselbe Zertifikat nutzen. Das zerstört dein Modell „ein Zertifikat = ein Netzwerk“. Nicht verwenden.
client-to-client Würde VPN-Clients untereinander sprechen lassen. Für zentrale Fernwartung meist unnötig und riskant. Nur aktivieren, wenn Standort-zu-Standort-Kommunikation ausdrücklich benötigt wird.
push "redirect-gateway def1" Würde gesamten Internetverkehr des Clients über den VPN-Server schicken. Für Site-to-Site-Fernwartung unnötig.
compress Kompression bleibt aus Sicherheitsgründen deaktiviert.
compat-mode OpenVPN warnt, dass diese Option Defaults auf weniger empfohlene Werte zurücksetzen kann. Besser einzelne Kompatibilitätsoptionen bewusst setzen.
cipher BF-CBC Blowfish/CBC ist historischer Altbestand und gehört nicht in neue Konfigurationen.
tls-version-min 1.0 Für neue betriebliche Setups nicht mehr akzeptabel. Wenn ein Gerät das braucht, gehört es in ein stark isoliertes Migrationsprojekt, nicht in normale Produktion.

28. Wann welche Instanz?

Gerät/Client Instanz Begründung
Aktueller OpenVPN-Client 2.5/2.6, aktueller Linux-/Windows-Client, moderne Appliance. modern.conf AEAD-Cipher, tls-crypt-v2, moderne Gruppen, keine Legacy-Fallbacks.
Phoenix-/Industriegerät mit Unterstützung für AES-GCM und tls-crypt-v2. modern.conf Wenn getestet stabil: modern verwenden.
Phoenix-/Industriegerät mit älterem OpenVPN-Stack, kein tls-crypt-v2, eventuell nur AES-256-CBC. compat.conf Weiterhin TLS 1.2+, Zertifikate, CRL und Firewall, aber mit Legacy-Fallback.
Gerät kann nur TLS 1.0, Blowfish oder Kompression. Keine der beiden Instanzen ohne Sonderfreigabe. Das ist nicht „älter“, das ist ein Sicherheitsproblem mit Gehäuse. Ersatz oder isolierte Übergangslösung planen.

29. Betriebs-Checkliste

30. Kurzfazit

modern.conf ist die Zielkonfiguration: stark, sauber und ohne Legacy-Ballast. compat.conf ist die pragmatische Betriebsvariante für ältere Phoenix-/Industrieclients: weiterhin kontrolliert, getrennt, mit Zertifikaten, CRL, Firewall und TLS 1.2+, aber mit bewusstem AES-256-CBC-Fallback und tls-auth.

Der entscheidende Sicherheitsgewinn entsteht nicht durch eine einzelne magische Zeile. Er entsteht durch die Kombination aus getrennter Instanz, eindeutiger PKI, ccd-exclusive, iroute, festen IPs, restriktiver Firewall, deaktivierter Kompression und sauberer Zertifikatssperrung. Kryptografie ist wichtig, aber Routingfehler und Copy-Paste-Zertifikate ruinieren dir den Tag meist schneller. Menschen nennen das dann „gewachsene Infrastruktur“.

AI

AI

GSD mit GitHub Copilot in VS Code nutzen

Diese Anleitung beschreibt, wie GSD installiert und mit GitHub Copilot Chat in VS Code genutzt wird. Die Installation wird als root ausgeführt.


1. Was ist GSD?

GSD steht für Get Shit Done. Es ist ein Workflow-System für KI-gestützte Entwicklung. GSD ergänzt Tools wie GitHub Copilot Chat, Claude Code, Codex, Cursor oder ähnliche Coding-Assistenten um strukturierte Abläufe.

Ohne GSD arbeitet ein KI-Chat oft sehr frei: Man beschreibt ein Problem, bekommt Code zurück, testet etwas, korrigiert wieder, verliert Kontext und irgendwann weiß niemand mehr genau, was eigentlich gebaut werden sollte. Willkommen in der modernen Softwareentwicklung, jetzt mit noch mehr Chatfenstern.

GSD soll genau dieses Problem lösen. Es zwingt die KI in einen klareren Ablauf:

Das Ziel ist nicht, dass die KI einfach wild Dateien ändert. Das Ziel ist ein nachvollziehbarer Entwicklungsprozess mit Planung, Status, Aufgaben und Prüfung.


2. Welches Problem löst GSD?

GSD hilft besonders bei diesen typischen Problemen:

GSD arbeitet deshalb mit Kommandos wie /gsd-new-project, /gsd-discuss-phase, /gsd-plan-phase, /gsd-execute-phase und /gsd-verify-work.


3. Voraussetzungen

Für die Installation werden benötigt:

Die folgenden Befehle werden als root ausgeführt.


4. Node.js und npm installieren

Zuerst werden die benötigten Pakete installiert:

apt-get update
apt-get install -y ca-certificates curl gnupg

Danach wird Node.js 22 eingerichtet:

curl -fsSL https://deb.nodesource.com/setup_22.x -o nodesource_setup.sh
bash nodesource_setup.sh
apt-get install -y nodejs

Anschließend prüfen:

node --version
npm --version
npx --version
git --version

Erwartet wird ungefähr:

node: Version 22 oder neuer
npm: Version 10 oder neuer
git: installiert

5. GSD global installieren

Damit die GSD-Hilfsprogramme dauerhaft verfügbar sind, wird GSD global installiert:

npm install -g @opengsd/get-shit-done-redux@latest

Danach prüfen:

which get-shit-done-redux
which gsd-sdk
gsd-sdk --help

Wichtig ist, dass gsd-sdk gefunden wird. Einige GSD-Workflows verwenden dieses Hilfsprogramm intern.


6. GSD in einem Projekt installieren

Nun in den gewünschten Projektordner wechseln:

cd /pfad/zum/projekt

Danach prüfen, ob es sich um ein Git-Projekt handelt:

git status

Anschließend GSD für GitHub Copilot lokal im Projekt installieren:

get-shit-done-redux --copilot --local --profile=core

Dadurch wird im Projekt ein Verzeichnis .github angelegt. Darin liegen unter anderem:

Installation prüfen:

ls -la .github
find .github -maxdepth 3 -type f | sort

7. GSD-Dateien ins Git übernehmen

Wenn GSD dauerhaft Teil des Projekts sein soll, sollten die GSD-Dateien committed werden:

git status
git add .github
git commit -m "Add GSD Copilot workflow configuration"

Danach sind die Copilot-Anweisungen und GSD-Skills im Repository versioniert. Das ist sinnvoll, damit alle Beteiligten mit denselben KI-Regeln arbeiten.


8. GSD in VS Code testen

Das Projekt in VS Code öffnen. Danach GitHub Copilot Chat öffnen und folgenden Befehl senden:

/gsd-help

Wenn GSD korrekt erkannt wird, zeigt Copilot eine Übersicht der verfügbaren GSD-Kommandos an.

Für ein bestehendes Projekt ist danach dieser Befehl sinnvoll:

/gsd-map-codebase

Damit analysiert GSD die vorhandene Codebasis und kann spätere Aufgaben besser einordnen.


9. GSD für ein neues Projekt nutzen

Für ein neues Projekt wird zuerst ein leerer oder vorbereiteter Projektordner angelegt und als Git-Repository initialisiert:

mkdir mein-neues-projekt
cd mein-neues-projekt
git init

Danach GSD im Projekt installieren:

get-shit-done-redux --copilot --local --profile=core

Danach das Projekt in VS Code öffnen und in Copilot Chat starten mit:

/gsd-new-project

GSD fragt dann nach Ziel, Umfang, Anforderungen und gewünschter Struktur. Daraus erstellt es eine Projektbasis mit Planungsdokumenten und nächsten Arbeitsschritten.


10. Typischer Workflow mit GSD

Ein normaler GSD-Ablauf sieht so aus:

  1. Projekt oder Feature beschreiben
  2. Phase besprechen
  3. Phase planen
  4. Phase umsetzen lassen
  5. Ergebnis prüfen
  6. Fortschritt committen
  7. Nächste Phase bearbeiten

10.1 Projektstatus anzeigen

/gsd-progress

Zeigt, wo das Projekt steht und welche nächsten Schritte sinnvoll sind.

10.2 Eine Phase besprechen

/gsd-discuss-phase 1

Dieser Schritt dient dazu, Ziel, Annahmen, Risiken und offene Fragen der Phase zu klären.

10.3 Eine Phase planen

/gsd-plan-phase 1

GSD erstellt daraus einen konkreten Plan. Dieser Plan sollte geprüft werden, bevor die KI Änderungen am Projekt macht.

10.4 Eine Phase ausführen

/gsd-execute-phase 1

GSD setzt die geplanten Aufgaben um. Danach sollte immer geprüft werden, welche Dateien geändert wurden.

git status
git diff

10.5 Arbeit prüfen

/gsd-verify-work 1

Dieser Schritt prüft, ob die Phase wirklich abgeschlossen ist. Das ist wichtig, weil „Code wurde erzeugt“ nicht automatisch bedeutet, dass „Code funktioniert“. Eine Erkenntnis, für die die Menschheit offenbar erst KI gebraucht hat.


11. Todos mit GSD erstellen

Kleine Aufgaben, Ideen oder Backlog-Einträge können mit GSD erfasst werden.

Beispiel:

/gsd-capture Todo: Login-Seite validieren und Fehlermeldungen verbessern

Weitere Beispiele:

/gsd-capture Idee: Statusseite für VPN-Verbindungen ergänzen
/gsd-capture Bug: Formular speichert leere Werte ohne Fehlermeldung
/gsd-capture Backlog: Exportfunktion für Konfigurationsdaten ergänzen

Danach kann mit folgendem Befehl geprüft werden, was als Nächstes ansteht:

/gsd-progress

12. Todos abarbeiten lassen

Für kleine, klar begrenzte Aufgaben kann /gsd-quick genutzt werden:

/gsd-quick Setze den offenen Todo "Login-Seite validieren und Fehlermeldungen verbessern" um.

Für sehr kleine Änderungen eignet sich /gsd-fast:

/gsd-fast "Korrigiere den Tippfehler in der Überschrift der Startseite."

Für größere Aufgaben sollte eine eigene Phase genutzt werden:

/gsd-discuss-phase 2
/gsd-plan-phase 2
/gsd-execute-phase 2
/gsd-verify-work 2

Nach jeder Umsetzung prüfen:

git status
git diff

Wenn die Änderungen passen:

git add .
git commit -m "Implement planned GSD changes"

13. Sichere Arbeitsweise

GSD ist hilfreich, aber es bleibt ein KI-gestütztes Werkzeug. Deshalb sollte man es wie einen sehr schnellen Junior-Entwickler behandeln: produktiv, motiviert und gelegentlich komplett überzeugt von Unsinn.

Empfohlene Regeln:


14. Nützliche GSD-Kommandos

Kommando Zweck
/gsd-help Zeigt die GSD-Hilfe an.
/gsd-map-codebase Analysiert eine bestehende Codebasis.
/gsd-new-project Startet einen neuen GSD-Projektworkflow.
/gsd-progress Zeigt aktuellen Stand und nächste Schritte.
/gsd-capture Speichert Ideen, Todos, Bugs oder Backlog-Einträge.
/gsd-discuss-phase <N> Bespricht Ziel und Rahmen einer Phase.
/gsd-plan-phase <N> Erstellt einen konkreten Plan für eine Phase.
/gsd-execute-phase <N> Setzt eine geplante Phase um.
/gsd-verify-work <N> Prüft, ob die Arbeit korrekt abgeschlossen wurde.
/gsd-quick Bearbeitet eine kleine Aufgabe mit GSD-Struktur.
/gsd-fast Bearbeitet eine sehr kleine Änderung direkt.

15. Beispiel: Kompletter Ablauf

Beispiel für ein neues Feature:

/gsd-capture Todo: Benutzerrollen im Adminbereich verbessern
/gsd-progress
/gsd-discuss-phase 1
/gsd-plan-phase 1
/gsd-execute-phase 1
/gsd-verify-work 1

Danach im Terminal prüfen:

git status
git diff

Wenn alles passt:

git add .
git commit -m "Improve admin user roles"

16. Zusammenfassung

GSD macht aus einem freien KI-Chat einen strukturierteren Entwicklungsprozess. Es hilft dabei, Anforderungen, Planung, Umsetzung, Todos und Prüfung sauberer zu organisieren.

Der wichtigste Start für bestehende Projekte:

/gsd-map-codebase
/gsd-progress

Der wichtigste Start für neue Projekte:

/gsd-new-project

Der wichtigste Sicherheitsgriff bleibt trotzdem:

git status
git diff

Denn auch mit GSD gilt: Vertrauen ist gut, Versionskontrolle ist besser.

Linux - Verschlüsselung

Linux - Verschlüsselung

Linux - LUKS LVM - Dropbear-initramfs

Verschlüsseltes System per Dropbear-Initramfs über SSH entsperren

Diese Anleitung gilt für Debian/Ubuntu-Systeme mit initramfs-tools und einer verschlüsselten Root-Partition. Die erste Variante verwendet ein normales, ungetaggtes Netzwerkinterface. Die zweite Variante verwendet ein getaggtes IEEE-802.1Q-VLAN.

Vor dem Neustart: Konsolen- oder KVM/IPMI-Zugriff bereithalten. Eine falsche Interface-, IP-, Gateway- oder VLAN-Konfiguration kann den SSH-Zugriff während des Bootens verhindern. Die normale Passworteingabe an der Konsole bleibt der Rückfallweg.

Anleitung 1: Dropbear-Initramfs ohne VLAN einrichten

Voraussetzungen

1. Netzwerkinformationen ermitteln

Interface, IP-Adresse und Default-Gateway anzeigen:

ip -br address
ip route show default

Beispiel:

Interface: enp4s0f0
IP-Adresse: 10.100.0.2
Netzmaske: 255.255.0.0
Gateway: 10.100.0.1

2. Pakete installieren

apt update
apt install -y dropbear-initramfs cryptsetup-initramfs

3. Statische Initramfs-Netzwerkkonfiguration eintragen

Datei öffnen:

nano /etc/initramfs-tools/initramfs.conf

Folgende Werte ergänzen beziehungsweise vorhandene Werte ersetzen:

DEVICE=enp4s0f0
IP=10.100.0.2::10.100.0.1:255.255.0.0::enp4s0f0:none

Aufbau der IP-Zeile:

IP=<Client-IP>:<Server-IP>:<Gateway>:<Netzmaske>:<Hostname>:<Interface>:<Autoconf>

Bei einer statischen Adresse bleiben Server-IP und Hostname im Beispiel leer. none deaktiviert die automatische Konfiguration per DHCP.

4. Dropbear absichern und Port festlegen

Datei öffnen:

nano /etc/dropbear/initramfs/dropbear.conf

Empfohlene Basiskonfiguration:

DROPBEAR_OPTIONS="-I 300 -j -k -s -p 2222"

Option

Bedeutung

-I 300

Inaktive Verbindung nach 300 Sekunden trennen

-j

Lokales Port-Forwarding deaktivieren

-k

Remote-Port-Forwarding deaktivieren

-s

Passwortanmeldung deaktivieren; nur Schlüssel erlauben

-p 2222

Separaten SSH-Port für das Initramfs verwenden

5. SSH-Schlüssel auf dem Client erzeugen

ssh-keygen -t ed25519 \
  -f ~/.ssh/id_initramfs_unlock_System_1 \
  -C "Initramfs unlock for System_1"

Den öffentlichen Schlüssel anzeigen:

cat ~/.ssh/id_initramfs_unlock_System_1.pub
Der private Schlüssel verbleibt ausschließlich auf dem Client. Nur die Datei mit der Endung .pub wird auf den Server übertragen.

6. Öffentlichen Schlüssel auf dem Server hinterlegen

install -d -m 0700 /etc/dropbear/initramfs
nano /etc/dropbear/initramfs/authorized_keys
chmod 0600 /etc/dropbear/initramfs/authorized_keys

In authorized_keys steht der vollständige öffentliche Schlüssel in einer Zeile:

no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAAC3... Initramfs unlock for System_1
Ein zugelassener Schlüssel ermöglicht eine Root-Shell im Initramfs. Den privaten Schlüssel deshalb wie ein Administrationspasswort schützen und bei Verlust sofort aus authorized_keys entfernen.

7. Initramfs neu erzeugen und Inhalt prüfen

update-initramfs -u -k all

lsinitramfs /boot/initrd.img-$(uname -r) | \
  grep -E 'dropbear|authorized_keys|cryptroot-unlock'

Die Ausgabe sollte unter anderem Dropbear, authorized_keys und cryptroot-unlock enthalten.

8. Komfortablen SSH-Zugriff auf dem Client einrichten

~/.ssh/config ergänzen:

Host System_1-unlock
    HostName 10.100.0.2
    User root
    Port 2222
    IdentityFile ~/.ssh/id_initramfs_unlock_System_1
    IdentitiesOnly yes
    PasswordAuthentication no
    UserKnownHostsFile ~/.ssh/known_hosts.initramfs

Die separate known_hosts-Datei verhindert Konflikte zwischen dem Host-Key des Initramfs und dem Host-Key des normal gestarteten SSH-Servers.

9. Neustarten und entsperren

reboot

Sobald der Server an der Festplattenabfrage wartet, vom Client verbinden:

ssh System_1-unlock

In der Initramfs-Shell:

cryptroot-unlock

Nach erfolgreicher Passworteingabe wird die SSH-Verbindung normalerweise beendet und der reguläre Bootvorgang fortgesetzt.

Fehlersuche

Anleitung 2: Dropbear-Initramfs über Tagged VLAN einrichten

Voraussetzungen

Beispielwerte:

Parent-Interface: enp1s0
VLAN-ID: 40
VLAN-Interface: enp1s0.40
IP-Adresse: 10.15.32.253
Netzmaske: 255.255.248.0
Gateway: 10.15.39.254
Die Initramfs-Netzwerkkonfiguration gilt nur während der frühen Bootphase. Das gestartete Betriebssystem muss das VLAN zusätzlich über Netplan, systemd-networkd oder /etc/network/interfaces konfigurieren.

1. Pakete installieren

apt update
apt install -y dropbear-initramfs cryptsetup-initramfs iproute2

2. VLAN und statische Adresse konfigurieren

/etc/initramfs-tools/initramfs.conf öffnen:

nano /etc/initramfs-tools/initramfs.conf

Folgende Werte eintragen:

VLAN="enp1s0:40"
DEVICE=enp1s0.40
IP=10.15.32.253::10.15.39.254:255.255.248.0::enp1s0.40:none

Die Variable VLAN verwendet das Format Parent-Interface:VLAN-ID. Das folgende Skript ist nicht auf VLAN 40 festgelegt. Für VLAN 120 würde die Konfiguration beispielsweise so aussehen:

VLAN="enp1s0:120"
DEVICE=enp1s0.120

Mehrere VLAN-Interfaces können ebenfalls vorbereitet werden:

VLAN="enp1s0:40 enp1s0:120"

3. Benötigte Kernelmodule aufnehmen

Treiber des physischen Interfaces ermitteln:

basename "$(readlink -f /sys/class/net/enp1s0/device/driver/module)"

In /etc/initramfs-tools/modules mindestens das VLAN-Modul ergänzen. Falls der Netzwerkkartentreiber nicht automatisch im Initramfs vorhanden ist, zusätzlich den zuvor ausgegebenen Modulnamen eintragen:

8021q
# Beispiel für einen NIC-Treiber:
# ixgbe

4. Programm ip ins Initramfs kopieren

Datei /etc/initramfs-tools/hooks/vlan-ip erstellen:

nano /etc/initramfs-tools/hooks/vlan-ip

Inhalt:

#!/bin/sh

PREREQ=""

prereqs()
{
    echo "$PREREQ"
}

case "$1" in
    prereqs)
        prereqs
        exit 0
        ;;
esac

. /usr/share/initramfs-tools/hook-functions

IP_BIN="$(command -v ip || true)"

if [ ! -x "$IP_BIN" ]; then
    echo "Fehler: ip aus dem Paket iproute2 wurde nicht gefunden" >&2
    exit 1
fi

manual_add_modules 8021q
copy_exec "$IP_BIN"

exit 0
chmod 0755 /etc/initramfs-tools/hooks/vlan-ip

5. Generisches VLAN-Bootskript erstellen

Das VLAN muss existieren, bevor Dropbear das Netzwerk konfiguriert. Deshalb wird das Skript als init-premount/00-vlan installiert und nicht unter local-top.

install -d -m 0755 /etc/initramfs-tools/scripts/init-premount
nano /etc/initramfs-tools/scripts/init-premount/00-vlan

Inhalt:

#!/bin/sh

PREREQ=""

prereqs()
{
    echo "$PREREQ"
}

case "$1" in
    prereqs)
        prereqs
        exit 0
        ;;
esac

. /scripts/functions
. /conf/initramfs.conf

for config_file in /conf/conf.d/*.conf; do
    [ -f "$config_file" ] && . "$config_file"
done

[ -n "${VLAN:-}" ] || exit 0

modprobe 8021q || {
    log_failure_msg "VLAN-Modul 8021q konnte nicht geladen werden"
    exit 1
}

for vlan_config in $VLAN; do
    parent=${vlan_config%%:*}
    vlan_id=${vlan_config#*:}

    if [ "$parent" = "$vlan_config" ]; then
        log_failure_msg "Ungueltige VLAN-Konfiguration: $vlan_config"
        exit 1
    fi

    case "$vlan_id" in
        ''|*[!0-9]*)
            log_failure_msg "Ungueltige VLAN-ID: $vlan_id"
            exit 1
            ;;
    esac

    if [ "$vlan_id" -lt 1 ] || [ "$vlan_id" -gt 4094 ]; then
        log_failure_msg "VLAN-ID ausserhalb 1 bis 4094: $vlan_id"
        exit 1
    fi

    vlan_device="${parent}.${vlan_id}"

    # Kurz auf das physische Interface warten.
    attempts=0
    while ! ip link show "$parent" >/dev/null 2>&1; do
        attempts=$((attempts + 1))
        if [ "$attempts" -ge 10 ]; then
            log_failure_msg "Interface $parent wurde nicht gefunden"
            exit 1
        fi
        sleep 1
    done

    ip link set "$parent" up || exit 1

    if ! ip link show "$vlan_device" >/dev/null 2>&1; then
        ip link add link "$parent" \
            name "$vlan_device" \
            type vlan id "$vlan_id" || exit 1
    fi

    ip link set "$vlan_device" up || exit 1
    log_success_msg "VLAN-Interface $vlan_device wurde aktiviert"
done

exit 0
chmod 0755 /etc/initramfs-tools/scripts/init-premount/00-vlan

6. Dropbear konfigurieren

/etc/dropbear/initramfs/dropbear.conf:

DROPBEAR_OPTIONS="-I 300 -j -k -s -p 2222"

7. SSH-Schlüssel erzeugen und eintragen

Auf dem Client:

ssh-keygen -t ed25519 \
  -f ~/.ssh/id_initramfs_unlock_System_1 \
  -C "Initramfs unlock for System_1"

cat ~/.ssh/id_initramfs_unlock_System_1.pub

Auf dem Server:

install -d -m 0700 /etc/dropbear/initramfs
nano /etc/dropbear/initramfs/authorized_keys
chmod 0600 /etc/dropbear/initramfs/authorized_keys

Beispielzeile:

no-port-forwarding,no-agent-forwarding,no-X11-forwarding ssh-ed25519 AAAAC3... Initramfs unlock for System_1

8. Initramfs bauen und prüfen

update-initramfs -u -k all

lsinitramfs /boot/initrd.img-$(uname -r) | \
  grep -E '8021q|scripts/init-premount/00-vlan|/ip$|dropbear|authorized_keys|cryptroot-unlock'

Optional kann die tatsächlich ins Initramfs übernommene Konfiguration kontrolliert werden:

lsinitramfs /boot/initrd.img-$(uname -r) conf/initramfs.conf

9. Switch konfigurieren und prüfen

10. SSH-Zugriff auf dem Client einrichten

~/.ssh/config:

Host System_1-unlock
    HostName 10.15.32.253
    User root
    Port 2222
    IdentityFile ~/.ssh/id_initramfs_unlock_System_1
    IdentitiesOnly yes
    PasswordAuthentication no
    UserKnownHostsFile ~/.ssh/known_hosts.initramfs

11. Neustarten und entsperren

reboot

Vom Client verbinden:

ssh System_1-unlock

Danach in der Initramfs-Shell:

cryptroot-unlock

Fehlersuche für Tagged VLAN

Quellen und weiterführende Dokumentation