Der "Digital Independence Day" - der Tag, an dem wir uns digital von Abhängigkeiten lösen und die Kontrolle über unsere Daten zurückgewinnen wollen. Für mich bedeutet das nicht nur ein abstraktes Konzept, sondern einen konkreten Schritt: Mein eigener Server. Auf diesem kleinen Rechenzentrum-Residenz möchte ich meine Webseiten, Anwendungen und Daten selbst verwalten – frei von den Zwängen großer Tech-Giganten.

Dieses Projekt ist mehr als nur Technik. Es ist ein Ausdruck meiner persönlichen Unabhängigkeit im digitalen Raum.

In diesem Text teile ich mein Abenteuer mit euch: die Herausforderungen der Serverkonfiguration, die Freude am eigenen "Server-Knowhow" und natürlich die Befriedigung, den Digital Independence Day jeden Tag aufs Neue zu leben."

Zuerst wird in meinem Fall mit Vmware Workstation eine lokale Maschine aufgesetzt.
4 CPU Kerne, 4 GB Arbeitsspeicher und wichtig für den späteren Server 2 Festplatten, da das System gleich auf ein RAID 1 vorbereitet werden soll.
So weiß man, dass alle nötigen Pakete installiert und eingerichtet sind.
Im späteren Verlauf wird eine Web-Oberfläche installiert – Virtualmin.

Damit werden die Domains, die auf dem Server laufen, und die Benutzer erstellt und verwaltet.
Als Firewall kommt CSF Firewall zum Einsatz.

Die aktuelle Version von CSF und Virtualmin hatten mit Debian 13 noch ein paar kleinere Probleme. Vor ein paar Versionen hat Debian angefangen das Logsystem von klassischen Logdateien unter /var/log auf Journal/SystemD umzustellen.
Anstatt jetzt Syslog auf einem aktuellen Debian nach zu installieren und vor allem Dingen anpassen zu müssen, habe ich mich dazu entschlossen in der VM mit Debian 9 anzufangen.

Und dann diese alte Version - die man auf keinen Fall in einem Rechenzentrum laufen lassen sollte -  auf die Debian 12 updaten. Das bekommt noch lange genug Updates, und ein Update von 12 auf 13 sollte kein Problem sein.

Nachdem dieses Grundsystem dann auf Debian 12 geupdatet wurde, haben wir eine virtuelle Maschine, die in einer RAID 1 Konfiguration läuft.
Wir haben Zugang per SSH.
Dann können wir die VM in ein TAR-Archiv einpacken, dazu nutzen wir die SystemRescueCD Diese sind in der VM gebootet und speichern die VM dann in einer Imagedatei. Dieses Image können wir dann auf der Server übertragen.

Wir werden dann den Hetzner Server im Rescue Modus booten.
Dort ein RAID-Verbund einrichten und Partitionen anlegen.
Das Server Image, was zu Hause erstellt wurde, auf diesen RAID Verbund entpacken.
Das System bootfähig machen und hoffen nix vergessen zu haben!
Den Server neu starten und hoffentlich unser eigenes Debian auf dem Hetzner Server im RZ installiert zu habeb.

Jetzt geht es an das Anpassen des Systems.
Also eigene Tools installieren, Sudo einrichten, die Debian 12 Repositories einrichten, Grub anpassen, die Bash-Autovervollständigung, IPv6 abschalten.

Der Server wird IPv4 only - im ersten Schritt.
IPv6 kommt später. Ich will mich im Moment noch nicht mit zwei Protokollen gleichzeitig rumschlagen.

Dann wird ein Default Editor auf der Konsole festgelegt.
Beim Login sollen verschiedene Status-Informationen angezeigt werden, welche Installation ist gebootet? Server oder Rescue? Ist das RAID okay? Wir lassen uns mit Neofetch noch zusätzliche Informationen zum System anzeigen.

Wir stellen die Datei "rc.local" wieder her. Eine Art "old-school" autoexec.bat – nur für Linux. Nicht ganz so konfortable wie SystemD Scripte, aber gut um nur schnell mal einen Befehl beim Hochfahren ausführen zu lassen.

Es werden eigene Ordner für unsere eigenen Scripte erstellt, verbessert die Übersicht.
Der SSH Dienst wird noch weiter abgesichert.

Steht dieses Grundsystem wird ein Backup erstellt und das System zusätzlich auf einer 2. Partiton als Rescue-System in Grub eingebunden.

Man kann dann über einen Befehl auf der Konsole einfach das voreingerichtet Rescue-System hochfahren. Zwar bietet Hetzner als Serverhoster auch ein Rescue System an, hier sind aber die Einstellungen so gesetzt das sie für alle Benutzer aller Server bei Hetzner passen müssen.
Ausserdem muss man sich immer in die Weboberfläche von Hetzner einloggen um das Rescue System von Hetzner zu nutzen.

Bei unserem System reicht ein einfaches "sudo reboot_to_rescue" und nach 2 Minuten ist das Rescue System bereit um ein komplettes Systembackup zu machen. Das wäre zwar auch aus dem laufenden Server System möglich, ist aber nicht ganz so sauber.

Danach wird Virtualmin installiert.
Virtualmin ist eine Konfigurationsoberfläche für Web Server. Es unterstützt Dienste wie Postfix, Apache, MySQL. Also ein All-in-one-Tool.
Die Programme werden nicht nur installiert und konfiguriert, sondern man kann damit auch die Domains erstellen. Man erspart sich damit das manuelle Einrichten auf der Konsole. Wenn man sich da mal vertippt.... das macht keinen Spaß.

Und ganz besonders das Einrichten, zum Beispiel von Apache Server macht Virtualmin viel einfacher. Man kann nur noch per HTTPS auf die Webseiten kommen, eine sichere Verschlüsselung wird eingerichtet – HTTPS ist leider nicht mehr HTTPS! Stichwort Cipher.

Alles, was Virtualmin macht, könnte man auch auf der Konsole machen.
Es speichert alle Einstellungen in den Konfigurationsdateien der entsprechenden Dienste.
Aber es geht halt sehr viel schneller. Wir werden die dann immer wieder die Virtualmin-Konfiguration checken lassen – von Virtualmin selbst!

Virtualmin wird auch die Systemzeit regelmäßig checken. Das kann man individuell einstellen, ich setze die Zeit viermal am Tag, also alle sechs Stunden.
Ist auf einem Privatserver jetzt nicht sooo wichtig, wir sind ja kein Big Data Rechenzentrum.
Aber die korrekte Zeit ist aber doch wichtig z.B. Für Email oder die Logfiles.
Eine PC Uhr läuft pro Tag ein paar Sekunden vor oder zurück – sich also nur darauf zu verlassen... keine gute Idee.

Virtualmin wird uns automatisch informieren wenn es Systemupdates geben sollte.
Systemupdates lasse ich grundsätzlich nicht automatisch installieren. Ich möchte wissen wann da was warum installiert worden ist.

Nachdem Virtualmin den Mailserver Postfix installiert hat, werden wir da noch ein paar Einstellungen vornehmen. Zum Beispiel die maximale Größe einer E-Mail, die angenommen wird.
Der FTP Server ProFTPd wird so eingestellt, dass Benutzer nur in ihrem Home-Verzeichnis aktiv sein können. Sie können also nicht auf die Daten anderer Nutzer zugreifen.

Der Zugriff auf die Virtualmin Oberfläche erfolgt über Benutzername und Kennwort. Hier fügen wir noch eine zweite Authentifikation hinzu, welche kompatibel zu Google Authenticator ist... Mehr Sicherheit.

Wir werden Virtualmin so einstellen, dass es keine unnötigen Module anzeigt, sondern nur, dass in der Oberfläche angezeigt wird, was wir auch nutzen können und wollen.

Wir richten dann tägliche Backups von Konfigurationsdateien ein. Virtualmin benutzt hier eigentlich das Programm etckeeper. Es ist allerdings von der Oberfläche her nicht so schön gelungen... (Hat es überhaupt eine?) wie ich finde.

Ich nutze hier einfach ein Backup des Ordners /etc, was einmal am Tag durchgeführt wird. Die Daten sind nicht sonderlich gross, sind ja nur Konfigurationsdaten. Und wir werden noch Email auf der Konsole einrichten.

Nachdem das Grundsystem dann steht, wird Virtualmin vernünftig eingerichtet.
Wir brauchen einen Hostnamen in Virtualmin, der zum Server passt. Dafür werden dann auch automatisch SSL-Zertifikate erstellt - und vor allem wird dieser Domain Name, der Hostname, als Default in Virtualmin gesetzt und die Zertifikate, die dort erstellt werden, werden dann auch für Postfix, Dovecot, Apache (Für die Domain hostname.domain.tld), VIrtualmin und MySQL zu benutzt.

Nachdem das Grundsystem installiert wurde und Virtualmin eingerichtet wurde, wird es Zeit das System NOCH WEITER abzusichern.

Hier nutzen wir die CSF Firewall.
Das steht für "ConfigServer Security & Firewall". Das Programm wurde bis Ende 2025 von einer englischen One-Man-Firma geschrieben. Der Support wurde leider eingestellt. Die Firma gibt es nicht mehr. Es ist jetzt ein Community-Projekt.

Das ist halt das Schöne an Open Source. Die CSF-Firewall nutze ich schon länger. Es ist nicht nur eine Firewall, die Ports blockiert, sondern sie wertet auch Log-Files aus, sperrt entsprechend nach Regeln bestimmte Angreifer.
Ist im Grunde genommen das, was "Fail2Ban" macht... Aber mehr Optionen und dafür auch schwerter einzurichten.

In Virtualmin wird eine Web-Oberfläche für CSF eingebaut. Es gibt eine grafische Log-Auswertung und wir werden noch ein paar CSF Tools installieren, die ich selber geschrieben habe.
Sie machen uns das Arbeiten mit der Firewall etwas leichter.

Gerade am Anfang bekommt man von CSF viele Status-Mails mit Diensten die laufen. Man will aber wahrscheinlich nicht jeden Tag 1000 Mails haben, weil CSF alle 2 Minuten checkt, was so los ist auf 127.0.0.1!
CSF wird bestimmte Dienste die unter bestimmten Benutzern laufen "whitelisten", so dass CSF weiß Dienst darf laufen und verschickt keine "Alarm Mail".

Da wir schon einen Server mit jeder Menge Arbeitsspeicher und viel Festplatten Platz haben, installiere ich zusätzlich noch Oracle VirtualBox, um auf dem Server auch virtuelle Maschinen betreiben zu können.
Die Verwaltung dieser virtuellen Maschinen kann man entweder über die Kommandozeile steuern, dazu gibt es ein Skript, oder um es noch einfacher zu machen, wird auf dem System noch PHPVirtualBox installiert.
Dann kann man das Ganze über eine grafische Web-Oberfläche machen.

Wir danach an unsere eigenen Dienste zu installieren und zu konfigurieren.

Da ist zum einen Syncthing, ein Programm, das es PCs untereinander ermöglicht Daten zu synchronisieren und abzugleichen - oder auch vom Handy.
Macht man Fotos auf dem Handy so werden diese automatisch auf dem Server gespeichert.

Wir werden die Log-Dateien für Syncthing über Logrotate verwalten lassen. Sodass die Log-Dateien nicht riesengroß werden, man aber immer ein paar Tage zurückgehen kann und zu schauen was da los ist, so wie es unter Linux auch üblich ist.

Das gleiche machen wir noch für rsyncd, den rsync Dienst. rsync ist auch zum Datei synchronisieren hat allerdings jetzt nicht unbedingt den Charme eines Syncthing Clients.
Vor allen Dingen kann Syncthing auf dem PC (Desktop) immer im Hintergrund laufen. Rsync muss man immer aufrufen oder per Script steuern.

Danach sollte der Server incl. seinem eigenen Rescue System "Feature-Ready" sein...

Zeit den DID zu feiern...

z.B. mit NextCloud für Kontakte, Termine, Online Besprechnungen..
Oder etwas kleiner und einfacher Baïkal oder Radicale mit InfCloud als WebUI um schnell und einfach nur CardDAV Einträge mit der FritzBox, dem Handy und Thunderbird syncron zu halten?

Eigenen eigenen TeamSpeak Server als Ersatz für Discord!
Einen XMMP Server zum chatten mit Freunden auf der ganzen Welt!
Eigener Mastodon Server...


Debian Version 0.01

### Grundlagen im RZ ###
#Festplatten mit Badblocks testen#

screen -S sda
Wenn nötig mdadm RAID anhalten
mdadm --stop /dev/md*
badblocks -svw -p 1 -b 4096 -c 65536 /dev/sda

screen -S sdb
badblocks -svw -p 1 -b 4096 -c 65536 /dev/sdb

Je nach Größe und Anzahl der Durchgänge kann das TAGE dauern!
Nutze daher nur 2 Durchgänge, normal wären 5!

0.5% = 3min ~ 40h für 2x schreiben und 2x lesen!

#RAM testen#
Nachdem die HDDs Ok sind testen wir jetzt den Arbeitsspeicher!
Hier ein kleines Script dafür!

#!/bin/bash

# Target configuration
target_user="root"

# Ensure memtester is installed
if ! command -v memtester >/dev/null; then
    echo "Installiere memtester..."
    sudo apt update && sudo apt install -y memtester
fi

# Get free memory in MB (minus a small safety margin for the OS)
free_mem=$(free -m | awk '/^Mem:/{print $4}')
test_mem=$((free_mem - 128))

if [ $test_mem -le 0 ]; then
    echo "Fehler: Zu wenig freier Speicher zum Testen."
    exit 1
fi

echo "--- ECC RAM Belastungstest ---"
echo "Teste $test_mem MB in 2 Durchgängen..."
echo "Parallel dazu wird auf ECC-Fehlermeldungen geprüft."

# Start background monitoring for ECC errors
(
    while true; do
        if [ -d /sys/devices/system/edac/mc ]; then
            for mc in /sys/devices/system/edac/mc/mc*; do
                ce=$(cat "$mc/ce_count")
                if [ "$ce" -gt 0 ]; then
                    echo "!!! ECC WARNING: $ce Correctable Errors detected on $(cat $mc/mc_name) !!!"
                fi
            done
        fi
        sleep 5
    done
) &
monitor_pid=$!

# Run memtester
if ! sudo memtester "${test_mem}M" 2; then
    echo ""
    echo "🟡 Warnung: Zugriff verweigert / Fehlende Rechte"
    echo "Datei: memtester binary / memory access"
    echo ""
    echo "Mögliche Lösung:"
    echo "Bitte prüfe im Skript-Header, ob die Variable target_user korrekt gesetzt ist (aktuell: target_user=\"$target_user\")."
    echo ""
    echo "Manuelle Reparatur:"
    echo "Führe das Skript mit 'sudo' aus oder prüfe die Berechtigungen für /dev/mem."
    kill $monitor_pid
    exit 1
fi

# Cleanup
kill $monitor_pid
echo "Test beendet. Bitte dmesg auf 'EDAC' Einträge prüfen."
#hier ist schluss

# LM Sensors testen/einrichten #
Sensor Module laden

#!/bin/bash

# Target configuration
target_user="root"

# Module list
modules=("coretemp" "jc42" "nct6775")

echo "--- Lade Hardware-Monitoring Module ---"

for mod in "${modules[@]}"; do
    if lsmod | grep -q "$mod"; then
        echo "Modul $mod ist bereits geladen."
    else
        echo "Lade $mod..."
        if ! sudo modprobe "$mod"; then
            echo ""
            echo "🟡 Warnung: Zugriff verweigert / Fehlende Rechte"
            echo "Datei: /lib/modules/$(uname -r)"
            echo ""
            echo "Mögliche Lösung:"
            echo "Bitte prüfe im Skript-Header, ob die Variable target_user korrekt gesetzt ist (aktuell: target_user=\"$target_user\")."
            echo ""
            echo "Manuelle Reparatur:"
            echo "Führe 'sudo modprobe $mod' manuell aus oder prüfe, ob 'kmod' installiert ist."
            # Wir brechen nicht ab, sondern versuchen das nächste Modul
        fi
    fi
done

echo -e "\n--- Aktuelle Sensor-Ausgabe ---"
if command -v sensors >/dev/null; then
    sensors
else
    echo "Sensors-Befehl nicht gefunden. Installiere lm-sensors..."
    sudo apt update && sudo apt install -y lm-sensors
    sensors
fi

# Check for specific JC42 (RAM) sensors
if ls /sys/bus/i2c/drivers/jc42/ >/dev/null 2>&1; then
    echo -e "\n✅ RAM-Sensoren (JC42) aktiv."
fi

# Target configuration
target_user="root"

echo "--- ASUS WS C246 DC Hardware-Monitor ---"

# Check for Nuvoton or ITE Sensor Chips typical for ASUS
if ! command -v sensors >/dev/null; then
    sudo apt update && sudo apt install -y lm-sensors
fi

# Show detailed voltages and temps
echo "Prüfe Spannungen und Temperaturen..."
sensors | grep -iE 'in0|in1|in2|+12V|+5V|Vcore|CPU Temperature|PCH'

if [ $? -ne 0 ]; then
    echo ""
    echo "🟡 Warnung: Zugriff verweigert / Fehlende Rechte"
    echo "Datei: /sys/class/hwmon"
    echo "Mögliche Lösung: Variable target_user prüfen (aktuell: $target_user)."
    echo "Manuelle Reparatur: sudo sensors-detect ausführen und Module laden."
    exit 1
fi

# Check for BMC/IPMI (if the ASMB9 module is populated)
echo -e "\n--- BMC / IPMI Status ---"
if sudo ipmitool sdr 2>/dev/null; then
    echo "BMC erkannt. Hier sind die Hardware-Logs:"
    sudo ipmitool sel list | tail -n 10
else
    echo "Kein aktives BMC-Modul (ASMB9) über Software-Interface gefunden."
fi

#hier ist schluss

fix:
#!/bin/bash

# Target configuration
target_user="root"

echo "--- Präzise ASUS WS C246 DC Sensor-Analyse ---"

# Fix for the grep warning (escaping the plus sign)
echo "Prüfe Spannungen und Temperaturen..."
sensors | grep -iE "Vcore|in[0-9]|\+12V|\+5V|AUXTIN"

echo -e "\n--- RAM-Temperaturen (JC42) ---"
if ls /sys/bus/i2c/drivers/jc42/ >/dev/null 2>&1; then
    sensors | grep -A 1 "jc42"
else
    echo "Keine JC42 Sensoren gefunden. Sind ECC-Module verbaut?"
fi

# Manual Repair Warning if Vcore looks impossible
vcore_val=$(sensors | grep "Vcore" | awk '{print $2}')
if [[ "$vcore_val" == "200.00" ]]; then
    echo ""
    echo "🟡 Warnung: Sensor-Skalierung ungenau"
    echo "Die Vcore wird vermutlich falsch ausgelesen (Offset-Problem)."
    echo ""
    echo "Mögliche Lösung:"
    echo "Bitte prüfe im Skript-Header, ob die Variable target_user korrekt gesetzt ist (aktuell: target_user=\"$target_user\")."
    echo "Oft hilft ein BIOS-Update oder eine spezifische /etc/sensors.d/asus_ws_c246.conf Datei."
fi

#hier ist schluss


#ECC RAM Temp auslesen#
#!/bin/bash

# Target configuration
target_user="root"

echo "--- Detaillierte ECC-RAM Temperatur-Analyse ---"

# Find all jc42 adapter paths and query them specifically
found=false
for sensor in /sys/bus/i2c/drivers/jc42/0-00*; do
    if [ -d "$sensor" ]; then
        # Extract the address (e.g., 0-0018)
        addr=$(basename "$sensor")
        echo "Prüfe RAM-Slot an Adresse: $addr"
        # Query sensors for this specific chip
        sensors "jc42-i2c-$addr" 2>/dev/null | grep -E 'temp1|ALARM'
        found=true
    fi
done

if [ "$found" = false ]; then
    echo "Keine aktiven JC42 Sensoren gefunden."
    echo ""
    echo "🟡 Warnung: Zugriff verweigert / Fehlende Hardware"
    echo "Datei: /sys/bus/i2c/drivers/jc42/"
    echo ""
    echo "Mögliche Lösung:"
    echo "Bitte prüfe im Skript-Header, ob die Variable target_user korrekt gesetzt ist (aktuell: target_user=\"$target_user\")."
    echo ""
    echo "Manuelle Reparatur:"
    echo "Stelle sicher, dass das Modul 'jc42' mit 'sudo modprobe jc42' geladen wurde."
    exit 1
fi

echo -e "\n--- Ende der Messung ---"
#hier ist schluss

#System stressen auf Stabilität testen#
#!/bin/bash

# Target configuration
target_user="root"

# Ensure sysbench is installed
if ! command -v sysbench >/dev/null; then
    echo "Installiere sysbench..."
    sudo apt update && sudo apt install -y sysbench
fi

echo "--- System Stress Test (CPU & RAM) ---"

# Start RAM test in background
echo "Starte RAM-Durchsatz-Test (schreibend)..."
sysbench memory --memory-oper=write --threads=2 run > /dev/null &
ram_pid=$!

# Start CPU test in foreground
echo "Starte CPU-Stress-Test (Primzahlen)..."
if ! sysbench cpu --cpu-max-prime=20000 --threads=$(nproc) run; then
    echo ""
    echo "🟡 Warnung: Zugriff verweigert / Fehlende Rechte"
    echo "Datei: sysbench execution"
    echo ""
    echo "Mögliche Lösung:"
    echo "Bitte prüfe im Skript-Header, ob die Variable target_user korrekt gesetzt ist (aktuell: target_user=\"$target_user\")."
    echo ""
    echo "Manuelle Reparatur:"
    echo "Stelle sicher, dass sysbench korrekt installiert ist und mit ausreichenden Rechten ausgeführt wird."
    kill $ram_pid 2>/dev/null
    exit 1
fi

wait $ram_pid
echo "Belastungstest abgeschlossen."
#hier ist schluss

VM erstellen @ home
4 Threads (2x2)
4 GB RAM
2x 80GB HDD SATA

SystemRescueCD oder ähnliches booten

check sind alle HDDs da?
lsscsi

#Partitionstabellen erstellen
parted /dev/sda mklabel gpt
parted /dev/sdb mklabel gpt

#create partitions
#boot 8MB - sd[ab]1
parted -a optimal -- /dev/sda mkpart primary 1MB 8MB
parted -a optimal -- /dev/sdb mkpart primary 1MB 8MB

#server 40GB - sd[ab]2
parted -a optimal -- /dev/sda mkpart primary 8MB 40GB
parted -a optimal -- /dev/sdb mkpart primary 8MB 40GB

#rescue 10GB - sd[ab]3
parted -a optimal -- /dev/sda mkpart primary 40GB 50GB
parted -a optimal -- /dev/sdb mkpart primary 40GB 50GB

#backup 20GB - sd[ab]4
parted -a optimal -- /dev/sda mkpart primary 50GB 70GB
parted -a optimal -- /dev/sdb mkpart primary 50GB 70GB

#data 5GB - sd[ab]5
parted -a optimal -- /dev/sda mkpart primary 70GB 75GB
parted -a optimal -- /dev/sdb mkpart primary 70GB 75GB

#swap 4GB - sda[ab]6
parted -a optimal -- /dev/sda mkpart primary 75GB 79GB
parted -a optimal -- /dev/sdb mkpart primary 75GB 79GB

#check partitions
fdisk -l /dev/sda
fdisk -l /dev/sdb

#Der Ergebnis sollte so aussehen:
root@rescue:~# fdisk -l /dev/sda
Disk /dev/sda: 80 GiB, 85899345920 bytes, 167772160 sectors
Disk model: VMware Virtual S
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 38141AEF-C18E-4C75-AD59-F7F92047C75E

Device         Start       End  Sectors  Size Type
/dev/sda1       2048     16383    14336    7M Linux filesystem
/dev/sda2      16384  78125055 78108672 37.2G Linux filesystem
/dev/sda3   78125056  97656831 19531776  9.3G Linux filesystem
/dev/sda4   97656832 136718335 39061504 18.6G Linux filesystem
/dev/sda5  136718336 146485247  9766912  4.7G Linux filesystem
/dev/sda6  146485248 154296319  7811072  3.7G Linux filesystem
root@rescue:~# fdisk -l /dev/sdb
Disk /dev/sdb: 80 GiB, 85899345920 bytes, 167772160 sectors
Disk model: VMware Virtual S
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: gpt
Disk identifier: 8F143815-E105-4201-B39F-95574802E1A6

Device         Start       End  Sectors  Size Type
/dev/sdb1       2048     16383    14336    7M Linux filesystem
/dev/sdb2      16384  78125055 78108672 37.2G Linux filesystem
/dev/sdb3   78125056  97656831 19531776  9.3G Linux filesystem
/dev/sdb4   97656832 136718335 39061504 18.6G Linux filesystem
/dev/sdb5  136718336 146485247  9766912  4.7G Linux filesystem
/dev/sdb6  146485248 154296319  7811072  3.7G Linux filesystem

Paritionen als Boot bzw. RAID markieren
#boot
parted /dev/sda set 1 bios_grub on
parted /dev/sdb set 1 bios_grub on

#server
parted /dev/sda set 2 raid on
parted /dev/sdb set 2 raid on

#rescue
parted /dev/sda set 3 raid on
parted /dev/sdb set 3 raid on

#backup
parted /dev/sda set 4 raid on
parted /dev/sdb set 4 raid on

#data
parted /dev/sda set 5 raid on
parted /dev/sdb set 5 raid on

#swap
parted /dev/sda set 6 raid on
parted /dev/sdb set 6 raid on

Das RAID erstellen
mdadm --create --metadata=1.2 --name=server /dev/md/0 --level=1 --raid-devices=2 /dev/sda2 /dev/sdb2 --assume-clean
mdadm --create --metadata=1.2 --name=rescue /dev/md/1 --level=1 --raid-devices=2 /dev/sda3 /dev/sdb3 --assume-clean
mdadm --create --metadata=1.2 --name=backup /dev/md/2 --level=1 --raid-devices=2 /dev/sda4 /dev/sdb4 --assume-clean
mdadm --create --metadata=1.2 --name=data /dev/md/3 --level=1 --raid-devices=2 /dev/sda5 /dev/sdb5 --assume-clean
mdadm --create --metadata=1.2 --name=swap /dev/md/4 --level=1 --raid-devices=2 /dev/sda6 /dev/sdb6 --assume-clean

#check RAID
cat /proc/mdstat
Das Ergibnis sollte so aussehen:
root@rescue:~# cat /proc/mdstat
Personalities : [raid0] [raid1] [raid4] [raid5] [raid6] [raid10]
md4 : active raid1 sdb6[1] sda6[0]
      3902464 blocks super 1.2 [2/2] [UU]

md3 : active raid1 sdb5[1] sda5[0]
      4878336 blocks super 1.2 [2/2] [UU]

md2 : active raid1 sdb4[1] sda4[0]
      19513344 blocks super 1.2 [2/2] [UU]

md1 : active raid1 sdb3[1] sda3[0]
      9756672 blocks super 1.2 [2/2] [UU]

md0 : active raid1 sdb2[1] sda2[0]
      39020544 blocks super 1.2 [2/2] [UU]

unused devices: <none>


#speed up raid build
echo 200000 > /proc/sys/dev/raid/speed_limit_min
echo 400000 > /proc/sys/dev/raid/speed_limit_max
cat /proc/mdstat

mkfs.ext4 -m2 /dev/md/0
mkfs.ext4 -m2 /dev/md/1
mkfs.ext4 -m2 /dev/md/2
mkfs.ext4 -m2 /dev/md/3
mkswap /dev/md/4

e2label /dev/md/0 server
e2label /dev/md/1 rescue
e2label /dev/md/2 backup
e2label /dev/md/3 data

#per blkid und grep checken ob die paritionen auf dem raid sind
root@rescue:~# blkid | grep md
/dev/md4: UUID="52b22d0d-b4c8-478d-9873-5a5e46fecf19" TYPE="swap"
/dev/md2: LABEL="backup" UUID="f7e3854b-a875-4722-9ba0-d67f3151719d" BLOCK_SIZE="4096" TYPE="ext4"
/dev/md0: LABEL="server" UUID="90c6e9a5-a5b9-4061-9af3-a4e75052b4e6" BLOCK_SIZE="4096" TYPE="ext4"
/dev/md3: LABEL="data" UUID="fbfce877-2acb-4562-a213-7a219eeca644" BLOCK_SIZE="4096" TYPE="ext4"
/dev/md1: LABEL="rescue" UUID="5dead53d-bd5e-44cc-8c5a-4459e69f6540" BLOCK_SIZE="4096" TYPE="ext4"

#server
blkid -o value -s UUID /dev/md/0 UUID="90c6e9a5-a5b9-4061-9af3-a4e75052b4e6"

#rescue
blkid -o value -s UUID /dev/md/1 UUID="5dead53d-bd5e-44cc-8c5a-4459e69f6540"

#backup
blkid -o value -s UUID /dev/md/2 UUID="f7e3854b-a875-4722-9ba0-d67f3151719d"

#data
blkid -o value -s UUID /dev/md/3 UUID="fbfce877-2acb-4562-a213-7a219eeca644"

#swap
blkid -o value -s UUID /dev/md/4 UUID="52b22d0d-b4c8-478d-9873-5a5e46fecf19"

mkdir /server
mkdir /rescue
mkdir /backup
mkdir /data

mount UUID=90c6e9a5-a5b9-4061-9af3-a4e75052b4e6 /server
mount UUID=5dead53d-bd5e-44cc-8c5a-4459e69f6540 /rescue
mount UUID=f7e3854b-a875-4722-9ba0-d67f3151719d /backup
mount UUID=fbfce877-2acb-4562-a213-7a219eeca644 /data
swapon UUID=52b22d0d-b4c8-478d-9873-5a5e46fecf19

alles checken z.B.
df -h

debian 9 installieren in der VM @ home
nur terminal und openssh-server

#now install openssh-server and sudo from the ISO
apt-get install openssh-server sudo

#add user to sudo group
adduser username sudo

Update von Debian 9 (Stretch)
Die Debian Repos für alte Debian Version liegen unter archive.debian.org

sudo nano /etc/apt/sources.list
deb http://archive.debian.org/debian/ stretch main contrib non-free
deb http://archive.debian.org/debian/ stretch-proposed-updates main contrib non-free
deb http://archive.debian.org/debian-security stretch/updates main contrib non-free

#update the system
sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade

#reboot the system
reboot

Update Debian 9 (Stretch) auf Debian 10 (Buster)

sudo nano /etc/apt/sources.list
deb http://archive.debian.org/debian/ buster main contrib non-free
deb http://archive.debian.org/debian/ buster-proposed-updates main contrib non-free
deb http://archive.debian.org/debian-security buster/updates main contrib non-free

sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade
sudo reboot

Update Debian 10 (Buster) auf Debian 11 (Bullseye)

sudo nano /etc/apt/sources.list
deb http://deb.debian.org/debian bullseye main contrib non-free
deb http://deb.debian.org/debian bullseye-updates main contrib non-free
deb http://security.debian.org/debian-security bullseye-security main contrib non-free

sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade
sudo reboot

Update Debian 11 (Bullseye) auf Debian 12 (Bookworm)
sudo nano /etc/apt/sources.list
deb http://deb.debian.org/debian bookworm main contrib non-free-firmware non-free
deb http://deb.debian.org/debian bookworm-updates main contrib non-free-firmware non-free
deb http://security.debian.org/debian-security bookworm-security main contrib non-free-firmware non-free

sudo apt-get update && sudo apt-get upgrade && sudo apt-get dist-upgrade
sudo reboot

#Voila
#A Debian 12 running on VM
#Upgraded from Debian 9
#using old stype logfiles
#check
ls -lha /var/log/auth.log
ls -lha /var/log/syslog
ls -lha /var/log/lastlog

#Time to clean up
#Remove old deb files, kernels and packages not needed anymore
sudo apt-get clean
dpkg -l | grep linux-image
sudo apt-get remove --purge linux-image-5.10.0-37-amd64 linux-image-4.9.0-19-amd64 linux-image-4.9.0-13-amd64 linux-image-4.19.0-27-amd64
sudo apt-get autoremove

Suche nach deinstallieren Paketen wo aber noch Konfigurationsdateien zurückgeblieben sind
dpkg -l | grep '^rc'
sudo apt-get remove --purge bsdmainutils irqbalance libglib2.0-0 libpython3.7-minimal libpython3.9-minimal python3.7-minimal python3.9-minimal sgml-base shared-mime-info xdg-user-dirs xml-core

Zeit SSH anzupassen

sudo nano /etc/ssh/sshd_config
Port 9999
ListenAddress 0.0.0.0
PermitRootLogin no
AuthorizedKeysFile .ssh/authorized_keys .ssh/authorized_keys2
PasswordAuthentication no
PubkeyAuthentication yes
UseDNS no

#set a SSH key for user that he can login
#so run this as your user NOT as root

cd && mkdir .ssh && chmod 700 .ssh && cd .ssh
nano authorized_keys
ssh-rsa bla bla
chmod 600 authorized_keys

Zeit ein Image oder hier tar Archive vom System zu machen
Dazu SystemRescueCD oder etwas vergleichbares booten, das Root File System mounten und verpacken.

