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:
+93
@@ -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"
|
||||
Reference in New Issue
Block a user