Skip to content
QiiksPublic

About

Per-app network scoping for Linux: cgroup v2 process identity and nftables filtering or routing, with no kernel module.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

netscope

Per-application network scoping for Linux: select an app once, launch it normally, and only that app's traffic is filtered or routed.

Alpha. Linux only. Kernel 5.13 or newer with cgroup v2 and nftables.

Install

Arch Linux / CachyOS (after the AUR package is published):

yay -S netscope-bin
sudo systemctl enable --now netscoped
sudo usermod -aG netscope "$USER"

Tarball and cargo install --git channels are documented in docs/install.md. Cargo installs binaries only; the systemd unit, sysusers file, and config come from the tarball or AUR. Full documentation: https://qiiks.github.io/netscope/.

Use

sudo netscope --uid "$(id -u)" add blocked --exe /usr/bin/curl --filter drop
curl --max-time 3 https://example.com   # fails
sudo netscope remove blocked

Route a selected app through a tunnel interface:

sudo netscope --uid "$(id -u)" add routed --exe /usr/bin/curl --route tun0

Scope a Windows game launched through Wine or Proton by selecting the .exe:

sudo netscope --uid "$(id -u)" add game --wine /games/Game/Game.exe --filter drop

Use --route tun0 in place of --filter drop to route the same target.

Or attach a running process by name, with no install path and no wrapper:

sudo netscope --uid "$(id -u)" add game --process Overwatch.exe --route tun0

--process matches a process's comm or an argv basename, so Overwatch, Overwatch.exe, and overwatch.exe are the same target. A running process is attached on the next poll and its existing sockets are marked, so a restart is not required. A new Wine/Proton launch is caught before the loader runs, so the game's first socket is already scoped.

netscope status, netscope list, netscope doctor, and netscope support-bundle cover inspection and troubleshooting. No wrapper is required: native ELF apps are caught at exec, Windows PEs launched through Wine/Proton are caught when Wine opens the PE, name targets hold the Wine loader, and Steam apps are matched by app id.

Two programs can share one process without fighting over its cgroup:

sudo netscope --uid "$(id -u)" open --consumer exitlink --name overwatch --process Overwatch.exe --route tun0
sudo netscope --uid "$(id -u)" open --consumer dropship --process Overwatch.exe --filter allow \
  --deny-cidr 34.142.0.0/16

Both consumers get the same profile and cgroup. Each owns its own policy; the last netscope close removes the scope. See shared scopes.

Desktop GUI

netscope-gui is a calm desktop inspector for the daemon: overview rates and links, scopes with their claims and permissions, an Add scope flow, and a Doctor screen whose fix commands copy to the clipboard. It opens peer holds by default, so closing the window releases them.

cargo build --release -p netscope-gui
./target/release/netscope-gui

The window reports a down daemon, retries every two seconds, and shows the exact systemctl start netscoped or usermod -aG netscope hint. --demo and headless --snap scenes exist for screenshots; normal launches never read demonstration data. See the GUI documentation.

Developer SDK & Protocol

Two programs can share one process through the netscope-client crate (cargo add netscope-client), or speak the daemon protocol directly from any language.

use netscope_client::{Client, Hold};

fn main() -> Result<(), netscope_client::ClientError> {
    let mut client = Client::connect()?;
    let scope = client.open_process("exitlink", "Overwatch.exe", 1000, Hold::Peer)?;
    client.close_scope(&scope.profile, "exitlink")
}

The daemon accepts newline-delimited JSON on /run/netscope/control.sock. The full envelope, every operation, hold semantics, and copy-pasteable Python, Go, C, and shell clients are in Daemon protocol & SDK.

How it works

  1. fanotify holds an exec of a selected native inode — or the first open of a selected Windows PE by a Wine/Proton loader — until the daemon has moved the process into /sys/fs/cgroup/netscope/<profile>.
  2. nftables matches socket cgroupv2 or the profile's socket mark and applies a filter or a routing mark.
  3. Route profiles set a dedicated routing table with a blackhole fallback. If the configured interface is down, traffic is dropped rather than leaking through the main table.

A name target also holds the Wine/Proton loader binaries and matches the launcher ancestry of the loader, so a new game launch is caught before its first socket. A polling fallback attaches already-running processes, Wine/Proton processes matched by command line, and Steam children. When a process is attached late, the daemon marks its existing sockets with the profile mark; the nftables rules match either the cgroup or that mark, so those sockets change route on the next packet without a restart.

Security

netscoped runs as root and accepts control messages only over a root-owned Unix socket in the netscope group. It loads no kernel module and opens no network listener. See SECURITY.md and docs/security.md.

Support

sudo netscope support-bundle

The bundle is written to /tmp, is root-only, and includes a review-before-upload warning. Attach it to a bug report after checking it.

License

Dual-licensed under MIT or Apache-2.0, at your option.

About

Per-app network scoping for Linux: cgroup v2 process identity and nftables filtering or routing, with no kernel module.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages