Annoncing Shix, tailored development shells made with Nix

Hello there !
I’m happy to announce Shix, my personal project aiming to create tailored development shells with Nix.

I was frustrating with the nix shell and nix develop features and wanted more, so I set up a little project that allows to easily create a shell which has:

  • A totally different look (theming of PS1, tmux)
  • Custom scripts and package configuration
  • Custom commands at shell startup / exit
  • Empty $HOME directory, with only the symlinks you want
  • Some custom behaviour (erase $HOME between each start)
  • A add_pkgs script that adds a new package to your shell, and remembers it for the next start

The whole thing is made as easy and as clear as possible.
Here is an example of a shell which is also used as a template when creating a new shell
You can test it without any installation by running

nix run github:litchipi/shix#shix -- remote github:litchipi/shix example

Please mind that for now it’s in the state of a personal project, but I’d love to get feedbacks, contributions, advices, etc …

For a more concrete example, here is the setup of shell I use for my occasional hacking sessions on HackTheBox:

pkgs:
let
  ps1tool = import ../tools/ps1.nix pkgs;
  colorstool = import ../tools/colors.nix pkgs;
  tmuxtool = import ../tools/tmux.nix pkgs;

  hostctl = "${pkgs.hostctl}/bin/hostctl";
  target_host = "target.pwn";
in rec {
  name = "H4x0R";

  colors = colorstool.create_palette {
    primary = "#B0403E";
    secondary = "#959595";
    tertiary = "#7575FF";
    highlight = "#30A01E";
    active = "#E0E0E0";
    inactive = "#802020";
  };

  tmux = {
    enable = true;
    configs_files = [ ../configs/tmux.conf ];
    theme_overwrite = vars: {
      "window-status-current-format" = tmuxtool.tmuxfmts [
        { txt = " "; bg = "default"; fg = colors.highlight; add="nobold,noitalics"; }
        { txt = "#W"; fg = colors.primary; add="bold"; }
        { txt = " "; fg = colors.highlight; add="nobold"; }
      ];
    };
    vars_overwrite = {
      status.right = {
        length= 40;
        mid = tmuxtool.statusbar.mem_usage "-h -t" "Total";
        left = "#(cat $HOME/.target && echo ' x' || echo 'No target defined')";
      };
      status.left = {
        length = 100;
        left = "#I:#P";
        mid = tmuxtool.statusbar.iface;
        right = tmuxtool.statusbar.int_ip;
      };
    };
  };

  shell = {
    ps1 = with colorstool; ps1tool.mkPs1 [
      { style = style.bold; color = colors.primary; text = name; }
      { style = style.italic; color = colors.secondary; text = "\\w"; }
      (ps1tool.mkGitPs1 { color = colors.tertiary; left = "<"; right = ">"; })
      { style = style.bold; color = colors.highlight; text = ">>"; }
    ];

    scripts = with colorstool; {
      set_target = ''
        echo -n "$1" > $HOME/.target
        export TARGET="$1"

        if grep "haxxor" /etc/hosts 1>/dev/null 2>/dev/null; then
          sudo ${hostctl} remove haxxor -q
        fi
        sudo ${hostctl} add domains haxxor ${target_host} -q --ip "$1"

        echo "[*] Target set to $TARGET, now accessible as \"${target_host}\""
        mkdir -p $HOME/data/$TARGET
        old_data=$(readlink $HOME/data)
        rm -f $HOME/data
        ln -s $old_data/$TARGET $HOME/data
      '';

      readnotes = ''
        touch $HOME/data/notes
        tail +1f $HOME/data/notes
      '';

      note = ''
        echo -e "$@\n" >> $HOME/data/notes
        echo -e "${style.bold}${ansi colors.primary}Saved${reset}"
      '';

      httpserver = "python3 -m http.server $@";
      nmap = "grc nmap $@";
      ipinfo = "curl ipinfo.io/$1";
      emailinfo = "curl emailrep.io/$1";

      watchtargetcx = ''
        if [ $# -eq 1 ]; then
          NBSEC=$1
        else
          NBSEC=1
        fi
        watch -n $NBSEC 'ping -c 1 -W $NBSEC $TARGET'
      '';

      htbconnect = "sudo openvpn ${../data/lab_litchipi.ovpn}";
    };

    bashInitExtra = ''
      if [ -f $HOME/.target ]; then
        export TARGET="$(cat $HOME/.target)"
      fi
    '';
  };

  initScript = ''
    if [ -f $HOME/.target ]; then
      set_target() {
        ${shell.scripts.set_target}
      }
      set_target $(cat $HOME/.target)
    fi
  '';

  exitScript = ''
    if grep "haxxor" /etc/hosts 1>/dev/null 2>/dev/null; then
      echo "[*] Removing hostctl profile ..."
      sudo ${hostctl} remove haxxor -q
    fi
  '';

  dirs = {
    paths.home = "$HOME/work/infosec/workspace";
    paths.data = "$HOME/work/infosec/data";

    symLinks = {
      direct = {
        seclists = builtins.fetchTarball {
          url = "https://github.com/danielmiessler/SecLists/archive/refs/tags/2022.2.tar.gz";
          sha256 = "0nv3prwhbagkjip2p63n0m7483ms3f6wwj8m3iwq92jqnnr0abjy";
        };
        "tools/custom" = "$HOME/dev/infosec/";
      };

      dirs_inside = {
        "tools/" = "$HOME/work/infosec/tools/";
      };
    };
  };

  packages = with pkgs; [
    nmap
    feroxbuster
    tcpdump
    wireshark-cli
    binwalk
    exploitdb
    rustcat
    mtr
    whatweb
    whois
    hash-identifier
    john
    metasploit
    python310
    grc
    curl
    openvpn
    chisel

    python310Packages.pip
    virtualenv
    pdftk
    zip unzip
  ];
}
3 Likes
Hosted by Flying Circus.