- Go 65.7%
- TypeScript 21.9%
- HTML 5.5%
- JavaScript 3.7%
- PowerShell 1.3%
- Other 1.9%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
|
All checks were successful
Image bauen / image (push) Successful in 50s
Windows 10 25H2 fuehrt hinter „path" noch „pid" — ein Feld, das aeltere Fassungen nicht haben und das in den meisten Beispielen fehlt. Aufgenommen von einem laufenden Host. Gedeutet wurde es von Anfang an richtig, weil der Parser die Positionen aus der Kopfzeile liest. Der Test haelt genau das fest: Stuende die Paketgroesse eine Stelle daneben, kaeme statt 363 eine Portnummer heraus. Damit stehen drei Kopfzeilenformen nebeneinander im Test — mit pid, ohne pid, und ganz ohne path. Alle drei gibt es im Bestand. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
||
| .forgejo/workflows | ||
| agent | ||
| deploy | ||
| design | ||
| docs | ||
| server | ||
| shared | ||
| web | ||
| .dockerignore | ||
| .gitignore | ||
| CHANGELOG.md | ||
| DECISIONS.md | ||
| go.mod | ||
| go.sum | ||
| Makefile | ||
| README.md | ||
nfthub
Zentrale Verwaltung der nftables-Firewalls mehrerer Linux-Hosts. Mehrere Besitzer („Owner") verwalten jeweils ausschließlich ihre eigenen Hosts: Firewall-Ereignisse einsehen und geblockten Verkehr per Quick-Action freischalten. Administratoren sehen und verwalten alles.
Kein SSH vom Server zu den Hosts, keine zentral hinterlegten Host-Zugangsdaten. Stattdessen verbindet sich ein Agent von jedem Host aus nach außen zum Server.
Frühe Alpha. Im eigenen Netz im Einsatz, aber weder im Funktionsumfang noch in den Schnittstellen festgelegt. Solange die Hauptversion 0 ist, kann sich in jeder Nebenversion alles ändern — auch das Datenbankschema und das Agent-Protokoll. Siehe CHANGELOG.md.
Architektur
flowchart TB
browser["Browser<br/>OIDC-Sitzung"]
server["nfthub-server<br/>REST-API + WebSocket<br/>Regelrenderer<br/>Weboberfläche (eingebettet)"]
agent1["nfthub-agent<br/>pve01"]
agent2["nfthub-agent<br/>docker01"]
agent3["nfthub-agent<br/>mail01"]
tab1["table inet nfthub<br/>NFLOG → Ereignisse"]
tab2["table inet nfthub<br/>NFLOG → Ereignisse"]
tab3["table inet nfthub<br/>NFLOG → Ereignisse"]
browser -- HTTPS --> server
agent1 -- "TLS, vom Host aufgebaut" --> server
agent2 --> server
agent3 --> server
agent1 --- tab1
agent2 --- tab2
agent3 --- tab3
Die Pfeile zeigen die Richtung des Verbindungsaufbaus, und sie ist bewusst so gewählt: Der Agent verbindet sich nach außen zum Server. Kein Host braucht einen eingehenden Port für die Verwaltung, und der Server hält keine Zugangsdaten zu den Hosts vor. Über dieselbe Verbindung fließen Ereignisse nach oben und Regeländerungen nach unten.
Drei Bestandteile in einem Repository:
server/— Go, ein einzelnes Binary. REST-API, OIDC-Anmeldung, Agent-Anbindung samt Freigabe, Regelrenderer. Die Weboberfläche und die Agent-Binaries sind pergo:embedeingebettet; es gibt nichts getrennt auszuliefern.agent/— Go, statisch gelinkt, ohne cgo. Läuft als Dienst auf jedem Host, liest Firewall-Ereignisse über NFLOG und wendet Regeln an.web/— React 19 mit TypeScript, Vite, TailwindCSS v4.
Wie ein Host dazukommt
Hosts werden nicht vorab angelegt. Sie entstehen dadurch, dass sich ein Agent meldet:
-
In der Oberfläche unter Hosts → Host hinzufügen stehen das Instanz-Token und — bei eigenem TLS — der Agent-TLS-Pin. Das Instanz-Token ist dauerhaft und mehrfach nutzbar; es meldet beliebig viele Hosts an.
-
Auf dem Host läuft das Einrichtungsskript:
curl -fsSL https://nfthub.example.org/download/install.sh \ | sh -s -- --server wss://nfthub.example.org:8443/agent/connect --token TOKENEs erkennt Debian/Ubuntu, die RHEL-Familie und Alpine, dazu systemd und OpenRC, wählt das passende Binary, prüft die Prüfsumme, legt Dienst und Konfiguration an und startet ihn. Wer die Schritte einzeln gehen will, findet sie im selben Dialog.
-
Der Agent tauscht das Enrollment-Token gegen ein dauerhaftes, host-gebundenes Token, das serverseitig nur als Hash liegt.
-
Der neue Host erscheint als ausstehend und bekommt erst nach der Freigabe durch einen Administrator Regeln. Bis dahin liefert er nichts und empfängt nichts.
Danach hält der Agent eine ausgehende TLS-Verbindung offen, sendet alle 30 Sekunden einen Heartbeat und empfängt darüber Regeländerungen. Fällt die Verbindung aus, wendet er beim nächsten Start den zuletzt bestätigten Regelstand aus seinem Zustand an — der Host steht also nie ungefiltert da, nur weil der Server gerade nicht erreichbar ist.
Entfernen geht denselben Weg zurück: install.sh --uninstall hält den Dienst an,
löscht die eigene nft-Table und räumt Binary, Konfiguration und Zustand ab.
Regeln
Regeln entstehen in der Oberfläche, nicht in einer Datei. Sie tragen einen
Geltungsbereich — Instanz, Owner oder einzelner Host — und werden in dieser
Reihenfolge zusammengeführt; das Speziellere gewinnt. Aus dem Ergebnis rendert
der Server eine vollständige table inet nfthub und schickt sie an den Agent,
der sie mit nft -f atomar anwendet und bei einem Fehler zurückrollt.
Zwei Betriebsarten je Host:
- Monitor — es wird nichts verworfen, nur protokolliert. Was eine Regel blockieren würde, erscheint als Ereignis. Der Weg, ein Regelwerk gefahrlos einzufahren.
- Enforce — die Regeln greifen.
Unantastbar sind zwei Dinge, die keine Regel überschreiben kann: die Management-Ports (Vorgabe 22) und der Weg des Agents zum Server. Steht die Grundregel auf „ausgehend alles verwerfen", setzt der Renderer die nötigen Ausnahmen selbst an den Anfang der Kette — inklusive Namensauflösung, ohne die ein Agent seinen Server nach einem Neustart nicht wiederfände. Ist der Weg zum Server nicht bekannt, verweigert er die Konfiguration lieber, als den Host abzuschneiden.
Geblockter Verkehr lässt sich direkt an der Ereigniszeile freischalten (Quick-Action): Umfang, Dauer und Geltungsbereich wählen, fertig. Die Rückmeldung nennt den tatsächlichen Rollout-Ausgang und bietet „Rückgängig" an.
Voraussetzungen
- Go ≥ 1.25 und Node ≥ 24 zum Bauen
- Ein OIDC-Provider (entwickelt und getestet gegen Authentik)
- Optional PostgreSQL ≥ 18; ohne Angabe wird SQLite verwendet
- Auf den Hosts:
nftundconntrack— das Einrichtungsskript installiert beides
Schnellstart
make build # Frontend bauen, einbetten, Binaries erzeugen
./dist/nfthub-server
Beim ersten Start läuft der Server im Einrichtungsmodus und gibt ein Setup-Token aus:
────────────────────────────────────────────────────────────────────────
nfthub läuft im Einrichtungsmodus.
Oberfläche: http://localhost:8080/setup
Setup-Token: BXGoqfyFD6Wj0B7v4o4nXDWZxryNNYwT
────────────────────────────────────────────────────────────────────────
Das Token gilt nur für diesen Prozess. Der Wizard führt durch fünf Schritte:
- Datenbank — SQLite (eine Datei) oder PostgreSQL (DSN), auf Wunsch mit mTLS. Die Migrationen laufen automatisch; ein Wechsel ist ohne Neustart möglich.
- Anmeldung — externe Basis-URL, Issuer, Client-ID und -Secret, Claims und Bootstrap-Admin-Gruppe. Die einzutragende Redirect-URI steht zum Kopieren bereit.
- Basis-Policy — Management-Ports und die Vorgabe für ausgehenden Verkehr.
- Benachrichtigung — SMTP für die Meldung, dass ein Host auf Freigabe wartet. Überspringbar.
- Abschluss — Konfiguration festschreiben, Einrichtungsmodus sperren.
Für die vollständige Inbetriebnahme — Server mit Docker oder systemd, eigenes TLS, Reverse Proxy, Agent-Verteilung per Ansible — siehe docs/deployment.md. Ergänzend: Authentik einrichten.
Entwicklung
make dev # Server auf :8080 plus Vite auf :5173 mit Hot Reload
make dev-seed # dasselbe mit erzeugten Fake-Ereignissen, ohne echte Hosts
make test # Go- und Frontend-Tests
make check # Formatierung, go vet, Typecheck, Tests
make test-postgres # dieselben Tests gegen eine echte PostgreSQL-Instanz
make test-postgres erwartet eine erreichbare Datenbank
(PG_DSN=postgres://… überschreibt das Ziel) und legt für jeden Test ein eigenes
Schema an, das danach wieder verschwindet. Ohne NFTHUB_TEST_PG_DSN laufen alle
Tests gegen SQLite. Beides zu fahren lohnt sich: Der PostgreSQL-Lauf hat
mehrfach Fehler aufgedeckt, die unter SQLite nicht auftreten können — zuletzt
eine Ganzzahl in einer BOOLEAN-Spalte, die dort jede Host-Anmeldung scheitern
ließ.
Einrichtung ohne Wizard
Für Infrastructure-as-Code lässt sich alles per Umgebung vorgeben. Ist die Konfiguration vollständig, wird der Wizard übersprungen. Halb ausgefüllt startet der Server bewusst nicht — lieber ein klarer Abbruch als eine Instanz, die teilweise konfiguriert ins Netz geht.
NFTHUB_BASE_URL=https://nfthub.example.org
NFTHUB_LISTEN=:8080
NFTHUB_DATA_DIR=/var/lib/nfthub
NFTHUB_MASTER_KEY_FILE=/run/secrets/nfthub-master-key
NFTHUB_TRUSTED_PROXIES=10.0.0.0/8
NFTHUB_DB_DRIVER=postgres
NFTHUB_DB_DSN=postgres://nfthub@db.example.org:5432/nfthub
NFTHUB_OIDC_ISSUER=https://auth.example.org/application/o/nfthub/
NFTHUB_OIDC_CLIENT_ID=…
NFTHUB_OIDC_CLIENT_SECRET=…
NFTHUB_OIDC_ADMIN_GROUPS=nfthub-admins
NFTHUB_OIDC_USERNAME_CLAIM=preferred_username
NFTHUB_OIDC_GROUPS_CLAIM=groups
NFTHUB_MGMT_TCP_PORTS=22
Alternativ nimmt NFTHUB_CONFIG=/etc/nfthub/nfthub.env dieselben Schlüssel aus
einer Datei entgegen; echte Umgebungsvariablen haben Vorrang.
Einen Master-Key erzeugen:
./dist/nfthub-server -master-key
Wird keiner angegeben, legt der Server beim ersten Start selbst einen unter
$NFTHUB_DATA_DIR/master.key an und weist deutlich darauf hin. Geht diese
Datei verloren, sind alle verschlüsselt gespeicherten Secrets unlesbar.
Sicherheitsmodell
Owner-Trennung wird serverseitig erzwungen, nicht im Frontend. Jede Abfrage, die Hosts, Ereignisse, Regeln oder Audit-Einträge liest oder verändert, nimmt einen Zugriffsbereich entgegen und baut die Einschränkung selbst in das SQL ein. Es gibt keine Variante ohne Zugriffsbereich, die man versehentlich verwenden könnte. Zugriffe auf fremde Objekte werden mit „nicht gefunden" beantwortet — mit derselben Antwort wie für nicht existierende Objekte, damit deren Existenz nicht durchsickert. Tests prüfen genau diese Grenze.
Anmeldung ausschließlich über OIDC, mit PKCE (S256), Nonce-Prüfung und einem
state, der genau einmal eingelöst werden kann. Die Rolle wird bei jeder
Anmeldung aus dem Gruppen-Claim neu bestimmt.
Sitzungen liegen serverseitig und sind damit widerrufbar. In der Datenbank
steht nur der SHA-256 des Cookie-Werts — ein Datenbankleck liefert keine
verwendbaren Sitzungen. Das Cookie ist HttpOnly und SameSite=Lax; das
Secure-Flag richtet sich nach dem Schema der konfigurierten Basis-URL.
Verändernde Anfragen brauchen zusätzlich das CSRF-Token der Sitzung.
Agent-Anmeldung: Ein neuer Host bleibt „ausstehend", bis ein Administrator ihn freigibt — der Besitz des Instanz-Tokens allein reicht nicht, um verwaltet zu werden. Das dauerhafte Agent-Token liegt serverseitig nur als Hash.
IP-Zugriffsbeschränkung wird bei jeder Anfrage geprüft, nicht nur bei der
Anmeldung — eine bestehende Sitzung soll einen Standortwechsel nicht überdauern.
X-Forwarded-For wird nur ausgewertet, wenn die direkte Gegenstelle in der Liste
vertrauenswürdiger Proxys steht; sonst zählt allein die Verbindungsadresse.
Aussperrschutz auf zwei Ebenen: Der letzte aktive Administrator lässt sich weder herabstufen noch sperren. Und der Regelrenderer verweigert Konfigurationen, die dem Agent den Weg zum Server abschneiden würden.
Secrets at rest: OIDC-Client-Secret und SMTP-Passwort werden mit AES-256-GCM verschlüsselt gespeichert. Der Master-Key kommt aus der Umgebung, aus einer Datei oder wird beim ersten Start erzeugt.
Content-Security-Policy ohne externe Quellen. Schriften sind lokal gebündelt,
es gibt keine CDN-Abhängigkeit. script-src kommt ohne unsafe-inline aus: Der
Server berechnet beim Start den SHA-256 des einen Inline-Skripts aus der
eingebetteten index.html und nimmt genau diesen Hash auf.
Der Server läuft unprivilegiert. Nur der Agent braucht Rechte auf dem Host, und er führt niemals Nutzereingaben als Shell-Befehl aus.
Konfigurationsschlüssel
| Variable | Vorgabe | Bedeutung |
|---|---|---|
NFTHUB_LISTEN |
:8080 |
Adresse des HTTP-Servers |
NFTHUB_DATA_DIR |
./data |
SQLite-Datei, Master-Key, Zustand |
NFTHUB_BASE_URL |
— | Externe Adresse; Grundlage für Redirect-URI und Cookies |
NFTHUB_TRUSTED_PROXIES |
leer | CIDRs, deren X-Forwarded-For ausgewertet wird |
NFTHUB_DB_DRIVER |
sqlite |
sqlite oder postgres |
NFTHUB_DB_DSN |
Datei im Datenverzeichnis | Verbindungszeichenfolge |
NFTHUB_DB_SSL* |
— | sslmode und Zertifikate für PostgreSQL-mTLS |
NFTHUB_DB_TLS_DIR |
/etc/nfthub/db-tls |
Wird nach vorbereiteten DB-Zertifikaten abgesucht |
NFTHUB_MASTER_KEY / _FILE |
wird erzeugt | Schlüssel für Secrets at rest |
NFTHUB_OIDC_* |
— | Issuer, Client, Claims, Admin-Gruppen |
NFTHUB_TLS_MODE |
leer | file, selfsigned oder acme — eigenes HTTPS ohne Proxy |
NFTHUB_TLS_LISTEN |
— | Adresse des HTTPS-Servers, etwa :8443 |
NFTHUB_MGMT_TCP_PORTS |
22 |
Management-Ports der Basis-Policy |
NFTHUB_SETUP_TOKEN_DISABLED |
false |
Setup-Token abschalten |
NFTHUB_LOG_LEVEL |
info |
debug, info, warn, error |
NFTHUB_DEV |
false |
Frontend vom Vite-Server beziehen |
Die vollständige Liste mit allen TLS- und SMTP-Schlüsseln steht kommentiert in deploy/docker/.env.example.
Betrieb hinter einem Reverse-Proxy
nfthub kann TLS selbst terminieren (NFTHUB_TLS_MODE) oder es einem Proxy
überlassen. Bei einem Proxy sind zwei Punkte wichtig:
NFTHUB_BASE_URLmuss die öffentliche Adresse sein. Sie bestimmt die Redirect-URI und entscheidet, ob Sitzungscookies dasSecure-Flag tragen.NFTHUB_TRUSTED_PROXIESmuss die Adresse des Proxys enthalten. Ohne diesen Eintrag sieht nfthub bei jedem Aufruf die Proxy-Adresse als Client-IP — die IP-Beschränkung liefe ins Leere, und das Audit-Log wäre wertlos.
Die Agents gehen üblicherweise am Proxy vorbei direkt auf den TLS-Port, damit zwischen Host und Server nichts vermittelt. Bei selbstsigniertem Zertifikat sichert der Agent-TLS-Pin diese Verbindung.
Projektstruktur
server/
cmd/nfthub-server/ Einstiegspunkt
internal/
agenthub/ Agent-Verbindungen, Handshake, Heartbeat
api/ Router, Handler, Auslieferung von Frontend und Agent
app/ Laufzeitverdrahtung, austauschbare Datenbank
auth/ OIDC, Sitzungen, CSRF, IP-Beschränkung
config/ Konfiguration aus Umgebung und Datei
db/ SQLite/PostgreSQL, Migrationen je Dialekt
dbtest/ Testdatenbanken für beide Dialekte
demo/ erzeugte Ereignisse für die Oberflächenentwicklung
live/ Verteilung der Ereignisse an die Browser
notify/ SMTP-Benachrichtigung
rollout/ Ausrollen der gerenderten Regeln
rules/ Regelmodell, Merge-Reihenfolge, nft-Renderer
secret/ Verschlüsselung der Secrets at rest
setup/ First-Run-Wizard
store/ Datenmodelle, Abfragen mit Owner-Scoping
tlsserve/ eigenes HTTPS: file, selfsigned, ACME
agent/
cmd/nfthub-agent/ Einstiegspunkt, -check, -version, -uninstall
internal/
agent/ Verdrahtung, Zustand über Neustarts hinweg
detect/ Firewall-Stack und Fähigkeiten des Hosts
events/ NFLOG über Netlink, /dev/kmsg als Rückfall
nft/ Anwenden mit Bestätigung und Rücknahme
selfupdate/ Selbstaktualisierung mit Rückfall
transport/ ausgehende TLS-Verbindung zum Server
uninstall/ restloses Entfernen vom Host
web/ React-Oberfläche
design/ Design-Referenz und destillierte Design-Tokens
deploy/
install.sh Einrichtungsskript für Debian, RHEL und Alpine
systemd/ Units für Server und Agent
openrc/ Init-Skript und conf.d für Alpine
docker/ Dockerfile, compose (SQLite und PostgreSQL), .env
ansible/ Rolle und Beispiel-Playbook für das Agent-Rollout
docs/ Inbetriebnahme, Authentik
Getroffene Annahmen und ihre Begründung stehen in DECISIONS.md.
Lizenz
Noch nicht festgelegt.