Portknocks sind wie ein akustisches Signal, was eine Geheimtür öffnet: nur wer die richtige Tonfolge kennt, darf eintreten. SPA (Single Packet Authorization) mit fwknop macht das verschlüsselt und zuverlässig. Für mobile Arbeit (wechselnde IPs) kombinieren wir SPA mit WireGuard als Fallback — oder nutzen SPA-Varianten, die nicht an eine feste Client-IP binden (z. B. --resolve-url oder GPG-SPA). nftables ist die Firewall-Engine, mit der wir die Zugriffsregeln sauber und performant verwalten.
Dieses Tutorial ist in zwei Teilen aufgeteilt:
Ziel: Server so einrichten, dass SSH standardmäßig versteckt ist (nur per SPA erreichbar) und zusätzlich WireGuard läuft, damit Clients auch bei wechselnder IP zuverlässig verbinden können.
Du brauchst physischen/KVM-Zugang oder eine Admin-IP, die du vorerst freihältst.
Lege unbedingt ein Backup von nftables an:
sudo nft list ruleset > ~/nftables-before-fwknop.conf
Arbeite am Anfang getestet in einer VM, wenn möglich.
Da kommen wir leider nicht drumherum, erst einmal die Dinge zu installieren, die wir brauchen:
# Server (Debian/LMDE/Ubuntu)
sudo apt update
sudo apt install fwknop-server fwknop-client nftables wireguard -y
sudo systemctl enable --now nftables
Prüfe:
nft --version
sudo systemctl status fwknop-server
Wir legen ein Set allowed_ssh an — fwknop fügt Client-IPs dort ein (mit Timeout). So bleibt die Firewall sauber.
# Table anlegen (falls noch nicht vorhanden)
sudo nft add table inet filter
# Set für erlaubte SSH-IPs (IPv4) mit timeout-Flag
sudo nft 'add set inet filter allowed_ssh { type ipv4_addr\; flags timeout\; }'
# (Optional) IPv6 Set
sudo nft 'add set inet filter allowed_ssh_v6 { type ipv6_addr\; flags timeout\; }'
# INPUT Chain mit policy drop
sudo nft 'add chain inet filter input { type filter hook input priority 0 \; policy drop\; }'
# Basisregeln
sudo nft 'add rule inet filter input iif "lo" accept'
sudo nft 'add rule inet filter input ct state established,related accept'
# Erlaube SSH für IPs, die im Set sind
sudo nft 'add rule inet filter input ip saddr @allowed_ssh tcp dport 22 accept'
# IPv6 Variante (optional)
sudo nft 'add rule inet filter input ip6 saddr @allowed_ssh_v6 tcp dport 22 accept'
Speichern (Boot-Persistence):
sudo nft list ruleset > /etc/nftables.conf
nft auf deinem System:which nft
# z.B. /usr/sbin/nft
fwknopd Interface einstellen (fwknop sniffet per pcap):
Editiere /etc/fwknop/fwknopd.conf (als root) und setze:PCAP_INTF enp3s0 # ersetze enp3s0 durch dein externes Interface
UseSyslog Y # optional, damit SPA-Versuche in syslog landen
access.conf (fwknop) anlegen — nft-Befehle als FW_CMD
Erstelle die /etc/fwknop/access.conf mit folgendem Template (ersetze die Base64-Schlüssel durch die später vom Client erzeugten):sudo nano /etc/fwknop/access.conf
Als Inhalt:
SOURCE: ANY;
OPEN_PORTS: tcp/22;
FW_ACCESS_TIMEOUT: 60;
DATA_COLLECT_MODE: PCAP;
KEY_BASE64: <DEIN_RIJNDAEL_KEY_BASE64>;
HMAC_KEY_BASE64: <DEIN_HMAC_KEY_BASE64>;
# nft add element mit timeout (FW_CMD führt das aus)
# %IP% = Platzhalter, den fwknop zur Laufzeit ersetzt
FW_CMD: /usr/sbin/nft add element inet filter allowed_ssh { %IP% timeout 60s }
FW_CMD_DEL: /usr/sbin/nft delete element inet filter allowed_ssh { %IP% }
Zugriffsrechte setzen:
sudo chown root:root /etc/fwknop/access.conf
sudo chmod 600 /etc/fwknop/access.conf
fwknop starten / neu starten:sudo systemctl restart fwknop-server 2>/dev/null || sudo systemctl restart fwknopd
sudo systemctl enable --now fwknop-server
sudo journalctl -u fwknop-server -f
Hinweis: Wenn deine nft Version timeout für Set-Elemente nicht unterstützt, verwende
FW_CMDohne timeout undFW_CMD_DELals Cleanup (fwknop kann dasFW_CMD_DELaufrufen), oder nutze ein kleines Timer-Script.
WireGuard ist die stabile Lösung für mobile Clients. Wenn WireGuard läuft, kannst du SSH über den Tunnel erreichen, völlig unabhängig von der öffentlichen Client-IP.
# Verzeichnisse
sudo mkdir -p /etc/wireguard
sudo chmod 700 /etc/wireguard
# Schlüssel (als root)
wg genkey | sudo tee /etc/wireguard/server_private.key | wg pubkey | sudo tee /etc/wireguard/server_public.key
Erstelle /etc/wireguard/wg0.conf (Beispiel):
[Interface]
Address = 10.10.10.1/24
ListenPort = 51820
PrivateKey = <INHALT VON /etc/wireguard/server_private.key>
# Optional: bring up nftables rules beim Start (PostUp/PostDown) oder verwalte nftables manuell
PostUp = /usr/sbin/nft add element inet filter allowed_ssh { 10.10.10.2 } || true
PostDown = /usr/sbin/nft delete element inet filter allowed_ssh { 10.10.10.2 } || true
Hinweis: In PostUp/PostDown kannst du erlauben, dass bestimmte VPN-Peers direkt SSH dürfen (z. B. 10.10.10.2). Alternativ verwaltest du Zugriff rein über die
allowed_sshSet-Einträge für öffentliche IPs — VPN-Tunneled Clients benötigen das nicht.
Aktiviere nun WireGuard:
sudo systemctl enable --now wg-quick@wg0
sudo wg show
Wenn SSH über den Tunnel laufen soll, sorge dafür, dass die INPUT-Chain VPN-Traffic zulässt (beispielsweise accept für iifname wg0 oder für das VPN-Subnet):
sudo nft 'add rule inet filter input iif "wg0" accept'
# oder gezielt: accept tcp dport 22 iif "wg0"
Wir behandeln die Router-Konfiguration unten (Fritz!Box & Speedport).
Backup nftables (wichtig):
sudo nft list ruleset > ~/nftables-before-fwknop.conf
Rollback (falls ausgesperrt):
sudo nft -f ~/nftables-before-fwknop.conf
Rollback-Script (optional):
cat > ~/fwknop-rollback-nft.sh <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
BACKUP="$HOME/nftables-before-fwknop.conf"
if [ ! -f "$BACKUP" ]; then
echo "Backup $BACKUP fehlt"
exit 1
fi
echo "Restore nftables from $BACKUP"
sudo nft -f "$BACKUP"
EOF
chmod +x ~/fwknop-rollback-nft.sh
sudo nft list ruleset — sieht man Table / Set / Rules?which nft — Pfad prüfen und ggf. in access.conf anpassen.ip addr — Interface prüfen (PCAP_INTF).sudo journalctl -u fwknop-server -f — Logs beobachten.Ziel: zwei Wege, wie du vom Laptop/Smartphone zuverlässig Zugriff bekommst:
--resolve-url (mobil, einfach)Konzept: Client ermittelt seine öffentliche IP über eine Web-URL (z. B. https://icanhazip.com oder besser: deine eigene kleine Resolver-URL) und packt diese IP ins SPA-Paket. Server öffnet genau diese IP.
# Beispiel: verwende deine Server-Domain im -D Feld
fwknop -A tcp/22 -D server.example.com --use-hmac --resolve-url https://icanhazip.com --verbose
Vorteile: funktioniert bei wechselnder IP; minimaler Konfig-Aufwand.
Nachteile: Privatsphäre (du fragst externe Dienste nach deiner IP). Ich empfehle, falls möglich, eine eigene kleine resolver-URL zu hosten (z. B. ein kleines CGI mit echo $_SERVER['REMOTE_ADDR']).
Server: keine Änderung nötig, access.conf wie in Teil A verwenden — fwknop setzt %IP% aus dem SPA.
Konzept: Client signiert/verschlüsselt SPA mit privatem GPG-Key. Server nutzt Public Key, um Pakete zu prüfen. Server vertraut dem Paketinhalt, nicht der Quelladresse → mobil und sicher.
Schritte (Kurz):
gpg --full-generate-key
# Notiere KeyID (z.B. ABCDEF12)
Server (Importiere Client Public Key):
Übertrage gpg --export --armor CLIENT_KEYID und importiere auf dem Server in /root/.gnupg oder in einem dedizierten gnupg-home für fwknop.
access.conf (GPG-Konfig):
GPG_HOME_DIR: /root/.gnupg
GPG_REMOTE_ID: <CLIENT_KEYID>
GPG_DECRYPT_ID: <SERVER_KEYID> # optional, wenn Entschlüsselung nötig
fwknop manpage (Optionen wie --use-gpg / --gpg-key — die Flags variieren mit Version).Vorteile: sehr sicher, keine Source-IP-Problematik. Nachteile: GPG-Keymanagement erforderlich (Passphrase, ggf. Smartcard).
Der Client ermittelt seine öffentliche IP über eine Web-URL (z. B. https://icanhazip.com oder eine eigene Resolver-URL) und sendet ein SPA an den Server. Einfach, robust bei wechselnden IPs.
# Auf dem Client ausführen (ersetzen!)
fwknop -A tcp/22 -D server.example.com --use-hmac --resolve-url https://icanhazip.com --verbose
Erläuterung / Platzhalter
-A tcp/22 → Zugriff auf SSH (Port 22).-D server.example.com → Hostname oder IP deines Servers (ersetzen!).--use-hmac → nutze HMAC (bei symmetrischen Keys).--resolve-url https://icanhazip.com → Dienst zur Bestimmung der öffentlichen IP; empfehle eigene Resolver-URL für Privatsphäre.--verbose → Ausgabe zum Debuggen.Schnell-Check (nach Ausführung):
# prüfe, ob ~/.fwknoprc Schlüssel enthält
grep -E '^(KEY_BASE64|HMAC_KEY_BASE64):' ~/.fwknoprc
Wenn Keys vorhanden sind, kannst du daraus /etc/fwknop/access.conf bauen (Server-Seite), z. B. mit dem AWK-Snippet im Tutorial.
Wenn du von überall ohne IP-Bindung zugreifen willst und GPG benutzt, signiere/verschlüssele das SPA mit deinem GPG-Key. (Flag-Namen können je nach fwknop-Version leicht variieren — prüfe man fwknop falls nötig.)
Beispiel (konzeptionell — ersetze CLIENT_KEYID und server.example.com):
# Beispiel (prüfe deine fwknop-Version / manpage für genaue gpg-Flags)
fwknop -A tcp/22 -D server.example.com --use-gpg --gpg-identity CLIENT_KEYID --verbose
Wichtige Hinweise
--use-gpg / --gpg-identity sind exemplarische Flags — die genaue Option kann je nach fwknop-Version leicht heißen –gpg-key / –gpg-id. Prüfe man fwknop oder fwknop –help./root/.gnupg) und access.conf so konfigurieren, dass GPG_REMOTE_ID / GPG_HOME_DIR gesetzt sind.Kurz-Check auf dem Client (GPG):
gpg --list-keys
# notiere die KeyID, die du dann in fwknop verwendest
access.conf vorzubauenWenn du nach dem --key-gen die ~/.fwknoprc hast, kannst du lokal schnell eine access.conf erzeugen (auf dem Client) und dann per scp auf den Server kopieren:
awk '/^KEY_BASE64:/ {k=$2} /^HMAC_KEY_BASE64:/ {h=$2} END { if(!k||!h){print "Fehler: Keys nicht gefunden" > "/dev/stderr"; exit 2} \
printf "SOURCE: ANY;\nOPEN_PORTS: tcp/22;\nFW_ACCESS_TIMEOUT: 60;\nDATA_COLLECT_MODE: PCAP;\n\nKEY_BASE64: \
%s\nHMAC_KEY_BASE64: %s\n", k, h }' ~/.fwknoprc > /tmp/fwknop_access.conf
Danach scp + sudo mv wie im Tutorial beschrieben, oder nutze das Einzeiler-Deploy aus dem Artikel.
wg genkey | tee client_private.key | wg pubkey > client_public.key
[Interface]
PrivateKey = <INHALT client_private.key>
Address = 10.10.10.2/24
DNS = 10.10.10.1
[Peer]
PublicKey = <SERVER_PUBLIC_KEY>
Endpoint = your.dyndns.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0
PersistentKeepalive = 25
Auf dem Server füge den Peer (öffentliche Schlüssel + AllowedIPs) zur wg0 Konfiguration hinzu (oder verwalte per wg set).
Firewall: erlaube wg0 traffic (siehe Teil A).
Vorteile: stabil, unabhängig von Client-IP, performant. Nachteile: initialer Setupaufwand, Peers verwalten.
Hinweis: Menübezeichnungen / Firmwarevarianten weichen leicht ab. Wenn die einzelnen Menüs nicht exakt passen, suche nach Begriffen wie „Internet“,
„Freigaben“, „Portfreigabe“, „NAT“ oder „Portweiterleitung“. Lies im Zweifelsfall das Handbuch deines Modells.
Speichern. Teste von außen (z. B. Handy über Mobilfunk) mit nc / wg / fwknop Aufruf.
Speichern und testen.
Für WireGuard: versuche mit wg/wg-quick die Verbindung vom Mobilgerät.
Für SPA: teste fwknop Client-Befehl mit --resolve-url vom Mobilnetz.
Zum Abschluss liefere ich noch 3 mögliche Konfigurationsvarianten für die Mobile Verbindung.
Konzept kurz: Client ermittelt seine öffentliche IP via HTTP(S) (z. B. https://icanhazip.com oder besser: deine eigene kleine resolver-URL) und packt diese IP ins SPA. Server fügt diese IP ins nft-Set ein.
Server — access.conf (nft-Integration)
Füge in /etc/fwknop/access.conf (ersetze Base64-Keys):
SOURCE: ANY;
OPEN_PORTS: tcp/22;
FW_ACCESS_TIMEOUT: 60;
DATA_COLLECT_MODE: PCAP;
KEY_BASE64: <DEIN_RIJNDAEL_KEY_BASE64>;
HMAC_KEY_BASE64: <DEIN_HMAC_KEY_BASE64>;
FW_CMD: /usr/sbin/nft add element inet filter allowed_ssh { %IP% timeout 60s }
FW_CMD_DEL: /usr/sbin/nft delete element inet filter allowed_ssh { %IP% }
Achte auf den korrekten
nft-Pfad (which nft) und dassallowed_sshexistiert.
fwknop -A tcp/22 -D server.example.com --use-hmac --resolve-url https://icanhazip.com --verbose
(Empfehlung: ersetze https://icanhazip.com durch eine eigene Resolver-URL, wenn dir Privatsphäre wichtig ist.)
Kurzcheck
sudo nft list set inet filter allowed_ssh → die IP sollte erscheinen.ssh -i ~/.ssh/id_rsa user@server.example.com.Vor-/Nachteile
Konzept kurz: Client signiert/verschlüsselt das SPA mit seinem privaten GPG-Key; Server überprüft/entschlüsselt mit dem zugehörigen Public Key. Server braucht nicht die Client-Source-IP zu binden (oder kann zusätzlich %IP% verwenden).