deploy: auto-fill password prompts from Proton Pass
Every deploy stopped at a password prompt. pass-cli is installed, so read the passwords from the HomeLab vault instead, per command: switch/boot/test darman@<config> -> nixos-rebuild's sudo prompt kexec/install root@<host> -> the target's ssh password nixos-rebuild prompts via getpass(), which reads /dev/tty and ignores a piped stdin, so that one runs under setsid: no controlling terminal means getpass falls back to stdin. kexec wraps the master ssh in `sshpass -e` (scp rides the control socket) and pins password auth so a key can't fall through into a second prompt; install uses nixos-anywhere's own --env-password. Missing pass-cli, a logged-out session, or an absent item all yield an empty string and the original interactive prompt -- nothing becomes mandatory. Passwords never reach a command line.
This commit is contained in:
+85
-11
@@ -19,6 +19,13 @@
|
||||
# SSH host key must be at ~/.config/homelab/<config>/ssh_host_ed25519_key.
|
||||
#
|
||||
# Runs from a non-NixOS host too (nixos-rebuild / nixos-anywhere via `nix run`).
|
||||
#
|
||||
# Password prompts are auto-filled from the "HomeLab" Proton Pass vault when
|
||||
# `pass-cli` is installed and logged in; otherwise every command prompts exactly
|
||||
# as before. Items:
|
||||
# darman@<config> darman's sudo password (switch/boot/test)
|
||||
# root@<host> root's ssh password (kexec/install)
|
||||
# Override with HOMELAB_PASS_ITEM / HOMELAB_PASS_ROOT_ITEM / HOMELAB_PASS_VAULT.
|
||||
set -euo pipefail
|
||||
|
||||
# Locate the repo root (flake dir) regardless of where this script lives on disk.
|
||||
@@ -29,6 +36,26 @@ export PATH="/nix/var/nix/profiles/default/bin:$PATH"
|
||||
|
||||
die() { echo "error: $*" >&2; exit 1; }
|
||||
|
||||
# The password field of a Proton Pass item ("--field password" prints the bare
|
||||
# value, one line), or empty if pass-cli is missing / logged out / has no such
|
||||
# item — every caller then falls back to the normal interactive prompt.
|
||||
proton_pass_password() {
|
||||
local title="$1"
|
||||
command -v pass-cli >/dev/null 2>&1 || return 0
|
||||
pass-cli item view \
|
||||
--vault-name "${HOMELAB_PASS_VAULT:-HomeLab}" \
|
||||
--item-title "$title" \
|
||||
--field password --output human 2>/dev/null | head -1
|
||||
}
|
||||
|
||||
# Path to an sshpass binary (system one, else built from nixpkgs). Empty if
|
||||
# neither is available.
|
||||
sshpass_bin() {
|
||||
command -v sshpass 2>/dev/null && return 0
|
||||
nix build --no-link --print-out-paths nixpkgs#sshpass 2>/dev/null \
|
||||
| sed 's|$|/bin/sshpass|'
|
||||
}
|
||||
|
||||
cmd="${1:-}"; [ -n "$cmd" ] || die "usage: ./deploy <kexec|install|switch|boot|test> ..."
|
||||
|
||||
case "$cmd" in
|
||||
@@ -49,10 +76,32 @@ case "$cmd" in
|
||||
o=(-o ControlMaster=auto -o "ControlPath=$cm" -o ControlPersist=300 \
|
||||
-o StrictHostKeyChecking=accept-new)
|
||||
|
||||
echo ">> connecting to root@$host (enter the root password once)"
|
||||
ssh "${o[@]}" "root@$host" 'mkdir -p /tmp/bin'
|
||||
# Root's password from Proton Pass, fed to ssh/scp via sshpass -e. Only the
|
||||
# first (master) connection authenticates; the rest ride the control socket.
|
||||
# SSHPASS is exported into the sshpass child only, never onto a command line.
|
||||
sp=()
|
||||
root_pw="$(proton_pass_password "${HOMELAB_PASS_ROOT_ITEM:-root@$host}" || true)"
|
||||
if [ -n "$root_pw" ]; then
|
||||
sshpass="$(sshpass_bin || true)"
|
||||
if [ -n "$sshpass" ]; then
|
||||
sp=(env "SSHPASS=$root_pw" "$sshpass" -e)
|
||||
# sshpass drives the password prompt; don't let a key/agent short-circuit
|
||||
# into an interactive one for a host that only accepts passwords.
|
||||
o+=(-o PreferredAuthentications=password -o PubkeyAuthentication=no)
|
||||
else
|
||||
echo ">> sshpass unavailable — falling back to the interactive prompt" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ${#sp[@]} -gt 0 ]; then
|
||||
echo ">> connecting to root@$host (password from Proton Pass)"
|
||||
else
|
||||
echo ">> connecting to root@$host (enter the root password once)"
|
||||
fi
|
||||
"${sp[@]}" ssh "${o[@]}" "root@$host" 'mkdir -p /tmp/bin'
|
||||
scp "${o[@]}" "$cpio" "root@$host:/tmp/bin/cpio"
|
||||
scp "${o[@]}" "$bbox" "root@$host:/tmp/bin/gzip" # busybox as gzip (argv0)
|
||||
unset root_pw
|
||||
|
||||
echo ">> streaming installer + kexec-ing. SSH drops as the box jumps into the"
|
||||
echo " RAM installer. Disks are untouched."
|
||||
@@ -79,11 +128,22 @@ case "$cmd" in
|
||||
install -Dm644 "$hostkey.pub" "$stage/etc/ssh/ssh_host_ed25519_key.pub"
|
||||
|
||||
echo ">> nixos-anywhere .#$config onto root@$host (OS disk WILL be wiped)"
|
||||
nix run github:nix-community/nixos-anywhere -- \
|
||||
--flake ".#$config" \
|
||||
--extra-files "$stage" \
|
||||
--generate-hardware-config nixos-generate-config "./hosts/$config/hardware-configuration.nix" \
|
||||
--target-host "root@$host"
|
||||
anywhere=(--flake ".#$config"
|
||||
--extra-files "$stage"
|
||||
--generate-hardware-config nixos-generate-config "./hosts/$config/hardware-configuration.nix"
|
||||
--target-host "root@$host")
|
||||
|
||||
# nixos-anywhere's --env-password reads root's ssh password from $SSHPASS
|
||||
# (it ships its own sshpass), so a vault hit skips the ssh-copy-id prompt.
|
||||
root_pw="$(proton_pass_password "${HOMELAB_PASS_ROOT_ITEM:-root@$host}" || true)"
|
||||
if [ -n "$root_pw" ]; then
|
||||
echo ">> root ssh password from Proton Pass"
|
||||
env "SSHPASS=$root_pw" nix run github:nix-community/nixos-anywhere -- \
|
||||
--env-password "${anywhere[@]}"
|
||||
else
|
||||
nix run github:nix-community/nixos-anywhere -- "${anywhere[@]}"
|
||||
fi
|
||||
unset root_pw
|
||||
;;
|
||||
|
||||
switch|boot|test)
|
||||
@@ -95,10 +155,24 @@ case "$cmd" in
|
||||
# security.sudo.wheelNeedsPassword = true, and --use-remote-sudo only
|
||||
# prefixes with sudo without ever prompting. Asks for darman's password
|
||||
# (the darman_password hash in each host's sops file).
|
||||
nix run nixpkgs#nixos-rebuild -- "$cmd" \
|
||||
--flake ".#$config" \
|
||||
--target-host "darman@$host" \
|
||||
--ask-sudo-password
|
||||
rebuild=(nix run nixpkgs#nixos-rebuild -- "$cmd"
|
||||
--flake ".#$config"
|
||||
--target-host "darman@$host"
|
||||
--ask-sudo-password)
|
||||
|
||||
item="${HOMELAB_PASS_ITEM:-darman@$config}"
|
||||
pw="$(proton_pass_password "$item" || true)"
|
||||
if [ -n "$pw" ] && command -v setsid >/dev/null 2>&1; then
|
||||
# nixos-rebuild prompts with getpass(), which reads /dev/tty and ignores a
|
||||
# piped stdin. setsid drops the controlling terminal, so getpass falls back
|
||||
# to stdin and takes the vault password (it warns about echo — harmless,
|
||||
# nothing is echoed since the password never reaches the terminal).
|
||||
echo ">> sudo password from Proton Pass ($item)"
|
||||
printf '%s\n' "$pw" | setsid -w "${rebuild[@]}"
|
||||
else
|
||||
"${rebuild[@]}"
|
||||
fi
|
||||
unset pw
|
||||
;;
|
||||
|
||||
image|flash)
|
||||
|
||||
Reference in New Issue
Block a user