Commit Graph
4354 Commits
Author SHA1 Message Date
k.renner 61462d6535 fix: Install libfido2 in bridge to satisfy auto-updated binary
ci/woodpecker/manual/rebuild-k8s Pipeline was successful
2026-04-18 23:57:26 +02:00
k.renner bc794c3b63 fix: bash-interpret bridge entrypoint so +x isn't required 2026-04-18 23:50:43 +02:00
k.renner 6249ed1588 fix: Grant protonmail-bridge needed capabilities to exec entrypoint 2026-04-18 23:48:25 +02:00
k.renner f258e39a6b fix: Pin protonmail-bridge to existing tag 3.19.0-1 2026-04-18 23:36:07 +02:00
k.rennerandClaude Opus 4.7 d71dec863b feat: Add ProtonMail Bridge and FTP scanner for paperless
- protonmail-bridge: StatefulSet with 1Gi ceph-block PVC, internal
  Service on imap/smtp. Initial login still requires `kubectl exec`
  into the pod (one-time, state persists on PVC).
- ftp-scanner: vsftpd Deployment, LoadBalancer on 10.2.40.82.
  Pre-creates Konstantin/Sarah folder layout in paperless-consume
  PVC; paperless-ngx's recursive consumer picks up uploads.
- CiliumNetworkPolicy allows world/host/remote-node → FTP ports.
- Generated scanner password stored SOPS-encrypted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 23:08:57 +02:00
k.rennerandClaude Opus 4.7 8a1268dd5b fix: Allow paperless → ingress-nginx for OIDC hairpin
ci/woodpecker/manual/migrate-authentik-db Pipeline was successful
ci/woodpecker/manual/migrate-paperless Pipeline was successful
Paperless hits auth.k.renner.uno for OIDC discovery, which Cilium
socketLB redirects to the ingress-nginx backend pod. Both sides of
that connection need matching allow rules: paperless egress to
ingress-nginx, and ingress namespace ingress from paperless.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:53:57 +02:00
k.rennerandClaude Opus 4.7 9a40d304bb fix: Enable Cilium socketLB for pod→LB hairpin
Without socketLB, pod connections to LoadBalancer external IPs go
out the wire and hit the L2-announced VIP, which doesn't reply to
cluster-internal pods. socketLB redirects via eBPF at connect()
time to the backend pod.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:48:47 +02:00
k.rennerandClaude Opus 4.7 dae312ba5d fix: Cilium lbExternalClusterIP for pod→LB hairpin
With L2 announcements, the LoadBalancer VIP is advertised via ARP on
the LAN, so traffic from a pod to its own cluster's LB VIP times out.
Enable bpf.lbExternalClusterIP so Cilium redirects the socket straight
to the backend pod, fixing server-to-server OIDC from paperless to
auth.k.renner.uno.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:28:29 +02:00
k.rennerandClaude Opus 4.7 1916ccdacb fix: Use external auth URL for paperless OIDC discovery
Django-allauth uses one server_url for both discovery and browser
redirects. With an internal cluster URL, Authentik returns auth/token
endpoints on the cluster-internal host — browsers can't resolve that.
Point server_url at the ingress-facing auth.k.renner.uno so every
endpoint the discovery doc returns is externally routable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:15:28 +02:00
k.rennerandClaude Opus 4.7 903df35cc9 fix: Allow paperless → authentik for server-side OIDC
Authentik's default-deny-ingress only allowed traffic from authentik,
ingress-nginx, database and monitoring. Paperless resolves OIDC
discovery + token exchange via the internal authentik-server svc,
so it needs ingress from the paperless namespace too.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 22:12:15 +02:00
k.rennerandClaude Opus 4.7 5cb2648fe8 fix: Wait for OBC Bound in infrastructure-ceph-cluster
ci/woodpecker/manual/migrate-authentik-db Pipeline was successful
ci/woodpecker/manual/migrate-paperless Pipeline was successful
kstatus doesn't recognize ObjectBucketClaim phase (OBC has no
status.conditions), so Flux's wait:true returned Ready even while
OBCs were still Pending. Add a CEL healthCheckExpr that blocks
until status.phase == Bound — this gates paperless-db and
authentik-db on the RGW bucket actually being provisioned.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 21:56:58 +02:00
k.rennerandClaude Opus 4.7 1c373d93f2 fix: Move paperless OBC into infrastructure-ceph-cluster
The migration-bucket ObjectBucketClaim lived in paperless-db's
kustomization, so the trigger-backup + restore Jobs started before
the OBC was Bound and their ConfigMap/Secret existed. Mirror the
authentik pattern: put the OBC in infrastructure/rook-ceph-cluster
so infrastructure-ceph-cluster's wait: true blocks dependent
kustomizations until the bucket is Bound.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 21:53:06 +02:00
k.rennerandClaude Opus 4.7 2437b7d733 fix: Auto-fetch latest access files in import script
The rebuild pipeline commits a new talosconfig/kubeconfig each run.
Without a git pull beforehand, import-cluster-access imports stale
configs and kubectl fails with x509 CA mismatch against the newly
rebuilt cluster.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 21:44:50 +02:00
rebuild-k8s pipeline d7bf358283 ci: Export cluster access files from rebuild 2026-04-18 19:40:32 +00:00
k.rennerandClaude Opus 4.7 125135d3fe ci: Split cluster bootstrap into phased pipeline steps
ci/woodpecker/manual/migrate-paperless Pipeline failed
ci/woodpecker/manual/rebuild-k8s Pipeline was successful
Break bootstrap.sh into three phases (talos, flux, wait-apps) that
each CI step can invoke independently. Export cluster access files
immediately after the talos phase so users have kubeconfig /
talosconfig even if later phases fail.

