NixDaemon

NixOS pentest workstation as one flake — IceBreaker's successor
git clone https://git.daemon-sec.xyz/NixDaemon.git
Log | Files | Refs | README

default.nix (10186B)


      1 # modules/hosts/generic/default.nix — NixDaemon for any machine: a PC, a
      2 # laptop, or a VM, x86_64 or aarch64.
      3 #
      4 #   sudo nixos-install --flake .#nixdaemon           (from the installer)
      5 #   sudo nixos-rebuild switch --flake .#nixdaemon    (afterwards)
      6 #   nh os switch -H nixdaemon                        (the same, with a diff)
      7 #
      8 # Everything you change lives in two files beside this one:
      9 #   _settings.nix               user, hostname, arch, boot mode, VM, desktop, toolkit
     10 #   _hardware-configuration.nix your disks, from nixos-generate-config
     11 #
     12 # This host is deliberately separate from modules/hosts/laptop (the author's
     13 # machine). It shares the toolkit (modules/features/pentest), the Niri desktop
     14 # and the self-contained home modules, and none of the personal parts: no
     15 # sops secrets, no private dotfiles checkout, no GPG/SSH identity, no fan or
     16 # NVIDIA workarounds. It builds without anyone's keys.
     17 #
     18 # More than one machine? `self.lib.mkHost` takes a settings set, so a second
     19 # host is one line:
     20 #   flake.nixosConfigurations.vm = self.lib.mkHost (import ./_settings.nix // { hostName = "vm"; vm = "qemu"; });
     21 # (it also needs its own hardware configuration — see mkHost's `hardware`.)
     22 { self, inputs, ... }:
     23 {
     24   flake.lib.mkHost =
     25     settings:
     26     inputs.nixpkgs.lib.nixosSystem {
     27       specialArgs = {
     28         inherit inputs settings;
     29         inherit (settings) user;
     30       };
     31       modules = [
     32         (settings.hardware or ./_hardware-configuration.nix)
     33         self.nixosModules.generic
     34       ];
     35     };
     36 
     37   flake.nixosConfigurations.nixdaemon = self.lib.mkHost (import ./_settings.nix);
     38 
     39   flake.nixosModules.generic =
     40     {
     41       config,
     42       pkgs,
     43       lib,
     44       user,
     45       settings,
     46       ...
     47     }:
     48     let
     49       s = settings;
     50       niri = s.desktop == "niri";
     51       xfce = s.desktop == "xfce";
     52       i3 = s.desktop == "i3";
     53       gui = s.desktop != "none";
     54     in
     55     {
     56       imports = [
     57         inputs.home-manager.nixosModules.home-manager
     58         self.nixosModules.pentest
     59       ]
     60       # desktop-options asserts that Hyprland or Niri is on, so it is only
     61       # pulled in when Niri is the desktop.
     62       ++ lib.optionals niri [
     63         self.nixosModules.desktop-options
     64         self.nixosModules.desktop-niri
     65       ]
     66       # The X11 desktops for VMs without 3D (modules/features/desktop/).
     67       ++ lib.optional xfce self.nixosModules.desktop-xfce
     68       ++ lib.optional i3 self.nixosModules.desktop-i3;
     69 
     70       config = lib.mkMerge [
     71         {
     72           assertions = [
     73             {
     74               assertion = builtins.elem s.desktop [
     75                 "niri"
     76                 "xfce"
     77                 "i3"
     78                 "none"
     79               ];
     80               message = "_settings.nix: desktop must be \"niri\", \"xfce\", \"i3\" or \"none\" (got \"${s.desktop}\")";
     81             }
     82             {
     83               assertion = builtins.elem s.vm [
     84                 "none"
     85                 "vmware"
     86                 "virtualbox"
     87                 "qemu"
     88                 "hyperv"
     89               ];
     90               message = "_settings.nix: vm must be none, vmware, virtualbox, qemu or hyperv (got \"${s.vm}\")";
     91             }
     92             {
     93               assertion = builtins.elem s.boot [
     94                 "efi"
     95                 "bios"
     96               ];
     97               message = "_settings.nix: boot must be \"efi\" or \"bios\" (got \"${s.boot}\")";
     98             }
     99             {
    100               # apktool pulls in aapt, which Google only ships for x86_64.
    101               assertion = !(s.system == "aarch64-linux" && (s.pentest.mobile or false));
    102               message = "_settings.nix: pentest.mobile is x86_64-only (aapt); turn it off on aarch64";
    103             }
    104           ];
    105 
    106           nixpkgs.hostPlatform = lib.mkDefault s.system;
    107           nixpkgs.config.allowUnfree = true; # burpsuite, and the NVIDIA/VMware bits some machines need
    108 
    109           ##### Boot ###############################################################
    110           boot.loader =
    111             if s.boot == "efi" then
    112               {
    113                 systemd-boot.enable = true;
    114                 systemd-boot.configurationLimit = 20;
    115                 efi.canTouchEfiVariables = true;
    116               }
    117             else
    118               {
    119                 grub.enable = true;
    120                 grub.device = s.biosDevice;
    121                 grub.configurationLimit = 20;
    122               };
    123 
    124           ##### Machine ############################################################
    125           networking.hostName = s.hostName;
    126           networking.networkmanager.enable = true;
    127           time.timeZone = s.timeZone;
    128           i18n.defaultLocale = s.locale;
    129           services.xserver.xkb.layout = s.keyboard;
    130           console.useXkbConfig = true;
    131 
    132           users.users.${user} = {
    133             isNormalUser = true;
    134             extraGroups = [
    135               "networkmanager"
    136               "wheel"
    137             ]
    138             # VirtualBox shared folders appear under /media/sf_<name> for this group.
    139             ++ lib.optional (s.vm == "virtualbox" && (s.sharedFolders or false)) "vboxsf";
    140             shell = pkgs.zsh;
    141             initialPassword = s.initialPassword;
    142             openssh.authorizedKeys.keys = s.sshKeys;
    143           };
    144 
    145           programs.zsh.enable = true;
    146           programs.nix-ld.enable = true; # prebuilt binaries (uv pythons, npm, Go releases)
    147 
    148           services.openssh = lib.mkIf s.ssh {
    149             enable = true;
    150             settings.PasswordAuthentication = false;
    151             settings.PermitRootLogin = "no";
    152           };
    153 
    154           ##### Nix ################################################################
    155           nix.settings.experimental-features = [
    156             "nix-command"
    157             "flakes"
    158           ];
    159           programs.nh = {
    160             enable = true;
    161             flake = "/home/${user}/NixDaemon"; # where docs/install.md clones it
    162             clean = {
    163               enable = true;
    164               dates = "weekly";
    165               extraArgs = "--keep 5 --keep-since 14d";
    166             };
    167           };
    168           environment.systemPackages = with pkgs; [
    169             git
    170             nvd
    171             nix-output-monitor
    172             pciutils
    173             usbutils
    174           ];
    175 
    176           ##### Hypervisor guest tools #############################################
    177           virtualisation.vmware.guest.enable = s.vm == "vmware";
    178           virtualisation.virtualbox.guest.enable = s.vm == "virtualbox";
    179           virtualisation.hypervGuest.enable = s.vm == "hyperv";
    180           services.qemuGuest.enable = s.vm == "qemu";
    181           services.spice-vdagentd.enable = s.vm == "qemu"; # clipboard + resize in virt-manager/UTM
    182 
    183           # VMware: open-vm-tools above gives time sync, clean shutdown, and —
    184           # under X11 (xfce, i3) — clipboard and automatic resize. The SVGA
    185           # adapter's 3D needs Mesa's vmwgfx driver, hence graphics on.
    186           hardware.graphics.enable = lib.mkIf (s.vm == "vmware" && gui) true;
    187           # Host folders shared in VMware's settings, at /mnt/hgfs/<name>.
    188           fileSystems."/mnt/hgfs" = lib.mkIf (s.vm == "vmware" && (s.sharedFolders or false)) {
    189             device = ".host:/";
    190             fsType = "fuse./run/current-system/sw/bin/vmhgfs-fuse";
    191             options = [ "umask=22" "uid=1000" "gid=100" "allow_other" "auto_unmount" "defaults" "nofail" "x-systemd.automount" ];
    192           };
    193 
    194           ##### Desktop ############################################################
    195 
    196           services.greetd = lib.mkIf niri {
    197             enable = true;
    198             useTextGreeter = true;
    199             settings.default_session.command = lib.concatStringsSep " " [
    200               "${pkgs.tuigreet}/bin/tuigreet"
    201               "--time"
    202               "--remember"
    203               "--remember-session"
    204               "--asterisks"
    205               "--sessions ${config.services.displayManager.sessionData.desktops}/share/wayland-sessions"
    206             ];
    207           };
    208 
    209 
    210           security.polkit.enable = lib.mkIf gui true;
    211           services.udisks2.enable = lib.mkIf gui true;
    212           services.gnome.gnome-keyring.enable = lib.mkIf gui true;
    213           programs.dconf.enable = lib.mkIf gui true;
    214           services.pulseaudio.enable = false;
    215           security.rtkit.enable = lib.mkIf gui true;
    216           services.pipewire = lib.mkIf gui {
    217             enable = true;
    218             alsa.enable = true;
    219             pulse.enable = true;
    220           };
    221 
    222           environment.sessionVariables = lib.mkIf niri {
    223             ELECTRON_OZONE_PLATFORM_HINT = "auto";
    224             NIXOS_OZONE_WL = "1";
    225           };
    226 
    227           fonts.packages = with pkgs; [
    228             nerd-fonts.jetbrains-mono # the terminal font (kitty, below)
    229             noto-fonts
    230             noto-fonts-color-emoji
    231           ];
    232 
    233           ##### Toolkit ############################################################
    234           daemon.pentest = {
    235             enable = true;
    236             # BloodHound CE's pinned neo4j is published for x86_64 only.
    237             bloodhound.enable = lib.mkDefault (s.system == "x86_64-linux");
    238           }
    239           // lib.mapAttrs (_: on: { enable = on; }) s.pentest;
    240 
    241           ##### Home ###############################################################
    242           home-manager = {
    243             useGlobalPkgs = true;
    244             useUserPackages = true;
    245             backupFileExtension = "hm-bak";
    246             extraSpecialArgs = { inherit inputs user; };
    247             users.${user} = self.homeModules.generic;
    248           };
    249 
    250           system.stateVersion = "26.05";
    251         }
    252 
    253         # optionalAttrs, not mkIf: daemon.desktop is only declared when Niri
    254         # imports desktop-options above, and mkIf still needs the option to exist.
    255         (lib.optionalAttrs niri {
    256           daemon.desktop = {
    257             hyprland.enable = false; # Hyprland's home side needs the author's dotfiles
    258             niri.enable = true;
    259               # The neutral desktop (no author location/wallpaper/font), with
    260               # this machine's keyboard layout.
    261               niri.package = self.legacyPackages.${s.system}.mkNiri {
    262                 noctaliaPkg = self.packages.${s.system}.noctalia;
    263                 xkb.layout = s.keyboard;
    264               };
    265           };
    266         })
    267       ];
    268     };
    269 }