Introduction
nix-config - My Nix Configuration
See the full documentation at szinn.github.io/nix-config.
I manage my personal machines using Nix and Home-Manager.
The machines consist of:
Machine | System | Purpose |
---|---|---|
hera | x86_64-linux | Intel NUC for running NixOS bare metal |
macvm | aarch64-darwin | Nix-Darwin VM running in Parallels on MacBook |
nixvm | aarch64-linux | NixOS VM running in Parallels on MacBook |
odin | aarch64-darwin | Main MacBook Pro laptop |
ragnar | x86_64-linux | NAS server |
Bootstrapping MacOS Machines
Darwin machines can be bootstrapped from the nix-config repository directly.
bash -c "$(curl -LsS https://raw.githubusercontent.com/szinn/nix-config/main/bootstrap.sh)"
At this point, NIX should be installed. Restart the shell.
To bootstrap the configuration now, run
nix run --extra-experimental-features nix-command --extra-experimental-features flakes nix-darwin -- switch --flake .#$(hostname -s)
There might be a few files in /etc that need renaming to complete the installation.
sudo mv /etc/nix/nix.conf /etc/nix.conf.before-nix-darwin
sudo mv /etc/shells /etc/shells.before-nix-darwin
sudo mv /etc/zshenv /etc/zshenv.before-nix-darwin
Afterwards, if the configuration is touched, darwin-rebuild is required to run the updates.
git add . ; darwin-rebuild switch --flake .#$(hostname -s)
If just the home
tree is touched, then home-manager is sufficient to run the updates.
git add . ; home-manager switch --flake.#$(whoami)@(hostname -s)
Bootstrapping NixOS Machines
The process for bootstrapping a NixOS machine is essentially following the VM process
Virtual Machine Playgrounds
A great test environment (if you have Parallels) is to create a MacVM to try things out in. It lets you blow it away and start fresh as many times as you want to ensure you have a repeatable environment without destroying your current working environment,
On my network, I configure the machines with specific MAC addresses to pick up DHCP configuration.
Darwin
For a Darwin VM, create the Apple MacOS VM as you normally would. Once the VM is created, shut it down, and run
prlctl set macOS --device-set net0 --type bridged
prlctl set macOS --device-set net0 --mac DECAFF200019
prlctl set macOS --memsize 16384
prlctl set macOS --cpus 4
Start the VM up again, install Prallel VMTools (which requires a reboot), enable remote login and ensure Terminal has full disk access.
NixOS
For NixOS, download the minimal image and the CLI can be used to create/configure the VM. Since I also want to use ZFS for experimentation, I create two additional disks.
prlctl create nixvm -o other
prlctl set nixvm --device-set cdrom0 --image ~/Downloads/nixos-minimal-23.11.1697.781e2a9797ec-aarch64-linux.iso --connect
prlctl set nixvm --device-set net0 --type bridged
prlctl set nixvm --device-set net0 --mac DECAFF20001A
prlctl set nixvm --memsize 16384
prlctl set nixvm --cpus 4
prlctl set nixvm --device-set hdd0 --size 128G
prlctl set nixvm --device-add hdd --size 80G
prlctl set nixvm --device-add hdd --size 80G
prlctl start nixvm
Change into root and set a password for now.
sudo su
passwd
ssh is now available to log in as root for the remainder of the setup.
# Partitioning
parted /dev/sda -- mklabel gpt
parted /dev/sda -- mkpart root ext4 512MB -8GB
parted /dev/sda -- mkpart swap linux-swap -8GB 100%
parted /dev/sda -- mkpart ESP fat32 1MB 512MB
parted /dev/sda -- set 3 esp on
# Formatting
mkfs.ext4 -L nixos /dev/sda1
mkswap -L swap /dev/sda2
mkfs.fat -F 32 -n boot /dev/sda3
# Mounting disks for installation
mount /dev/disk/by-label/nixos /mnt
mkdir -p /mnt/boot
mount /dev/disk/by-label/boot /mnt/boot
swapon /dev/sda2
# Generating default configuration
nixos-generate-config --root /mnt
From this config copy the configuration and fetch the hardware configuration.
scp hosts/nixvm/configuration.nix root@nixvm:/mnt/etc/nixos/configuration.nix
scp root@nixvm:/mnt/etc/nixos/hardware-configuration.nix hosts/nixvm/hardware-configuration.nix
Then back to the VM
nixos-install
reboot
nixos-rebuild switch
Set the password for the user that was created.
passwd scotte
ssh in as the user
mkdir .local
cd .local
git clone https://github.com/szinn/nix-config.git
cd nix-config
nix develop
NIXPKGS_ALLOW_UNFREE=1 nix-shell -p _1password
op account add
eval $(op signin)
./scripts/fetch-secrets
An encrypted age key is required for secrets required during the rebuild.
Copy the output of the ssh-to-age
execution to .sops.yaml
in the appropriate entry.
ssh-to-age < /etc/ssh/ssh_host_ed25519_key.pub
Run task sops:re-encrypt
which will re-encrypt the secrets for this VM.
Remove groucho from the mountPoolsAtBoot since the zpool doesn't exist.
Finally, apply the configuration.
sudo nixos-rebuild switch --flake .
Create the zpool for groucho and then readd to the mountPoolsAtBoot.
sudo zpool create groucho mirror /dev/sdb /dev/sdc
Secrets Management
Login Secrets Management
The password for a user can be set by encrypting a specific password with the host key on the target machine.
Use ssh-to-age
to convert /etc/ssh/ssh_host_ed25519_key.pub to an age public key and add to .sops.yaml.
Run task sops:re-encrypt to re-encrypt secrets with the appropriate keys.
Secrets
I store additional secrets in 1Password and fetch them through the script fetch-secrets. This ensures they are not out in the wild at all.
All secrets are stored as document-type entries with the file as the document. Secrets that are for machine alpha are tagged with "alpha". As well, there must be a path for the secret and an optional script.
Paths are specified in the 1Password item as additional info text fields. The label is one of:
alpha:path
Darwin:path
default:path
alpha:path will specify the path on machine alpha. If alpha:path is missing, then a Darwin machine will look for Darwin:path and use that. Similarly, Linux:path will be used on a Linux (or NixOS) machine. If both of those are missing, default:path will be used.
Optional scripts are named the same way with :script
as the suffix. The script will be executed after the secret is fetched to the specified path.
The combination of path and script is useful for tasks such as loading GPG secret keys. Set the path to .
and then for a secret file named secret-keys.asc
the script
gpg --import ./secret-keys.asc
rm ./secret-keys.asc
will load the secrets into GPG and then erase the armoured file afterwards.