
Knocker, ein knock-basierter Zugangskontrolldienst für dein Homelab

Knocker ist ein selbst gehosteter Dienst, der ein HTTP-basiertes „Klopf-Klopf“-Single-Packet-Authorization (SPA)-Gateway für dein Homelab bereitstellt, mit Clients für das Web, die CLI (inkl. GNOME) und Android. Es kann als Authentifizierung für deinen Reverse-Proxy wie Caddy oder sogar auf Firewall-Ebene mittels der FirewallD-Integration verwendet werden. Es ermöglicht dir, deine Dienste vollständig privat zu halten und sie nur auf Anfrage für autorisierte IP-Adressen freizuschalten.
Dies ist ideal für Homelab-Umgebungen, in denen du Dienste ohne dauerhafte VPN-Verbindung für das Internet freigeben möchtest und gleichzeitig deine öffentliche Angriffsfläche minimierst.
sequenceDiagram
participant User
participant Caddy as Reverse Proxy (Caddy)
participant Knocker
participant Service as Protected Service
User->>Caddy: HTTP request to protected service
Caddy->>Knocker: GET /verify (copies X-Forwarded-For)
Knocker-->>Knocker: check always_allowed_ips / excluded_paths / whitelist
alt IP whitelisted
Knocker-->>Caddy: 200 OK (empty body)
Caddy->>Service: forward request
Service-->>Caddy: 200 OK
Caddy-->>User: 200 OK
else IP not whitelisted
Knocker-->>Caddy: 401 Unauthorized (empty body)
Caddy-->>User: 401 Unauthorized
end
Note over User,Knocker: Performing a "knock" (to add whitelist entry)
User->>Knocker: POST /knock (X-Api-Key, optional ip_address, ttl)
Knocker->>Knocker: validate API key, determine client IP
Knocker->>Knocker: update whitelist.json with expiry
Knocker-->>User: 200 OK (whitelisted_entry, expires_at, expires_in_seconds)Dieses Projekt ist für die Bereitstellung als Docker-Container mit der bereitgestellten docker-compose.yml konzipiert. Es verwendet die vorgebauten Docker-Images mit Unterstützung für AMD64, ARMv8 und ARMv7.
Knocker bietet verschiedene Image-Tags für unterschiedliche Anwendungsfälle:
latest – Neuestes stabiles Release (empfohlen für die Produktion)v1.2.3 – Bestimmte Versions-Tags (festgepinnte Versionen)main – Entwicklungsbranch (fortlaufende Updates, möglicherweise instabil)Konfiguration:
knocker.example.yaml in knocker.yaml um.knocker.yaml in eigene, sichere, zufällige Zeichenfolgen.trusted_proxies in knocker.yaml. Sie sollte dem Subnetz des Reverse-Proxy-Netzwerks entsprechen (docker network inspect xxx).whitelist.storage_path im Arbeitsverzeichnis der App, unter /data oder /tmp.firewalld.enabled: true setzt und die zugehörigen Einstellungen anpasst. Hinweis: Dies erfordert, dass der Container als Root läuft.Dienst starten:
docker compose up -d
Dies zieht das vorgebaute knocker-Image und startet sowohl den knocker- als auch den caddy-Dienst.
Knocker fungiert als Authentifizierungs-Gateway für deinen Reverse-Proxy. Es bietet einen Verify-Endpunkt, um zu prüfen, ob die anfragende IP auf der Whitelist steht. Falls nicht, antwortet es mit 401, und der Reverse-Proxy lehnt die Verbindung ab.
Caddy hat die Direktive forward_auth, um Verbindungen über einen Auth-Endpunkt zu prüfen.
Ein wiederverwendbares Snippet definieren: Es ist bewährte Praxis, ein Snippet in deiner Caddyfile für die Auth-Prüfung zu definieren.
Dienste schützen: Importiere das Snippet für jeden Dienst, den du schützen möchtest.
Beispiel Caddyfile:
# Caddyfile
# Definiere ein wiederverwendbares Snippet für die Knock-Klopf-Prüfung.
# Es zeigt auf den Knocker-Dienst über Docking's internes DNS.
(knocker_auth) {
forward_auth knocker:8000 {
uri /verify
}
}
# Der öffentliche Endpunkt zum Durchführen des Knocks.
# Stelle sicher, dass diese Domain auf die IP deines Caddy-Servers zeigt.
knock.your-domain.com {
reverse_proxy knocker:8000
}
# Ein Beispiel eines geschützten Dienstes.
jellyfin.your-domain.com {
import knocker_auth # Wendet die forward_auth-Prüfung an
reverse_proxy jellyfin_service_name:8096
}
Wenn ein Benutzer nicht auf der Whitelist steht, gibt die forward_auth-Direktive von Caddy eine 401 Unauthorized-Antwort mit leerem Körper zurück.
Wichtiger Hinweis: Die handle_errors-Direktive von Caddy funktioniert nicht mit forward_auth-Antworten. Die Fehlerantwort kommt direkt vom Authentifizierungsdienst (Knocker), nicht von Caddy selbst, daher kann handle_errors diese Antworten nicht abfangen oder ändern.
Knocker bietet eine erweiterte Firewall-Integration über firewalld, die dynamische, zeitbasierte Firewall-Regeln erstellt, die automatisch basierend auf der in den Knock-Anfragen angegebenen TTL verfallen. Diese Funktion arbeitet auf Netzwerkebene und ermöglicht es dir, Knocker für nicht-HTTP-Dienste wie SSH oder Gameserver zu verwenden.
sequenceDiagram
participant Client as User
participant Firewall as Firewalld (knocker zone)
participant Knocker
participant Service as Protected Service (port 22)
Note over Client,Firewall: Initial state — monitored port is blocked by default
Client->>Firewall: TCP SYN to Service:22
Firewall-->>Client: DROP (no response)
Note over Client,Knocker: User performs a knock to whitelist their IP
Client->>Knocker: POST /knock (X-Api-Key, optional ip_address, ttl)
Knocker->>Knocker: validate API key & determine client IP
Knocker->>Firewall: add rich accept rule for client IP on port 22 with timeout
Firewall-->>Knocker: success
Note over Firewall,Client: New rule overrides DROP due to higher priority
Client->>Firewall: TCP SYN to Service:22
Firewall->>Service: forward packet
Service-->>Client: TCP SYN-ACK (connection established)
Knocker->>Knocker: update whitelist.json with expiryKnocker benötigt FirewallD 2.0+ wegen der Abhängigkeit von der Zonen-Prioritätsfunktion. Es ist in Debian 13, Ubuntu 24.04 LTS und anderen aktuellen stabilen Distributionen verfügbar.
FirewallD wurde gewählt, weil es die CLI-Schnittstelle vom Daemon trennt. Dies ermöglicht es Knocker, firewalld von einem Docker-Container aus zu steuern, indem der D-Bus-Socket des Systems eingebunden wird. Außerdem unterstützt FirewallD zeitgesteuerte Regeln, sodass Knocker-Regeln automatisch am Ende der TTL verfallen.
FIREWALLD FUNKTIONIERT NICHT MIT DOCKER-PUBLISHED-PORTS, siehe dieses Issue für weitere Details.
Voraussetzungen
Konfiguration
knocker.yaml-Konfiguration. Die Einstellungen sind bereits in der Beispielkonfiguration vorhanden.Aktive Regeln überwachen:
# Knock-Zone prüfen
firewall-cmd --zone=knocker --list-all
# Rich Rules anzeigen
firewall-cmd --zone=knocker --list-rich-rules
# Regeländerungen überwachen
journalctl -u firewalld -f
Ausführliche Informationen zur Konfiguration, Architektur und Fehlerbehebung finden sich im vollständigen FirewallD-Integrationsleitfaden.
Wenn du Knocking für IPs hinter Tailscale oder anderen IPs aktivierst, können Probleme aufgrund der Funktionsweise des Userland-Proxys auftreten; die Anfrage-IP kann von der tatsächlichen IP-Adresse abweichen.
Das Deaktivieren des Userland-Proxys sollte das Problem beheben, aber teste deine Einrichtung unbedingt. Du kannst auch das Host-Netzwerk verwenden.
/knock (POST)Dieser Endpunkt validiert einen API-Schlüssel und setzt eine IP auf die Whitelist.
Header:
X-Api-Key: Dein geheimer API-Schlüssel.Body (Optional):
allow_remote_whitelist: true):
{"ip_address": "YOUR_TARGET_IP_OR_CIDR"}
Beispiel (Whitelisting der eigenen IP):
curl -i -H "X-Api-Key: YOUR_SECRET_KEY" https://knock.your-domain.com/knock
Erfolgsantwort (200 OK):
{
"whitelisted_entry": "1.2.3.4",
"expires_at": 1672534800,
"expires_in_seconds": 3600
}
/verify (GET)Dieser Endpunkt wird von Caddys forward_auth verwendet, um zu prüfen, ob die IP des Clients auf der Whitelist steht. Er gibt bei Erfolg 200 OK und bei Misserfolg 401 Unauthorized zurück. X-Forwarded-For, X-Forwarded-Host und X-Forwarded-Uri werden nur als vertrauenswürdig eingestuft, wenn die Anfrage von server.trusted_proxies stammt.
Caddy leitet die relevanten X-Forwarded-*-Request-Header bereits an Knocker weiter, sodass /verify die Auth-Entscheidung treffen kann.
Das Projekt enthält eine vollständige Testsuite.
Dieses Projekt verwendet Astral's Python-Toolchain:
uv für Abhängigkeitsverwaltung, Umgebungen und Befehlsausführungruff für Linting und Formatierungty für TypprüfungSo führst du die Tests lokal aus:
uv installieren:
curl -LsSf https://astral.sh/uv/install.sh | sh
Projektumgebung synchronisieren:
uv sync --all-groups
Überprüfungen ausführen:
uv run pytest
uv run --group lint ruff check .
uv run --group lint ruff format --check .
uv run --group type ty check
Es gibt eine Entwicklungsumgebung unter dev, mit Bash-Skripten für Integrationstests mit Caddy und getrennt davon mit firewalld.
Die Standard-Test-Stacks sind dev/docker-compose.yml und dev/docker-compose.ci.yml; beide exponieren Caddy auf http://localhost:18080 und https://localhost:18443.
Der CI führt die Caddy-Tests aus, aber firewalld benötigt einen privilegierten Runner, weshalb es lokal ausgeführt werden muss und nicht Teil des CI ist.
Interaktive Dokumentationsendpunkte (/docs, /redoc, /openapi.json) sind standardmäßig deaktiviert. Um sie zu aktivieren, setze Folgendes in knocker.yaml:
documentation:
enabled: true
openapi_output_path: "openapi.json"
Wenn die Dokumentation deaktiviert ist (Standard), entfernt Knocker diese Endpunkte und löscht alle zuvor generierten Schema-Dateien, um veraltete Artefakte zu vermeiden.
Eine formelle API-Spezifikation und eine Zusammenfassung der architektonischen Entscheidungen findest du in der Dokumentation.
Knocker wurde vollständig vibe-coded. Die anfängliche Implementierung erfolgte mit Gemini 2.5 Pro, dank der Token, die im Roo Code/Requesty-Hackathon zur Verfügung gestellt wurden.
Weitere Funktionen wurden größtenteils mit dem GitHub Copilot Agent (Sonnet 4/später 4.5) erstellt, der viele Korrekturen benötigte, die hauptsächlich von GPT-5 mini/CODEX in Roo Code, Opencode und der Standard-Copilot-Erweiterung durchgeführt wurden.
Ich habe mein Bestes gegeben, Änderungen stets geplant und nach jeder Änderung getestet. Aber wenn du Anti-KI bist, werde ich deine Meinung dazu wahrscheinlich nicht ändern können.