installer v1.15.0: DR-tier-by-default sweep (F-7 pbs-apply, F-10 age, F-9 wg default-on, F-1 single version + hub const, F-2 dry-run auth, F-8 rotation pointer, ACL felhom-pbs kept) + hostinstall_gates.py
Claude-Session: https://claude.ai/code/session_01NptTCFtu7dz2Ru89qHRagN
This commit is contained in:
+114
-23
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
#===============================================================================
|
||||
# felhom-host-install.sh v1.14.0
|
||||
# felhom-host-install.sh — version: the SCRIPT_VERSION variable below (single source;
|
||||
# `-h` and the run banner both read it — the v1.14.0/v1.13.0 header/banner drift was drill F-1).
|
||||
# Day-0 host-bootstrap for a Felhom Proxmox host (operator-deploy model).
|
||||
#
|
||||
# Run by the operator on a FRESHLY-PVE-INSTALLED box (after a manual PVE install
|
||||
@@ -19,6 +20,17 @@
|
||||
# new credential. The checksum trust root is the HUB, not Gitea. This removes the
|
||||
# old prerequisite "install the agent binary + unit manually".
|
||||
#
|
||||
# v1.15.0 (DR-tier-by-default, DRILL-day0-vm-2026-07-12 sweep): every install now deploys the FULL
|
||||
# DR plumbing uniformly — capability is baked, activation is a hub flag. F-7: ships the
|
||||
# `felhom-pbs-apply` wrapper (like mkfs/selfupdate — pbsdr capabilities no longer born DEGRADED).
|
||||
# F-10: installs `age` (escrow-ceremony identity-wrap dependency). F-9: renders
|
||||
# `wg_tunnel.enabled: true` (WG is BASE infrastructure like cloudflared, not a DR component —
|
||||
# operator decision 2026-07-12; byo consent moves to the disclosure ack + tester agreement, the
|
||||
# byo wg-off config assert is retired). The default --acl-storages set keeps `felhom-pbs`
|
||||
# (drill-proven: narrowing it 403s the PBS-DR apply-bridge later); a missing felhom-pbs storage
|
||||
# is expected pre-tier and no longer a byo refusal. F-1: single VERSION source (SCRIPT_VERSION).
|
||||
# F-2: dry-run fetch lines show the real auth mode (anonymous vs -u). F-8: step 4b + the final
|
||||
# summary now say OUT LOUD that root@pam was rotated + where to retrieve it.
|
||||
# v1.14.0 (CAMPAIGN-3 Part 6): --mode now also templates the agent's top-level `deployment_mode`
|
||||
# ("appliance" | "byo") — authoritative from --mode, written even over a --preserve-from config, so a
|
||||
# byo box can never inherit an appliance host-service self-heal. Gates the agent's node self-heal
|
||||
@@ -75,10 +87,12 @@
|
||||
# touches root@pam (step 4b skipped), REQUIRES explicit
|
||||
# --cores AND --memory, refuses --enable-oob and
|
||||
# --rotate-recovery, dies on non-9.x PVE (unless --force)
|
||||
# and on --acl-storages entries absent from this box,
|
||||
# and on --acl-storages entries absent from this box
|
||||
# (except felhom-pbs — the PBS-DR tier creates it later),
|
||||
# prints the full host-mutation list and requires typing
|
||||
# the host's short hostname, and asserts the agent config
|
||||
# keeps lan_resolver/wg_tunnel/oob OFF.
|
||||
# keeps lan_resolver/oob OFF (wg_tunnel is ON in BOTH
|
||||
# modes since v1.15.0 — base infra, disclosed in the ack).
|
||||
# (dr the 10D DR stub — documented seam, not implemented.)
|
||||
#
|
||||
# Options:
|
||||
@@ -135,7 +149,7 @@
|
||||
# Uninstall (local host teardown — no hub contact, no passphrase):
|
||||
# --uninstall cleanly revert an install: destroy the Felhom guest, remove the agent
|
||||
# (unit/sudoers/binary/state/config/user + runtime artifacts: shared-parent
|
||||
# unit, mkfs wrapper, hook snippet, dnsmasq snippets), the pveum
|
||||
# unit, mkfs + pbs-apply wrappers, hook snippet, dnsmasq snippets), the pveum
|
||||
# role/user/token/ACL, and the install state file. Refuses a non-Felhom guest (no
|
||||
# /etc/felhom-bootstrap mount) and skips host-level removal if OTHER Felhom
|
||||
# guests remain (both overridable with --force). Typed vmid confirmation
|
||||
@@ -168,7 +182,9 @@
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_VERSION="1.13.0" # keep in sync with the header line at the top of this file
|
||||
SCRIPT_VERSION="1.15.0" # the SINGLE version source (F-1): -h, the run banners, and the hub
|
||||
# Setup-tab copy (hub internal/web/configs.go hostInstallVersion —
|
||||
# scripts/hostinstall_gates.py asserts the two stay equal) all follow it.
|
||||
|
||||
# Operator signing keys pinned at day-0 (GL-4; doc 04 §3 two-key model). EMPTY by default — the pin
|
||||
# CEREMONY is an operator step: generate the real keypairs OFFLINE, then fill these four constants
|
||||
@@ -291,9 +307,14 @@ ART_GOLDEN_SHA=""
|
||||
#-------------------------------------------------------------------------------
|
||||
# Helpers
|
||||
#-------------------------------------------------------------------------------
|
||||
# Print the header through the last option line (the "# State (" line is the end marker — keeps the
|
||||
# range from drifting as the header grows; v1.9.1's numeric '2,95p' had already drifted).
|
||||
usage() { sed -n '2,/^# State (/p' "$0" | sed '$d' | sed 's/^# \{0,1\}//'; exit 0; }
|
||||
# Print the header through the last option line. Markers, not line numbers (v1.9.1's numeric
|
||||
# '2,95p' had drifted): start at the "# Day-0 host-bootstrap" line, end before "# State (".
|
||||
# The version line is printed from SCRIPT_VERSION — the ONLY version source (F-1).
|
||||
usage() {
|
||||
echo "felhom-host-install.sh v${SCRIPT_VERSION}"
|
||||
sed -n '/^# Day-0 host-bootstrap/,/^# State (/p' "$0" | sed '$d' | sed 's/^# \{0,1\}//'
|
||||
exit 0
|
||||
}
|
||||
|
||||
run() { # simple (no pipes/redirects) mutating command
|
||||
if $DRY_RUN; then log_dry "$*"; else "$@"; fi
|
||||
@@ -402,6 +423,12 @@ _git_auth_args() {
|
||||
fi
|
||||
}
|
||||
|
||||
# _dry_auth_hint — the dry-run print's auth stanza (drill F-2): the old hardcoded "curl -u <git>"
|
||||
# lied on the anonymous-fetch branch. Requires resolve_git_creds to have run (both call sites do).
|
||||
_dry_auth_hint() {
|
||||
if [[ -n "$GIT_TOKEN" ]]; then echo "curl -u <git>"; else echo "curl (anonymous — no git credential)"; fi
|
||||
}
|
||||
|
||||
# Fetch a Gitea generic-package URL to a dest (authed when a credential exists, else anonymous),
|
||||
# then VERIFY its sha256 against the expected (hub-vouched) value. Aborts on any mismatch —
|
||||
# verify-before-use; the sha is the integrity root either way. $1=url $2=dest $3=expected_sha
|
||||
@@ -599,7 +626,7 @@ _uninstall_statement() {
|
||||
if [[ "$scope" == "full" ]]; then
|
||||
echo " - the felhom-agent: binary, unit, sudoers, config (+ its .bak backups), state dir, service user"
|
||||
echo " - self-update artifacts: guarded wrapper, A/B slots (.prev/.new.*), rollback unit, start-limit drop-in"
|
||||
echo " - break-glass watchdog + OOB artifacts (where present); guest-hook snippet; dnsmasq snippets"
|
||||
echo " - break-glass watchdog + OOB artifacts (where present); guest-hook snippet; dnsmasq snippets; the mkfs + pbs-apply wrappers"
|
||||
echo " - pveum: the Felhom roles/user/token/scoped ACL$( $pool_removed && printf '; the emptied %s pool' "$PVE_POOL")"
|
||||
echo " - the install state file"
|
||||
if $REMOVE_GOLDEN; then echo " - the golden vzdump (--remove-golden)"; fi
|
||||
@@ -856,8 +883,9 @@ run_uninstall() {
|
||||
fi
|
||||
if [[ -d /mnt/felhom-drives ]]; then run rmdir /mnt/felhom-drives 2>/dev/null || true; fi
|
||||
|
||||
# 4d. Guarded-mkfs wrapper, guest-hook snippet, lan-resolver dnsmasq snippets (drill R3-R5).
|
||||
# 4d. Guarded-mkfs + pbs-apply wrappers, guest-hook snippet, lan-resolver dnsmasq snippets (drill R3-R5).
|
||||
if [[ -f /usr/local/sbin/felhom-mkfs-guarded ]]; then run rm -f /usr/local/sbin/felhom-mkfs-guarded; else log_skip " felhom-mkfs-guarded already absent"; fi
|
||||
if [[ -f /usr/local/sbin/felhom-pbs-apply ]]; then run rm -f /usr/local/sbin/felhom-pbs-apply; else log_skip " felhom-pbs-apply already absent"; fi
|
||||
if [[ -f /var/lib/vz/snippets/felhom-guest-hook.sh ]]; then run rm -f /var/lib/vz/snippets/felhom-guest-hook.sh; fi
|
||||
local dconf _dnsmasq_touched=false
|
||||
for dconf in /etc/dnsmasq.d/felhom-*.conf; do
|
||||
@@ -923,7 +951,7 @@ run_uninstall() {
|
||||
|
||||
# 8. Summary + the GL-4 kept-vs-wiped statement.
|
||||
_uninstall_statement full
|
||||
log_success "UNINSTALL complete — removed: guest $vmid, the felhom-agent (unit/sudoers/binary/state/config+baks/user + selfupdate-artifacts/shared-parent/mkfs-wrapper/hook-snippet/dnsmasq-snippets), the pveum role/user/token/ACL,$( $pool_removed && printf ' the %s pool,' "$PVE_POOL") and $STATE_FILE."
|
||||
log_success "UNINSTALL complete — removed: guest $vmid, the felhom-agent (unit/sudoers/binary/state/config+baks/user + selfupdate-artifacts/shared-parent/mkfs-wrapper/pbs-apply-wrapper/hook-snippet/dnsmasq-snippets), the pveum role/user/token/ACL,$( $pool_removed && printf ' the %s pool,' "$PVE_POOL") and $STATE_FILE."
|
||||
if $REMOVE_GOLDEN; then log_info " golden vzdump: removed."; else log_info " golden vzdump: left in place (--remove-golden to remove)."; fi
|
||||
log_info " NOTE: the 'sudo' and 'dnsmasq' packages were left installed (system packages); the host record still exists in the hub — remove it there if desired."
|
||||
$DRY_RUN && log_warn " DRY-RUN: nothing above was actually executed."
|
||||
@@ -1239,9 +1267,14 @@ _byo_disclosure_ack() {
|
||||
+ ${AGENT_UNIT} (+ felhom-agent-rollback.service + start-limit drop-in)
|
||||
+ ${AGENT_SUDOERS} + state dir ${AGENT_STATE_DIR} + config ${AGENT_CONFIG} (0600)
|
||||
host: /usr/local/sbin/felhom-mkfs-guarded + /usr/local/sbin/felhom-selfupdate-guarded
|
||||
+ /usr/local/sbin/felhom-pbs-apply (PBS-DR apply wrapper — DR capability is baked
|
||||
on every install; ACTIVATION stays a hub flag, off = zero effect on this host)
|
||||
+ felhom-mgmt-watchdog service+timer+script + /etc/tmpfiles.d/felhom-privsep.conf
|
||||
+ guest-hook snippet under /var/lib/vz/snippets/ (agent-installed at runtime)
|
||||
+ the 'sudo' package if absent + install state dir ${STATE_DIR}
|
||||
+ the 'sudo' and 'age' packages if absent + install state dir ${STATE_DIR}
|
||||
wg: an OUTBOUND WireGuard tunnel to the Felhom hub (wg_tunnel.enabled=true — base
|
||||
infrastructure like the cloudflared tunnel; hands-free peer registration; the
|
||||
operator can disable it per-customer hub-side on request)
|
||||
EOF
|
||||
if $SKIP_PROVISION; then
|
||||
echo " guest: none (--skip-provision)"
|
||||
@@ -1250,7 +1283,7 @@ EOF
|
||||
echo " + the golden vzdump imported onto storage '${ARCHIVE_STORAGE}'"
|
||||
fi
|
||||
echo " update: operator-signed self-update authority: ${RESOLVED_OP_ID:-NONE (self-update stays dormant)}"
|
||||
echo " NOT touched in byo mode: root@pam (no break-glass), host DNS (:53), WireGuard tunnels, OOB sshd."
|
||||
echo " NOT touched in byo mode: root@pam (no break-glass), host DNS (:53), OOB sshd."
|
||||
echo ""
|
||||
if $DRY_RUN; then
|
||||
log_dry "would prompt: Type this host's short hostname ($(hostname -s)) to acknowledge the byo install"
|
||||
@@ -1367,10 +1400,20 @@ step_preflight() {
|
||||
|
||||
# --acl-storages existence (GL-2): the scoped ACL grants Datastore.* at each of these paths — a
|
||||
# grant on a storage this box doesn't have silently protects nothing (campaign-2 R2 class). byo
|
||||
# refuses; appliance warns (the default set includes felhom-pbs, absent on most customer boxes).
|
||||
# refuses; appliance warns. EXCEPTION (v1.15.0, drill retrofit-ACL lesson): `felhom-pbs` is
|
||||
# EXPECTED absent on a fresh box — the PBS-DR tier apply creates it later, and the grant MUST
|
||||
# be pre-positioned or the apply-bridge 403s (Datastore.Allocate on /storage/felhom-pbs — the
|
||||
# drill hit exactly this after narrowing --acl-storages). Do NOT narrow the set to silence
|
||||
# the info line.
|
||||
local _missing_storages=() _acls
|
||||
for _acls in "${PVE_STORAGES[@]}"; do
|
||||
pvesm status --storage "$_acls" >/dev/null 2>&1 || _missing_storages+=("$_acls")
|
||||
if ! pvesm status --storage "$_acls" >/dev/null 2>&1; then
|
||||
if [[ "$_acls" == "felhom-pbs" ]]; then
|
||||
log_info " acl storage 'felhom-pbs' not present yet — expected: the PBS-DR tier creates it; the grant is pre-positioned deliberately"
|
||||
else
|
||||
_missing_storages+=("$_acls")
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [[ ${#_missing_storages[@]} -gt 0 ]]; then
|
||||
if [[ "$MODE" == "byo" ]]; then
|
||||
@@ -1380,7 +1423,7 @@ step_preflight() {
|
||||
log_warn " acl storage '$_acls' not found (pvesm status) — its grant will point at nothing"
|
||||
done
|
||||
elif [[ "$MODE" == "byo" ]]; then
|
||||
log_info " acl storages all present: ${PVE_STORAGES[*]}"
|
||||
log_info " acl storages all present or pre-positioned: ${PVE_STORAGES[*]}"
|
||||
fi
|
||||
|
||||
# Host DNS :53 gate (byo only) — GL-8/F6. In byo the agent's own lan-resolver stays OFF (a config
|
||||
@@ -1629,7 +1672,11 @@ step_break_glass() {
|
||||
-H "Authorization: Bearer $HOST_API_KEY" -H 'Content-Type: application/json' -d @- 2>/dev/null)
|
||||
newpw="" # scrub the plaintext from the shell var the moment it is vaulted
|
||||
case "$code" in
|
||||
200) log_success " root@pam password set + vaulted to the hub (retrieve via the operator /admin path; never logged here)" ;;
|
||||
200) log_success " root@pam password set + vaulted to the hub (retrieve via the operator /admin path; never logged here)"
|
||||
# Drill F-8: say this OUT LOUD — the operator was live-surprised by a 401 at the PVE
|
||||
# GUI. Pointer only, never the credential.
|
||||
log_warn " NOTE: the root@pam password just CHANGED — the old one now fails at the PVE web GUI (:8006)."
|
||||
log_warn " Retrieve the new one at hub → host page (vaulted recovery credential)." ;;
|
||||
401|403) die "recovery-credential vault rejected ($code) — host key/authorization problem" ;;
|
||||
*) die "recovery-credential vault failed (HTTP $code)" ;;
|
||||
esac
|
||||
@@ -1661,7 +1708,7 @@ step_agent_install() {
|
||||
local url="$GITEA_BASE/api/packages/$GITEA_OWNER/generic/felhom-agent/$ART_AGENT_VER/felhom-agent"
|
||||
log_info " fetching agent binary v$ART_AGENT_VER from Gitea …"
|
||||
if $DRY_RUN; then
|
||||
log_dry "curl -u <git> -o /tmp/felhom-agent.new $url ; verify sha256=$ART_AGENT_SHA ; install -m0755 -> $AGENT_BIN"
|
||||
log_dry "$(_dry_auth_hint) -o /tmp/felhom-agent.new $url ; verify sha256=$ART_AGENT_SHA ; install -m0755 -> $AGENT_BIN"
|
||||
else
|
||||
local tmp; tmp=$(mktemp -t felhom-agent.XXXXXX)
|
||||
fetch_verify "$url" "$tmp" "$ART_AGENT_SHA"
|
||||
@@ -1689,6 +1736,21 @@ step_agent_install() {
|
||||
log_success " sudo installed ($(sudo --version 2>/dev/null | head -1))"
|
||||
fi
|
||||
fi
|
||||
# `age` (drill F-10) — the escrow ceremony's identity-wrap dependency. NOT installed by PVE by
|
||||
# default; a fresh-box ceremony died on it live (the demo host only had it from its spike-era
|
||||
# install). DR-tier-by-default: baked on every install, both modes. Idempotent.
|
||||
if ! command -v age >/dev/null 2>&1; then
|
||||
if $DRY_RUN; then
|
||||
log_dry "apt-get install -y age # escrow ceremony identity-wrap dependency (F-10)"
|
||||
else
|
||||
log_info " installing the 'age' package (escrow ceremony identity-wrap dependency) …"
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y -q age >/dev/null 2>&1 \
|
||||
|| { apt-get update -q >/dev/null 2>&1; DEBIAN_FRONTEND=noninteractive apt-get install -y -q age >/dev/null 2>&1; } \
|
||||
|| die "failed to install the 'age' package (the escrow ceremony needs it)"
|
||||
log_success " age installed ($(age --version 2>/dev/null | head -1))"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Resolve visudo by absolute path too (non-login SSH PATH can miss /usr/sbin).
|
||||
local VISUDO; VISUDO=$(command -v visudo 2>/dev/null || echo /usr/sbin/visudo)
|
||||
|
||||
@@ -1790,6 +1852,22 @@ step_agent_install() {
|
||||
log_success " installed /usr/local/sbin/felhom-selfupdate-guarded (0755, the guarded A/B binary-swap path)"
|
||||
fi
|
||||
|
||||
# PBS-DR apply wrapper (drill F-7) — the ONLY pbs-apply path the FELHOM_PBSDR sudoers alias
|
||||
# permits. The sudoers has shipped the alias since the PBS-DR slice; the binary itself did not
|
||||
# ship, so the 3 pbsdr-* capabilities were born DEGRADED ("binary not found") on every fresh
|
||||
# box. DR-tier-by-default (2026-07-12): capability is baked on EVERY install (activation is a
|
||||
# hub flag). Install BEFORE the sudoers, 0755 root:root, bash -n first — the mkfs pattern.
|
||||
if $DRY_RUN; then
|
||||
log_dry "fetch configs/felhom-pbs-apply ; bash -n ; install 0755 -> /usr/local/sbin/felhom-pbs-apply"
|
||||
else
|
||||
local patmp; patmp=$(mktemp -t felhom-pbsapply.XXXXXX)
|
||||
fetch_raw "configs/felhom-pbs-apply" "$patmp"
|
||||
bash -n "$patmp" || { rm -f "$patmp"; die "fetched felhom-pbs-apply failed bash -n — refusing to install"; }
|
||||
install -m 0755 -o root -g root "$patmp" /usr/local/sbin/felhom-pbs-apply
|
||||
rm -f "$patmp"
|
||||
log_success " installed /usr/local/sbin/felhom-pbs-apply (0755, the guarded PBS-DR apply path)"
|
||||
fi
|
||||
|
||||
# Sudoers — fetch the canonical file, validate with visudo -cf BEFORE installing (0440 root:root).
|
||||
if $DRY_RUN; then
|
||||
log_dry "fetch configs/felhom-agent.sudoers ; visudo -cf ; install 0440 -> $AGENT_SUDOERS"
|
||||
@@ -1983,8 +2061,8 @@ step_agent_config() {
|
||||
fi
|
||||
if [[ "$MODE" == "byo" ]]; then
|
||||
log_dry "assert (byo) written config: lan_resolver.enable is false/absent"
|
||||
log_dry "assert (byo) written config: wg_tunnel.enabled is false/absent"
|
||||
log_dry "assert (byo) written config: oob.enabled is false/absent"
|
||||
log_dry "(byo NOTE: wg_tunnel.enabled=true is now the DEFAULT both modes — WG is base infrastructure; consent = the typed disclosure ack)"
|
||||
fi
|
||||
log_dry "systemctl restart felhom-agent && felhom-agent --config $AGENT_CONFIG --selftest"
|
||||
_state_mark agent_config; return 0
|
||||
@@ -2036,6 +2114,13 @@ if os.environ.get('INSTALL_MODE') == 'byo':
|
||||
base.setdefault('lan_resolver', {"enable": False})
|
||||
else:
|
||||
base.setdefault('lan_resolver', {"enable": True})
|
||||
# WG tunnel (drill F-9; operator decision 2026-07-12 #5): WG is BASE infrastructure like
|
||||
# cloudflared — always-on for every install, both modes (it is also the future OOB-management
|
||||
# transport; the PBS-DR tier merely RIDES it). Registration is hands-free (the drill proved it:
|
||||
# keygen -> hub peer 10.77.0.x/32 -> handshake, no vouch). setdefault, not override: a
|
||||
# --preserve-from config that deliberately disabled it is respected (the per-customer opt-out
|
||||
# lives hub-side; byo consent = the disclosure ack + the tester agreement).
|
||||
base.setdefault('wg_tunnel', {"enabled": True})
|
||||
# Day-0 overrides (always authoritative)
|
||||
base['proxmox'] = {
|
||||
"endpoint":"https://127.0.0.1:8006",
|
||||
@@ -2079,20 +2164,22 @@ PY
|
||||
# a customer/BYO box until their production endpoints exist. Parse the JUST-WRITTEN file (same
|
||||
# interpreter pattern as the write above) and refuse to start the daemon on any true value —
|
||||
# a --preserve-from carrying lan_resolver.enable=true is exactly what this catches.
|
||||
# NOTE (v1.15.0 / decision 5): wg_tunnel.enabled was REMOVED from the byo-forbidden set — WG is
|
||||
# base infrastructure (always-on both modes, like cloudflared); byo consent moved to the typed
|
||||
# disclosure ack (the WG tunnel is now on that list) + the tester-agreement disclosure line.
|
||||
if [[ "$MODE" == "byo" ]]; then
|
||||
AGENT_CONFIG="$AGENT_CONFIG" python3 <<'PY' || die "byo config assert FAILED — see the keys above; a byo box must keep lan_resolver/wg_tunnel/oob off and deployment_mode=byo (fix the preserved config and re-run with --resume)"
|
||||
AGENT_CONFIG="$AGENT_CONFIG" python3 <<'PY' || die "byo config assert FAILED — see the keys above; a byo box must keep lan_resolver/oob off and deployment_mode=byo (fix the preserved config and re-run with --resume)"
|
||||
import json, os, sys
|
||||
d = json.load(open(os.environ['AGENT_CONFIG']))
|
||||
bad = []
|
||||
if d.get('lan_resolver', {}).get('enable'): bad.append('lan_resolver.enable (takes over host DNS on :53)')
|
||||
if d.get('wg_tunnel', {}).get('enabled'): bad.append('wg_tunnel.enabled (opens an outbound WG tunnel)')
|
||||
if d.get('oob', {}).get('enabled'): bad.append('oob.enabled (starts a second operator sshd)')
|
||||
if d.get('deployment_mode') == 'appliance': bad.append('deployment_mode=appliance (unlocks host-service self-heal on a host we do not own)')
|
||||
if bad:
|
||||
print('byo-forbidden config keys are TRUE: ' + '; '.join(bad), file=sys.stderr)
|
||||
sys.exit(1)
|
||||
PY
|
||||
log_success " byo config asserts passed (lan_resolver.enable / wg_tunnel.enabled / oob.enabled off; deployment_mode=byo)"
|
||||
log_success " byo config asserts passed (lan_resolver.enable / oob.enabled off; deployment_mode=byo; wg_tunnel intentionally ON — base infrastructure)"
|
||||
fi
|
||||
|
||||
# health: read-only selftest (proxmox) must pass before provisioning
|
||||
@@ -2144,7 +2231,7 @@ step_golden() {
|
||||
|
||||
local url="$GITEA_BASE/api/packages/$GITEA_OWNER/generic/felhom-golden/$ART_GOLDEN_VER/golden.tar.zst"
|
||||
if $DRY_RUN; then
|
||||
log_dry "curl -u <git> -o <dump>/vzdump-lxc-${GOLDEN_VMID}-<ts>.tar.zst $url ; verify sha256=$ART_GOLDEN_SHA ; set GOLDEN_VOLID"
|
||||
log_dry "$(_dry_auth_hint) -o <dump>/vzdump-lxc-${GOLDEN_VMID}-<ts>.tar.zst $url ; verify sha256=$ART_GOLDEN_SHA ; set GOLDEN_VOLID"
|
||||
GOLDEN_VOLID="${ARCHIVE_STORAGE}:backup/vzdump-lxc-${GOLDEN_VMID}-<dry-run>.tar.zst"
|
||||
_state_mark golden; return 0
|
||||
fi
|
||||
@@ -2267,6 +2354,10 @@ step_verify() {
|
||||
else
|
||||
log_warn "Day-0 provision completed WITH WARNINGS — review the checks above"
|
||||
fi
|
||||
# Drill F-8: repeat the break-glass pointer in the FINAL summary (the 4b line scrolls away).
|
||||
if _state_has break_glass; then
|
||||
log_info "root@pam was rotated + vaulted at step 4b — retrieve at hub → host page (the old GUI password no longer works)."
|
||||
fi
|
||||
}
|
||||
|
||||
#-------------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user