Commit Graph
8 Commits
Author SHA1 Message Date
Scooby HuskyandClaude Sonnet 5 5670a941d4 Deploy real GitLab on the VPS (not just Gitea)
User wants actual GitLab on the VPS, not just Gitea's pull-mirror -
deliberate scope increase from the original plan's 'keep the VPS
lighter' design for git hosting specifically.

Simplified single-node equivalent of apps/gitlab (home) - same chart
version (7.7.0), same Authentik OIDC provider/client_id (added a second
redirect_uri to the shared GitLab provider in infrastructure/authentik/
gitlab-blueprint.yaml), same known chart bugs already diagnosed at home
(redis-init-fix.yaml for the initializer-ordering bug) - but:
  - 1 CNPG instance (local-path) instead of home's 3-instance HA
  - No Praefect - pointless HA routing with a single Gitaly node
  - No PgBouncer - direct CNPG connection, traffic is low enough
  - No SAML, no Container Registry, no KAS - addable later if needed
  - Own Traefik Ingress + https-redirect Middleware from the start
    (found needed the hard way on Vault/Gitea/n8n/Nextcloud/Authentik
    earlier this session - applying that lesson here immediately)

New Vault paths this needed: secret/vps/gitlab-redis (freshly generated,
this Redis instance is new, nothing to reuse) and widened the
vps-eso-reader policy to also allow secret/gitlab-oauth (the shared
Authentik client, already used by home's GitLab).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 21:04:48 -05:00
Scooby HuskyandClaude Sonnet 5 cbf7c07711 Vault -> VPS secret pipeline: replaces manual kubectl/ssh secret copies
User correctly flagged this: manually copying secrets between home and
the VPS defeats the entire point of having Vault. Every VPS secret this
session (MinIO creds, Cloudflare token, CNPG certs, the pg-authentik-app
password) was a one-off kubectl create secret copy-paste, because the
VPS's k3s cluster had no Vault/ESO pipeline at all - this builds one.

- infrastructure/authentik/manifests/ha-postgres-app-pushsecret.yaml:
  home pushes pg-authentik-app's password into Vault at
  secret/vps/pg-authentik-app (PushSecret, not just Get - this password is
  CNPG-generated, not Vault-native, so it has to originate from a push).

- infrastructure/vps-eso/manifests/clustersecretstore.yaml: ESO on the
  VPS (installed via helm, out-of-band like k3s/cert-manager - see the
  file's own header) authenticates to home's Vault via AppRole (not
  Kubernetes auth - the VPS is a separate cluster with no federation to
  home's API server). Reachable via a new public
  https://vault.kube.huskypup.net record - Vault was deliberately kept
  off the public internet before this, explicitly confirmed with the user
  before opening it. Traffic goes through the same Istio ingress gateway
  already serving other public hosts, so unlike the ha-authentik-postgres
  NodePort case, no PeerAuthentication/AuthorizationPolicy change was
  needed - it arrives as a normal in-mesh call from the gateway's own
  identity, not raw external TCP to a pod.

  Found and fixed a real Vault gotcha while wiring this up: tried to
  remove the AppRole's token_bound_cidrs restriction (added first, before
  discovering Vault can't see the VPS's real source IP through the
  gateway - it only ever sees the gateway's own pod IP) by omitting the
  field from a follow-up  - that does NOT clear it, the
  AppRole role endpoint preserves omitted fields rather than resetting
  them to default. Had to explicitly write token_bound_cidrs=. Spent a
  while chasing a misleading 403 permission denied on auth/token/
  lookup-self before finding this - vault token capabilities said read
  was allowed (policy was fine), the actual rejection was IP-bound token
  use from an unbound context.

- infrastructure/vps-standby/authentik/manifests/
  pg-authentik-app-externalsecret.yaml: pulls it back down, Merge policy
  (only overwrites the password key - host/dbname/username stay local,
  CNPG still needs its own local -rw hostname for internal use).

- argocd-apps/vps-standby/vps-eso.yaml: new Application for the
  ClusterSecretStore.

Verified end-to-end: password now matches between home and VPS's
pg-authentik-app secrets via this pipeline (not the earlier manual patch),
confirmed by comparing both live secret values after ESO's sync.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-20 18:26:26 -05:00
Scooby Husky 5bc1be2f00 Replace DNS-flip failover watcher with static vps.huskypup.net subdomains
The DNS-flip watcher (scripts/vps-dns-failover/) was designed but never
actually installed on the VPS despite being tracked as done - real gap,
found when asked whether the standby services are actually reachable.

New design: instead of dynamically flipping *.kube.huskypup.net between
home and VPS IPs, give the VPS site its own permanent, always-resolving
subdomain - vault/gitea/auth/n8n/nextcloud.vps.huskypup.net, each with
real Ingress+TLS on the VPS's own Traefik+cert-manager (both already
installed by Phase 0 bootstrap, just never wired up). No token-scoping
decision needed since there's no dynamic flipping - reuses the same
cert-manager token pattern as home.

Also scales Authentik/n8n/Nextcloud from 0 to 1 replica on the VPS so
the replicated data is actually browsable at all times, not just
present-but-unreachable. Their CNPG clusters are still read-only
replicas (spec.replica.enabled: true) - writes will error until a
deliberate manual promotion, but reads/browsing work now. Vault and
Gitea were already running continuously.
2026-08-18 18:23:08 -05:00
Scooby Husky 85c916285e Phase 2: deploy n8n and Nextcloud warm standbys on the VPS
Same CNPG replica-cluster pattern as Authentik (continuous WAL replay
from home via VPS MinIO, app at 0 replicas until manual promotion - see
infrastructure/vps-standby/authentik/manifests/cnpg-cluster.yaml for the
full rationale).

n8n: N8N_ENCRYPTION_KEY copied byte-identical from home (kubectl, not
git) - decrypts stored credentials in the replicated DB, same reasoning
as Vault's unseal key / root token copies.

Nextcloud: adds infrastructure/vps-standby/nextcloud/manifests/
pvc-restore-cronjob.yaml, the read side of home's existing
nextcloud-pvc-sync restic backup - restores the latest snapshot from VPS
MinIO into this standby's PVC daily at 04:00 (2h after home's 02:00
backup). No Redis on the VPS side (no redis-operator deployed there,
out of scope for a standby that isn't serving traffic - Nextcloud
degrades gracefully to DB-based locking without it).
2026-08-17 22:31:41 -05:00
Scooby Husky 1a657ad0a4 Phase 2: deploy Authentik warm standby on the VPS
CNPG operator on the VPS (argocd-apps/vps-standby/cnpg-operator.yaml),
plus a CNPG 'replica cluster' for pg-authentik (infrastructure/
vps-standby/authentik/manifests/cnpg-cluster.yaml) that continuously
replays WAL shipped from home's pg-authentik via VPS MinIO's
cnpg-backups/pg-authentik bucket path - CNPG's native replica-cluster
mode, not a custom restore script like Vault needed (Vault has no
equivalent built-in continuous-replication-into-object-store feature).

The Authentik app itself runs at replicas: 0 in normal operation - the
replica DB is read-only until a deliberate manual promotion
(spec.replica.enabled: false), and a pod trying to write against a
read-only DB would just crashloop uselessly. values.yaml's authentik:
block is copied byte-identical from home's on purpose: the chart
deterministically derives its generated Secret's AUTHENTIK_SECRET_KEY
from these literal values.yaml strings, so both clusters land on the
same key without manually copying it - required since that key decrypts
things stored encrypted in the replicated DB.
2026-08-17 22:28:10 -05:00
Scooby HuskyandClaude Sonnet 5 777edf3895 Phase 2: deploy Gitea warm standby on the VPS
Pull-mirrors home GitLab's Homelabv4 repo on Gitea's own built-in mirror
scheduler (6h interval) - no custom sync job needed, per the original
plan. SQLite instead of the chart's default HA Postgres + Valkey cluster
(single-instance standby holding one small repo, not worth the extra
moving parts). A PostSync Job creates the mirror once, idempotently;
Gitea's scheduler handles all ongoing pulls after that.

Also added a public Cloudflare CNAME for gitlab.kube.huskypup.net ->
home.kube.huskypup.net: the VPS has no route to home's LAN via Netbird
(none of the mesh peers advertise that subnet, confirmed live), so
GitLab needs to be reachable the same way any other internet client
reaches it - home's public IP already has port 443 forwarded to
istio-ingressgateway from earlier this session.

Admin and GitLab-mirror credentials are plain Secrets created directly
on the VPS cluster (kubectl, not git) - same pattern as vault-unseal-key.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 22:05:24 -05:00
Scooby HuskyandClaude Sonnet 5 d0c5563671 Phase 2: deploy Vault warm standby on the VPS
Single-node Vault (raft storage, standalone mode) plus a restore CronJob
that pulls the latest raft snapshot from the VPS's own local MinIO
(already receiving snapshots every 6h from home) and restores it every
6h, offset 30 minutes after the home-side snapshot job.

Not an independent root of trust: raft snapshot restore replaces the
entire storage backend including the keyring, so after every restore
this Vault is sealed with HOME's actual keyring - unsealing it needs a
copy of home's real unseal key, stored directly on the VPS cluster
(kubectl, not git, same pattern as vps-minio-root-secret). First-run
bootstrap uses a throwaway single-Shamir-key init just to get through
the very first restore, then is irrelevant from then on.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-17 21:42:09 -05:00
Scooby Husky 7990f1fa47 Add VPS warm-standby/backup site (Phase 0-1b)
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.
2026-08-17 14:59:26 -05:00