secexit
-- secexit blocks unauthorized outbound traffic using eBPF.
Zero config. Zero latency. Zero trust.
secexit is designed to strictly control egress (outbound) network traffic on Linux systems. It uses kernel-space enforcement (eBPF) for unbypassable IP-based blocking.
This architecture ensures that even if an application bypasses the DNS filter (e.g., by using a static binary or hardcoded IPs), the kernel-level eBPF filter will catch and drop the traffic.
Quickstart
1. Define egress policy
Create a policy file at ~/.config/secexit/policy.json.
This file can be provided as a HTTP URL allowing for multiple secexit (like all your containers) to refer to the same policy.
Use SECEXIT_POLICY env var to set a different path to this file.
2. Run daemon
The daemon loads eBPF program into kernel. It requires root to attach to the cgroup hooks.
Run via Cargo:
Or run the built binary directly:
Output:
[INFO secexit_common] secexit policy (v1) loaded from: /root/.config/secexit/policy.json
[INFO secexit_daemon] eBPF program attached.
[INFO secexit_daemon] applying 0 static IP rules...
[INFO secexit_daemon] resolving 1 domains...
[INFO secexit_daemon] + domain '[www.google.com](https://www.google.com)' blocked (1 IPs added)
[INFO secexit_daemon] secexit daemon running. Waiting for Ctrl-C...
3. Check its working
Once daemon is running the kernel will drop packets destined for blocked IPs.
Test with curl:
)
Expected Result:
* Host [www.google.com:443](https://www.google.com:443) was resolved.
* IPv4: 142.251.141.164
* Trying 142.251.141.164:443...
* Immediate connect fail for 142.251.141.164: Operation not permitted
curl: (7) Failed to connect to [www.google.com](https://www.google.com) port 443: Operation not permitted
Test with ping:
)
# Output: ping: connect: Operation not permitted
Test with your browser:
Open up your favourite browser and attempt to resolve https://www.google.com
Note: As eBPF requires IP addresses the daemon resolves the domains defined in your policy at startup and pushes the resulting IPs to the kernel blocklist.
Optional: Userspace Shim
If you cannot use eBPF or want to block DNS resolution in userspace (Layer 7), use LD_PRELOAD. This reads the same policy.json but hooks getaddrinfo.
LD_PRELOAD=./target/release/libsecexit_shim.so )
Output:
[secexit] BLOCKED DOMAIN: [www.google.com](https://www.google.com)
curl: (6) Could not resolve host: [www.google.com](https://www.google.com)
Installation
RPM (dnf)
For distro package, build with packaging/secexit.spec (see docs/packaging.md), or install from COPR:
Crates.io
After publishing the crates, install the daemon with:
Development
The project consists of three main components:
1. secexit-ebpf (Kernel Space / Layer 3)
An eBPF program attached to the cgroup connect4 hook. It inspects every IPv4 TCP/UDP connection attempt directly at the kernel level. It enforces specific IP blocks or a global "Lockdown Mode" (kill switch). It cannot be bypassed by userspace applications (even static Go binaries!).
2. secexit-daemon (Controller)
A Rust async daemon (Tokio + Aya) that loads eBPF program and reads policy.json. It performs DNS lookups in the context environment for blocked domains and pushes the resulting IPs to the eBPF map. It also toggles the global kill switch via a shared eBPF map.
3. secexit-shim (Userspace / Layer 7)
A shared object library which is used with LD_PRELOAD. It hooks getaddrinfo to intercept & block DNS requests based on policy.json. As this is userspace, it can be bypassed by direct connect() calls or custom DNS resolvers.
Prerequisites
- OS: Linux (Kernel 5.8+ required for full cgroup eBPF support).
- Rust: Nightly toolchain (required for building eBPF).
- Tools:
bpf-linker,cargo-xtask.
Build Process
Order matters. The kernel program must be built before the userspace daemon, as the daemon embeds the resulting ELF binary.
- Build eBPF Kernel Program:
- Build Userspace Components:
FAQ
Why not support globbing of domain names?
eBPF operates strictly on IP addresses. To block a domain, secexit-daemon must resolve it to an IP first. Supporting wildcards (e.g., *.google.com) would require scanning the entire DNS space or intercepting all DNS packets in userspace, which significantly increases complexity and latency.
**Error: Invalid ELF header size**
You likely built the project for the host architecture (x86_64) instead of BPF. Run cargo clean and then explicitly run cargo xtask build-ebpf --release.
Error: Operation not permitted (OS Error)
You are not running the daemon as sudo. eBPF operations require root privileges.
Logs not showing?
Ensure the environment variable RUST_LOG=info is set. For raw kernel logs, check /sys/kernel/tracing/trace_pipe.
What's with the name? A bad joke relating to mainframes (No relation to IBM !).