Pipeline steps:
1. recreate-vms       — tofu replace of all 3 Talos VMs
2. talos-bootstrap    — generate + apply Talos, bootstrap K8s, get kubeconfig
3. export-access      — SOPS-encrypt + commit talos/kube config
4. flux-bootstrap     — install flux, wait for infrastructure + Ceph
5. wait-apps          — wait for apps/authentik/paperless to reconcile

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 21:33:02 +02:00
k.rennerandClaude Opus 4.7 42bd935e3a ci: Wait for paperless during cluster bootstrap
ci/woodpecker/manual/rebuild-k8s Pipeline failed
Paperless data migration pulls ~3.5GB from S3 on first boot; give
it a longer window (15 minutes) than authentik before declaring
the bootstrap complete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 20:36:15 +02:00
k.rennerandClaude Opus 4.7 7d41a71fb1 refactor: Stream paperless backups directly to S3 from VM
ci/woodpecker/manual/migrate-paperless Pipeline was successful
Ansible fetch OOM'd on the 3.5GB media tarball. Install mc on the
paperless VM and stream pg_dump + tar outputs straight into S3
with mc pipe. Nothing touches the CI runner disk. Also drops the
per-pipeline S3 credential vars — credentials come from env.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 20:22:24 +02:00
k.rennerandClaude Opus 4.7 7915f31894 refactor: Use ansible fetch instead of ssh for backup transfer
ci/woodpecker/manual/migrate-paperless Pipeline failed
Ansible already has the SSH connection; pulling files back via
fetch module mirrors the authentik migration pattern and avoids
the separate ssh/scp dance.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 20:16:01 +02:00
k.rennerandClaude Opus 4.7 4223367f14 fix: Use scp instead of ssh cat for backup transfer
ci/woodpecker/manual/migrate-paperless Pipeline failed
The ssh cat-with-redirect pattern hit a remote shell quoting issue.
scp is simpler and purpose-built for file transfer.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 20:14:07 +02:00
k.rennerandClaude Opus 4.7 24cc4208c5 fix: Find paperless DB container by compose label
ci/woodpecker/manual/migrate-paperless Pipeline failed
Container name depends on compose project name and version (dashes
vs underscores). Discover dynamically via compose service label
instead of hardcoding paperless-ngx-db-1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 20:08:23 +02:00
k.rennerandClaude Opus 4.7 7c8a5327e8 fix: Set PAPERLESS_ALLOWED_HOSTS=* so probes don't 400
Django raised DisallowedHost for kubelet probe requests (pod IP as
Host header), making the probe fail and the pod crashloop. Paperless
is behind ingress-nginx, which terminates the external Host header
and forwards to the pod by IP — so Django ALLOWED_HOSTS adds no
security. CSRF_TRUSTED_ORIGINS stays scoped to the ingress hostnames.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:52:10 +02:00
k.rennerandClaude Opus 4.7 06a9c28c81 fix: Use dynamic Proxmox inventory in paperless migration
ci/woodpecker/manual/migrate-paperless Pipeline failed
ci/woodpecker/manual/migrate-authentik-db Pipeline was successful
Install community.proxmox collection in CI so dynamic inventory
resolves paperless.srv.renner.uno. SSH as ladmin (VM convention).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:37:21 +02:00
k.rennerandClaude Opus 4.7 11a294d70b feat: Add Paperless data migration pipeline
ci/woodpecker/manual/migrate-paperless Pipeline failed
Mirror the Authentik pattern:
- ansible/plays/backup-paperless.yml: pg_dump + tar data+media on old VM
- .woodpecker/migrate-paperless.yml: run backup, upload to Ceph RGW
- K8s ObjectBucketClaim in paperless namespace
- trigger-backup Job calls Woodpecker API
- restore-db Job pg_restores into pprl-pg
- restore-data Job extracts data+media tarballs onto CephFS PVCs

