From 737b74c58de0712973f81c91aa07748c02deef70 Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Sat, 24 Jan 2026 10:55:16 -0800 Subject: adding a new VM for testing Re-key all the secrets. --- README.org | 33 ++++------- flake.lock | 85 +++++++++++++++++++++++----- flake.nix | 10 ++++ justfile | 16 +++--- machines/test.nix | 47 ++++++++++++++++ modules/host-config.nix | 4 ++ profiles/defaults.nix | 15 +++++ profiles/disk/btrfs-on-luks.nix | 102 ++++++++++++++++++---------------- profiles/hardware/synology-vm.nix | 4 +- profiles/remote-unlock.nix | 2 +- profiles/state.nix | 59 ++++++++++++++++++++ profiles/users/admin-user.nix | 2 +- profiles/wireguard.nix | 4 ++ secrets/acme-cloudflare-env.age | Bin 704 -> 594 bytes secrets/anthropic-api-key.age | Bin 865 -> 755 bytes secrets/argonath/wireguard.age | Bin 691 -> 581 bytes secrets/authelia-jwks.age | Bin 2350 -> 2240 bytes secrets/authelia-jwt-key.age | 22 ++++---- secrets/authelia-storage-key.age | Bin 733 -> 623 bytes secrets/authelia-users.yaml.age | Bin 905 -> 795 bytes secrets/bree/disk-passphrase.age | Bin 667 -> 557 bytes secrets/bree/disk-unlock-key.age | Bin 1045 -> 935 bytes secrets/bree/wireguard.age | Bin 691 -> 581 bytes secrets/grafana-oidc.age | 22 ++++---- secrets/miniflux-oidc.age | Bin 719 -> 609 bytes secrets/restic-nas-smb-config.age | Bin 755 -> 645 bytes secrets/restic-pw.age | 24 ++++---- secrets/rivendell/wireguard.age | 22 ++++---- secrets/rsync-ssh-nas.age | Bin 1045 -> 935 bytes secrets/ssh-remote-builder.age | Bin 1045 -> 935 bytes secrets/test/ssh_host_ed25519_key.age | Bin 0 -> 813 bytes secrets/test/wireguard.age | Bin 0 -> 581 bytes tools/provision-nixos.py | 87 +++++++++++++++++++++++++++++ 33 files changed, 418 insertions(+), 142 deletions(-) create mode 100644 machines/test.nix create mode 100644 profiles/state.nix create mode 100644 secrets/test/ssh_host_ed25519_key.age create mode 100644 secrets/test/wireguard.age create mode 100644 tools/provision-nixos.py diff --git a/README.org b/README.org index dac91fb..df66363 100644 --- a/README.org +++ b/README.org @@ -17,14 +17,13 @@ nix run nix-darwin -- switch --flake . Finally, switch the default shell via =chsh=, and set it to =/run/current-system/sw/bin/fish=. Best to reboot to complete the installation. - ** Steps for a new droplet on DigitalOcean Start by creating a droplet using Debian. Create a new host configuration. Once the droplet is provisioned, we can use =nixos-anywhere= to convert the droplet to a NixOS installation. #+begin_src sh -nix run github:nix-community/nixos-anywhere -- --flake .# --target-host root@ +just deploy-nixos flake ip #+end_src Once the host reboots, check that it's converted to NixOS by running =uname -a=. @@ -33,31 +32,31 @@ Once the host reboots, check that it's converted to NixOS by running =uname -a=. - use UEFI for boot - use the ISO generated with =nix build .#nixosConfigurations.iso.config.system.build.isoImage= - boot to the installer -- run =nix run github:nix-community/nixos-anywhere -- --flake .# --target-host +- run =just deploy-nixos name ip= ** Create the nixos installer Run #+begin_src sh nix build .#nixosConfigurations.iso.config.system.build.isoImage #+end_src +If you need to install this on a USB drive, use the Samsung disk for this. Follow these steps: +- =diskutil list= to identify the disk (e.g. =/dev/disk5=) +- =diskutil unmountDisk /dev/disk5= to un-mount the drive + Then copy to a USB stick with: #+begin_src sh sudo dd if=result/iso/nixos-minimal-25.05git.25e53aa156d-x86_64-linux.iso of=/dev/rdisk5 bs=1M conv=sync status=progress #+end_src - ** Bare metal machine We can install remotely a machine with =nixos-anywhere=, including full disk encryption. -First, create a password in 1password for the machine (using the convention "nix//encryption"). Next run the following snippet to create the SSH host key for init boot (this is needed so we can ssh to the host to unlock it). +First, create a password in =passage= for the machine (using the convention =hosts//disk-encryption/passphrase=), and the ssh private key (=ed25519=). Next run the following snippet to create the SSH host key for init boot (this is needed so we can ssh to the host to unlock it). #+begin_src sh -set temp (mktemp -d) -ssh-keygen -t ed25519 -N "" -C "initrd-root-ssh" -f "$temp/etc/initrd/ssh_host_ed25519_key" -nix run github:nix-community/nixos-anywhere -- --flake .#rivendell --build-on remote --disk-encryption-keys /tmp/pass (op read "op://Private/vmifhwbjtvaqp3422gfbjxdq2y/password"|psub) --target-host root@192.168.1.112 --extra-files "$temp" +just deploy-nixos name ip #+end_src * DNS Update records through the [[https://dash.cloudflare.com/2c659eeaf2ae9a0206c589c706b3748e/fcuny.net][console]]. - * Secrets Get the identity under =secrets/identity.txt= with: #+begin_src sh @@ -68,7 +67,7 @@ age-plugin-yubikey --list --slot 1 > identity.txt To create or edit a secret: #+begin_src sh cd (git rev-parse --show-toplevel)/secrets -agenix -i identity.txt -e users/fcuny/llm.age +age -R $PASSAGE_RECIPIENTS_FILE -o users/fcuny/llm.age #+end_src And to rekey the secrets: @@ -80,8 +79,10 @@ agenix -i identity.txt -r You can validate that the file is correct with: #+begin_src sh cd (git rev-parse --show-toplevel)/secrets -nix eval --file secrets.nix +nix eval --json --pretty --file secrets.nix +age-inspect --json users/fcuny/llm.age #+end_src +The output of =age-inspect= should list in the =stanza_types= key at least one =ssh-ed25519= (it indicates one of the recipient is using a SSH key). * Network ** Wireguard *** New host @@ -138,13 +139,3 @@ Then: #+begin_src shell psql -U postgres -h localhost -p 35432 #+end_src -** Build the ISO -To install nixos, we can build our own iso. For this, run: -#+begin_src shell -just build-iso -#+end_src - -If you need to install this on a USB drive, use the Samsung disk for this. Follow these steps: -- =diskutil list= to identify the disk (e.g. =/dev/disk5=) -- =diskutil unmountDisk /dev/disk5= to un-mount the drive -- =sudo dd if=result/iso/nixos-minimal-25.11git.30a3c519afc-x86_64-linux.iso of=/dev/rdisk5 status=progress= diff --git a/flake.lock b/flake.lock index 569d905..1935384 100644 --- a/flake.lock +++ b/flake.lock @@ -26,7 +26,7 @@ "cl-nix-lite": { "inputs": { "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs", + "nixpkgs": "nixpkgs_2", "systems": "systems_2", "treefmt-nix": "treefmt-nix" }, @@ -339,12 +339,52 @@ "type": "github" } }, + "home-manager_3": { + "inputs": { + "nixpkgs": [ + "impermanence", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1768598210, + "narHash": "sha256-kkgA32s/f4jaa4UG+2f8C225Qvclxnqs76mf8zvTVPg=", + "owner": "nix-community", + "repo": "home-manager", + "rev": "c47b2cc64a629f8e075de52e4742de688f930dc6", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "home-manager", + "type": "github" + } + }, + "impermanence": { + "inputs": { + "home-manager": "home-manager_3", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1768941735, + "narHash": "sha256-OyxsfXNcOkt06/kM+4bnuC8moDx+t7Qr+RB0BBa83Ig=", + "owner": "nix-community", + "repo": "impermanence", + "rev": "69ecf31e8fddc9354a4b418f3a517445d486bb54", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "impermanence", + "type": "github" + } + }, "mac-app-util": { "inputs": { "cl-nix-lite": "cl-nix-lite", "flake-compat": "flake-compat", "flake-utils": "flake-utils", - "nixpkgs": "nixpkgs_3", + "nixpkgs": "nixpkgs_4", "systems": "systems_3", "treefmt-nix": "treefmt-nix_2" }, @@ -365,7 +405,7 @@ "my-go-tools": { "inputs": { "flake-utils": "flake-utils_2", - "nixpkgs": "nixpkgs_5", + "nixpkgs": "nixpkgs_6", "pre-commit-hooks": "pre-commit-hooks", "treefmt-nix": "treefmt-nix_3" }, @@ -401,16 +441,16 @@ }, "nixpkgs": { "locked": { - "lastModified": 1766736597, - "narHash": "sha256-BASnpCLodmgiVn0M1MU2Pqyoz0aHwar/0qLkp7CjvSQ=", + "lastModified": 1768564909, + "narHash": "sha256-Kell/SpJYVkHWMvnhqJz/8DqQg2b6PguxVWOuadbHCc=", "owner": "nixos", "repo": "nixpkgs", - "rev": "f560ccec6b1116b22e6ed15f4c510997d99d5852", + "rev": "e4bae1bd10c9c57b2cf517953ab70060a828ee6f", "type": "github" }, "original": { "owner": "nixos", - "ref": "nixos-25.11", + "ref": "nixos-unstable", "repo": "nixpkgs", "type": "github" } @@ -447,6 +487,22 @@ } }, "nixpkgs_2": { + "locked": { + "lastModified": 1766736597, + "narHash": "sha256-BASnpCLodmgiVn0M1MU2Pqyoz0aHwar/0qLkp7CjvSQ=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "f560ccec6b1116b22e6ed15f4c510997d99d5852", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-25.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { "locked": { "lastModified": 1761236834, "narHash": "sha256-+pthv6hrL5VLW2UqPdISGuLiUZ6SnAXdd2DdUE+fV2Q=", @@ -462,7 +518,7 @@ "type": "github" } }, - "nixpkgs_3": { + "nixpkgs_4": { "locked": { "lastModified": 1732617236, "narHash": "sha256-PYkz6U0bSEaEB1al7O1XsqVNeSNS+s3NVclJw7YC43w=", @@ -478,7 +534,7 @@ "type": "github" } }, - "nixpkgs_4": { + "nixpkgs_5": { "locked": { "lastModified": 1761236834, "narHash": "sha256-+pthv6hrL5VLW2UqPdISGuLiUZ6SnAXdd2DdUE+fV2Q=", @@ -494,7 +550,7 @@ "type": "github" } }, - "nixpkgs_5": { + "nixpkgs_6": { "locked": { "lastModified": 1767325753, "narHash": "sha256-o4rBYiMcleYJx1dTiknARe3mfKfrc4m8WT/cWG+OG1M=", @@ -507,7 +563,7 @@ "url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz" } }, - "nixpkgs_6": { + "nixpkgs_7": { "locked": { "lastModified": 1767480499, "narHash": "sha256-EFkzO1bnguFRUEeNvdkkGEU/P35obLVp+36NuaIQSr8=", @@ -593,10 +649,11 @@ "disko": "disko", "emacs-overlay": "emacs-overlay", "home-manager": "home-manager_2", + "impermanence": "impermanence", "mac-app-util": "mac-app-util", "my-go-tools": "my-go-tools", "nixos-hardware": "nixos-hardware", - "nixpkgs": "nixpkgs_6", + "nixpkgs": "nixpkgs_7", "nur": "nur", "pre-commit-hooks": "pre-commit-hooks_2", "treefmt-nix": "treefmt-nix_4" @@ -664,7 +721,7 @@ }, "treefmt-nix": { "inputs": { - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_3" }, "locked": { "lastModified": 1766000401, @@ -682,7 +739,7 @@ }, "treefmt-nix_2": { "inputs": { - "nixpkgs": "nixpkgs_4" + "nixpkgs": "nixpkgs_5" }, "locked": { "lastModified": 1766000401, diff --git a/flake.nix b/flake.nix index cf2b69f..7e95916 100644 --- a/flake.nix +++ b/flake.nix @@ -48,6 +48,8 @@ inputs.nixpkgs.follows = "nixpkgs"; }; + impermanence.url = "github:nix-community/impermanence"; + mac-app-util.url = "github:hraban/mac-app-util"; my-go-tools.url = "git+https://code.fcuny.net/x"; @@ -66,6 +68,7 @@ emacs-overlay, nur, my-go-tools, + impermanence, ... }: let @@ -114,6 +117,7 @@ nixosAdminUser = baseAdminUser // { uid = 1000; + gid = 100; }; darwinAdminUser = baseAdminUser // { @@ -125,6 +129,7 @@ agenix.nixosModules.age disko.nixosModules.disko home-manager.nixosModules.home-manager + impermanence.nixosModules.impermanence ./modules/default.nix ]; @@ -155,6 +160,10 @@ system = "x86_64-linux"; config = ./machines/iso.nix; }; + test = { + system = "x86_64-linux"; + config = ./machines/test.nix; + }; }; darwin = { mba-m2 = { @@ -292,6 +301,7 @@ git just nixos-rebuild + uv ]; }; } diff --git a/justfile b/justfile index 3d49102..edebd3e 100644 --- a/justfile +++ b/justfile @@ -29,29 +29,29 @@ switch: @echo "switching to new config..." sudo darwin-rebuild switch --flake . +# build the iso to bootstrap a new machine [group('linux')] build-iso: @echo "building an ISO for nixos..." nix build .#nixosConfigurations.iso.config.system.build.isoImage +# provision a new machine with nixos-anywhere +[group('linux')] +deploy-nixos flake ip: + ./tools/deploy-nixos.py --flake {{flake}} --target-ip {{ip}} + +# rebuild the nixos configuration for a host [group('linux')] rbuild hostname: @echo "building {{hostname}} nixos config..." nixos-rebuild build --keep-going --flake ".#{{hostname}}" --target-host {{hostname}} --fast --use-remote-sudo --use-substitutes +# apply the nixos configuration for a host [group('linux')] rswitch hostname: @echo "switching {{hostname}} to new config..." nixos-rebuild switch --keep-going --flake ".#{{hostname}}" --target-host {{hostname}} --fast --use-remote-sudo --use-substitutes -# sync agenix key from 1password -[group('secrets')] -sync-agenix-key: - @echo "copying agenix SSH key from 1password..." - mkdir -p ~/.ssh - op --account my.1password.com read "op://Private/agenix/private key?ssh-format=openssh" > ~/.ssh/agenix - op --account my.1password.com read "op://Private/agenix/public key" > ~/.ssh/agenix.pub - # generate a new OIDC secret [group('secrets')] oidc-secret: diff --git a/machines/test.nix b/machines/test.nix new file mode 100644 index 0000000..3c6e138 --- /dev/null +++ b/machines/test.nix @@ -0,0 +1,47 @@ +{ + adminUser, + lib, + config, + ... +}: +{ + wgPublicKey = "c3z4rypRBn+kFj31I6Z90pjVjRYB8w5GCoq1tZP+4mc="; + publicKey = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDbwKdEY93hEVOx2DS4EMliiVTmsyxjqUG2stgCRMGwj"; + ephemeralRoot = true; + + age = { + identityPaths = [ "/persist/etc/ssh/ssh_host_ed25519_key" ]; + secrets = { + wireguard.file = ../secrets/test/wireguard.age; + }; + }; + + imports = [ + ../profiles/core-metrics.nix + ../profiles/defaults.nix + ../profiles/disk/btrfs-on-luks.nix + ../profiles/hardware/synology-vm.nix + ../profiles/home-manager.nix + ../profiles/remote-unlock.nix + ../profiles/server.nix + ../profiles/state.nix + ../profiles/users/admin-user.nix + ../profiles/users/home-manager.nix + ../profiles/wireguard.nix + ]; + + disko.devices.disk.disk1.device = "/dev/sda"; + + networking.hostName = "test"; + networking.useDHCP = lib.mkDefault true; + systemd.network.wait-online.anyInterface = lib.mkDefault config.networking.useDHCP; + + system.stateVersion = "25.11"; # Did you read the comment? + + home-manager.users.${adminUser.name} = { + home.homeDirectory = "/home/${adminUser.name}"; + imports = [ + ../home/profiles/minimal.nix + ]; + }; +} diff --git a/modules/host-config.nix b/modules/host-config.nix index 92d8eea..ff1eaa5 100644 --- a/modules/host-config.nix +++ b/modules/host-config.nix @@ -13,5 +13,9 @@ type = lib.types.str; default = "xxxx"; }; + ephemeralRoot = lib.mkOption { + type = lib.types.bool; + default = false; + }; }; } diff --git a/profiles/defaults.nix b/profiles/defaults.nix index 2683c5a..834c28d 100644 --- a/profiles/defaults.nix +++ b/profiles/defaults.nix @@ -5,6 +5,17 @@ adminUser, ... }: +let + inherit (lib // builtins) + attrNames + hasAttr + mkIf + length + ; + hasState = + hasAttr "persistence" config.environment && (length (attrNames config.environment.persistence)) > 0; + hasSecrets = config.age.secrets != { }; +in { imports = [ ./cgroups.nix @@ -145,4 +156,8 @@ vim wireguard-tools ]; + + system.activationScripts.agenixNewGeneration = mkIf ( + hasSecrets && hasState && config.ephemeralRoot + ) { deps = [ "persist-files" ]; }; } diff --git a/profiles/disk/btrfs-on-luks.nix b/profiles/disk/btrfs-on-luks.nix index 3fe57f7..aea2c0c 100644 --- a/profiles/disk/btrfs-on-luks.nix +++ b/profiles/disk/btrfs-on-luks.nix @@ -1,4 +1,4 @@ -{ ... }: +{ lib, config, ... }: let btrfsopt = [ "compress=zstd" @@ -13,55 +13,52 @@ 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 = [ - "fmask=0022" - "dmask=0022" - ]; - }; + disk.disk1 = { + type = "disk"; + device = lib.mkDefault "/dev/nvme0n1"; + content = { + type = "gpt"; + partitions = { + boot = { + name = "boot"; + size = "1M"; + type = "EF02"; + }; + esp = { + size = "2G"; + type = "EF00"; + content = { + type = "filesystem"; + format = "vfat"; + mountpoint = "/boot"; }; - luks = { - size = "100%"; + }; + luks = { + size = "100%"; + content = { + type = "luks"; + name = "nixos"; + passwordFile = "/tmp/disk.key"; + settings.allowDiscards = true; content = { - type = "luks"; - name = "nixos"; - passwordFile = "/tmp/pass"; - settings = { - allowDiscards = true; - }; - content = { - type = "btrfs"; - extraArgs = [ "-f" ]; - subvolumes = { - "@root" = { - mountpoint = "/"; - mountOptions = btrfsopt; - }; - "@home" = { - mountpoint = "/home"; - mountOptions = btrfsopt; - }; - "@nix" = { - mountpoint = "/nix"; - mountOptions = btrfsopt; - }; - "@data" = { - mountpoint = "/data"; - mountOptions = btrfsopt; - }; + type = "btrfs"; + extraArgs = [ "-f" ]; + subvolumes = { + "/root" = lib.mkIf (!config.ephemeralRoot) { + mountpoint = "/"; + mountOptions = btrfsopt; + }; + "/nix" = { + mountpoint = "/nix"; + mountOptions = btrfsopt; + }; + "/data" = { + mountpoint = "/data"; + mountOptions = btrfsopt; + }; + "/persist" = { + mountpoint = "/persist"; + mountOptions = btrfsopt; }; }; }; @@ -70,5 +67,14 @@ in }; }; }; + nodev."/" = lib.mkIf config.ephemeralRoot { + fsType = "tmpfs"; + mountOptions = [ + "size=16G" + "defaults" + "mode=755" + ]; + }; }; + fileSystems."/persist".neededForBoot = true; } diff --git a/profiles/hardware/synology-vm.nix b/profiles/hardware/synology-vm.nix index 053cf41..970a23c 100644 --- a/profiles/hardware/synology-vm.nix +++ b/profiles/hardware/synology-vm.nix @@ -15,7 +15,9 @@ "sd_mod" "sr_mod" ]; - boot.initrd.kernelModules = [ ]; + boot.initrd.kernelModules = [ + "virtio_net" + ]; boot.kernelModules = [ "kvm-amd" ]; boot.extraModulePackages = [ ]; diff --git a/profiles/remote-unlock.nix b/profiles/remote-unlock.nix index 9812ce8..0e2cb1b 100644 --- a/profiles/remote-unlock.nix +++ b/profiles/remote-unlock.nix @@ -12,7 +12,7 @@ enable = true; port = 911; hostKeys = [ - "/etc/initrd/ssh_host_ed25519_key" + "/persist/secrets/ssh_host_ed25519_key" ]; authorizedKeys = with adminUser.userinfo.sshPublicKeys; [ onepassword diff --git a/profiles/state.nix b/profiles/state.nix new file mode 100644 index 0000000..0869d11 --- /dev/null +++ b/profiles/state.nix @@ -0,0 +1,59 @@ +{ + adminUser, + config, + lib, + ... +}: +{ + system.activationScripts = lib.mkIf config.ephemeralRoot { + "createPersistentStorageDirs".deps = [ + "var-lib-private-permissions" + "home-user-permissions" + "users" + "groups" + ]; + "var-lib-private-permissions" = { + deps = [ "specialfs" ]; + text = '' + mkdir -p /persist/var/lib/private + chmod 0700 /persist/var/lib/private + ''; + }; + "home-user-permissions" = { + deps = [ "specialfs" ]; + text = '' + mkdir -p /persist/save/home/${adminUser.name} + chown -R ${toString adminUser.uid}:${toString adminUser.gid} /persist/save/home/${adminUser.name} + chmod 0700 /persist/save/home/${adminUser.name} + ''; + }; + }; + + environment.persistence."/persist" = { + enable = config.ephemeralRoot; + hideMounts = true; + directories = [ + "/root" + "/var/lib/containers" + "/var/lib/nixos" + "/var/lib/systemd" + "/var/log" + ]; + files = [ + "/etc/machine-id" + "/etc/ssh/ssh_host_ed25519_key" + "/etc/ssh/ssh_host_ed25519_key.pub" + ]; + }; + + environment.persistence."/persist/save" = { + enable = config.ephemeralRoot; + hideMounts = true; + users.${adminUser.name} = { + directories = [ ]; + files = [ + ".ssh/known_hosts" + ]; + }; + }; +} diff --git a/profiles/users/admin-user.nix b/profiles/users/admin-user.nix index 2e33603..8624fb4 100644 --- a/profiles/users/admin-user.nix +++ b/profiles/users/admin-user.nix @@ -10,7 +10,7 @@ inherit (adminUser) uid; shell = pkgs.fish; isNormalUser = true; - hashedPassword = "$y$j9T$U3mXpCzXC1VUp8wV5snJz/$32vTk0KwVXvP/jLO13nMlGPHy0nCe4ZtebdvqU4hwmD"; + hashedPassword = "$y$j9T$TbnCB1V7n6W9C32LEroix1$P.OvRgLlhSbwFvR6ADE43Gc5Hc0ezxXMETS/DjIeXC8"; openssh.authorizedKeys.keys = pkgs.lib.attrValues adminUser.userinfo.sshPublicKeys; extraGroups = [ "wheel" diff --git a/profiles/wireguard.nix b/profiles/wireguard.nix index 76586ba..5620699 100644 --- a/profiles/wireguard.nix +++ b/profiles/wireguard.nix @@ -22,6 +22,10 @@ let publicKey = hostConfigurations.rivendell.wgPublicKey; endpoint = "192.168.1.114"; }; + test = { + ip = 41; + publicKey = hostConfigurations.rivendell.wgPublicKey; + }; }; wgPort = 51820; diff --git a/secrets/acme-cloudflare-env.age b/secrets/acme-cloudflare-env.age index f204f55..0db746f 100644 Binary files a/secrets/acme-cloudflare-env.age and b/secrets/acme-cloudflare-env.age differ diff --git a/secrets/anthropic-api-key.age b/secrets/anthropic-api-key.age index 3cf3e31..524df4e 100644 Binary files a/secrets/anthropic-api-key.age and b/secrets/anthropic-api-key.age differ diff --git a/secrets/argonath/wireguard.age b/secrets/argonath/wireguard.age index 7f25906..5edd73d 100644 Binary files a/secrets/argonath/wireguard.age and b/secrets/argonath/wireguard.age differ diff --git a/secrets/authelia-jwks.age b/secrets/authelia-jwks.age index 57e62ca..f036674 100644 Binary files a/secrets/authelia-jwks.age and b/secrets/authelia-jwks.age differ diff --git a/secrets/authelia-jwt-key.age b/secrets/authelia-jwt-key.age index 44c511b..e1d24b2 100644 --- a/secrets/authelia-jwt-key.age +++ b/secrets/authelia-jwt-key.age @@ -1,13 +1,11 @@ age-encryption.org/v1 --> ssh-ed25519 pFjJaA ab95Xy3qdnkT+1QNbE7nM0KTxEsvZbJSaGlQHkMeuVY -Aos+Y6URghnBymVQXMW4KMB4MupcMFVubUILUgfObcM --> ssh-ed25519 Y5h84Q EXMHz89OpNU9k6ehLg8UiR9QVS5IRAiZ/QM5U5gb3BU -h5nndbtxIXsMw5VKQTXPWIzzI1wZxa1LpoA2sXnUnco --> piv-p256 p72k4g AyneNJUchntzS/JK92mNx7sExe+Qw319/8wys4rXcCJ7 -LaAxI45aCP3qlK+dxNiBYWMTdWk8IgxDaBtwiEAOzSY --> piv-p256 3VHSDQ AoL8W+RHWh3VCPyIqSnRKdqL2mOtsSNadlApadjmvLYD -aDy2pnH4fqfornVC5VWCVnVXOppA2fU4ZYBXQduq2wU --> piv-p256 EwG8WQ ApU0xcZy2cLt+ZlTMJCpB2/qZ2+FsTBAtiMXGMUHW3Dr -fcY3frM+Rh8y6QzGZViTYfsSAg7mNtu3o5rpick0yb0 ---- 0Hkdlz6iYjC4NXrIut1N4XnSFHKAnrtQVPvE3Ua4rA8 -aq\FWɮa D~+lu~d0q$?RIy2=Xr"0Q"Pv['Ie,>|*Fk~?N \6ą0 ? \ No newline at end of file +-> piv-p256 p72k4g AoOkiRon1Euqtk0bGkg7jj3/moc86443Qiv3o/eD8Mvu +2iXCi4fFHVyS6EhR8s0RqymZujwd+CkQq53N2fhbQbg +-> piv-p256 3VHSDQ AgFXfoGRwV6ojVkhpdInCEgVE59KhoUBpEQCnyQMFTMa +OLx/OYmfYwVyciUnrsKHMMuNbVj7MN0eHfvBK3rV8l8 +-> piv-p256 EwG8WQ A3tES/tGDJ7eHADR3ThB8HwLevNSRKfV/qxO8+sfhKnG +rG4sBBi8Q7Y9jkLftD2sHr1aI5muE64bNfOjEGdUvzU +-> ssh-ed25519 Y5h84Q x1Wqj46Ro+2N6wBGMOQ5Y/dHHURmtWk3eG+6/20XLXs +GNYg1JTQ/UHwPtFJ2TcpIsi7g0D0E8Q2sVcm0Xij2Ig +--- TbVqe8KDEi/RSGkjNIN2m7enPpb6tTaemJTipak53cI +.cHT0퓼Vm|ؐJ*M[<NJGaNY @~ƙXٙrI_f ʈ, ӜS.J\Ȑ{<&sQ*ȉ=ɇ"_8rtG0 \ No newline at end of file diff --git a/secrets/authelia-storage-key.age b/secrets/authelia-storage-key.age index 5aa6d4e..1878c7c 100644 Binary files a/secrets/authelia-storage-key.age and b/secrets/authelia-storage-key.age differ diff --git a/secrets/authelia-users.yaml.age b/secrets/authelia-users.yaml.age index 15082f3..191a68d 100644 Binary files a/secrets/authelia-users.yaml.age and b/secrets/authelia-users.yaml.age differ diff --git a/secrets/bree/disk-passphrase.age b/secrets/bree/disk-passphrase.age index e755597..d42cfa1 100644 Binary files a/secrets/bree/disk-passphrase.age and b/secrets/bree/disk-passphrase.age differ diff --git a/secrets/bree/disk-unlock-key.age b/secrets/bree/disk-unlock-key.age index 5845c22..d79c0e9 100644 Binary files a/secrets/bree/disk-unlock-key.age and b/secrets/bree/disk-unlock-key.age differ diff --git a/secrets/bree/wireguard.age b/secrets/bree/wireguard.age index cb548f9..b618190 100644 Binary files a/secrets/bree/wireguard.age and b/secrets/bree/wireguard.age differ diff --git a/secrets/grafana-oidc.age b/secrets/grafana-oidc.age index 9c1ac05..fabee96 100644 --- a/secrets/grafana-oidc.age +++ b/secrets/grafana-oidc.age @@ -1,13 +1,11 @@ age-encryption.org/v1 --> ssh-ed25519 pFjJaA kJ/tIhFivUGhT9Gx8cNeAcxdghf0taPWzqUhR8qo4Qc -AIvNKY3xm6z7SUJ5On8RE6Ucjsv6TlaeYaOYTgyvQq4 --> ssh-ed25519 Y5h84Q XNWAeUxr9DjIzWM4qhIOhEJ6Ecp5ypCX0Yvcz9Xesj4 -oZ2FkjFw0qTwgiYcVMWtzPoqGWZY1l+U73J+TgZ3X1w --> piv-p256 p72k4g A0Mdy32eOyBtKGo5dGoEC7FMpm9ZN5+Raa60lQBn1TmE -6li+w4ekWyfjvOs9HuhEOLsashLP+juLk2hx2pLgjwA --> piv-p256 3VHSDQ AjByue1R3VFjCow5Z6Mqe52Vz3o3o6IfbqRHlzcT4l7y -KRhQPUqHlQopY2oMLJKzgznBWMH9BkxLs93VK84fDFs --> piv-p256 EwG8WQ A87+jQw1s4BFTg6mUG62NCrgpCr/7EQoOEsqcSYQkDRd -HSX9YmHQHzzVxfoEB7x+W0S2nqO44CVHdLQ/FbTGO60 ---- XKDOSYe7vUkzn6sSLb3KHLBm9v/H9paHMKmkUnTB/Jg -ǣ3o^G4dyJ =T@J)+A-Z UXVe BQ}V,OdL<oY*1Ae頠`; \ No newline at end of file +-> piv-p256 p72k4g A4oi87WF3sdfjikAhFnGojH4wFhN8PS/LYcTJeeSBPap +pAaJUbWOECxcvCQKxQ2lSUBHCQO7bAZWuDl5YM2fRu8 +-> piv-p256 3VHSDQ Alk3vY0FgDeN/gCdOSxgnUo/hfVZ7URyD0cJuQv0yww3 +gAQg5IVFyPa3f1bS+n5C6MU3s/WWMhMlW7yD7TiT8rA +-> piv-p256 EwG8WQ Aqk2hmEcO5LyuU4HVFaCfMs0VLlHD4o5pZq2hG69ZJ6z +dNOTD0LgWcbCDJr2dO8DdTa5tZouNWNKWaRG/UOIJWI +-> ssh-ed25519 Y5h84Q LNqSVvXMBSpHNh+gKbqW/jESC1MKXzdSD9exBNw7TXc +6MsJL/xRuWiXIwwl4jzpI5BKM42m21jIjAiBYK7ur/w +--- GEENxbXM7M/YFFieNUZTsWbZ5iNTvs2IUMHLlPni+OQ + agqI&J>g/k&V R+ZF*}#>%ϳ8_nMH=m ssh-ed25519 pFjJaA syGYV6BJA1h3LhjxbemVAmYitI5aegvlNrrjAdCsH3k -Lxo6Y5Dv/G8D4GXBNcN+OPscp3QfFN+N4vs4DKxE0lU --> ssh-ed25519 Y5h84Q SGVhQY9w4IqevD+IJy4BvmcFm9YIhvbWXUVLwwmbnwM -75wLxUwTGAmYBmTtN1CxV/oZZukFLk7xovtGBmvz6uo --> piv-p256 p72k4g A/f4dbKBG2cI37D3D4J3WPE6Yx6vzNdP+5ntufwM/Gha -lEPCCOwtjpq8HJg9SmjIcQMh9gJFWbhYQFjxu4dSXGc --> piv-p256 3VHSDQ Apy0ALbHVi49Bj8w2ek/i/eV3/05/A9dDPBMQPn9FkPx -GBtBwEsIQLOyyAM3FYikVVz7okArqQ9jkXwSF29F3Xk --> piv-p256 EwG8WQ AwWFId0nB8e6gxB+NnwRFMCPqIIEccDR6nl//AouYjDN -3sggJZLjKxwOhy1s9knJRAWBX8XZxQxJyu00ATHNksU ---- WxnsY5qOy2Mp2lmdGCp2nwsiTj7YAg2hYGbDiFx3Q3I -B~?>hMK([UC[oP҅()x_.T~y"ҝķvB \ No newline at end of file +-> piv-p256 p72k4g AzrfxleD97vj+b0i/4MirdjWWywswZyt0cqe7NYo9YQ4 +fOWYwjASDxhqUcNp6w1uQZmwI51/4blsHlbFGlZM91I +-> piv-p256 3VHSDQ Ascoksz5SOuRdWgH2TfVm2wuGOAdwfY5+Zsc9MGAouXA +Z7T8RemQIs/Mkdjrv+iYMx6N9kG+jjYIW3L2KEHE4G0 +-> piv-p256 EwG8WQ AgEaKl9tYMlfoSmBiZavstcJNOyxaMRdJSUFDspr6nP2 +e0voo2M30ZrNJWCfPoAnEXoRwd7UW3mTqdiSv2OzHMI +-> ssh-ed25519 Y5h84Q FIKfbc0p7IyAF0ay+kLcA0/df673JsOUnkxpUr220nE +CP9UvOFluuRATTjbaT9SfZAJQCrkrqS9vKVX9YMoR8w +-> ssh-ed25519 E2Yu8Q 5IivN0WwNdllxfMFbEmuaA6txuboiH3eecOztNHClVY +c/tLSOQ68p5xRrGiEq3fBdDPVw6fbFqB2fIK9tqBsDE +--- HqeM47p0TjMol/fRmjLHOOR0+yXp4pNuqw2Th+4jm1k +62UE:?g8JMx'?{Gog W}Vp]2qh \ No newline at end of file diff --git a/secrets/rivendell/wireguard.age b/secrets/rivendell/wireguard.age index 2be928e..9c967a5 100644 --- a/secrets/rivendell/wireguard.age +++ b/secrets/rivendell/wireguard.age @@ -1,13 +1,11 @@ age-encryption.org/v1 --> ssh-ed25519 pFjJaA 2qv6LHBYtAiaDhZ8sAyaw+bSqQiyFkQG9ZB1y1aolxE -9uOB2Nrm0QZvo3wIB8/mNqmTOorYZdK8OK3wotbtUyA --> ssh-ed25519 Y5h84Q b9FraCr9H3n/fFfmVhWWiJre56xr7QVj2rG1A93FkGU -iYoBQE9x+pMVZFVTtTaU2br/BbU/+B8rZWQLrOzQgpw --> piv-p256 p72k4g A7ErQNxB/ZHqci4wbRIlLcGzVKJPL66YSvOGperLqoa2 -q8ok0ZH+qhWg4wodnhrGdO+o6YgstfkP0jiqJCNfjh0 --> piv-p256 3VHSDQ A3CZRYI7mdEdYfjoiBupjgt4IuxspjEAiF/U0L7BUPgM -dfefVCTufhqSpmC7VRPlHU1uesBxswQtT2sG0ZhN5kU --> piv-p256 EwG8WQ AwOVx3tXdx2hKs5mXbalBIcNgdmDFx1B5ZSptImOmc6G -/fAXlZ6TFT3uPi/8nsTygmMWQ9skuFHBACEgoPBv4qw ---- 8O55twCZmB3scabd9rETAM2O1a+qHw2q5rwLHuU8K1E -VsX6BE[鞇T4hn]˟wq 3MShD`exl))˫z  揙Eg \ No newline at end of file +-> piv-p256 p72k4g AkjAdyuaUbt/YlcOZmguIXlc2Rsh7JdkQUt6UI9hS+S8 +5Gg1XNO9vuelTW5FV/YnBttvWadD3VPHFv2LHKuY7xM +-> piv-p256 3VHSDQ AhEFbuqTOmEU+JOk+zOu7cbAjPO7AouMpZW6aa6E/ZNc +HM6g7zA/ayZKodXRL1g8qV8AlGD+E4mvaEJ3bXnxWKw +-> piv-p256 EwG8WQ A3rSmT6cUMN/IAil9DMm8oVJp+Ibvy+avGzhap4y8fFX +2nfr7mZXTakgHbKTTSlWCdzBQ/4ztG+I5Za37c4qXm0 +-> ssh-ed25519 Y5h84Q 0NQxJc1wCM2oTAqAKerFX6z9sX95dPeDEedAhnJsTUI +55XP3UyeiQsWJOpreMh5uNmr8fxoLAc1hlZnZyUQ2zw +--- g+cGlQYSZ3ZTO4HQaSVyt6BN1HlIt6oqcTTEYgpuxlw +&{?8@yԷ-b9)~І Jpث*􊲝rssWeOR[r0) \ No newline at end of file diff --git a/secrets/rsync-ssh-nas.age b/secrets/rsync-ssh-nas.age index 4c84a2e..c768b22 100644 Binary files a/secrets/rsync-ssh-nas.age and b/secrets/rsync-ssh-nas.age differ diff --git a/secrets/ssh-remote-builder.age b/secrets/ssh-remote-builder.age index b4c94a4..4878d95 100644 Binary files a/secrets/ssh-remote-builder.age and b/secrets/ssh-remote-builder.age differ diff --git a/secrets/test/ssh_host_ed25519_key.age b/secrets/test/ssh_host_ed25519_key.age new file mode 100644 index 0000000..f36df9c Binary files /dev/null and b/secrets/test/ssh_host_ed25519_key.age differ diff --git a/secrets/test/wireguard.age b/secrets/test/wireguard.age new file mode 100644 index 0000000..f5d3937 Binary files /dev/null and b/secrets/test/wireguard.age differ diff --git a/tools/provision-nixos.py b/tools/provision-nixos.py new file mode 100644 index 0000000..9946f03 --- /dev/null +++ b/tools/provision-nixos.py @@ -0,0 +1,87 @@ +#!/usr/bin/env -S uv run +# /// script +# dependencies = [] +# /// + +import argparse +import subprocess +import tempfile +import shutil +from pathlib import Path + +def run_cmd(cmd, capture=False): + """Run a command and optionally capture output.""" + if capture: + result = subprocess.run(cmd, shell=True, capture_output=True, text=True, check=True) + return result.stdout.strip() + else: + subprocess.run(cmd, shell=True, check=True) + + +def derive_public_key(private_key_path): + """Derive public key from private key.""" + return run_cmd(f"ssh-keygen -y -f {private_key_path}", capture=True) + + +def main(): + parser = argparse.ArgumentParser(description='Deploy NixOS using nixos-anywhere') + parser.add_argument('--flake', required=True, help='Name of the flake configuration') + parser.add_argument('--target-ip', required=True, help='IP address of target VM') + args = parser.parse_args() + + flake_name = args.flake + target_ip = args.target_ip + + temp_dir = Path(tempfile.mkdtemp()) + + try: + initrd_ssh_dir = temp_dir / "persist" / "secrets" + initrd_ssh_dir.mkdir(parents=True) + + host_ssh_dir = temp_dir / "persist" / "etc" / "ssh" + host_ssh_dir.mkdir(parents=True) + + ssh_key = run_cmd(f"passage show hosts/{flake_name}/ssh/ssh_host_ed25519_key", capture=True) + + initrd_ssh_key_path = initrd_ssh_dir / "ssh_host_ed25519_key" + initrd_ssh_key_path.write_text(ssh_key + '\n') + initrd_ssh_key_path.chmod(0o600) + + host_ssh_key_path = host_ssh_dir / "ssh_host_ed25519_key" + host_ssh_key_path.write_text(ssh_key + '\n') + host_ssh_key_path.chmod(0o600) + + public_key = derive_public_key(host_ssh_key_path) + + initrd_ssh_pub_path = initrd_ssh_dir / "ssh_host_ed25519_key.pub" + initrd_ssh_pub_path.write_text(public_key + '\n') + initrd_ssh_pub_path.chmod(0o644) + + host_ssh_pub_path = host_ssh_dir / "ssh_host_ed25519_key.pub" + host_ssh_pub_path.write_text(public_key + '\n') + host_ssh_pub_path.chmod(0o644) + + disk_key = run_cmd(f"passage show hosts/{flake_name}/disk-encryption/passphrase", capture=True) + disk_key_file = Path("/tmp/disk.key") + disk_key_file.write_text(disk_key) + disk_key_file.chmod(0o600) + + cmd = [ + "nix", "run", "github:nix-community/nixos-anywhere", "--", + "--flake", f".#{flake_name}", + "--build-on", "remote", + "--disk-encryption-keys", "/tmp/disk.key", str(disk_key_file), + "--target-host", f"root@{target_ip}", + "--extra-files", str(temp_dir) + ] + + subprocess.run(cmd, check=True) + + finally: + # Clean up + shutil.rmtree(temp_dir) + if Path("/tmp/disk.key").exists(): + Path("/tmp/disk.key").unlink() + +if __name__ == "__main__": + main() -- cgit v1.2.3