### Deploy Example Application Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/tunnel-binding-with-reverse-proxy/README.md Apply the manifests for the example application to deploy it. ```shell kubectl apply -f manifests/hello/ ``` -------------------------------- ### Deploy Example Applications Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/tunnel-binding-simple/README.md Apply the Kubernetes manifests for the example applications. This creates the necessary deployments and services for 'whoami-1' and 'whoami-2'. ```shell kubectl apply -f manifests/whoami-1/ kubectl apply -f manifests/whoami-2/ ``` -------------------------------- ### Declarative Installation with Kustomization Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/getting-started.md Use this method for a recommended declarative installation. Ensure the 'ref' in the kustomization.yaml points to the desired version. ```yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: cloudflare-operator-system resources: # ensure you update the ref in this line to the latest version - https://github.com/adyanth/cloudflare-operator.git/config/default?ref=v0.13.1 ``` -------------------------------- ### Imperative Installation of a Specific Tag Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/getting-started.md Install a specific tagged version of the operator using kubectl and kustomize. Always prefer installing specific tags for stability. ```bash kubectl apply -k 'https://github.com/adyanth/cloudflare-operator.git//config/default?ref=v0.13.1' ``` -------------------------------- ### Imperative One-Shot Installation Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Installs the operator using a direct `kubectl apply` command with a Kustomize URL. Recommended to use a specific tag for production environments. ```bash # Install a specific tag (recommended for production) kubectl apply -k 'https://github.com/adyanth/cloudflare-operator.git//config/default?ref=v0.13.1' # Install the latest main branch kubectl apply -k 'https://github.com/adyanth/cloudflare-operator.git/config/default?ref=main' ``` -------------------------------- ### Explain Tunnel Spec Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/configuration/tunnel-and-cluster-tunnel.md Use this command to get the latest documentation on Tunnel and ClusterTunnel configuration options after installing the CRDs. ```bash kubectl explain tunnel.spec ``` -------------------------------- ### Imperative Installation of the Latest Version Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/getting-started.md Install the latest version of the operator without explicitly checking tags. Use with caution as this deploys a point-in-time version. ```bash kubectl apply -k 'https://github.com/adyanth/cloudflare-operator.git/config/default?ref=main' kubectl apply -k 'https://github.com/adyanth/cloudflare-operator/config/default' ``` -------------------------------- ### Kustomize Installation of Cloudflare Operator Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Installs the operator and its CRDs using Kustomize. Ensure the correct Git repository and tag are specified for your deployment. ```yaml apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization namespace: cloudflare-operator-system resources: - https://github.com/adyanth/cloudflare-operator.git/config/default?ref=v0.13.1 ``` -------------------------------- ### Install cert-manager Source: https://github.com/adyanth/cloudflare-operator/wiki/Testing-Webhooks-locally Install cert-manager using its official release manifest. This tool is used for automating TLS certificate management within Kubernetes. ```bash kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.17.2/cert-manager.yaml ``` -------------------------------- ### ClusterTunnel v1alpha2 Configuration Example Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/configuration/tunnel-and-cluster-tunnel.md This YAML defines a ClusterTunnel resource for v1alpha2. It includes settings for Cloudflare account details, specifying either a new or existing tunnel, and cloudflared configuration options like fallback target and TLS verification. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha2 kind: ClusterTunnel # or Tunnel metadata: name: tunnel-cr-name spec: # Cloudflare details cloudflare: ## AccountName and AccountId cannot be both empty. If both are provided, Account ID is used if valid, else falls back to Account Name accountId: account-id accountName: Account Name domain: example.com # Domain where the tunnel runs email: admin@example.com # Email ID used to login to Cloudflare # Cloudflare credentials secret, and its key overrides. All the overrides are optional and default to the shown values. secret: cloudflare-secrets ## Key in the secret to use for Cloudflare API token. See getting started for information on scopes CLOUDFLARE_API_TOKEN: CLOUDFLARE_API_TOKEN ## Key in the secret to use for Cloudflare API Key. Needs Email also to be provided. For delete operations on new tunnels only, or as an alternate to API Token CLOUDFLARE_API_KEY: CLOUDFLARE_API_KEY ## Key in the secret to use as credentials.json for an existing tunnel CLOUDFLARE_TUNNEL_CREDENTIAL_FILE: CLOUDFLARE_TUNNEL_CREDENTIAL_FILE ## Key in the secret to use as tunnel secret for an existing tunnel CLOUDFLARE_TUNNEL_CREDENTIAL_SECRET: CLOUDFLARE_TUNNEL_CREDENTIAL_SECRET # Either existingTunnel or newTunnel can be specified, not both newTunnel: name: new-tunnel existingTunnel: ## Existing Tunnel id/name to run on. Tunnel Name and Tunnel ID cannot be both empty. If both are provided, id is used if valid, else falls back to name id: name: existing-tunnel # cloudflared configuration fallbackTarget: http_status:404 # The default service to point cloudflared to. Defaults to http_status:404 noTlsVerify: false # Disables the TLS verification to backend services globally originCaPool: homelab-ca # Secret containing CA certificates to trust. Must contain tls.crt to be trusted globally and optionally other certificates (see the caPool service annotation for usage) deployPatch: | spec: replicas: 2 template: spec: containers: - name: cloudflared image: cloudflare/cloudflared:2025.4.0 # Image to run. Used for running a pinned image. Can be swapped out to an arm based image if needed ``` -------------------------------- ### Verify CRD Installation Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Checks if the Cloudflare Operator Custom Resource Definitions (CRDs) have been successfully installed in the cluster. ```bash kubectl get crd | grep cfargotunnel.com ``` -------------------------------- ### Ingress Nginx with Custom SSO Annotations Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/tunnel-binding-with-reverse-proxy/README.md Configure ingress-nginx to use authelia for custom SSO. This setup directs traffic through a Cloudflare tunnel to ingress-nginx, enabling 2FA for applications that do not natively support it. ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: annotations: cert-manager.io/cluster-issuer: "letsencrypt-prod" # in order to direct cloudflare tunnel to ingress-nginx, we have a single TunnelBinding resource in ingress-nginx. # this is what controls ingress to ingress-nginx and allows us to put 2fa infront of apps that don't support it natively. nginx.ingress.kubernetes.io/rewrite-target: "/" nginx.ingress.kubernetes.io/auth-method: "GET" nginx.ingress.kubernetes.io/auth-url: "http://authelia.authelia.svc.cluster.local:8080/api/authz/auth-request" nginx.ingress.kubernetes.io/auth-signin: "https://authelia.?rm=$request_method" nginx.ingress.kubernetes.io/auth-response-headers: "Remote-User,Remote-Name,Remote-Groups,Remote-Email" --- SNIP --- ``` -------------------------------- ### Update CRD Webhook ClientConfig to Use URL Source: https://github.com/adyanth/cloudflare-operator/wiki/Testing-Webhooks-locally Patch CRD webhook configurations to use a direct URL for the webhook service instead of a Kubernetes service reference. Ensure the IP address and port match your local setup. ```diff diff --git a/config/crd/patches/webhook_in_clustertunnels.yaml b/config/crd/patches/webhook_in_clustertunnels.yaml index 47fb964..799348f 100644 --- a/config/crd/patches/webhook_in_clustertunnels.yaml +++ b/config/crd/patches/webhook_in_clustertunnels.yaml @@ -8,9 +8,6 @@ spec: strategy: Webhook webhook: clientConfig: - service: - namespace: system - name: webhook-service - path: /convert + url: https://100.120.130.56:9443/convert conversionReviewVersions: - v1 diff --git a/config/crd/patches/webhook_in_tunnels.yaml b/config/crd/patches/webhook_in_tunnels.yaml index ae3c304..3171929 100644 --- a/config/crd/patches/webhook_in_tunnels.yaml +++ b/config/crd/patches/webhook_in_tunnels.yaml @@ -8,9 +8,6 @@ spec: strategy: Webhook webhook: clientConfig: - service: - namespace: system - name: webhook-service - path: /convert + url: https://100.120.130.56:9443/convert conversionReviewVersions: - v1 ``` -------------------------------- ### Deploying with Kustomization Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/getting-started.md Apply the kustomization configuration to your Kubernetes cluster. Both kubectl and kustomize build followed by kubectl apply are supported. ```bash # either approach will work kubectl apply -k . kustomize build . | kubectl apply -f - ``` -------------------------------- ### Deploy Ingress-Nginx Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/tunnel-binding-with-reverse-proxy/README.md Build and apply the ingress-nginx manifests using kustomize to deploy the reverse proxy. ```shell kustomize build --enable-helm manifests/ingress-nginx | kubectl apply -f - ``` -------------------------------- ### Route Traffic via TunnelBinding and Ingress Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Configure a wildcard TunnelBinding to direct all *.example.com traffic to an ingress controller. Use standard Kubernetes Ingress resources for service routing. Ensure the TunnelBinding is configured with a 'zz-' prefix for correct ordering. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha1 kind: TunnelBinding metadata: name: zz-ingress-nginx # "zz-" prefix ensures this is last in cloudflared config namespace: ingress-nginx subjects: - name: wildcard spec: fqdn: "*.example.com" target: https://ingress-nginx-controller.ingress-nginx.svc.cluster.local:443 noTlsVerify: true tunnelRef: kind: ClusterTunnel name: k3s-cluster-tunnel --- # Standard Kubernetes Ingress (processed by nginx, not cloudflare-operator) apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app namespace: my-app-ns annotations: nginx.ingress.kubernetes.io/rewrite-target: / spec: ingressClassName: nginx rules: - host: myapp.example.com http: paths: - path: / pathType: Prefix backend: service: name: my-app-svc port: number: 8080 ``` -------------------------------- ### Configure Manager with Webhook Server Options Source: https://github.com/adyanth/cloudflare-operator/wiki/Testing-Webhooks-locally Modify the controller-runtime manager to use a custom webhook server certificate directory. Replace `/path/to/dev/certs/` with the actual path where your TLS certificates will be stored. ```diff diff --git a/cmd/main.go b/cmd/main.go index cec59e7..52e45dd 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -35,6 +35,7 @@ import ( signgs.k8s.io/controller-runtime/pkg/log/zap signgs.k8s.io/controller-runtime/pkg/metrics/filters metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" + signgs.k8s.io/controller-runtime/pkg/webhook networkingv1alpha1 "github.com/adyanth/cloudflare-operator/api/v1alpha1" nnetworkingv1alpha2 "github.com/adyanth/cloudflare-operator/api/v1alpha2" @@ -100,6 +101,9 @@ func main() { LeaderElection: enableLeaderElection, LeaderElectionID: "9f193cf8.cfargotunnel.com", LeaderElectionNamespace: clusterResourceNamespace, + WebhookServer: webhook.NewServer(webhook.Options{ + CertDir: "/path/to/dev/certs/", + }), }) if err != nil { setupLog.Error(err, "unable to start manager") ``` -------------------------------- ### Deploy TunnelBinding Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/tunnel-binding-with-reverse-proxy/README.md Apply the TunnelBinding manifest to configure the cluster tunnel to point to the reverse proxy. ```shell kubectl apply -f manifests/tunne-binding.yaml ``` -------------------------------- ### Apply Kustomization Manifest Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Applies the Kustomize configuration to your Kubernetes cluster. This command can be used directly or after building the manifest. ```bash kubectl apply -k . # or kustomize build . | kubectl apply -f - ``` -------------------------------- ### Verify Tunnel and Status Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Apply the `Tunnel` YAML and verify its creation within the specified namespace. You can also inspect the tunnel's status fields resolved by the operator. ```bash kubectl apply -f tunnel.yaml -n my-app-ns kubectl get tunnel -n my-app-ns # NAME TUNNELID # my-app-tunnel 550e8400-e29b-41d4-a716-446655440000 # Check status fields resolved by the operator kubectl get tunnel my-app-tunnel -n my-app-ns -o jsonpath='{.status}' # {"accountId":"...","tunnelId":"550e8400-...","tunnelName":"my-existing-tunnel","zoneId":"..."} ``` -------------------------------- ### Apply and Check TunnelBinding Status Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Apply the TunnelBinding configuration and check its status to verify resolved hostnames and backend targets. ```bash kubectl apply -f tunnelbinding.yaml -n my-app-ns # Check binding status – shows resolved hostnames and backend targets kubectl get tunnelbinding my-services-binding -n my-app-ns -o jsonpath='{.status}' ``` -------------------------------- ### Create Cloudflare API Token Secret (Imperative) Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/operator-authentication/README.md Use this command to create a Kubernetes secret for storing your Cloudflare API token imperatively. Replace `` with your actual token. ```bash kubectl create secret generic cloudflare-secrets \ --namespace cloudflare-operator-system \ --from-literal CLOUDFLARE_API_TOKEN= ``` -------------------------------- ### Expose Service as TCP Tunnel (TunnelBinding) Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/configuration/access-tunnel.md Use TunnelBinding in the source cluster to expose a service via a TCP tunnel. Ensure the protocol is set to 'tcp'. This makes the service accessible over the internet and should be secured with Cloudflare Access ZTNA if necessary. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha1 kind: TunnelBinding metadata: name: postgres subjects: - kind: Service # Default name: postgres spec: fqdn: db.example.com protocol: tcp tunnelRef: kind: Tunnel # Or ClusterTunnel name: k3s-tunnel ``` -------------------------------- ### Verify ClusterTunnel Resources Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Apply the `ClusterTunnel` YAML and verify the creation of the tunnel and its associated Kubernetes resources like Deployments, ConfigMaps, and Secrets. ```bash kubectl apply -f cluster-tunnel.yaml # Verify tunnel and its managed resources kubectl get clustertunnel # NAME TUNNELID # k3s-cluster-tunnel 550e8400-e29b-41d4-a716-446655440000 kubectl get deployment k3s-cluster-tunnel -n cloudflare-operator-system # NAME READY UP-TO-DATE AVAILABLE AGE # k3s-cluster-tunnel 2/2 2 2 1m kubectl get configmap k3s-cluster-tunnel -n cloudflare-operator-system # NAME DATA AGE # k3s-cluster-tunnel 1 1m ``` -------------------------------- ### Verify Secret Creation Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Confirms that the Cloudflare API credentials secret has been successfully created and is available in the specified namespace. ```bash kubectl apply -f secret.yaml kubectl get secret cloudflare-secrets -n cloudflare-operator-system ``` -------------------------------- ### Create AccessTunnel in Client Cluster Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/configuration/access-tunnel.md Create an AccessTunnel object in the client cluster to connect to the exposed service. Specify the target FQDN and optionally the protocol and service port. A serviceToken can be provided if the tunnel is secured with Cloudflare Access. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha1 kind: AccessTunnel metadata: name: postgres target: fqdn: db.example.com # From the tunnel protocol: tcp # Optional svc: port: 5432 # serviceToken: # Optional, needed if tunnel is secured using Cloudflare Access # secretRef: nameOfSecret # CLOUDFLARE_ACCESS_SERVICE_TOKEN_ID: CLOUDFLARE_ACCESS_SERVICE_TOKEN_ID # Optional to remap keys in secret # CLOUDFLARE_ACCESS_SERVICE_TOKEN_TOKEN: CLOUDFLARE_ACCESS_SERVICE_TOKEN_TOKEN # Optional to remap keys in secret ``` -------------------------------- ### Create Local Proxy for Remote Service with AccessTunnel Source: https://context7.com/adyanth/cloudflare-operator/llms.txt In the client cluster, use AccessTunnel to create a local Kubernetes Service that proxies TCP traffic through Cloudflare to a remote service. ```yaml # --- CLIENT CLUSTER: create a local proxy to the remote postgres --- apiVersion: networking.cfargotunnel.com/v1alpha1 kind: AccessTunnel metadata: name: postgres namespace: app-ns target: fqdn: db.example.com # Matches the server-side TunnelBinding fqdn protocol: tcp svc: port: 5432 # Local port exposed as a Kubernetes Service # serviceToken: # Uncomment if the tunnel is secured with Cloudflare Access ZTNA # secretRef: cf-access-token-secret # CLOUDFLARE_ACCESS_SERVICE_TOKEN_ID: CLOUDFLARE_ACCESS_SERVICE_TOKEN_ID # CLOUDFLARE_ACCESS_SERVICE_TOKEN_TOKEN: CLOUDFLARE_ACCESS_SERVICE_TOKEN_TOKEN ``` -------------------------------- ### Verify ClusterTunnel Resource Creation Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/tunnel-simple/README.md Check if the ClusterTunnel resource was successfully created and if a corresponding configmap and deployment have been generated. This confirms the tunnel is active and ready. ```bash kubectl get clustertunnel kubectl get tunnel -n cloudflare-operator-system # should return something like # NAME TUNNELID # k3s-cluster-tunnel ``` ```bash kubectl get configmap k3s-cluster-tunnel -n cloudflare-operator-system # should return something like # NAME DATA AGE # k3s-cluster-tunnel 1 5m ``` ```bash kubectl get deployment k3s-cluster-tunnel -n cloudflare-operator-system # should return something like # NAME READY UP-TO-DATE AVAILABLE AGE # k3s-cluster-tunnel 1/1 1 1 5m ``` -------------------------------- ### Expose Server Cluster Service with TunnelBinding Source: https://context7.com/adyanth/cloudflare-operator/llms.txt In the server cluster, use TunnelBinding with `protocol: tcp` to expose a TCP service (e.g., PostgreSQL) over a Cloudflare tunnel. ```yaml # --- SERVER CLUSTER: expose postgres over Cloudflare tunnel --- apiVersion: networking.cfargotunnel.com/v1alpha1 kind: TunnelBinding metadata: name: postgres-tunnel namespace: database-ns subjects: - name: postgres # Must be an existing Service named "postgres" spec: fqdn: db.example.com protocol: tcp # Required – HTTP will not work for TCP access tunnelRef: kind: ClusterTunnel name: k3s-cluster-tunnel ``` -------------------------------- ### Deploy Tunnel or ClusterTunnel Manifest Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/tunnel-simple/README.md Apply either the tunnel.yaml or cluster-tunnel.yaml manifest to deploy your Cloudflare Tunnel or ClusterTunnel resource. Ensure all placeholder values in the manifest are replaced before deployment. ```bash # execute one of these, not both kubectl apply -f manifests/tunnel.yaml kubectl apply -f manifests/cluster-tunnel.yaml ``` -------------------------------- ### InsertOrUpdateCName: Create or Update Proxied CNAME Record Source: https://context7.com/adyanth/cloudflare-operator/llms.txt This Go function creates a new proxied CNAME record if `dnsId` is empty, or updates an existing record if `dnsId` is provided. It's used to point a custom domain to a Cloudflare Tunnel. ```go // Signature func (c *API) InsertOrUpdateCName(fqdn, dnsId string) (newDnsId string, err error) // Example: create CNAME for app.example.com → .cfargotunnel.com cfAPI := &cf.API{ Log: logger, Domain: "example.com", ValidZoneId: "zone-abc123", ValidTunnelId: "550e8400-e29b-41d4-a716-446655440000", ValidTunnelName: "k3s-prod-tunnel", CloudflareClient: cfClient, } // Create (dnsId == "" means insert) newId, err := cfAPI.InsertOrUpdateCName("app.example.com", "") if err != nil { log.Fatalf("failed to create CNAME: %v", err) } // newId = "dns-record-id-xyz" // Update existing record (provide existing dnsId to update in-place) _, err = cfAPI.InsertOrUpdateCName("app.example.com", "dns-record-id-xyz") ``` -------------------------------- ### Create Cloudflare API Token Secret (Declarative) Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/operator-authentication/README.md This snippet shows how to apply a Kubernetes secret manifest for your Cloudflare API token. Ensure you replace `` in `manifests/secret.yaml` with your actual token before applying. ```yaml kubectl apply -f manifests/secret.yaml ``` -------------------------------- ### Define cert-manager Issuers and Certificates Source: https://github.com/adyanth/cloudflare-operator/wiki/Testing-Webhooks-locally Configure cert-manager with self-signed and CA issuers, and then define a Certificate resource for the webhook. This includes specifying DNS names and IP addresses for the certificate, and the secret name where the TLS key and certificate will be stored. ```yaml apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: selfsigned-issuer spec: selfSigned: {} --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: test-ca spec: isCA: true commonName: test-ca subject: organizations: - ACME Inc. organizationalUnits: - Widgets secretName: test-ca-secret privateKey: algorithm: ECDSA size: 256 issuerRef: name: selfsigned-issuer kind: Issuer group: cert-manager.io --- apiVersion: cert-manager.io/v1 kind: Issuer metadata: name: test-ca-issuer spec: ca: secretName: test-ca-secret --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: webhook spec: secretName: webhook-tls isCA: false usages: - server auth - client auth dnsNames: - "webhook.test.svc.cluster.local" - "*" ipAddresses: - "100.120.130.56" issuerRef: name: test-ca-issuer ``` -------------------------------- ### Configure TunnelBinding for Wildcard Domains Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Use TunnelBinding with `disableDNSUpdates: true` to manage wildcard CNAME records manually in the Cloudflare dashboard. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha1 kind: TunnelBinding metadata: name: wildcard-ingress subjects: - name: ingress-nginx-svc spec: fqdn: "*.example.com" target: https://ingress-nginx-controller.ingress-nginx.svc.cluster.local:443 noTlsVerify: true tunnelRef: kind: ClusterTunnel name: k3s-cluster-tunnel disableDNSUpdates: true # Manage wildcard CNAME manually in Cloudflare dashboard ``` -------------------------------- ### Create Cloudflare API Token Secret (Declarative) Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Defines a Kubernetes Secret resource in YAML format for storing Cloudflare API credentials. This method is suitable for GitOps workflows. ```yaml apiVersion: v1 kind: Secret metadata: name: cloudflare-secrets namespace: cloudflare-operator-system type: Opaque stringData: CLOUDFLARE_API_TOKEN: "" # Optional: needed only for deleting new tunnels or as an alternate to API token # CLOUDFLARE_API_KEY: "" # CLOUDFLARE_API_EMAIL: "admin@example.com" ``` -------------------------------- ### Tunnel Deletion Lifecycle Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Explains the correct order for deleting `Tunnel` or `ClusterTunnel` resources to avoid orphaned tunnels on Cloudflare, emphasizing the use of Kubernetes finalizers. ```APIDOC ## Tunnel deletion lifecycle ### Correct deletion order When deleting a `Tunnel` or `ClusterTunnel` that was created with `newTunnel`, the operator uses a Kubernetes finalizer (`cfargotunnel.com/finalizer`) to ensure the `cloudflared` Deployment is scaled to zero before the tunnel is deleted from the Cloudflare API. Deleting the credential Secret **before** the tunnel will leave the tunnel orphaned on Cloudflare. ```bash # CORRECT order – delete Tunnel first, then Secret kubectl delete tunnel my-app-tunnel -n my-app-ns # Watch until the finalizer is removed and the resource is fully gone kubectl wait --for=delete tunnel/my-app-tunnel -n my-app-ns --timeout=60s # Only then delete the credential secret kubectl delete secret cloudflare-secrets -n my-app-ns # WRONG – this prevents operator from cleaning up on Cloudflare: # kubectl delete secret cloudflare-secrets ← DO NOT do this first # kubectl delete tunnel my-app-tunnel ``` ``` -------------------------------- ### Correct Tunnel Deletion Order Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Ensures proper cleanup by deleting the Tunnel resource before its associated credential Secret, leveraging a Kubernetes finalizer to scale down the cloudflared Deployment. ```bash # CORRECT order – delete Tunnel first, then Secret kubectl delete tunnel my-app-tunnel -n my-app-ns # Watch until the finalizer is removed and the resource is fully gone kubectl wait --for=delete tunnel/my-app-tunnel -n my-app-ns --timeout=60s # Only then delete the credential secret kubectl delete secret cloudflare-secrets -n my-app-ns # WRONG – this prevents operator from cleaning up on Cloudflare: # kubectl delete secret cloudflare-secrets ← DO NOT do this first # kubectl delete tunnel my-app-tunnel ``` -------------------------------- ### Deploy Tunnel Binding Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/tunnel-binding-simple/README.md Apply the Kubernetes manifest for the tunnel binding resource. This configures cloudflared to manage the routing for your applications. ```bash kubectl apply -f manifests/cloudflare-operator/tunnel-binding.yaml ``` -------------------------------- ### InsertOrUpdateCName Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Upserts a proxied CNAME DNS record pointing to `.cfargotunnel.com`. This function is called automatically by the `TunnelBindingReconciler`. ```APIDOC ### `InsertOrUpdateCName` — upsert a proxied CNAME record Creates or updates a proxied CNAME DNS record pointing to `.cfargotunnel.com`. Called automatically by the `TunnelBindingReconciler`; shown here for reference. ```go // Signature func (c *API) InsertOrUpdateCName(fqdn, dnsId string) (newDnsId string, err error) // Example: create CNAME for app.example.com → .cfargotunnel.com cfAPI := &cf.API{ Log: logger, Domain: "example.com", ValidZoneId: "zone-abc123", ValidTunnelId: "550e8400-e29b-41d4-a716-446655440000", ValidTunnelName: "k3s-prod-tunnel", CloudflareClient: cfClient, } // Create (dnsId == "" means insert) newId, err := cfAPI.InsertOrUpdateCName("app.example.com", "") if err != nil { log.Fatalf("failed to create CNAME: %v", err) } // newId = "dns-record-id-xyz" // Update existing record (provide existing dnsId to update in-place) _, err = cfAPI.InsertOrUpdateCName("app.example.com", "dns-record-id-xyz") ``` ``` -------------------------------- ### Verify Cloudflare DNS Records Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Confirm that the TunnelBinding has created the necessary CNAME and TXT DNS records in Cloudflare for your services. ```text # Verify DNS records were created in Cloudflare – a proxied CNAME and a companion TXT # CNAME app.example.com → 550e8400-e29b-41d4-a716-446655440000.cfargotunnel.com (proxied) # TXT _managed.app.example.com → {"DnsId":"...","TunnelId":"...","TunnelName":"..."} ``` -------------------------------- ### GetManagedDnsTxt: Read Tunnel Ownership TXT Record Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Retrieves the `_managed.` TXT record to verify if the current tunnel instance is authorized to manage the DNS record, preventing conflicts. Returns a boolean `canUse` indicating if management is permitted. ```go // Returns: txtId, DnsManagedRecordTxt{DnsId, TunnelName, TunnelId}, canUse bool, error txtId, record, canUse, err := cfAPI.GetManagedDnsTxt("app.example.com") if err != nil { log.Fatalf("error reading TXT: %v", err) } if !canUse { // FQDN is managed by a different tunnel log.Printf("FQDN owned by tunnel %s (%s)", record.TunnelName, record.TunnelId) return } // txtId == "" means no TXT record yet – safe to create fmt.Printf("Existing CNAME id in TXT: %s\n", record.DnsId) ``` -------------------------------- ### Access Remote Service via Local Kubernetes Service Source: https://context7.com/adyanth/cloudflare-operator/llms.txt After applying AccessTunnel, applications in the client cluster can connect to the specified local service name and port, which is transparently proxied. ```bash # After applying both resources, applications in app-ns can connect to: # postgres.app-ns.svc.cluster.local:5432 # which transparently proxies through Cloudflare to the server cluster's postgres. kubectl get accesstunnel postgres -n app-ns # NAME AGE # postgres 2m # Application connection string (no changes needed to the app) # DATABASE_URL=postgres://user:pass@postgres.app-ns.svc:5432/mydb ``` -------------------------------- ### Extract and Save TLS Certificates Source: https://github.com/adyanth/cloudflare-operator/wiki/Testing-Webhooks-locally Retrieve the generated CA certificate, webhook TLS certificate, and private key from Kubernetes secrets and save them to the specified local directory. This step is crucial for configuring the webhook server to use TLS. ```bash kubectl get secrets test-ca-secret -o yaml | yq .data | yq '."tls.crt"' | base64 -d > /path/to/dev/certs/ca.crt kubectl get secrets webhook-tls -o yaml | yq .data | yq '."tls.crt"' | base64 -d > /path/to/dev/certs/tls.crt kubectl get secrets webhook-tls -o yaml | yq .data | yq '."tls.key"' | base64 -d > /path/to/dev/certs/tls.key ``` -------------------------------- ### Apply kubectl Patches with deployPatch in v1alpha2 CRDs Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/migrations/crd/v1alpha2.md Use the 'deployPatch' field to apply kubectl-style JSON/YAML patches to the cloudflared deployment. This allows for customization of replicas, tolerations, node selectors, and image without updating the CRD itself. Note the specific container name 'cloudflared' required for patching. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha2 kind: ClusterTunnel ... spec: deployPatch: | spec: replicas: 2 # instead of size template: spec: tolerations: ... # tolerations as before nodeSelector: ... # nodeSelectors as before. Note the plural v/s singular containers: - name: cloudflared # Note the usage of cloudflared name to select the container to patch image: ... # image as before ... ``` -------------------------------- ### Bind Kubernetes Services to a Tunnel with TunnelBinding Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Use TunnelBinding to map Kubernetes Services to a Tunnel or ClusterTunnel. The controller automatically configures cloudflared, DNS records, and handles cleanup. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha1 kind: TunnelBinding metadata: name: my-services-binding namespace: my-app-ns # Must match Tunnel namespace (or any ns for ClusterTunnel) subjects: - name: frontend-svc # Name of the Kubernetes Service in this namespace spec: fqdn: app.example.com # Cloudflare DNS CNAME target (optional – defaults to .) protocol: http # http | https | tcp | udp | ssh | rdp | smb (auto-detected if omitted) - name: api-svc spec: fqdn: api.example.com protocol: https noTlsVerify: true # Skip TLS verification for this backend - name: db-svc spec: fqdn: db.example.com protocol: tcp target: tcp://db-svc.my-app-ns.svc:5432 # Override computed target - name: metrics-svc # FQDN auto-generated as metrics-svc.example.com tunnelRef: kind: Tunnel # Tunnel | ClusterTunnel name: my-app-tunnel disableDNSUpdates: false # Set true to manage CNAME manually (e.g. wildcard DNS) ``` -------------------------------- ### TunnelSpec Configuration Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Reference for the `TunnelSpec` fields used by both `Tunnel` and `ClusterTunnel` resources, including required fields, optional settings, and defaults. ```APIDOC ## `TunnelSpec` — shared Tunnel / ClusterTunnel configuration fields ### Full `TunnelSpec` reference Both `Tunnel` and `ClusterTunnel` share the same `TunnelSpec`. Key fields with their defaults are listed below. ```yaml # Full annotated TunnelSpec (api/v1alpha2/tunnel_types.go) spec: cloudflare: domain: "example.com" # Required secret: "cloudflare-secrets" # Required – K8s Secret name accountId: "" # Preferred over accountName accountName: "My Cloudflare Account" # Fallback if accountId unset email: "admin@example.com" # Only needed with API Key CLOUDFLARE_API_TOKEN: CLOUDFLARE_API_TOKEN # Default key name in secret CLOUDFLARE_API_KEY: CLOUDFLARE_API_KEY # Default key name in secret CLOUDFLARE_TUNNEL_CREDENTIAL_FILE: CLOUDFLARE_TUNNEL_CREDENTIAL_FILE # For existingTunnel CLOUDFLARE_TUNNEL_CREDENTIAL_SECRET: CLOUDFLARE_TUNNEL_CREDENTIAL_SECRET newTunnel: # Mutually exclusive with existingTunnel name: "my-new-tunnel" existingTunnel: # Mutually exclusive with newTunnel id: "550e8400-..." name: "my-tunnel-name" # Fallback if id is invalid protocol: "auto" # auto (default) | quic | http2 fallbackTarget: "http_status:404" # Default: http_status:404 noTlsVerify: false # Global TLS skip for all backends originCaPool: "homelab-ca" # Secret with tls.crt to trust as root CA # Strategic-merge patch applied to the cloudflared Deployment # Useful for pinning image version, setting replicas, resource limits, etc. deployPatch: | spec: replicas: 3 template: spec: containers: - name: cloudflared image: cloudflare/cloudflared:2025.4.0 resources: requests: cpu: 100m memory: 64Mi limits: cpu: 500m memory: 256Mi ``` ``` -------------------------------- ### Override Operator Flags in Deployment Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Patches the operator's Deployment manifest to override default command-line flags. This allows customization of cluster-scoped resource namespaces, DNS overwrite behavior, leader election, and metrics binding. ```yaml apiVersion: apps/v1 kind: Deployment metadata: name: cloudflare-operator-controller-manager namespace: cloudflare-operator-system spec: template: spec: containers: - name: manager args: - --cluster-resource-namespace=cloudflare-operator-system - --overwrite-unmanaged-dns=true - --leader-elect=true - --metrics-bind-address=:8443 ``` -------------------------------- ### Tunnel Binding for Authelia Service Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/tunnel-binding-with-reverse-proxy/README.md Bind a Cloudflare tunnel to the authelia service. This configuration maps the FQDN `authelia.` to the authelia service within the cluster. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha1 kind: TunnelBinding metadata: name: authelia namespace: authelia subjects: - name: authelia # the fqdn authelia. maps to this service tunnelRef: kind: ClusterTunnel name: example-tunnel ``` -------------------------------- ### Tunnel Binding for Wildcard Domain with Ingress Nginx Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/examples/tunnel-binding-with-reverse-proxy/README.md Bind a Cloudflare tunnel to handle wildcard domain traffic, directing it to ingress-nginx. The `zz-` prefix ensures this route is processed last by cloudflared, preventing conflicts with more specific routes like authelia. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha1 kind: TunnelBinding metadata: # At this time cloudflare-operator does not deterministically sort wildcards to the end of cloudflared's config file. # This is important because cloudflared goes through the list in order, and this ingress being before the authelia ingress # would mean authelia is impossible to reach. # By prefixing with `zz-` we ensure this is the last route in the cloudflared config file name: zz-ingress-nginx subjects: - name: wildcard spec: fqdn: "*." target: https://ingress-nginx-controller.ingress-nginx.svc.cluster.local:443 tunnelRef: kind: ClusterTunnel name: example-tunnel ``` -------------------------------- ### TunnelSpec Configuration Fields Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Defines the configuration parameters for both Tunnel and ClusterTunnel resources, including Cloudflare API credentials, tunnel creation methods (new or existing), protocol settings, and deployment patching. ```yaml # Full annotated TunnelSpec (api/v1alpha2/tunnel_types.go) spec: cloudflare: domain: "example.com" # Required secret: "cloudflare-secrets" # Required – K8s Secret name accountId: "" # Preferred over accountName accountName: "My Cloudflare Account" # Fallback if accountId unset email: "admin@example.com" # Only needed with API Key CLOUDFLARE_API_TOKEN: CLOUDFLARE_API_TOKEN # Default key name in secret CLOUDFLARE_API_KEY: CLOUDFLARE_API_KEY # Default key name in secret CLOUDFLARE_TUNNEL_CREDENTIAL_FILE: CLOUDFLARE_TUNNEL_CREDENTIAL_FILE # For existingTunnel CLOUDFLARE_TUNNEL_CREDENTIAL_SECRET: CLOUDFLARE_TUNNEL_CREDENTIAL_SECRET newTunnel: # Mutually exclusive with existingTunnel name: "my-new-tunnel" existingTunnel: # Mutually exclusive with newTunnel id: "550e8400-..." name: "my-tunnel-name" # Fallback if id is invalid protocol: "auto" # auto (default) | quic | http2 fallbackTarget: "http_status:404" # Default: http_status:404 noTlsVerify: false # Global TLS skip for all backends originCaPool: "homelab-ca" # Secret with tls.crt to trust as root CA # Strategic-merge patch applied to the cloudflared Deployment # Useful for pinning image version, setting replicas, resource limits, etc. deployPatch: | spec: replicas: 3 template: spec: containers: - name: cloudflared image: cloudflare/cloudflared:2025.4.0 resources: requests: cpu: 100m memory: 64Mi limits: cpu: 500m memory: 256Mi ``` -------------------------------- ### TunnelBinding Resource Configuration Source: https://github.com/adyanth/cloudflare-operator/blob/main/docs/configuration/tunnel-binding.md Defines a TunnelBinding resource to configure endpoints for services. It specifies subjects (target services) and tunnelRef (tunnel details). The disableDNSUpdates field can prevent automatic DNS record updates. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha1 kind: TunnelBinding metadata: name: svc-binding subjects: - kind: Service # Default name: svc01 spec: fqdn: mysvc.example.com protocol: http target: http://svc01.ns.svc.cluster.local:8080 caPool: custom.crt noTlsVerify: false - name: svc02 # Points to the second service tunnelRef: kind: Tunnel # Or ClusterTunnel name: k3s-tunnel disableDNSUpdates: false ``` -------------------------------- ### Adopt Existing Namespace-Scoped Cloudflare Tunnel Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Use `Tunnel` to adopt an existing Cloudflare tunnel by its ID or name within a specific namespace. This is a namespace-scoped resource. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha2 kind: Tunnel metadata: name: my-app-tunnel namespace: my-app-ns spec: cloudflare: accountId: "abcdef1234567890abcdef1234567890" domain: "example.com" secret: cloudflare-secrets CLOUDFLARE_TUNNEL_CREDENTIAL_FILE: CLOUDFLARE_TUNNEL_CREDENTIAL_FILE existingTunnel: id: "550e8400-e29b-41d4-a716-446655440000" fallbackTarget: "http_status:404" originCaPool: homelab-ca ``` -------------------------------- ### GetManagedDnsTxt Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Reads the `_managed.` TXT record to determine which tunnel owns a CNAME, preventing conflicts. Returns the TXT record ID, its content, and a boolean indicating if it's safe to use. ```APIDOC ### `GetManagedDnsTxt` — read ownership TXT record Returns the `_managed.` TXT record used to track which tunnel owns a CNAME, preventing conflicts between multiple operator instances or external DNS managers. ```go // Returns: txtId, DnsManagedRecordTxt{DnsId, TunnelName, TunnelId}, canUse bool, error txtId, record, canUse, err := cfAPI.GetManagedDnsTxt("app.example.com") if err != nil { log.Fatalf("error reading TXT: %v", err) } if !canUse { // FQDN is managed by a different tunnel log.Printf("FQDN owned by tunnel %s (%s)", record.TunnelName, record.TunnelId) return } // txtId == "" means no TXT record yet – safe to create fmt.Printf("Existing CNAME id in TXT: %s\n", record.DnsId) ``` ``` -------------------------------- ### Create Cluster-Scoped Cloudflare Tunnel Source: https://context7.com/adyanth/cloudflare-operator/llms.txt Use `ClusterTunnel` to provision a new tunnel on Cloudflare or adopt an existing one. This resource is cluster-scoped and manages `cloudflared` deployments, ConfigMaps, and Secrets. ```yaml apiVersion: networking.cfargotunnel.com/v1alpha2 kind: ClusterTunnel metadata: name: k3s-cluster-tunnel spec: cloudflare: accountId: "abcdef1234567890abcdef1234567890" domain: "example.com" secret: cloudflare-secrets CLOUDFLARE_API_TOKEN: CLOUDFLARE_API_TOKEN newTunnel: name: k3s-prod-tunnel fallbackTarget: "http_status:404" noTlsVerify: false protocol: auto deployPatch: | spec: replicas: 2 template: spec: containers: - name: cloudflared image: cloudflare/cloudflared:2025.4.0 ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.