# BastionGuard™ Secure Connection **BastionGuard™ Secure Connection** is an interactive application firewall for GNU/Linux inspired by Little Snitch. It intercepts outgoing network connections and asks the user whether to allow or deny them, while remembering previous choices. > BastionGuard™ is a trademark of Calogero Scarnà. The BastionGuard™ name and > branding are not licensed under the GPL. --- ## Architecture The entire project is written in **C++23**. The daemon and the UI are two separate processes communicating through a **Unix domain socket** using a length-prefixed JSON protocol. No gRPC, no protobuf, and no code generation. ```text ┌─────────────────────────────────────────────────────┐ │ UI (C++/gtkmm4) bastionguard-sc-ui │ │ ├── MainWindow (sidebar + notebook, unobtrusive) │ │ ├── AlertDialog (allow/deny popup, NON-modal) │ │ │ ├── app name + icon from .desktop │ │ │ └── "🛡 proxy configured" hint on match │ │ ├── ConnectionsPage (real-time log) │ │ ├── AppsPage (aggregated per-application view) │ │ ├── RulesPage, StatsPage, AboutPage │ │ ├── TrayIcon (D-Bus/StatusNotifierItem) │ │ └── UIServer (Unix socket server) │ │ ▲ │ │ │ Unix socket + framed JSON │ │ │ (unix:///tmp/bsd-daemon.sock) │ │ ▼ │ │ Daemon (C++) bsc-daemon │ │ ├── NFQueue + nft chain bsc_intercept │ │ ├── In-memory "always" verdict cache │ │ ├── isAsking flag (one popup at a time) │ │ ├── SocketSnapshot (/proc/net fallback) │ │ ├── ProcMon + ProcEventMonitor (netlink) │ │ ├── Rule engine (simple/regexp/range/network/list) │ │ ├── DnsResolver (reverse DNS) │ │ ├── TaskRunner (scan/heartbeat/rules_reload) │ │ └── UIClient (Unix socket + automatic reconnect) │ └─────────────────────────────────────────────────────┘ ``` The **UI is the server** — the daemon connects to the UI. ### nft chain installed by the daemon At startup, the daemon automatically installs an `inet bsc_intercept output` chain through `nft`. The chain: 1. **Whitelists** infrastructure traffic that must not require user approval, because doing so would make the system unusable: loopback, DNS (UDP/TCP 53), DHCP (67/68), mDNS (5353), NTP (123), ICMP/ICMPv6, and IGMP. 2. **Intercepts** all other new traffic (`ct state new`) and sends it to NFQUEUE number 0 with `bypass`. If the daemon dies, packets are allowed through instead of becoming stuck. The chain is removed during shutdown. If `nft` or `CAP_NET_ADMIN` is missing, the daemon automatically falls back to polling `/proc/net/{tcp,udp,tcp6,udp6}`. ### Verdict cache and isAsking To avoid overwhelming the user with popups: - An **in-memory cache** in the daemon stores `"always"` verdicts for the current session using the key `app_path|host|port`. If the user selects `"allow always"` for Firefox → google.com:443, the next 100 connections are allowed without another popup. - An **isAsking flag** prevents more than one popup from being active at the same time. While the user is deciding on an alert, any additional incoming connections receive the `default_action`—typically `allow`—instead of opening a second dialog. ### Wire protocol Framing: `[uint32_le length][UTF-8 JSON payload]`. Daemon → UI messages: `hello`, `ping`, `ask_rule`, `alert`. UI → daemon messages: `pong`, `ask_rule_reply`, `notification`. Defined in `common/include/bsc/proto/Wire.hpp`. --- ## Dependencies ### Build dependencies | Package | Minimum version | |---------|-----------------| | `meson` | ≥ 1.0 | | `ninja` | any | | `c++` | GCC ≥ 13 or Clang ≥ 16 (C++23) | | `pkg-config` | any | ### UI runtime dependencies | Package | Notes | |---------|-------| | `libgtkmm-4.0-dev` | GUI toolkit | | `libglibmm-2.68-dev` | GLib C++ bindings | | `libgiomm-2.68-dev` | Gio C++ bindings | | `libsystemd-dev` | sd-bus for the system tray | | `libgdk-pixbuf-2.0-dev` | Icons | | `nlohmann-json3-dev` | Protocol serialization | ### Daemon runtime dependencies | Package | Notes | |---------|-------| | `libnetfilter-queue-dev` | Packet interception (optional) | | `nftables` | `nft` used to install the `bsc_intercept` chain | | `nlohmann-json3-dev` | Configuration, rule, and protocol parsing | | `libssl-dev` | MD5/SHA1/SHA256 process hashing | ### Installing dependencies on Debian/Ubuntu ```bash sudo apt install \ libgtkmm-4.0-dev libglibmm-2.68-dev libgiomm-2.68-dev \ libsystemd-dev libgdk-pixbuf-2.0-dev \ libnetfilter-queue-dev nftables \ nlohmann-json3-dev libssl-dev \ meson ninja-build pkg-config ``` --- ## Building ```bash meson setup build --prefix=/usr --sysconfdir=/etc --localedir=share/locale meson compile -C build -j$(nproc) sudo meson install -C build ``` --- ## Running ```bash sudo systemctl enable --now bsc-daemon bastionguard-sc-ui ``` The main window can remain closed. Essential interaction takes place through the non-modal, independent `AlertDialog`, so it does not block the rest of the desktop or the main UI. ### Development ```bash # UI only, without the daemon — displays "Daemon disconnected" BSC_DATA=ui/data build/ui/bastionguard-sc-ui # Daemon in development mode, as root, with a real NFQUEUE sudo build/daemon/bsc-daemon \ --config-file daemon/data/default-config.json \ --rules-path /etc/bastionguard-secure-connectiond/rules \ --ui-socket unix:///tmp/bsd-daemon.sock \ --debug ``` --- ## Configuration ### Communication socket Default: `unix:///tmp/bsd-daemon.sock` with `0666` permissions. The daemon runs as root, while the UI runs as a regular user. ### InterceptUnknown `"InterceptUnknown": true` is enabled by default. Every new connection without a matching rule triggers a popup. Change it to `false` to make the daemon always apply `DefaultAction` without asking the user. ### Proxy BSC **does not manage** proxy traffic; that is handled by `bastionguard-cef`. However, it automatically detects proxies configured through `http_proxy`, `HTTPS_PROXY`, `ALL_PROXY`, and `SOCKS_PROXY`, including values loaded from `/etc/environment`, `~/.profile`, and `~/.bashrc`. When the destination matches a configured proxy, the `AlertDialog` displays a green 🛡 indicator so the user can immediately recognize expected proxy traffic. No special nft rule is required. Traffic to the proxy follows the regular interception pipeline, and the user can select `"Allow always"` on the first popup to avoid seeing it again. ### JSON rules Rules are stored in `/etc/bastionguard-secure-connectiond/rules/`. Example: ```json { "name": "allow-proxy", "enabled": true, "action": "allow", "duration": "always", "operator": { "type": "simple", "operand": "dest.host", "data": "proxy.local" } } ``` Supported operands: - `process.path` - `process.command` - `process.hash.{md5,sha1,sha256}` - `user.id` - `user.name` - `dest.ip` - `dest.host` - `dest.port` - `protocol` - `list` - `lists` --- ## Debugging The protocol uses JSON over a Unix socket, so it can be inspected directly: ```bash # While the daemon is running, inspect the traffic # Format: 4-byte little-endian length + JSON payload sudo socat - UNIX-CONNECT:/tmp/bsd-daemon.sock | xxd ``` `utils/scripts/fake_ui.py` is a minimal fake server written in Python. It simulates the UI and is useful for testing the daemon without compiling the GUI. --- ## Uninstallation ```bash sudo systemctl stop bsc-daemon sudo systemctl disable bsc-daemon sudo ninja -C build uninstall sudo nft delete table inet bsc_intercept 2>/dev/null ``` --- ## License **BastionGuard™ Secure Connection** is free software licensed under GPL-3.0. The code is inspired by [OpenSnitch](https://github.com/evilsocket/opensnitch). Copyright © 2025–2026 Calogero Scarnà > BastionGuard™ is a trademark of Calogero Scarnà. > The BastionGuard™ name and branding are **not** licensed under the GPL.