Internet → Cloudflare Edge → Tunnel → cloudflared pod → K8s Service
↑ (runs in-cluster)
Access gate (email OTP)
When a user visits the exposed hostname:
cloudflared pod running in the cluster receives the requestcloudflared forwards it to the target Kubernetes Service via svc.cluster.localThe operator defines two custom resources in the chutes.troubleshat.com API group:
ChuteConfig by name and a local Kubernetes Service. Declares a hostname and access policy (allowed emails/domains). The controller orchestrates the full Cloudflare resource chain.status.otpIdentityProviderIdstatus.ready = trueExecutes a step-by-step creation sequence. Each Cloudflare resource ID is stored in status so reconciliation is idempotent and resumable — if the operator restarts mid-reconciliation, it picks up where it left off.
| Step | Action | Status Field |
|---|---|---|
| 1 | Create Cloudflare Tunnel (named chute-{namespace}-{name}) |
status.tunnelId |
| 2 | PUT tunnel ingress config (hostname → svc.cluster.local mapping) |
— |
| 3 | Create CNAME DNS record ({tunnelId}.cfargotunnel.com) |
status.dnsRecordId |
| 4 | Create Access Application (self-hosted, OTP only) | status.accessAppId |
| 5 | Create Access Policy (email/domain allow rules) | status.accessPolicyId |
| 6 | Create K8s Secret with tunnel token + cloudflared Deployment | — |
The cloudflared Deployment runs with:
/ready endpoint (port 2000)TUNNEL_TOKEN environment variable from a SecretThe operator uses a finalizer (chutes.troubleshat.com/cleanup) to ensure Cloudflare resources are cleaned up before the Kubernetes object is removed.
Deletion proceeds in reverse order:
The cloudflared Deployment and tunnel token Secret are garbage-collected automatically via owner references.
The internal/cloudflare/ package is a thin HTTP client wrapping the Cloudflare v4 REST API. It covers:
All methods accept context.Context for cancellation. The standard Cloudflare response envelope ({success, errors, result}) is handled in a shared do() method.
All Ensure* methods are find-or-create: they first check if the resource already exists (by name or domain), returning it if found, or creating it otherwise. This makes reconciliation safe to retry.
| Resource | Name Pattern |
|---|---|
| Cloudflare Tunnel | chute-{namespace}-{instance} |
| Access Application | chute-{namespace}-{instance} |
| Access Policy | chute-operator-allow |
| K8s tunnel token Secret | chute-{instance}-tunnel-token |
| K8s cloudflared Deployment | chute-{instance}-cloudflared |