feat(cloudflared): wire tunnel token secret and document bootstrap

- Create SOPS-encrypted cloudflared-secrets.enc.yaml with tunnel token
- Add Cloudflare vars to .env.example (CLOUDFLARE_CONNECTOR_TOKEN, ACCOUNT_ID, TUNNEL_ID, API_TOKEN)
- Document Phase 0 cloudflared-token Secret creation in BOOTSTRAP.md (manual step until CMP plugin wires it)
- Note: Cloudflare-side TCP routing (cp1.homelab -> 192.168.1.213:6443, etc.) must be configured manually in Zero Trust dashboard

Tunnel already deployed as ArgoCD Application in k8s/argocd/apps/60-applications.yaml (wave 8); this closes the missing Secret gap and documents the bootstrap path.
This commit is contained in:
Story Crater Bot
2026-07-20 10:50:20 -07:00
parent a207c56637
commit 063308308f
4 changed files with 57 additions and 4 deletions
+11
View File
@@ -68,3 +68,14 @@ AUTHENTIK_TEMPORAL_CLIENT_ID=
# ── CI/CD ──────────────────────────────────────────────────────────────────────
# Forgejo Personal Access Token (from rock user) for pushing images to registry
FORGEJO_RIOTPIAO_PAT=
# ── Cloudflare Tunnel (remote off-LAN access to kubectl/talosctl) ──────────────
# From Cloudflare Zero Trust dashboard → Networks → Tunnels
# CLOUDFLARE_CONNECTOR_TOKEN: full tunnel token (JWT-like base64 string)
# CLOUDFLARE_ACCOUNT_ID: your account ID (hex string)
# CLOUDFLARE_TUNNEL_ID: tunnel UUID
# CLOUDFLARE_API_TOKEN: API token for programmatic tunnel config (optional)
CLOUDFLARE_CONNECTOR_TOKEN=
CLOUDFLARE_ACCOUNT_ID=
CLOUDFLARE_TUNNEL_ID=
CLOUDFLARE_API_TOKEN=
+17 -3
View File
@@ -2,6 +2,18 @@
**Homelab:** A bare-metal three-node Kubernetes cluster running Talos Linux with a full observability stack, SSO via Authentik, secret management via Vault, and CI/CD infrastructure (Forgejo + Argo CD, deployed).
## Cluster Topology (3 control-plane HA, since 2026-07-20)
| Node | IP | Zone | Scheduling | Storage |
|------|----|----|-----------|---------|
| `talos-cp-1` | .213 | az-a | schedulable (all workloads) | sole Longhorn node |
| `talos-cp-2` | .163 | az-b | dedicated (`NoSchedule`) | none |
| `talos-cp-3` | .166 | az-c | dedicated (`NoSchedule`) | none |
3 voting etcd members peering on the LAN. Only `talos-cp-1` runs workloads and
holds storage → stateful services are single-instance (e.g. CNPG `ddb-cluster`
= 1 instance). API endpoint is single-homed to `.213` (no VIP yet).
## Service Integration Routes
**New service? Pick your stack below:**
@@ -31,9 +43,9 @@ Apps: cicd (Forgejo + Argo CD), sqs (Kafka + kmsvc), temporal, story-crater-back
**Architecture principles:**
- Immutable OS (Talos — no SSH, declarative config)
- Secrets in Vault (never commit `.env`, credentials in Secret volumes)
- Helmfile = single source of truth (`helmfile.yaml.gotmpl`)
- Pull-based GitOps (Argo CD, no kubeconfig in CI)
- Secrets in Vault + SOPS-encrypted (`*.enc.yaml`, age); never commit `.env`
- ArgoCD app-of-apps = CD source of truth (`k8s/argocd/root``k8s/argocd/apps/*`); helmfile is deprecated. Two-phase bootstrap in `k8s/argocd/bootstrap/BOOTSTRAP.md`
- Pull-based GitOps (Argo CD, no kubeconfig in CI); iterate = `git push` to Forgejo → auto-sync
- Federated OIDC (Authentik provider for all services)
@@ -53,6 +65,8 @@ Apps: cicd (Forgejo + Argo CD), sqs (Kafka + kmsvc), temporal, story-crater-back
3. **Secrets via volumes** — Never `--env` flag in pod specs (exposes in `kubectl describe`)
4. **External services via Ingress** — All public endpoints via TLS (homelab-ca)
5. **Never commit `.env`** — Only `.env.example` in git; real secrets in Vault
6. **Never rename or wipe `talos-cp-1` (.213)** — sole Longhorn storage node; renaming orphans its node CR and faults every volume (permanent data loss). Rename/reprovision only the dedicated CPs.
7. **Control-plane etcd advertises on the LAN** — keep `cluster.etcd.advertisedSubnets: ["192.168.1.0/24"]`, else Talos advertises on WireGuard and new members hang as etcd learners.
## Git & Release
@@ -0,0 +1,17 @@
cloudflared:
tunnelToken: ENC[AES256_GCM,data:GrZNC75M1T304m+MBbgcL9Wa6VD3Cc+joERjUWD7PsI5NlqFpVuorzub4OTaiE/F0sqDXtXdSwQP9jGTkgUnNbviUVEIBDb+zuiyzCfzuhc53oMvoUYRkRNeH5DR9TBEszspj8+mjQMDAKQFKtTmyDodgh8DdPg8LE8YTCqyx6CcpkGZ8yWY06VoKpZfFSOan/gHwRwG2500P8U+rzI676EKVUhWjBFP0iTbwDQR7spI7oz8Gon/0Q==,iv:O3i0v+M5L3i9O7SbBtDAJe5IsQDgw+alI0Y9arZNojs=,tag:/T63FivzvbLFaXEaBsn5FA==,type:str]
sops:
age:
- enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBpU2tjOGpJNk9leE9LSnlx
b2tVbFFMMkZ3OHZuU0VhcUp5Tjl4ZDh2R1U4CjFFMURDWFJtN1lETUNpSmEwbU9u
aEIxUW5qWTJGTnlobjV0emlpY00rM28KLS0tIFd0cllZeXFodFhJNTJMNkRLNyt1
cXVyNnM4Y2pXeUFTYzU1OXlaOXR2RWcKp7/M/YFfpJg4L6a0AcYTV3Rza+bzaOeD
OUIwyns8ZsPtU8ILbRYUUdD2EJFiOPnWP4yX70Ak10v12gfB7vRJ6A==
-----END AGE ENCRYPTED FILE-----
recipient: age1smu533f803gmd0jq60s2zaj9zlznajy0ca6rtewd4r37mr2hs3uqsrldfh
lastmodified: "2026-07-20T17:49:35Z"
mac: ENC[AES256_GCM,data:MhuuuInJjGbzoftxVqMZUov1tJpOt5Vb8GwTr0R12hRCVHwdV+cuC1YeXTQidYaduO7neGnYe0p8ESLEpyY06j4nSpCvGRPPaxTeSeA8IcIk71xNtA1X0FVPv51s59rpvVSjiMDCqrcOv1aJybfQnBZoc/9bfmCYGqhDkD6sg1U=,iv:ThTjUGYE9GiyIgXS+0KDLYS20RPJjMG9DKyMSoKGk/4=,tag:KqZYps5qSuKPUI4U3Iabmw==,type:str]
unencrypted_suffix: _unencrypted
version: 3.13.2
+12 -1
View File
@@ -66,7 +66,18 @@ argocd app sync forgejo
git remote add forgejo http://forgejo.riotpiao.com:3000/riotpiao.com/homelab.git
git push forgejo main
# 7. Deploy everything else from git — one sync brings up the whole cluster.
# 7. (Optional) Cloudflared tunnel secret — if the SOPS CMP plugin is NOT yet
# wired up: create the cloudflared-token Secret manually before wave 8 syncs.
# Skip this step if the CMP plugin is already live and handling SOPS decryption.
kubectl create namespace cloudflared --dry-run=client -o yaml | kubectl apply -f -
sops -d k8s/applications/cloudflared/cloudflared-secrets.enc.yaml \
| yq '.cloudflared.tunnelToken' \
| kubectl create secret generic cloudflared-token -n cloudflared \
--from-literal=token=- --dry-run=client -o yaml | kubectl apply -f -
# Once the SOPS CMP plugin is deployed (Phase 1 wave 0), migrate this Secret
# into the plugin's script output and remove this manual step.
# 8. Deploy everything else from git — one sync brings up the whole cluster.
kubectl apply -f k8s/argocd/projects/homelab-project.yaml # AppProject
kubectl apply -k k8s/argocd/root # app-of-apps root
argocd app sync homelab-root