English | 简体中文
Documentation version: On
main, this guide describes development code and may include unreleased changes. For v1.7.0, use the v1.7.0 security guide. Checkrustnet --versionfor your installed version.
RustNet processes untrusted network data, making defense-in-depth security critical. This document describes the security measures implemented.
Hardening available since v1.7.0: RustNet aborts on a failed requested UID/GID drop, retains secure output descriptors without Seatbelt output-path exceptions, and checks for output-file collisions. The Linux startup socket inventory described below is also available since v1.7.0.
On Linux, macOS, and FreeBSD, a requested root UID/GID drop must succeed before
packet-processing workers start, even in best-effort mode. A failed transition
aborts startup instead of continuing as root or with a partially changed identity.
Unavailable optional sandbox layers may still degrade gracefully. Explicit
--no-uid-drop or --no-sandbox opt-outs retain their existing behavior.
- Landlock Sandboxing (Linux)
- Seatbelt Sandboxing (macOS)
- FreeBSD Sandboxing
- Privilege Drop and Job Object Sandboxing (Windows)
- Privilege Requirements
- Read-Only Operation
- Network Communication
- Log File Privacy
- eBPF Security
- Threat Model
- Supply Chain Security
- Audit and Compliance
- Reporting Security Issues
On Linux 5.13+, RustNet uses Landlock to restrict its own capabilities after initialization. This limits the damage if a vulnerability in packet parsing is exploited.
| Restriction | Kernel Version | Description |
|---|---|---|
| Filesystem | 5.13+ | Read access limited to /proc, interface-statistics sysfs paths, and account, resolver, GeoIP, and optional Kubernetes paths needed at runtime |
| Network | 6.7+ | TCP bind blocked; TCP connect blocked except destination port 53 when reverse DNS is enabled. The current Landlock policy does not restrict UDP |
| Capabilities | Any | CAP_NET_RAW dropped after pcap socket opened |
| Capabilities | Any | CAP_BPF, CAP_PERFMON dropped after eBPF programs loaded |
| Root uid | Any | When started as root (e.g. sudo rustnet), the process drops to the invoking user (SUDO_UID/SUDO_GID) or nobody after initialization |
| Privileges | 3.5+ | PR_SET_NO_NEW_PRIVS set by RustNet itself — always, even with --no-sandbox — prevents privilege escalation via setuid binaries |
- Initialization phase: RustNet loads eBPF programs, opens packet capture handles, and creates log files
- Privilege lock:
PR_SET_NO_NEW_PRIVSis set (applied even when the sandbox is disabled) - Capability drop:
CAP_NET_RAW,CAP_BPF, andCAP_PERFMONare removed from the process - Root uid drop: when running as root, the process switches to the invoking sudo user (or
nobody) viasetresuid/setresgid. Already-open capture sockets, eBPF programs, and log/export files keep working. This matters most on kernels without Landlock, where the uid drop is the main containment - Landlock: Restricts filesystem and network access
When Landlock is enforced, an attacker exploiting DPI/packet parsing is subject to these restrictions. The TCP restrictions require Linux 6.7+ and an active Landlock network policy:
- Cannot read arbitrary files (credentials, configs, etc.)
- Cannot open new writable paths; existing output descriptors remain writable
- Cannot make outbound TCP connections other than DNS on destination port 53 when reverse DNS is enabled
- Cannot bind TCP ports (reverse shell blocked)
- Cannot create new raw sockets (capability dropped)
- Cannot escalate privileges via setuid binaries (
PR_SET_NO_NEW_PRIVS, set even with--no-sandbox) - Does not run as root: under
sudo rustnetthe process continues as the invoking user, so even on kernels without Landlock a compromise does not yield root
--no-sandbox Disable Landlock sandboxing, capability dropping, and the
root uid drop (PR_SET_NO_NEW_PRIVS is still set)
--sandbox-strict Require full sandbox enforcement or exit
--no-uid-drop Keep running as root instead of dropping to
SUDO_UID/SUDO_GID (or nobody) after initialization
Trade-off of the root uid drop: the procfs fallback for process attribution can
then only inspect processes owned by the target user, and Kubernetes log
directories under /var/log/pods may become unreadable. On Linux 5.11 and
newer, the default eBPF path takes a one-shot task-file socket inventory before
the drop, so pre-existing sockets owned by other users remain attributable. If
you rely on procfs-only attribution, such as a build without eBPF or an older
kernel, and need to attribute other users' processes, use --no-uid-drop.
- Kernel < 5.13: Sandboxing skipped, warning logged
- Kernel 5.13-6.6: Filesystem restrictions only
- Kernel 6.7+: Filesystem and TCP restrictions; UDP remains unrestricted by the current Landlock policy
- Docker: Landlock may be restricted; app continues normally
On macOS 10.5+, RustNet uses Seatbelt (sandbox_init_with_parameters) to restrict its own capabilities after initialization. This limits the damage if a vulnerability in packet parsing is exploited.
| Restriction | Description |
|---|---|
| Outbound network | TCP/UDP outbound blocked except destination port 53 when reverse DNS is enabled; Unix sockets (Mach IPC) allowed |
| Filesystem reads | User home directories blocked (/Users, /var/root); GeoIP paths explicitly allowed |
| Filesystem writes | All user home directories blocked (/Users, /var/root) |
| Output files | Writes use retained descriptors; no output-path write exceptions are granted |
| Process execution | All binaries blocked except /usr/sbin/lsof |
| Root uid | When started as root (e.g. sudo rustnet), the process drops to the invoking user (SUDO_UID/SUDO_GID) or nobody after initialization |
- Initialization phase: RustNet opens packet capture handles (BPF/PKTAP) and creates log files
- Prepare outputs: JSON logging, PCAP, PCAP sidecar (
.connections.jsonl), and PCAPNG files are securely opened before the sandbox. Their opened file identities are checked for collisions before capture outputs are truncated, and the descriptors remain open across the uid drop - Root uid drop: when running as root, the process switches to the invoking sudo user (or
nobody) viasetgid/setuid. Already-open capture and log/export descriptors keep working - Sandbox application:
sandbox_init_with_parametersis called; already-open file descriptors survive unchanged, only future operations are restricted
RustNet uses an allow-default SBPL profile with targeted denies. A
deny-default profile would require explicitly whitelisting all system libraries,
Mach ports, locale data, fonts, and other OS internals, which is fragile across
OS releases. The targeted rules block reads and writes under user home
directories, outbound network connections except DNS on port 53 when enabled,
and execution of all binaries except /usr/sbin/lsof.
--json-log, --pcap-export, and --pcapng-export write through securely pre-opened file descriptors, including the PCAP sidecar. RustNet does not reopen these paths after sandboxing and passes no output-path write exceptions to the Seatbelt profile. This also avoids granting write access to a different file if a pathname is replaced after preparation.
All three flags work normally within the sandbox.
Available since v1.7.0: On Unix, existing explicit output files must belong to the effective user or a valid sudo caller. Output paths are opened relative to validated directory descriptors. Directories with unsafe ownership or permissions are rejected. Automatic diagnostic logs are created only as new files in a validated logs/ directory. Use trusted private launch and output directories; changing permissions on an existing file cannot revoke descriptors another process already holds.
If an attacker exploits a vulnerability in DPI/packet parsing:
- Cannot read SSH keys, AWS credentials, browser profiles, or other credential files under
/Users - Cannot write to SSH keys, AWS credentials, browser profiles, or other credential files
- Cannot make outbound TCP/UDP connections other than DNS on destination port 53 when reverse DNS is enabled
- Cannot execute binaries other than
/usr/sbin/lsof(no shell escapes via/bin/sh,/usr/bin/curl, etc.) - Does not run as root: under
sudo rustnetthe process continues as the invoking user
--no-sandbox Disable Seatbelt sandboxing and the root uid drop
--sandbox-strict Require full sandbox enforcement or exit
--no-uid-drop Keep running as root instead of dropping to
SUDO_UID/SUDO_GID (or nobody) after initialization
Trade-off of the root uid drop: the default PKTAP attribution path is
unaffected (process metadata arrives in-band on the already-open capture fd),
but the lsof fallback (active when PKTAP is unavailable, e.g. with an explicit
--interface) then only sees the target user's processes. Use --no-uid-drop
if you rely on lsof attribution for other users' processes.
sandbox_init_with_parameters is a private (undocumented) macOS API. It has been stable since macOS 10.5 and is used by Chromium, Firefox, and Safari for process sandboxing, but it could theoretically change without notice. BestEffort degrades gracefully if the API behaves unexpectedly rather than preventing the app from running. Use --sandbox-strict to require sandboxing or abort.
Unlike Linux Landlock, clipboard copy (c key) works normally under Seatbelt. macOS clipboard uses NSPasteboard, which communicates via Mach IPC over Unix domain sockets — the SBPL profile explicitly allows (network-outbound (remote unix-socket)).
On Linux, clipboard requires access to Wayland sockets (/run/user/UID/wayland-0) or X11 sockets (/tmp/.X11-unix/). Landlock grants no pathname write exceptions and blocks these sockets, so clipboard is unavailable when Landlock is active.
FreeBSD does not currently have sandboxing enabled. A full Capsicum sandbox using cap_enter() with libcasper for privileged process lookup is planned — see ROADMAP.md for details.
Until Capsicum lands, the primary containment on FreeBSD is a root privilege drop: when started as root (e.g. sudo rustnet), the process drops to the invoking user (SUDO_UID/SUDO_GID) or nobody after the BPF capture devices are open, via setresuid/setresgid. Already-open capture and log/export descriptors keep working, and pre-created export files are handed over to the target user. Note that doas does not set SUDO_UID, so doas users get the nobody fallback.
Trade-off: process attribution uses sockstat, which as a non-root user only sees the target user's sockets. Use --no-uid-drop if you need attribution for other users' processes.
--no-uid-drop Keep running as root instead of dropping to
SUDO_UID/SUDO_GID (or nobody) after initialization
On Windows, RustNet removes dangerous privileges from the process token and applies a Job Object to prevent child process creation after initialization.
| Restriction | Description |
|---|---|
| Privilege removal | SeDebugPrivilege, SeTakeOwnershipPrivilege, SeBackupPrivilege, SeRestorePrivilege, and other dangerous privileges permanently removed |
| Child processes | Job Object blocks creation of child processes (reverse shell, exec-based exfiltration) |
- Initialization phase: RustNet opens Npcap handles and creates log files
- Privilege removal:
AdjustTokenPrivilegeswithSE_PRIVILEGE_REMOVEDpermanently strips dangerous privileges from the process token - Job Object: A Job Object with
JOB_OBJECT_LIMIT_ACTIVE_PROCESS = 1is applied, preventing any child process creation
If an attacker exploits a vulnerability in DPI/packet parsing:
- Cannot debug other processes (SeDebugPrivilege removed)
- Cannot take ownership of arbitrary files (SeTakeOwnershipPrivilege removed)
- Cannot bypass ACLs to read files (SeBackupPrivilege removed)
- Cannot spawn child processes (cmd.exe, powershell.exe, curl.exe — blocked by Job Object)
- Cannot load kernel drivers (SeLoadDriverPrivilege removed)
Windows sandboxing provides fewer filesystem and network restrictions than Linux Landlock or macOS Seatbelt. FreeBSD currently has no comparable sandbox:
- No filesystem restriction — Windows lacks a process-wide filesystem sandbox equivalent to Landlock or Seatbelt
- No network restriction; outbound connections remain possible
- Privilege removal only affects privileges the elevated process held
--no-sandbox Disable privilege removal and job object
--sandbox-strict Require full sandbox enforcement or exit
RustNet requires privileged access for packet capture:
| Platform | Requirement |
|---|---|
| Linux | CAP_NET_RAW capability or root |
| macOS | Root or BPF group membership (access_bpf group) |
| Windows | Administrator (for Npcap) |
| FreeBSD | Root or BPF device access |
- Raw socket access - Intercept network traffic at low level (read-only, non-promiscuous mode)
- BPF device access - Load packet filters into kernel
- eBPF programs - Optional kernel probes for enhanced process tracking (Linux only)
Instead of running as root, grant only the required capabilities:
# Modern Linux (5.8+): packet capture + eBPF
sudo setcap 'cap_net_raw,cap_bpf,cap_perfmon+eip' $(which rustnet)
# Packet capture only (no eBPF process detection)
sudo setcap cap_net_raw+eip $(which rustnet)Legacy pre-5.8 kernels required CAP_SYS_ADMIN for eBPF operations. RustNet
does not grant this broad capability automatically; use CAP_NET_RAW only and
let eBPF fall back to procfs unless you explicitly accept the extra risk.
After sandbox application, CAP_NET_RAW and eBPF-loading capabilities are
dropped - the process retains only the minimum privileges needed.
RustNet only monitors traffic; it does not:
- Modify packets
- Block connections
- Inject traffic
- Alter routing tables
- Change firewall rules
The packet capture is opened in non-promiscuous, read-only mode.
RustNet has no telemetry, cloud service, or remote API. Its GeoIP lookups use
local database files. Reverse DNS is enabled by default and may send PTR
queries for observed IP addresses to the system's configured DNS resolver.
Use --no-resolve-dns to avoid those lookups. Captured traffic, logs, and
exports are written locally unless you choose to share them.
Log files may contain sensitive information:
- IP addresses and ports
- Hostnames and SNI data
- Process names and PIDs
- DNS queries and responses
Best Practices:
- Disable logging by default (no
--log-levelflag) - Secure log directory permissions
- Implement log rotation and retention policies
- Review logs for sensitive data before sharing
When using eBPF for enhanced process detection (default on Linux):
- Requires additional kernel capabilities (
CAP_BPF,CAP_PERFMON) - eBPF programs are verified by kernel before loading
- Limited to read-only operations (no packet modification)
- On Linux 5.11+, a one-shot task-file iterator inventories pre-existing socket owners
- Automatically falls back to procfs if eBPF fails
What RustNet protects against:
- Unauthorized users cannot capture packets without proper permissions
- Capability-based permissions limit blast radius of compromise
- Landlock (Linux) and Seatbelt (macOS) sandboxes contain potential exploitation
What RustNet does NOT protect against:
- Users with packet capture permissions can see all unencrypted traffic
- Root/Administrator users can modify RustNet or capture packets directly
- Physical access to the machine enables packet capture
- Network-level attacks (RustNet is a monitoring tool, not a security appliance)
Both Landlock (Linux) and Seatbelt (macOS) enforce restrictions even when RustNet runs as root (UID 0). Once applied, the sandbox cannot be reversed from within the process — on Linux, RustNet sets PR_SET_NO_NEW_PRIVS directly before applying any restrictions (Landlock requires and would set it as well), which is irreversible per-process and applied even with --no-sandbox.
However, sandboxing does not protect against supply chain attacks. A compromised binary would simply not apply the sandbox. Root can also:
- Pass
--no-sandboxto skip sandboxing entirely (exceptPR_SET_NO_NEW_PRIVS) - Unload the Landlock LSM kernel module
- Disable SIP on macOS (which controls sandbox enforcement)
- Use
ptraceto modify a running process
For this reason, running with fine-grained capabilities (setcap cap_net_raw=eip) is strongly preferred over running as root.
RustNet takes the following measures to protect against supply chain attacks:
- Dependency lockfile:
Cargo.lockis committed to the repository, pinning all transitive dependency versions and recording source checksums. This prevents silent version upgrades. - Security audit:
cargo auditruns in CI on every push and pull request, checking dependencies against the RustSec Advisory Database and detecting yanked releases. A scheduled daily workflow re-checks the committedCargo.lock, so newly published advisories and yanks surface without requiring a push. - CI action pinning: All GitHub Actions are pinned by commit SHA (not tags), preventing tag-rewriting attacks on upstream actions.
- Conservative dependency policy: New dependencies require justification and are reviewed for maintenance status and security track record (see
CONTRIBUTING.md). - Build-time integrity: The Windows Npcap SDK download in
build.rsis verified against a hardcoded SHA256 checksum. - Code signing: macOS releases are signed with an Apple Developer certificate and notarized.
- Checksum verification: All packaging workflows (Homebrew, Chocolatey, AUR) calculate and double-verify SHA256 checksums before publishing.
cargo install rustnetfetches the latest compatible versions from crates.io and does not useCargo.lock. Users building from source should verify the source tarball checksum.- Build scripts (
build.rs) and proc-macros execute arbitrary code at compile time. While all current dependencies are well-established crates, this is an inherent risk of the Rust build model.
For production environments:
- Audit logging of who runs RustNet with packet capture privileges
- Network monitoring policies and compliance with data protection regulations
- User access reviews for privileged network access
- Automated capability management via configuration management systems
Please report security vulnerabilities via GitHub Issues or contact the maintainers directly.