Compare commits

...
Author SHA1 Message Date
aaron a5e69c043d chore: update installation guide with secure-boot setup 2026-10-03 12:47:27 +02:00
aaron b2a2e7d161 Merge remote-tracking branch 'origin/main' into feature/secure_boot 2026-10-03 11:41:27 +02:00
aaron 66a19890bd chore: remove old vm host entry since it is not used anymore (#89)
Reviewed-on: #89
Co-authored-by: aaron <aaron@0x29a.ch>
2026-10-03 11:37:40 +02:00
aaron 450559f8c6 feature: data-drives on argon and boot securty (#87)
- increase security by restricting /boot to root only
- add data-drives for encrypted drives that will get mounted through crypttab rather than the initrd

Reviewed-on: #87
Co-authored-by: aaron <aaron@0x29a.ch>
2026-10-03 10:57:34 +02:00
aaron b75f44ceab feature(lanzaboote): add support for secure-boot through lanazaboote 2026-10-03 10:39:28 +02:00
14 changed files with 235 additions and 315 deletions
+54 -2
View File
@@ -75,7 +75,7 @@ fileSystems."/" =
fileSystems."/boot" = fileSystems."/boot" =
{ device = "/dev/disk/by-label/nixboot"; { device = "/dev/disk/by-label/nixboot";
fsType = "vfat"; fsType = "vfat";
options = [ "fmask=0022" "dmask=0022" ]; options = [ "fmask=0077" "dmask=0077" ]; # only root may read /boot
}; };
fileSystems."/home" = fileSystems."/home" =
{ device = "/dev/disk/by-label/nixhome"; { device = "/dev/disk/by-label/nixhome";
@@ -102,10 +102,62 @@ nixos-install
## how to deploy the initial config ## how to deploy the initial config
- Don't forget to install the bootloader, if you changed it since `nixos-install` - Don't forget to install the bootloader, if you changed it since `nixos-install`
- The fresh install has no flakes and no git yet, a temporary shell is enough
- If the installer iso is older than the flake's nixpkgs, `--no-reexec` keeps
`nixos-rebuild` from re-executing a newer version of itself, which can fail
against the older systemd of the fresh install (only needed once)
- Set a password for the user before rebooting, `users.nix` doesn't set one
```bash ```bash
$ sudo nixos-rebuild --install-bootloader switch --flake .#host_name $ export NIX_CONFIG="experimental-features = nix-command flakes"
$ nix-shell -p git
$ sudo nixos-rebuild --install-bootloader boot --flake .#host_name --no-reexec
$ sudo passwd aaron
``` ```
## how to enable secure boot
Secure Boot uses lanzaboote with our own keys, see `modules/nixos/lanzaboote.nix`.
Do this after the initial config is deployed and boots.
1. create the keys, before the first rebuild with the lanzaboote module
```bash
# the keys end up in /var/lib/sbctl, they never leave the host
$ sudo nix run nixpkgs#sbctl -- create-keys
```
2. import `modules/nixos/lanzaboote.nix` in the host's `configuration.nix`, rebuild and check the signatures
```bash
$ sudo nixos-rebuild switch --flake .#host_name
# EFI/Linux/*.efi and the bootloader must be signed, kernels in EFI/nixos
# are not, lanzaboote checks them by hash
$ sudo sbctl verify
```
3. reboot once with Secure Boot still disabled, to make sure the new entries boot
4. in the firmware (F2), erase all Secure Boot keys, then boot nixos again
```bash
# "Setup Mode: ✗ Enabled", the ✗ only means the state is insecure
$ sudo sbctl status
```
5. enroll the keys
```bash
# only our own keys, no microsoft keys
$ sudo sbctl enroll-keys
```
- sbctl refuses if the TPM eventlog shows option ROMs (e.g. a dedicated gpu).
Don't force it, either trust exactly those with `--tpm-eventlog` or
microsoft's keys with `--microsoft`. Without either, the firmware may not
start the device, in the worst case the machine doesn't boot at all.
6. enable Secure Boot in the firmware and set a firmware password, then check
```bash
$ bootctl status | grep "Secure Boot" # enabled (user)
$ sudo sbctl status # Secure Boot: ✓ Enabled
```
- If it doesn't boot anymore, disabling Secure Boot in the firmware boots as before
- Losing the keys isn't fatal: erase the keys in the firmware and start over at step 1
## how to upgrade the system ## how to upgrade the system
```bash ```bash
$ cd /path/to/repo $ cd /path/to/repo
+1 -1
View File
@@ -10,7 +10,7 @@ My personal NixOS configurations.
├── hosts ├── hosts
│ ├── argon # desktop │ ├── argon # desktop
│ ├── neon # laptop │ ├── neon # laptop
│ └── default # test vm │ └── krypton # second laptop
│ ├── configuration.nix # host-specific settings and module imports │ ├── configuration.nix # host-specific settings and module imports
│ ├── hardware-configuration.nix # generated by nixos-generate-config │ ├── hardware-configuration.nix # generated by nixos-generate-config
│ └── niri.nix # host-specific niri settings (outputs, input) │ └── niri.nix # host-specific niri settings (outputs, input)
Generated
+99
View File
@@ -1,5 +1,36 @@
{ {
"nodes": { "nodes": {
"crane": {
"locked": {
"lastModified": 1789760033,
"narHash": "sha256-jtT4yxZpR8seYnlCMMWSSPlFN92zO6ICuZ8pJrmi86k=",
"owner": "ipetkov",
"repo": "crane",
"rev": "73b980519cefc727a5f6cc8e5c0947a2f9be6edd",
"type": "github"
},
"original": {
"owner": "ipetkov",
"repo": "crane",
"type": "github"
}
},
"flake-compat": {
"flake": false,
"locked": {
"lastModified": 1767039857,
"narHash": "sha256-vNpUSpF5Nuw8xvDLj2KCwwksIbjua2LZCqhV1LNRDns=",
"owner": "NixOS",
"repo": "flake-compat",
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
"type": "github"
},
"original": {
"owner": "NixOS",
"repo": "flake-compat",
"type": "github"
}
},
"flake-parts": { "flake-parts": {
"inputs": { "inputs": {
"nixpkgs-lib": [ "nixpkgs-lib": [
@@ -41,6 +72,30 @@
"type": "github" "type": "github"
} }
}, },
"lanzaboote": {
"inputs": {
"crane": "crane",
"nixpkgs": [
"nixpkgs"
],
"pre-commit": "pre-commit",
"rust-overlay": "rust-overlay"
},
"locked": {
"lastModified": 1790423250,
"narHash": "sha256-sr4Kfp6yaXUeyKL6n0Y8/ElcR3momIRC20HFc+LF22s=",
"owner": "nix-community",
"repo": "lanzaboote",
"rev": "c1c5edd31802d181c8aa2c71588995d93425d650",
"type": "github"
},
"original": {
"owner": "nix-community",
"ref": "v1.2.0",
"repo": "lanzaboote",
"type": "github"
}
},
"nixos-hardware": { "nixos-hardware": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -119,15 +174,59 @@
"type": "github" "type": "github"
} }
}, },
"pre-commit": {
"inputs": {
"flake-compat": "flake-compat",
"nixpkgs": [
"lanzaboote",
"nixpkgs"
]
},
"locked": {
"lastModified": 1789514889,
"narHash": "sha256-PFD1nxptCXJyJoiYO1gf/5Vv43yqGpHtMC6LUhl9/pQ=",
"owner": "cachix",
"repo": "git-hooks.nix",
"rev": "59f4ca0d063a1a3ec722c88b51a33004862e5379",
"type": "github"
},
"original": {
"owner": "cachix",
"repo": "git-hooks.nix",
"type": "github"
}
},
"root": { "root": {
"inputs": { "inputs": {
"home-manager": "home-manager", "home-manager": "home-manager",
"lanzaboote": "lanzaboote",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixvim": "nixvim", "nixvim": "nixvim",
"noctalia": "noctalia" "noctalia": "noctalia"
} }
}, },
"rust-overlay": {
"inputs": {
"nixpkgs": [
"lanzaboote",
"nixpkgs"
]
},
"locked": {
"lastModified": 1789889921,
"narHash": "sha256-aL/ogiN7qZCGeNovS1f9hX73jwYIKb4Pcq6AZm57WtY=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "1fb104a12a8667045559b2575d6d448ae2fbd99b",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": { "systems": {
"flake": false, "flake": false,
"locked": { "locked": {
+4 -1
View File
@@ -11,6 +11,10 @@
url = "github:noctalia-dev/noctalia-shell"; url = "github:noctalia-dev/noctalia-shell";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
lanzaboote = {
url = "github:nix-community/lanzaboote/v1.2.0";
inputs.nixpkgs.follows = "nixpkgs";
};
nixos-hardware = { nixos-hardware = {
url = "github:NixOS/nixos-hardware"; url = "github:NixOS/nixos-hardware";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
@@ -53,7 +57,6 @@
in in
{ {
nixosConfigurations = { nixosConfigurations = {
default = mkHost "default";
neon = mkHost "neon"; neon = mkHost "neon";
argon = mkHost "argon"; argon = mkHost "argon";
krypton = mkHost "krypton"; krypton = mkHost "krypton";
+8 -27
View File
@@ -1,17 +1,7 @@
# argon: desktop with an amd gpu and three luks-encrypted data drives. # argon: desktop with an amd gpu and three luks-encrypted data drives.
# The drives are unlocked in the initrd with keyfiles from /etc/nixos/keys.
{ lib, pkgs, ... }: { pkgs, ... }:
let
# luks name -> partition uuid, mounted at /mnt/<name>
drives = {
data1 = "dfae62cc-bad1-4879-bf9a-461bde833625";
data2 = "8312edae-9247-481b-a313-52a7f848f027";
nvmecache = "2352250e-4ebe-4f9a-bf66-0d4aaa961bd8";
};
key = name: "/etc/nixos/keys/${name}.key";
in
{ {
imports = [ imports = [
../../modules/nixos/appimage.nix ../../modules/nixos/appimage.nix
@@ -19,6 +9,7 @@ in
../../modules/nixos/audio.nix ../../modules/nixos/audio.nix
../../modules/nixos/bootloader.nix ../../modules/nixos/bootloader.nix
../../modules/nixos/certificates.nix ../../modules/nixos/certificates.nix
../../modules/nixos/data-drives.nix
../../modules/nixos/gnupg.nix ../../modules/nixos/gnupg.nix
../../modules/nixos/greetd.nix ../../modules/nixos/greetd.nix
../../modules/nixos/locales.nix ../../modules/nixos/locales.nix
@@ -46,22 +37,12 @@ in
}; };
hardware.amdgpu.initrd.enable = true; hardware.amdgpu.initrd.enable = true;
# encrypted data drives # encrypted data drives, see data-drives.nix
boot.initrd.secrets = lib.mapAttrs' (name: _: lib.nameValuePair (key name) (key name)) drives; local.dataDrives = {
boot.initrd.luks.devices = lib.mapAttrs (name: uuid: { data1 = "dfae62cc-bad1-4879-bf9a-461bde833625";
device = "/dev/disk/by-uuid/${uuid}"; data2 = "8312edae-9247-481b-a313-52a7f848f027";
keyFile = key name; nvmecache = "2352250e-4ebe-4f9a-bf66-0d4aaa961bd8";
# trim for ssds, ignored by drives without discard support };
allowDiscards = true;
}) drives;
fileSystems = lib.mapAttrs' (
name: _:
lib.nameValuePair "/mnt/${name}" {
device = "/dev/mapper/${name}";
fsType = "ext4";
options = [ "nofail" ];
}
) drives;
environment.systemPackages = with pkgs; [ environment.systemPackages = with pkgs; [
amdgpu_top amdgpu_top
+2 -2
View File
@@ -39,8 +39,8 @@
device = "/dev/disk/by-label/nixboot"; device = "/dev/disk/by-label/nixboot";
fsType = "vfat"; fsType = "vfat";
options = [ options = [
"fmask=0022" "fmask=0077"
"dmask=0022" "dmask=0077"
]; ];
}; };
-139
View File
@@ -1,139 +0,0 @@
# 0x29a nixos config
{ pkgs, ... }:
{
# use flakes
nix.settings.experimental-features = [
"nix-command"
"flakes"
];
# configure bootloader
boot.loader.systemd-boot.enable = false;
boot.loader.grub = {
enable = true;
device = "nodev";
efiSupport = true;
};
boot.loader.efi.canTouchEfiVariables = true;
boot.kernelParams = [
"quiet"
"console.ttyS0,115200n8" # allow terminals on serial console
];
# plymouth boot eye candy
boot.initrd.systemd.enable = true;
boot.plymouth.enable = true;
boot.plymouth.theme = "spinner";
# use latest kernel
boot.kernelPackages = pkgs.linuxPackages_latest;
# guest agent for proxmox
services.qemuGuest.enable = true;
# networking
networking.hostName = "nixos";
networking.networkmanager.enable = true;
# time zone.
time.timeZone = "Europe/Amsterdam";
# select internationalisation properties
i18n.defaultLocale = "en_US.UTF-8";
i18n.extraLocaleSettings = {
LC_ADDRESS = "en_US.UTF-8";
LC_IDENTIFICATION = "en_US.UTF-8";
LC_MEASUREMENT = "en_US.UTF-8";
LC_MONETARY = "en_US.UTF-8";
LC_NAME = "en_US.UTF-8";
LC_NUMERIC = "en_US.UTF-8";
LC_PAPER = "en_US.UTF-8";
LC_TELEPHONE = "en_US.UTF-8";
LC_TIME = "en_US.UTF-8";
};
console = {
font = "Lat2-Terminus16";
keyMap = "de_CH-latin1";
};
# window manager configs
services.xserver = {
enable = true;
xkb.layout = "ch";
xkb.options = "eurosign:e,caps:escape";
windowManager.qtile = {
enable = true;
extraPackages =
python3Packages: with python3Packages; [
qtile-extras
];
};
};
environment.pathsToLink = [ "/libexec" ];
environment.variables.EDITOR = "vim";
# enable sound
services.pipewire = {
enable = true;
pulse.enable = true;
};
# define a user account
users.users.aaron = {
isNormalUser = true;
group = "users";
extraGroups = [ "wheel" ];
shell = pkgs.zsh;
packages = with pkgs; [
tree
fastfetch
];
};
# browser
programs.firefox.enable = true;
# zsh config
programs.zsh = {
enable = true;
enableCompletion = true;
autosuggestions.enable = true;
ohMyZsh = {
enable = true;
plugins = [
"git"
"sudo"
];
theme = "agnoster";
};
};
# system packages
environment.systemPackages = with pkgs; [
vim
neovim
git
ghostty
kitty
wget
gnupg
];
# Some programs need SUID wrappers
programs.mtr.enable = true;
programs.gnupg.agent = {
enable = true;
enableSSHSupport = true;
};
# enable the OpenSSH daemon
services.openssh.enable = true;
# firewall configs
networking.firewall.allowedTCPPorts = [ 22 ];
# networking.firewall.allowedUDPPorts = [ ... ];
# install state version
system.stateVersion = "25.05"; # Don't change
}
-60
View File
@@ -1,60 +0,0 @@
# 0x29a nixos hardware configuration
{
config,
lib,
pkgs,
modulesPath,
...
}:
{
imports = [
(modulesPath + "/profiles/qemu-guest.nix")
];
# boot and initrd configs
boot.initrd.availableKernelModules = [
"uhci_hcd"
"ehci_pci"
"ahci"
"virtio_pci"
"virtio_scsi"
"sd_mod"
"sr_mod"
];
boot.initrd.kernelModules = [
"dm-snapshot"
"cryptd"
];
boot.kernelModules = [ ];
boot.extraModulePackages = [ ];
boot.initrd.luks.devices."cryptroot".device = "/dev/disk/by-label/nixcrypt";
# file systems
fileSystems."/" = {
device = "/dev/disk/by-label/nixroot";
fsType = "ext4";
};
fileSystems."/boot" = {
device = "/dev/disk/by-label/nixboot";
fsType = "vfat";
options = [
"fmask=0022"
"dmask=0022"
];
};
fileSystems."/home" = {
device = "/dev/disk/by-label/nixhome";
fsType = "ext4";
};
swapDevices = [
{ device = "/dev/disk/by-label/nixswap"; }
];
# enable dhcp on all interfaces
networking.useDHCP = lib.mkDefault true;
# set host arch
nixpkgs.hostPlatform = lib.mkDefault "x86_64-linux";
}
-79
View File
@@ -1,79 +0,0 @@
{ pkgs, ... }:
{
# user config
home.username = "aaron";
home.homeDirectory = "/home/aaron";
# packages
home.packages = with pkgs; [
fastfetch
zip
xz
unzip
p7zip
jq
mtr
iperf3
dnsutils
ldns
socat
nmap
cowsay
file
which
tree
zstd
gnupg
btop
iotop
iftop
strace
lsof
sysstat
lm_sensors
ethtool
pciutils
usbutils
];
# configure git
programs.git.settings = {
enable = true;
userName = "aaron";
userEmail = "aaron@0x29a.ch";
};
programs.zsh = {
enable = true;
autosuggestion.enable = true;
syntaxHighlighting.enable = true;
oh-my-zsh = {
enable = true;
plugins = [
"git"
"sudo"
"python"
"ansible"
];
theme = "agnoster";
};
};
## deploy qtile config
#home.file."qtile-config" = {
# target = ".config/qtile/config.py";
# source = "${inputs.qtile-config}/qtile_config/config.py";
# force = true;
#};
home.sessionVariables = {
EDITOR = "vim";
};
# enable home manager
programs.home-manager.enable = true;
# don't change
home.stateVersion = "25.05";
}
+1
View File
@@ -12,6 +12,7 @@
../../modules/nixos/certificates.nix ../../modules/nixos/certificates.nix
../../modules/nixos/docker.nix ../../modules/nixos/docker.nix
../../modules/nixos/gnupg.nix ../../modules/nixos/gnupg.nix
../../modules/nixos/lanzaboote.nix
../../modules/nixos/locales.nix ../../modules/nixos/locales.nix
../../modules/nixos/networking.nix ../../modules/nixos/networking.nix
../../modules/nixos/niri.nix ../../modules/nixos/niri.nix
+2 -2
View File
@@ -40,8 +40,8 @@
device = "/dev/disk/by-label/nixboot"; device = "/dev/disk/by-label/nixboot";
fsType = "vfat"; fsType = "vfat";
options = [ options = [
"fmask=0022" "fmask=0077"
"dmask=0022" "dmask=0077"
]; ];
}; };
+2 -2
View File
@@ -43,8 +43,8 @@
device = "/dev/disk/by-label/nixboot"; device = "/dev/disk/by-label/nixboot";
fsType = "vfat"; fsType = "vfat";
options = [ options = [
"fmask=0022" "fmask=0077"
"dmask=0022" "dmask=0077"
]; ];
}; };
+37
View File
@@ -0,0 +1,37 @@
# luks-encrypted data drives, unlocked after boot via /etc/crypttab with
# keyfiles from /etc/nixos/keys/<name>.key on the encrypted root.
# Not in boot.initrd.luks on purpose: keyfiles there end up in the initrd
# on the unencrypted /boot.
{ config, lib, ... }:
let
drives = config.local.dataDrives;
in
{
options.local.dataDrives = lib.mkOption {
type = lib.types.attrsOf lib.types.str;
default = { };
example = {
data1 = "dfae62cc-bad1-4879-bf9a-461bde833625";
};
description = "luks name -> luks partition uuid, mounted at /mnt/<name>";
};
config = lib.mkIf (drives != { }) {
environment.etc.crypttab.text = lib.concatStrings (
lib.mapAttrsToList (
name: uuid: "${name} /dev/disk/by-uuid/${uuid} /etc/nixos/keys/${name}.key luks,discard,nofail\n"
) drives
);
fileSystems = lib.mapAttrs' (
name: _:
lib.nameValuePair "/mnt/${name}" {
device = "/dev/mapper/${name}";
fsType = "ext4";
options = [ "nofail" ];
}
) drives;
};
}
+25
View File
@@ -0,0 +1,25 @@
# Secure Boot with lanzaboote. systemd-boot stays the bootloader, but lanzaboote
# installs it signed and writes signed boot entries instead of the nixos module.
# The keys are created once per host with `sudo sbctl create-keys` before the
# first rebuild, and enrolled into the firmware with `sbctl enroll-keys`.
{
inputs,
lib,
pkgs,
...
}:
{
imports = [ inputs.lanzaboote.nixosModules.lanzaboote ];
boot.loader.systemd-boot.enable = lib.mkForce false;
boot.lanzaboote = {
enable = true;
pkiBundle = "/var/lib/sbctl";
};
# key management and verification
environment.systemPackages = [ pkgs.sbctl ];
}