nsproxy-rs is a Linux command-line tool that forces applications to use a specified SOCKS5 or HTTP proxy, using network namespaces for transparent traffic interception.
It creates a TUN device inside an isolated network namespace, runs the target application there, and forwards all TCP traffic through the configured proxy server. DNS queries are intercepted locally using a fake-IP scheme (similar to proxychains-ng), which prevents DNS leaks by sending domain names directly to the proxy for remote resolution.
- Fork a child process into a new network namespace (with user namespace fallback for unprivileged users).
- Configure a TUN device as the sole network interface inside the namespace.
- The parent process reads raw IP packets from the TUN device and processes them through a user-space TCP/IP stack (smoltcp).
- DNS queries (UDP port 53) are intercepted and answered with fake IPs from the 198.18.0.0/15 pool. A bidirectional mapping between domain names and fake IPs is maintained.
- When the application connects to a fake IP, the original domain name is recovered from the mapping and a CONNECT request is sent to the proxy using the domain name (ATYP=DOMAIN for SOCKS5, Host header for HTTP).
- Data is shuttled bidirectionally between the smoltcp TCP socket and the upstream proxy connection.
- Published host TCP ports are accepted by the parent and connected directly to the namespace-side TUN address through smoltcp.
- Supports SOCKS5 and HTTP CONNECT proxy protocols.
- Supports proxy authentication (username/password).
- Transparent to the application -- works with statically linked binaries.
- No root privilege required (uses unprivileged user namespaces).
- No DNS leaks -- domain names are resolved by the proxy server.
- Supports publishing host TCP ports to services inside the namespace.
- Does not affect other processes on the system.
cargo build --releaseThe binary will be at target/release/nsproxy.
Run unit tests with cargo test. To also run the Linux integration tests on a
host with user, network, and mount namespace support, /dev/net/tun, and
Python 3, run cargo test -- --ignored --test-threads=1.
nsproxy [OPTIONS] <COMMAND>...
Options:
-x, --proxy <PROXY> Default route
-r, --rule <RULE> Routing rule (repeatable); see "Routing rules" below
-b, --bind <SRC:DST> Bind-mount a file in the namespace (repeatable)
-p, --publish <SPEC> Publish a host TCP port to the namespace (repeatable)
-v, --verbose Enable log output; repeat for trace-level logs
-h, --help Print help
Proxy format:
direct
socks5://[user:pass@]host:port
http://[user:pass@]host:port
nsproxy -x socks5://127.0.0.1:1080 curl http://example.com
nsproxy -x http://user:pass@proxy:8080 wget http://example.com
nsproxy -x direct -r domain:example.com=socks5://127.0.0.1:1080 curl http://example.com
nsproxy -x direct -b ./custom.conf:/etc/example.conf cat /etc/example.conf
nsproxy -x direct -p 8080:80 web-server --listen 0.0.0.0:80
nsproxy -x direct -p 127.0.0.1:8443:443/tcp web-server --listen 0.0.0.0:443
nsproxy -x socks5://127.0.0.1:1080 ssh user@remote-host
nsproxy -x socks5://127.0.0.1:1080 -r cidr:10.0.0.0/8=direct curl http://internalProxy server hostnames are resolved using the host's resolver. The 32-second connection timeout covers proxy hostname resolution, TCP connection setup, and the complete SOCKS5 or HTTP CONNECT handshake.
SIGTERM, SIGINT, and SIGHUP are forwarded to the managed command tree. The reaper waits for descendants to exit and forces termination after a two-second grace period if necessary. Each subsequent termination signal is forwarded as received without extending the original grace period.
Connections that match a --rule (-r) rule use the route on the right side
instead of the default selected by -x. The flag is repeatable, and both a
match and a route are required:
ip:<address>=<proxy>
cidr:<network>/<prefix>=<proxy>
domain:<host>=<proxy>
domain-regex:<regex>=<proxy>
For example:
-r ip:1.1.1.1=socks5://127.0.0.1:1081
-r cidr:10.0.0.0/8=direct
-r domain:example.com=http://127.0.0.1:8080
IP and CIDR rules use longest-prefix matching; the first rule wins when two matching prefixes have the same length. Domain and domain-regex rules use the first matching rule in command-line order.
Notes:
ip/cidrrules match the destination IP. They take effect when the application connects to a numeric address (no DNS lookup involved).domain/domain-regexrules match the host name extracted from the intercepted DNS query. Matching is case-insensitive fordomain.- A
directdomain route uses the host's resolver, so it opts in to host-side DNS resolution for that domain.
Use repeatable --bind (-b) options before the command to expose custom
files inside the command's mount namespace:
nsproxy -x direct -b ./config.toml:/etc/myapp/config.toml myappFor example, create a symbolic link for the US Eastern time zone and mount the
link over the existing /etc/localtime when running date:
ln -s /usr/share/zoneinfo/America/New_York ./new-york-localtime
nsproxy -x direct -b ./new-york-localtime:/etc/localtime dateBoth paths may be relative to the directory where nsproxy is started. Each
path must name either a regular file or a symbolic link; dangling symbolic
links are allowed. Symbolic links themselves are mounted without following
their targets. A relative source link keeps its original link text, which is
then resolved relative to the destination's parent directory. Bind mounts are
read-write; directories and Docker-style mode suffixes such as :ro are not
supported. Duplicate targets and the internal DNS mount targets
/etc/resolv.conf and /etc/nsswitch.conf are rejected.
Use repeatable --publish (-p) options before the command to expose TCP
services running inside the network namespace:
[HOST_IP:]HOST_PORT:NS_PORT[/tcp]
For example:
nsproxy -x direct -p 127.0.0.1:8080:80/tcp server --listen 0.0.0.0:80HOST_IP defaults to 0.0.0.0, which exposes the port on every host IPv4
interface. The /tcp suffix is optional. UDP, IPv6 addresses, random host
ports, and port ranges are not supported.
The namespace service must listen on 172.23.255.255 (the TUN address) or
0.0.0.0; a service bound only to namespace loopback (127.0.0.1) cannot be
reached. Published connections bypass proxy and routing rules. The service sees
the source as the TUN gateway 172.23.255.254, not as the external client's
original address.
-
Linux kernel with user namespace support.
-
File bind mounts (
-b/--bind) require Linux >= 5.2. Other features do not use this newer mount API. -
On Ubuntu >= 23.10, you may need to disable the AppArmor restriction:
sudo sysctl -w kernel.apparmor_restrict_unprivileged_userns=0
- TCP only (UDP forwarding is not implemented).
- IPv4 only.
- Listening programs are reachable from outside only through explicitly published TCP ports.
- Connections to loopback addresses refer to the namespace, not the host.
sudoandsuwill not work inside the namespace (only one UID is mapped).
This project is a Rust reimplementation inspired by nsproxy by NaLan ZeYu. The original C implementation uses lwIP as its user-space TCP/IP stack and forwards DNS to a real server; this version uses smoltcp and a fake-DNS approach instead.
scproxy is a related tool that uses seccomp user notifications to redirect connections while keeping TCP processing in the kernel. Both projects support SOCKS5 and HTTP CONNECT proxies and work with statically linked programs.
| Consideration | scproxy | nsproxy-rs |
|---|---|---|
| Performance | Higher forwarding performance with native kernel TCP | More overhead from TUN and user-space TCP/IP processing |
| Linux compatibility | Requires Linux 5.14+ and the required seccomp/pidfd features | Broader support for older kernels with namespaces and TUN |
| Isolation | Inherits existing namespaces and uses host networking | Stronger isolation through separate network and mount namespaces |
Choose scproxy when performance is the priority and your system meets its
kernel and permission requirements. Choose nsproxy-rs when compatibility
with older kernels or namespace isolation matters more, or when you need file
bind mounts and explicit TCP port publishing. nsproxy-rs requires namespace
support and /dev/net/tun; its optional file bind mounts require Linux 5.2+.
See each project's requirements and limitations to choose the best fit for your
environment.