Getting started

Installation

Flakes

To install nono.nix using flakes, add it to your flake inputs:

# flake.nix
{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  inputs.nono-nix.url = "gitlab:jhebden/nono.nix";
  outputs = { nixpkgs, nono-nix, ... }: {
    # ...
  };
}

nono.nix requires pkgs.nono, which is available in nixpkgs unstable (Linux 5.13+ for Landlock, 6.7+ for per-port network rules).

callPackage

To install nono.nix using a different dependency manager like niv, you can use pkgs.callPackage:

let
  sources = import ./nix/sources.nix;
  pkgs = import sources.nixpkgs {};
  nono-nix = pkgs.callPackage sources."nono.nix" {};
  nono = nono-nix.nono pkgs;
in
  # ...

Initializing

The core of nono.nix is a nono function that wraps derivations in a kernel-enforced sandbox. To set it up, initialize the library with an instantiated nixpkgs.

Call nono-nix.lib.nono for a basic setup, or nono-nix.lib.extend for advanced configuration.

Basic configuration

pkgs = import nixpkgs { system = "x86_64-linux"; };
nono = nono-nix.lib.nono pkgs;

Advanced configuration

Use nono-nix.lib.extend to set additional options:

pkgs = import nixpkgs { system = "x86_64-linux"; };
nono = nono-nix.lib.extend {
  inherit pkgs;

  # Other configuration options here...
};

For a full list of options see Advanced Configuration.


Usage

Once you have an initialized nono function, it takes the following positional arguments:

  • name: (String) — the nix store name of the wrapper script.
  • executable to wrap: (String or Derivation) — the program to sandbox. Can be a derivation like pkgs.hello, or a string path like "${pkgs.hello}/bin/hello". A derivation is passed to lib.getExe.
  • permissions: (List of Permissions) — controls what the sandbox can access. By default the wrapped program runs with very limited permissions; use combinators here to grant what it needs. If this argument is a function, nono.combinators is passed in and the return value is used.

Overlays

You can sandbox packages in a nixpkgs overlay using nono-nix.lib.mkOverlay. This takes an attribute set with prev, final, and packages (a function from combinators to {packageName = permissions}).

All Advanced Configuration options can be passed into mkOverlay as well.

Example:

nonoOverlay = final: prev: nono-nix.lib.mkOverlay {
  inherit final prev;
  packages = combinators: with combinators; {
    nodejs = [ network ];
    firefox = [ network gui gpu ];
  };
};
pkgs = import nixpkgs {
  system = "x86_64-linux";
  overlays = [ nonoOverlay ];
};

Each overlaid package gets .jailed and .unjailed variants:

{
  environment.systemPackages = [
    pkgs.firefox          # sandboxed (default)
    pkgs.firefox.jailed   # sandboxed (explicit)
    pkgs.firefox.unjailed # original
  ];
}

Examples

Provide nono.nix as a module arg in a NixOS configuration

# flake.nix
{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  inputs.nono-nix.url = "gitlab:jhebden/nono.nix";
  outputs = { nixpkgs, nono-nix, ... }: {
    nixosConfigurations.my-host = nixpkgs.lib.nixosSystem {
      system = "x86_64-linux";
      modules = [
        ({ pkgs, ... }: { _module.args.nono = nono-nix.lib.nono pkgs; })

        ({ nono, pkgs, ... }: {
          environment.systemPackages = [
            (nono "sandboxed-hello" pkgs.hello [])
          ];
        })
      ];
    };
  };
}

Sandbox a flake package

# flake.nix
{
  inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
  inputs.nono-nix.url = "gitlab:jhebden/nono.nix";
  outputs = { nixpkgs, nono-nix, ... }: {
    packages.x86_64-linux =
      let
        pkgs = import nixpkgs { system = "x86_64-linux"; };
        nono = nono-nix.lib.nono pkgs;
      in {
        my-sandboxed-package = nono "sandboxed-hello" pkgs.hello [];
      };
  };
}