From dcfd574f593b1949d4a2423532e1670d9ad4afcb Mon Sep 17 00:00:00 2001 From: Roy Dumblauskas Date: Fri, 16 Oct 2026 00:09:34 -0500 Subject: [PATCH] new build script --- .gitignore | 2 +- nixos/configuration.nix | 5 + virtual-machines/vm-test/build-image.sh | 165 ++++++++++++++++++++---- virtual-machines/vm-test/flake.nix | 4 +- 4 files changed, 150 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index 0ede32e..7071223 100755 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,4 @@ server-info.txt nixos/out nixos/error -virtual-machines/*/result/ +virtual-machines/*/result diff --git a/nixos/configuration.nix b/nixos/configuration.nix index 914acf1..99036f6 100755 --- a/nixos/configuration.nix +++ b/nixos/configuration.nix @@ -206,6 +206,11 @@ # Init with `incus admin init --minimal` virtualisation.incus.enable = true; + # allow sandboxes to see my temp keys + nix.settings.extra-sandbox-paths = [ + "/run/sops-virtual-key.txt=/run/sops-virtual/virtual-key.txt" + ]; + networking = { hostName = meta.hostname; hostId = meta.hostId; diff --git a/virtual-machines/vm-test/build-image.sh b/virtual-machines/vm-test/build-image.sh index bec8b5c..ec5375c 100755 --- a/virtual-machines/vm-test/build-image.sh +++ b/virtual-machines/vm-test/build-image.sh @@ -1,54 +1,176 @@ #!/usr/bin/env bash -set -euo pipefail +set -Eeuo pipefail # Usage: -# ./build-image.sh [vmname] +# ./build-image.sh [vmname] # -# Examples: -# ./build-image.sh nixos/custom/vm -# ./build-image.sh nixos/custom/my-vm my-vm +# Example: +# ./build-image.sh vm-test nixos/custom/jellybean +# +# Run from anywhere; all project operations use virtual-machines/. -if [[ $# -lt 1 || $# -gt 2 ]]; then - echo "Usage: $0 [vmname]" - echo - echo " alias Required Incus image alias" - echo " vmname NixOS configuration name (default: vm)" +SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +if [[ $# -lt 2 || $# -gt 3 ]]; then + echo "Usage: $0 [vmname]" exit 1 fi -ALIAS="$1" -VMNAME="${2:-vm}" +VM_FOLDER="$1" +ALIAS="$2" +VMNAME="${3:-vm}" + +VM_DIR="$SCRIPT_DIR/$VM_FOLDER" +SOPS_CONFIG="$SCRIPT_DIR/.sops.yaml" +AGE_DIR="/run/sops-virtual" +AGE_KEY="$AGE_DIR/virtual-key.txt" +SANDBOX_KEY="/run/sops-virtual-key.txt" + +for cmd in age-keygen sops yq nix incus sudo; do + command -v "$cmd" >/dev/null 2>&1 || { + echo "Missing required command: $cmd" >&2 + exit 1 + } +done + +[[ -d "$VM_DIR" ]] || { + echo "VM directory not found: $VM_DIR" >&2 + exit 1 +} + +[[ -f "$VM_DIR/flake.nix" ]] || { + echo "No flake.nix found in $VM_DIR" >&2 + exit 1 +} + +[[ -f "$SOPS_CONFIG" ]] || { + echo "SOPS config not found: $SOPS_CONFIG" >&2 + exit 1 +} + +[[ -d "$VM_DIR/secrets" ]] || { + echo "Secrets directory not found: $VM_DIR/secrets" >&2 + exit 1 +} + +# Refuse to overwrite a key that may belong to another running build. +if sudo test -e "$AGE_KEY"; then + echo "Temporary key already exists: $AGE_KEY" >&2 + echo "Remove it or investigate the previous build before continuing." >&2 + exit 1 +fi + +SOPS_BACKUP="$(mktemp)" +AGE_TEMP="$(mktemp)" +cp -p "$SOPS_CONFIG" "$SOPS_BACKUP" + +CLEANED_UP=0 + +cleanup() { + local status=$? + + if [[ "$CLEANED_UP" -eq 1 ]]; then + return + fi + CLEANED_UP=1 + + # Restore the repository's original SOPS configuration. + cp "$SOPS_BACKUP" "$SOPS_CONFIG" + rm -f "$SOPS_BACKUP" "$AGE_TEMP" + + # Remove the temporary private key, even if the build failed. + sudo rm -f "$AGE_KEY" + sudo rmdir "$AGE_DIR" 2>/dev/null || true + + echo "Temporary age key removed." + + return "$status" +} + +trap cleanup EXIT +trap 'exit 130' INT +trap 'exit 143' TERM + +echo "Generating temporary age identity..." + +age-keygen -o "$AGE_TEMP" +AGE_PUBLIC_KEY="$(age-keygen -y "$AGE_TEMP")" + +# Make the key readable by Nix build users, but not ordinary users. +sudo install -d -o root -g nixbld -m 0750 "$AGE_DIR" +sudo install -o root -g nixbld -m 0440 \ + "$AGE_TEMP" "$AGE_KEY" + +# Update age recipients in the SOPS creation rules temporarily. +# Requires mikefarah/yq v4 and simple comma-separated age fields. +export AGE_PUBLIC_KEY + +if ! yq -e \ + '.creation_rules | any(.[]; has("age"))' \ + "$SOPS_CONFIG" >/dev/null; then + echo "No creation_rules with an age field found in .sops.yaml" >&2 + exit 1 +fi + +yq -i ' + (.creation_rules[] | select(has("age")) | .age) |= ( + split(",") + | map(trim) + | map(select(. != "")) + | . + [strenv(AGE_PUBLIC_KEY)] + | unique + | join(",") + ) +' "$SOPS_CONFIG" + +echo "Updating SOPS recipients for secrets in $VM_FOLDER..." + +shopt -s nullglob +SECRET_FILES=("$VM_DIR"/secrets/*) +shopt -u nullglob + +if [[ ${#SECRET_FILES[@]} -eq 0 ]]; then + echo "No secret files found in $VM_DIR/secrets" >&2 + exit 1 +fi + +for secret in "${SECRET_FILES[@]}"; do + [[ -f "$secret" ]] || continue + echo "Updating recipients: ${secret#"$SCRIPT_DIR"/}" + sops updatekeys --yes "$secret" +done + +echo "Building VM metadata..." -# Build the NixOS LXC metadata tarball TARBALL_DIR="$(nix build \ - ".#nixosConfigurations.${VMNAME}.config.system.build.metadata" \ + "./$VM_FOLDER#nixosConfigurations.${VMNAME}.config.system.build.metadata" \ --print-out-paths)" -# Find the tarball TARBALL="$(find "$TARBALL_DIR" -type f -name '*.tar.xz' -print -quit)" if [[ -z "$TARBALL" ]]; then - echo "Could not find a .tar.xz tarball in: $TARBALL_DIR" + echo "Could not find a .tar.xz tarball in: $TARBALL_DIR" >&2 exit 1 fi echo "Metadata tarball: $TARBALL" -# Build the QEMU disk image +echo "Building QEMU disk image..." + QCOW2_DIR="$(nix build \ - ".#nixosConfigurations.${VMNAME}.config.system.build.qemuImage" \ + "./$VM_FOLDER#nixosConfigurations.${VMNAME}.config.system.build.qemuImage" \ --print-out-paths)" QCOW2="$QCOW2_DIR/nixos.qcow2" if [[ ! -f "$QCOW2" ]]; then - echo "QEMU image not found: $QCOW2" + echo "QEMU image not found: $QCOW2" >&2 exit 1 fi -echo "Found QEMU image: $QCOW2" +echo "QEMU image: $QCOW2" -echo echo "Importing image into Incus..." incus image import \ @@ -56,6 +178,5 @@ incus image import \ "$TARBALL" \ "$QCOW2" -echo echo "Successfully imported image: $ALIAS" diff --git a/virtual-machines/vm-test/flake.nix b/virtual-machines/vm-test/flake.nix index 00c3e54..ffe7a6f 100644 --- a/virtual-machines/vm-test/flake.nix +++ b/virtual-machines/vm-test/flake.nix @@ -27,9 +27,7 @@ pkgs.runCommand "sops-decrypt-${filename}" { # Use special separate injectable key that can only decrypt these dummy secrets - SOPS_AGE_KEY_FILE = "/home/roy/.config/sops/age/virtual-key.txt"; - - __noChroot = true; + SOPS_AGE_KEY_FILE = "/run/sops-virtual-key.txt"; } '' ${pkgs.sops}/bin/sops -d ${./secrets}/${filename} > $out