On Linux today, sandboxing is uneven. Filesystem access is reasonably well contained through bind mounts, while D-Bus is handled by xdg-dbus-proxy. Networking, however, remains largely unfiltered. Once an application is running inside a sandbox, it can typically initiate outbound connections to any destination, including devices on the local network, which may cause privacy concerns.

Referencing others

Other operating systems approach this problem differently. Apple introduced local network permission controls in WWDC20, requiring explicit user approval before an application can access local network addresses. Google’s platforms generally permit unrestricted outbound networking. Windows focuses primarily on limiting inbound connections, leaving outbound traffic mostly unconstrained.

None of these approaches translate cleanly to Linux sandboxing as it exists today. We found that Portable as a sandbox engine can and should do better.

Design principals

Portable, the sandboxing engine behind moeOS, is designed around strict isolation with minimal compromise. The goal is not to approximate security, but to enforce it while preserving functionality. Other sandbox engines, compromise too much on security, and we believe that it is our responsibility to push security and privacy measures further for the ecosystem. Performance matters too, routing packets through userspace for filtering is not an option.

Given these constraints, we decided to implement this on the package level, i.e. packager defines the rules for networking in advance. The Portable daemon communicates with a privileged daemon to enforce efficient network firewall.

Introducing netsock

At the time there is no reliable, high performance and mature userspace software for network isolation. We also hold the belief of the UNIX philosophy to only do one thing. Rather than extending an existing tool beyond its intended scope, we introduced a small, focused component: netsock into the plot.

In a sentence, netsock is a daemon that securely manipulates the modern Linux kernel packet classification framework on demand.

The program is written in Go, pulls in minimal dependencies, and is locked down to only have network administration privileges. It responds dynamically to sandbox engines’ requests, builds on the foundation of nftables to do kernel-level packet filtering based on user-defined rule sets. Being robust yet vital is key to netsock. It operates on pre-defined rules to prevent broken rules generated by sandboxes, validates peer UID to prevent malicious signals from spoofing identity, and dynamically resolves incoming signal as actual IPs to block.

netsock operates based on Linux control groups v2, aka the unified version of cgroups. Portable utilises this kernel feature already, and it takes measures to prevent application from escaping from their own control groups via unprivileged namespaces and bind mounting. This secure mechanism of holding sandboxed processes is key to netsock. It allows the daemon to apply restrictions on the very specific app, preventing any of the processes to escape, while not affecting other apps.

How does it work?

Portable now accepts a new configuration key networkDeny, of which may hold a space separated list of denied destinations. This is parsed and send to netsock via Unix Domain Socket while portable is starting. netsock will automatically oversee the control group and add rules on demand, then it informs the peer about the status of operation. Sandbox engines should have a fail-open state before it is fully stable. Rules remain effective throughout the entire lifecycle of the app, and every app has at most one effective rule set.


The above concludes how Portable version 14 and netsock work together to properly enforce network firewall on a per-app basis. This approach does not try to solve networking policy for Linux as a whole, nor does it attempt to retrofit user prompts or coarse system-wide switches onto an ecosystem that was never built for them. It treats network access as another resource that should be explicitly scoped, enforced by the kernel, and tied to the lifetime of an application. There is still room for iteration. Tooling will mature, defaults will evolve, and mistakes will surface. But closing the gap between filesystem isolation and network isolation is long overdue. We believe that taking advantage of the isolation mechanisms available today is only viable path to a secure sandbox.

P.S. For now, UDP port 53 is always allowed to avoid breaking DNS.