feat(infra): production host bootstrap and bare-metal Stalwart scaffolding

Host provisioning for the single-server production target: SSH + firewall
hardening (nftables allowlist), k3s node registration, bare-metal Stalwart
install with systemd units and TLS cert-sync from the cluster secret, and
Restic encrypted backup/restore (primary + DR) with timer units. Host-specific
secrets live in config.env (gitignored); config.env.example is the template.
Also gitignores MemPalace per-project files.
This commit is contained in:
Ronni Baslund
2026-06-07 00:19:48 +02:00
parent 5ed3d2bc5f
commit 3831c85285
18 changed files with 1432 additions and 0 deletions
+93
View File
@@ -0,0 +1,93 @@
#!/usr/bin/env bash
#
# Register the AX41 as a single-node k3s cluster in Rancher (Custom cluster,
# provisioning v2). Run AFTER bootstrap.sh — the firewall already allows the
# outbound 443 the cluster-agent needs (no inbound rule required).
#
# This downloads Rancher's system-agent installer and runs it. The agent then
# pulls the cluster spec from Rancher and stands up k3s with the configured
# roles. The Rancher Custom cluster MUST be created with the K3s distribution.
#
# Security note: Rancher here is addressed by IP, whose TLS cert won't match,
# so we fetch the installer with --insecure. That's acceptable because the
# agent verifies Rancher's CA via --ca-checksum for its ongoing connection.
# Move Rancher behind rancher.dezky.eu + a valid cert to drop the insecure fetch.
#
# Usage (on the server):
# sudo ./register.sh # register this node
# sudo ./register.sh --force # re-run even if an agent is already present
set -euo pipefail
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
info() { echo -e "${BLUE}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
error() { echo -e "${RED}[ERROR]${NC} $*" >&2; }
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
HOST_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
CONFIG_FILE="${CONFIG_FILE:-$HOST_DIR/config.env}"
FORCE=0
[[ "${1:-}" == "--force" ]] && FORCE=1
if [[ $EUID -ne 0 ]]; then
error "Run with sudo/root (the agent installer needs root)."
exit 1
fi
if [[ ! -f "$CONFIG_FILE" ]]; then
error "Missing $CONFIG_FILE — fill in the RANCHER_* values first."
exit 1
fi
# shellcheck disable=SC1090
source "$CONFIG_FILE"
: "${RANCHER_SERVER_URL:?RANCHER_SERVER_URL required}"
: "${RANCHER_NODE_TOKEN:?RANCHER_NODE_TOKEN required}"
: "${RANCHER_CA_CHECKSUM:?RANCHER_CA_CHECKSUM required}"
: "${RANCHER_NODE_ROLES:=--etcd --controlplane --worker}"
: "${RANCHER_INSECURE_FETCH:=true}"
# ── Idempotency guard ──────────────────────────────────────────────────────
if systemctl list-unit-files 2>/dev/null | grep -q '^rancher-system-agent'; then
if [[ $FORCE -eq 0 ]]; then
warn "rancher-system-agent already installed — node looks registered."
warn "Re-run with --force to register again. Skipping."
exit 0
fi
warn "rancher-system-agent present, but --force given — proceeding."
fi
# ── Fetch installer ────────────────────────────────────────────────────────
INSECURE_FLAG=""
if [[ "$RANCHER_INSECURE_FETCH" == "true" ]]; then
INSECURE_FLAG="--insecure"
warn "Fetching installer insecurely (Rancher reached by IP). CA checksum still pins the agent connection."
fi
TMP_INSTALLER="$(mktemp /tmp/rancher-system-agent-install.XXXXXX.sh)"
trap 'rm -f "$TMP_INSTALLER"' EXIT
info "Downloading system-agent installer from ${RANCHER_SERVER_URL} ..."
# shellcheck disable=SC2086
curl -fsSL $INSECURE_FLAG "${RANCHER_SERVER_URL}/system-agent-install.sh" -o "$TMP_INSTALLER"
ok "Installer downloaded ($(wc -c < "$TMP_INSTALLER") bytes)."
# ── Register ───────────────────────────────────────────────────────────────
info "Registering node with roles: ${RANCHER_NODE_ROLES}"
info "(token masked: ${RANCHER_NODE_TOKEN:0:6}…)"
# shellcheck disable=SC2086
sh "$TMP_INSTALLER" \
--server "${RANCHER_SERVER_URL}" \
--label 'cattle.io/os=linux' \
--token "${RANCHER_NODE_TOKEN}" \
--ca-checksum "${RANCHER_CA_CHECKSUM}" \
${RANCHER_NODE_ROLES}
echo ""
ok "Registration submitted. Watch progress in Rancher (cluster goes Active in a few minutes)."
info "On the node you can follow along with:"
info " journalctl -u rancher-system-agent -f"
info " k3s kubectl get nodes # once k3s is up"