Cloudflare Tunnel Ingress Controller

repository·master·Indexed 22 days ago

https://github.com/strrl/cloudflare-tunnel-ingress-controller

A Kubernetes ingress controller that simplifies exposing services to the internet by leveraging Cloudflare Tunnels. It translates standard Kubernetes Ingress resources using the cloudflare-tunnel ingress class into Cloudflare tunnel routing rules and DNS records without requiring additional Custom Resource Definitions (CRDs).

Tokens
18.6K
Snippets
43
Records
64
Agent score
77%

What's inside cloudflare-tunnel-ingress-controller

  1. Key capabilities of the controller

    master

    The controller provides several core automation and observability features:

    • Native Ingress Support: Uses existing Kubernetes Ingress resources without requiring new custom API types.
    • Automated Lifecycle: Handles tunnel creation, DNS record management, and the rollout of cloudflared connectors.
    • Flexible Credential Management: Supports credentials via a bundled cloudflare-api secret or a custom secret reference.
    • Observability: Allows monitoring of controller activity through Kubernetes events, logs, and metrics endpoints.
  2. What is the Cloudflare Tunnel Ingress Controller?

    master

    The Cloudflare Tunnel Ingress Controller automates the exposure of Kubernetes services through Cloudflare Tunnels using native Kubernetes Ingress resources.

    Instead of using custom resource definitions (CRDs), the controller watches for standard Ingress objects assigned to the cloudflare-tunnel IngressClass. When a matching Ingress is detected, the controller automatically:

    1. Provisions Cloudflare Argo tunnels.
    2. Starts cloudflared connectors.
    3. Manages DNS records to route traffic securely to the target Kubernetes Service.
  3. How cloudflared connectors are managed

    master

    The controller uses a component called ControlledCloudflaredConnector to ensure cloudflared processes are running and correctly configured.

    Reconciliation Loop

    Once the controller instance is elected leader, the ControlledCloudflaredConnector runs a reconciliation loop every 10 seconds. This loop:

    1. Fetches the tunnel token.
    2. Compares the existing Kubernetes Secret and Deployment against the desired configuration.
    3. Creates the connector resources if they are missing.
    4. Updates existing resources if they drift (e.g., changes to the container image, replica count, command, token Secret version, or pod customization).

    This 10-second loop makes the connector Deployment self-healing, ensuring that even if the Deployment is modified independently of an Ingress event, it will be reverted to the desired state.

  4. How the Cloudflare Tunnel Ingress Controller works

    master

    The Cloudflare Tunnel Ingress Controller acts as a bridge between the Kubernetes control plane and the Cloudflare control plane. It continuously translates Kubernetes Ingress resources into Cloudflare tunnel routing rules and DNS records.

    Key Concepts

    • Control Plane vs. Data Plane: The controller manages configuration (the control plane) but does not sit in the path of network traffic. Traffic flows from the Cloudflare edge through cloudflared connectors directly to your Kubernetes Services.
    • Exposure: This is the internal abstraction used by the controller. An Exposure represents the mapping of a public hostname and path prefix to a specific Kubernetes Service and its origin options.
    • Tunnel Rules: The controller converts all active Exposures into a single, ordered list of ingress rules for the Cloudflare tunnel. Rules are ordered by specificity: specific hostnames are placed before wildcards, and longer paths are placed before shorter paths, ending with a final HTTP 404 rule.
    • cloudflared Connectors: These are the actual processes (running in Kubernetes Deployments) that maintain outbound connections to Cloudflare and forward incoming traffic to your Services.
  5. How DNS management and ownership work

    master

    The controller manages DNS records to route public traffic to your tunnel. It uses a combination of CNAME and TXT records to ensure safe management and cleanup.

    DNS Components

    • CNAME Record: A proxied CNAME (e.g., app.example.com) that points to your tunnel domain (<tunnel-id>.cfargotunnel.com).
    • Ownership TXT Record: A record (e.g., _ctic_managed.app.example.com) used to prove that the controller and the tunnel own the CNAME. This provides a safety boundary during reconciliation to prevent the accidental deletion of CNAMEs that might have been repointed by external systems.

    Disabling DNS Management

    If you set disable-dns-management: "true" in your configuration:

    • The controller will still create tunnel ingress rules for your Exposures.
    • The controller will not create or update DNS records.
    • The controller permits hostnames that are outside of its visible Cloudflare zones.
    • When relinquishing previously managed records, the controller will only remove a CNAME if it still points to the managed tunnel, preserving any CNAMEs that an external system has already repointed.
  6. How the Cloudflare Tunnel Ingress Controller works

    master
    The controller enables publishing Kubernetes services through Cloudflare Tunnel using standard Kubernetes Ingress resources. It uses the cloudflare-tunnel ingress class to identify which ingress resources it should manage. Because it leverages standard Kubernetes objects, no additional Custom Resource Definitions (CRDs) are required to use it.
  7. How wildcard hostnames are matched

    master

    The controller supports wildcard hostnames (e.g., *.example.com) and automatically creates the corresponding wildcard DNS records. The controller sorts rules deterministically so that routing follows these precedence rules:

    1. Exact match vs. Wildcard: An exact hostname always takes precedence over a wildcard (e.g., app.example.com matches an exact rule before falling back to *.example.com).
    2. Specificity: A more specific wildcard wins over a broader one (e.g., *.internal.example.com is matched before *.example.com).
    3. Path Length: For the same hostname, rules with longer paths are matched first.

    Note: The order of rules within your Ingress manifest does not affect matching, as the controller re-sorts them before applying the tunnel configuration.

  8. Understand uninstall behavior and resource cleanup

    master

    When you uninstall the Helm release, the following cleanup behavior occurs:

    Automatically Cleaned Up

    • Connector Deployment: The managed cloudflared deployment is removed via Kubernetes garbage collection (due to owner references).
    • Tunnel Token Secret: The secret created by the controller is removed.

    Resources That Persist (Manual Cleanup Required)

    • Cloudflare Tunnel: The tunnel itself is an external resource and is not deleted. To reuse it, reinstall with the same cloudflare.tunnelName. To remove it, use the Cloudflare dashboard or API.
    • DNS Records: The controller cleans up DNS records when an Ingress resource is deleted. If you uninstall the controller while Ingress resources still exist, those DNS records will remain. Recommendation: Delete your Ingress resources before uninstalling the controller.
  9. Quickstart: Install and configure the Cloudflare Tunnel Ingress Controller

    master

    Follow these steps to install the controller using Helm and expose a Kubernetes Service through a Cloudflare Tunnel using a standard Ingress resource.

    Prerequisites

    • Kubernetes Cluster: Version 1.26 or later with cluster-admin access.
    • Tools: kubectl and helm configured for your cluster.
    • Cloudflare Account: An active zone with Argo Tunnel access enabled.
    • Cloudflare API Token: Must have the following permissions:
      • Account.Cloudflare Tunnel:Edit
      • Zone.DNS:Edit
      • Zone.Zone:Read
    • Cloudflare Account ID.
    • Target Service: A service (e.g., kubernetes-dashboard in the kubernetes-dashboard namespace) that you want to expose.

    1. Install the controller

    Use Helm to install the controller and create the necessary credential Secret. Replace the placeholders with your actual API token, account ID, and desired tunnel name.

    helm upgrade --install --wait \
      cloudflare-tunnel-ingress-controller \
      cloudflare-tunnel-ingress-controller \
      --repo https://helm.strrl.dev \
      --namespace cloudflare-tunnel-ingress-controller \
      --create-namespace \
      --set cloudflare.apiToken="<CLOUDFLARE_API_TOKEN>" \
      --set cloudflare.accountId="<CLOUDFLARE_ACCOUNT_ID>" \
      --set cloudflare.tunnelName="<TUNNEL_NAME>"

    2. Create your first Ingress

    To expose a service, create an Ingress resource with the ingressClassName set to cloudflare-tunnel.

    1. Save the following manifest as dashboard-ingress.yaml.
    2. Replace dash.example.com with a hostname that exists in your Cloudflare zone.
    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: dashboard
      namespace: kubernetes-dashboard
    spec:
      ingressClassName: cloudflare-tunnel
      rules:
        - host: dash.example.com
          http:
            paths:
              - path: /
                pathType: Prefix
                backend:
                  service:
                    name: kubernetes-dashboard
                    port:
                      number: 80
    1. Apply the manifest:
    kubectl apply -f dashboard-ingress.yaml

    3. Verify the Ingress

    Access your service via https://<your-chosen-hostname>. If the service is configured correctly, it should load through the Cloudflare Tunnel.

    helm upgrade --install --wait \
      cloudflare-tunnel-ingress-controller \
      cloudflare-tunnel-ingress-controller \
      --repo https://helm.strrl.dev \
      --namespace cloudflare-tunnel-ingress-controller \
      --create-namespace \
      --set cloudflare.apiToken="<CLOUDFLARE_API_TOKEN>" \
      --set cloudflare.accountId="<CLOUDFLARE_ACCOUNT_ID>" \
      --set cloudflare.tunnelName="<TUNNEL_NAME>"
  10. Configure Cloudflare credentials via Helm

    master

    The controller requires a Kubernetes secret named cloudflare-api in its namespace. You can have the Helm chart create this secret automatically by providing the credentials as values during installation.

    Required values:

    • cloudflare.apiToken: The Cloudflare API token created with the required scopes.
    • cloudflare.accountId: The account identifier that owns the tunnel.
    • cloudflare.tunnelName: The friendly name for the tunnel to be created or reused.
    helm upgrade --install cloudflare-tunnel-ingress-controller \
      strrl.dev/cloudflare-tunnel-ingress-controller \
      --set cloudflare.apiToken="<CLOUDFLARE_API_TOKEN>" \
      --set cloudflare.accountId="<CLOUDFLARE_ACCOUNT_ID>" \
      --set cloudflare.tunnelName="<TUNNEL_NAME>"
  11. Scale and protect cloudflared replicas

    master

    To ensure tunnel connectivity availability, scale the cloudflared replicas and protect them from voluntary disruptions using a PodDisruptionBudget (PDB).

    When configuring the cloudflared section in your Helm values, you can specify replicaCount and a pdb block. In the pdb block, use either minAvailable or maxUnavailable, but do not use both.

    cloudflared:
      replicaCount: 2
      pdb:
        enabled: true
        minAvailable: 1