Nixwrap is a command line utility to easily wrap processes in a sandbox preventing them from accessing your system. Nixwrap can hinder access to environment variables, files, devices and the network. Under the hood, Nixwrap is based on Bubblewrap which in turn uses Linux Namespaces.
The goal of Nixwrap is to make sandboxing easy to use for common use cases, reducing the barrier to entry. While it will not provide perfect protection against any untrusted code, it does add protection for simple common threats.
You need to run
npm installon a project, but you cannot trust all its dependencies.
To run npm install only with write access to the current working directory and network access, simply do:
wrap -n npm installYou need to run a GUI application, but you want to limit access to your filesystem and environment variables.
To run vscodium using nix3-run with network and display access, only writing to $HOME/some-dir:
wrap -n -d -p -w $HOME/some-dir nix run nixpkgs#vscodiumYou need to run a
pythonscript that has access to your audio hardware.
Run a python script with Pulse Audio and Pipewire access, but not sharing the current working directory:
wrap -a -p python my-tool.pyYou want to run your opencode agent with access to the current working directory.
Use wrap.lib.${system}.mkWrap to build a wrapped launcher, allow current working directory access, opencode config and cache directory access as well as network access.
{
inputs.nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
inputs.flake-utils.url = "github:numtide/flake-utils";
inputs.wrap.url = "github:rti/nixwrap";
outputs = { nixpkgs, flake-utils, wrap, ... }:
flake-utils.lib.eachDefaultSystem (system:
let
pkgs = nixpkgs.legacyPackages.${system};
in {
packages.default = wrap.lib.${system}.mkWrap {
name = "opencode";
package = pkgs.opencode;
shareCwd = true;
capabilities.network = true;
mounts = {
readOnly = [ "~/.config/opencode" ];
writable = [ "~/.local/share/opencode" ];
};
};
});
}From the project directory, run:
nix run .By default, the imperative wrap command will:
- ✅ Prevent network access. (Use
-nto allow.) - ✅ Prevent access to Wayland and X. (Use
-d(desktop) to allow.) - ✅ Prevent camera access. (Use
-cto allow.) - ✅ Prevent audio access. (Use
-ato allow.) - ✅ Prevent DBus socket access. (Use
-bto allow.) - ✅ Prevent access to user name information. (Use
-uto allow.) - ❗ Allow write access to the current working directory. (Use
-pto prevent.) - ❗ Allow read only access to all paths in
$NIX_PROFILES. - ❗ Allow read only access to nix store and config paths.
- ❗ Allow read only access to common bin and lib paths.
- ❗ Allow access to a set of common environment variables.
wrap [OPTIONS] [-- BWRAP_ARGS] PROGRAM_TO_WRAP_WITH_ARGS
-d Allow Desktop access, Wayland, X11, and rendering hardware.
-n Allow Network access.
-a Allow Audio access.
-c Allow Camera access.
-b Allow DBus access.
-u Allow System user information access.
-e VAR Allow env var VAR access.
-r PATH Mount PATH into sandbox in read-only mode.
-w PATH Mount PATH into sandbox in read write mode.
-H PATH Set and writable-mount the sandbox HOME path.
Mounts -r/-w paths from host HOME at matching sandbox HOME.
-v Verbose output for debugging.
-p Do not share current working directory. By default wrap will share
the current working directory as a write mount and cd into it
before running the program. With this option, wrap will not share
the directory and will start in HOME instead. Takes precedence over -f.
-f Force share current working directory. By default wrap will share
the current working directory as a write mount and cd into it only
if it is not the caller's home and does not match these patterns:
^/$, ^/home$, ^/boot, ^/etc, ^/proc, ^/run, ^/sys, ^/var
This option bypasses that check, but does not override -p.
-m Manual unsharing. By default wrap unshares ipc, net, pid, and uts
and tries to unshare (continue on failues) user and cgroup
namespaces. With this option, wrap does not automatically unshare
any namespaces. Use together with bwrap --unshare-* options
(man bwrap(1)) to unshare manually.
The lib.mkWrap function takes the following arguments.
| Argument | Description |
|---|---|
name |
Name of the generated launcher. Required. |
package |
Package containing the executable. Required. |
executable |
Executable under the package's bin directory. Defaults to package.pname. |
arguments |
Fixed arguments before those supplied at launch. Defaults to []. |
home |
Host directory to create if needed and mount as the writable sandbox home. Omit to keep the default isolated home. |
shareCwd |
Share and start in the launch-time working directory, subject to wrap's protected-path rules. Defaults to false. |
mounts.readOnly |
Host paths to mount read-only. Defaults to []. |
mounts.writable |
Host paths to mount writable. Defaults to []. |
capabilities |
Boolean attributes: network, desktop, audio, camera, dbus, userInfo. All default to disabled. |
environment |
Attribute set of variables: a value sets the variable; null passes through the caller's value. Defaults to {}. Set home, not environment.HOME, to change the sandbox home. |
Paths in home, mounts.readOnly, and mounts.writable may begin with ~/; the launcher resolves that prefix against the caller's HOME at runtime. The rest of the path stays literal: shell variables, globs, and commands are not expanded.
When home is set, mounts beneath the caller's HOME are relocated beneath the sandbox home.
Nixwrap is at the moment tested exclusively on NixOS, even though the concept should work in any distribution that ships a current kernel.
wrap is licensed under the MIT License. See the LICENSE file for more details.