Über mdadm lassen sich auch die Namen der Dateisysteme ausgeben

mdadm --examine --brief --scan
ARRAY /dev/md/server  metadata=1.2 UUID=5f99973b:c62116ae:50953dbc:ef038594
ARRAY /dev/md/rescue  metadata=1.2 UUID=4979ffdd:bcd7ccba:61f270cb:9c0e66b5
ARRAY /dev/md/backup  metadata=1.2 UUID=13543f72:1ee8f64c:e0617fb9:79c502dd
ARRAY /dev/md/data  metadata=1.2 UUID=989c1c16:26a2842a:b5333c8a:b69370d7
ARRAY /dev/md/swap  metadata=1.2 UUID=33e3b5dd:215e13c6:e42a9533:1d1b8249

mkdir /server
mount /dev/md/server /server

mkdir /backup
mount /dev/md/backup /backup

Überprüfen kann man das einfach

df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/md124       37G  1.2G   34G   4% /server
/dev/md126       19G   24K   18G   1% /backup

Jetzt mir tar ein backup von /server machen.

cd /server
datum=$(date +%Y-%m-%d)
tar --use-compress-program=pbzip2 -cvpf /backup/server_grundsystem_$datum.tar.bz2 .

hat das funktioniert?
ls -lah /backup
-rw-r--r-- 1 root root 342M Jan  5 23:51 server_grundsystem_2026-01-05.tar.bz2

Die Datei server_grundsystem_2026-01-05.tar.bz2 jetzt irgendwo speichern das man sie später
auf der Hetzner Server hochladen kann.

#Zurück ins RZ, Hetzner Rescue System booten und Paritionen einrichten
hetzner_server_raid_einrichten
werden alle Platten erkannt?
lsscsi

Ist schon ein RAID vorhanden?
cat /proc/mdstat

Wenn ja RAID stoppen
mdadm --stop /dev/md123
mdadm --stop /dev/md124
mdadm --stop /dev/md125
mdadm --stop /dev/md126
mdadm --stop /dev/md127

Platten schnell löschen
wipefs -a /dev/sda
wipefs -a /dev/sdb
sgdisk --zap-all /dev/sda
sgdisk --zap-all /dev/sdb

Ich erstelle die Partionen in GiB, sind ca. 7% grösser als GB.
Bei einer 8TB Platte sind 7452GiB verfübar.
Anzeigen lassen kann man sich das mit

parted /dev/sda unit GiB print free
7452 GiB

Neues RAID erstellen
create GPT partion table

parted /dev/sda mklabel gpt
parted /dev/sdb mklabel gpt

create partitions
boot 8MB - sda1

parted -a optimal -- /dev/sda mkpart primary 1MB 8MB
parted -a optimal -- /dev/sdb mkpart primary 1MB 8MB

server 320GiB - sda2

parted -a optimal -- /dev/sda mkpart primary 8MB 320GiB
parted -a optimal -- /dev/sdb mkpart primary 8MB 320GiB

rescue 16GiB - sda3

parted -a optimal -- /dev/sda mkpart primary 320GiB 336GiB
parted -a optimal -- /dev/sdb mkpart primary 320GiB 336GiB

#backup 200GiB - sda4
parted -a optimal -- /dev/sda mkpart primary 336GiB 536GiB
parted -a optimal -- /dev/sdb mkpart primary 336GiB 536GiB

#swap 64GiB - sda5
parted -a optimal -- /dev/sda mkpart primary 536GiB 600GiB
parted -a optimal -- /dev/sdb mkpart primary 536GiB 600GiB

#data rest (minus 5GiB) - sda6
parted -a optimal -- /dev/sda mkpart primary 600GiB 7447GiB
parted -a optimal -- /dev/sdb mkpart primary 600GiB 7447GiB

Damit wären noch 5 GiB Platz unbenutzt.
Sollte man mal schnell 5 GiB brauchen oder falls eine Platte sollte
Sie wegen eines Defektes ausgetaucht werden müssen ein paar MB kleiner ausfallen sollte.

#check partitions
fdisk -l /dev/sda
fdisk -l /dev/sdb

root@rescue ~ # fdisk -l /dev/sda
Disk /dev/sda: 7.28 TiB, 8001563222016 bytes, 15628053168 sectors
Disk model: HGST HUH721008AL
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 35A175A2-0245-49BB-86B9-EB1119D45BDC

Device          Start         End     Sectors  Size Type
/dev/sda1        2048       16383       14336    7M Linux filesystem
/dev/sda2       16384   671088639   671072256  320G Linux filesystem
/dev/sda3   671088640   704643071    33554432   16G Linux filesystem
/dev/sda4   704643072  1124073471   419430400  200G Linux filesystem
/dev/sda5  1124073472  1258291199   134217728   64G Linux filesystem
/dev/sda6  1258291200 15617490943 14359199744  6.7T Linux filesystem
root@rescue ~ # fdisk -l /dev/sdb
Disk /dev/sdb: 7.28 TiB, 8001563222016 bytes, 15628053168 sectors
Disk model: HGST HUH721008AL
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 4096 bytes
Disklabel type: gpt
Disk identifier: 21CC194C-693B-48F3-A754-55593D50315A

Device          Start         End     Sectors  Size Type
/dev/sdb1        2048       16383       14336    7M Linux filesystem
/dev/sdb2       16384   671088639   671072256  320G Linux filesystem
/dev/sdb3   671088640   704643071    33554432   16G Linux filesystem
/dev/sdb4   704643072  1124073471   419430400  200G Linux filesystem
/dev/sdb5  1124073472  1258291199   134217728   64G Linux filesystem
/dev/sdb6  1258291200 15617490943 14359199744  6.7T Linux filesystem

Jetzt müssen wir dem System noch sagen was wie die Partitionen verwendet werden sollen

#boot
parted /dev/sda set 1 bios_grub on
parted /dev/sdb set 1 bios_grub on

#server
parted /dev/sda set 2 raid on
parted /dev/sdb set 2 raid on

#rescue
parted /dev/sda set 3 raid on
parted /dev/sdb set 3 raid on

#backup
parted /dev/sda set 4 raid on
parted /dev/sdb set 4 raid on

#swap
parted /dev/sda set 5 raid on
parted /dev/sdb set 5 raid on

#data
parted /dev/sda set 6 raid on
parted /dev/sdb set 6 raid on

Jetzt teilen wir MDADM mit welche Platten zusammen ein RAID1 bilden sollen und wie es heissen soll.
ACHTUNG
Ich nutze hier die Option --assume-clean
Das ist GEFÄHRLICH man sollte sie weglassen, warum ich das jetzt mache darauf werde hier nicht weiter gehen...
Kurz und knapp... es spart mir jetzt Zeit und ich verlege einen Schritt der jetzt gemacht werden sollte in die Zukunft

mdadm --create --metadata=1.2 --name=server /dev/md/0 --level=1 --raid-devices=2 /dev/sda2 /dev/sdb2 --assume-clean
mdadm --create --metadata=1.2 --name=rescue /dev/md/1 --level=1 --raid-devices=2 /dev/sda3 /dev/sdb3 --assume-clean
mdadm --create --metadata=1.2 --name=backup /dev/md/2 --level=1 --raid-devices=2 /dev/sda4 /dev/sdb4 --assume-clean
mdadm --create --metadata=1.2 --name=swap /dev/md/3 --level=1 --raid-devices=2 /dev/sda5 /dev/sdb5 --assume-clean
mdadm --create --metadata=1.2 --name=data /dev/md/4 --level=1 --raid-devices=2 /dev/sda6 /dev/sdb6 --assume-clean

#check RAID
cat /proc/mdstat
root@rescue ~ # cat /proc/mdstat
Personalities : [raid1]
md4 : active raid1 sdb6[1] sda6[0]
      7179467776 blocks super 1.2 [2/2] [UU]
      bitmap: 54/54 pages [216KB], 65536KB chunk

md3 : active raid1 sdb5[1] sda5[0]
      67042304 blocks super 1.2 [2/2] [UU]

md2 : active raid1 sdb4[1] sda4[0]
      209583104 blocks super 1.2 [2/2] [UU]
      bitmap: 0/2 pages [0KB], 65536KB chunk

md1 : active raid1 sdb3[1] sda3[0]
      16759808 blocks super 1.2 [2/2] [UU]

md0 : active raid1 sdb2[1] sda2[0]
      335404032 blocks super 1.2 [2/2] [UU]
      bitmap: 0/3 pages [0KB], 65536KB chunk

unused devices: <none>


Lässt man --assume-clean weg und will den RAID Aufbau beschleunigen
macht hier keinen Sinn...
speed up raid build

echo 200000 > /proc/sys/dev/raid/speed_limit_min
echo 400000 > /proc/sys/dev/raid/speed_limit_max
cat /proc/mdstat

Erstellen von Dateisystemen auf den RAID1 Partitionen und einbinden des SWAP Speichers
mkfs.ext4 -m1 /dev/md/0
mkfs.ext4 -m0 /dev/md/1
mkfs.ext4 -m0 /dev/md/2
mkfs.ext4 -m0 /dev/md/4
mkswap /dev/md/3

Vielleicht nochmal eine Zusammenfassung vom RAID anschauen?
mdadm --examine --brief --scan

Dateisysteme benennen
e2label /dev/md/0 server
e2label /dev/md/1 rescue
e2label /dev/md/2 backup
e2label /dev/md/4 data

Abfragen der UUIDs zum mounten
blkid -o value -s UUID /dev/md/0 57763470-aa57-465c-9a5d-a61c0354401e server
blkid -o value -s UUID /dev/md/1 51448698-12e1-4d34-b131-3519536b748b rescue
blkid -o value -s UUID /dev/md/2 03b049f2-df78-4984-8055-a992656f6a75 backup
blkid -o value -s UUID /dev/md/3 bbf574dc-1a51-49d2-b512-c75ae5d47dc8 swap
blkid -o value -s UUID /dev/md/4 70adc66b-075d-46ab-82c5-263323a44a9f data

Verzeichnisse erstellen und mounten
mkdir /server && mkdir /rescue && mkdir /backup && mkdir /data
mount LABEL=server /server && mount LABEL=rescue /rescue && mount LABEL=backup /backup && mount LABEL=data /data

Hat soweit alles geklappt?
Haben die Partitionen die richtige Grösse und Namen?

df -h
Filesystem                    Size  Used Avail Use% Mounted on
/dev/md0                      314G   28K  311G   1% /server
/dev/md1                      3.9G   24K  3.8G   1% /rescue
/dev/md2                      251G   28K  249G   1% /backup
/dev/md3                      6.6T   28K  6.6T   1% /data

Die Datei server_grundsystem_2026-01-05.tar.bz2 auf den Server im Verzeichnis /backup speichern.
mc ist hier dein Freund!
Und im Verzeichnis /server entpacken

cd /server
tar -xvpf /backup/server_grundsystem_2026-01-05.tar.bz2 . --numeric-owner

Das Kernel Dateisystem nach /server mounten

cd
mount -o bind /dev /server/dev
mount -o bind /sys /server/sys
mount -o bind /proc /server/proc

Ins Server System "wechseln"
chroot /server /bin/bash

Zeit das Images welches ja noch die Einstellen aus der VM von Zuhause hat auf Hetzner anzupassen

nano /etc/network/interfaces
auto eth0
iface eth0 inet static
  address 1.2.3.4
  netmask 2.3.4.5
  gateway 3.4.5.6
  dns-nameservers 1.1.1.1 8.8.8.8

nano /etc/default/grub
GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="server"
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0 loglevel=0 noresume nohibernate"
GRUB_DISABLE_OS_PROBER=true

cd /etc/grub.d/
chmod 400 20_linux_xen 30_os-prober 30_uefi-firmware 40_custom 41_custom

nano /etc/resolv.conf
nameserver 1.1.1.1
nameserver 8.8.8.8

nano /etc/hostname
server

nano /etc/fstab
#<file system> <mount point> <type> <options> <dump> <pass>
UUID=57763470-aa57-465c-9a5d-a61c0354401e / ext4 errors=remount-ro 0 1
UUID=51448698-12e1-4d34-b131-3519536b748b /rescue ext4 defaults 0 2
UUID=03b049f2-df78-4984-8055-a992656f6a75 /backup ext4 defaults 0 2
UUID=bbf574dc-1a51-49d2-b512-c75ae5d47dc8 none swap sw 0 0
UUID=70adc66b-075d-46ab-82c5-263323a44a9f /data ext4 defaults 0 2

nano /etc/hosts
127.0.0.1 localhost.localdomain localhost
1.2.3.4 hostname.domain.tld hostname
127.0.1.1 hostname.domain.tld hostname

/usr/share/mdadm/mkconf

/usr/share/mdadm/mkconf > /etc/mdadm/mdadm.conf
nano /etc/initramfs-tools/conf.d/resume
RESUME=none

Stellen wir das RAID gleich auf kurze Zahlen
nano /etc/mdadm/mdadm.conf
ARRAY /dev/md/0  metadata=1.2 UUID=114cc9e2:3bd499a8:407d4290:030d381b name=rescue:server
ARRAY /dev/md/1  metadata=1.2 UUID=bde62eb9:d837ae9e:d3462008:9b707324 name=rescue:rescue
ARRAY /dev/md/2  metadata=1.2 UUID=c2cfb659:fad63212:5e78aa0a:e6973891 name=rescue:backup
ARRAY /dev/md/3  metadata=1.2 UUID=b0d05c87:605c7cd3:fca05443:db9b9879 name=rescue:swap
ARRAY /dev/md/4  metadata=1.2 UUID=2a0c1aec:e7471fe9:90ddf9e6:5bd5a92c name=rescue:data

update-grub
update-initramfs -u -k all
dpkg-reconfigure grub-pc
reboot

Nach ~ 2 Minuten warten...
Login per SSH sollte möglich sein!

Der Server läuft jetzt mit Debian 12, auf der "echten" Hardware.
Ist unter seiner IPv4 Adresse erreichbar, die auch fest in /etc/network/interfaces steht.
Im Gegensatz zu einem frisch installieren Debian 12 oder 13 sind auch noch die alten Logdateien da!
Erreichbar ist nur ein SSH Ports der nicht auf dem default Port läuft,
so gibt es schonal weniger Angriffe von Bots und Skript-Kiddies.

Überprüfen kann man das einfach mit
sudo ss -tulpen
Netid           State            Recv-Q           Send-Q                     Local Address:Port                     Peer Address:Port           Process
tcp             LISTEN           0                128                              0.0.0.0:9999                          0.0.0.0:*               users:(("sshd",pid=656,fd=3)) ino:22920 sk:1 cgroup:/system.slice/ssh.service <->

raid namen?
df -h

#so der Server sollte jetzt mir einem RAID1 laufen und booten...
#Zeit mit dem Einrichten des Systems anzufangen

Erstaml checken ob es Upates für Debian gibt
sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-update

und wenn es einen neuen Kernel gibt
sudo reboot

Wo das Grundsystem jetzt läuft installieren wir erstmal ein paar Tools
sudo apt-get update && sudo apt-get install screen molly-guard htop mc pbzip2 man less

sudoers anpassen
sudo nano /etc/sudoers
Defaults pwfeedback
$BENUTZER ALL = NOPASSWD: /usr/bin/cat, /usr/sbin/reboot, /usr/sbin/shutdown

Vollständige Debian 12 Reops mit Quellcode erstmal aber auskommentiert
sudo nano /etc/apt/sources.list

deb http://deb.debian.org/debian/ bookworm main contrib non-free non-free-firmware
#deb-src http://deb.debian.org/debian/ bookworm main contrib non-free non-free-firmware

deb http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware
#deb-src http://security.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware

deb http://deb.debian.org/debian/ bookworm-updates main contrib non-free non-free-firmware
#deb-src http://deb.debian.org/debian/ bookworm-updates main contrib non-free non-free-firmware

sudo apt-get update

bash autovervollstaendigung
sudo apt-get install bash-completion
sudo nano /etc/bash.bashrc
# enable bash completion in interactive shells
if ! shopt -oq posix; then
  if [ -f /usr/share/bash-completion/bash_completion ]; then
    . /usr/share/bash-completion/bash_completion
  elif [ -f /etc/bash_completion ]; then
    . /etc/bash_completion
  fi
fi

kein IPv6
sudo nano /etc/sysctl.d/99-sysctl.conf
net.ipv6.conf.all.disable_ipv6=1
net.ipv6.conf.default.disable_ipv6=1
net.ipv6.conf.lo.disable_ipv6=1
net.ipv6.conf.eth0.disable_ipv6=1

default editor
sudo nano /etc/environment
EDITOR=nano

