This document explains how CRD scope affects Secret lookup and provides guidance on proper credential configuration.
The Cloudflare Operator manages resources at two different scopes:
- Cluster-scoped: Resources accessible across all namespaces
- Namespaced: Resources confined to a specific namespace
The scope determines where the operator looks for Cloudflare API credentials (Secrets).
These resources are not bound to any namespace. Secrets must be in the operator namespace (cloudflare-operator-system).
| CRD | Category | Description |
|---|---|---|
| CloudflareCredentials | Core | Shared API credential configuration |
| ClusterTunnel | Tunnel | Cluster-wide Cloudflare Tunnel |
| VirtualNetwork | Network | Traffic isolation virtual network |
| NetworkRoute | Network | CIDR routing through tunnel |
| WARPConnector | Network | WARP connector for site-to-site |
| AccessGroup | Access | Reusable access policy group |
| AccessIdentityProvider | Access | Identity provider configuration |
| GatewayRule | Gateway | DNS/HTTP/L4 policy rule |
| GatewayList | Gateway | List for gateway rules |
| GatewayConfiguration | Gateway | Global gateway settings |
| DeviceSettingsPolicy | Device | WARP client configuration |
| DevicePostureRule | Device | Device health check rule |
| TunnelGatewayClassConfig | K8s Integration | Gateway API integration config |
These resources exist within a specific namespace. Secrets must be in the same namespace as the resource.
| CRD | Category | Description |
|---|---|---|
| Tunnel | Tunnel | Namespace-scoped Cloudflare Tunnel |
| TunnelBinding | Tunnel | Bind Services to Tunnels with DNS |
| AccessApplication | Access | Zero Trust application |
| AccessServiceToken | Access | M2M authentication token |
| AccessTunnel | Access | Access-protected tunnel endpoint |
| PrivateService | Network | Private IP service exposure |
| DNSRecord | DNS | DNS record management |
| TunnelIngressClassConfig | K8s Integration | Ingress integration config |
For namespaced CRDs, the operator looks for the Secret in the same namespace as the resource.
# Example: Tunnel in "production" namespace
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: Tunnel
metadata:
name: my-tunnel
namespace: production # Resource namespace
spec:
cloudflare:
accountId: "abc123"
domain: example.com
secret: cloudflare-credentials # Must exist in "production" namespaceSecret must be in the same namespace:
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-credentials
namespace: production # Same as Tunnel namespace
type: Opaque
stringData:
CLOUDFLARE_API_TOKEN: "your-token"For cluster-scoped CRDs, the operator looks for the Secret in the operator namespace (cloudflare-operator-system).
# Example: ClusterTunnel (cluster-scoped)
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: ClusterTunnel
metadata:
name: shared-tunnel
# No namespace - cluster-scoped
spec:
cloudflare:
accountId: "abc123"
domain: example.com
secret: cloudflare-credentials # Must exist in operator namespaceSecret must be in the operator namespace:
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-credentials
namespace: cloudflare-operator-system # Operator namespace
type: Opaque
stringData:
CLOUDFLARE_API_TOKEN: "your-token"TunnelBinding is namespaced but can reference either a Tunnel (namespaced) or ClusterTunnel (cluster-scoped).
Referencing a Tunnel (same namespace):
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: TunnelBinding
metadata:
name: my-binding
namespace: production
spec:
tunnelRef:
kind: Tunnel
name: my-tunnel # Must be in "production" namespaceReferencing a ClusterTunnel:
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: TunnelBinding
metadata:
name: my-binding
namespace: production
spec:
tunnelRef:
kind: ClusterTunnel
name: shared-tunnel # Cluster-scoped, no namespace neededflowchart TB
subgraph OperatorNS["cloudflare-operator-system"]
OpSecret["Secret: cloudflare-credentials"]
Operator["Controller Manager"]
end
subgraph ClusterScope["Cluster-Scoped Resources"]
CT["ClusterTunnel"]
VN["VirtualNetwork"]
NR["NetworkRoute"]
AG["AccessGroup"]
GR["GatewayRule"]
end
subgraph ProdNS["production namespace"]
ProdSecret["Secret: cloudflare-credentials"]
Tunnel["Tunnel"]
TB["TunnelBinding"]
AA["AccessApplication"]
end
subgraph DevNS["development namespace"]
DevSecret["Secret: cloudflare-credentials"]
DevTunnel["Tunnel"]
DevTB["TunnelBinding"]
end
CT --> OpSecret
VN --> OpSecret
NR --> OpSecret
AG --> OpSecret
GR --> OpSecret
Tunnel --> ProdSecret
TB --> ProdSecret
AA --> ProdSecret
DevTunnel --> DevSecret
DevTB --> DevSecret
TB -.->|can reference| CT
DevTB -.->|can reference| CT
For cluster-scoped resources, consider using the CloudflareCredentials CRD to centralize credential management:
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: CloudflareCredentials
metadata:
name: main-credentials
spec:
accountId: "abc123"
secret:
name: cloudflare-api-token
namespace: cloudflare-operator-systemFor multi-tenant environments, create separate Secrets per namespace with appropriately scoped API tokens:
# Production - Full access token
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-credentials
namespace: production
stringData:
CLOUDFLARE_API_TOKEN: "production-token-with-full-access"
---
# Development - Limited access token
apiVersion: v1
kind: Secret
metadata:
name: cloudflare-credentials
namespace: development
stringData:
CLOUDFLARE_API_TOKEN: "dev-token-with-limited-zones"When multiple namespaces need to share a tunnel:
# Cluster-scoped tunnel (credentials in operator namespace)
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: ClusterTunnel
metadata:
name: shared-tunnel
spec:
newTunnel:
name: k8s-shared-tunnel
cloudflare:
accountId: "abc123"
domain: example.com
secret: cloudflare-credentials
---
# TunnelBinding in any namespace can reference it
apiVersion: networking.cloudflare-operator.io/v1alpha2
kind: TunnelBinding
metadata:
name: app-binding
namespace: team-a
spec:
tunnelRef:
kind: ClusterTunnel
name: shared-tunnel
subjects:
- kind: Service
name: my-appThe operator's ClusterRole grants permissions to read Secrets across all namespaces:
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "watch"]This is necessary because:
- Namespaced resources need Secrets in their namespace
- Cluster-scoped resources need Secrets in the operator namespace
- TunnelBinding may reference ClusterTunnel credentials
Error: Secret "cloudflare-credentials" not found
Cause: Secret is in the wrong namespace.
Solution:
- Check if the CRD is cluster-scoped or namespaced
- Create the Secret in the correct namespace:
- Namespaced CRD: Same namespace as the resource
- Cluster-scoped CRD:
cloudflare-operator-system
Error: secrets "cloudflare-credentials" is forbidden
Cause: RBAC not configured correctly.
Solution: Ensure the operator ServiceAccount has permissions to read Secrets in the target namespace.
Error: Tunnel "my-tunnel" not found
Cause: Wrong tunnel reference type or namespace.
Solution:
- For
kind: Tunnel: Ensure the Tunnel exists in the same namespace as the TunnelBinding - For
kind: ClusterTunnel: Ensure the ClusterTunnel exists (cluster-scoped, no namespace)
| Resource Type | Secret Location | Example Namespace |
|---|---|---|
| Tunnel | Same as resource | production |
| ClusterTunnel | Operator namespace | cloudflare-operator-system |
| TunnelBinding | Same as resource | production |
| VirtualNetwork | Operator namespace | cloudflare-operator-system |
| NetworkRoute | Operator namespace | cloudflare-operator-system |
| AccessApplication | Same as resource | production |
| AccessGroup | Operator namespace | cloudflare-operator-system |
| DNSRecord | Same as resource | production |
| GatewayRule | Operator namespace | cloudflare-operator-system |