Trigger from CLI:
  woodpecker pipeline create gitea_admin/homelab --branch master \
    --var "PIPELINE=migrate-paperless"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:30:09 +02:00
k.rennerandClaude Opus 4.7 70f133568f revert: Restore httpGet probes for paperless
The /api/ probes were working — DisallowedHost log entries are
informational noise, not readiness failures.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:05:30 +02:00
k.rennerandClaude Opus 4.7 363b883993 refactor: Use tcpSocket probes for paperless
Switch liveness/readiness from httpGet to tcpSocket — avoids Django
ALLOWED_HOSTS check failing on kubelet's pod-IP probe requests
without needing to hardcode a Host header.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 19:04:54 +02:00
k.rennerandClaude Opus 4.7 9029c540b8 fix: Set Host header on paperless probes
ci/woodpecker/manual/migrate-authentik-db Pipeline was successful
Django rejected probe requests with the pod IP as Host header since
ALLOWED_HOSTS only includes the ingress hostnames. Set Host header
on the probe to paperless.k.renner.uno.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:40:53 +02:00
k.rennerandClaude Opus 4.7 dabcf22b4f fix: Disable service links for paperless pod
K8s auto-injects env vars like PAPERLESS_PORT=tcp://svc-ip:80 from
sibling Services, which paperless-ngx interprets as its listen port
and fails to start. Disabling enableServiceLinks strips these.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:24:27 +02:00
k.rennerandClaude Opus 4.7 cfdccf71b6 fix: Grant paperless CAP_CHOWN for s6-overlay init
The paperless-ngx image runs s6-overlay as root and chowns mounted
volumes to uid 1000. Dropping ALL capabilities stripped CAP_CHOWN,
causing init-folders to fail. Add back CHOWN/DAC_OVERRIDE/FOWNER/
SETGID/SETUID and allow privilege escalation so s6 can switch users.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:14:18 +02:00
k.rennerandClaude Opus 4.7 a6972a0714 feat: Deploy Paperless-ngx to K8s
Deploy paperless-ngx with CNPG PostgreSQL, Redis, Gotenberg, Tika in
dedicated paperless namespace. OIDC via internal Authentik. Exposed
on paperless.k.renner.uno and docs.k.renner.uno for testing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 17:08:54 +02:00
k.rennerandClaude Opus 4.7 a6588f9f2e feat: Add script to import cluster access files
scripts/import-cluster-access.sh decrypts the SOPS-encrypted
talosconfig and kubeconfig from kubernetes/access/ into ~/.talos/
and ~/.kube/. Run after a rebuild-k8s pipeline completes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:44:35 +02:00
k.rennerandClaude Opus 4.7 cdac4439d2 chore: Remove set -x tracing from bootstrap
Pipeline succeeded end-to-end on the next run — tracing no longer
needed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:43:08 +02:00
rebuild-k8s pipeline d481186d5c ci: Export cluster access files from rebuild 2026-04-18 14:41:52 +00:00
k.rennerandClaude Opus 4.7 29e9b41352 debug: Enable set -x tracing in bootstrap
ci/woodpecker/manual/migrate-authentik-db Pipeline was successful
ci/woodpecker/manual/rebuild-k8s Pipeline was successful
Find where the script is dying silently after "Generating Talos configs".

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:11:40 +02:00
k.rennerandClaude Opus 4.7 e94ae192a6 fix: Use awk instead of python for HostnameConfig strip
ci/woodpecker/manual/rebuild-k8s Pipeline failed
The python3 yaml filter was failing silently in the pipeline. Switch
to a pure-awk doc filter that splits on --- and drops any document
matching kind: HostnameConfig. Verified with talosctl validate.

Also drop stderr suppression on talosctl gen config so future failures
surface in the log.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 16:04:17 +02:00
k.rennerandClaude Opus 4.7 590e17b573 fix: Stash access files in workspace between steps
ci/woodpecker/manual/rebuild-k8s Pipeline failed
ci/woodpecker/cron/security-scan Pipeline was successful
Woodpecker steps use separate containers; home dirs don't persist.
Copy talosconfig/kubeconfig from ~ to access-staging/ in the bootstrap
step so the export step can read them.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 10:26:22 +02:00
k.rennerandClaude Opus 4.7 8262d9cba3 feat: Export talosconfig + kubeconfig from rebuild pipeline
New export-access step encrypts both access files with SOPS and
commits them to kubernetes/access/. After a rebuild, a user can
pull and decrypt to get working talosctl/kubectl access.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 10:25:06 +02:00
k.rennerandClaude Opus 4.7 b80743fb02 fix: Disable progress bar when not running in a TTY
ci/woodpecker/manual/migrate-authentik-db Pipeline was successful
In CI (Woodpecker) there's no terminal, so the \r-based progress bar
looks garbled. Fall back to plain per-step status lines when stdout
isn't a TTY.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 10:08:06 +02:00
Flux 3b98f01658 Add Flux v2.8.5 component manifests 2026-04-18 08:01:32 +00:00
k.rennerandClaude Opus 4.7 1b7e2dd478 fix: Strip auto-generated HostnameConfig from Talos 1.12+ config
ci/woodpecker/manual/rebuild-k8s Pipeline was successful
Talos 1.12 gen config emits a HostnameConfig document that conflicts
with machine.network.hostname in per-node patches. Strip the auto-
generated HostnameConfig so existing patches continue to work.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 09:57:01 +02:00
k.rennerandClaude Opus 4.7 c893465216 fix: Pin talosctl version and apply patches at apply-time
ci/woodpecker/manual/rebuild-k8s Pipeline failed
Apply all patches (cluster, cilium-cni, per-node) during apply-config
rather than during gen-config. Matches the previous working flow and
avoids merge issues with the generated base config.

Pin talosctl to v1.12.6 to match the Talos image used on VMs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 09:49:25 +02:00
k.rennerandClaude Opus 4.7 4153224987 fix: Don't exit bootstrap when talosctl apply-config fails
ci/woodpecker/manual/rebuild-k8s Pipeline failed
With set -e, err=\$(failing-cmd) exits the script before the exit
code can be checked. Use if cmd; then ... form so the failure is
inside a conditional and set -e permits it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 08:43:32 +02:00
k.rennerandClaude Opus 4.7 dabe590778 debug: Show per-attempt errors during Talos maintenance wait
ci/woodpecker/manual/rebuild-k8s Pipeline failed
Surface the actual talosctl apply-config failure on each retry so we
can diagnose why the loop is silently exhausting 60 attempts.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 08:32:00 +02:00
k.rennerandClaude Opus 4.7 6de44e2913 refactor: Use DNS server exclusively for node resolution
ci/woodpecker/manual/rebuild-k8s Pipeline failed
The Proxmox DNS server is the canonical source of truth for node IPs.
Remove the Proxmox API fallback — trust the DNS and let its retry loop
handle transient boot delays.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-18 08:14:50 +02:00
k.rennerandClaude Opus 4.6 b1c1b70971 fix: Replace grep -oP with sed for BusyBox compatibility
ci/woodpecker/manual/rebuild-k8s Pipeline failed
Alpine's BusyBox grep doesn't support Perl regex (-P).
Use sed for Proxmox API IP extraction instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 07:55:29 +02:00
k.rennerandClaude Opus 4.6 8c467e4d41 fix: Resolve node IPs via Proxmox API instead of stale DNS
ci/woodpecker/manual/rebuild-k8s Pipeline failed
After VM recreation, DNS is stale because the Proxmox DNS server
cache hasn't refreshed. Query the QEMU guest agent via Proxmox API
to get the correct IPv4, falling back to DNS.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 07:42:03 +02:00
k.rennerandClaude Opus 4.6 59b3770320 feat: Generate Talos configs dynamically in bootstrap
ci/woodpecker/manual/rebuild-k8s Pipeline failed
Replace static controlplane.yaml with talosctl gen config at bootstrap
time. Cluster settings (CIDRs, SANs, features) are in patches/cluster.yaml.
Certs and tokens are generated fresh each rebuild.

No more talosconfig_b64 CI secret needed — talosconfig is generated
alongside the node configs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 02:00:39 +02:00
k.rennerandClaude Opus 4.6 ca037772ef fix: Add talosconfig secret to rebuild pipeline
CI runner needs talosconfig for talosctl commands after apply-config.
Base64-encoded in CI secret talosconfig_b64.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 01:56:38 +02:00
k.rennerandClaude Opus 4.6 7af1513835 fix: Re-resolve DNS each retry in bootstrap, increase wait
ci/woodpecker/manual/rebuild-k8s Pipeline failed
After VM recreation, DHCP assigns new IPs. The Proxmox DNS cache
refreshes every 60s but the old A records have 300s TTL. Re-resolve
on each attempt and increase retries to 60 (10 min).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 19:57:15 +02:00
k.rennerandClaude Opus 4.6 db4face276 fix: Force IPv4 for Talos API in bootstrap script
ci/woodpecker/manual/rebuild-k8s Pipeline failed
Cilium dual-stack adds ULA IPv6 to nodes, which DNS returns.
CI runner prefers IPv6 but Talos API doesn't listen on ULA.
Resolve to IPv4 explicitly with dig for apply-config --insecure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 18:57:24 +02:00
k.rennerandClaude Opus 4.6 c8bc56f636 fix: Add boot wait and DNS check before bootstrap
ci/woodpecker/manual/rebuild-k8s Pipeline failed
VMs need time to boot into Talos maintenance mode after creation.
Add 60s wait and DNS resolution check for debugging.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 18:34:38 +02:00