Archived
Compare commits
5
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7e51168d1b | ||
|
|
ab5206b1c7 | ||
|
|
2041557ab3 | ||
|
|
2fd483697b | ||
|
|
89186b0dee |
@@ -93,9 +93,10 @@ Beyond `codex-setup.sh`/`codex-maintenance.sh` above, `scripts/` also has:
|
||||
(`--force-rebuild` to skip that and always rebuild), and probes
|
||||
nix-cache's substituter/remote-builder reachability once up front rather
|
||||
than letting every `nix build` call retry against it individually.
|
||||
Refuses to create a target whose host identity already has a real
|
||||
deployment elsewhere (`variables.nix`'s `deployedTargets`) unless
|
||||
`--allow-duplicate-host` is passed. `--dry-run` throughout both modes.
|
||||
Refuses to create a target whose host identity already exists live on
|
||||
the node (checked directly via `qm`/`pct`, not any file in this repo)
|
||||
unless `--allow-duplicate-host` is passed. `--dry-run` throughout both
|
||||
modes.
|
||||
- `scripts/env.sh` — shared config (`PROXMOX_HOST`, storage pool, bridge,
|
||||
default cores/memory) sourced by `create-proxmox-resource.sh`. Add new
|
||||
cross-script config here instead of duplicating it per-script.
|
||||
|
||||
@@ -28,10 +28,12 @@ list:
|
||||
| `proxmox-pxe-boot` / `lxc-pxe-boot` | HTTP/iPXE boot asset host (`proxmox-pxe-boot` is the real, deployed one — previously the flat `pxe-boot` target) |
|
||||
| `linode-tailscale-exit-node` / `proxmox-tailscale-exit-node` / `lxc-tailscale-exit-node` | Tailscale exit node (no deployed target yet; `lxc-tailscale-exit-node` is the one planned for actual use) |
|
||||
|
||||
The "(real, deployed)" targets above are also tracked machine-readably in
|
||||
`variables.nix`'s `deployedTargets` — keep both in sync when a deployment
|
||||
changes. `scripts/create-proxmox-resource.sh` reads that list to refuse
|
||||
creating a same-identity duplicate of an already-deployed host by accident.
|
||||
This table is the only place "(real, deployed)" status is tracked — there's
|
||||
no separate machine-readable copy to keep in sync. `scripts/create-proxmox-resource.sh`
|
||||
guards against creating a same-identity duplicate of an already-deployed host
|
||||
by checking the Proxmox node itself (live `qm`/`pct` state) rather than any
|
||||
file in this repo, since a static list can't track whether a resource still
|
||||
actually exists.
|
||||
|
||||
Each buildtype's `hosts/<name>/host.nix` carries the per-machine identity
|
||||
(hostname, hostId, per-machine secrets, `system.stateVersion`) that must stay
|
||||
|
||||
@@ -1,22 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export NIX_CONFIG="${NIX_CONFIG:-}
|
||||
experimental-features = nix-command flakes
|
||||
accept-flake-config = false
|
||||
warn-dirty = false
|
||||
"
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib/nix-bootstrap.sh
|
||||
source "${script_dir}/lib/nix-bootstrap.sh"
|
||||
# shellcheck source=lib/nix-eval.sh
|
||||
source "${script_dir}/lib/nix-eval.sh"
|
||||
|
||||
MODE="${1:-validate}"
|
||||
|
||||
ensure_nix_profile() {
|
||||
if [ -f /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh ]; then
|
||||
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
||||
elif [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then
|
||||
. "$HOME/.nix-profile/etc/profile.d/nix.sh"
|
||||
fi
|
||||
}
|
||||
|
||||
ensure_nix_profile
|
||||
|
||||
if ! command -v nix >/dev/null 2>&1; then
|
||||
@@ -24,8 +16,7 @@ if ! command -v nix >/dev/null 2>&1; then
|
||||
exit 127
|
||||
fi
|
||||
|
||||
hosts_json="$(nix eval --json --no-use-registries --no-accept-flake-config .#nixosConfigurations --apply builtins.attrNames)"
|
||||
hosts="$(echo "$hosts_json" | jq -r '.[]')"
|
||||
hosts="$(list_flake_targets .)"
|
||||
|
||||
echo "Hosts:"
|
||||
echo "$hosts"
|
||||
@@ -44,17 +35,17 @@ fi
|
||||
|
||||
echo
|
||||
echo "Checking Nix formatting with nixpkgs-fmt..."
|
||||
nix run --no-use-registries --no-accept-flake-config github:NixOS/nixpkgs/nixos-25.11#nixpkgs-fmt -- --check .
|
||||
nix run "${NIX_EVAL_FLAGS[@]}" github:NixOS/nixpkgs/nixos-25.11#nixpkgs-fmt -- --check .
|
||||
|
||||
echo
|
||||
echo "Running statix lint..."
|
||||
nix run --no-use-registries --no-accept-flake-config github:NixOS/nixpkgs/nixos-25.11#statix -- check .
|
||||
nix run "${NIX_EVAL_FLAGS[@]}" github:NixOS/nixpkgs/nixos-25.11#statix -- check .
|
||||
|
||||
echo
|
||||
echo "Evaluating host toplevel derivations..."
|
||||
for host in $hosts; do
|
||||
echo "==> $host"
|
||||
nix eval --raw --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.toplevel.drvPath"
|
||||
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.toplevel.drvPath"
|
||||
|
||||
# lxc-* hosts deploy via a directly pct-restore-able tarball instead of
|
||||
# nixos-install (see docs/auto-installer.md); proxmox-* hosts can
|
||||
@@ -64,22 +55,21 @@ for host in $hosts; do
|
||||
case "$host" in
|
||||
lxc-*)
|
||||
echo "==> $host (tarball)"
|
||||
nix eval --raw --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.tarball.drvPath"
|
||||
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.tarball.drvPath"
|
||||
;;
|
||||
proxmox-*)
|
||||
echo "==> $host (diskoImagesScript)"
|
||||
nix eval --raw --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.diskoImagesScript.drvPath"
|
||||
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.diskoImagesScript.drvPath"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo
|
||||
echo "Evaluating buildable packages..."
|
||||
packages_json="$(nix eval --json --no-use-registries --no-accept-flake-config .#packages.x86_64-linux --apply builtins.attrNames)"
|
||||
packages="$(echo "$packages_json" | jq -r '.[]')"
|
||||
packages="$(nix eval --json "${NIX_EVAL_FLAGS[@]}" .#packages.x86_64-linux --apply builtins.attrNames | jq -r '.[]')"
|
||||
for pkg in $packages; do
|
||||
echo "==> packages.x86_64-linux.${pkg}"
|
||||
nix eval --raw --no-use-registries --no-accept-flake-config ".#packages.x86_64-linux.${pkg}"
|
||||
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#packages.x86_64-linux.${pkg}"
|
||||
done
|
||||
|
||||
if [[ "$MODE" == "dry-run" ]]; then
|
||||
@@ -87,16 +77,16 @@ if [[ "$MODE" == "dry-run" ]]; then
|
||||
echo "Running dry-run builds for all hosts. This will not create result symlinks."
|
||||
for host in $hosts; do
|
||||
echo "==> Dry-run build: $host"
|
||||
nix build --dry-run --no-link --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.toplevel"
|
||||
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.toplevel"
|
||||
|
||||
case "$host" in
|
||||
lxc-*)
|
||||
echo "==> Dry-run build: $host (tarball)"
|
||||
nix build --dry-run --no-link --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.tarball"
|
||||
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.tarball"
|
||||
;;
|
||||
proxmox-*)
|
||||
echo "==> Dry-run build: $host (diskoImagesScript)"
|
||||
nix build --dry-run --no-link --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.diskoImagesScript"
|
||||
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.diskoImagesScript"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
@@ -105,7 +95,7 @@ if [[ "$MODE" == "dry-run" ]]; then
|
||||
echo "Running dry-run builds for all packages."
|
||||
for pkg in $packages; do
|
||||
echo "==> Dry-run build: packages.x86_64-linux.${pkg}"
|
||||
nix build --dry-run --no-link --no-use-registries --no-accept-flake-config ".#packages.x86_64-linux.${pkg}"
|
||||
nix build --dry-run --no-link "${NIX_EVAL_FLAGS[@]}" ".#packages.x86_64-linux.${pkg}"
|
||||
done
|
||||
fi
|
||||
|
||||
|
||||
+7
-15
@@ -1,19 +1,11 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
export NIX_CONFIG="${NIX_CONFIG:-}
|
||||
experimental-features = nix-command flakes
|
||||
accept-flake-config = false
|
||||
warn-dirty = false
|
||||
"
|
||||
|
||||
ensure_nix_profile() {
|
||||
if [ -f /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh ]; then
|
||||
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
||||
elif [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then
|
||||
. "$HOME/.nix-profile/etc/profile.d/nix.sh"
|
||||
fi
|
||||
}
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# shellcheck source=lib/nix-bootstrap.sh
|
||||
source "${script_dir}/lib/nix-bootstrap.sh"
|
||||
# shellcheck source=lib/nix-eval.sh
|
||||
source "${script_dir}/lib/nix-eval.sh"
|
||||
|
||||
install_nix_if_missing() {
|
||||
if command -v nix >/dev/null 2>&1; then
|
||||
@@ -79,13 +71,13 @@ if ! command -v jq >/dev/null 2>&1; then
|
||||
fi
|
||||
|
||||
echo "Available NixOS hosts:"
|
||||
hosts="$(nix eval --json --no-use-registries --no-accept-flake-config .#nixosConfigurations --apply builtins.attrNames | jq -r '.[]')"
|
||||
hosts="$(list_flake_targets .)"
|
||||
echo "$hosts"
|
||||
|
||||
echo "Evaluating all host toplevel derivations..."
|
||||
for host in $hosts; do
|
||||
echo "==> Evaluating $host"
|
||||
nix eval --raw --no-use-registries --no-accept-flake-config ".#nixosConfigurations.${host}.config.system.build.toplevel.drvPath"
|
||||
nix eval --raw "${NIX_EVAL_FLAGS[@]}" ".#nixosConfigurations.${host}.config.system.build.toplevel.drvPath"
|
||||
done
|
||||
|
||||
echo "Codex setup complete."
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
#
|
||||
# SAFETY:
|
||||
# - The default (create) mode only ever creates a NEW resource -- it
|
||||
# refuses to run if the target VMID already exists on the node.
|
||||
# refuses to run if the target VMID already exists on the node, or if
|
||||
# a VM/CT identified as --host already exists under any other VMID
|
||||
# (checked live against the node; --allow-duplicate-host overrides).
|
||||
# - --modify only ever touches a resource you name explicitly via
|
||||
# --vmid, shows exactly what will change first, and (outside
|
||||
# --dry-run) always requires typing that VMID back to confirm before
|
||||
@@ -24,6 +26,8 @@ set -euo pipefail
|
||||
repo_root="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
# shellcheck source=env.sh
|
||||
source "${repo_root}/scripts/env.sh"
|
||||
# shellcheck source=lib/nix-eval.sh
|
||||
source "${repo_root}/scripts/lib/nix-eval.sh"
|
||||
|
||||
sync_keys="${repo_root}/scripts/sync-host-keys.sh"
|
||||
|
||||
@@ -56,10 +60,11 @@ Create mode (default):
|
||||
--force-rebuild Skip the "does the node already have this
|
||||
image" check -- always build fresh and
|
||||
overwrite what's there.
|
||||
--allow-duplicate-host Required if --host already has a real
|
||||
deployment elsewhere (variables.nix's
|
||||
deployedTargets) -- otherwise refused, since
|
||||
it'd share that host's hostName/hostId.
|
||||
--allow-duplicate-host Required if a VM/CT identified as --host
|
||||
already exists on the node (checked live via
|
||||
qm/pct, not any file in this repo) --
|
||||
otherwise refused, since it'd share that
|
||||
host's hostName/hostId.
|
||||
|
||||
Modify mode (reconfigure an EXISTING resource -- requires --modify):
|
||||
--modify Switch to modify mode.
|
||||
@@ -237,15 +242,21 @@ platform_prefix="lxc"
|
||||
[[ -z "$memory" ]] && memory="$PROXMOX_DEFAULT_MEMORY_MB"
|
||||
|
||||
# --- discover / resolve the flake target from --host --------------------
|
||||
# Emits "<target>\t<hostName>" pairs for every ${platform_prefix}-* flake
|
||||
# target -- the one source both --list and the --host lookup below read
|
||||
# from, so they can never see a different set of targets from each other.
|
||||
targets_for_platform() {
|
||||
local target
|
||||
for target in $(list_flake_targets "$repo_root" 2>/dev/null | grep -- "^${platform_prefix}-"); do
|
||||
printf '%s\t%s\n' "$target" "$(flake_target_hostname "$repo_root" "$target")"
|
||||
done
|
||||
}
|
||||
|
||||
list_hosts() {
|
||||
local target hostname
|
||||
for target in $(nix eval --json --no-use-registries --no-accept-flake-config \
|
||||
"${repo_root}#nixosConfigurations" --apply builtins.attrNames 2>/dev/null \
|
||||
| jq -r --arg p "${platform_prefix}-" '.[] | select(startswith($p))'); do
|
||||
hostname="$(nix eval --raw --no-use-registries --no-accept-flake-config \
|
||||
"${repo_root}#nixosConfigurations.${target}.config.networking.hostName" 2>/dev/null)"
|
||||
while IFS=$'\t' read -r target hostname; do
|
||||
printf ' %-12s -> %s\n' "$hostname" "$target"
|
||||
done
|
||||
done < <(targets_for_platform)
|
||||
}
|
||||
|
||||
if [[ "$do_list" -eq 1 ]]; then
|
||||
@@ -260,16 +271,12 @@ if [[ -z "$host" ]]; then
|
||||
fi
|
||||
|
||||
flake_target=""
|
||||
for target in $(nix eval --json --no-use-registries --no-accept-flake-config \
|
||||
"${repo_root}#nixosConfigurations" --apply builtins.attrNames \
|
||||
| jq -r --arg p "${platform_prefix}-" '.[] | select(startswith($p))'); do
|
||||
hn="$(nix eval --raw --no-use-registries --no-accept-flake-config \
|
||||
"${repo_root}#nixosConfigurations.${target}.config.networking.hostName")"
|
||||
if [[ "$hn" == "$host" ]]; then
|
||||
while IFS=$'\t' read -r target hostname; do
|
||||
if [[ "$hostname" == "$host" ]]; then
|
||||
flake_target="$target"
|
||||
break
|
||||
fi
|
||||
done
|
||||
done < <(targets_for_platform)
|
||||
|
||||
if [[ -z "$flake_target" ]]; then
|
||||
echo "ERROR: no ${platform_prefix}-* target has hostName '${host}'." >&2
|
||||
@@ -286,25 +293,57 @@ fi
|
||||
# feeds straight into the guest's real hostname) disagree with host.nix.
|
||||
[[ -z "$name" ]] && name="$host"
|
||||
|
||||
# --- refuse to duplicate a host that's already really deployed ----------
|
||||
# Checked by hostName, not exact flake target: proxmox-server being
|
||||
# deployed also blocks --type lxc --host server, since both would carry
|
||||
# the same hosts/server/host.nix identity (hostName, hostId).
|
||||
if [[ "$allow_duplicate_host" -eq 0 ]]; then
|
||||
deployed_targets_json="$(nix eval --json --no-use-registries --no-accept-flake-config \
|
||||
--file "${repo_root}/variables.nix" deployedTargets)"
|
||||
for dt in $(echo "$deployed_targets_json" | jq -r '.[]'); do
|
||||
dt_hostname="$(nix eval --raw --no-use-registries --no-accept-flake-config \
|
||||
"${repo_root}#nixosConfigurations.${dt}.config.networking.hostName" 2>/dev/null || true)"
|
||||
if [[ "$dt_hostname" == "$host" ]]; then
|
||||
echo "ERROR: '${host}' already has a real deployment (${dt}, per variables.nix's" >&2
|
||||
echo "deployedTargets). Creating ${flake_target} would share its hostName/hostId --" >&2
|
||||
echo "refusing by default. Pass --allow-duplicate-host if you really mean to spin" >&2
|
||||
echo "up a separate test instance of this host (it'll still get its own distinct" >&2
|
||||
echo "sops key and VMID, never touching ${dt})." >&2
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
# --- refuse to duplicate a host that's already live on the node ---------
|
||||
# Queries the node itself (qm/pct's own name/hostname config), not any
|
||||
# static list in this repo -- a file can't track whether a resource still
|
||||
# actually exists, and this used to be checked against variables.nix's
|
||||
# deployedTargets, which drifted stale (it kept naming a VM as "the real
|
||||
# deployment" well after that VM had been destroyed, blocking its own
|
||||
# redeploy) until that list was dropped in favour of this live check. This
|
||||
# only catches guests identified with the default --name (== --host, what
|
||||
# this script itself always uses unless --name is overridden) -- a guest
|
||||
# manually renamed on the node afterwards wouldn't match, but nothing here
|
||||
# creates guests that way.
|
||||
if [[ "$allow_duplicate_host" -eq 1 ]]; then
|
||||
echo
|
||||
echo "--allow-duplicate-host: skipping the check for an existing '${host}' on ${node}."
|
||||
elif [[ "$dry_run" -eq 1 ]]; then
|
||||
echo
|
||||
echo "[dry-run] would check ${node} for an existing VM/CT identified as '${host}'"
|
||||
else
|
||||
echo
|
||||
echo "==> Checking ${node} for an existing VM/CT identified as '${host}'..."
|
||||
ssh_check_status=0
|
||||
existing="$(ssh "$ssh_target" bash -s -- "$host" <<'REMOTE_SCRIPT'
|
||||
target="$1"
|
||||
for id in $(qm list 2>/dev/null | awk 'NR>1{print $1}'); do
|
||||
n="$(qm config "$id" 2>/dev/null | grep -oP '^name:\s*\K\S+' || true)"
|
||||
[[ "$n" == "$target" ]] && echo "vm ${id} ${n}"
|
||||
done
|
||||
for id in $(pct list 2>/dev/null | awk 'NR>1{print $1}'); do
|
||||
n="$(pct config "$id" 2>/dev/null | grep -oP '^hostname:\s*\K\S+' || true)"
|
||||
[[ "$n" == "$target" ]] && echo "lxc ${id} ${n}"
|
||||
done
|
||||
REMOTE_SCRIPT
|
||||
)" || ssh_check_status=$?
|
||||
if [[ "$ssh_check_status" -ne 0 ]]; then
|
||||
echo "ERROR: couldn't reach ${node} (ssh exited ${ssh_check_status}) to check for an" >&2
|
||||
echo "existing '${host}' resource -- refusing to guess. Fix connectivity and retry," >&2
|
||||
echo "or pass --allow-duplicate-host if you're sure none exists (this skips the" >&2
|
||||
echo "check entirely)." >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ -n "$existing" ]]; then
|
||||
echo "ERROR: '${host}' already exists on ${node}:" >&2
|
||||
echo "$existing" | while read -r kind id n; do
|
||||
echo " - ${kind} VMID ${id} (${n})" >&2
|
||||
done
|
||||
echo "Refusing to create a second resource sharing this identity. Pass" >&2
|
||||
echo "--allow-duplicate-host to create one anyway (it gets its own distinct" >&2
|
||||
echo "sops key and VMID -- the existing resource(s) above are left untouched)," >&2
|
||||
echo "or use --modify to reconfigure the existing one instead." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Target: ${flake_target} (host=${host}, type=${type}) -> Proxmox resource '${name}'"
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared Nix bootstrap for scripts/codex-setup.sh and
|
||||
# scripts/codex-maintenance.sh: the nix.conf settings both need in effect
|
||||
# before a single `nix` command runs (flakes enabled, never honor a flake
|
||||
# input's own nixConfig, no "dirty tree" warning spam), plus a helper to
|
||||
# pull an already-installed Nix's daemon/profile script onto PATH if it
|
||||
# isn't there yet. Source this instead of copying it -- see CLAUDE.md.
|
||||
export NIX_CONFIG="${NIX_CONFIG:-}
|
||||
experimental-features = nix-command flakes
|
||||
accept-flake-config = false
|
||||
warn-dirty = false
|
||||
"
|
||||
|
||||
ensure_nix_profile() {
|
||||
if [ -f /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh ]; then
|
||||
. /nix/var/nix/profiles/default/etc/profile.d/nix-daemon.sh
|
||||
elif [ -f "$HOME/.nix-profile/etc/profile.d/nix.sh" ]; then
|
||||
. "$HOME/.nix-profile/etc/profile.d/nix.sh"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared flake-introspection helpers for scripts/*.sh. Source alongside
|
||||
# env.sh:
|
||||
# source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/lib/nix-eval.sh"
|
||||
#
|
||||
# NIX_EVAL_FLAGS: --no-use-registries so a call here never resolves through
|
||||
# the user's global flake registry (every call targets this repo's own
|
||||
# flake, or an explicit github: ref, not a registry alias); --no-accept-flake-config
|
||||
# so a flake input's own nixConfig (e.g. a dependency's substituters) is
|
||||
# never honored -- matches accept-flake-config = false already set repo-wide
|
||||
# (see lib/nix-bootstrap.sh / CLAUDE.md). Reuse this array rather than
|
||||
# retyping the two flags at each call site.
|
||||
declare -a NIX_EVAL_FLAGS=(--no-use-registries --no-accept-flake-config)
|
||||
|
||||
# list_flake_targets <flake_ref>
|
||||
# Prints the attribute names under <flake_ref>#nixosConfigurations, one per
|
||||
# line, e.g.:
|
||||
# list_flake_targets . # from inside the repo
|
||||
# list_flake_targets "$repo_root" # from anywhere
|
||||
list_flake_targets() {
|
||||
local flake_ref="$1"
|
||||
nix eval --json "${NIX_EVAL_FLAGS[@]}" \
|
||||
"${flake_ref}#nixosConfigurations" --apply builtins.attrNames \
|
||||
| jq -r '.[]'
|
||||
}
|
||||
|
||||
# flake_target_hostname <flake_ref> <target>
|
||||
# Prints one nixosConfigurations target's config.networking.hostName.
|
||||
# Empty (not an error under set -e) if the target doesn't exist or the
|
||||
# eval otherwise fails -- callers that need to distinguish "empty" from
|
||||
# "eval failed" should check $? themselves instead of relying on this.
|
||||
flake_target_hostname() {
|
||||
local flake_ref="$1" target="$2"
|
||||
nix eval --raw "${NIX_EVAL_FLAGS[@]}" \
|
||||
"${flake_ref}#nixosConfigurations.${target}.config.networking.hostName" 2>/dev/null
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env bash
|
||||
# Shared SSH-host-key / age-conversion helpers for scripts/sync-host-keys.sh
|
||||
# and scripts/prepare-host-key.sh -- both generate the same kind of key
|
||||
# (ed25519, no passphrase, the sops-nix age-derivation input) and convert it
|
||||
# to an age recipient the same way; kept in one place so the two can't
|
||||
# drift apart.
|
||||
#
|
||||
# Uses NIX_OPTS (an array of extra `nix-shell` options -- see env.sh's
|
||||
# nix_extra_opts) if the caller has already set it, so a decision to avoid
|
||||
# an unreachable nix-cache is reused here instead of probed again. Falls
|
||||
# back to no extra options if the caller never sourced env.sh.
|
||||
if ! declare -p NIX_OPTS >/dev/null 2>&1; then
|
||||
declare -a NIX_OPTS=()
|
||||
fi
|
||||
|
||||
# generate_host_ed25519_key <hostname> <keyfile>
|
||||
# Writes <keyfile> and <keyfile>.pub. Caller is responsible for refusing to
|
||||
# overwrite an existing keyfile -- this always runs ssh-keygen fresh.
|
||||
generate_host_ed25519_key() {
|
||||
local hostname="$1" keyfile="$2"
|
||||
nix-shell "${NIX_OPTS[@]}" -p openssh --run \
|
||||
"ssh-keygen -t ed25519 -N '' -C '${hostname}' -f '${keyfile}'" >/dev/null
|
||||
}
|
||||
|
||||
# ssh_pubkey_to_age <pubkeyfile>
|
||||
# Prints the age public key derived from an ed25519 SSH public key file.
|
||||
ssh_pubkey_to_age() {
|
||||
local pubkeyfile="$1"
|
||||
nix-shell "${NIX_OPTS[@]}" -p ssh-to-age --run "ssh-to-age -i '${pubkeyfile}'"
|
||||
}
|
||||
@@ -23,6 +23,10 @@
|
||||
set -euo pipefail
|
||||
|
||||
repo_root="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
# shellcheck source=env.sh
|
||||
source "${repo_root}/scripts/env.sh"
|
||||
# shellcheck source=lib/ssh-host-keys.sh
|
||||
source "${repo_root}/scripts/lib/ssh-host-keys.sh"
|
||||
|
||||
hostname="${1:?usage: scripts/prepare-host-key.sh <hostname>}"
|
||||
sops_yaml="${repo_root}/.sops.yaml"
|
||||
@@ -41,9 +45,10 @@ if [[ -f "$keyfile" ]]; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
nix-shell -p openssh --run "ssh-keygen -t ed25519 -N '' -C '${hostname}' -f '${keyfile}'" >/dev/null
|
||||
nix_extra_opts
|
||||
generate_host_ed25519_key "$hostname" "$keyfile"
|
||||
|
||||
age_pub="$(nix-shell -p ssh-to-age --run "ssh-to-age -i '${keyfile}.pub'")"
|
||||
age_pub="$(ssh_pubkey_to_age "${keyfile}.pub")"
|
||||
|
||||
cat <<EOF
|
||||
|
||||
|
||||
@@ -31,6 +31,10 @@ editor="${repo_root}/scripts/lib/sync-host-keys-edit-sops.py"
|
||||
|
||||
# shellcheck source=env.sh
|
||||
source "${repo_root}/scripts/env.sh"
|
||||
# shellcheck source=lib/nix-eval.sh
|
||||
source "${repo_root}/scripts/lib/nix-eval.sh"
|
||||
# shellcheck source=lib/ssh-host-keys.sh
|
||||
source "${repo_root}/scripts/lib/ssh-host-keys.sh"
|
||||
|
||||
mkdir -p "$keydir"
|
||||
|
||||
@@ -118,12 +122,10 @@ EOF
|
||||
}
|
||||
|
||||
discover_targets() {
|
||||
nix eval --json --no-use-registries --no-accept-flake-config \
|
||||
"${repo_root}#nixosConfigurations" --apply builtins.attrNames \
|
||||
| jq -r '.[] | select(. != "installer")'
|
||||
# installer is the one nixosConfigurations target that doesn't import
|
||||
# sops-nix at all (see CLAUDE.md's "Security Notes" -- hardcoded login
|
||||
# password instead) -- config.sops.secrets doesn't exist for it.
|
||||
list_flake_targets "$repo_root" | grep -v '^installer$'
|
||||
}
|
||||
|
||||
locally_managed_hosts() {
|
||||
@@ -159,7 +161,7 @@ queue_host_sync() {
|
||||
echo "[dry-run] ${host}: would generate host key"
|
||||
else
|
||||
echo "==> ${host}: generating host key"
|
||||
nix-shell "${NIX_OPTS[@]}" -p openssh --run "ssh-keygen -t ed25519 -N '' -C '${host}' -f '${keyfile}'" >/dev/null
|
||||
generate_host_ed25519_key "$host" "$keyfile"
|
||||
fi
|
||||
else
|
||||
echo "==> ${host}: host key already present"
|
||||
@@ -170,7 +172,7 @@ queue_host_sync() {
|
||||
if [[ "$dry_run" -eq 1 ]]; then
|
||||
age_pub="dry-run-placeholder-not-a-real-key"
|
||||
else
|
||||
age_pub="$(nix-shell "${NIX_OPTS[@]}" -p ssh-to-age --run "ssh-to-age -i '${keyfile}.pub'")"
|
||||
age_pub="$(ssh_pubkey_to_age "${keyfile}.pub")"
|
||||
fi
|
||||
add_keys_json="$(jq --arg host "$host" --arg key "$age_pub" \
|
||||
'. + [{host: $host, age_key: $key}]' <<<"$add_keys_json")"
|
||||
|
||||
@@ -156,20 +156,4 @@
|
||||
keep = 20; # number of rotated logs to retain before deleting the oldest
|
||||
};
|
||||
|
||||
# Flake targets with a real, currently-running deployment somewhere —
|
||||
# matches README.md's Hosts table "(real, deployed)" annotations; update
|
||||
# both together. Not consumed by any NixOS module (nothing in the actual
|
||||
# system config should behave differently because of this) — it's read
|
||||
# by scripts/create-proxmox-resource.sh to refuse creating a same-identity
|
||||
# duplicate of an already-deployed host (shared hostName/hostId) unless
|
||||
# you explicitly pass --allow-duplicate-host.
|
||||
deployedTargets = [
|
||||
"linode-minimal"
|
||||
"proxmox-minimal"
|
||||
"lxc-nix-cache"
|
||||
"proxmox-server"
|
||||
"proxmox-docker"
|
||||
"proxmox-gui"
|
||||
"proxmox-pxe-boot"
|
||||
];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user