sudo nano /etc/profile.d/alias.sh
alias ll="ls -lah"
alias cls="clear"
alias dir="ls -lha | less"
alias zeit='echo -n "Berlin: $(date +%H:%M:%S) "; echo -n "UTC: $(date +%H:%M:%S -u) "; echo "Sekunden:" $(numfmt --grouping $(date +%s))'
alias cpu_speed='watch -n 1 "grep \"^[c]pu MHz\" /proc/cpuinfo"'

sudo chown root:root /etc/profile.d/alias.sh && sudo chmod 644 /etc/profile.d/alias.sh

raid_status_beim_login
sudo nano /etc/profile.d/raid_check.sh
#!/bin/bash
MDSTAT="/proc/mdstat"
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'   # No Color
if grep -qE "_|inactive|faulty" "$MDSTAT"; then
    echo -e "${RED}Achtung Fehler im RAID${NC}"
else
    echo -e "${GREEN}Das RAID ist OK${NC}"
fi

sudo chown root:root /etc/profile.d/raid_check.sh && sudo chmod 644 /etc/profile.d/raid_check.sh

anzeige_welche_installation_gebootet_ist_beim_login
sudo nano /etc/profile.d/check_boot.sh
#!/bin/bash
# Gelbe Farbe
YELLOW='\033[1;33m'
NC='\033[0m'  # No Color
echo -e "${YELLOW}Server Installation${NC}"

sudo chown root:root /etc/profile.d/check_boot.sh && sudo chmod 644 /etc/profile.d/check_boot.sh

neofetch_installieren_und_beim_start_anzeigen_lassen
sudo apt-get update && sudo apt-get install neofetch

sudo nano /etc/profile.d/neofetch.sh
#!/bin/bash
printf "\n"
neofetch

sudo chown root:root /etc/profile.d/neofetch.sh && sudo chmod 755 /etc/profile.d/neofetch.sh

rc-local_wieder_herstellen
sudo nano /etc/systemd/system/rc-local.service

[Unit]
Description=/etc/rc.local
ConditionPathExists=/etc/rc.local
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
ExecStart=/etc/rc.local start
TimeoutSec=0
StandardOutput=tty
RemainAfterExit=yes
SysVStartPriority=99

[Install]
WantedBy=multi-user.target

sudo chown root:root /etc/systemd/system/rc-local.service && sudo chmod 644 /etc/systemd/system/rc-local.service

sudo nano /etc/rc.local
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

exit 0

sudo chown root:root /etc/rc.local && sudo chmod 770 /etc/rc.local && sudo systemctl enable rc-local && sudo systemctl start rc-local


boot.log_beim_starten
sudo nano /etc/rc.local
echo $(hostname -f) started at $(date +%Y-%m-%d) $(date +%H:%M) >> /root/reboot.log

Turn_off_standby_and_co
sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target

Time_Sync
sudo apt-get install systemd-timesyncd && sudo systemctl enable systemd-timesyncd && sudo systemctl start systemd-timesyncd

sudo nano /etc/systemd/timesyncd.conf
[Time]
NTP=ptbtime1.ptb.de ptbtime2.ptb.de ptbtime3.ptb.de
FallbackNTP=0.debian.pool.ntp.org 1.debian.pool.ntp.org 2.debian.pool.ntp.org 3.debian.pool.ntp.org

sudo systemctl stop systemd-timesyncd && sudo systemctl start systemd-timesyncd

timedatectl status

WoL_Wake_on_LAN
sudo apt-get update && sudo apt-get install ethtool

sudo nano /etc/rc.local
/usr/sbin/ethtool -s eth0 wol g

ping_fix_for_user
sudo su
echo 'net.ipv4.ping_group_range = 0 2147483647' > /etc/sysctl.d/99-ping.conf
sysctl -p /etc/sysctl.d/99-ping.conf
exit

Make_nano_a_bit_nicer
sudo nano /etc/nanorc
set constantshow
set indicator
set linenumbers

Force_secure_passwords
sudo apt-get install libpam-pwquality

sudo nano /etc/pam.d/common-password
password [success=1 default=ignore] pam_unix.so obscure use_authtok try_first_pass yescrypt sha512 shadow remember=5

sudo nano /etc/security/pwquality.conf
minlen = 14
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1
maxrepeat = 2
usercheck = 0
dictcheck = 1
minclass = 4
maxsequence = 3

Pfade_fuer_eigene_systemweite_Scripts_erstellen
sudo mkdir -p /opt/usr/bin /opt/usr/sbin
sudo chmod 755 /opt/usr/bin /opt/usr/sbin

Pfade_fuer_eigene_systemweite_Scripts_beim_login_setzen
sudo nano /etc/profile.d/opt-usr-path.sh
if [ -d /opt/usr/bin ]; then
    PATH="/opt/usr/bin:$PATH"
fi
if [ -d /opt/usr/sbin ]; then
    PATH="/opt/usr/sbin:$PATH"
fi
export PATH

allow_opt_usr_bin_and_opt_usr_sbin_in_sudo
sudo nano /etc/sudoers
Defaults        secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/opt/usr/sbin:/opt/usr/bin"

script_5_sekunden
sudo nano /opt/usr/bin/5_sekunden
#!/bin/bash
while true; do echo $(date +%H:%M:%S); sleep 5; done

sudo chown root:root /opt/usr/bin/5_sekunden && sudo chmod 555 /opt/usr/bin/5_sekunden

script_apt_clean
sudo nano /opt/usr/sbin/apt_clean
#!/bin/bash
apt-get clean
rm /var/lib/apt/lists/deb.debian.* >> /dev/null 2>&1
rm /var/lib/apt/lists/security.debian* >> /dev/null 2>&1
rm /var/lib/apt/lists/software.virtualmin.com* >> /dev/null 2>&1

sudo chown root:root /opt/usr/sbin/apt_clean && sudo chmod 750 /opt/usr/sbin/apt_clean
/opt/usr/sbin/apt_clean in sudoers eintragen

script_cache_clean
sudo nano /opt/usr/sbin/clean_cache
#!/bin/bash
sync; echo 1 > /proc/sys/vm/drop_caches
sync; echo 2 > /proc/sys/vm/drop_caches
sync; echo 3 > /proc/sys/vm/drop_caches

sudo chown root:root /opt/usr/sbin/clean_cache && sudo chmod 755 /opt/usr/sbin/clean_cache

sudo nano /etc/sudoers
NOPASSWD: /opt/usr/sbin/clean_cache

script_ports
sudo apt-get update && sudo apt-get install net-tools

sudo nano /opt/usr/bin/ports

#!/bin/bash
#netstat -tulpen
netstat -tulpen | awk 'NR>2 {print $1, $4, $NF}' | sed -E 's/[0-9]+\///' | sort -u -t: -k2n | column -t

sudo chown root:root /opt/usr/bin/ports && sudo chmod 555 /opt/usr/bin/ports

sudo nano /etc/sudoers
NOPASSWD: /opt/usr/bin/ports

script_rechte_anpassen
sudo nano /opt/usr/bin/rechte_anpassen

#!/bin/bash
echo "BEISPIEL:"
echo "chown user:group -R ."
echo "find . -type d -exec chmod 750 {} +"
echo "find . -type f -exec chmod 640 {} +"

sudo chown root:root /opt/usr/bin/rechte_anpassen && sudo chmod 555 /opt/usr/bin/rechte_anpassen

script_updates
sudo nano /opt/usr/sbin/updates

#!/bin/bash
apt-get update && apt-get upgrade --yes && apt-get dist-upgrade --yes && apt-get clean

sudo chown root:root /opt/usr/sbin/updates && sudo chmod 550 /opt/usr/sbin/updates
/opt/usr/sbin/updates in sudoers eintragen!

Restic_installieren
sudo apt-get install restic wget && sudo restic self-update && sudo mkdir -p /etc/bash_completion.d && sudo restic generate --bash-completion /etc/bash_completion.d/restic

SSH_hardening_www.sshaudit.com
sudo su
rm /etc/ssh/ssh_host_*
ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""
ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""
echo -e "\nHostKey /etc/ssh/ssh_host_ed25519_key\nHostKey /etc/ssh/ssh_host_rsa_key" >> /etc/ssh/sshd_config
awk '$5 >= 3071' /etc/ssh/moduli > /etc/ssh/moduli.safe
mv /etc/ssh/moduli.safe /etc/ssh/moduli
echo -e "# Restrict key exchange, cipher, and MAC algorithms, as per sshaudit.com\n# hardening guide.\n KexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,gss-curve25519-sha256-,diffie-hellman-group16-sha512,gss-group16-sha512-,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\n\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\nMACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\n\nHostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nRequiredRSASize 3072\n\nCASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nGSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\nHostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\nPubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\n" > /etc/ssh/sshd_config.d/ssh-audit_hardening.conf
exit

apt_only_with_IPv4
sudo nano /etc/apt/apt.conf.d/90force-ipv4
Acquire::ForceIPv4 "true";

Reihenfolge_der_Scripte_beim_Login_anpassen
sudo su -
cd /etc/profile.d/
mv neofetch.sh zz_000_neofetch.sh
mv check_boot.sh zz_100_check_boot.sh
mv raid_check.sh zz_200_raid_check.sh

script_reboot_to_rescue
sudo nano /opt/usr/sbin/reboot_to_rescue
#!/bin/bash
/usr/sbin/grub-reboot 2 && /usr/bin/sync && /usr/sbin/reboot

sudo chmod 750 /opt/usr/sbin/reboot_to_rescue

sudo nano /etc/sudoers
NOPASSWD: /opt/usr/sbin/reboot_to_rescue

Grub_Eintrag_fuer_Rescue_System
sudo nano /etc/grub.d/11_rescue
#! /bin/sh
echo "Füge einen Starteintrag für Rescue ein" >&2
cat << EOF
menuentry "Rescue" {
    insmod part_gpt
    insmod search_fs_uuid
    search --fs-uuid --no-floppy --set=root 51448698-12e1-4d34-b131-3519536b748b
    configfile /boot/grub/grub.cfg
}
EOF

sudo chmod 755 /etc/grub.d/11_rescue
sudo update-grub

script_um_das_Rescue_System_zu_sichern
sudo nano /opt/usr/sbin/backup_rescue
#!/bin/bash
DATUM=$(date +%Y-%m-%d_%H-%M)
cd /rescue
tar --use-compress-program=pbzip2 -cvpf /backup/rescue_${DATUM}_$1.tar.bz2 .
cd /backup
md5sum rescue_${DATUM}_$1.tar.bz2 > rescue_${DATUM}_$1.tar.bz2.md5

sudo chown root:root /opt/usr/sbin/backup_rescue && sudo chmod 750 /opt/usr/sbin/backup_rescue
sudo nano /etc/sudoers
NOPASSWD: /opt/usr/sbin/backup_rescue

#Das eigene Rescue System installieren
sudo su
cd /rescue
tar -xvpf /backup/server_grundsystem_2026-01-05.tar.bz2 . --numeric-owner
cd

mount -o bind /dev /rescue/dev
mount -o bind /sys /rescue/sys
mount -o bind /proc /rescue/proc

chroot /rescue /bin/bash

nano /etc/network/interfaces
auto eth0
iface eth0 inet static
  address 1.2.3.4
  netmask 2.3.4.5
  gateway 3.4.5.6
  dns-nameservers 1.1.1.1 8.8.8.8
 
nano /etc/default/grub
GRUB_DEFAULT=0
GRUB_TIMEOUT=5
GRUB_DISTRIBUTOR="rescue at server"
GRUB_CMDLINE_LINUX_DEFAULT=""
GRUB_CMDLINE_LINUX="net.ifnames=0 biosdevname=0 loglevel=0 noresume nohibernate"
GRUB_DISABLE_OS_PROBER=true

cd /etc/grub.d/
chmod 400 20_linux_xen 30_os-prober 30_uefi-firmware 40_custom 41_custom

nano /etc/resolv.conf
nameserver 1.1.1.1
nameserver 8.8.8.8

nano /etc/hostname
server

etc_fstab_auf_rescue_anpassen
nano /etc/fstab
#<file system> <mount point> <type> <options> <dump> <pass>
UUID=51448698-12e1-4d34-b131-3519536b748b / ext4 errors=remount-ro 0 1
UUID=57763470-aa57-465c-9a5d-a61c0354401e /server ext4 defaults 0 2

server_verzeichnis_auf_dem_Rescue_System_erstellen
mkdir /server

Grub_2_und_RAID_Problem_loesen
#Problem with grub-reboot and RAID
#reboot config is with raid permnent and saved in the server/boot partition.
#can be fixed by copying a file
#
WARNING: Detected GRUB environment block on diskfilter device
2 will remain the default boot entry until manually cleared with:
    grub-editenv /boot/grub/grubenv unset next_entry

mount /server
cp /server/boot/grub/grubenv /root/server_boot_grub_grubenv
nano /root/server_boot_grub_grubenv
next_entry=0

nano /usr/sbin/reboot_to_server
cp /root/server_boot_grub_grubenv /server/boot/grub/grubenv && chown root:root /server/boot/grub/grubenv && chmod 644 /server/boot/grub/grubenv && sync && reboot

chown root:root /usr/sbin/reboot_to_server
chmod 500 /usr/sbin/reboot_to_server

Rescue_Installation_beim_Login_anzeigen
nano /etc/profile.d/zz_100_check_boot.sh
!/bin/bash
YELLOW='\033[1;33m'
NC='\033[0m'  # No Color
echo -e "${YELLOW}Rescue Installation${NC}"

update-grub

Rescue_System_Benutzer_anlegen_und_SSH_Key

#Create own user for rescue system
sudo adduser rescue
sudo adduser rescue sudo
sudo nano /etc/sudoers
sudo su - rescue

cd && mkdir .ssh && chmod 700 .ssh && cd .ssh
nano authorized_keys2
ssh-rsa bla bla
chmod 600 authorized_keys2
exit

Rescue_System_alten_Benutzer_entfernen
#delete old user and group
sudo userdel -r $BENUTZER
sudo groupdel $BENUTZER

#Weitere Tools installieren
config-start Server_rsync_installieren
sudo apt-get update && sudo apt-get install rsync

Script_backup_server_to_home
#!/bin/bash
function f_backup() {
while [ 1 ]
do
echo "rsync server to home"
chown backupserver:backupserver /backup/ -R
find /backup/ -type d -exec chmod 750 {} +
find /backup/ -type f -exec chmod 640 {} +
sudo -u backupserver rsync -rt -e "ssh -p 99000" /backup/ backupserver@host.domain.tld:/backup_server/
if [ "$?" = "0" ] ; then
echo  "rsync completed normally"
exit
else
echo "Rsync failure. Backing off and retrying..."
sleep 180
fi
done
}

function f_verbose() {
while [ 1 ]
do
echo "rsync server to home"
chown backupserver:backupserver /backup/ -R
find /backup/ -type d -exec chmod 750 {} +
find /backup/ -type f -exec chmod 640 {} +
sudo -u backupserver rsync -rtv --progress -e "ssh -p 99000" /backup/ backupserver@host.domain.tld:/hdd/backup_server/
if [ "$?" = "0" ] ; then
echo  "rsync completed normally"
exit
else
echo "Rsync failure. Backing off and retrying..."
sleep 180
fi
done
}

if [[ "$1" == "v" ]]; then
    f_backup_verbose
else
    f_backup
fi

sudo chown root:root /opt/usr/sbin/backup_server_to_home && sudo chmod 750 /opt/usr/sbin/backup_server_to_home

sudo nano /etc/sudoers
NOPASSWD: /usr/sbin/backup_server_to_home

#backup_server_to_home MUST be run at least once by you on the console to allow and save the SSH Key from the other system!

sudo nano /etc/crontab
0 3 * * * root /opt/usr/sbin/backup_server_to_home
config-end

config-start Rescue_System_Script_backup_server
sudo nano /opt/usr/sbin/backup_server
#!/bin/bash
DATUM=$(date +%Y-%m-%d_%H-%M)
cd /server
tar --use-compress-program=pbzip2 -cvpf /backup/server_${DATUM}_$1.tar.bz2 .
cd /backup
md5sum server_${DATUM}_$1.tar.bz2 > server_${DATUM}_$1.tar.bz2.md5

sudo chown root:root /opt/usr/sbin/backup_server && sudo chmod 750 /opt/usr/sbin/backup_server
sudo nano /etc/sudoers
NOPASSWD: /opt/usr/sbin/backup_server
config-end

User_backupuser_anlegen_und_SSH_Key_erstellen
sudo adduser backupserver
sudo su - backupserver
cd && mkdir .ssh && chmod 700 .ssh/ && cd .ssh
ssh-keygen -o -a 100 -t ed25519
cat id_ed25519.pub
ssh-ed25519 ********************** backupserver@server
exit

SSH_anpassen_Timeout
sudo nano /etc/ssh/sshd_config
ClientAliveInterval 300
ClientAliveCountMax 5

Benutzer_$BENUTZER_zur_Gurppe_backupuser_hinzufuegen
sudo adduser $BENUTZER backupserver

tldr_installieren

sudo apt-get install tealdeer
sudo mkdir -p /usr/local/share/tldr
sudo chmod 755 /usr/local/share/tldr
sudo TEALDEER_CACHE_DIR=/usr/local/share/tldr tldr --update
echo 'export TEALDEER_CACHE_DIR=/usr/local/share/tldr' | sudo tee /etc/profile.d/tldr.sh

Updates per Cron
sudo nano /opt/usr/sbin/update-tldr-systemwide.sh
#!/bin/bash
# Systemweiten tldr-Cache aktualisieren
export TEALDEER_CACHE_DIR=/usr/local/share/tldr
/usr/bin/tldr --update

sudo chmod +x /opt/usr/sbin/update-tldr-systemwide.sh

nano /etc/crontab
0 0 * * 0 root /opt/usr/sbin/update-tldr-systemwide.sh > /dev/null 2>&1

test:
sudo /opt/usr/sbin/update-tldr-systemwide.sh

#Virtualmin installieren
Virtualmin_installieren
sudo apt-get update
cd && mkdir virtualmin && cd virtualmin && wget https://software.virtualmin.com/gpl/scripts/install.sh && chmod 700 install.sh
sudo screen ./install.sh

#ensure SSH login to host after reboot!
https://hostname.domain.tld:10000
Webmin - Networking -> FirewallD -> Allow Port 9999 TCP
Remove Port 22
add Port 9999

Reboot

Virtualmin_Hostname_Problem
nach der installation von virtualmin ist der hostname
hostname.domain.tld sollte aber nach meiner Menung nur Hostname sein

hostname -f ist
hostname.domain.tld

hostname wieder auf server geändert
sudo nano /etc/hostname

Reboot hier

Virtualmin Post-Installation Wizard
Run email domain lookup server -> Yes, faster mail processing with more RAM used (≈70M)
Enable virus scanning with ClamAV -> Yes, uses up to 2G of RAM
Run SpamAssassin server filter -> No, slower mail processing with less RAM used
Run MariaDB database server -> yes
Run PostgreSQL database server -> no
Change MariaDB password -> SicheresPasswwortMySQL
Primary nameserver -> server -> hostname.domain.tld
Master administrator's email address -> webmin@hostname.domain.tld
Checking Configuration

Mails_von_root_and_Benutzer_weiterleiten
sudo nano /root/.forward
$BENUTZER
sudo chown root:root /root/.forward
sudo chmod 664 /root/.forward

Virtualmin_Dienste_auf_IPv4_umstellen_Spamassassin
sudo nano /etc/default/spamd
#OPTIONS="--create-prefs --max-children 5 --helper-home-dir"
OPTIONS="--create-prefs --max-children 5 --helper-home-dir -4"

Virtualmin_Dienste_auf_IPv4_umstellen_Dovecot
sudo nano /etc/dovecot/dovecot.conf
#listen = *, ::
listen=*

Virtualmin_Dienste_auf_IPv4_umstellen_Postfix
sudo nano /etc/postfix/main.cf
#inet_protocols = all
inet_protocols = ipv4

Virtualmin_Dienste_auf_IPv4_umstellen_Webmin
sudo nano /etc/webmin/miniserv.conf
#ipv6=1
ipv6=0

Virtualmin_Dienste_auf_IPv4_umstellen_ProFTPd
sudo nano /etc/proftpd/proftpd.conf
#UseIPv6 on
UseIPv6 off

Virtualmin_Dienste_auf_IPv4_umstellen_Bind
sudo nano /etc/default/named
#OPTIONS="-u bind"
OPTIONS="-u bind -4"

Virtualmin_CalmAV_fix_wenn_noetig
#ClanAV sig update per WinSCP if needed
sudo su
/var/lib/clamav
chown clamav:clamav /var/lib/clamav -R

Virtualmin_Default_SSL_Hostname_loeschen
#If Virtualmin creates a hostname.domain.tld with SSL cert you may want to delete it
#and create a new one so it shows up in normal doamin list and it not hidden anymore
Server -> Virtualmin -> System Settings -> Features and Plugins
click on DNS for Domain -> Domains "1" will bring up hostname.domain.tld
Search Results -> hostname.domain.tld -> Enabled features
disable DNS for domain
disable Apache website

Virtualmin -> hostname.domain.tld -> Disable and Delete -> Delete Virtual Server

Virtualmin_unnoetige_Dienste_deaktivieren
#stop and remove unneeded service, reinstall needed packages
Webmin -> System -> Bootup and Shutdown
etckeeper, etckeeper, fail2ban, firewalld -> Disable Now and On Boot

Virtualmin_unnoetige_Dienste_deinstallieren
sudo apt-get remove --purge etckeeper git fail2ban firewalld
sudo apt-get autoremove
sudo apt-get install bind9-utils ipset whois curl wget


Virtualmin_config_checken
Virtualmin -> ReCheck Config

Virtualmin_anpassen
Server -> Webmin -> Wemin Config -> Webmin Themes -> Framed Theme

Virtualmin -> System Settings -> Account Plans -> Default Plan ->
Quota for entire server - Unlimited
Quota for server administrator user - Unlimited

Virtualmin -> System Settings -> Features and Plugins
Check Feature or Plugin and default

Virtualmin -> System Settings -> Virtualmin Configuration -> User interface settings ->
Show mailbox size in users list - yes
Show last login in users list - yes
Show databases in users list - yes
Sort virtual servers by - Domain name
Show Pro features overview - No

Virtualmin -> System Settings -> Virtualmin Configuration -> Defaults for new domains ->
Domain name style in username - Full domain name
Force group name to be always same as username - yes
Password field type - Enter password twice

Virtualmin -> System Settings -> Virtualmin Configuration -> Spam filtering options ->
Default delivery for spam -> $HOME/Maildir/.spam/
Default delivery for viruses -> $HOME/Maildir/.virus/
Default spam whitelist option -> Enabled

Virtualmin -> System Settings -> Virtualmin Configuration -> Backup and restore ->
Backup compression format - bzip2
Bzip2 compression command - pbzip2

Virtualmin -> System Settings -> Server Template -> Default ->

DNS for Domain -> Address records for new domains - only Domain and www
DNS for Domain -> Hostname for MX record  -> Hostname -> hostname.domain.tld
DNS for Domain -> Primary DNS server hostname -> Hostname -> hostname.domain.tld

Mail for Domain -> Default quota for mail users - Unlimited
Mail for Domain -> Format for usernames that include domain - "username.domain"

Website for Domain -> Redirect webmail.${DOM} - no
Website for Domain -> Redirect admin.${DOM} - no
Website for Domain -> Redirects for new websites -> Redirect all HTTP requests to HTTPS
Enable HTTP2 protocol for new websites -> yes

Virtualmin -> Email Settings -> Email Greylisting -> enable
Virtualmin -> Email Settings -> Mail Client Configuration -> Enable mail client autoconfiguration? no

Virtualmin_Backups_Planung

sudo mkdir /backup/vmin_full && sudo mkdir /backup/vmin_differential

Virtualmin -> Backup and Restore -> Scheduled Backups
Full Backup
Backup destinations -> Local file or derectory -> /backup/vmin_full/%Y-%m-%d/
Delete old backups -> Yes, after 94 Days
Additional destination options
Do strftime-style time substitutions on file or directory name
Transfer each virtual server after it is backed up
Backup format -> Create destination directory
Email backup report to -> $BENUTZER@hostname.domain.tld
Only send email on failure
Scheduled backup time
Backup level -> Full (all files)
At cron time 0 2 1,10,20 * *

Differential Backup
Backup destinations -> Local file or derectory -> /backup/vmin_differential/%Y-%m-%d/
Delete old backups -> Yes, after 94 Days
Additional destination options
Do strftime-style time substitutions on file or directory name
Transfer each virtual server after it is backed up
Backup format -> Create destination directory
Email backup report to -> $BENUTZER@hostname.domain.tld
Only send email on failure
Scheduled backup time
Backup level -> Differential (changes since full backup)
At cron time 0 2 2-9,11-19,21-31 * *

Virtualmin_mehr_anpassen
Webmin -> Webmin -> Usermin Configuration -> IP Access Control -> Resolve hostnames on every request -> yes
Webmin -> Webmin -> Usermin Configuration -> IP Access Control -> Only allow from listed addresses
hostname.domain.tld #you want allow to have access all times

Webmin -> Webmin -> Webmin Configuration -> IP Access Control -> Resolve hostnames on every request -> yes
Webmin -> Webmin -> Webmin Configuration -> IP Access Control -> Only allow from listed addresses
hostname.domain.tld #yout want allow to have access all times

Webmin -> Webmin -> Webmin Configuration -> Sending Email ->
Mail sending options -> From address for email from Webmin -> Address -> webmin@hostname.domain.tld
Test $BENUTZER@hostname.domain.tld
Webmin -> Server -> Read User Mail -> user

Virtualmin_Systemd-timesync_durch_NTP_ersetzen_old-school-rulez
sudo apt-get update
sudo systemctl status systemd-timesyncd
sudo systemctl stop systemd-timesyncd
sudo systemctl disable systemd-timesyncd
sudo apt-get remove --purge systemd-timesyncd
sudo apt-get install ntpdate smartmontools

Virtualmin_config_check
Virtualmin -> System Settings -> Re-Check Configuration

Virtualmin_NTP_nutzen
Webmin -> Hardware -> System Time -> Time server sync ->
Timeserver hostnames or addresses - ptbtime1.ptb.de
Synchronize on schedule? - Yes, at times below ..

Virtualmin_Software_Updates_nur_anzeigen_lassen
Webmin -> System -> Software Package Updates -> Scheduled Updates
Check for updates -> yes, every day
Email updates report to $BENUTZER@hostname.domain.tld
Action when update needed - Just notify for any updates

Virtualmin_Postfix_Mail_Groesse
Webmin -> Servers -> Postfix Mail Server -> General Resource Control
Max size of a message -> 204800000 (200 MB)

Virtualmin_ProFTPd_Benutzer_ins_home_einsperren
Webmin -> Servers -> ProFTPD Server -> Files and Directories -> Limit users to directories -> home

Virtualmin_2FA
Webmin -> Webmin -> Webmin Configuration -> Two-Factor Authentication
Webmin -> Webmin -> Webmin Users -> root klicken -> Two-Factor Authentication

Virtualmin_Benutzer_keine_unnoetigen_Module_anzeigen_lassen
Webmin -> Webmin -> Webmin Users -> username -> Available Webmin Modules

uncheck
System: Bacula Backup System, LDAP Users and Groups, LDAP Client
Servers: PostgreSQL Database Server, Samba Windows File Sharing, Squid Report Generator, Squid Proxy Server
Networking:  ADSL Client, Fail2Ban Intrusion Detector, IPsec VPN Configuration, Linux Firewall,
             NFS Exports, PPP Dialup Client, PPTP VPN Server, Shorewall Firewall, FirewallD, Kerberos5,
             Linux IPv6 Firewall, NIS Client and Server, PPP Dialin Server, PPTP VPN Client, Shorewall6 Firewall
Hardware: iSCSI Client, iSCSI TGTd, Logical Volume Management, Printer Administration, iSCSI Server, iSCSI Target
Cluster: all
Tools: Command Shell, Terminal, File Manager, Upload and Download, Custom Commands

Taegliche_Backups_von_etc_als_Ersatz_fuer_etckeeper
sudo mkdir /backup/etc
sudo nano /opt/usr/sbin/backup_etc
#!/bin/bash
heute=$(date +%Y-%m-%d_%H-%M)
tar --use-compress-program=pbzip2 -cpf /backup/etc/etc_${heute}.tar.bz2 -C /etc .

sudo chown root:root /opt/usr/sbin/backup_etc
sudo chmod 750 /opt/usr/sbin/backup_etc

sudo nano /etc/sudoers
NOPASSWD: /opt/usr/sbin/backup_etc

sudo nano /etc/crontab
0 1 * * * root /opt/usr/sbin/backup_etc
30 1 * * * root /opt/usr/sbin/backup_server_to_home

keine Subdomains wie ServerAlias mail.${DOM} anlegen lassen
Virtualmin -> Virtualmin -> System Settings -> Server Templates -> Website for domain
Directives and settings for new websites

fstab etwas sicherer machen
 #<file system> <mount point> <type> <options> <dump> <pass>
 2 UUID=57763470-aa57-465c-9a5d-a61c0354401e / ext4 errors=remount-ro,grpquota,relatime,quota,usrquota,rw 0 1
 3 UUID=51448698-12e1-4d34-b131-3519536b748b /rescue ext4 ro,nofail,defaults 0 2
 4 UUID=03b049f2-df78-4984-8055-a992656f6a75 /backup ext4 nofail,defaults 0 2
 5 UUID=bbf574dc-1a51-49d2-b512-c75ae5d47dc8 none swap nofail,sw 0 0
 6 UUID=70adc66b-075d-46ab-82c5-263323a44a9f /data ext4 nofail,defaults 0 2

#Mail auf der Kosole einrichten und an unser Mail System anpassen
erst nach VirtualMin
EMail_auf_der_Konsole

sudo apt-get update && sudo apt-get install mutt msmtp

nano ~/.muttrc
set sendmail="/usr/bin/msmtp"
set from=$BENUTZER@hostname.domain.tld

nano ~/.msmtprc
defaults
tls on
tls_starttls on
tls_certcheck off
auth on
logfile /home/$BENUTZER/msmtprc.log
account default
host 127.0.0.1
from $BENUTZER@hostname.domain.tld
user $BENUTZER
password SicheresPasswwort$BENUTZER
port 25

chmod 600 .msmtprc
ls -lha .msmtprc

echo "Test Message with Attachment" | mutt -s "Mail with file" $BENUTZER@hostname.domain.tld -a ~/.bash_history

#Make mutt use our maildir

nano ~/.muttrc
set sendmail="/usr/bin/msmtp"
set from=$BENUTZER@hostname.domain.tld
set mbox_type=Maildir
set spoolfile="~/Maildir/"
set folder="~/Maildir/"
set mask=".*"
set record="+.Sent"
set postponed="+.Drafts"

# Generate mailboxes for each maildir subdir
mailboxes ! + `\
for file in ~/Maildir/.*; do \
  box=$(basename "$file"); \
  if [ ! "$box" = '.' -a ! "$box" = '..' -a ! "$box" = '.customflags' \
      -a ! "$box" = '.subscriptions' ]; then \
   echo -n "\"+$box\" "; \
  fi; \
done`

# Marcos to display folder list when changing maildir folders
macro index c "<change-folder>?<toggle-mailboxes>" "open a different folder"
macro pager c "<change-folder>?<toggle-mailboxes>" "open a different folder"

# Macros to display folder list when copying/moving messages
macro index C "<copy-message>?<toggle-mailboxes>" "copy a message to a mailbox"
macro index M "<save-message>?<toggle-mailboxes>" "move a message to a mailbox"

#Default Domain (hostname.domain.tld) anlegen
Virtualmin_hostname.domain.tld_anlegen
#Time to get real
#create a host in Virtualmin
#hostname.domain.tld
#use this SSL Cert for the system
Virtualmin -> hostname.domain.tld -> Manage Virtual Server -> Setup SSL Certificate -> Set as default

#CSF Firewall installieren und Grundeinrichtung
CSF_firewall_by_Aetherinox

#Install dependencies
sudo apt-get update && sudo apt-get install -y ipset libcrypt-ssleay-perl libio-socket-inet6-perl libio-socket-ssl-perl libnet-libidn-perl libsocket6-perl perl wget bind9-dnsutils

sudo perl -MCPAN -e 'install GD::Graph' # für "schöne" Statistiken
perl -MCPAN -e 'install Sys::Syslog'

#Download and extract
cd && mkdir csf && cd csf && wget https://download.configserver.dev/csf.tgz && tar -xf csf.tgz && cd csf

#run Run Pre-install Tests
sudo perl csftest.pl

#Install csf
sudo sh install.sh
sudo cp /etc/csf/csf.conf /root/csf.conf.default
ls -lha /etc/csf/csf.conf
-rw------- 1 root root 123K 11. Jan 15:54 /etc/csf/csf.conf

config-start CSF_firewall_base_config
#Now comes the fun part... edit the config file
sudo nano /etc/csf/csf.conf

#Basic setup
TESTING = "0"
RESTRICT_SYSLOG = "3"
TCP_IN = "20,21,22,25,53,853,80,110,143,443,465,587,993,995,9999,10000,20000"
TCP_OUT = "20,21,22,25,53,853,80,110,113,443,587,993,995,25000"
UDP_OUT = "20,21,53,853,113,123,33434:33523"

#Enable DynDNS in csf
DYNDNS = "1"
DYNDNS_IGNORE = "1"
GLOBAL_DYNDNS_INTERVAL = "300"

#binary paths for Debian 12.12 as of 2025-12-20

IPTABLES = "/usr/sbin/iptables"
IPTABLES_SAVE = "/usr/sbin/iptables-save"
IPTABLES_RESTORE = "/usr/sbin/iptables-restore"
IP6TABLES = "/usr/sbin/ip6tables"
IP6TABLES_SAVE = "/usr/sbin/ip6tables-save"
IP6TABLES_RESTORE = "/usr/sbin/ip6tables-restore"
MODPROBE = "/usr/sbin/modprobe"
IFCONFIG = "/usr/sbin/ifconfig"
SENDMAIL = "/usr/sbin/sendmail"
PS = "/usr/bin/ps"
VMSTAT = "/usr/bin/vmstat"
NETSTAT = "/usr/bin/netstat"
LS = "/usr/bin/ls"
MD5SUM = "/usr/bin/md5sum"
TAR = "/usr/bin/tar"
CHATTR = "/usr/bin/chattr"
UNZIP = "/usr/bin/unzip"
GUNZIP = "/usr/bin/gunzip"
DD = "/usr/bin/dd"
TAIL = "/usr/bin/tail"
GREP = "/usr/bin/grep"
ZGREP = "/usr/bin/zgrep"
IPSET = "/usr/sbin/ipset"
SYSTEMCTL = "/usr/bin/systemctl"
HOST = "/usr/bin/host"
IP = "/usr/bin/ip"
CURL = "/usr/bin/curl"
WGET = "/usr/bin/wget"

#Logfile locations for this Debian 9 ->12.12 as of 2025-12-20

HTACCESS_LOG = "/var/log/apache2/error.log"
MODSEC_LOG = "/var/log/apache2/error.log"
SSHD_LOG = "/var/log/auth.log"
SU_LOG = "/var/log/auth.log"
SUDO_LOG = "/var/log/auth.log"
FTPD_LOG = "/var/log/proftpd/proftpd.log"
SMTPAUTH_LOG = "/var/log/auth.log"
POP3D_LOG = "/var/log/mail.log"
IMAPD_LOG = "/var/log/mail.log"
IPTABLES_LOG = "/var/log/syslog"
SUHOSIN_LOG = "/var/log/syslog"
BIND_LOG = "/var/log/syslog"
SYSLOG_LOG = "/var/log/syslog"
WEBMIN_LOG = "/var/log/auth.log"
config-end

CSF_firewall_DynDNS_namen_fuer_auto_whitelisting
sudo nano /etc/csf/csf.dyndns
#fqdn only
#hostname.domain.tld you want to allow, not the local hostname
config-end

config-start CSF_firewall_Prozesse_ueberwachen_reset
#CSF Process tracking
#remove all line with exe:/ from /etc/csf/csf.pignore.
#lfd will send you emails with deamons running, grep them an create a /etc/csf/csf.pignore suiteable for your system.
#Debian 12 2025-12-28
exe:/usr/bin/bash
exe:/usr/bin/dbus-daemon
exe:/usr/bin/pbzip2
exe:/usr/bin/perl
exe:/usr/bin/screen
exe:/usr/bin/watch
exe:/usr/lib/dovecot/anvil
exe:/usr/lib/dovecot/stats
exe:/usr/lib/postfix/sbin/pickup
exe:/usr/lib/postfix/sbin/qmgr
exe:/usr/lib/postfix/sbin/tlsmgr
exe:/usr/lib/systemd/systemd
exe:/usr/lib/virtualbox/VBoxSVC
exe:/usr/lib/virtualbox/vboxwebsrv
exe:/usr/sbin/apache2
exe:/usr/sbin/clamd
exe:/usr/sbin/mariadbd
exe:/usr/sbin/opendkim
exe:/usr/sbin/php-fpm8.2
exe:/usr/sbin/proftpd
exe:/usr/sbin/sshd

#restart csf
sudo csf -ra

config-start CSF_firewall_Install_Support_to_Virtualmin
Webmin -> Webmin -> Wemin Configuration -> Virtualmin Module ->
Install from local file -> /usr/local/csf/csfwebmin.tgz
config-end

sudo chown root:root /etc/csf/csf.conf
sudo chmod 600 /etc/csf/csf.conf

config-start CSF_Firewall_DynDNS_Hostname
sudo nano /etc/csf/csf.dyndns
config-end

config-start CSF_Firewall_Prozess_Tracking
sudo nano /etc/csf/csf.pignore
config-end

config-start Script_Easy_Firewall_restart_mit_sudo_ohne_PW_abfrage
sudo nano /opt/usr/sbin/firewall_restart
#!/bin/bash
/usr/sbin/csf -ra

sudo chown root:root /opt/usr/sbin/firewall_restart
sudo chmod 750 /opt/usr/sbin/firewall_restart

sudo nano /etc/sudoers
$USER ALL = NOPASSWD: /opt/usr/sbin/firewall_restart
config-end

config-start CSF_Tools_Firewall_EMail_durchsuchen
mails nach
find . -type f -exec grep -l "Excessive resource" {} + | xargs grep -h "Executable:" | sed 's/.*Executable: *//' | sort -u
find . -type f -exec grep -l "Excessive resource" {} + | xargs grep -h "Executable:" | sed 's/.*Executable: *//' | sort -u | sed 's|^|exe:|'
config-end

