Introduction

Warning

These docs contain information that relates to my setup. They may or may not work for you.


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:

MachineSystemPurpose
herax86_64-linuxIntel NUC for running NixOS bare metal
macvmaarch64-darwinNix-Darwin VM running in Parallels on MacBook
nixvmaarch64-linuxNixOS VM running in Parallels on MacBook
odinaarch64-darwinMain MacBook Pro laptop
ragnarx86_64-linuxNAS 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.