cfgate.iocfgate v0.2.0-alpha.11 · Release documentation
Gateway API Primer
Coming from Ingress?
Section titled “Coming from Ingress?”Gateway API is the Kubernetes successor to Ingress, providing a role-oriented, portable, and expressive API for service networking. If you are migrating from an Ingress-based Cloudflare operator (such as STRRL/cloudflare-tunnel-ingress-controller or adyanth/cloudflare-operator), this page explains the key concepts you need to understand.
Gateway API separates concerns by role: infrastructure providers define GatewayClasses, cluster operators create Gateways, and application developers attach Routes. This maps cleanly to cfgate’s architecture.
Key Concepts
Section titled “Key Concepts”GatewayClass
Section titled “GatewayClass”A GatewayClass defines which controller handles a class of Gateways. It is a cluster-scoped resource (not namespaced).
cfgate registers the controller name cfgate.io/cloudflare-tunnel-controller. You create a GatewayClass that references this controller name, and cfgate will handle all Gateways bound to that class.
Think of GatewayClass as the “driver”: it tells Kubernetes which software manages Gateways of this type.
apiVersion: gateway.networking.k8s.io/v1kind: GatewayClassmetadata: name: cfgatespec: controllerName: cfgate.io/cloudflare-tunnel-controllerYou only need one GatewayClass for cfgate. The controller accepts it automatically when spec.controllerName matches.
Gateway
Section titled “Gateway”A Gateway is a runtime instance bound to a GatewayClass. In cfgate, a Gateway represents a Cloudflare Tunnel endpoint.
The cfgate.io/tunnel-ref annotation connects the Gateway to a CloudflareTunnel resource. The GatewayClass tells Kubernetes that cfgate manages this Gateway; the Gateway itself is the runtime binding between the tunnel and the routes.
apiVersion: gateway.networking.k8s.io/v1kind: Gatewaymetadata: name: cloudflare-tunnel namespace: cfgate-system annotations: cfgate.io/tunnel-ref: cfgate-system/my-tunnelspec: gatewayClassName: cfgate listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: AllKey points:
gatewayClassName: cfgatebinds this Gateway to the cfgate GatewayClasscfgate.io/tunnel-reflinks to the CloudflareTunnel that provides the actual tunnelallowedRoutes.namespaces.from: Allpermits routes from any namespace to attach (default isSame, which restricts to the Gateway’s namespace)- The
portandprotocolfields satisfy the Gateway API spec but do not determine what cloudflared actually serves. Cloudflared routing is driven by the routes themselves
Routes (HTTPRoute)
Section titled “Routes (HTTPRoute)”Routes attach to Gateways via parentRefs and define routing rules. In cfgate, each route becomes one or more cloudflared ingress rules.
Current route support is HTTPRoute only.
apiVersion: gateway.networking.k8s.io/v1kind: HTTPRoutemetadata: name: my-app namespace: defaultspec: parentRefs: - name: cloudflare-tunnel namespace: cfgate-system hostnames: - app.example.com rules: - backendRefs: - name: my-service port: 80This creates a cloudflared ingress rule: app.example.com routes to http://my-service.default.svc.cluster.local:80.
Per-route behavior is configured via annotations on the Route resource. See Annotations Reference for the full list.
How cfgate Uses Gateway API
Section titled “How cfgate Uses Gateway API”The full chain from infrastructure to application routing:
flowchart LR HR["HTTPRoute (namespace: default) parentRefs: cf-tunnel hostnames: app.example.com backendRefs: my-svc:80"]
GW["Gateway (namespace: cfgate-system) gatewayClassName: cfgate tunnel-ref: .../tun listeners: http/80/All"]
GC["GatewayClass (cluster-scoped) controllerName: cfgate.io/cloudflare-tunnel-controller"]
CT["CloudflareTunnel (namespace: cfgate-system) spec.tunnel.name: tun spec.cloudflare: ..."]
HR -- parentRefs --> GW GW -- gatewayClassName --> GC GW -- tunnel-ref --> CT- GatewayClass tells Kubernetes that cfgate handles Gateways with
controllerName: cfgate.io/cloudflare-tunnel-controller. - Gateway creates the tunnel binding via the
cfgate.io/tunnel-refannotation. The controller sets the Gateway status toProgrammedwhen the tunnel is ready. - Routes define which hostnames and paths map to which backend services. cfgate collects routes attached to Gateways it manages and pushes them as cloudflared ingress rules.
- CloudflareDNS (optional) watches routes and creates CNAME records pointing hostnames to the tunnel domain.
The cfgate-system Namespace
Section titled “The cfgate-system Namespace”cfgate installs into cfgate-system by default:
- Helm:
--namespace cfgate-system --create-namespacecreates it automatically - Kustomize: The
install.yamlmanifest includes the namespace definition
CloudflareTunnel and CloudflareDNS resources typically live in cfgate-system alongside the controller. Routes and the services they reference can be in any namespace.
To allow routes from other namespaces to attach to a Gateway in cfgate-system, set allowedRoutes.namespaces.from: All on the Gateway listener:
spec: listeners: - name: http protocol: HTTP port: 80 allowedRoutes: namespaces: from: All # Routes from any namespace can attachWithout this, only routes in the same namespace as the Gateway can attach. You can also use Selector with label selectors for finer control.
Comparison with Ingress
Section titled “Comparison with Ingress”| Concept | Ingress | Gateway API (cfgate) |
|---|---|---|
| Controller selection | IngressClass | GatewayClass |
| Runtime instance | Implicit (Ingress resources create it) | Explicit Gateway resource |
| Routing rules | Ingress resource (host + path rules) | HTTPRoute |
| Per-route config | Annotations on Ingress | Annotations on Route |
| Multi-tenancy | Namespace isolation only | Gateway allowedRoutes with namespace selectors |
| Protocol support | HTTP/HTTPS only | HTTPRoute-driven HTTP routing |
| Role separation | None (one resource does everything) | GatewayClass (infra), Gateway (ops), Route (dev) |
| Cross-namespace routing | Not supported | Built-in via parentRefs with namespace |
Migration Notes
Section titled “Migration Notes”If you are migrating from an Ingress-based Cloudflare operator:
- Create a GatewayClass and Gateway. These replace the implicit infrastructure that Ingress-based operators manage behind the scenes.
- Convert Ingress resources to HTTPRoutes. Each Ingress host/path rule becomes an HTTPRoute. The
parentRefsfield replaces the IngressClass binding. - Move annotations. Ingress annotations on the Ingress resource move to per-route annotations on HTTPRoute resources. Annotation names may differ; see Annotations Reference.
- Set up CloudflareDNS. Ingress operators often handle DNS automatically. With cfgate, DNS management is a separate CRD (CloudflareDNS) that you configure explicitly.
Further Reading
Section titled “Further Reading”- Gateway API documentation
- CloudflareTunnel Reference
- CloudflareDNS Reference
- CloudflareAccessPolicy Reference
- CloudflareAccessApplication Reference
- Annotations Reference
- Troubleshooting
- Service Mesh Integration
Supported HTTPRoute behavior
Section titled “Supported HTTPRoute behavior”cfgate tunnel ingress supports one Kubernetes Service backend per rule, hostname matching, and omitted, Exact, PathPrefix, or Go-compatible RegularExpression path matches. Any positive weight for the single backend receives all matching traffic. A zero-weight backend receives no traffic; cfgate emits an HTTP 500 response for its match so requests cannot fall through to a broader rule. A rule without a backend also returns HTTP 500. Multiple backend references cannot be forwarded because weighted distribution is not implemented; their matches return HTTP 500.
Method, header, and query matches, rule or backend filters, request timeouts, retries, and session persistence are unsupported. A route using any of these fields receives Accepted=False with reason UnsupportedValue and contributes no forwarding rules. cfgate does not silently drop a restriction. Other valid HTTPRoutes remain active. Missing, unauthorized, or unsupported backend references set ResolvedRefs=False. Their specific matches return HTTP 500, while valid sibling rules remain active. This prevents invalid matches from falling through to a broader forwarding route. Only the canonical empty API group denotes a core Service; the literal group core is unsupported. A missing port defaults to 80 only when the Service actually exposes port 80.
Routes are evaluated by current GatewayClass ownership, listener permissions, namespace selectors, hostname intersection, and cross-namespace backend ReferenceGrants. Existing status is not used as authorization. A transient Kubernetes read failure aborts configuration synchronization rather than publishing a partially resolved configuration.
More specific hostnames precede overlapping wildcard hostnames. Within a hostname, exact paths precede regular expressions, followed by prefixes with the longest source path first. Regular-expression precedence is implementation-defined: longer expressions precede shorter expressions. Equal matches use the oldest route creation timestamp, then lexical namespace/name, then the first matching rule. Regular expressions remain active ahead of a catch-all prefix. For prefixes, trailing slashes are ignored: /foo and /foo/ both match /foo and /foo/bar, but not /foobar. Exact paths retain trailing-slash significance. Configuration hashing preserves this ordered evaluation.