Ein Blog über Homelab, Netzwerk und Security, der mit einem Server anfängt – wie sollte es auch anders sein. In diesem ersten Teil geht es darum, warum es Kabelbruch überhaupt gibt, warum die Seite nicht in meinem Homelab läuft und wie ich den kleinen Server dafür abgesichert habe. Inklusive der Stellen, an denen ich mich selbst ausgesperrt habe. Fast.

Warum ein Blog – und warum „Kabelbruch“?

Ich bin ITler, mein Alltag dreht sich um Netzwerk und Security. Und „Kabelbruch“ passte eigentlich schon immer gut zu dem, was ich mache – ich fand den Namen einfach witzig. Ein Kabelbruch ist ja genau die Art Fehler, nach der man stundenlang sucht, obwohl die Konfiguration perfekt ist.

Den Blog gibt es, weil ich meine Projekte gern teilen möchte: was ich so baue, woran ich scheitere und welche Erfolge ich habe. Deshalb steht über allem das Motto: Basteln, ausprobieren, auch mal scheitern und darüber schreiben. Die Fehlschläge gehören hier ausdrücklich dazu – meistens lernt man aus denen sowieso mehr.

Außerdem bin ich ein großer Fan von Open Source und versuche, in meinem privaten Umfeld wo immer möglich darauf zu setzen. Das zieht sich auch durch diese Seite: Vom Betriebssystem über den Webserver bis zum Git-Server und dem Seitengenerator ist hier alles Open Source.

Und weil ein Blog über Homelab und Security irgendwo laufen muss, ist sein Aufbau gleich das erste Projekt. Daraus ist diese kleine Serie geworden:

  1. Die Idee und der Server (dieser Teil)
  2. Caddy, DNS und Security-Header
  3. Die Pipeline: vom git push zur fertigen Seite
  4. Mail, Impressum und Favicon

Die Grundsatzentscheidungen

Statisch statt WordPress

Ich wollte eine Seite, die ich verstehe, die wenig Pflege braucht und kaum Angriffsfläche bietet. Deshalb ist Kabelbruch eine statische Website, gebaut mit Hugo. Ein statischer Seitengenerator macht aus Markdown-Dateien fertige HTML-Seiten. Auf dem Server liegen am Ende nur noch Dateien – keine Datenbank, kein PHP, kein Login-Formular, das man angreifen könnte.

Die Artikel schreibe ich als Markdown-Dateien in einem Git-Repository. Das hat einen netten Nebeneffekt: Ich habe eine Versionsgeschichte für jeden Text und kann Code-Beispiele direkt mit Syntax-Hervorhebung einbauen.

Nicht im Homelab

Natürlich war die erste Idee, die Seite einfach zu Hause auf meinem Proxmox-Server laufen zu lassen. Ich habe mich trotzdem dagegen entschieden:

  • Eine öffentliche Website im Heimnetz bedeutet eine Portfreigabe oder einen Tunnel, ein eigenes DMZ-Netz und deutlich mehr Monitoring. Das kann man machen, aber es muss gut durchdacht sein.
  • Fällt zu Hause der Strom oder das Internet aus, wäre der Blog auch weg.

Mein Kompromiss: Gebaut wird die Seite im Homelab, ausgeliefert von einem kleinen Server im Rechenzentrum. Mein Heimnetz muss dafür nichts nach außen öffnen – die Verbindung geht nur von innen nach außen. Wie das genau funktioniert, zeige ich in Teil 3.

Welcher Hoster?

Meine erste Wahl wäre Hetzner gewesen. Allerdings hat Hetzner 2026 kräftig an der Preisschraube gedreht: Zum April stiegen die Cloud-Server-Preise im Schnitt um rund 32 Prozent, im Juni folgte für einige Server-Linien eine weitere Runde. Ganz allein ist Hetzner damit nicht – auch andere Anbieter haben erhöht, Grund sind vor allem die gestiegenen Preise für Arbeitsspeicher. Trotzdem wollte ich mir die Alternativen ansehen.

Die gute Nachricht: Für eine statische Seite braucht man fast nichts. Ich habe mich für einen kleinen VPS (Virtual Private Server, also eine eigene virtuelle Maschine beim Anbieter) bei netcup entschieden:

TarifVPS nano G11.5s
CPU / RAM / SSD2 vCores / 2 GB / 60 GB
Preis3,69 € im Monat inkl. MwSt. (6 Monate Laufzeit, Stand Oktober 2026)
StandortDeutschland
BetriebssystemDebian 13 „trixie“

Für die Website allein ist das sogar überdimensioniert. Aber ich habe schon die nächste Idee im Hinterkopf (dazu am Ende mehr), und dafür ist ein bisschen Luft ganz praktisch.

Ein Tipp beim Preisvergleich: Manche Anbieter zeigen Preise netto, andere inklusive 19 % MwSt. Wer das nicht beachtet, vergleicht Äpfel mit Birnen.

Und noch etwas Rechtliches: Sobald personenbezogene Daten auf dem Server landen – und eine IP-Adresse zählt schon dazu –, gehört ein Vertrag zur Auftragsverarbeitung (AV-Vertrag nach Art. 28 DSGVO) mit dem Hoster dazu. Bei netcup lässt sich der im Kundenmenü abschließen. Ich bin kein Anwalt, aber das ist eine Sache von zehn Minuten und sollte man einfach machen.

Voraussetzungen

Was ich für diesen Teil benutzt habe:

  • Einen VPS mit Debian 13 (frisch installiert, Root-Zugang per SSH)
  • Einen Linux-Rechner mit OpenSSH-Client
  • Einen vorhandenen SSH-Schlüssel vom Typ ed25519

In allen Beispielen steht 203.0.113.10 für die IP meines Servers und thomas für meinen Benutzer. Setz dort deine eigenen Werte ein.

Schritt 1: Den SSH-Schlüssel hinterlegen

Ein SSH-Schlüssel ist sicherer als jedes Passwort – sofern man den richtigen erwischt. Bei mir lagen schon zwei Schlüssel in ~/.ssh/, einer davon für meinen Git-Server. Mit ssh-keygen -lf sieht man, welcher Schlüssel welchen Kommentar trägt, und damit meistens auch, wofür er gedacht war:

ls ~/.ssh/*.pub
ssh-keygen -lf ~/.ssh/id_ed25519.pub

Den öffentlichen Teil (die .pub-Datei – der private Schlüssel verlässt den eigenen Rechner nie) kopiere ich auf den Server:

ssh-copy-id -i ~/.ssh/id_ed25519.pub root@203.0.113.10

Damit ich mir die IP nicht merken muss, bekommt der Server einen Eintrag in der ~/.ssh/config auf meinem Rechner:

Host kabelbruch
    HostName 203.0.113.10
    User root
    IdentityFile ~/.ssh/id_ed25519
    IdentitiesOnly yes

IdentitiesOnly yes sorgt dafür, dass genau dieser Schlüssel benutzt wird und SSH nicht der Reihe nach alle durchprobiert. Ab jetzt reicht ssh kabelbruch.

Schritt 2: Ein eigener Benutzer statt root

Als root zu arbeiten ist bequem, aber genau deshalb keine gute Idee. Ich lege mir einen eigenen Benutzer an, der per sudo Administratorrechte bekommt:

apt update && apt install -y sudo
adduser thomas
usermod -aG sudo thomas

mkdir -p /home/thomas/.ssh
cp /root/.ssh/authorized_keys /home/thomas/.ssh/
chown -R thomas:thomas /home/thomas/.ssh
chmod 700 /home/thomas/.ssh
chmod 600 /home/thomas/.ssh/authorized_keys

Das Passwort, das adduser abfragt, brauche ich später nur noch für sudo, nicht mehr zum Anmelden.

Wichtig: Bevor man irgendetwas am SSH-Zugang ändert, immer eine zweite Sitzung offen lassen. Das ist der Rettungsanker, falls man sich aussperrt.

Schritt 3: Passwörter und root-Login abschalten

Jetzt kommt der eigentliche Härtungsschritt. Ich lege die Einstellungen in einer eigenen Datei unter sshd_config.d ab, statt die Hauptkonfiguration zu bearbeiten:

sudo tee /etc/ssh/sshd_config.d/00-hardening.conf > /dev/null <<'EOF'
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitRootLogin no
EOF

sudo sshd -t && sudo systemctl reload ssh
sudo sshd -T | grep -E 'passwordauth|permitroot'

Die Ausgabe sollte passwordauthentication no und permitrootlogin no zeigen.

Warum 00- im Dateinamen? Bei sshd gewinnt der erste gefundene Wert. Manche Server-Images bringen eine eigene Datei mit, zum Beispiel 50-cloud-init.conf mit PasswordAuthentication yes. Mit 00- am Anfang wird meine Datei zuerst gelesen und setzt sich durch.

Gescheitert, Teil 1: Permission denied

Mein erster Versuch sah so aus:

cat > /etc/ssh/sshd_config.d/00-hardening.conf <<'EOF'
...
EOF

Ergebnis: Permission denied – und gleich danach sshd: command not found. Ich war schon als thomas angemeldet, nicht mehr als root. Und hier kommt eine klassische Falle: Ein sudo vor dem Befehl hilft bei der Umleitung > nicht, denn die Umleitung macht die Shell, und die läuft weiterhin als normaler Benutzer. Die Lösung ist sudo tee, wie oben im Beispiel. Das sshd: command not found hat übrigens einen ganz harmlosen Grund: sshd liegt in /usr/sbin, und das steht bei normalen Benutzern nicht im Pfad. Mit sudo davor wird es gefunden.

Gescheitert, Teil 2: Plötzlich komme ich nicht mehr rein

Nach der Härtung wollte ich mich testweise neu verbinden:

$ ssh kabelbruch
root@203.0.113.10: Permission denied (publickey).

Kurzer Schreckmoment. Dabei hat schlicht alles funktioniert: In meiner ~/.ssh/config stand noch User root – und root darf sich ja jetzt nicht mehr anmelden. Die Sperre hat also genau das getan, was sie sollte.

Ich habe dann grep auf die ~/.ssh/config losgelassen, um nachzusehen – allerdings im falschen Terminal, nämlich auf dem Server statt auf meinem Laptop. Dort gibt es diese Datei natürlich nicht. Eine Faustregel, die ich mir seitdem merke: Steht im Prompt der Name des Servers, bin ich auf dem Server. Alles rund um den eigenen SSH-Schlüssel und die SSH-Config passiert lokal.

Nachdem in der Config User thomas stand, ging es sofort. Der Gegentest gehört trotzdem dazu:

ssh kabelbruch            # klappt als thomas
ssh root@203.0.113.10     # wird abgelehnt

Schritt 4: Die Firewall

Als Nächstes kommt eine einfache Firewall. Ich nutze ufw (Uncomplicated Firewall), die macht ihrem Namen alle Ehre:

sudo apt install -y ufw
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
sudo ufw status verbose

Die Reihenfolge ist entscheidend: SSH erlauben, bevor man ufw enable ausführt. Sonst sperrt man sich mit dem Aktivieren selbst aus. ufw warnt beim Einschalten trotzdem, dass bestehende SSH-Verbindungen unterbrochen werden könnten. Diese Warnung kommt immer, egal welche Regeln existieren. Wenn die SSH-Regel drin ist, kann man sie mit y bestätigen.

Am Ende sind nur noch SSH (22), HTTP (80) und HTTPS (443) offen, für IPv4 und IPv6. Port 80 und 443 brauche ich in Teil 2 für den Webserver und die TLS-Zertifikate.

Schritt 5: Automatische Sicherheitsupdates

Ein Server, der sich nicht selbst aktualisiert, wird irgendwann vergessen. Debian bringt dafür unattended-upgrades mit:

sudo apt install -y unattended-upgrades apt-listchanges
sudo dpkg-reconfigure -plow unattended-upgrades

Im Dialog „Ja“ wählen, fertig. Sicherheitsupdates werden ab jetzt täglich automatisch eingespielt.

Stolperfallen & Learnings

  • Immer eine zweite SSH-Sitzung offen lassen, solange man am Zugang schraubt.
  • sudo hilft nicht bei >, dafür gibt es sudo tee.
  • 00- im Dateinamen unter sshd_config.d, weil der erste Wert gewinnt.
  • Erst SSH in der Firewall erlauben, dann ufw enable.
  • Auf den Prompt schauen: Laptop oder Server? Die Hälfte meiner Fehler an diesem Abend waren Befehle im falschen Terminal.
  • Permission denied (publickey) nach der Härtung ist oft gar kein Fehler, sondern die neue Sperre bei der Arbeit. Erst die eigene SSH-Config prüfen.

Fazit & Ausblick

Nach etwa einer Stunde hatte ich einen Server, auf dem man sich nur noch mit meinem Schlüssel und nicht als root anmelden kann, bei dem nur drei Ports offen sind und der sich selbst mit Sicherheitsupdates versorgt. Das ist kein Hochsicherheitstrakt, aber eine solide Grundlage – und genau das, was ein öffentlicher Server mindestens braucht.

Was als Nächstes kommt: In Teil 2 installiere ich Caddy als Webserver, stelle das DNS ein und setze Security-Header. Dabei hat mich eine Content-Security-Policy eine Weile beschäftigt: Plötzlich waren auf meiner Seite Mond und Sonne gleichzeitig zu sehen.

Und die Idee im Hinterkopf, für die der Server ein bisschen Luft hat: ein eigener kleiner Fediverse-Server. Aber dazu mehr, wenn es so weit ist.