From 936f84775814b6af84e03e2ca015732d7c7ceebb Mon Sep 17 00:00:00 2001 From: Denys Pavlov Date: Tue, 31 Dec 2024 13:07:18 -0500 Subject: [PATCH] feat(nix): init watson system --- flake.lock | 227 ++++++++++++++++++++++++++++++- flake.nix | 14 +- modules/impermanence/default.nix | 83 +++++++++++ modules/secureboot/default.nix | 25 ++++ systems/watson/default.nix | 8 ++ systems/watson/disko.nix | 112 +++++++++++++++ 6 files changed, 464 insertions(+), 5 deletions(-) create mode 100644 modules/impermanence/default.nix create mode 100644 modules/secureboot/default.nix create mode 100644 systems/watson/default.nix create mode 100644 systems/watson/disko.nix diff --git a/flake.lock b/flake.lock index 2190cc98..e4e84cb4 100644 --- a/flake.lock +++ b/flake.lock @@ -16,6 +16,21 @@ "type": "github" } }, + "crane": { + "locked": { + "lastModified": 1731098351, + "narHash": "sha256-HQkYvKvaLQqNa10KEFGgWHfMAbWBfFp+4cAgkut+NNE=", + "owner": "ipetkov", + "repo": "crane", + "rev": "ef80ead953c1b28316cc3f8613904edc2eb90c28", + "type": "github" + }, + "original": { + "owner": "ipetkov", + "repo": "crane", + "type": "github" + } + }, "declarative-cachix": { "locked": { "lastModified": 1698062126, @@ -31,6 +46,27 @@ "type": "github" } }, + "disko": { + "inputs": { + "nixpkgs": [ + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1734088167, + "narHash": "sha256-snPBgTqwn3FPZVdFC5yt7Bnk3squim1vZOZ8CObWykk=", + "owner": "nix-community", + "repo": "disko", + "rev": "65a441502c9382d41ada1adbc9bd31d6c9b00fe2", + "type": "github" + }, + "original": { + "owner": "nix-community", + "ref": "latest", + "repo": "disko", + "type": "github" + } + }, "emacs-overlay": { "inputs": { "nixpkgs": "nixpkgs", @@ -99,6 +135,22 @@ } }, "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_3": { "flake": false, "locked": { "lastModified": 1733328505, @@ -114,7 +166,7 @@ "type": "github" } }, - "flake-compat_3": { + "flake-compat_4": { "locked": { "lastModified": 1717312683, "narHash": "sha256-FrlieJH50AuvagamEvWMIE6D2OAnERuDboFDYAED/dE=", @@ -130,6 +182,27 @@ } }, "flake-parts": { + "inputs": { + "nixpkgs-lib": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1730504689, + "narHash": "sha256-hgmguH29K2fvs9szpq2r3pz2/8cJd2LPS+b4tfNFCwE=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "506278e768c2a08bec68eb62932193e341f55c90", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-parts_2": { "inputs": { "nixpkgs-lib": [ "nixpkgs-wayland", @@ -223,6 +296,28 @@ "type": "github" } }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "pre-commit-hooks-nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, "home-manager": { "inputs": { "nixpkgs": [ @@ -243,6 +338,46 @@ "type": "github" } }, + "impermanence": { + "locked": { + "lastModified": 1734945620, + "narHash": "sha256-olIfsfJK4/GFmPH8mXMmBDAkzVQ1TWJmeGT3wBGfQPY=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "d000479f4f41390ff7cf9204979660ad5dd16176", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, + "lanzaboote": { + "inputs": { + "crane": "crane", + "flake-compat": "flake-compat_2", + "flake-parts": "flake-parts", + "nixpkgs": [ + "nixpkgs" + ], + "pre-commit-hooks-nix": "pre-commit-hooks-nix", + "rust-overlay": "rust-overlay" + }, + "locked": { + "lastModified": 1734994463, + "narHash": "sha256-S9MgfQjNt4J3I7obdLOVY23h+Yl/hnyibwGfOl+1uOE=", + "owner": "nix-community", + "repo": "lanzaboote", + "rev": "93e6f0d77548be8757c11ebda5c4235ef4f3bc67", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "lanzaboote", + "type": "github" + } + }, "lib-aggregate": { "inputs": { "flake-utils": "flake-utils_3", @@ -264,7 +399,7 @@ }, "nix-eval-jobs": { "inputs": { - "flake-parts": "flake-parts", + "flake-parts": "flake-parts_2", "nix-github-actions": "nix-github-actions", "nixpkgs": "nixpkgs_3", "treefmt-nix": "treefmt-nix" @@ -325,9 +460,25 @@ "type": "github" } }, + "nixos-hardware": { + "locked": { + "lastModified": 1735388221, + "narHash": "sha256-e5IOgjQf0SZcFCEV/gMGrsI0gCJyqOKShBQU0iiM3Kg=", + "owner": "NixOS", + "repo": "nixos-hardware", + "rev": "7c674c6734f61157e321db595dbfcd8523e04e19", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "master", + "repo": "nixos-hardware", + "type": "github" + } + }, "nixos-wsl": { "inputs": { - "flake-compat": "flake-compat_2", + "flake-compat": "flake-compat_3", "flake-utils": "flake-utils_2", "nixpkgs": [ "nixpkgs" @@ -394,9 +545,25 @@ "type": "github" } }, + "nixpkgs-stable_2": { + "locked": { + "lastModified": 1730741070, + "narHash": "sha256-edm8WG19kWozJ/GqyYx2VjW99EdhjKwbY3ZwdlPAAlo=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "d063c1dd113c91ab27959ba540c0d9753409edf3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.05", + "repo": "nixpkgs", + "type": "github" + } + }, "nixpkgs-wayland": { "inputs": { - "flake-compat": "flake-compat_3", + "flake-compat": "flake-compat_4", "lib-aggregate": "lib-aggregate", "nix-eval-jobs": "nix-eval-jobs", "nixpkgs": [ @@ -479,17 +646,48 @@ "type": "github" } }, + "pre-commit-hooks-nix": { + "inputs": { + "flake-compat": [ + "lanzaboote", + "flake-compat" + ], + "gitignore": "gitignore", + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable_2" + }, + "locked": { + "lastModified": 1731363552, + "narHash": "sha256-vFta1uHnD29VUY4HJOO/D6p6rxyObnf+InnSMT4jlMU=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "cd1af27aa85026ac759d5d3fccf650abe7e1bbf0", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, "root": { "inputs": { "alacritty-theme": "alacritty-theme", "declarative-cachix": "declarative-cachix", + "disko": "disko", "emacs-overlay": "emacs-overlay", "fish-docker-compose": "fish-docker-compose", "fish-ssh-agent": "fish-ssh-agent", "flake-compat": "flake-compat", "flake-utils": "flake-utils", "home-manager": "home-manager", + "impermanence": "impermanence", + "lanzaboote": "lanzaboote", "nix-index-database": "nix-index-database", + "nixos-hardware": "nixos-hardware", "nixos-wsl": "nixos-wsl", "nixpkgs": "nixpkgs_2", "nixpkgs-wayland": "nixpkgs-wayland", @@ -499,6 +697,27 @@ "wsl-open": "wsl-open" } }, + "rust-overlay": { + "inputs": { + "nixpkgs": [ + "lanzaboote", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1731897198, + "narHash": "sha256-Ou7vLETSKwmE/HRQz4cImXXJBr/k9gp4J4z/PF8LzTE=", + "owner": "oxalica", + "repo": "rust-overlay", + "rev": "0be641045af6d8666c11c2c40e45ffc9667839b5", + "type": "github" + }, + "original": { + "owner": "oxalica", + "repo": "rust-overlay", + "type": "github" + } + }, "systems": { "locked": { "lastModified": 1681028828, diff --git a/flake.nix b/flake.nix index d9bb4311..8a6d0218 100644 --- a/flake.nix +++ b/flake.nix @@ -4,6 +4,12 @@ inputs = { flake-utils.url = "github:numtide/flake-utils"; nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + disko.url = "github:nix-community/disko/latest"; + disko.inputs.nixpkgs.follows = "nixpkgs"; + impermanence.url = "github:nix-community/impermanence"; + lanzaboote.url = "github:nix-community/lanzaboote"; + lanzaboote.inputs.nixpkgs.follows = "nixpkgs"; + nixos-hardware.url = "github:NixOS/nixos-hardware/master"; home-manager.url = "github:nix-community/home-manager"; home-manager.inputs.nixpkgs.follows = "nixpkgs"; nixpkgs-wayland.url = "github:nix-community/nixpkgs-wayland"; @@ -15,7 +21,6 @@ vscode-server.url = "github:nix-community/nixos-vscode-server"; nix-index-database.url = "github:nix-community/nix-index-database"; nix-index-database.inputs.nixpkgs.follows = "nixpkgs"; - # zellij.url = "github:a-kenji/zellij-nix"; flake-compat = { url = "github:edolstra/flake-compat"; @@ -101,6 +106,13 @@ ); in { tormund = mkSystem [./systems/tormund]; + watson = mkSystem [ + inputs.disko.nixosModules.disko + inputs.impermanence.nixosModules.impermanence + inputs.lanzaboote.nixosModules.lanzaboote + inputs.nixos-hardware.nixosModules.lenovo-thinkpad-t14-amd-gen4 + ./systems/watson + ]; nixos = mkSystem [ {system.stateVersion = "23.05";} ./systems/wsl-nixos diff --git a/modules/impermanence/default.nix b/modules/impermanence/default.nix new file mode 100644 index 00000000..c7dfed30 --- /dev/null +++ b/modules/impermanence/default.nix @@ -0,0 +1,83 @@ +# based on https://github.com/matthewpi/nixos-config/blob/cffedc488740767402615c8790b82bcdff0f3509/modules/persistence/default.nix +{lib, ...}: { + # Setup a service that will automatically rollback the root subvolume to a fresh state. + boot.initrd.systemd.services.rollback = let + luksName = "crypted"; + rootSubvolume = "rootfs"; + in { + description = "Rollback BTRFS root subvolume to a fresh state"; + wantedBy = ["initrd.target"]; + before = ["sysroot.mount"]; + after = ["systemd-cryptsetup@${luksName}.service"]; + unitConfig.DefaultDependencies = "no"; + serviceConfig.Type = "oneshot"; + + script = '' + mkdir -p /btrfs + mount -t btrfs -o compress=zstd,noatime,nodev,noexec,nosuid,discard=async /dev/mapper/${luksName} /btrfs + + echo 'Cleaning subvolumes...' + btrfs subvolume list -o /btrfs/${rootSubvolume} | cut -f9 -d ' ' | + while read subvolume; do + echo 'Deleting /'"$subvolume"' subvolume...' + btrfs subvolume delete /btrfs/"$subvolume" + done && + echo 'Deleting /${rootSubvolume} subvolume...' && + btrfs subvolume delete /btrfs/${rootSubvolume} + + echo 'Restoring blank /${rootSubvolume} subvolume...' + btrfs subvolume snapshot /btrfs/${rootSubvolume}-blank /btrfs/${rootSubvolume} + + umount /btrfs + rm -d /btrfs + ''; + }; + + # Disable sudo lectures, as they will be shown after every reboot otherwise. + security.sudo.extraConfig = '' + # rollback results in sudo lectures after each reboot + Defaults lecture = never + ''; + + fileSystems."/persist".neededForBoot = lib.mkDefault true; + + # Persistence + environment.persistence."/persist" = { + directories = [ + { + directory = "/etc/nixos"; + mode = "0755"; + } + { + directory = "/var/lib/nixos"; + mode = "0755"; + } + + { + directory = "/etc/secureboot"; + mode = "0700"; + } + + # for systemd sandboxes + { + directory = "/var/lib/private"; + mode = "0700"; + } + { + directory = "/var/cache/private"; + mode = "0700"; + } + + { + directory = "/root"; + mode = "0700"; + } + ]; + + files = [ + "/etc/machine-id" + ]; + + hideMounts = true; + }; +} diff --git a/modules/secureboot/default.nix b/modules/secureboot/default.nix new file mode 100644 index 00000000..62602c76 --- /dev/null +++ b/modules/secureboot/default.nix @@ -0,0 +1,25 @@ +{ + config, + pkgs, + lib, + ... +}: { + # Make tpm kernel modules available in the initrd + boot.initrd.availableKernelModules = ["tpm-crb" "tpm-tis"]; + + # Install fido2, sbctl, and tpm packages + environment.systemPackages = with pkgs; [libfido2 sbctl tpm2-tools tpm2-tss]; + + # Ensure bootspec is enabled + boot.bootspec.enable = lib.mkDefault true; + + # Lanzaboote replaces the systemd-boot module + boot.loader.systemd-boot.enable = lib.mkForce (!config.boot.lanzaboote.enable); + + # Configure lanzaboote for secureboot + boot.lanzaboote = { + enable = lib.mkDefault true; + configurationLimit = 5; + pkiBundle = lib.mkDefault "/persist/etc/secureboot"; + }; +} diff --git a/systems/watson/default.nix b/systems/watson/default.nix new file mode 100644 index 00000000..fe32935d --- /dev/null +++ b/systems/watson/default.nix @@ -0,0 +1,8 @@ +{...}: { + imports = [ + ./disko.nix + ../../modules/impermanence + ../common.nix + ]; + system.stateVersion = "24.11"; +} diff --git a/systems/watson/disko.nix b/systems/watson/disko.nix new file mode 100644 index 00000000..f8348f77 --- /dev/null +++ b/systems/watson/disko.nix @@ -0,0 +1,112 @@ +# based on: https://github.com/matthewpi/nixos-config/blob/cffedc488740767402615c8790b82bcdff0f3509/systems/nxb/disko.nix +{ + lib, + config, + ... +}: let + luksName = "crypted"; + rootSubvolume = "rootfs"; +in { + disko.devices = { + disk = { + main = { + type = "disk"; + device = "/dev/nvme0n1"; + content = { + type = "gpt"; + partitions = { + ESP = { + size = "2G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; + mountOptions = ["umask=0077"]; + }; + }; + luks = { + size = "100%"; + content = { + type = "luks"; + name = luksName; + passwordFile = "/tmp/luks.key"; # Interactive + settings = { + allowDiscards = true; + bypassWorkqueues = true; + }; + content = let + defaultOptions = [ + # Disable access times on files. + "noatime" + # TODO: document + "nodev" + # Prevent SUID binaries from being used. + # + # Any binaries that need SUID privileges will be created by Nix + # and put into `/run/wrappers/bin` which has SUID permissions. + "nosuid" + # Use asynchronous discard (https://wiki.archlinux.org/title/Btrfs#SSD_TRIM) + "discard" + ]; + in { + type = "btrfs"; + extraArgs = [ + "-L" + "nixos" + "-f" + ]; + # rollback root for impermanence + postCreateHook = '' + MNTPOINT="$(mktemp -d)" + SRCMNT='/dev/mapper/${luksName}' + + mount -t btrfs -o 'compress=zstd,noexec,noatime,nodev,nosuid,discard' "$SRCMNT" "$MNTPOINT" + trap 'umount $MNTPOINT; rmdir $MNTPOINT' EXIT + + btrfs subvolume snapshot -r "$MNTPOINT"/${rootSubvolume} "$MNTPOINT"/${rootSubvolume}-blank + ''; + subvolumes = { + "@${rootSubvolume}" = { + mountpoint = "/"; + mountOptions = ["compress=zstd" "noexec"] ++ defaultOptions; + }; + "@nix" = { + mountpoint = "/nix"; + mountOptions = ["compress=zstd"] ++ defaultOptions; + }; + "@persist" = { + mountpoint = "/persist"; + mountOptions = ["compress=no" "noexec"] ++ defaultOptions; + }; + "@persist/home" = { + mountpoint = "/persist/home"; + mountOptions = ["compress=no"] ++ defaultOptions; + }; + "@persist/git" = { + mountpoint = "/persist/home/git"; + mountOptions = ["compress=zstd"] ++ defaultOptions; + }; + "@var/log" = { + mountpoint = "/var/log"; + mountOptions = ["compress=zstd" "noexec"] ++ defaultOptions; + }; + "@swap" = { + mountpoint = "/.swap"; + swap.swapfile.size = "40G"; # 32G RAM + overhead + }; + }; + }; + }; + }; + }; + }; + }; + }; + }; + fileSystems = { + "/persist".neededForBoot = lib.mkIf config.impermanence.enable true; + "/var/log".neededForBoot = true; + "/.swap".neededForBoot = true; + }; +}