Skip to content
API Documentation
BastionGuard™
bastionguard-secure-connection Directory Reference

Directories

 
common
 
daemon
 
ui
 
utils

Detailed Description

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.

┌─────────────────────────────────────────────────────┐
│ 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

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

meson setup build --prefix=/usr --sysconfdir=/etc --localedir=share/locale
meson compile -C build -j$(nproc)
sudo meson install -C build

Running

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

# 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:

{
"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:

# 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

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.

Copyright © 2025–2026 Calogero Scarnà

BastionGuard™ is a trademark of Calogero Scarnà. The BastionGuard™ name and branding are not licensed under the GPL.