feat(cloudflared): manage tunnel ingress locally via ConfigMap

The tunnel was remotely managed from the Zero Trust dashboard: the
Deployment ran 'run --token' and ignored the ConfigMap that already
existed in the tree. Switch to a locally-managed tunnel so the ingress
routing table lives in git.

- deployment: mount cloudflared-config (config.yml) and the
  cloudflared-credentials Secret; drop TUNNEL_TOKEN env
- configmap.yaml: per-hostname ingress rules to ingress-nginx via the
  CoreDNS rewrite, plus a 404 catch-all
- secret-generator: register the SOPS-encrypted credentials file
- homelab-ca: add the cloudflared namespace copy so the connector trusts
  the ingress certificate (force-ssl-redirect makes plaintext impossible)

Credentials were extracted from the live cloudflared-token Secret and
re-encrypted with SOPS; no plaintext secret is committed.
This commit is contained in:
2026-10-25 19:57:37 -07:00
parent d4e9dbf350
commit 227a67a8c1
6 changed files with 168 additions and 8 deletions
+58
View File
@@ -0,0 +1,58 @@
# cloudflared ingress configuration.
#
# This list is the tunnel's routing table: for each incoming hostname the
# connector dials the matching origin. The final rule is a catch-all, so a
# hostname absent from this list gets 404 from the connector — DNS alone does
# not route a request into a service.
#
# Management model: this file makes the tunnel LOCALLY managed. cloudflared is
# given a credentials file instead of a token, so Cloudflare stops pushing the
# remotely-managed ingess list and this ConfigMap wins. Revert the Deployment to
# `run --token` to return to the dashboard-managed list.
#
# Origin services self-reference the public hostname. That resolves because
# CoreDNS rewrites each of these names to ingress-nginx
# (terraform/files/coredns/Corefile), so the connector reaches the in-cluster
# ingress instead of looping back out through Cloudflare.
apiVersion: v1
kind: ConfigMap
metadata:
name: cloudflared-config
namespace: cloudflared
data:
config.yml: |
tunnel: 32d0bf93-e1bd-45b8-93a9-71ff9f83de80
credentials-file: /etc/cloudflared/creds/credentials.json
# WARP-to-Tunnel routing. The remotely-managed config had this enabled;
# dropping the key would break WARP access to private addresses. cloudflared
# 2026.x has no `enabled` field — the key's presence is what enables it, and
# `enabled: true` here makes the binary warn about an unused key.
warp-routing: {}
ingress:
- hostname: paperless.riotpiao.com
service: https://paperless.riotpiao.com
- hostname: authentik.riotpiao.com
service: https://authentik.riotpiao.com
- hostname: img.riotpiao.com
service: https://img.riotpiao.com
- hostname: riotpiao.com
service: https://riotpiao.com
- hostname: grafana.riotpiao.com
service: https://grafana.riotpiao.com
- hostname: gotify.riotpiao.com
service: https://gotify.riotpiao.com
- hostname: igotify.riotpiao.com
service: https://igotify.riotpiao.com
- hostname: poimen.riotpiao.com
service: https://poimen.riotpiao.com
- hostname: comfyui.riotpiao.com
service: https://comfyui.riotpiao.com
- hostname: forgejo.riotpiao.com
service: https://forgejo.riotpiao.com
# ZITADEL. Without this rule the connector answers 404 for the hostname
# even though the DNS CNAME points at the tunnel.
- hostname: zitadel.riotpiao.com
service: https://zitadel.riotpiao.com
- service: http_status:404
+48 -8
View File
@@ -13,23 +13,63 @@ spec:
labels:
app: cloudflared
spec:
# Ingress-nginx forces HTTPS (force-ssl-redirect), so cloudflared must
# dial https://<hostname> and trust homelab-ca or the TLS handshake fails
# with "certificate signed by unknown authority". Same pattern as
# authentik-values.yaml: merge the system bundle with homelab-ca.crt.
initContainers:
- name: merge-ca-certs
image: debian:bookworm
imagePullPolicy: IfNotPresent
securityContext:
runAsUser: 0
command:
- sh
- -c
- (cat /etc/ssl/certs/ca-certificates.crt 2>/dev/null; cat /homelab-ca/homelab-ca.crt) > /merged/ca-bundle.crt
volumeMounts:
- name: homelab-ca
mountPath: /homelab-ca
readOnly: true
- name: merged-ca
mountPath: /merged
volumes:
- name: config
configMap:
name: cloudflared-config
- name: credentials
secret:
secretName: cloudflared-credentials
- name: homelab-ca
configMap:
name: homelab-ca
- name: merged-ca
emptyDir: {}
containers:
- name: cloudflared
image: cloudflare/cloudflared:latest
args:
- tunnel
- --no-autoupdate
- run
- --protocol
- http2
- --token
- $(TUNNEL_TOKEN)
- --config
- /etc/cloudflared/config.yml
- run
env:
- name: TUNNEL_TOKEN
valueFrom:
secretKeyRef:
name: cloudflared-token
key: token
- name: SSL_CERT_FILE
value: /merged/ca-bundle.crt
volumeMounts:
- name: config
mountPath: /etc/cloudflared/config.yml
subPath: config.yml
readOnly: true
- name: credentials
mountPath: /etc/cloudflared/creds
readOnly: true
- name: merged-ca
mountPath: /merged
readOnly: true
resources:
requests:
cpu: 10m
+1
View File
@@ -3,3 +3,4 @@ kind: Kustomization
namespace: cloudflared
resources:
- deployment.yaml
- configmap.yaml