offline

command module
v1.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 8 Imported by: 0

README ΒΆ

πŸ“΄ offline

code-quality security License OpenSSF Scorecard CI carbon Release

offline is a Linux sandbox wrapper that executes another program with its network access completely isolated: user/net/mount/PID/IPC/UTS namespaces, capability dropping, and a seccomp filter blocking network syscalls.

Linux only, and it isolates the network β€” nothing else. The wrapped program keeps your filesystem and your environment; what it cannot do is reach anything off the machine. Unprivileged user namespaces must be enabled on the host.

./offline curl https://example.com

The wrapped process runs inside a restricted environment where it cannot access the internet.

How it works

One binary, two stages. The first cannot isolate itself β€” unshare(2) makes the child the first process inside a new PID namespace β€” so it re-executes itself and passes the flags through the environment:

  offline [flags] <program> [args...]
      β”‚
      β”‚  stage 1 β€” on the host
      β”œβ”€ parse flags
      └─ clone(CLONE_NEWUSER|NEWNET|NEWNS|NEWPID|NEWIPC|NEWUTS)
             uid/gid mapped 1:1, setgroups off
             _AIRGAP_STAGE=1 in the environment
             β”‚
             β–Ό
         re-exec of the same binary
             β”‚
             β”‚  stage 2 β€” inside the namespaces
             β”œβ”€ prctl(PR_SET_NO_NEW_PRIVS)      no setuid escape hatch
             β”œβ”€ --keep-loopback ? ifup "lo"     before the filter, it needs socket(2)
             β”œβ”€ PR_CAPBSET_DROP 0..63           bounding set emptied
             β”œβ”€ PR_CAP_AMBIENT_CLEAR_ALL        ambient set emptied
             β”œβ”€ seccomp: allow by default, deny
             β”‚     socket(AF_INET|AF_INET6|AF_PACKET)
             β”‚     connect bind listen accept accept4 send* recv*
             └─ exec <program> [args...]

Three layers, so a gap in one is not a gap in the sandbox: the namespace leaves nothing to route to, the empty capability set leaves nothing to reconfigure it with, and the filter refuses the syscalls anyway.

Features

  • Network namespace isolation, so there is nothing to reach: no physical interfaces, no routes, no default gateway, no DNS connectivity, no inbound connections. offline ip route prints nothing.
  • User namespace isolation with a single-entry uid/gid map β€” the caller is root inside and nobody outside, which is what buys the other namespaces without any privilege on the host. Caveat: it needs unprivileged user namespaces enabled; some hardened distros and Docker profiles turn them off.
  • Mount namespace isolation, so mounts made inside the sandbox never reach the host. Caveat: the host filesystem itself is still visible and writable β€” this tool takes away the network, not the disk.
  • PID and IPC namespace isolation: host processes are invisible, and IPC objects are not shared.
  • UTS namespace isolation, so the hostname is the sandbox's own.
  • Capability dropping, bounding and ambient set, so no capability can be regained by exec'ing a file that carries one.
  • Seccomp filtering as the last layer, allow-by-default and deny-by -exception: the goal is to stop networking, not to enumerate an allowlist the wrapped program would trip over. Denials are EPERM.
  • --keep-loopback for the local case β€” 127.0.0.1 works, external addresses still fail at the routing layer. Caveat: with loopback up the connect/bind family has to be allowed, so the filter is thinner here; AF_PACKET stays blocked either way.
  • --log-external routes denials through a userspace notifier and prints them. Caveat: it logs the syscall name and pid, not the destination address.

Requirements

Linux kernel with support for user, network, PID, mount, and IPC namespaces, plus seccomp.

Ubuntu/Debian dependencies:

sudo apt install pkg-config libseccomp-dev

Go dependencies are pinned in go.mod (github.com/seccomp/libseccomp-golang, golang.org/x/sys) and resolved automatically by go build/go mod tidy.

Install

One-liner (clones/updates a checkout under ~/.local/share/offline and runs make install):

curl -fsSL https://raw.githubusercontent.com/fabiocicerchia/offline/main/install.sh | bash

Build from source:

go build -o offline offline.go
# or
make build
make install                       # drops the binary in ~/.local/bin (on your PATH)
make install BINDIR=/usr/local/bin # ...or anywhere else

Usage

$ offline --help
usage: offline [--keep-loopback] [--log-external] <program> [args...]

Runs <program> with its network access fully isolated: private user, network,
mount, PID, IPC and UTS namespaces, an emptied capability set, and a seccomp
filter refusing the network syscalls.

Flags:
  -keep-loopback
    	keep the loopback interface (127.0.0.1) up and reachable
  -log-external
    	log blocked network syscalls to stderr

A real run β€” the wrapped curl gets no further than the filter:

$ offline --log-external curl -s -m 3 https://example.com
offline: blocked connect (pid 9)
offline: blocked connect (pid 9)
offline: blocked socket (pid 9)
offline: blocked socket (pid 9)
exit status 7
offline curl https://example.com   # fails: no network in the sandbox
offline bash                       # an offline shell

More in docs/getting-started.md.

Common errors

Package libseccomp was not found in the pkg-config search path. The cgo binding needs the development headers, not just the runtime library: sudo apt install pkg-config libseccomp-dev (or libseccomp-devel on Fedora/RHEL).

fork/exec /usr/local/bin/offline: operation not permitted The host refuses unprivileged user namespaces. Check sysctl kernel.unprivileged_userns_clone and, on Ubuntu, sysctl kernel.apparmor_restrict_unprivileged_userns; a container runtime can also block the clone flags through its own seccomp profile.

exit status N Not an offline failure β€” the wrapped program exited non-zero and offline relays that on stderr. curl returning 7 ("failed to connect") is the sandbox working.

Documentation

Full docs live in docs/ (mkdocs). Runnable examples live in examples/.

References

The design is a straight reading of the manual pages; they are the reference, not this file.

Release cycle

Semantic Versioning. Releases are cut by release-please from Conventional Commits, and the tag is the only source of truth for a version.

  • Major β€” a change to what is isolated, i.e. to the security guarantee.
  • Minor β€” new flags or behaviour that leaves the guarantee intact.
  • Patch β€” fixes; only the latest minor gets them.

Every change to the namespace flags, the capability sweep or the seccomp filter is security-sensitive by definition and is treated as such, whatever the version number says.

Contributing

See CONTRIBUTING.md. By participating you agree to the Code of Conduct.

Security

Found a vulnerability, especially a network-isolation bypass? See SECURITY.md β€” please don't open a public issue.

Support

Need help implementing this? Get in touch.

License

MIT Β© Fabio Cicerchia

Documentation ΒΆ

Overview ΒΆ

offline β€” run a program with its network access fully isolated.

A small, self-contained sandbox, small enough to read in one sitting. One binary, two stages:

default            build the namespace set and re-exec self inside it
_AIRGAP_STAGE=1    (runs INSIDE the namespaces) drop privileges, install
                   the seccomp filter, then exec the wrapped program

The stages exist because unshare(2) semantics make the CHILD, not the caller, the first process inside a new PID namespace β€” there is no way to apply the full set to the running process and still have it behave.

Isolation is layered, so a gap in one layer is not a gap in the sandbox:

namespaces     no interfaces, no routes, no host PIDs/IPC/mounts
capabilities   the bounding and ambient sets are emptied
seccomp        socket(AF_INET/AF_INET6/AF_PACKET) and the connect family

Build: go build -o offline offline.go Usage: offline [--keep-loopback] [--log-external] <program> [args...]

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL