Skip to content

Latest commit

 

History

58 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

KFlared

KFlared manages Cloudflare Tunnels for two paths: it publishes selected Kubernetes Gateway API hostnames through controller-owned tunnels, and it can route a Service ClusterIP privately to enrolled Cloudflare One clients. Workloads and the Traefik data plane remain on private Kubernetes networking.

The first release is deliberately an integration controller, not a Gateway API implementation:

Cloudflare edge
  -> remotely managed Cloudflare Tunnel
  -> official cloudflared connector Deployment
  -> internal Traefik Service
  -> Traefik Gateway and HTTPRoutes
  -> workload Services

See the KFlared documentation, architecture, and security model before operating the controller.

MVP scope

  • Traefik 3.7.10+ using GatewayClass controller traefik.io/gateway-controller
  • Gateway API v1.6.1 Gateway and HTTPRoute
  • one CloudflareTunnelBinding, one existing Gateway, and one remotely managed tunnel
  • one named HTTP listener and concrete, non-wildcard HTTPRoute hostnames
  • binding, Gateway, and HTTPRoutes in the application namespace; an origin Service may be cross-namespace with a matching ReferenceGrant
  • official cloudflare/cloudflared:2026.8.3, one or more replicas (one by default)
  • per-binding ExternalDNS DNSEndpoint automation, enabled by default, with an explicit opt-out for externally managed DNS targets

GRPCRoute, wildcard hostnames, HTTPS origins, direct Service backend routing, shared/imported tunnels, externally managed connectors, and native GatewayClass ownership are deferred.

CloudflareTunnelPrivateRoute is a separate API for private CIDR routing. It targets a referenced Service's current ClusterIP as a single /32 route and does not configure public hostnames or HTTPRoutes. See the private route configuration and architecture. Cloudflare One client enrollment/access policy and the user's Kubernetes credentials remain external prerequisites.

Prerequisites

  • Kubernetes 1.35-1.37
  • Gateway API v1.6.1 CRDs
  • Traefik 3.7.10+ with Kubernetes Gateway provider enabled
  • a normal internal ClusterIP Traefik origin Service (dedicated single-port Service recommended)
  • a Cloudflare account API token with only Cloudflare Tunnel/Connector write access required for the selected account
  • optional ExternalDNS and its externaldns.k8s.io/v1alpha1 DNSEndpoint CRD
  • optional External Secrets Operator and a ClusterSecretStore when using the chart's ExternalSecret integration

The token does not need DNS edit permission. Put it only in kflared; the controller has no cluster-wide Secret permission:

kubectl -n kflared create secret generic cloudflare-api-token \
  --from-literal=api-token='<CLOUDFLARE_API_TOKEN>'

Helm users can instead opt in to the chart's externalSecrets values. The chart then creates an ExternalSecret targeting cloudflare-api-token; External Secrets Operator and the referenced ClusterSecretStore must already exist.

Install and configure

Kustomize is the canonical manifest source. Install prerequisites first, then build and deploy an image:

Project automation requires Go and PowerShell 7 or newer. Task is pinned in the isolated tools/task module and does not need to be installed globally. Run tasks through the repository-local wrapper, for example ./task.ps1 --list (PowerShell: .\\task.ps1 --list).

./task.ps1 docker-build docker-push IMG=<registry>/kflared:<tag>
./task.ps1 install
./task.ps1 deploy IMG=<registry>/kflared:<tag>

The checked-in Kustomize manager manifest and Helm chart default to the kflared controller class. Set spec.controller: kflared on each provider and binding it owns. Override the chart's controllerClass when running multiple KFlared installations in one cluster, and use the matching value in their resources.

Create a provider, label an allowed tenant namespace, and create a binding. Adapt the examples under config/samples to the actual account ID, DNS zones, Gateway, listener, and Traefik Service.

The binding keeps its Gateway and eligible HTTPRoutes in its application namespace. Its originServiceRef may point to a Service in another namespace if that Service namespace contains a matching Gateway API ReferenceGrant. KFlared checks this custom reference during reconciliation; Kubernetes does not automatically enforce it, and the grant does not provide network access. Revoking authorization deprograms the tunnel and removes managed DNS. Since grants cannot authorize a particular port, use a dedicated single-port ClusterIP origin Service. Origin selection is per binding, allowing one provider to serve multiple origins, multiple providers to share an origin, and incremental blue-green migration.

kubectl apply -f config/samples/kflared_v1alpha1_clustercloudflareprovider.yaml
kubectl label namespace my-app kflared.kodeblox.com/cloudflare-provider=default
kubectl -n my-app apply -f config/samples/kflared_v1alpha1_cloudflaretunnelbinding.yaml

Inspect conditions and DNS requirements:

kubectl get clustercloudflareproviders
kubectl -n my-app get cloudflaretunnelbindings -o yaml

spec.dnsAutomationEnabled defaults to true. When enabled but the DNSEndpoint CRD is unavailable, status.dnsRecords is authoritative and DNSAutomationReady=False reports ManualConfigurationRequired. The MVP intentionally cannot acknowledge or verify manually managed DNS, so Ready remains false. Set the field to false when the public hostname must retain another DNS target; KFlared then removes any binding-owned DNSEndpoint, does not prescribe replacement CNAMEs, and treats the intentional opt-out as ready.

Development

The module targets Go 1.27.0, controller-runtime v0.25.0, Gateway API v1.6.1, and cloudflare-go/v7 v7.8.0. On this workstation, invoke the existing versioned executable and do not alter the default Go installation:

go1.27.0 test ./...
go1.27.0 build ./cmd

Generated code and manifests remain Kubebuilder-controlled. Use the pinned controller-gen version from Taskfile.yaml and verify the resulting diff. See testing.

The supported Helm chart is rooted at charts. It combines the current Helm starter structure with the controller resources derived from Kubebuilder's Kustomize output. Its plain CRDs live in Helm's special charts/crds/ directory; pass --include-crds when rendering the complete chart.

License

Apache License 2.0. See LICENSE.

About

Gateway controller for Cloudflare Tunnel

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages