Homeserver-Sicherheitsarchitektur: Unterschied zwischen den Versionen
Admin (Diskussion | Beiträge) |
Admin (Diskussion | Beiträge) |
||
| Zeile 11: | Zeile 11: | ||
Die Kehrseite: Sowie der Server über's Internet erreichbar ist, ist er angreifbar. | Die Kehrseite: Sowie der Server über's Internet erreichbar ist, ist er angreifbar. | ||
Das ist kein theoretisches Szenario. Meiner Erfahrung nach ist es eine Frage von Stunden, bis die ersten Bots "anklopfen", nach laufenden Diensten suchen, nach offenen Ports, nach Schwachstellen. | Das ist kein theoretisches Szenario. Meiner Erfahrung nach ist es eine Frage von Stunden, bis die ersten Bots "anklopfen", nach laufenden Diensten suchen, nach offenen Ports, nach Schwachstellen. | ||
Die öffentliche Erreichbarkeit des eigenen Servers über die ungesicherte Adresse... | |||
Kurz: Die öffentliche Erreichbarkeit des eigenen Servers über die ungesicherte Adresse... | |||
* http://myhomeserver.dyndns.de | * http://myhomeserver.dyndns.de | ||
...ist fahrlässig. | ...ist fahrlässig. | ||
Version vom 27. Mai 2026, 23:43 Uhr
Homeserver-Sicherheitsarchitektur: Aufbau, Funktionsweise und Diagnose
Einen eigenen Server an's Internet zu bekommen, ist nicht allzu schwierig. Ein Dienst, der die eigene, vom Internet-Provider zugewiesene IP-Adresse in einen festen Domain-Namen umwandelt (siehe: Dynamisches DNS), wird im privaten Bereich die Voraussetzung sein. Es gibt verschiedene Anbieter, oft auch kostenlose.
Beispiel: Der eigenen, von außen sichtbaren IP 78.123.234.45 wird vom DynDDS-Dienst die Domain <myhomeserver.dyndns.de> zugewiesen. Einmal eingerichtet, wird der eigene Rechner antworten, wenn die Domain http://myhomeserver.dyndns.de in die Browser-Adressleiste einkopiert wird.
Motivation für einen Server mit OPNsense, Managed Switch und Docker
Die Kehrseite: Sowie der Server über's Internet erreichbar ist, ist er angreifbar. Das ist kein theoretisches Szenario. Meiner Erfahrung nach ist es eine Frage von Stunden, bis die ersten Bots "anklopfen", nach laufenden Diensten suchen, nach offenen Ports, nach Schwachstellen.
Kurz: Die öffentliche Erreichbarkeit des eigenen Servers über die ungesicherte Adresse...
...ist fahrlässig.
Aufgrund einer mangelhaften Konfiguration (offener Samba-Port) hatte ich die sehr reale Erfahrung einer russischen Hackerbande, die einige Hundert Textdateien mit Zahlungsaufforderungen und einen Verschlüsselungstrojaner auf meinem Rechner hinterlassen hatte. Gerettet hatten mich damals die Dateirechte von Linux. Das fehlende E(x)ecutional-Flag und die eingeschränkten Benutzerrechte hatten den Trojaner ausgebremst.
Trotzdem: Nie wieder! Ein wenig von dem, was ich gelernt habe, gebe ich ich hier weiter.
Einleitung
Dieser Artikel beschreibt den Aufbau einer sicheren Netzwerkarchitektur für einen Homeserver, der über das öffentliche Internet erreichbar ist. Der Beitrag richtet sich an alle, die ihre Daten selbst kontrollieren wollen — und dabei nicht unfreiwillig Teil einer globalen Angriffs-Infrastruktur werden möchten. Er will zum Verständnis beitragen, wie die einzelnen Komponenten zusammenspielen — und wie man systematisch vorgeht, wenn etwas nicht funktioniert.
Der Fokus liegt nicht auf einer spezifischen Hardware-Konfiguration, sondern auf dem allgemeinen Prinzip: Wie kommt ein Request von außen sicher zu einem Docker-Container?
Annahmen / Szenario
Folgende Komponenten werden in diesem Artikel vorausgesetzt:
- Homeserver (Raspberry Pi, Intel NUC, oder beliebiger Linux-Server)
- Betriebssystem: Debian/Ubuntu oder vergleichbar
- Docker mit mehreren Diensten (z.B. WordPress, Nextcloud, MediaWiki)
- Caddy als Reverse Proxy (alternativ funktionieren nginx, Apache, oder (ggf. kostenpflichtige) proprietäre Lösungen. Caddy allerdings hat das für mich charmante Feature, sich eigenständig um die TSL-Zertifikate und ihre Verlängerung bei LetsEncrypt zu kümmern. Wer aufgrund "zu vieler" Fehlversuche bei der Zertifikats-Einrichtung von LetsEncrypt auf Tage gesperrt wurde, wird mein Faible über diesen Automatismus nachvollziehen oder teilen können.)
- fail2ban als Brute-Force-Schutz
- DynDNS-Dienst der dem Homeserver eine feste Domain zuweist
- Beispiel:
myhomeserver.dyndns.de
- Beispiel:
- Router / Heimnetz-Gateway
- Beispiel-IP:
192.168.2.1 - Aufgabe: Portweiterleitung, WLAN, DynDNS-Aktualisierung
- Typisch: FritzBox, aber jeder Router mit Portweiterleitung funktioniert
- Beispiel-IP:
- OPNsense Firewall (dedizierte Hardware-Firewall)
- WAN-Interface:
192.168.2.2(im Router-Netz) - LAN-Interface / Server-VLAN:
192.168.10.1 - Aufgabe: Firewall, NAT, DHCP, IDS/IPS
- WAN-Interface:
- Managed Switch (optional, für VLAN-Segmentierung)
- Trennt Server-VLAN vom Rest des Netzes
- Homeserver-IP:
192.168.10.100(per DHCP-Reservation oder besser: Per statischer IP-Adresse fest)
Abschnitt 1: Wie funktionieren die Weiterleitungen? Wer macht was?
Die Kette von außen nach innen
Ein Request von außen (z.B. Browser ruft https://myhomeserver.dyndns.de auf) durchläuft folgende Stationen:
Internet
↓ DNS-Auflösung: dyndns.de → öffentliche IP des Heimnetzes
Router / Gateway (192.168.2.1)
↓ Portweiterleitung: :80 + :443 (und nur! diese!!) → 192.168.2.2
OPNsense WAN-Interface (192.168.2.2)
↓ Destination NAT: :80/:443 → 192.168.10.100
Homeserver eno1/eth0 (192.168.10.100)
↓ Docker-Port-Binding: 0.0.0.0:80 + 0.0.0.0:443
Caddy / Reverse Proxy (Docker-intern)
↓ Reverse Proxy anhand des Host-Headers
Ziel-Container (z.B. wordpress, nextcloud, ...)
Damit ist der Server auf der IP-Ebene (Layer 3 des OSI-Modells) von außen hin "unsichtbar": Die Firewall (OPNsense) lebt, mit dem Gateway, im 192.168.2-er Netz. Von dort routet sie alle Anfragen über die Ports 80 und 443 an das Interface 192.168.10.100. Dahinter, also im 192.168.10-er-Subnetz, residiert der Heimserver.
Am Ende dieses Beitrages gibt es Sicherheitshinweise. Bitte dringend beachten, dass der Router ausschließlich die Ports 80/TCP und 443/TCP nach außen frei gibt. Der Webserver wird dabei so konfiguriert, dass HTTP-Daten über Port 80 nach HTTPS auf Port 443 umgeleitet werden.
Die Idee, ausschließlich HTTPS zuzulassen, läge nahe. Damit bräuchte sich der Webserver nicht um die Weiterleitung auf Port 443 zu befassen. Bedauerlicherweise wird in diesem Fall der Umgang mit dem TLS-Zertifikat, das den HTTPS-Datenstrom verschlüsselt, scheitern. LetsEncrypt benötigt bei Einrichtung und Verlängerung seiner Zertifikate einen offenen 80-er Port.
Sollte es nötig sein, den Server von außen zu administrieren, wird man darüber nachdenken, einen VPN-Tunnel einzurichten. Sicherer jedoch - und einfacher - ist es dennoch, die Administration ausschließlich über einen zertifikats-geschützten SSH-Zugang im inneren 192.168.-er-Netz zuzulassen und, abgesehen von 80 und 443, auf jeden nach außen exponierten Port zu verzichten.
Wer macht was?
| Komponente | Aufgabe | Konfigurationsort |
|---|---|---|
| Router / Gateway | DynDNS-Aktualisierung, erste NAT-Stufe, WLAN | Router Web-UI → Portfreigaben |
| OPNsense Firewall | Firewall-Regeln, zweite NAT-Stufe (Destination NAT), DHCP für Server-VLAN | OPNsense Web-UI (Standard: https://<wan-ip>:443 oder :8443) |
| Caddy / Reverse Proxy | TLS-Terminierung, Let's Encrypt, Weiterleitung an Container | Caddyfile auf dem Homeserver |
| Docker | Container-Isolation, internes Netz (Standard: 172.18.0.0/16) | docker-compose.yml pro Dienst |
| fail2ban | Brute-Force-Schutz auf Applikationsebene | Jail-Configs im fail2ban-Container |
Wichtig: OPNsense hat ZWEI Firewall-Engines
OPNsense 26.x hat zwei parallele Regelwerke:
- Rules [new] — neue API-basierte Engine
- Rules — alte Engine (Legacy)
Neue Regeln immer in beiden anlegen! Bei Problemen immer beide prüfen.
Abschnitt 2: Wo steht was?
IP-Adressen (Beispiel-Setup)
| Gerät | Interface | IP | Bemerkung |
|---|---|---|---|
| Router / Gateway | — | 192.168.2.1 | Gateway, WLAN, DynDNS |
| OPNsense WAN | wan-interface | 192.168.2.2 | Statisch konfiguriert |
| OPNsense LAN / Server-VLAN | lan-interface | 192.168.10.1 | Gateway für Server-Netz |
| Homeserver | eno1 / eth0 | 192.168.10.100 | Fest per DHCP-Reservation (MAC-Adresse) |
| Client-Geräte | WLAN | 192.168.2.x | Im Router-Netz, nicht im Server-VLAN |
Homeserver: Netzwerk-Interface-Konfiguration
Der Homeserver bezieht seine IP per DHCP vom OPNsense-DHCP-Server. Bei Debian/Ubuntu mit systemd-networkd:
/etc/systemd/network/10-eth0.network
Korrekter Inhalt (DHCP):
[Match] Name=eno1 [Network] DHCP=ipv4 [DHCP] RouteMetric=100
⚠️ Niemals eine statische IP oder einen Gateway manuell eintragen!
Der Default-Gateway kommt immer vom DHCP-Server der OPNsense-Firewall (192.168.10.1).
Kea DHCP Lease-Datei (OPNsense)
/var/db/kea/kea-leases4.csv
Hier verwaltet Kea die aktiven Leases. Bei Problemen mit doppelten IPs kann diese Datei (nach configctl kea stop) auf den Header reduziert werden:
address,hwaddr,client_id,valid_lifetime,expire,subnet_id,fqdn_fwd,fqdn_rev,hostname,state,user_context,pool_id
Abschnitt 3: Systematische Diagnose — Schritt für Schritt
Grundprinzip: Von außen nach innen
Immer von außen nach innen vorgehen — nie raten, immer messen. Jede Schicht einzeln prüfen.
Schritt 1: Ist OPNsense überhaupt erreichbar?
# Vom Client-Rechner: ping 192.168.2.2
⚠️ OPNsense antwortet standardmäßig nicht auf ICMP/Ping von außen — das ist korrekt!
Stattdessen prüfen:
# Web-UI erreichbar? https://192.168.2.2:8443 # Router zeigt OPNsense als verbundenes Gerät? http://192.168.2.1
Schritt 2: OPNsense intern prüfen
Per SSH oder serieller Konsole auf OPNsense:
# WAN-Interface OK? ifconfig <wan-interface> # Erwartete Ausgabe: inet 192.168.2.2, status: active # LAN-Interface OK? ifconfig <lan-interface> # Erwartete Ausgabe: inet 192.168.10.1, status: active # Router erreichbar? ping -c 3 192.168.2.1 # Homeserver erreichbar? ping -c 3 192.168.10.100
Schritt 3: Homeserver direkt prüfen
# SSH auf Homeserver: ssh root@192.168.10.100 # Alle Container laufen? docker ps # Internet vom Homeserver aus erreichbar? ping -c 3 9.9.9.9 # Routing-Tabelle korrekt? ip route show # Erwartete Default-Route: default via 192.168.10.1 dev eno1 # NICHT: default via 192.168.178.x — das wäre falsch! # Welche IP hat das Interface? ip addr show eno1 # Erwartete IP: 192.168.10.100
Schritt 4: Caddy / Reverse Proxy prüfen
# Läuft Caddy und antwortet er lokal? curl -v http://localhost # Erwartete Antwort: HTTP 308 Redirect → https://localhost/ # Caddy-Logs auf Fehler prüfen: docker logs caddy --tail 50
Typische Fehler in den Caddy-Logs:
| Fehlermeldung | Bedeutung | Lösung |
|---|---|---|
dial tcp ...:443: connect: no route to host |
Caddy kommt nicht ins Internet (Let's Encrypt nicht erreichbar) | Routing auf Homeserver prüfen (Schritt 3) |
no route to host bei Ping auf 9.9.9.9 |
Falscher Default-Gateway | ip route show prüfen, falsche Route entfernen
|
Schritt 5: NAT und Firewall-Regeln prüfen (OPNsense Shell)
# NAT-Regeln anzeigen: pfctl -s nat | grep <PORT> # Firewall-Regeln anzeigen: pfctl -s rules | grep <PORT> # Traffic auf WAN-Interface beobachten (kommt der Request an?): tcpdump -i <wan-interface> port <PORT> # Traffic auf LAN-Interface beobachten (wird er weitergeleitet?): tcpdump -i <lan-interface> port <PORT>
Diagnose-Logik:
tcpdump WAN sieht Traffic, LAN nicht → NAT-Regel prüfen! tcpdump WAN sieht keinen Traffic → Portweiterleitung im Router prüfen!
Schritt 6: Bekannte Fallstricke
| Problem | Symptom | Ursache | Fix |
|---|---|---|---|
| Falsche statische Route | ping 9.9.9.9 schlägt fehl |
Netzwerk-Config hat statischen Gateway eingetragen | Auf DHCP umstellen, systemctl restart systemd-networkd
|
| Homeserver hat falsche IP | NAT-Regeln greifen nicht | DHCP hat beim Neustart temporär andere IP vergeben | DHCP-Reservation per MAC-Adresse einrichten |
| DHCP-Leases durcheinander | Server taucht mit zwei IPs auf | Neustart hat neuen Lease vor Ablauf des alten erzeugt | configctl kea stop, Lease-CSV bereinigen, configctl kea start
|
| OPNsense Firewall-Regel fehlt | Dienste von außen nicht erreichbar, intern OK | Regel nur in einer der beiden Engines angelegt | Regel in beiden Engines anlegen (Rules [new] + Rules) |
| NAT-Regel ohne Subnetz-Maske | Traffic kommt nicht an | 192.168.178.0 statt 192.168.178.0/24 eingetragen |
Subnetz-Maske ergänzen — ohne /24 gilt nur die Netzadresse selbst! |
| Source/Destination Port vertauscht | Verbindungen werden blockiert | Bei Firewall-Regel Port unter "Source" statt "Destination" eingetragen | Source Port: any / Destination Port: gewünschter Port |
Notfall-Cheatsheet
# OPNsense: Firewall temporär deaktivieren (Notfall!) pfctl -d # OPNsense: Firewall wieder aktivieren pfctl -e # OPNsense: Kea DHCP stoppen/starten configctl kea stop configctl kea start # Homeserver: Falsche Default-Route entfernen ip route del default via <falsche-gateway-ip> dev eno1 # Homeserver: IP manuell setzen (Sofortlösung bei DHCP-Problemen) ip addr add 192.168.10.100/24 dev eno1 # Homeserver: Netzwerk-Dienst neu starten (Verbindung kurz weg!) systemctl restart systemd-networkd # Homeserver: Docker-Logs prüfen docker logs caddy --tail 50 docker ps # OPNsense: Alias-Tabellen prüfen (Blocklisten) pfctl -t <tabellenname> -T show | wc -l pfctl -t <tabellenname> -T show | grep <IP>
Sicherheitshinweise
- SSH-Ports nicht über das Internet freigeben — nur intern erreichbar lassen! Wenn das nicht geht: VPN-Tunnel!
- Portweiterleitung im Router auf das Minimum beschränken (nur die Ports 80 + 443 öffnen)
- Für interne Admin-Interfaces (OPNsense Web-UI, Monitoring) keine externen Freigaben
- DHCP-Reservations per MAC-Adresse oder statische IP-Adressen verhindern IP-Wechsel nach Neustarts
- Bei Firewall-Regeln: Block-Regeln immer vor Pass-Regeln platzieren
Abschließend eine absolute Regel, die immer und durch die Ewigkeiten gilt:
- Dienste, die auf einem Rechner NICHT existieren, werden niemals angegriffen! Wenig ist hier mehr!
Dieser Artikel basiert auf praktischen Erfahrungen aus dem Betrieb eines selbst gehosteten Servers mit OPNsense, Cisco-Switch und Docker-Stack. Erstellt: Mai 2026