#CSF Firewall eigene Regeln erstellen
csf eigene regeln
nano /etc/csf/regex.custom.pm

 if (($globlogs{SMTPAUTH_LOG} =~ %S) && ($line =~ /^\S+ \S+ postfix\/smtpd\[\d+\]: warning: .*\[(\d+\.\d+\.\d+\.\d+)\]: SASL (?:LOGIN|PLAIN) authentication failed:>
68     return ("Custom SASL Attack Block",$1,"my_sasl_jail","2","25,465,587,143,993","1");
69 }


#Meine CSF Tools installieren
#!/bin/bash

cat << 'EOF_CSF1' > /opt/usr/sbin/csf_tools_bin_check
#!/bin/bash
###############################################################################
#                                                                             #
#   ██████╗ ███████╗███████╗    ████████╗ ██████╗  ██████╗ ██╗     ███████╗   #
#  ██╔════╝ ██╔════╝██╔════╝    ╚══██╔══╝██╔═══██╗██╔═══██╗██║     ██╔════╝   #
#  ██║      ███████╗█████╗         ██║   ██║   ██║██║   ██║██║     ███████╗   #
#  ██║      ╚════██║██╔══╝         ██║   ██║   ██║██║   ██║██║     ╚════██║   #
#  ╚██████╗ ███████║██║            ██║   ╚██████╔╝╚██████╔╝███████╗███████║   #
#   ╚═════╝ ╚══════╝╚═╝            ╚═╝    ╚═════╝  ╚═════╝ ╚══════╝╚══════╝   #
#                                                                             #
#  CSF TOOLKIT                                                                #
#  binary check                                                               #
#  Copyright (c) 2025 Oliver Arp aka The_Kangaroo                             #
#  Licensed under the GNU General Public License (GPL)                        #
#                                                                             #
###############################################################################
#
#System Binary Validator
#
#Verifies that all 28 critical system executables (iptables, grep, etc.) exist and match the paths defined in csf.conf.
#It automatically suggests the correct paths if a mismatch is detected during the system scan.
#
CONF_FILE="/etc/csf/csf.conf"
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color

# 1. Root check
if [[ $EUID -ne 0 ]]; then
   echo -e "${RED}ERROR: Root privileges required.${NC}"
   exit 1
fi

BINARY_LIST="IPTABLES IPTABLES_SAVE IPTABLES_RESTORE IP6TABLES IP6TABLES_SAVE IP6TABLES_RESTORE MODPROBE IFCONFIG SENDMAIL PS VMSTAT NETSTAT LS MD5SUM TAR CHATTR UNZIP GUNZIP DD TAIL GREP ZGREP IPSET SYSTEMCTL HOST IP CURL WGET"

ERR_COUNT=0

# 2. Validation loop
for VAR in $BINARY_LIST; do
    echo -n "Searching for $VAR... "
   
    LINE=$(grep -E "^$VAR =" "$CONF_FILE")
   
    if [[ -n "$LINE" ]]; then
        BIN_PATH=$(echo "$LINE" | awk -F'"' '{print $2}' | awk '{print $1}')

        if [[ -x "$BIN_PATH" ]]; then
            echo -e "found $VAR under $BIN_PATH"
            echo -e "${GREEN}$VAR ok${NC}"
        else
            ((ERR_COUNT++))
            ACTUAL_PATH=$(which "$(basename "$BIN_PATH")" 2>/dev/null)
           
            if [[ -n "$ACTUAL_PATH" ]]; then
                echo -e "found $VAR under $ACTUAL_PATH"
                echo -e "${RED}ERROR: Path in config ($BIN_PATH) does not match system!${NC}"
            else
                echo -e "${RED}ERROR: $VAR not found on system!${NC}"
            fi
        fi
    else
        echo -e "${RED}$VAR not defined in config!${NC}"
    fi
    echo "--------------------------------------------------------"
done

# 3. Final Summary
if [[ $ERR_COUNT -gt 0 ]]; then
    echo -e "${RED}Validation failed: $ERR_COUNT binaries have path mismatches or are missing!${NC}"
else
    echo -e "${GREEN}All binaries validated successfully (100.00%).${NC}"
fi
EOF_CSF1

cat << 'EOF_CSF2' > /opt/usr/sbin/csf_tools_log_file_validator
#!/bin/bash
###############################################################################
#                                                                             #
#   ██████╗ ███████╗███████╗    ████████╗ ██████╗  ██████╗ ██╗     ███████╗   #
#  ██╔════╝ ██╔════╝██╔════╝    ╚══██╔══╝██╔═══██╗██╔═══██╗██║     ██╔════╝   #
#  ██║      ███████╗█████╗         ██║   ██║   ██║██║   ██║██║     ███████╗   #
#  ██║      ╚════██║██╔══╝         ██║   ██║   ██║██║   ██║██║     ╚════██║   #
#  ╚██████╗ ███████║██║            ██║   ╚██████╔╝╚██████╔╝███████╗███████║   #
#   ╚═════╝ ╚══════╝╚═╝            ╚═╝    ╚═════╝  ╚═════╝ ╚══════╝╚══════╝   #
#                                                                             #
#  CSF TOOLKIT                                                                #
#  log file validator                                                         #
#  Copyright (c) 2025 Oliver Arp aka The_Kangaroo                             #
#  Licensed under the GNU General Public License (GPL)                        #
#                                                                             #
###############################################################################
#
#Active Log Source Checker
#
#Scans all enabled log definitions to ensure the physical log files exist
#and are readable by the LFD daemon. Color-coded results quickly identify
#dead links or missing log files that would otherwise cause monitoring gaps.
#
# Configuration file path
CONF_FILE="/etc/csf/csf.conf"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

# 1. Check for root/sudo privileges
if [[ $EUID -ne 0 ]]; then
   echo -e "${RED}ERROR: This script must be run as root or with sudo.${NC}"
   exit 1
fi

# 2. Check if the config file exists
if [[ ! -r "$CONF_FILE" ]]; then
    echo -e "${RED}ERROR: Cannot read $CONF_FILE.${NC}"
    exit 1
fi

echo "Starting CSF Log Configuration Validation..."
echo "--------------------------------------------------------"

ERR_COUNT=0
TOTAL_COUNT=0

# 3. Process active log definitions
grep -E "^[A-Z0-9_]+_LOG =" "$CONF_FILE" | while read -r line; do

    SERVICE=$(echo "$line" | awk -F'=' '{print $1}' | tr -d ' ')
    LOGPATH=$(echo "$line" | awk -F'"' '{print $2}')

    # 4. Filter: Only check if it's an actual path (starts with /)
    if [[ "$LOGPATH" == /* ]]; then
        ((TOTAL_COUNT++))
        echo -n "Checking $SERVICE... "
       
        if [[ -f "$LOGPATH" ]]; then
            echo -e "${GREEN}OK${NC} ($LOGPATH)"
        else
            echo -e "${RED}MISSING${NC} ($LOGPATH)"
            ((ERR_COUNT++))
        fi
    else
        # Skip switches like "1", "0" or empty values (Optionaler Info-Output)
        echo -e "${BLUE}[INFO]${NC}     Skipping $SERVICE: Not a path ($LOGPATH)"
    fi
done

echo "--------------------------------------------------------"

# 5. Final Summary
if [[ $ERR_COUNT -gt 0 ]]; then
    echo -e "${RED}Validation failed: $ERR_COUNT of $TOTAL_COUNT active logfiles are missing!${NC}"
else
    echo -e "${GREEN}Success: All $TOTAL_COUNT active logfiles found (100.00%).${NC}"
fi
EOF_CSF2

cat << 'EOF_CSF3' > /opt/usr/sbin/csf_tools_logrotate_check
#!/bin/bash
###############################################################################
#                                                                             #
#   ██████╗ ███████╗███████╗    ████████╗ ██████╗  ██████╗ ██╗     ███████╗   #
#  ██╔════╝ ██╔════╝██╔════╝    ╚══██╔══╝██╔═══██╗██╔═══██╗██║     ██╔════╝   #
#  ██║      ███████╗█████╗         ██║   ██║   ██║██║   ██║██║     ███████╗   #
#  ██║      ╚════██║██╔══╝         ██║   ██║   ██║██║   ██║██║     ╚════██║   #
#  ╚██████╗ ███████║██║            ██║   ╚██████╔╝╚██████╔╝███████╗███████║   #
#   ╚═════╝ ╚══════╝╚═╝            ╚═╝    ╚═════╝  ╚═════╝ ╚══════╝╚══════╝   #
#                                                                             #
#  CSF TOOLKIT                                                                #
#  logrotate check for CSF logs                                               #
#  Copyright (c) 2025 Oliver Arp aka The_Kangaroo                             #
#  Licensed under the GNU General Public License (GPL)                        #
#                                                                             #
###############################################################################
#
#Log Maintenance Auditor
#
#Cross-references active CSF log paths with the system's logrotate configuration
#to prevent uncontrolled file growth. Identifies unmanaged logs in red to protect
#your server from potential disk space exhaustion.
#
# Configuration paths
CSF_CONF="/etc/csf/csf.conf"
LOGROTATE_DIR="/etc/logrotate.d"
LOGROTATE_CONF="/etc/logrotate.conf"

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m' # No Color

# 1. Root check
if [[ $EUID -ne 0 ]]; then
   echo -e "${RED}ERROR: This script must be run as root.${NC}"
   exit 1
fi

echo "Starting CSF vs. Logrotate Consistency Check..."
echo "--------------------------------------------------------"

ERR_COUNT=0
TOTAL_COUNT=0

# 2. Get all active log paths from CSF
# Wir filtern hier nur die Zeilen, die wirklich einen Pfad in Anführungszeichen haben
grep -E "^[A-Z0-9_]+_LOG =" "$CSF_CONF" | grep '"/' | while read -r line; do
   
    SERVICE=$(echo "$line" | awk -F'=' '{print $1}' | tr -d ' ')
    LOGPATH=$(echo "$line" | awk -F'"' '{print $2}')

    ((TOTAL_COUNT++))
    echo -n "Checking $SERVICE ($LOGPATH)... "

    # 3. Check if the path is mentioned in any logrotate config
    if grep -q "$LOGPATH" "$LOGROTATE_CONF" || grep -rq "$LOGPATH" "$LOGROTATE_DIR" 2>/dev/null; then
        echo -e "${GREEN}OK${NC}"
    else
        echo -e "${RED}NOT FOUND in logrotate!${NC}"
        ((ERR_COUNT++))
    fi
done

echo "--------------------------------------------------------"

# 4. Final Summary
if [[ $ERR_COUNT -gt 0 ]]; then
    echo -e "${RED}Warning: $ERR_COUNT of $TOTAL_COUNT logs are not managed by logrotate!${NC}"
    echo "This could lead to disk space issues."
else
    echo -e "${GREEN}Excellent: All $TOTAL_COUNT active logs are covered by logrotate (100.00%).${NC}"
fi
EOF_CSF3

sudo chown root:root /opt/usr/sbin/csf_tools_bin_check
sudo chown root:root /opt/usr/sbin/csf_tools_log_file_validator
sudo chown root:root /opt/usr/sbin/csf_tools_logrotate_check
sudo chmod 555 /opt/usr/sbin/csf_tools_bin_check
sudo chmod 555 /opt/usr/sbin/csf_tools_log_file_validator
sudo chmod 555 /opt/usr/sbin/csf_tools_logrotate_check

echo "sudo nano /etc/sudoers"
echo "ALL=/opt/usr/sbin/csf_tools_bin_check, /opt/usr/sbin/csf_tools_log_file_validator, /opt/usr/sbin/csf_tools_logrotate_check"

#Virtualbox und phpVirtualBox installieren und einrichten
Virtualbox_installieren_und_phpVirtualBox
sudo su
wget -O- https://www.virtualbox.org/download/oracle_vbox_2016.asc | sudo gpg --yes --output /usr/share/keyrings/oracle-virtualbox-2016.gpg --dearmor

sudo nano /etc/apt/sources.list
#Virtualbox
deb [arch=amd64 signed-by=/usr/share/keyrings/oracle-virtualbox-2016.gpg] https://download.virtualbox.org/virtualbox/debian bookworm contrib

sudo apt-get update
sudo apt-get install virtualbox-7.2
wget https://download.virtualbox.org/virtualbox/7.2.4/Oracle_VirtualBox_Extension_Pack-7.2.4-170995.vbox-extpack
sudo VBoxManage extpack install Oracle_VirtualBox_Extension_Pack-7.2.4-170995.vbox-extpack
rm Oracle_VirtualBox_Extension_Pack-7.2.4-170995.vbox-extpack

sudo adduser vbox
sudo adduser vbox vboxusers

create vbox.hostname.domain.tld in Virtualmin
create vbox.hostname.domain.tld in Cloudflare.com

sudo su - vbox.hostname.domain.tld
wget https://github.com/phpvirtualbox/phpvirtualbox/archive/refs/tags/7.2-2.zip
extract to public_html/
exit

php -v #Version 8.2.29
sudo apt-get install libapr1 libaprutil1 libaprutil1-dbd-sqlite3 libaprutil1-ldap php8.2-common php8.2-mysql php8.2-soap php-pear

sudo nano /etc/default/virtualbox
VBOXWEB_USER=vbox
VBOXWEB_HOST=127.0.0.1

sudo mkdir /data/vbox
sudo mkdir /data/vbox/vms
sudo mkdir /data/vbox/isos

wget https://download.virtualbox.org/virtualbox/7.2.4/VBoxGuestAdditions_7.2.4.iso
sudo cp VBoxGuestAdditions_7.2.4.iso /data/vbox/isos/VBoxGuestAdditions_7.2.4.iso
sudo chmod 440 /data/vbox/isos/VBoxGuestAdditions_7.2.4.iso
sudo chown vbox:vbox /data/vbox -R
rm VBoxGuestAdditions_7.2.4.iso

sudo su - vbox.hostname.domain.tld
cd public_html/
cp config.php-example config.php

nano config.php
var $password = 'SicheresPasswwortvbox';
var $language = 'de';
var $vrdeaddress = '1.2.3.4';
var $consoleHost = '1.2.3.4';
#var $previewWidth = 180;
var $previewWidth = 640;
var $browserRestrictFolders = array('/data/vbox');

reboot & login to https://vbox.hostname.domain.tld to ceate VMs _should_ work

systemctl list-units "vbox*"
systemctl list-unit-files | grep vbox
systemctl status vboxweb-service.service
journalctl -u vboxweb-service.service -xe

Script_laufende_VMs_anzeigen
sudo su - vbox
cd
nano list_running_vms.sh
#!/bin/bash
vboxmanage list runningvms
chmod 750 list_running_vms.sh

Script_VMs_automatischer_start
sudo su - vbox
cd
nano start.sh
#!/bin/bash
#/usr/bin/vboxmanage startvm --type headless
#/usr/bin/vboxmanage startvm --type headless 2e1afc3e-0ab7-45f0-a1a4-b23febdc236c

chmod 750 start.sh

exit

VMs_automatisch_per_SystemD_starten
sudo nano /etc/systemd/system/startvms.service
[Unit]
Description=Start VMs
After=network.target

[Service]
Type=simple
Restart=no
User=vbox
ExecStartPre=/bin/sleep 15
ExecStart=/home/vbox/start.sh

[Install]
WantedBy=multi-user.target

sudo chown root:root /etc/systemd/system/startvms.service
sudo chmod 660 /etc/systemd/system/startvms.service

sudo systemctl daemon-reload
sudo systemctl enable startvms.service
sudo systemctl start startvms.service

ExecStartPre=/usr/bin/sleep 5 #hinzufügen
sudo nano /lib/systemd/system/vboxweb-service.service
[Unit]
SourcePath=/usr/lib/virtualbox/vboxweb-service.sh
Description=
Before=runlevel2.target runlevel3.target runlevel4.target runlevel5.target shutdown.target
After=network-online.target vboxdrv.service
Conflicts=shutdown.target

[Service]
Type=forking
Restart=no
TimeoutSec=5min
IgnoreSIGPIPE=no
KillMode=process
GuessMainPID=no
RemainAfterExit=yes
ExecStartPre=/bin/sleep 5
ExecStart=/usr/lib/virtualbox/vboxweb-service.sh start
ExecStop=/usr/lib/virtualbox/vboxweb-service.sh stop

[Install]
WantedBy=multi-user.target

#VirtualBox per TUI
sudo su - vbox
nano vbox_control.sh
#!/bin/bash

# ---------------------------------------------------------------------------------
# Dieses Programm nutzt für seine Funktionen nur die folgenden Programme:
# - VBoxManage, pgrep, stat, date, du, grep, awk, cut, tr, mapfile, ls, basename
# ---------------------------------------------------------------------------------

# --- Variablen (Konfiguration) ---
VM_BASE_PATH="/data/vbox/vms/"
ISO_PATH="/data/vbox/isos/"
WIDTH=95
LANG_DEFAULT="DE"
target_user="vbox"

# --- Texte Laden (Assoziatives Array) ---
declare -A TXT
set_language() {
    if [[ "$1" == "EN" ]]; then
        LANG_CURRENT="EN"
        TXT[title]="VIRTUALBOX CLI MANAGER"
        TXT[help_head]="VIRTUALBOX CLI MANAGER - HELP / SUMMARY"
        TXT[help_usage]="Usage: $0 [Option]"
        TXT[help_opt_h]="  -h     Show this help"
        TXT[help_opt_en]="  -en    Start in English"
        TXT[help_opt_de]="  -de    Start in German"
        TXT[help_desc]="Summary of Features:"
        TXT[help_f1]="  * VM-Management: Start (Headless), Pause, ACPI-Shutdown, Power-Off"
        TXT[help_f2]="  * Snapshots:     Create, List and Restore VM states"
        TXT[help_f3]="  * Configuration: CPU/RAM adjustments and ISO Mounting"
        TXT[help_f4]="  * Provisioning:  Guided creation of new VMs with Disk/NIC"
        TXT[help_keys]="Interactive Keys:"
        TXT[help_k1]="  [s] Refresh list, [l] Toggle Language, [q] Exit, [Num] Select VM"
        TXT[help_paths]="Paths:"
        TXT[overview]="CURRENT LAB OVERVIEW"
        TXT[status]="STATUS"; TXT[uptime]="UPTIME"; TXT[mode]="MODE"
        TXT[action]="CHOOSE ACTION"
        TXT[menu1]="START (HEADLESS)"; TXT[menu2]="PAUSE (STANDBY)"; TXT[menu3]="SHUTDOWN (ACPI)"
        TXT[menu4]="SNAPSHOT MGR"; TXT[menu5]="SHOW DETAILS"; TXT[menu6]="HARD STOP (POWER)"
        TXT[menu7]="CREATE VM"; TXT[menu8]="DELETE VM"; TXT[menu9]="EDIT VM"
        TXT[menu10]="ISO OVERVIEW"; TXT[menu_l]="l) LANGUAGE (DE/EN)"; TXT[menu_q]="q) EXIT"; TXT[menu_s]="s) REFRESH"
        TXT[err_dep]="ERROR: Missing dependencies"; TXT[err_perm]="🟡 Warning: Access Denied / Missing Permissions"
        TXT[prompt_vm]="WHICH VM (Nr)? (s for back): "; TXT[confirm]="Confirm with"; TXT[done]="DONE. PRESS ANY KEY..."
        TXT[v_name]="NAME"; TXT[v_ram]="RAM (MB)"; TXT[v_hdd]="HDD (MB)"; TXT[v_iso]="Select ISO Archive"; TXT[back]="s) Back"
        TXT[det_storage]="STORAGE & ISO CONFIG"
    else
        LANG_CURRENT="DE"
        TXT[title]="VIRTUALBOX CLI MANAGER"
        TXT[help_head]="VIRTUALBOX CLI MANAGER - HILFE / ZUSAMMENFASSUNG"
        TXT[help_usage]="Nutzung: $0 [Option]"
        TXT[help_opt_h]="  -h     Diese Hilfe anzeigen"
        TXT[help_opt_en]="  -en    Auf Englisch starten"
        TXT[help_opt_de]="  -de    Auf Deutsch starten"
        TXT[help_desc]="Funktionsübersicht:"
        TXT[help_f1]="  * VM-Steuerung:  Start (Headless), Pause, ACPI-Shutdown, Power-Off"
        TXT[help_f2]="  * Snapshots:     Erstellen, Listen und Wiederherstellen von Zuständen"
        TXT[help_f3]="  * Konfiguration: CPU/RAM Anpassung und ISO-Mounting"
        TXT[help_f4]="  * Provisioning:  Geführtes Erstellen neuer VMs inkl. Disk/Netzwerk"
        TXT[help_keys]="Tastenbefehle:"
        TXT[help_k1]="  [s] Aktualisieren, [l] Sprache wechseln, [q] Beenden, [Nr] VM wählen"
        TXT[help_paths]="Pfade:"
        TXT[overview]="AKTUELLE LABOR-ÜBERSICHT"
        TXT[status]="STATUS"; TXT[uptime]="UPTIME"; TXT[mode]="MODUS"
        TXT[action]="AKTION WÄHLEN"
        TXT[menu1]="STARTEN (HEADLESS)"; TXT[menu2]="PAUSIEREN (STANDBY)"; TXT[menu3]="HERUNTERFAHREN (ACPI)"
        TXT[menu4]="SNAPSHOT MANAGEMENT"; TXT[menu5]="DETAILS ANZEIGEN"; TXT[menu6]="HARTER STOP (POWER)"
        TXT[menu7]="VM ERSTELLEN"; TXT[menu8]="VM LÖSCHEN"; TXT[menu9]="VM BEARBEITEN"
        TXT[menu10]="ISO ÜBERSICHT"; TXT[menu_l]="l) SPRACHE (DE/EN)"; TXT[menu_q]="q) BEENDEN"; TXT[menu_s]="s) AKTUALISIEREN"
        TXT[err_dep]="FEHLER: Fehlende Abhängigkeiten"; TXT[err_perm]="🟡 Warnung: Zugriff verweigert / Fehlende Rechte"
        TXT[prompt_vm]="WELCHE VM (Nr)? (s für zurück): "; TXT[confirm]="Bestätige mit"; TXT[done]="FERTIG. BELIEBIGE TASTE..."
        TXT[v_name]="NAME"; TXT[v_ram]="RAM (MB)"; TXT[v_hdd]="HDD (MB)"; TXT[v_iso]="ISO Archiv wählen"; TXT[back]="s) Zurück"
        TXT[det_storage]="STORAGE & ISO KONFIGURATION"
    fi
}

# --- Hilfe-Funktion ---
show_help() {
    echo -e "\033[1;32m${TXT[help_head]}\033[0m"
    echo -e "${TXT[help_usage]}"
    echo -e "${TXT[help_opt_h]}\n${TXT[help_opt_en]}\n${TXT[help_opt_de]}"
    echo -e "\n${TXT[help_desc]}"
    echo -e "${TXT[help_f1]}\n${TXT[help_f2]}\n${TXT[help_f3]}\n${TXT[help_f4]}"
    echo -e "\n${TXT[help_keys]}\n${TXT[help_k1]}"
    echo -e "\n${TXT[help_paths]}\n  VM:  $VM_BASE_PATH\n  ISO: $ISO_PATH"
    exit 0
}

# --- Initialisierung & Parameter ---
set_language "$LANG_DEFAULT"
case "$1" in
    -h|--help) show_help ;;
    -en) set_language "EN" ;;
    -de) set_language "DE" ;;
esac

identify_distro() { [[ -f /etc/os-release ]] && grep -E "^ID=" /etc/os-release | cut -d'=' -f2 | tr -d '"' || echo "unknown"; }
check_dependencies() {
    local tools=("VBoxManage" "pgrep" "stat" "date" "du" "grep" "awk" "cut" "tr" "ls" "basename")
    local missing=()
    for t in "${tools[@]}"; do command -v "$t" &> /dev/null || missing+=("$t"); done
    if [ ${#missing[@]} -ne 0 ]; then
        DID=$(identify_distro); echo -e "\033[0;31m${TXT[err_dep]}: ${missing[*]}\033[0m"
        case "$DID" in rocky|rhel|fedora|almalinux) echo "sudo dnf install VirtualBox-7.x procps-ng coreutils" ;; debian|ubuntu|devuan|kali) echo "sudo apt update && sudo apt install virtualbox procps coreutils" ;; *) echo "Bitte installiere die Pakete manuell." ;; esac; exit 1
    fi
}
check_dependencies

handle_error() {
    local file="$1"
    echo -e "\033[1;33m${TXT[err_perm]}\033[0m\nDatei: $file\nLösung: target_user prüfen (aktuell: target_user=\"$target_user\").\nManuelle Reparatur: chmod 0644 $file && chown $target_user:root $file"
    read -p "${TXT[done]}" d
}

get_vm_ip() { local ip=$(VBoxManage guestproperty get "$1" "/VirtualBox/GuestInfo/Net/0/V4/IP" 2>/dev/null | grep "Value:" | awk '{print $2}'); [[ -z "$ip" || "$ip" == "No" ]] && echo "---" || echo "$ip"; }
get_uptime() {
    local pid=$(pgrep -f "$1"); if [[ -z "$pid" ]]; then echo "---"; else
        local start=$(stat -c %Y /proc/$pid 2>/dev/null); [[ -z "$start" ]] && { echo "---"; return; }
        local s=$(( $(date +%s) - start )); local d=$((s/86400)); local h=$(((s%86400)/3600)); local m=$(((s%3600)/60))
        [ $d -gt 0 ] && echo "${d}d ${h}h" || { [ $h -gt 0 ] && echo "${h}h ${m}m" || echo "${m}m"; }
    fi
}
refresh_vm_list() { unset VM_NAMES; unset VM_UUIDS; mapfile -t VM_NAMES < <(VBoxManage list vms | awk -F '"' '{print $2}'); mapfile -t VM_UUIDS < <(VBoxManage list vms | awk -F '[{}]' '{print $2}'); }

select_iso() {
    local isos=("$ISO_PATH"/*.iso); [[ ! -e "${isos[0]}" ]] && { echo "Keine ISOs gefunden!"; return 1; }
    echo -e "\033[1;32m   ${TXT[v_iso]}\033[0m"
    for i in "${!isos[@]}"; do printf "   %02d) %s\n" "$((i+1))" "$(basename "${isos[$i]}")"; done
    read -p "   WAHL (Nr) oder (s) skip: " choice
    [[ "$choice" == "s" ]] && { SELECTED_ISO="emptydrive"; return 0; }
    if [[ "$choice" =~ ^[0-9]+$ ]] && [ "$choice" -le "${#isos[@]}" ]; then SELECTED_ISO="${isos[$((choice-1))]}"; return 0; fi
    return 1
}

draw_header() {
    local mode_text="$1"; local skip_list="$2"
    clear; echo -e "\033[0;32m==============================================================================================="
    printf "%*s\n" $(( (${#TXT[title]} + WIDTH) / 2 )) "${TXT[title]}"
    echo -e "===============================================================================================\033[0m"
    [[ -n "$mode_text" ]] && echo -e "\033[1;33m   ${TXT[mode]}: $mode_text\033[0m"
    if [[ "$skip_list" != "true" ]]; then
        printf "%-4s | %-25s | %-15s | %-12s | %-15s\n" "NR" "VM NAME" "IP" "${TXT[status]}" "${TXT[uptime]}"
        echo "-----------------------------------------------------------------------------------------------"
        for i in "${!VM_NAMES[@]}"; do
            local uuid="${VM_UUIDS[$i]}"; local status="OFFLINE"; local ip="---"; local uptime="---"
            VBoxManage list runningvms | grep -q "$uuid" && { status="AKTIV"; ip=$(get_vm_ip "$uuid"); uptime=$(get_uptime "$uuid"); }
            printf "%02d   | %-25s | %-15s | %-12s | %-15s\n" "$((i+1))" "${VM_NAMES[$i]}" "$ip" "$status" "$uptime"
        done
        echo "-----------------------------------------------------------------------------------------------"
    fi
}

while true; do
    refresh_vm_list
    draw_header ""
    printf "  1) %-25s  4) %-25s  7) %-25s\n" "${TXT[menu1]}" "${TXT[menu4]}" "${TXT[menu7]}"
    printf "  2) %-25s  5) %-25s  8) %-25s\n" "${TXT[menu2]}" "${TXT[menu5]}" "${TXT[menu8]}"
    printf "  3) %-25s  6) %-25s  9) %-25s\n" "${TXT[menu3]}" "${TXT[menu6]}" "${TXT[menu9]}"
    printf " 10) %-25s\n" "${TXT[menu10]}"
    echo "-----------------------------------------------------------------------------------------------"
    printf "  %-20s      %-20s      %-20s\n" "${TXT[menu_q]}" "${TXT[menu_s]}" "${TXT[menu_l]}"
    echo "-----------------------------------------------------------------------------------------------"
    read -p "${TXT[action]}: " action
    [[ "$action" == "l" || "$action" == "L" ]] && { [[ "$LANG_CURRENT" == "DE" ]] && set_language "EN" || set_language "DE"; continue; }
    [[ "$action" == "q" || "$action" == "Q" ]] && break
    [[ "$action" == "s" || "$action" == "S" ]] && continue

    if [[ "$action" =~ ^[1-6|8-9]$ ]]; then
        cur_mode_key="menu$action"; cur_mode_text="${TXT[$cur_mode_key]}"
        draw_header "$cur_mode_text"
        read -p "${TXT[prompt_vm]}" vm_num; [[ "$vm_num" == "s" ]] && continue
        idx=$((vm_num-1))
        if [[ "$vm_num" =~ ^[0-9]+$ ]] && [ "$idx" -ge 0 ] && [ "$idx" -lt "${#VM_UUIDS[@]}" ]; then
            SEL_UUID="${VM_UUIDS[$idx]}"; SEL_NAME="${VM_NAMES[$idx]}"
            case $action in
                1) VBoxManage startvm "$SEL_UUID" --type headless ;;
                2) read -p "${TXT[confirm]} 'PAUSE': " c; [[ "$c" == "PAUSE" ]] && VBoxManage controlvm "$SEL_UUID" savestate ;;
                3) read -p "${TXT[confirm]} 'ACPI': " c; [[ "$c" == "ACPI" ]] && VBoxManage controlvm "$SEL_UUID" acpipowerbutton ;;
                4) draw_header "$cur_mode_text: $SEL_NAME" "true"
                   echo "1) Take  2) List  3) Restore  ${TXT[back]}"; read -p "Wahl: " snc
                   case $snc in 1) read -p "Name: " sn; VBoxManage snapshot "$SEL_UUID" take "$sn" || handle_error "$VM_BASE_PATH" ;; 2) VBoxManage snapshot "$SEL_UUID" list ;; 3) read -p "Snapshot Name: " sn; VBoxManage snapshot "$SEL_UUID" restore "$sn" ;; esac ;;
                5) draw_header "$cur_mode_text: $SEL_NAME" "true"
                   echo -e "\033[1;34m[ SYSTEM ]\033[0m"
                   VBoxManage showvminfo "$SEL_UUID" | grep -E "^Name:|^Guest OS:|^UUID:|^Config file:|^Memory size|^Number of CPUs"
                   lock_info=$(VBoxManage showvminfo "$SEL_UUID" | grep "Session Lock")
                   if [[ -n "$lock_info" ]]; then
                       echo -e "Session Lock: \033[1;31m${lock_info#*:}\033[0m"
                       if ! VBoxManage list runningvms | grep -q "$SEL_UUID"; then
                          echo -e "\033[1;33mWARNUNG: VM ist OFFLINE aber GESPERRT (Zombie?).\033[0m"
                          echo -e "Prüfe laufende Prozesse: pgrep -af $SEL_NAME"
                       fi
                   fi
                   echo -e "\n\033[1;34m[ NETWORK ]\033[0m"
                   VBoxManage showvminfo "$SEL_UUID" | grep -E "^NIC" | grep -v "disabled"
                   echo -e "\n\033[1;34m[ ${TXT[det_storage]} ]\033[0m"
                   VBoxManage showvminfo "$SEL_UUID" | grep -iE "Controller|SATA|IDE" | grep -v "Port Count"
                   VBoxManage showvminfo "$SEL_UUID" | grep -iE "(.iso|.vdi|emptydrive)" | sed 's/^/  /'
                   ;;
                6) read -p "${TXT[confirm]} 'STOP': " c; [[ "$c" == "STOP" ]] && VBoxManage controlvm "$SEL_UUID" poweroff ;;
                8) read -p "${TXT[confirm]} 'DELETE': " c; [[ "$c" == "DELETE" ]] && {
                       if VBoxManage list runningvms | grep -q "$SEL_UUID"; then
                          echo -e "\033[1;31mFEHLER: VM läuft noch! Erst ausschalten.\033[0m"
                       else
                          VBoxManage unregistervm "$SEL_UUID" --delete
                       fi
                   } ;;
                9) read -p "${TXT[confirm]} 'EDIT': " c; [[ "$c" == "EDIT" ]] && {
                       draw_header "$cur_mode_text: $SEL_NAME" "true"
                       echo "1) CPU  2) RAM  3) ISO Mount  4) ISO Unmount  ${TXT[back]}"; read -p "Wahl: " edc
                       case $edc in
                           1) read -p "CPUs: " v; VBoxManage modifyvm "$SEL_UUID" --cpus "$v" ;;
                           2) read -p "RAM (MB): " v; VBoxManage modifyvm "$SEL_UUID" --memory "$v" ;;
                           3) select_iso && VBoxManage storageattach "$SEL_UUID" --storagectl "IDE" --port 0 --device 0 --type dvddrive --medium "$SELECTED_ISO" ;;
                           4) VBoxManage storageattach "$SEL_UUID" --storagectl "IDE" --port 0 --device 0 --type dvddrive --medium emptydrive ;;
                       esac
                   } ;;
            esac
            read -p "${TXT[done]}" d
        fi
    fi
    if [[ "$action" == "7" ]]; then
        draw_header "${TXT[menu7]}" "true"
        read -p "${TXT[v_name]} (${TXT[back]}): " n; [[ -z "$n" || "$n" == "s" ]] && continue
        read -p "${TXT[v_ram]} [1024]: " r; r=${r:-1024}; read -p "${TXT[v_hdd]} [10240]: " h; h=${h:-10240}
        select_iso && iso="$SELECTED_ISO" || iso="emptydrive"
        VBoxManage createvm --name "$n" --basefolder "$VM_BASE_PATH" --register || { handle_error "$VM_BASE_PATH"; continue; }
        VBoxManage modifyvm "$n" --memory "$r" --nic1 bridged --bridgeadapter1 eth0
        VBoxManage storagectl "$n" --name "SATA" --add sata
        VBoxManage createmedium disk --filename "$VM_BASE_PATH/$n/$n.vdi" --size "$h"
        VBoxManage storageattach "$n" --storagectl "SATA" --port 0 --device 0 --type hdd --medium "$VM_BASE_PATH/$n/$n.vdi"
        VBoxManage storagectl "$n" --name "IDE" --add ide
        VBoxManage storageattach "$n" --storagectl "IDE" --port 0 --device 0 --type dvddrive --medium "$iso"
        read -p "${TXT[done]}" d
    fi
    [[ "$action" == "10" ]] && { draw_header "${TXT[menu10]}" "true"; ls -1 "$ISO_PATH"/*.iso 2>/dev/null | xargs -n1 basename; read -p "${TXT[done]}" d; }
done
echo -e "\033[0m"
exit 0

#hier ist schluss

chmod 750 vbox_control.sh

exit

sudo su - vbox.hostname.domain.tld
htpasswd -c /home/vbox.hostname.domain.tld/.htpasswd $BENUTZER
chown vbox.hostname.domain.tld:vbox.hostname.domain.tld /home/vbox.hostname.domain.tld/.htpasswd
chmod 640 /home/vbox.hostname.domain.tld/.htpasswd

nano /home/vbox.hostname.domain.tld/public_html/.htaccess
AuthType Basic
AuthName "VirtualBox Webzugriff"
AuthUserFile /home/vbox.hostname.domain.tld/.htpasswd
Require valid-user

# Optional: Zugriff auf die .htaccess selbst verbieten
<Files ".htaccess">
    Require all denied
</Files>
chown vbox.hostname.domain.tld:vbox.hostname.domain.tld /home/vbox.hostname.domain.tld/public_html/.htaccess

#Backup vom 2. PC auf den Server einrichten:
ssh von home@domain.tld nach hostname.domain.tld einrichten
@home als $BENUTZER
cd && mkdir .ssh && chmod 700 .ssh/ && cd .ssh
ssh-keygen -o -a 100 -t ed25519
cat id_ed25519.pub
ssh-ed25519 ******************************* $BENUTZER@server.lan

@hostname.domain.tld als $BENUTZER
cd
.ssh
nano authorized_keys
ssh-ed25519 ****************************** $BENUTZER@server.lan

rsync -avP /pfad/zur/datei $BENUTZER@hostname.domain.tld:/ziel/pfad/

#Syncthing installieren und einrichten
Syncthing_installieren_und_config
sudo adduser syncthing
sudo mkdir /data/syncthing
sudo mkdir /home/syncthing/bin
sudo chown syncthing:syncthing /home/syncthing -R
sudo su - syncthing
cd
wget https://github.com/syncthing/syncthing/releases/download/v2.0.12/syncthing-linux-amd64-v2.0.12.tar.gz
nach bin entpacken
exit

#Start per systemd

sudo nano /etc/systemd/system/syncthing.service
[Unit]
Description=Syncthing
After=network.target
StartLimitIntervalSec=60
StartLimitBurst=4

[Service]
User=syncthing
ExecStart=/home/syncthing/bin/syncthing serve --gui-address=127.0.0.1:8384 --home=/data/syncthing --no-browser --no-restart --logfile=/var/log/syncthing.log
Restart=on-failure
RestartSec=1
SuccessExitStatus=3 4
RestartForceExitStatus=3 4
ProtectSystem=full
PrivateTmp=true
SystemCallArchitectures=native
MemoryDenyWriteExecute=true
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target

sudo chown root:root /etc/systemd/system/syncthing.service
sudo chmod 660 /etc/systemd/system/syncthing.service

sudo systemctl enable syncthing
sudo systemctl start syncthing
sudo systemctl status syncthing

Virtualmin
syncthing.hostname.domain.tld erstellen
HTTP/HTTPS -> 127.0.0.1:8384
Virtualmin -> Virtualmin -> syncthing.hostname.domain.tld -> Web Configuration -> Edit Proxy Website ->
Proxying enabled -> yes
Proxy to URL -> http://127.0.0.1:8384/

sudo touch /var/log/syncthing.log
sudo chown syncthing:syncthing /var/log/syncthing.log
sudo chmod 660 /var/log/syncthing.log

sudo cat /var/log/syncthing.log
sudo systemctl restart syncthing
sudo cat /var/log/syncthing.log

Syncthing logrotate
sudo nano /etc/logrotate.d/syncthing
/var/log/syncthing.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
    copytruncate
    create 640 syncthing syncthing
}

sudo chown root:root /etc/logrotate.d/syncthing
sudo chmod 0644 /etc/logrotate.d/syncthing

sudo logrotate -d /etc/logrotate.d/syncthing

PHP Update auf 8.4
dpkg -l | grep php | tee php_packages_alt.txt

sudo apt-get update
sudo apt-get -y install lsb-release ca-certificates curl apt-transport-https
sudo curl -sSLo /tmp/debsuryorg-archive-keyring.deb https://packages.sury.org/debsuryorg-archive-keyring.deb
sudo dpkg -i /tmp/debsuryorg-archive-keyring.deb
sudo sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
sudo apt-get update

sudo apt install php8.4-cli

php -v
sudo systemctl status php8.4-fpm
sudo apt install php8.4-common php8.4-{bcmath,bz2,curl,gd,gmp,intl,mbstring,opcache,readline,xml,zip}

sudo apt-get install \
php-cgi \
php-cli \
php-common \
php-curl \
php-fpm \
php-gd \
php-mbstring \
php-mysql \
php-pear \
php-xml \
php8.4-cgi \
php8.4-cli \
php8.4-common \
php8.4-curl \
php8.4-fpm \
php8.4-gd \
php8.4-mbstring \
php8.4-mysql \
php8.4-opcache \
php8.4-readline \
php8.4-soap \
php8.4-xml

sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade

sudo a2enconf php8.4-fpm

#MariaDB Update
sudo apt-get install apt-transport-https curl
sudo mkdir -p /etc/apt/keyrings
sudo curl -o /etc/apt/keyrings/mariadb-keyring.pgp 'https://mariadb.org/mariadb_release_signing_key.pgp'

sudo nano /etc/apt/sources.list
#MariaDB
deb [signed-by=/etc/apt/keyrings/mariadb-keyring.pgp] https://deb.mariadb.org/12.1/debian bookworm main

sudo apt-get update
sudo apt-get upgrade
sudo apt-get dist-upgrade
sudo apt-get autoremove

sudo apt-get install \
liblzo2-2 libsnappy1v5 \
mariadb-plugin-provider-bzip2 \
mariadb-plugin-provider-lz4 \
mariadb-plugin-provider-lzma \
mariadb-plugin-provider-lzo \
mariadb-plugin-provider-snappy

sudo mariadb-secure-installation

#rsyncd einrichten
sudo apt-get update && sudo apt-get install rsync
whereis rsync | grep bin -> /usr/bin/rsync

sudo mkdir /etc/rsyncd/

sudo nano /etc/rsyncd/rsyncd.conf
use chroot = true
transfer logging = true
log file = /var/log/rsyncd.log

[ext500]
comment = ext500
path = /data/rsync/ext500
read only = no
list = no
uid = $BENUTZER
gid = $BENUTZER

sudo chown root:root /etc/rsyncd/rsyncd.conf
sudo chmod 660 /etc/rsyncd/rsyncd.conf

sudo mkdir /data/rsync
sudo mkdir /data/rsync/ext500

per SystemD starten
sudo nano /etc/systemd/system/rsyncd.service
[Unit]
Description=rsyncd
After=network.target

[Service]
ExecStart=/usr/bin/rsync --config=/etc/rsyncd/rsyncd.conf --daemon --no-detach --address=127.0.0.1

[Install]
WantedBy=multi-user.target

sudo chown root:root /etc/systemd/system/rsyncd.service
sudo chmod 664 /etc/systemd/system/rsyncd.service

sudo touch /var/log/rsyncd.log

sudo systemctl daemon-reload
sudo systemctl enable rsyncd
sudo systemctl start rsyncd
sudo systemctl status rsyncd

sudo nano /etc/logrotate.d/rsyncd
/var/log/rsyncd.log {
    weekly
    rotate 4
    compress
    missingok
    notifempty
    copytruncate
    create 640 root root
}

sudo chown root:root /etc/logrotate.d/rsyncd
sudo chmod 0644 /etc/logrotate.d/rsyncd

sudo logrotate -d /etc/logrotate.d/rsyncd

sudo chown $BENUTZER:$BENUTZER /data/rsync/ext500
-------
sudo rm /etc/logrotate.d/rsyncd
sudo rm /var/log/rsyncd.log
sudo systemctl stop rsyncd
sudo systemctl disable rsyncd
systemctl show -p FragmentPath rsyncd
sudo rm /etc/systemd/system/rsyncd.service
sudo rm /etc/rsyncd/rsyncd.conf

#EMail DKIM einrichten
DKIM
Virtualmin -> EMail Settings -> DomainKeys Indentified Mail
Key erstellen
DKIM DNS records for domains
202600._domainkey IN TXT ( "v=DKIM1; k=rsa; t=s; p=*" )

Cloudflare Edit DNS
Add Record
Type - TXT
Name - 202600._domainkey
Content - "v=DKIM1; k=rsa; t=s; p=*"

DMARC:
v=DMARC1; p=reject; rua=mailto:rua@hostname.domain.tld; ruf=mailto:ruf@hostname.domain.tld; adkim=s; aspf=s

Benutzer in Virtualmin unter hostname.domain.tld anlegen
rua@hostname.domain.tld und ruf@hostname.domain.tld
MX eintrag in Cloudflare für hostname.domain.tld anlegen
SPF eintrag in Cloudflare für hostname.domain.tld anlegen
DKIM eintrag in Cloudflare für hostname.domain.tld anlegen
DMARC eintrag in Cloudflare für hostname.domain.tld anlegen

txt
202600._domainkey.server
"v=DKIM1; k=rsa; t=s; p=*"