diff --git a/README.md b/README.md index f2242a0c..f85fc855 100644 --- a/README.md +++ b/README.md @@ -107,7 +107,7 @@ make i3-debug # make hypr-debug ``` -To deploy this flake from NixOS's official ISO image(purest installation method), please refer to [ryan4yin/nix-config/nixos-install](https://github.com/ryan4yin/nix-config/tree/nixos-install) +To deploy this flake from NixOS's official ISO image(purest installation method), please refer to [./hosts/idols/ai/nixos-installer/](./hosts/idols/ai/nixos-installer/) ## How to create & managage VM from this flake? diff --git a/hosts/idols/ai/hardware-configuration.nix b/hosts/idols/ai/hardware-configuration.nix index 58d0b38c..583c6172 100644 --- a/hosts/idols/ai/hardware-configuration.nix +++ b/hosts/idols/ai/hardware-configuration.nix @@ -26,6 +26,8 @@ boot.kernelModules = ["kvm-intel"]; boot.kernelParams = ["nvidia.NVreg_PreserveVideoMemoryAllocations=1"]; boot.extraModulePackages = []; + # clear /tmp on boot to get a stateless /tmp directory. + boot.tmp.cleanOnBoot = true; # Enable binfmt emulation of aarch64-linux, this is required for cross compilation. boot.binfmt.emulatedSystems = ["aarch64-linux" "riscv64-linux"]; @@ -82,6 +84,12 @@ options = ["subvol=@snapshots" "compress-force=zstd:1"]; }; + fileSystems."/tmp" = { + device = "/dev/disk/by-uuid/1167076c-dee1-486c-83c1-4b1af37555cd"; + fsType = "btrfs"; + options = ["subvol=@tmp" "compress-force=zstd:1"]; + }; + # mount swap subvolume in readonly mode. fileSystems."/swap" = { device = "/dev/disk/by-uuid/1167076c-dee1-486c-83c1-4b1af37555cd"; diff --git a/hosts/idols/ai/nixos-installer/.gitignore b/hosts/idols/ai/nixos-installer/.gitignore new file mode 100644 index 00000000..29f4f2fa --- /dev/null +++ b/hosts/idols/ai/nixos-installer/.gitignore @@ -0,0 +1,3 @@ +result +result/ +.DS_Store diff --git a/hosts/idols/ai/nixos-installer/README.md b/hosts/idols/ai/nixos-installer/README.md new file mode 100644 index 00000000..90c03ada --- /dev/null +++ b/hosts/idols/ai/nixos-installer/README.md @@ -0,0 +1,243 @@ +# Nix Environment for Deploying my NixOS Configuration + +This flake prepares a Nix environment for setting my desktop [../hosts/idols/ai](../hosts/idols/ai/)(on main flake) up on a new machine. + +TODOs: + +- [ ] dcalarative disk partitioning with [disko](https://github.com/nix-community/disko) + +## Why an extra flake is needed? + +The configuration of the main flake, [../flake.nix](../flake.nix), is heavy, and it takes time to debug & deploy. +This simplified flake is tiny and can be deployed very quickly, it helps me to: + +1. Adjust & verify my `hardware-configuration.nix` modification quickly before deploying the `main` flake. +2. Test some new filesystem related features on a NixOS virtual machine, such as impermanence, Secure Boot, TMP2, Encryption, etc. + +## Steps to Deploying the `main` flake + +First, create a USB install medium from NixOS's official ISO image and boot from it. + +### 1. Encrypting with LUKS(everything except ESP) + +> https://nixos.org/manual/nixos/stable/#sec-installation-manual-partitioning + +> [dm-crypt/Encrypting an entire system - Arch Wiki](https://wiki.archlinux.org/title/Dm-crypt/Encrypting_an_entire_system) + +> [Encrypted /boot - GRUB2 - Arch Wiki](https://wiki.archlinux.org/title/GRUB#Encrypted_/boot) + +> [Frequently asked questions (FAQ) - cryptsetup](https://gitlab.com/cryptsetup/cryptsetup/wikis/FrequentlyAskedQuestions) + +Securing a root file system is where dm-crypt excels, feature and performance-wise. +An encrypted root file system protects everything on the system, it make the system a black box to the attacker. + +1. The EFI system partition(ESP) must be left unencrypted, and is mounted at `/boot` + 1. Since the UEFI firmware can only load boot loaders from unencrypted partitions. +2. Secure Boot is enabled, everything in ESP is signed. +3. The BTRFS file system with subvolumes is used for the root partition, and the swap area is a swapfile on a dedicated BTRFS subvolume, thus the swap area is also encrypted. + +And the boot flow is: + +1. The UEFI firmware loads the boot loader from the ESP(`/boot`). +2. The boot loader loads the kernel and initrd from the ESP(`/boot`). +3. **The initrd prompts for the passphrase to unlock the root partition**. +4. The initrd unlocks the root partition and mounts it at `/`. +5. The initrd continues the boot process, and hands over the control to the kernel. + +Partitioning the disk: + +```bash +# Create a GPT partition table +parted /dev/nvme0n1 -- mklabel gpt + +# NixOS by default uses the ESP (EFI system partition) as its /boot partition +# Create a 512MB EFI system partition +parted /dev/nvme0n1 -- mkpart ESP fat32 2MB 629MB + +# set the boot flag on the ESP partition +# Format: +# set partition flag state +parted /dev/nvme0n1 -- set 1 esp on + +# Create the root partition using the rest of the disk +# Format: +# mkpart [part-type name fs-type] start end +parted /dev/nvme0n1 -- mkpart primary 630MB 100% + +# show disk status +lsblk +``` + +Encrypting the root partition: + +```bash +lsblk +# show cryptsetup's compiled in defualts +cryptsetup --help + +# encrypt the root partition with luks2 and argon2id, will prompt for a passphrase, which will be used to unlock the partition. +cryptsetup luksFormat --type luks2 --pbkdf argon2id --cipher aes-xts-plain64 --key-size 512 --hash sha512 /dev/nvme0n1p2 + +# show status +cryptsetup luksDump /dev/nvme0n1p2 + +# open(unlock) the device with the passphrase you just set +cryptsetup luksOpen /dev/nvme0n1p2 crypted-nixos + +# show disk status +lsblk +``` + +Formatting the root partition: + +```bash +mkfs.fat -F 32 -n ESP /dev/nvme0n1p1 +# format the root partition with btrfs and label it +mkfs.btrfs -L crypted-nixos /dev/mapper/crypted-nixos + +# mount the root partition and create subvolumes +mount /dev/mapper/crypted-nixos /mnt +btrfs subvolume create /mnt/@nix +btrfs subvolume create /mnt/@tmp +btrfs subvolume create /mnt/@swap +btrfs subvolume create /mnt/@persistent +btrfs subvolume create /mnt/@snapshots +umount /mnt + +# Use tmpfs for root - stateless +mount -t tmpfs tmpfs /mnt + +# Remount the root partition with the subvolumes you just created +# +# Enable zstd compression to: +# 1. Reduce the read/write operations, which helps to: +# 1. Extend the life of the SSD. +# 2. improve the performance of disks with low IOPS / RW throughput, such as HDD and SATA SSD. +# 2. Save the disk space. +mkdir /mnt/{nix,tmp,swap,persistent,snapshots,boot} +mount -o compress-force=zstd:1,noatime,subvol=@nix /dev/mapper/crypted-nixos /mnt/nix +mount -o compress-force=zstd:1,subvol=@tmp /dev/mapper/crypted-nixos /mnt/tmp +mount -o subvol=@swap /dev/mapper/crypted-nixos /mnt/swap +mount -o compress-force=zstd:1,noatime,subvol=@persistent /dev/mapper/crypted-nixos /mnt/persistent +mount -o compress-force=zstd:1,noatime,subvol=@snapshots /dev/mapper/crypted-nixos /mnt/snapshots +mount /dev/nvme0n1p1 /mnt/boot + +# create a swapfile on btrfs file system +# This command will disable CoW / compression on the swap subvolume and then create a swapfile. +# because the linux kernel requires that swapfile must not be compressed or have copy-on-write(CoW) enabled. +btrfs filesystem mkswapfile --size 96g --uuid clear /mnt/swap/swapfile + +# check whether the swap subvolume has CoW disabled +# the output of `lsattr` for the swap subvolume should be: +# ---------------C------ /swap/swapfile +# if not, delete the swapfile, and rerun the commands above. +lsattr /mnt/swap + +# mount the swapfile as swap area +swapon swapfile +``` + +Now, the disk status should be: + +```bash +# show disk status +$ lsblk +nvme0n1 259:0 0 1.8T 0 disk +├─nvme0n1p1 259:2 0 600M 0 part /mnt/boot +└─nvme0n1p2 259:3 0 1.8T 0 part + └─crypted-nixos 254:0 0 1.8T 0 crypt /mnt/swap + /mnt/persistent + /mnt/snapshots + /mnt/nix + /mnt/tmp + +# show swap status +$ swapon -s +Filename Type Size Used Priority +/swap/swapfile file 100663292 0 -2 +``` + +### 2. Generating the NixOS Configuration and Installing NixOS + +Clone this repository: + +```bash +# enter an shell with git/vim/ssh-agent/gnumake available +nix-shell -p git vim gnumake + +# clone this repository +git clone https://github.com/ryan4yin/nix-config.git +``` + +Then, generate the NixOS configuration: + +```bash +# nixos configurations +nixos-generate-config --root /mnt + +# we need to update our filesystem configs in old hardware-configuration.nix according to the generated one. +cp /etc/nixos/hardware-configuration.nix ./nix-config/hosts/idols/ai/hardware-configuration-new.nix +vim . +``` + +Then, Install NixOS: + +```bash +cd ~/nix-config + +# run this command if you're retrying to run nixos-install +rm -rf /mnt/etc + +# install nixos +# NOTE: the root password you set here will be discarded when reboot +nixos-install --root /mnt --flake .#ai --no-root-password + +# if you want to use a cache mirror, run this command instead +# replace the mirror url with your own +nixos-install --root /mnt --flake .#ai --no-root-password --option substituters "https://mirror.sjtu.edu.cn/nix-channels/store" + +# enter into the installed system, check password & users +nixos-enter + +# copy the essential files into /persistent +# otherwise the / will be cleared and data will lost +## NOTE: impermanence just create links from / to /persistent +## We need to copy files into /persistent manually!!! +mv /etc/machine-id /persistent/etc/ +mv /etc/ssh /persistent/etc/ + + +# delte the generated configuration after editing +rm -f /mnt/etc/nixos +rm ~/nix-config/hosts/idols/ai/hardware-configuration-new.nix + +# commit the changes after installing nixos successfully +git config --global user.email "ryan4yin@linux.com" +git config --global user.name "Ryan Yin" +git commit -am "feat: update hardware-configuration" + +# copy our configuration to the installed file system +cp -r ../nix-config /mnt/etc/nixos +``` + +And then reboot. + + +## Deploying the main flake's NixOS configuration + +After rebooting, we can deploy the main flake's NixOS configuration by running: + +```bash +# 1. Add the ssh key to the ssh-agent, so that nixos-rebuild can use it to pull my private git repositories. +ssh-add ~/.ssh/xxx + +sudo mv /etc/nixos ~/nix-config +chown -R ryan:ryan ~/nix-config + +cd ~/nix-config + +# deploy the configuration +make hypr +``` + +Finally, to enable secure boot, follow the instructions in [lanzaboote - Quick Start](https://github.com/nix-community/lanzaboote/blob/master/docs/QUICK_START.md) and [nix-config/ai/secure-boot.nix](https://github.com/ryan4yin/nix-config/blob/main/hosts/idols/ai/secureboot.nix) diff --git a/hosts/idols/ai/nixos-installer/configuration.nix b/hosts/idols/ai/nixos-installer/configuration.nix new file mode 100644 index 00000000..f6a6122d --- /dev/null +++ b/hosts/idols/ai/nixos-installer/configuration.nix @@ -0,0 +1,9 @@ +{...}: { + networking.hostName = "ai"; + + # Enable networking + networking.networkmanager.enable = true; + networking.defaultGateway = "192.168.5.201"; + + system.stateVersion = "23.11"; +} diff --git a/hosts/idols/ai/nixos-installer/flake.lock b/hosts/idols/ai/nixos-installer/flake.lock new file mode 100644 index 00000000..afab0785 --- /dev/null +++ b/hosts/idols/ai/nixos-installer/flake.lock @@ -0,0 +1,43 @@ +{ + "nodes": { + "impermanence": { + "locked": { + "lastModified": 1697303681, + "narHash": "sha256-caJ0rXeagaih+xTgRduYtYKL1rZ9ylh06CIrt1w5B4g=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "0f317c2e9e56550ce12323eb39302d251618f5b5", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1701389149, + "narHash": "sha256-rU1suTIEd5DGCaAXKW6yHoCfR1mnYjOXQFOaH7M23js=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "5de0b32be6e85dc1a9404c75131316e4ffbc634c", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "impermanence": "impermanence", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/hosts/idols/ai/nixos-installer/flake.nix b/hosts/idols/ai/nixos-installer/flake.nix new file mode 100644 index 00000000..5868b97a --- /dev/null +++ b/hosts/idols/ai/nixos-installer/flake.nix @@ -0,0 +1,24 @@ +{ + description = "NixOS configuration of Ryan Yin"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-23.11"; + impermanence.url = "github:nix-community/impermanence"; + }; + + outputs = inputs @ {nixpkgs, ...}: { + nixosConfigurations = { + ai = nixpkgs.lib.nixosSystem { + system = "x86_64-linux"; + specialArgs = inputs; + modules = [ + ./configuration.nix + ./system.nix + + ../hardware-configuration.nix + ../impermanence.nix + ]; + }; + }; + }; +} diff --git a/hosts/idols/ai/nixos-installer/system.nix b/hosts/idols/ai/nixos-installer/system.nix new file mode 100644 index 00000000..fca0ecd0 --- /dev/null +++ b/hosts/idols/ai/nixos-installer/system.nix @@ -0,0 +1,53 @@ +{pkgs, ...}: { + # Set your time zone. + time.timeZone = "Asia/Shanghai"; + + # Select internationalisation properties. + i18n.defaultLocale = "en_US.UTF-8"; + + i18n.extraLocaleSettings = { + LC_ADDRESS = "zh_CN.UTF-8"; + LC_IDENTIFICATION = "zh_CN.UTF-8"; + LC_MEASUREMENT = "zh_CN.UTF-8"; + LC_MONETARY = "zh_CN.UTF-8"; + LC_NAME = "zh_CN.UTF-8"; + LC_NUMERIC = "zh_CN.UTF-8"; + LC_PAPER = "zh_CN.UTF-8"; + LC_TELEPHONE = "zh_CN.UTF-8"; + LC_TIME = "zh_CN.UTF-8"; + }; + + # ssh-agent is used to pull my private secrets repo from github when depoloying my nixos config. + programs.ssh.startAgent = true; + + # List packages installed in system profile. To search, run: + # $ nix search wget + environment.systemPackages = with pkgs; [ + neovim # Do not forget to add an editor to edit configuration.nix! The Nano editor is also installed by default. + git + gnumake + wget + curl + nix-output-monitor + ]; + + users.groups = { + "ryan" = {}; + }; + + # Don't allow mutation of users outside the config. + users.mutableUsers = false; + # Define a user account. Don't forget to set a password with ‘passwd’. + users.users.ryan = { + # generated by `mkpasswd -m scrypt` + # we have to use initialHashedPassword here, if your'are using tmpfs for / + initialHashedPassword = "$7$CU..../....Sdl/JRH..9eIvZ6mE/52r.$xeR6lyvTcVVKt28Owcoc/vPOOECcYSiq1xjw/QCz2t0"; + isNormalUser = true; + description = "ryan"; + extraGroups = ["ryan" "networkmanager" "wheel"]; + }; + users.users.root.initialHashedPassword = "$7$CU..../....X6uvZYnFD.i1CqqFFNl4./$4vgqzIPyw5XBr0aCDFbY/UIRRJr7h5SMGoQ/ZvX3FP2"; + + # make ryan a trusted user so he can set custom nix substituters url(cache mirror) to speed up nixos-rebuild. + nix.settings.trusted-users = ["ryan"]; +} diff --git a/secrets/nixos.nix b/secrets/nixos.nix index d586f23c..ea734a60 100644 --- a/secrets/nixos.nix +++ b/secrets/nixos.nix @@ -45,6 +45,12 @@ mode = "0600"; owner = username; }; + + "nix-access-tokens" = { + file = "${mysecrets}/nix-access-tokens.age"; + mode = "0600"; + owner = username; + }; }; # place secrets in /etc/