diff --git a/k8s/apps/cloudflared/configmap.yaml b/k8s/apps/cloudflared/configmap.yaml new file mode 100644 index 0000000..48bdccb --- /dev/null +++ b/k8s/apps/cloudflared/configmap.yaml @@ -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 diff --git a/k8s/apps/cloudflared/deployment.yaml b/k8s/apps/cloudflared/deployment.yaml index 178f801..8087b62 100644 --- a/k8s/apps/cloudflared/deployment.yaml +++ b/k8s/apps/cloudflared/deployment.yaml @@ -13,23 +13,63 @@ spec: labels: app: cloudflared spec: + # Ingress-nginx forces HTTPS (force-ssl-redirect), so cloudflared must + # dial https:// 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 diff --git a/k8s/apps/cloudflared/kustomization.yaml b/k8s/apps/cloudflared/kustomization.yaml index 3e60949..93f4bb6 100644 --- a/k8s/apps/cloudflared/kustomization.yaml +++ b/k8s/apps/cloudflared/kustomization.yaml @@ -3,3 +3,4 @@ kind: Kustomization namespace: cloudflared resources: - deployment.yaml + - configmap.yaml diff --git a/k8s/argocd/secrets/cloudflared-credentials.enc.yaml b/k8s/argocd/secrets/cloudflared-credentials.enc.yaml new file mode 100644 index 0000000..d3e1028 --- /dev/null +++ b/k8s/argocd/secrets/cloudflared-credentials.enc.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: Secret +metadata: + name: cloudflared-credentials + namespace: cloudflared +type: Opaque +stringData: + credentials.json: ENC[AES256_GCM,data:LnJVGuxJAOwDYbLgX00OHICDDldZgwgvGafOIwKoJKnjg3FjFMQYThNCOL/H+6tctuKixJNcAckjg70tmCKWxNq9I35XX/YSqob6Qv4fywv32hsNIAe44BU035MGZ5zmc6bZZ8wAgTJZw6Th4TzY7taFBCRdj2fW4T+qVfq7strn68G4ufVMK6UXlOkEDLjR2Vxj32lqSLdbdMma96XB+265hwL6IVfaugG662mcGhPwgHk=,iv:NFmyHN5m2mJUBgn1qgmJZ2YgkFxBZyp7+gU1Ng6B/7M=,tag:/66JtHK5Yu7pRxB0b0eifQ==,type:str] +sops: + age: + - enc: | + -----BEGIN AGE ENCRYPTED FILE----- + YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBsaDVycUE4RFpoRjdpTlpX + dGpzN3hwRFQ2cmtXVXVzWnZJOXJEZXZRWEhNCkJwbm9QV0lGcGtNRTJqbStrc3l1 + Q2M4bE1WbzJpTXZQWjJyTGpFS0JYcVUKLS0tIDBvU2U5aEtVTTZUaE85eEkyYitG + bGtSVkhKNnRuNlZWMWh6Z1M4VXM1WU0KdusvtM85V0xrO+uJFUY+UH0tuwdF0GiW + 79J4Rlxk8ne25psKsvwjLYLxZ53xjFHCC4FCHTNkYwTKCDEKjw46Lg== + -----END AGE ENCRYPTED FILE----- + recipient: age1e5fq3hwxy78psus2nfvmtmua36g0u3suk78ephw6246l974d2utsvn0hla + encrypted_regex: ^(data|stringData)$ + lastmodified: "2026-10-26T02:45:04Z" + mac: ENC[AES256_GCM,data:KF7KwXCnNLAg1YI3u0lLI/ho4sITHAq09ac2WO4TK3TCthkuivb89YjhuuJKs0NFhjn6aRLqmZavUqcXH2HujiryqbLNVexOShQM0wSzgCzmWdbi0UzpgJ4KcQA3PufDtsQz4HHLXx396EbZrM3Ie3WSu/PvRm5iWOxzTGCGc9Q=,iv:Eo920oXXD1FLZVJXywZ6C+LRL+qZjmnZXv0+Oa/GMb8=,tag:6LuPVSet4WjvrvGUAxaJlw==,type:str] + version: 3.13.2 diff --git a/k8s/argocd/secrets/secret-generator.yaml b/k8s/argocd/secrets/secret-generator.yaml index a9d39f1..990b289 100644 --- a/k8s/argocd/secrets/secret-generator.yaml +++ b/k8s/argocd/secrets/secret-generator.yaml @@ -11,6 +11,7 @@ files: - agent-pod-ssh-key.enc.yaml - authentik-secrets.enc.yaml - cloudflare-secrets.enc.yaml + - cloudflared-credentials.enc.yaml - forgejo-registry-pat-api.enc.yaml - forgejo-registry-pull.enc.yaml - forgejo-runner-token.enc.yaml diff --git a/k8s/bootstrap/cert-manager/homelab-ca-configmap.yaml b/k8s/bootstrap/cert-manager/homelab-ca-configmap.yaml index 780e11c..ab80c71 100644 --- a/k8s/bootstrap/cert-manager/homelab-ca-configmap.yaml +++ b/k8s/bootstrap/cert-manager/homelab-ca-configmap.yaml @@ -271,3 +271,40 @@ data: NDKi7/Gxha790QN3TD/U7p9GcEtfXKv8KISKfxnfD9o9Yx6pWOXcgvGefQkpXuRm kGzDM+aY/VKs5Et5/RyKavBDihuOcumLsmoRg/bFoB/xpeURTyiehUdu -----END CERTIFICATE----- +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: homelab-ca + namespace: cloudflared +data: + ca.crt: | + -----BEGIN CERTIFICATE----- + MIIFBjCCAu6gAwIBAgIJAKoBPdKPDL5AMA0GCSqGSIb3DQEBCwUAMDAxEzARBgNV + BAMMCmhvbWVsYWItY2ExGTAXBgNVBAoMEHJpb3RwaWFvLmhvbWVsYWIwHhcNMjYw + ODEyMjA1NDQzWhcNMzYwODA5MjA1NDQzWjAwMRMwEQYDVQQDDApob21lbGFiLWNh + MRkwFwYDVQQKDBByaW90cGlhby5ob21lbGFiMIICIjANBgkqhkiG9w0BAQEFAAOC + Ag8AMIICCgKCAgEAzw796vZRFOBc/orUu9J6rv91A3SNLJsqz/VuetwAXCaVSlK5 + kL/RaBKKkcXToCC5avwix6W21FnwLUW4R8QlQdin8+XEHpx2LY5R4GmSvaCbU7wm + YwYgVGQVv8KlgcMbz/pF09vXU29cHUHwlkN+BWVGAxVGWxam94j1pBzV0dOhhRw2 + q5sZYUUrPO5l775od2FMMnMzoRFugxDvJof0p3fAChNAIiFhP95lrcb2YsTwK+a2 + 6mk3AZc0gMtmgVsH+zryGFcAmWLixq41ye/H23ktj2bjEHCr9mIDpuSOKLyLBzrg + js/HqdxvwkZkkxwXft0Vi7GN0/LGHMdPrDE499pJelAHbxUpBoOk8sfXSNnfESI8 + v20LybATGrKXFlOhMKshY1a3JLAoZTaW0xkG4PM4HT7lK/ovr/NLF6kdP7HxJFFQ + +4WjySiwbjusNobpTaQWVwCjD3Imf+2feAqJeX3z+P3EhxfrmvEojLOdUiiPcjMn + CFl9u2EpSlax53umSkcBQJGfFKM17fZzhtakjqhCTzAfXXlxZy4hsHDtfzRcfaoB + Zz/PiJ0TtJSC7TvxZZoPpGexQdoeT7kJEksTvGi5OSfJD7HaKe5RQSZGgpz7ATB+ + YMBKbjhMXVu+TAdcO1qZRN0UCncrliN61ki9F0tE8kNgpS9biZ6kheoJMZUCAwEA + AaMjMCEwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAYYwDQYJKoZIhvcN + AQELBQADggIBAFN1At11d/+Ue6cEjCklnRQpLuD5agXaE+pi5oHNfnggSlO8UjUj + lDzfnmxi3IAI6HruzDARM9J95qJboUxWOAEmK9SNnZNS/1b0GyikP3YcF1gIFqtW + 6SgmVfC1cVUFKGt/+QQzZxhOLuoydv9Ozu12nxsTW/WY2t+cpeL+1wrsM22RA6W/ + xV/bzYbwzCUT9v4tMZLZHk5CkXztVFXSJyW/8w9rVZdUGZM+mwwn81PLSE0v3s/2 + wJH8eUqGl73i3Xgn7CVanN4cJYMGYVVlRD6Qr6muBN4Vh0ETY1PrjzsZ6XsV6vk9 + glMbQnErYMZHZIDtnVdaBt60MecUD9O4eLeip0SE6bVRAucKBXA9xNMilMQEL8/X + FBMAxXl10rowBlbX2oaFSOM80eLTla4a3gSL7+0vua3C4Dtv2bLo8ZjCyqXwOJeb + N85cqcVZStz3qlgQN1nG2qGE3BS6scOs7+mUGe9CmS8acJV++PKm8CtmLH6/ibjV + tAc/6+Vg/keo0kty7H7L6oQd+gKpuaJ2I+wKYe4l9UwHi9zEb8YjzGB4n2AtsoWI + NDKi7/Gxha790QN3TD/U7p9GcEtfXKv8KISKfxnfD9o9Yx6pWOXcgvGefQkpXuRm + kGzDM+aY/VKs5Et5/RyKavBDihuOcumLsmoRg/bFoB/xpeURTyiehUdu + -----END CERTIFICATE-----