mirror of
https://gitlab.kube.huskypup.net/Scooby/Homelabv4.git
synced 2026-08-20 23:16:49 +00:00
Foundation for a DR/backup path using an always-on VPS as a second ArgoCD-managed cluster, plus DB/backup standardization work that fell out of it: - vps-standby ArgoCD cluster destination + AppProject, MinIO backup receiver, VPS bootstrap script (k3s, Netbird, cert-manager) - Dual-site DNS failover watcher + home-IP DDNS CronJob, Cloudflare token moved out of git into Vault+ExternalSecret - Nextcloud migrated from ad-hoc MariaDB to CNPG + redis-operator (matches n8n/Authentik/GitLab's backup-native pattern) - Authentik's CNPG manifests moved into the actual ArgoCD-synced manifests/ path (were present but never wired into the sync path) - Vault raft-snapshot CronJob, CNPG barmanObjectStore backups (Authentik/n8n/Nextcloud), Nextcloud file-PVC restic sync - all targeting the new VPS MinIO receiver See VPS Warm-Standby plan doc for full design rationale.
170 lines
6.2 KiB
Bash
Executable File
170 lines
6.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# vps-bootstrap.sh - Phase 0: turn a bare VPS into the vps-standby ArgoCD destination
|
|
#
|
|
# Run this ON THE VPS itself (as root, or via sudo), not against the home cluster.
|
|
# Installs k3s (single node), joins the existing self-hosted Netbird mesh, and
|
|
# installs cert-manager with the same Cloudflare DNS-01 ClusterIssuer pattern used
|
|
# at home — so TLS issuance works identically regardless of which site is "live"
|
|
# (DNS-01 only needs DNS control, not public HTTP reachability).
|
|
#
|
|
# This script does NOT register the cluster with ArgoCD — that's a one-time manual
|
|
# step run from your workstation/home cluster once this script prints the kubeconfig
|
|
# (ArgoCD can't reach the VPS until it exists, and shouldn't hold cluster-admin creds
|
|
# for a box it doesn't manage yet).
|
|
#
|
|
# Usage:
|
|
# sudo ./scripts/vps-bootstrap.sh <netbird-setup-key> <cloudflare-dns-edit-token>
|
|
#
|
|
# Prerequisites:
|
|
# - A Netbird setup key (Netbird dashboard → Settings → Setup Keys → create
|
|
# a reusable, non-ephemeral key)
|
|
# - A Cloudflare API token scoped to Zone:DNS:Edit for kube.huskypup.net only
|
|
# (create a NEW token for this — do not reuse the one from
|
|
# infrastructure/cert-manager/manifests/secret-cf-token.yaml, that one is
|
|
# being rotated/retired; see Phase 0.5)
|
|
# - Ubuntu/Debian VPS with a public IP, run as root
|
|
|
|
set -euo pipefail
|
|
|
|
NETBIRD_SETUP_KEY="${1:?Usage: $0 <netbird-setup-key> <cloudflare-dns-edit-token>}"
|
|
CLOUDFLARE_TOKEN="${2:?Usage: $0 <netbird-setup-key> <cloudflare-dns-edit-token>}"
|
|
|
|
NETBIRD_MGMT_URL="https://netbird.kube.huskypup.net"
|
|
LETSENCRYPT_EMAIL="garrettstone499@gmail.com"
|
|
DNS_ZONE="kube.huskypup.net"
|
|
CERT_MANAGER_VERSION="v1.13.2" # matches infrastructure/cert-manager chart version at home
|
|
|
|
if [ "$(id -u)" -ne 0 ]; then
|
|
echo "ERROR: run as root (sudo $0 ...)" >&2
|
|
exit 1
|
|
fi
|
|
|
|
echo "================================================="
|
|
echo "VPS Standby Bootstrap - Phase 0"
|
|
echo "================================================="
|
|
echo ""
|
|
|
|
# --- k3s -----------------------------------------------------------------
|
|
if command -v k3s >/dev/null 2>&1; then
|
|
echo "✅ k3s already installed, skipping install"
|
|
else
|
|
echo "Installing k3s (single node)..."
|
|
# Keep the built-in Traefik ingress controller — this is a lean standby box,
|
|
# not a mirror of home's Istio/Envoy-Gateway mesh. servicelb is fine too
|
|
# since this is a single node with a real public IP.
|
|
curl -sfL https://get.k3s.io | sh -s - \
|
|
--write-kubeconfig-mode 644 \
|
|
--disable metrics-server
|
|
echo "✅ k3s installed"
|
|
fi
|
|
|
|
echo "Waiting for k3s node to be Ready..."
|
|
for i in $(seq 1 30); do
|
|
if k3s kubectl get nodes 2>/dev/null | grep -q " Ready"; then
|
|
echo "✅ node is Ready"
|
|
break
|
|
fi
|
|
sleep 5
|
|
done
|
|
k3s kubectl get nodes
|
|
|
|
export KUBECONFIG=/etc/rancher/k3s/k3s.yaml
|
|
|
|
# --- Netbird ---------------------------------------------------------------
|
|
if command -v netbird >/dev/null 2>&1 && netbird status 2>/dev/null | grep -q "Management: Connected"; then
|
|
echo "✅ Netbird already connected, skipping"
|
|
else
|
|
echo "Installing Netbird client..."
|
|
curl -fsSL https://pkgs.netbird.io/install.sh | sh
|
|
echo "Joining Netbird mesh (${NETBIRD_MGMT_URL})..."
|
|
netbird up --management-url "${NETBIRD_MGMT_URL}" --setup-key "${NETBIRD_SETUP_KEY}"
|
|
echo "✅ Netbird joined"
|
|
fi
|
|
echo "Note: this Netbird session is used opportunistically for backup/sync traffic"
|
|
echo "while home is up. It is NOT the path used to reach this VPS when home is down —"
|
|
echo "that's direct SSH on this box's public IP. See plan doc, decision #2."
|
|
|
|
# --- cert-manager ------------------------------------------------------------
|
|
echo ""
|
|
echo "Installing cert-manager ${CERT_MANAGER_VERSION}..."
|
|
if ! command -v helm >/dev/null 2>&1; then
|
|
curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
|
|
fi
|
|
|
|
helm repo add jetstack https://charts.jetstack.io 2>/dev/null || true
|
|
helm repo update jetstack
|
|
|
|
if helm -n cert-manager status cert-manager >/dev/null 2>&1; then
|
|
echo "✅ cert-manager already installed"
|
|
else
|
|
helm install cert-manager jetstack/cert-manager \
|
|
--namespace cert-manager \
|
|
--create-namespace \
|
|
--version "${CERT_MANAGER_VERSION}" \
|
|
--set installCRDs=true \
|
|
--wait --timeout 300s
|
|
echo "✅ cert-manager installed"
|
|
fi
|
|
|
|
echo "Waiting for cert-manager webhook to be ready..."
|
|
k3s kubectl -n cert-manager rollout status deployment/cert-manager-webhook --timeout=120s
|
|
|
|
# --- Cloudflare DNS-01 ClusterIssuer (same pattern as home) -----------------
|
|
echo ""
|
|
echo "Applying Cloudflare token Secret + ClusterIssuer..."
|
|
cat <<EOF | k3s kubectl apply -f -
|
|
apiVersion: v1
|
|
kind: Secret
|
|
metadata:
|
|
name: cloudflare-token-secret
|
|
namespace: cert-manager
|
|
type: Opaque
|
|
stringData:
|
|
cloudflare-token: "${CLOUDFLARE_TOKEN}"
|
|
---
|
|
apiVersion: cert-manager.io/v1
|
|
kind: ClusterIssuer
|
|
metadata:
|
|
name: letsencrypt-production
|
|
spec:
|
|
acme:
|
|
server: https://acme-v02.api.letsencrypt.org/directory
|
|
email: ${LETSENCRYPT_EMAIL}
|
|
privateKeySecretRef:
|
|
name: letsencrypt-production
|
|
solvers:
|
|
- dns01:
|
|
cloudflare:
|
|
email: ${LETSENCRYPT_EMAIL}
|
|
apiTokenSecretRef:
|
|
name: cloudflare-token-secret
|
|
key: cloudflare-token
|
|
selector:
|
|
dnsZones:
|
|
- "${DNS_ZONE}"
|
|
EOF
|
|
echo "✅ ClusterIssuer letsencrypt-production ready"
|
|
|
|
# --- Output for ArgoCD registration -----------------------------------------
|
|
echo ""
|
|
echo "================================================="
|
|
echo "✅ VPS foundation bootstrap complete"
|
|
echo "================================================="
|
|
echo ""
|
|
echo "Next step (run from your workstation, NOT this VPS):"
|
|
echo ""
|
|
echo " 1. Copy this VPS's kubeconfig to your workstation, e.g.:"
|
|
echo " scp root@<vps-ip>:/etc/rancher/k3s/k3s.yaml ~/vps-standby-kubeconfig.yaml"
|
|
echo " Then edit the 'server:' line inside it to use this VPS's public IP"
|
|
echo " instead of 127.0.0.1."
|
|
echo ""
|
|
echo " 2. Register it with ArgoCD:"
|
|
echo " KUBECONFIG=~/vps-standby-kubeconfig.yaml argocd cluster add default --name vps-standby"
|
|
echo ""
|
|
echo " 3. Confirm registration:"
|
|
echo " argocd cluster list"
|
|
echo ""
|
|
echo "This box is otherwise reachable via:"
|
|
echo " - Netbird (while home's self-hosted mesh is up)"
|
|
echo " - Direct SSH on its public IP (always, break-glass path)"
|