Jailed nix shell (was: Proper way to override shell binary in "nix shell")

Below is a working solution. It wraps zsh with bubblewrap and uses a combinator to inject creation of python virtual env along with activation. Virtualenv is created in tmpfs in /env dir so it does not persist after shell is closed. I used no-new-session combinator to allow fzf to do it’s TUI things to terminal.

Now the only option I’m missing it to pass all stuff from parent nix store to jail to avoid fixing myriad of issues like missing grep :smiley: EDIT: added /run/current-system/sw/bin ro-bind and added this to $PATH and now jailed shell has access to all host’s software.

It will run with nix run :smiley: so the workflow will look like this:

  • clone some sus git repo like github.com/sus-dude/free-dc-privesc-dude-i-swear
  • copy the flake (or gen from template)
  • nix run
  • pip install -r totally_legit_deps.txt
  • profit
{
  description = "A very basic jail flake";

  inputs = {
    nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable";
    jail-nix.url = "sourcehut:~alexdavid/jail.nix";
  };

  outputs =
    {
      self,
      nixpkgs,
      jail-nix,
      ...
    }@inputs:
    let
      user = "myuser";
      system = "x86_64-linux";
      pkgs = import nixpkgs { inherit system; };

      jail = jail-nix.lib.init pkgs;

      python3-env = pkgs.python3.withPackages (py: [
        # any packages from nixox is we want them outside of venv
        py.virtualenv
      ]);

      pentest-env = pkgs.symlinkJoin {
        name = "pentest-env";
        paths = [
          python3-env
          pkgs.zsh
          pkgs.zsh-completions
          pkgs.openssl # hate this crap, always makes me miserable :D
          pkgs.coreutils
          pkgs.gcc # often needed for pip compiling
          pkgs.fzf
          # pkgs.eza and so one
        ];
      };

      jailed-shell = jail "zsh" "${pentest-env}/bin/zsh" [
        jail.combinators.network
        jail.combinators.mount-cwd
        # next two entries expose all software from outside of the container
        (jail.combinators.ro-bind "/run/current-system/sw/bin" "/run/current-system/sw/bin")
        (jail.combinators.add-path "/run/current-system/sw/bin")
        # make zsh work with my config
        (jail.combinators.add-path "${pentest-env}/bin")
        (jail.combinators.readonly "/home/${user}/.zsh")
        (jail.combinators.readonly "/home/${user}/.zshenv")
        (jail.combinators.readonly "/etc/os-release")
        (jail.combinators.readonly "/usr/bin")
        (jail.combinators.ro-bind "${pkgs.fzf}/share/fzf" "/usr/share/fzf")
        # tmpfs for temporary virtualenv
        (jail.combinators.tmpfs "/env")
        # needed for fzf to work
        (jail.combinators.no-new-session)
        # wrapper that initializes virtualenv prior to running shell
        (jail.combinators.wrap-entry (entry: ''
          echo "Launching jail..."
          virtualenv /env
          export VIRTUAL_ENV=/env
          export PATH="/env/bin:$PATH"
          exec ${entry}
        ''))
      ];
    in
    {
      apps.${system}.default = {
        type = "app";
        program = "${jailed-shell}/bin/zsh";
      };
    };
}
1 Like