From d30084162ebdc8e9ea6d45fe8803849bb34586a1 Mon Sep 17 00:00:00 2001 From: Konstantin Renner Date: Fri, 26 Jun 2026 12:12:35 +0200 Subject: [PATCH] cloudflared: decommission the Cloudflare tunnel ingress MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All 8 public hostnames have moved off the tunnel to the NetBird reverse proxy, so cloudflared routes nothing but a 404. Remove the component entirely: - delete kubernetes/infrastructure/cloudflared/ (Deployment, ConfigMap, tunnel-credentials Secret, Kustomization) + drop it from infrastructure/kustomization.yaml (Flux prunes the workload) - remove the cloudflared image-automation policy + README row - drop the cloudflared-to-ingress-only CiliumNetworkPolicy - scrub stale "cloudflared" comments (netbird-ingress, k8s-gateway, ingress-default-deny external-entry note) Cloudflare is still used for DNS (external-dns-cloudflare) and cert-manager DNS-01 — only the tunnel is retired. The Cloudflare-side tunnel itself (Zero Trust) can be deleted separately. MERGE LAST — only after every per-service proxy cutover is live and verified; cloudflared already serves no routes, so this just removes the dormant workload. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../infrastructure/cloudflared/configmap.yaml | 15 ----- .../cloudflared/deployment.yaml | 56 ------------------- .../cloudflared/kustomization.yaml | 6 -- .../cloudflared/secret.sops.yaml | 23 -------- .../infrastructure/image-automation/README.md | 1 - .../image-automation/cloudflared.yaml | 29 ---------- .../image-automation/kustomization.yaml | 1 - .../k8s-gateway/helmrelease.yaml | 2 +- kubernetes/infrastructure/kustomization.yaml | 1 - .../netbird-ingress/deployment.yaml | 6 +- .../network-policies/ingress-isolation.yaml | 52 ++--------------- 11 files changed, 8 insertions(+), 184 deletions(-) delete mode 100644 kubernetes/infrastructure/cloudflared/configmap.yaml delete mode 100644 kubernetes/infrastructure/cloudflared/deployment.yaml delete mode 100644 kubernetes/infrastructure/cloudflared/kustomization.yaml delete mode 100644 kubernetes/infrastructure/cloudflared/secret.sops.yaml delete mode 100644 kubernetes/infrastructure/image-automation/cloudflared.yaml diff --git a/kubernetes/infrastructure/cloudflared/configmap.yaml b/kubernetes/infrastructure/cloudflared/configmap.yaml deleted file mode 100644 index 9f7a25a2..00000000 --- a/kubernetes/infrastructure/cloudflared/configmap.yaml +++ /dev/null @@ -1,15 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: cloudflared-config - namespace: ingress -data: - config.yaml: | - tunnel: d2386ccd-a665-41e7-8631-603fdafcb006 - credentials-file: /etc/cloudflared/credentials.json - metrics: 0.0.0.0:2000 - no-autoupdate: true - - ingress: - # Catch-all - - service: http_status:404 diff --git a/kubernetes/infrastructure/cloudflared/deployment.yaml b/kubernetes/infrastructure/cloudflared/deployment.yaml deleted file mode 100644 index 64c7e99c..00000000 --- a/kubernetes/infrastructure/cloudflared/deployment.yaml +++ /dev/null @@ -1,56 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cloudflared - namespace: ingress - labels: - app: cloudflared -spec: - replicas: 2 - selector: - matchLabels: - app: cloudflared - template: - metadata: - labels: - app: cloudflared - spec: - containers: - - name: cloudflared - image: cloudflare/cloudflared:2026.6.1 # {"$imagepolicy": "flux-system:cloudflared"} - args: - - tunnel - - --config - - /etc/cloudflared/config.yaml - - run - volumeMounts: - - name: config - mountPath: /etc/cloudflared/config.yaml - subPath: config.yaml - readOnly: true - - name: credentials - mountPath: /etc/cloudflared/credentials.json - subPath: credentials.json - readOnly: true - ports: - - containerPort: 2000 - name: metrics - resources: - requests: - cpu: 50m - memory: 64Mi - limits: - memory: 256Mi - livenessProbe: - httpGet: - path: /ready - port: metrics - initialDelaySeconds: 10 - periodSeconds: 30 - volumes: - - name: config - configMap: - name: cloudflared-config - - name: credentials - secret: - secretName: cloudflared-credentials diff --git a/kubernetes/infrastructure/cloudflared/kustomization.yaml b/kubernetes/infrastructure/cloudflared/kustomization.yaml deleted file mode 100644 index 4c48ad17..00000000 --- a/kubernetes/infrastructure/cloudflared/kustomization.yaml +++ /dev/null @@ -1,6 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: - - secret.sops.yaml - - configmap.yaml - - deployment.yaml diff --git a/kubernetes/infrastructure/cloudflared/secret.sops.yaml b/kubernetes/infrastructure/cloudflared/secret.sops.yaml deleted file mode 100644 index 154a1337..00000000 --- a/kubernetes/infrastructure/cloudflared/secret.sops.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: ENC[AES256_GCM,data:Knk=,iv:EePXth4ClrsLa/tNd8rKJa0TSpoRppF/UY+vowhbGHI=,tag:K6vVMfB8nlylawgaVJ6IkQ==,type:str] -kind: ENC[AES256_GCM,data:Q6eVCfmA,iv:N1nHKbFGcx7m7462eNVeVxrbvap/iiO1g+iafHnupys=,tag:11bX04mASxHDP7kLQENIqw==,type:str] -metadata: - name: ENC[AES256_GCM,data:3pJApagBL7I11uL12jJQSIf0ggX19og=,iv:6Jw/eXrjE8Xyc/aNKK2tCB9HuLTwq96mt93j7hc+1q8=,tag:Z5whorYxqp0mHl04IPFPYg==,type:str] - namespace: ENC[AES256_GCM,data:mzcIBgFCew==,iv:wgUBU2Irb2vOmPcc/Xynnb1Nm5kiWnBKmlwbNrcHSw0=,tag:Beg4WmnK6aiPC3p0/aHzJg==,type:str] -type: ENC[AES256_GCM,data:r/eSbu7o,iv:hzwYi1eoVaYzh8hCiKTza6lGfA0NUCMYCqmKnB/Ttac=,tag:URwYkY42hS2OZQwFnQRVVQ==,type:str] -stringData: - credentials.json: ENC[AES256_GCM,data:znWLH6jv69hlFS4NWF3gnCIHQBwLRGHlWCw23uxSwR6x0drwfx5muMfQpriXwBG6GPKkq6hMb0cej2Whh4E/kj9DkpoEFc5MfVats8dVr18pSI+DmY8JFmcRpcrKb5dmBR3crfOzo4cGXwFJPYzoONCXIfo0k92zNIhBQ/sXIstMFbMjE3Svdxz+z/iV2QeJ8YSDnNRyYr3HY1RvTXdf2Pbe,iv:079x5CbOg/090x/cmZItHdXfp3vSL3WgE67BJQ1cY4Y=,tag:Jt9CDyJQm7wt7AyaZS92KQ==,type:str] -sops: - age: - - recipient: age1yqjvqkwwwxdj7mr546zfs5ckzuwndp69ya2n2drd4vypwns8w4ys04qfy3 - enc: | - -----BEGIN AGE ENCRYPTED FILE----- - YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBxQXIreW1nN3dNbU54ZXI0 - SGVISDBFL3Ric2ovbnJTUjVJb1ZEUFArNlV3CnhibWQvY0RVc2FSWW9lSEFVM1dN - THV0cDBqZU9uWTJ1eU00ZlM3Z2RyQU0KLS0tIEp5WlExT3liQmIxTXBTRGU0Sm9D - V0RMekxwZ1lLaFRNek1weWNGQkRndWsKjWZTtkUBAS17khEVynekGoP+qf57ZPz1 - VkWtWrkDmDtmyTVgXMOmlEWJoBmjirMvxLRZ9QBoZCSLOIptTmFkvg== - -----END AGE ENCRYPTED FILE----- - lastmodified: "2026-04-05T20:18:25Z" - mac: ENC[AES256_GCM,data:cTdHAtXgfsfbL83dpJyRZjGzOrk5eIjjpDtSJ3132cyNHw72+cLf1JEkyOG9VecZAkdLQFH1fmuRSnyrGUHRv/yk0dS41BU5XtILQNRYoiNAMC9o/LmdTnl7B6KLAdVP7AzmiV+1bftoZn4C3Nzs52TPxsrtmHd0ToCnPFzlG0c=,iv:p6V8ATSK4NlbvXov4oYbEhdDkChp1SRmetYRL1Z09TI=,tag:kUOwsltBy9vW/h6GSrhR7A==,type:str] - unencrypted_suffix: _unencrypted - version: 3.10.2 diff --git a/kubernetes/infrastructure/image-automation/README.md b/kubernetes/infrastructure/image-automation/README.md index 24a949fc..edb8cc66 100644 --- a/kubernetes/infrastructure/image-automation/README.md +++ b/kubernetes/infrastructure/image-automation/README.md @@ -42,7 +42,6 @@ existing `flux-system` GitRepository whose Secret was created by | matter-server | `8.x` | Pre-release filter | | zwave-js-ui | `11.x` | | | wyoming-{whisper,openwakeword,speech-to-phrase} | `3.x` / `2.x` / `1.x` | | -| cloudflared | `2026.x` | Calver | | gatus | `5.x` | v-prefix filter | | velero-plugin-for-aws | `1.13.x` | Tight: must match velero server major.minor | | gotenberg | `8.x` | Plain-variant filter (rejects `-libreoffice`/`-chromium`/`-cloudrun`/`-aws-lambda`) | diff --git a/kubernetes/infrastructure/image-automation/cloudflared.yaml b/kubernetes/infrastructure/image-automation/cloudflared.yaml deleted file mode 100644 index dfe4427b..00000000 --- a/kubernetes/infrastructure/image-automation/cloudflared.yaml +++ /dev/null @@ -1,29 +0,0 @@ -apiVersion: image.toolkit.fluxcd.io/v1 -kind: ImageRepository -metadata: - name: cloudflared - namespace: flux-system -spec: - image: cloudflare/cloudflared - interval: 1h ---- -# Calver: YYYY.M.PATCH. Open-ended floor (>=2026.0.0) so January -# rollovers don't silently freeze the policy on the previous year's -# top tag. The filterTags regex restricts candidates to bare-numeric -# calver triplets, so non-release tags (latest, *-amd64, …) can't slip -# through; the floor only guards against rolling back to pre-2026 -# releases. -apiVersion: image.toolkit.fluxcd.io/v1 -kind: ImagePolicy -metadata: - name: cloudflared - namespace: flux-system -spec: - imageRepositoryRef: - name: cloudflared - filterTags: - pattern: '^(?P\d+\.\d+\.\d+)$' - extract: '$ver' - policy: - semver: - range: ">=2026.0.0" diff --git a/kubernetes/infrastructure/image-automation/kustomization.yaml b/kubernetes/infrastructure/image-automation/kustomization.yaml index 0c474b96..be736051 100644 --- a/kubernetes/infrastructure/image-automation/kustomization.yaml +++ b/kubernetes/infrastructure/image-automation/kustomization.yaml @@ -4,7 +4,6 @@ resources: - update-automation.yaml - audiobookshelf.yaml - ceph.yaml - - cloudflared.yaml - cnpg-barman-plugin.yaml - firefox.yaml - gatus.yaml diff --git a/kubernetes/infrastructure/k8s-gateway/helmrelease.yaml b/kubernetes/infrastructure/k8s-gateway/helmrelease.yaml index e523ceab..632635e6 100644 --- a/kubernetes/infrastructure/k8s-gateway/helmrelease.yaml +++ b/kubernetes/infrastructure/k8s-gateway/helmrelease.yaml @@ -16,7 +16,7 @@ spec: # # Names not backed by an in-cluster Ingress fall through to the Proxmox # DNS HA VIP (10.2.40.53) — preserving resolution of LXC/VM-hosted -# services (gitea, immich, cloudflared, …) without manual enumeration. +# services (gitea, immich, …) without manual enumeration. apiVersion: helm.toolkit.fluxcd.io/v2 kind: HelmRelease metadata: diff --git a/kubernetes/infrastructure/kustomization.yaml b/kubernetes/infrastructure/kustomization.yaml index 697c5fc2..540e438c 100644 --- a/kubernetes/infrastructure/kustomization.yaml +++ b/kubernetes/infrastructure/kustomization.yaml @@ -15,7 +15,6 @@ resources: - rook-ceph/ - ceph-csi-drivers/ - cloudnative-pg/ - - cloudflared/ - netbird-ingress/ - coredns-config/ - k8s-gateway/ diff --git a/kubernetes/infrastructure/netbird-ingress/deployment.yaml b/kubernetes/infrastructure/netbird-ingress/deployment.yaml index 2c9f2bc7..e350342c 100644 --- a/kubernetes/infrastructure/netbird-ingress/deployment.yaml +++ b/kubernetes/infrastructure/netbird-ingress/deployment.yaml @@ -1,5 +1,5 @@ -# NetBird mesh ingress — a second, parallel ingress path alongside -# cloudflared. This pod enrolls as a NetBird *routing peer* with the +# NetBird mesh ingress — the mesh-side ingress path. This pod enrolls as +# a NetBird *routing peer* with the # self-hosted management server (netbird.renner.uno) and forwards mesh # traffic to in-cluster services (the routes/nameservers are configured # NetBird-side, pointing at this peer). @@ -19,7 +19,7 @@ # --own socket (socket-LB DNAT)--> service ClusterIP/LB VIP # # Egress is locked down by the netbird-ingress-egress CiliumNetworkPolicy -# (network-policies/ingress-isolation.yaml), mirroring cloudflared. +# (network-policies/ingress-isolation.yaml). apiVersion: apps/v1 kind: Deployment metadata: diff --git a/kubernetes/infrastructure/network-policies/ingress-isolation.yaml b/kubernetes/infrastructure/network-policies/ingress-isolation.yaml index 1adc265f..06c3f2bd 100644 --- a/kubernetes/infrastructure/network-policies/ingress-isolation.yaml +++ b/kubernetes/infrastructure/network-policies/ingress-isolation.yaml @@ -1,49 +1,5 @@ -# Cloudflared can only reach the traefik controller pods (in-cluster -# tunnel-to-ingress hop) plus DNS and Cloudflare's edge over UDP/7844 -# and TCP/443. Cilium's socket-LB DNATs the ClusterIP → pod IP at the -# socket layer, so the egress policy sees the pod's container ports — -# traefik runs on 8443/8000 internally (Service 443/TCP → targetPort -# websecure → containerPort 8443). -apiVersion: cilium.io/v2 -kind: CiliumNetworkPolicy -metadata: - name: cloudflared-to-ingress-only - namespace: ingress -spec: - endpointSelector: - matchLabels: - app: cloudflared - egress: - - toEndpoints: - - matchLabels: - app.kubernetes.io/name: traefik - toPorts: - - ports: - - port: "8443" - protocol: TCP - - port: "8000" - protocol: TCP - # Allow DNS resolution - - toEndpoints: - - matchLabels: - "k8s:io.kubernetes.pod.namespace": kube-system - k8s-app: kube-dns - toPorts: - - ports: - - port: "53" - protocol: UDP - # Allow Cloudflare tunnel connections (external) - - toEntities: - - world - toPorts: - - ports: - - port: "7844" - protocol: UDP - - port: "443" - protocol: TCP ---- -# NetBird mesh-ingress routing peer: same egress-lockdown discipline as -# cloudflared above. It may reach (a) traefik (by label selector — Cilium +# NetBird mesh-ingress routing peer — egress lockdown. It may reach (a) +# traefik (by label selector — Cilium # applies LB/DNAT before egress policy, so forwarded mesh→VIP traffic is # enforced on traefik's identity, no static VIP needed); (b) DNS; (c) the # NetBird control/data plane on netbird.renner.uno — mgmt/signal/relay @@ -93,8 +49,8 @@ spec: # Any in-cluster pod may reach traefik (required for east-west OIDC # flows via k8s_gateway — audiobookshelf → Authentik etc.); we don't # enumerate namespaces, which would require policy edits every time a -# new app lands. External entry is still bounded by the -# Cloudflare-proxied / MetalLB / host probe paths. +# new app lands. External entry is now via the NetBird reverse proxy +# (over the mesh) and the BGP-advertised LB VIP / host probe paths. apiVersion: cilium.io/v2 kind: CiliumNetworkPolicy metadata: -- 2.54.0