This document provides a technical deep-dive into the architecture and implementation of Exit Gate.
Exit Gate is a Linux application firewall that monitors network connections at the kernel level using eBPF (extended Berkeley Packet Filter) and provides a user-friendly Electron-based GUI for managing firewall rules.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β User Space β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Electron GUI (React + TypeScript) β β
β β - Connection Prompts β β
β β - Rule Management β β
β β - Statistics Dashboard β β
β β - Connection History β β
β βββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββ β
β β Unix Socket IPC β
β βββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββ β
β β Rust Daemon β β
β β ββββββββββββββββββββ ββββββββββββββββ βββββββββββββββ β β
β β β Rule Engine β β IPC Server β β Process β β β
β β β - Match rules β β - Unix sock β β Info β β β
β β β - Priority β β - Messages β β - /proc β β β
β β ββββββββββββββββββββ ββββββββββββββββ βββββββββββββββ β β
β β ββββββββββββββββββββ ββββββββββββββββββββββββββββββββ β β
β β β SQLite DB β β eBPF Manager β β β
β β β - Rules β β - Load programs β β β
β β β - History β β - Read ring buffer β β β
β β ββββββββββββββββββββ ββββββββββββββββ¬ββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββ β
β β libbpf-rs β
βββββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββΌβββββββββββββββββββββ
β Kernel Space β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β eBPF Programs (C) β β
β β - kprobe/tcp_connect - Track TCP connections β β
β β - kprobe/udp_sendmsg - Track UDP packets β β
β β - kprobe/inet_csk_accept - Track incoming connections β β
β β β β
β β Maps: β β
β β - Ring Buffer: Send events to userspace β β
β β - Hash Map: Store verdicts β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Linux Kernel β β
β β - Network Stack β β
β β - Socket Layer β β
β β - TCP/IP Implementation β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
File: ebpf/network_monitor.bpf.c
The eBPF component is responsible for monitoring network activity at the kernel level. It consists of several kprobes that attach to kernel functions:
-
tcp_connect - Monitors outbound TCP connections
- Captures: PID, UID, source/dest IP, ports
- Triggered when a process initiates a TCP connection
-
udp_sendmsg - Monitors UDP traffic
- Captures: PID, UID, source/dest IP, ports
- Triggered when a process sends UDP packets
-
inet_csk_accept - Monitors inbound TCP connections
- Captures: Connection details for server applications
- Useful for monitoring listening services
- Ring Buffer (
events): 256KB circular buffer for sending connection events to userspace - Hash Map (
verdicts): Stores allow/deny decisions keyed by connection tuple - Hash Map (
process_cache): Caches process information to reduce overhead
struct connection_event {
u32 pid, tid, uid, gid;
u8 event_type, protocol;
u16 family, sport, dport;
union { u32 saddr_v4; u8 saddr_v6[16]; };
union { u32 daddr_v4; u8 daddr_v6[16]; };
char comm[16];
u64 timestamp;
};Directory: daemon/src/
The daemon is the core of Exit Gate, written in Rust for memory safety and performance.
- Entry point and orchestration
- Initializes all components
- Handles graceful shutdown
- Coordinates between eBPF, IPC, and rule engine
- Loads eBPF programs using libbpf-rs
- Attaches kprobes to kernel functions
- Polls ring buffer for events
- Manages eBPF map operations
- Implements the rule matching engine
- Supports multiple criteria types:
- Executable path (exact or regex)
- Command line arguments
- Destination IP/network
- Destination port/range
- Hostname (exact or regex)
- Protocol (TCP/UDP)
- UID/GID
- Priority-based rule evaluation
- Temporal rules (once, process lifetime, forever)
- SQLite database interface using sqlx
- Stores permanent rules
- Maintains connection history
- Automatic cleanup of old entries
- Async operations with connection pooling
- Unix socket server for GUI communication
- JSON-based message protocol
- Bidirectional communication:
- Daemon β GUI: Connection prompts, events, stats
- GUI β Daemon: Rule operations, prompt responses
- Multiple client support
- Reads process information from /proc
- Extracts executable path, command line, UID/GID
- Resolves process tree
- Calculates executable checksums
1. Check process-specific rules (Duration::Process)
2. Check temporary rules (Duration::Once, Duration::UntilRestart)
3. Check permanent rules (Duration::Forever)
4. Within each category, sort by priority (higher first)
5. Return first matching rule's action
6. If no match, prompt user
Rules Table:
CREATE TABLE rules (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
enabled INTEGER,
action TEXT, -- "allow" or "deny"
duration TEXT, -- "once", "process", "forever", "untilrestart"
priority INTEGER,
created_at TEXT,
updated_at TEXT,
hit_count INTEGER,
last_hit TEXT,
-- Criteria fields
executable TEXT,
executable_regex TEXT,
cmdline TEXT,
dest_ip TEXT,
dest_network TEXT,
dest_port INTEGER,
dest_port_min INTEGER,
dest_port_max INTEGER,
dest_host TEXT,
dest_host_regex TEXT,
protocol TEXT,
uid INTEGER,
gid INTEGER
);Connection History Table:
CREATE TABLE connection_history (
id INTEGER PRIMARY KEY,
timestamp TEXT,
pid INTEGER,
uid INTEGER,
gid INTEGER,
executable TEXT,
cmdline TEXT,
dest_ip TEXT,
dest_port INTEGER,
dest_host TEXT,
protocol TEXT,
action TEXT,
rule_id INTEGER REFERENCES rules(id)
);Directory: electron/src/
Modern Electron application with React and Material-UI.
main.ts (Main Process)
ββ Creates BrowserWindow
ββ Connects to daemon via Unix socket
ββ Forwards messages between renderer and daemon
ββ Shows native notifications
preload.ts (Preload Script)
ββ Exposes safe IPC APIs to renderer
ββ Implements security boundary
App.tsx (Renderer Process)
ββ Dashboard: Statistics and recent activity
ββ RulesManager: CRUD operations for rules
ββ ConnectionHistory: Searchable history table
ββ ConnectionPrompt: Modal dialogs for decisions
All messages are JSON objects with a type field:
Client β Daemon:
{"type": "GetRules"}
{"type": "AddRule", "rule": {...}}
{"type": "UpdateRule", "rule": {...}}
{"type": "DeleteRule", "rule_id": 123}
{"type": "GetHistory", "limit": 100}
{"type": "GetStats"}
{"type": "RespondToPrompt", "prompt_id": "uuid", "action": "allow", "remember": true, "duration": "forever"}Daemon β Client:
{"type": "RulesList", "rules": [...]}
{"type": "HistoryData", "entries": [...]}
{"type": "StatsData", "stats": {...}}
{"type": "ConnectionPrompt", "prompt_id": "uuid", "pid": 1234, "executable": "/usr/bin/curl", ...}
{"type": "ConnectionEvent", "timestamp": "...", "pid": 1234, "action": "allow", ...}
{"type": "Success", "message": "..."}
{"type": "Error", "message": "..."}1. Application makes network connection
β
2. Kernel calls tcp_connect() or udp_sendmsg()
β
3. eBPF program captures event
β
4. Event sent to userspace via ring buffer
β
5. Daemon reads event from ring buffer
β
6. Daemon checks rule engine
β
7a. Match found β Apply action, log to DB
7b. No match β Send ConnectionPrompt to GUI
β
8. User makes decision in GUI
β
9. GUI sends RespondToPrompt to daemon
β
10. Daemon applies action, optionally creates rule
β
11. Daemon logs to connection history
- Daemon: Runs as root (required for eBPF)
- GUI: Runs as regular user
- Communication: Unix socket with file permissions
- Programs verified by kernel verifier
- No direct memory access outside eBPF context
- Bounded loops and stack usage
- Cannot crash the kernel
The daemon requires:
CAP_BPF: Load eBPF programsCAP_PERFMON: Access performance monitoringCAP_NET_ADMIN: Network administrationCAP_SYS_ADMIN: System administration (for older kernels)
- Minimal per-connection overhead (~1-2 Β΅s)
- Ring buffer reduces context switches
- Map lookups are O(1)
- No packet inspection, only metadata
- Async I/O with Tokio
- Connection pooling for database
- Lazy evaluation of rules
- Caching of process information
- Virtual scrolling for large lists
- Debounced search
- Pagination for history
- React memoization for expensive renders
- Complete eBPF Integration: Full libbpf-rs implementation
- IPv6 Support: Extend monitoring to IPv6 connections
- DNS Resolution: Resolve IPs to hostnames
- Application Groups: Group rules by application type
- GeoIP Filtering: Block/allow by country
- Import/Export: Backup and restore rules
- Rule Templates: Pre-defined rule sets
- Network Timeline: Visual connection timeline
- BPF CO-RE: Portable eBPF programs
- Ring Buffer Batching: Process multiple events at once
- Rule Compilation: Compile rules to eBPF for kernel-side filtering
- Bloom Filters: Fast negative lookups for rules
-
eBPF program fails to load
- Check kernel version (5.8+)
- Verify CONFIG_BPF=y in kernel config
- Check for BTF support
-
Permission denied on socket
- Verify socket permissions
- Check daemon is running as root
-
High CPU usage
- May indicate too many connection attempts
- Consider creating broader rules
- Check for connection loops
# Enable debug logging
sudo RUST_LOG=debug /usr/local/bin/exit-gate-daemon
# View eBPF program logs
sudo bpftool prog tracelog