opensnitch-cli (C) — OpenSnitch komplett in der Konsole
opensnitch-cli ersetzt die Qt-GUI von OpenSnitch
durch ein einzelnes C-Programm: einen headless gRPC-UI-Dienst (den der unveränderte
opensnitchd genau wie die GUI anspricht), eine Befehlszeile für alles, was die GUI kann, und
ein Vollbild-Text-UI (ncurses) mit Tabs für Events, Regeln, Firewall, Nodes, Alerts, Logs und
Einstellungen — inklusive Dialog für Verbindungsanfragen.
Der Daemon (opensnitchd, Go) bleibt unverändert; er ist im OpenSnitch-Design der gRPC-Client,
die GUI (bzw. hier opensnitch-cli serve) ist der Server.
┌──────────────┐ gRPC (unix socket / TCP / TLS) ┌───────────────────────────┐
│ opensnitchd │ ─────────────────────────────────▶ │ opensnitch-cli serve │
│ (Go, root) │ ◀───────────────────────────────── │ DB (sqlite), Regeln, ... │
└──────────────┘ └─────────────┬─────────────┘
control socket │ (JSON lines)
┌───────────────────────────────────────────┼──────────────┐
▼ ▼ ▼ ▼
opensnitch-cli tui opensnitch-cli prompt opensnitch-cli rules/fw/events/...
Bauen
Abhängigkeiten (Debian/Ubuntu):
sudo apt install gcc make pkg-config libnghttp2-dev libprotobuf-c-dev protobuf-c-compiler \
libsqlite3-dev libssl-dev libncurses-dev
make
sudo make install # /usr/local/bin/opensnitch-cli (PREFIX=... änderbar)
Fedora: libnghttp2-devel protobuf-c-devel protobuf-c-compiler sqlite-devel openssl-devel ncurses-devel pkgconf-pkg-config.
Arch: libnghttp2 protobuf-c sqlite openssl ncurses.
Die Proto-Datei (proto/ui.proto, unverändert aus OpenSnitch) ist bereits mit protoc-c übersetzt
(make proto regeneriert sie); cJSON (MIT) ist unter third_party/ mitgeliefert.
make install installiert Binary, Manpage (man opensnitch-cli), Bash-Vervollständigung und die
systemd-Benutzer-Unit (PREFIX, DESTDIR werden beachtet).
RPM (Fedora, RHEL, Rocky, Alma)
sudo dnf install rpm-build rpmdevtools gcc make pkgconf-pkg-config libnghttp2-devel protobuf-c-devel \
sqlite-devel openssl-devel ncurses-devel systemd-rpm-macros # EL9: vorher CRB freischalten
rpmdev-setuptree && make dist && cp opensnitch-cli-1.9.1.tar.gz ~/rpmbuild/SOURCES/ && cp packaging/opensnitch-cli.spec ~/rpmbuild/SPECS/
rpmbuild -ba ~/rpmbuild/SPECS/opensnitch-cli.spec
sudo dnf install ~/rpmbuild/RPMS/x86_64/opensnitch-cli-1.9.1-1.*.rpm
systemctl --user enable --now opensnitch-cli
rpmbuild -bs erzeugt daraus auch ein Source-RPM (opensnitch-cli-1.9.1-1.*.src.rpm), das sich mit rpmbuild --rebuild oder mock überall bauen lässt.
Details (CRB/EPEL, System-Unit, Laufzeitabhängigkeiten) in packaging/README.md.
Schnellstart
opensnitch-cli tui # Text-UI; startet `serve` automatisch im Hintergrund, falls noch keiner läuft
Das entspricht dem Start von opensnitch-ui. Alternativ getrennt:
opensnitch-cli serve # Dienst im Vordergrund; auf einem Terminal fragt er Verbindungen direkt hier ab
opensnitch-cli serve --background # headless (Log: ~/.config/opensnitch-cli/serve.log), oder als systemd-Unit (siehe unten)
opensnitch-cli prompt # nur die Pop-ups beantworten (startet serve ebenfalls bei Bedarf; --no-autostart)
opensnitch-cli stop # Dienst beenden
Ein im Hintergrund gestarteter Dienst läuft nach dem Beenden des TUI weiter. Verbindungsanfragen sieht dann,
wer ein tui oder prompt offen hat – und auf dem Desktop zusätzlich eine Benachrichtigung mit den Buttons
Allow/Deny/Reject (settings set notifications.type desktop; braucht notify-send aus libnotify ≥ 0.7.10 und
einen Notification-Dienst, der Aktionen kann: GNOME, KDE, dunst, mako …). Ein Klick beantwortet die Anfrage mit
Standardziel und -dauer; ohne Antwort greift nach prompts.timeout Sekunden die Standardaktion (deny).
prompts.timeout 0 heißt „warten, solange der Daemon lässt“ (≈110 s; der Daemon selbst gibt nach 120 s auf).
Beachten: Anfragen werden wie in der GUI eine nach der anderen gezeigt. Bei einem Schwall unbeantworteter
Anfragen wartet die letzte bis zu (N−1) × Timeout, bis sie überhaupt erscheint – mit 0 also bis zu 110 s je
Anfrage davor (der Daemon hat höchstens 6 gleichzeitig offen). Wer „langsame Pop-ups“ sieht, sollte deshalb
beim Standard von 30 s bleiben; Anfragen, die der Daemon inzwischen aufgegeben hat, werden sofort übersprungen.
Zieht der Daemon eine Anfrage zurück (Timeout auf seiner Seite, Neustart), verschwindet sie auch aus TUI/Prompt.
Ohne laufendes serve gilt das DefaultAction des Daemons (siehe Sicherheit).
Der Daemon muss auf denselben Socket zeigen wie serve (Standard unix:///tmp/osui.sock, siehe
Server.Address in /etc/opensnitchd/default-config.json). Für TCP/TLS:
opensnitch-cli settings set server.address '[::]:50051'
opensnitch-cli settings set server.auth.type tls-simple # oder tls-mutual
opensnitch-cli settings set server.auth.cert /etc/opensnitch-cli/server.crt
opensnitch-cli settings set server.auth.certkey /etc/opensnitch-cli/server.key
Welcher Socket ist wo? (Fehlersuche)
Es gibt zwei Sockets:
- gRPC-Socket Daemon ↔ serve – der Daemon verbindet sich zum serve. Der Pfad steht beim Daemon in
/etc/opensnitchd/default-config.jsonunterServer.Address(Standardunix:///tmp/osui.sock) und bei serve insettings show server.address. Beide müssen übereinstimmen. Prüfen:Die Socket-Datei wird mit Rechten 0660 angelegt; läuftopensnitch-cli settings show server.address # was serve öffnet sudo grep -A2 '"Server"' /etc/opensnitchd/default-config.json # was der Daemon sucht ss -xlp | grep osui.sock # lauscht serve? (bzw. ss -tlnp für TCP) sudo journalctl -u opensnitch -n 20 # der Daemon meldet "connected to UI"/"UI not connected"opensnitchdals root und serve als normaler Benutzer, passt das (root darf alles). Umgekehrt (serve als root, Daemon ohne root) muss der Daemon-Benutzer in die Gruppe des Socket-Besitzers. - Control-Socket CLI/TUI ↔ serve –
$XDG_RUNTIME_DIR/opensnitch-cli-<uid>.sock(z. B./run/user/1000/opensnitch-cli-1000.sock); anzeigen mitopensnitch-cli statusbzw. beim Start von serve ([serve] control socket: …). Abweichender Pfad:--control-socket PATHbei serve und den Clients odersettings set server.control_socket PATH. Die Fehlermeldung „serve is not running (socket …)“ nennt den Pfad, unter dem der Client gesucht hat.
Keine Pop-ups? → opensnitch-cli doctor
doctor prüft alles, was zwischen einer Verbindung und dem Pop-up liegen kann, und sagt, was zu tun ist:
läuft opensnitchd überhaupt, stimmt sein Server.Address mit server.address überein, existiert der Socket
mit den richtigen Rechten, ist der Daemon mit serve verbunden, ist das Abfangen an, gibt es eine Catch-all-Regel
(die alles erlaubt und deshalb nie fragt), steht DefaultAction auf deny, sind Pop-ups aktiv und hört jemand
zu (tui/prompt/serve im Terminal). Als root zeigt es zusätzlich die relevanten Zeilen des Daemon-Logs.
serve selbst meldet im Terminal, wenn ein Daemon verbindet oder wegbricht, und warnt nach 5 s, wenn keiner kommt.
Bekannter, behobener Fehler (Build vor dem 5.9.2026): der Daemon verband sich, aber jeder RPC wurde mit
PROTOCOL_ERROR abgewiesen, weil grpc-go den Socket-Pfad als HTTP/2-:authority schickt und nghttp2 das nach
HTTP/1-Regeln verwarf; im Daemon-Log stand dann Subscribing to GUI ... RST_STREAM ... PROTOCOL_ERROR. Wer das
sieht, hat einen alten Build (opensnitch-cli --version).
Die häufigsten Ursachen: der Daemon ist nicht gestartet (systemctl status opensnitchd); die Socket-Adressen
passen nicht; die GUI war früher in Benutzung und hat eine Regel wie „allow always *“ hinterlassen; das Abfangen
wurde abgeschaltet; die Verbindung kam von einem Prozess, für den schon eine Regel gilt.
Sicherheit: Socket-Rechte und Verhalten ohne verbundenes UI
Socket-Rechte (opensnitch-cli status zeigt beide Sockets mit Modus und Besitzer):
| Socket | Standardpfad | Rechte | Wer darf verbinden |
|---|---|---|---|
| gRPC (Daemon → serve) | /tmp/osui.sock |
0660, Besitzer = serve-Benutzer |
der Daemon (root darf immer) und Prozesse des serve-Benutzers/-Gruppe. Bestehende fremde Socket-Datei wird nicht übernommen (serve bricht ab), eine lebende serve-Instanz wird nicht verdrängt. |
| Control (CLI/TUI → serve) | $XDG_RUNTIME_DIR/opensnitch-cli-<uid>.sock |
0600 in einem 0700-Verzeichnis |
nur derselbe Benutzer. Läuft serve als root (System-Unit), bedient nur root Regeln/Firewall/Konfiguration. |
| TCP-Listener | z. B. [::]:50051 |
keine Dateirechte | alle, die den Port erreichen – dort server.auth.type tls-mutual mit Client-Zertifikaten verwenden (der Daemon unterstützt das in Server.Authentication). |
Wer sich mit dem gRPC-Socket verbinden kann, kann sich als Daemon ausgeben (Ereignisse/Alerts einspeisen, Regeln empfangen); wer den Control-Socket erreicht, verwaltet die Regeln. Beides ist mit den Standardpfaden auf den Besitzer beschränkt. Es wird keine Authentifizierung über den Unix-Socket hinaus gemacht – genau wie bei der GUI.
Ohne verbundenes UI arbeitet der Daemon nach seiner eigenen Konfiguration – und die ist ab Werk fail-open.
Vorhandene Regeln (dauerhafte aus /etc/opensnitchd/rules und temporäre im Speicher des Daemons) werden
weiter angewendet; für Verbindungen ohne passende Regel gilt DefaultAction aus
/etc/opensnitchd/default-config.json, und das ist in der mitgelieferten Konfiguration allow. Das gilt vor dem
Start von serve, nach opensnitch-cli stop, bei einem Absturz und bei Socket-Problemen (falscher Pfad, Rechte).
Ist serve verbunden, überschreibt es – wie die GUI – die Standardaktion nur im Speicher des Daemons
(prompts.default_action, Standard deny); nach der Trennung gilt wieder die Datei. Außerdem bekommen bei
InterceptUnknown: false Verbindungen ohne identifizierbaren Prozess die DefaultAction ohne Nachfrage.
Deshalb erzwingt serve fail-closed (nodes.fail_closed, Standard an): wenige Sekunden nach dem Verbinden
eines Daemons, dessen DefaultAction noch allow ist, schickt serve ihm seine Konfiguration mit
DefaultAction: deny (nodes.fail_closed_action: deny oder reject). Der Daemon schreibt das in
/etc/opensnitchd/default-config.json – die Einstellung gilt damit dauerhaft, auch wenn serve nicht läuft,
und Verbindungen ohne passende Regel werden geblockt. Mit nodes.intercept_unknown true wird zusätzlich
InterceptUnknown eingeschaltet (Verbindungen ohne identifizierbaren Prozess werden abgefragt statt nach
Standardaktion behandelt). Dasselbe von Hand: opensnitch-cli config set DefaultAction deny oder im TUI im
Nodes-Reiter d. opensnitch-cli status und die Statuszeile des TUI warnen, solange ein verbundener Daemon
noch auf allow steht; ausschalten lässt sich die Durchsetzung mit settings set nodes.fail_closed false. Zwei weitere Grenzfälle: Der Daemon wartet auf eine Antwort des UI
höchstens 120 s (dann seine Standardaktion) – serve antwortet nach prompts.timeout (30 s) mit der eigenen
Standardaktion, also vorher. Und opensnitch-cli interception off schaltet das Abfangen komplett ab
(alles erlaubt), bis es wieder eingeschaltet wird; mit interception.persist_state merkt sich serve das.
Das Text-UI (opensnitch-cli tui)
Das TUI ist das GUI-Hauptfenster („Network Statistics“) als Textoberfläche – gleiche Aufteilung, gleiche Reiter, gleiche Dialoge, nur mit Tastatur bedient:
OpenSnitch Network Statistics Status: ● Connected (1 node) v1.9.1
Node: unix:/local (host) Daemon: 1.9.0 Uptime: 2:13:05 Rules: 12 Connections: 3401 Dropped: 7 Interception: ON
I Stop interception F Firewall P Preferences N New rule E Edit D Delete S Save CSV H Help Q Quit
Events │ Nodes │ Rules (12) │ Hosts │ Applications │ Addresses │ Ports │ Users │ Alerts
Time Action Destination Proto Process Args Rule
2026-09-04 20:59:08 allow example.com:443 tcp /usr/bin/curl curl https://… allow-curl
...
╭─ OpenSnitch ────────────────────────────────────────────────────────────────╮
│ curl │
│ curl is connecting to example.com on TCP port 443 │
│ Source IP: 192.168.1.10:51234 Destination: 93.184.216.34:443 (example.com)│
│ Process ID: 4242 User ID: 1000 (user) Checksum: d41d8… │
│ Path: /usr/bin/curl Command: curl https://example.com │
│ What to do? (↑↓) < from this executable: /usr/bin/curl > 1/13 │
│ Advanced: [i] Dst IP [ ] [p] Dst port [ ] [u] User ID [ ] [s] Checksum [ ]
│ Duration: (←→) < until reboot > │
│ Allow (a) Deny (d) 23s Reject (r) Enter=default ?=details│
╰─────────────────────────────────────────────────────────────────────────────╯
- Statuszeile und Buttons wie in der GUI: Node, Daemon-Version, Uptime, Regeln, Verbindungen, Dropped;
IInterception starten/stoppen,FFirewall-Dialog,PPreferences,N/E/DRegel neu/bearbeiten/löschen,STabelle als CSV speichern,HHilfe. Suche/und Limitl(50 … alle) wie die Suchleiste/Limit-Box der GUI. - Reiter
1–9oderTab: Events (live, mit Node-Spalte bei mehreren Daemons), Nodes (alle je gesehenen Nodes wie in der GUI-Tabelle), Rules (links der Regel-Baum All/Allow/Deny/Reject/Temporary/Permanent,←→), Hosts, Applications, Addresses, Ports, Users (What/Hits/Last seen –Enterzeigt die Events des Eintrags,Esczurück), Alerts. - Vom Event zur Regel: Klick auf den Namen in der Spalte „Rule“, Doppelklick auf ein Event oder
röffnet die zugehörige bestehende Regel zur Bearbeitung. Das funktioniert auch in den Detailansichten von Hosts, Applications usw.; die Regel wird auf dem Node des Events bearbeitet. Für gelöschte Regeln und Events ohne Regel erscheint eine Meldung.Enterbleibt für Prozessdetails,Eerstellt eine neue Regel. - Host/IP/CIDR auflösen:
F3auf einem Event, Host oder einer Adresse zeigt die Auflösung. Im Regel- und Verbindungsdialog zuerst das Host- oder IP/Netz-Feld auswählen, dannF3: Hostname → IPv4/IPv6, IP → Hostname, CIDR → normalisiertes Netz sowie erste/letzte Adresse. DNS läuft im Hintergrund auf dem Rechner der TUI;Escbricht die Anzeige ab, nach 8 Sekunden endet das Warten. Regeln werden dadurch nicht umgeschrieben. Ein Netz hat keinen einzelnen DNS-Namen. - Mausbedienung: Formularfelder und Auswahllisten anklicken, Checkboxen umschalten, Save/Cancel sowie Allow/Deny/Reject direkt anklicken; das Mausrad scrollt. Auch Firewall-Aktionen und Yes/No-Rückfragen sind anklickbar. Die Tastaturbedienung bleibt verfügbar, auch bei 80 Spalten.
- Pop-up = Regel-Editor. Eine Verbindungsanfrage öffnet sich als dasselbe Formular wie „Regel bearbeiten“,
vorausgefüllt mit den Fakten der Verbindung (Prozess, Kommandozeile, User, PID, IP, Host, Port, Protokoll,
Checksum, Prozessbaum) und der Standardbedingung angehakt. Sobald das Formular offen ist, wird die Anfrage für
die ganze Zeit gehalten, die der Daemon erlaubt (110 s) – kein Rennen gegen die 30 s. Und das Formular schließt
sich nie von selbst: Läuft die Daemon-Frist während des Bearbeitens ab (der Daemon entscheidet die eine
Verbindung dann nach seiner Standardaktion, das lässt sich von außen nicht ändern), bleibt das Formular mit
allen Eingaben offen, und „Apply“ legt die Regel für die nächsten Verbindungen an. Nichts geht verloren.
a/d/rantworten sofort mit den Feldern, wie sie gerade stehen;s/Applymit dem Action-Feld;Spacehakt Bedingungen an oder ab,Enterändert einen Wert,←→wechselt Action/Dauer/Protokoll und das Preset „Domain / Netz / Muster“ (die Ziele des Original-Pop-ups:*.domain, /24-/16-/8-Netze, IP-Muster, AppImage/Snap).Escfragt: warten (später mitpwieder öffnen; die Kopfzeile zeigt „pending (p)“), Standard sofort anwenden, oder zurück. - Regel-Editor (
N,E, oderEauf einem Event = Regel aus Event): Formular mit Name, Beschreibung, Enabled, Priority rule, Don't log, Action, Duration und den Bedingungen der GUI (Pfad, Kommandozeile, User ID, PID, Interface, IP/Netz/Alias, Host, Port80,443oder1024-65535, Protokoll, Listen-Verzeichnisse, Checksum). Reguläre Ausdrücke werden wie in der GUI automatisch erkannt. Unveränderte bestehende Bedingungen behalten ihren Typ, ihre Groß-/Kleinschreibungseinstellung und auch mehrfach vorhandene Operanden. Einzelne IPs bleibendest.ip; CIDR-Netze verwendendest.network. IPv4-Präfixe sind auf 0–32, IPv6-Präfixe auf 0–128 begrenzt. - Firewall-Dialog (
F): Firewall an/aus (e), Inbound-/Outbound-Policy (i/o) – wie in der GUI setzt „drop“ die Policy und legt die Regeln an, die established/related- und Loopback-Verkehr weiter durchlassen (dieselben Regeln und UUIDs wie die GUI-Profile); „accept“ entfernt sie wieder.Pauf einer anderen Chain ändert nur die Policy (mit Rückfrage). Profile (p), Regeln mitN/E/D, Formular mit Chain, Expressions, Verdict, Parametern. Im Firewall-Dialog öffnetaeinen Port (mit optionalemfrom IP/CIDR),Ablockiert einen Port; Deny-/Reject-Regeln werden automatisch vor die Allow-Regeln einsortiert (nftables wertet den ersten Treffer), Loopback und established/related bleiben immer erlaubt. - Preferences (
P): dieselben Gruppen wie im GUI-Dialog – Pop-ups (Standardaktion/-dauer/-ziel, Timeout, Advanced-Vorgaben), UI (Theme, Limit, Benachrichtigungen), Rules, Nodes, Database, Server. - Nodes:
EnterDetails,cöffnet die Daemon-Konfiguration (JSON) im$EDITORund schickt sie an den Daemon (wie der Konfig-Editor der GUI),iInterception. - Events:
EnterProzessdetails live vom Daemon (Status, Descriptors, Environment, Maps, Stack – der Prozessdialog der GUI),SpaceRoh-Event. - Formulare:
↑↓bewegen,SpaceHäkchen,EnterWert eingeben/auswählen,←→Auswahl ändern,s/F2speichern,Escabbrechen.
Themes (Preferences › UI › Theme oder settings set ui.theme dark_blue, auch tui --theme NAME): die
qt-material-Farbschemata der GUI als Terminalfarben – default (Terminalfarben), dark_blue, dark_teal,
dark_amber, dark_cyan, dark_purple, dark_red, dark_pink, dark_yellow, dark_lightgreen und die
light_*-Varianten (256-Farben-Terminal; auf 8-Farben-Terminals wird nur die Akzentfarbe übernommen).
Befehle und Unterbefehle sind Groß-/Kleinschreibung-unabhängig (STATUS, Rules LIST, TOP Hosts gehen alle). Maus wird unterstützt (Reiter, Buttons, Zeilen, Doppelklick = Enter, Pop-up-Buttons, Mausrad). Ctrl-C beendet
sauber und stellt das Terminal wieder her. Ohne UTF-8-Locale (z. B. LANG=C per SSH) wird auf C.UTF-8
ausgewichen, sonst mit ASCII-Zeichen gezeichnet (OPENSNITCH_CLI_ASCII=1 erzwingt das). Neu gezeichnet wird nur
bei Änderungen bzw. einmal pro Sekunde – auch über langsame Verbindungen brauchbar. Es können beliebig viele
tui/prompt-Instanzen gleichzeitig laufen (z. B. tmux + SSH); die erste Antwort gewinnt. Die Spalten passen
sich der Terminalbreite an (ab 80 Spalten brauchbar, ab 120 mit allen Spalten).
Befehlszeile
Alle Befehle haben --help; die meisten kennen --node NODE (mehrere Daemons), --all, --json, -y.
Verbindungsanfragen
opensnitch-cli prompt # interaktiv, blockt bis Ctrl-C
opensnitch-cli prompt --list # was wartet gerade?
opensnitch-cli prompt --answer "allow host always +uid"
Antwort-Grammatik: <a|allow|d|deny|r|reject> [ziel] [dauer] [+ip +port +uid +sum] [name=..],
Ziel als Nummer oder Schlüssel (exe cmd port ip host dom1.. net24 net16 net8 ipre1.. user pid appimage snap alias),
Dauer once 30s 5m 15m 30m 1h 12h restart always (GUI-Namen until reboot/forever gehen auch);
? = Details, skip = Standard sofort, list, quit.
Ohne Antwort greift nach prompts.timeout Sekunden der Standard (prompts.default_action usw.; 0 = so
lange warten, wie der Daemon erlaubt).
Regeln
opensnitch-cli rules list [--filter TEXT] [--enabled|--disabled] [--temporary|--permanent]
opensnitch-cli rules add deny always --op host~'^(|.*\.)ads\.example$' -d "kein Werbe-Tracking"
opensnitch-cli rules add allow 1h --op path=/usr/bin/curl --op port=443 --name curl-https
opensnitch-cli rules add allow always --op net=10.0.0.0/8 --op uid=1000 --precedence --nolog
opensnitch-cli rules edit curl-https --duration always --op path=/usr/bin/curl --op port:range=1024-65535
opensnitch-cli rules enable|disable|delete NAME rules show NAME
opensnitch-cli rules export ./backup | rules import ./backup
opensnitch-cli rules --dir /etc/opensnitchd/rules list|add|edit|delete ... # offline, ohne serve (Dateien direkt)
Bedingungen (--op, mehrere = UND-Liste): path= cmd= host= host~regexp ip= port= net= uid= pid= proto= md5= iface=,
port:range=a-b, domains=/pfad/liste (lists), process.env.NAME=wert, !host= (case-sensitive).
Events, Statistik, Logs
opensnitch-cli events -n 100 --action deny --process curl # aus der Datenbank
opensnitch-cli events -f # live
opensnitch-cli top hosts|procs|addrs|ports|users|rules|protocols|pids|cmdlines
opensnitch-cli stats opensnitch-cli alerts [--clear]
opensnitch-cli logs -f opensnitch-cli logs --daemon -n 200 # /var/log/opensnitchd.log
opensnitch-cli proc PID node-monitor netstat --proto tcp sockets
opensnitch-cli db info | purge --days 7 | vacuum | clean connections
Das serve-Log landet in server.log_file (im Hintergrund automatisch ~/.config/opensnitch-cli/serve.log),
die Datenbank wird mit settings set database.type file dauerhaft (database.file, Standard
~/.config/opensnitch-cli/events.db; GUI-Datenbankdateien sind kompatibel).
System-Firewall (nftables, wie der GUI-Firewall-Dialog)
UFW ablösen mit einem Befehl – fertige Policies (fw preset):
opensnitch-cli fw preset server --ports 22,80,443 # deny inbound, allow outbound, established/related bleibt, Ports offen
opensnitch-cli fw allow-port 51820 --proto udp # weiteren Port öffnen (WireGuard); --proto tcp,udp = zwei Regeln
opensnitch-cli fw allow-port 22 --from 10.0.0.0/8 # nur aus einem IPv4-Netz (wie UFW: allow from … to port 22)
opensnitch-cli fw deny-port 23 fw reject-port 3389 # blockieren (deny=drop, reject=aktiv ablehnen); stehen vor den allow-Regeln
opensnitch-cli fw delete-port 22 # entfernt alle Varianten dieses Ports (tcp und udp, alle Quellen)
opensnitch-cli fw preset lockdown # auch ausgehend dicht außer established/related + loopback
opensnitch-cli fw preset open # alles wieder erlauben (Preset zurücknehmen, auch forward wieder accept)
opensnitch-cli fw repair # fehlende Standard-Chains des Daemons neu anlegen (inkl. Interception-Queue)
opensnitch-cli fw preview input-drop # zeigt die EXAKTE system-fw.json, die gesendet würde (ohne anzuwenden)
opensnitch-cli fw preview server --ports 22,80 # zum Vergleichen/Debuggen auf dem Zielsystem
Wichtig: die Presets fassen die forward-Chain nicht an – OpenSnitch fängt weitergeleitete Verbindungen
(Container, VMs, Docker) über die Queue-Regel in mangle_forward ab; diese Chain auf drop zu zwingen bricht
gerouteten/ausgehenden Verkehr auf jedem Host, der routet. ufw-default = deny in / allow out / established+related + loopback (UFWs Standard); server = das plus SSH
(und --ports); desktop = ohne offene Ports (Regeln und Pop-ups erledigen den Rest); lockdown = auch
ausgehend dicht; open = alles erlauben (Input, Output und Forward wieder accept, Profil-Regeln weg). Profile und Presets legen fehlende Standard-Chains des Daemons vorher automatisch wieder an (auch die
Interception-Queue in mangle_forward/mangle_output), sodass eine geleerte oder unvollständige
system-fw.json nicht in einem kaputten Zustand endet; fw repair (bzw. R im Firewall-Dialog) macht nur das,
ohne die Policy zu ändern – eine neu angelegte filter_input/mangle_output hat dann die Daemon-Voreinstellung
(Policy accept, nur die Standardregeln), repair sagt das auch. Von Ports geöffnete Regeln sind Inbound-accept-Regeln, die vor der
Drop-Policy stehen. Im TUI: Firewall-Dialog (F) → p (Presets, fragt nach Ports) und a (Port öffnen).
Mehrere Protokolle in einer Port-Regel schreibt die CLI wie die GUI: fw add --expr 'tcp,udp dport 53' wird zu
meta l4proto tcp,udp dport 53, woraus der Daemon ein anonymes Protokoll-Set baut (meta l4proto { tcp, udp } th dport 53 in nftables). allow-port --proto tcp,udp legt weiterhin zwei getrennte, einzeln löschbare Regeln an.
Regeln ohne Ausdruck lehnt die CLI ab – der Daemon würde sie stillschweigend nicht anlegen, sie sähen aber im
fw show wie eine Catch-all-Regel aus. delete-port 22 entfernt nur Port 22, nicht 2200 oder 22-1024.
Reihenfolge in filter_input wie bei UFW (nftables nimmt den ersten Treffer): zuerst die Profil-Regeln (Loopback,
established/related – ein deny-port trennt also keine bestehende Sitzung), dann deny/reject, dann allow, dann der
Rest der Chain. Was der Daemon kann und was nicht (alles aus daemon/firewall/nftables abgeleitet und gegen
opensnitchd 1.8.0 geprüft): --from nimmt eine IPv4-Adresse oder ein IPv4-Netz; CIDR kennt der Daemon nicht, die
CLI übersetzt 10.0.0.0/8 in den Bereich 10.0.0.0-10.255.255.255 (auch bei fw add --expr 'ip saddr 10.0.0.0/8');
IPv6-Quellen werden abgelehnt (der Daemon vergleicht nur IPv4-Adressen). --proto tcp,udp erzeugt zwei Regeln,
weil nftables die Ausdrücke einer Regel UND-verknüpft und „tcp dport X und udp dport X" nie zutrifft. Die vom
Daemon mitgelieferte, deaktivierte Regel „Allow SSH server connections when input policy is DROP" bleibt unangetastet;
server legt seine eigene aktive Regel mit stabiler UUID (osn-port-tcp-22-any-accept) an. Der Outbound-Drop
(lockdown, fw policy output drop) enthält zusätzlich zum GUI-Profil eine Loopback-Regel (oifname lo), sonst
wären neue Verbindungen nach 127.0.0.1/::1 tot, sobald die Interception-Queue nicht davor steht (Interception aus,
Daemon weg); output accept entfernt sie mit. Weitergeleiteter Verkehr (net.ipv4.ip_forward, Container, VMs)
bleibt von allen Presets unberührt – wer routet und das nicht will: opensnitch-cli fw policy forward drop.
Zwei Eigenheiten des Daemons, die man kennen sollte: (1) Nach jeder Änderung an der System-Firewall lädt er sie
neu und kann dabei seine eigenen Interception-Regeln (die queue-Regeln in mangle_output/filter_input) vorübergehend
verlieren – beobachtet bei zwei Änderungen kurz hintereinander; sein Prüf-Thread baut sie beim nächsten Intervall
(FwOptions.MonitorInterval, Standard 15 s) wieder ein. Solange die Output-Policy auf drop steht (lockdown,
fw policy output drop), schlagen neue ausgehende Verbindungen in diesem Fenster fehl – bestehende laufen weiter.
Das ist Daemon-Verhalten, die GUI hat es genauso. (2) Fehler beim
Laden einzelner Regeln meldet der Daemon asynchron; sie können erst mit der Antwort auf den nächsten Befehl
ankommen. serve erkennt solche nachgereichten Meldungen daran, dass sie keine Regel der gerade gesendeten
Konfiguration nennen, und protokolliert sie nur (serve.log), statt den Befehl als gescheitert zu melden.
Firewall ausschalten (fw disable) und Chains löschen. Der Daemon fasst Chain-Policies nicht mehr an, sobald
die Firewall in der Konfiguration aus ist – er löscht nur die konfigurierten Regeln. Eine Chain, die gerade auf
drop steht (preset server, fw policy input drop), bliebe also ohne ihre Allow-Regeln stehen und würde alles
verwerfen; auf einem entfernten Rechner heißt das ausgesperrt. fw disable (und fw chain-delete einer Chain mit
drop-Policy, und e im Firewall-Dialog) setzt deshalb erst die betroffenen Policies auf accept, schickt das,
und schaltet dann ab – genau wie es die Qt-GUI macht. Im Log steht dann „resetting the drop policy …"; nach
fw enable sind die Policies auf accept, die gewünschte Policy also neu setzen (fw preset …).
Bekannter, behobener Fehler (Builds bis 49e30ea, 5.9.2026): die CLI nummerierte die Regeln einer Chain als
Position 0, 1, 2 … durch. Der Daemon reicht Position aber unverändert als nftables-Handle („füge vor Regel
mit diesem Handle ein") an den Kernel weiter, und solche Handles gibt es nicht – jede Regel außer der ersten
scheiterte mit rule not added (…) (Daemon-Log: netlink receive: no such file or directory). Nach
fw policy input drop oder fw preset server war deshalb nur die erste Regel aktiv, die established/related-Regel
fehlte, Antworten auf ausgehende Verbindungen wurden verworfen („Outbound kaputt"). Die Reihenfolge steckt allein
in der Reihenfolge der Regeln (so macht es auch die GUI); Position ist jetzt immer 0, pos in fw show ist der
Index. Eine mit einem alten Build angelegte system-fw.json heilt jeder fw-Befehl bzw. fw import der
exportierten Datei. Meldet der Daemon beim Laden einen Fehler, übernimmt serve die Konfiguration trotzdem als
Ist-Zustand (der Daemon hat sie bereits gespeichert) und nennt die betroffenen Regeln; prüfen mit
sudo nft list table inet opensnitch.
opensnitch-cli fw show | rules [--hook input]
opensnitch-cli fw add --hook input --expr 'tcp dport 22' --expr 'ct state new' --target accept -d ssh
opensnitch-cli fw add --hook output --type mangle --expr 'meta skuid 1000' --target queue --target-params 'num 0'
opensnitch-cli fw policy input drop # wie die GUI: Policy DROP + Regeln für established/related und loopback (Profil)
opensnitch-cli fw policy input accept # entfernt diese Profil-Regeln wieder; --raw ändert NUR die Chain-Policy (kann aussperren)
opensnitch-cli fw profile input-drop fw enable|disable
opensnitch-cli fw edit UUID --add-expr 'iifname eth0' fw move UUID 0 fw disable-rule UUID fw delete UUID
opensnitch-cli fw chain-add --hook prerouting --type natdest --name nat_pre fw chain-delete nat_pre
opensnitch-cli fw export system-fw.json fw import system-fw.json
Daemon- und CLI-Konfiguration, Nodes
opensnitch-cli config show [DefaultAction] config set DefaultAction deny config set Stats.MaxEvents 500
opensnitch-cli config loglevel 1 config edit config apply datei.json
opensnitch-cli settings show|set|reset|edit|path # ~/.config/opensnitch-cli/config.json
opensnitch-cli interception on|off nodes [--history] [--show NODE] status stop
Integration / Skripting (introspect)
Für die Einbindung in eine Hosting-Umgebung gibt es einen maschinenlesbaren Rundumblick:
opensnitch-cli introspect # alles als JSON: version, build, api, sockets, settings, nodes
# (je Node: config, stats, firewall, rules), letzte events/alerts, database
opensnitch-cli introspect nodes rules # nur bestimmte Abschnitte
opensnitch-cli introspect --commands # Liste aller Control-Kommandos
opensnitch-cli introspect nodes | jq '.nodes[].firewall.enabled'
introspect gibt immer JSON aus (unabhängig von --json). Das Feld api ist die Version des Control-Protokolls
und wird nur bei inkompatiblen Änderungen erhöht – Skripte können darauf prüfen. Mit --json liefern sowohl die
lesenden Befehle (rules list, fw show, status, nodes, events, top, stats, alerts, db info …) als
auch die ändernden (rules add/edit/enable/disable/delete, fw policy/preset/allow-port/…/repair/import,
config set, interception, db purge/vacuum/clean, stop) die Antwort des Control-Servers als JSON-Objekt,
und einen Exit-Code ≠ 0 bei Fehlern. Ausnahmen: fw export FILE/rules export DIR schreiben weiter die Dateien,
prompt/events -f/proc/netstat geben mit --json je Ereignis eine JSON-Zeile aus (Hinweise gehen nach
stderr, damit die Zeilen für jq -c . sauber bleiben).
Der Control-Socket (server.control_socket, Standard $XDG_RUNTIME_DIR/opensnitch-cli-<uid>.sock) ist die
Schnittstelle; er spricht zeilenweise JSON, falls jemand direkt integrieren will statt über die CLI.
Bash-Vervollständigung
make install legt completion/opensnitch-cli.bash unter $PREFIX/share/bash-completion/completions/ ab
(sonst: source completion/opensnitch-cli.bash). Vervollständigt Befehle, Optionen, Regelnamen und Nodes.
Betrieb als Dienst
make install (bzw. das RPM) legt die Benutzer-Unit opensnitch-cli.service an:
systemctl --user enable --now opensnitch-cli
(ohne Installation: sed 's|@BINDIR@|/usr/local/bin|' systemd/opensnitch-cli.service.in > ~/.config/systemd/user/opensnitch-cli.service)
Ohne laufendes serve fällt der Daemon auf sein eigenes DefaultAction zurück (wie ohne GUI).
Desktop-Benachrichtigungen für verpasste Anfragen: settings set notifications.type desktop (nutzt notify-send).
Tests
Drei Stufen, alle im Paket unter tests/ und tools/:
Die ergänzten Bedienungs- und Referenztests vom 24.9.2026 sind in
docs/WORK-NOTES-2026-09-24.md dokumentiert:
python3 tests/tui-interaction.py prüft Maus/Tastatur und den gespeicherten Zustand;
bash tests/real-daemon-isolated.sh prüft den echten Daemon mit nftables und einem lokalen HTTP-Server
in eigenen Netzwerk-Namensräumen (benötigt root/sudo). tests/qt-reference.py verwendet die originalen
Qt-Regeldialoge aus einem separat vorhandenen OpenSnitch-Checkout.
tests/run-tests.sh– Integrationstest ohne echten Daemon. Startetserve, dazutools/fake-daemon.py(ein kleiner Python-opensnitchd-Ersatz mitgrpcio– also ein echter gRPC-Client gegen unseren Server) und fährt alles durch: Subscribe/Ping/Notifications/PostAlert, Pop-up beantworten (auch Timeout und Fehleingaben), Regeln anlegen/ändern/löschen/exportieren/importieren, Firewall, Konfiguration, Interception, Events/Top/Stats/Alerts, Prozess-/Node-/Netstat-Tasks, Logs und das TUI (per Pseudoterminal mittools/tui-driver.py, das Tasten schickt und den Bildschirm prüft). Brauchtpython3,grpcio,protobufundpyte(pip install grpcio protobuf pyte). Endet mitALL OK.make && bash tests/run-tests.shtests/smoke-real-daemon.sh– gegen den echtenopensnitchd(in der VM). Prüft, ob der Daemon verbunden ist, obDefaultActionaufdenysteht (fail-closed), löst mitcurleine echte Verbindungsanfrage aus und beantwortet sie von der Kommandozeile, legt eine Allow- und eine Deny-Regel an und prüft, dasscurlentsprechend durchkommt bzw. geblockt wird, kontrolliert die gespeicherten Events, und – mitsudo/nft– dass nachfw policy input dropalle Profil-Regeln (Loopback, established/related) wirklich in nftables stehen (genau die Prüfung, die den Position-Fehler gefunden hätte). Temporäre Regeln und die Firewall-Konfiguration werden wieder hergestellt. AusgabePASS/FAILje Prüfung, Exit-Code ≠ 0 bei Fehlern.tests/smoke-real-daemon.sh # Ziel-URL optional: tests/smoke-real-daemon.sh https://example.org/tools/go-daemon-sim.py– der Go-Daemon auf Draht-Ebene, ohne Go. Der echte Daemon benutzt grpc-go 1.32, das sich in Details anders verhält als der Python-gRPC-Client (u. a. schickt es beim Unix-Socket den rohen Pfad als:authority). Das Skript spricht rohes HTTP/2 (h2-Bibliothek) genau so wie grpc-go und fährt die Daemon-Sequenz durch: Ping mit 150 Events unter der 1-s-Deadline, Subscribe mit 300 Regeln (mehrere Frames, Flusskontrolle), Notifications-Stream inkl. Antwort, AskRule. Läuft intests/run-tests.shmit.- Von Hand im TUI:
opensnitch-cli tui, in einem zweiten Terminalcurl https://example.com→ das Pop-up muss erscheinen;d(oder Klick auf „Deny“) →curlbricht ab; Rules-Reiter zeigt die neue Regel.
Was hier vor Auslieferung getestet wurde: Stufen 1 und 3 komplett (plus AddressSanitizer-Läufe für Trennungen und
Abstürze), das RPM-Spec per rpmbuild. Genau Stufe 3 hat den Fehler gefunden, an dem der echte Daemon
scheiterte (siehe Abschnitt „Keine Pop-ups?“). Stufe 2 lief am 5.9.2026 in einer WSL2-Ubuntu-VM gegen
opensnitchd 1.8.0 (nftables, eBPF-Prozessmonitor): Pop-ups, Regeln, und die komplette Firewall-Reihe
(fw policy input|output drop|accept, alle Presets, allow-/deny-/reject-/delete-port, --from, tcp,udp,
repair, export/import) mit Kontrolle des echten Rulesets per nft list table inet opensnitch und
Verbindungstests mit curl bei ein- und ausgeschalteter Interception. Dabei wurde der Position-Fehler (siehe
Firewall-Abschnitt) gefunden und behoben; der Fake-Daemon lehnt seither dieselben Dinge ab wie der echte
(Position ≠ 0, CIDR, tcp+udp in einer Regel).
Was dabei gefunden und behoben wurde, steht in docs/REAL-DAEMON-FIXES.md.
Nicht portiert
Qt-spezifisches: Themes, Tray-Icon, Plugin-System der GUI, Diagramme. Alles andere (Regeln inkl. Listen/Regex/Netzwerk-Aliase, Firewall-Editor, Prozess-Monitor, Netstat, Nodes, Alerts, Statistiken, DB-Wartung, Version-Warnung, Persistieren des Interception-Zustands) ist vorhanden.
Lizenz: GPL-3.0 (wie OpenSnitch); cJSON: MIT (third_party/LICENSE.cJSON).