update crds

This commit is contained in:
2026-08-18 19:18:31 +01:00
parent 717d09d1f3
commit bb5fc11402
268 changed files with 88606 additions and 1644 deletions
@@ -0,0 +1,733 @@
{
"description": "CloudflareAccessApplication binds Gateway API targets to reusable Cloudflare Access policies.",
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"metadata": {
"type": "object"
},
"spec": {
"description": "CloudflareAccessApplicationSpec defines Gateway API target bindings to reusable Access policies.",
"properties": {
"application": {
"description": "Application defines Access Application settings shared by generated apps.\nThe path field overrides any path derived from HTTPRoute rules.",
"properties": {
"allowedIdps": {
"description": "AllowedIdps restricts which identity providers can authenticate.\nValues are Cloudflare Identity Provider UUIDs.\nWhen empty, all IdPs configured in the account are allowed.",
"items": {
"type": "string"
},
"maxItems": 25,
"type": "array"
},
"appLauncherVisible": {
"default": true,
"description": "AppLauncherVisible controls whether the application appears in the\nCloudflare App Launcher dashboard. Use pointer to distinguish\nexplicit false (hidden) from absent (default visible).",
"type": "boolean"
},
"autoRedirectToIdentity": {
"description": "AutoRedirectToIdentity auto-redirects to the identity provider\nwhen a single IdP is configured in allowedIdps. Skips the IdP\nselection page.",
"type": "boolean"
},
"corsHeaders": {
"description": "CORSHeaders configures CORS for browser-based APIs behind Access.\nWhen set, Cloudflare responds to OPTIONS preflight on behalf of the origin.\nMutually exclusive with optionsPreflightBypass.",
"properties": {
"allowAllHeaders": {
"description": "AllowAllHeaders allows all HTTP request headers.",
"type": "boolean"
},
"allowAllMethods": {
"description": "AllowAllMethods allows all HTTP request methods.",
"type": "boolean"
},
"allowAllOrigins": {
"description": "AllowAllOrigins allows all origins.",
"type": "boolean"
},
"allowCredentials": {
"description": "AllowCredentials includes credentials (cookies, authorization headers,\nor TLS client certificates) with CORS requests.",
"type": "boolean"
},
"allowedHeaders": {
"description": "AllowedHeaders lists specific allowed HTTP request headers.\nIgnored when allowAllHeaders is true.",
"items": {
"type": "string"
},
"maxItems": 50,
"type": "array"
},
"allowedMethods": {
"description": "AllowedMethods lists specific allowed HTTP request methods.\nIgnored when allowAllMethods is true.",
"items": {
"description": "CORSAllowedMethod is an HTTP method allowed for CORS requests.",
"enum": [
"GET",
"POST",
"HEAD",
"PUT",
"DELETE",
"CONNECT",
"OPTIONS",
"TRACE",
"PATCH"
],
"type": "string"
},
"maxItems": 9,
"type": "array"
},
"allowedOrigins": {
"description": "AllowedOrigins lists specific allowed origins.\nIgnored when allowAllOrigins is true.",
"items": {
"type": "string"
},
"maxItems": 50,
"type": "array"
},
"maxAge": {
"description": "MaxAge is the maximum number of seconds preflight results can be cached.",
"maximum": 86400,
"minimum": 0,
"type": "integer"
}
},
"type": "object",
"additionalProperties": false
},
"customDenyMessage": {
"description": "CustomDenyMessage shown when access is denied.",
"maxLength": 1024,
"type": "string"
},
"customDenyUrl": {
"description": "CustomDenyURL redirects to this URL when denied (instead of message).",
"type": "string"
},
"customNonIdentityDenyUrl": {
"description": "CustomNonIdentityDenyURL is the URL users are redirected to when\ndenied by a non-identity (service auth) policy. Separate from\ncustomDenyUrl which handles identity-based denials.",
"maxLength": 1024,
"type": "string"
},
"domain": {
"description": "Domain is the protected domain (auto-generated from routes if omitted).\nMax 253: RFC 1035 section 2.3.4 FQDN presentation-format limit.",
"maxLength": 253,
"type": "string",
"x-kubernetes-validations": [
{
"message": "each DNS label must not exceed 63 octets (RFC 1035 section 2.3.4)",
"rule": "self == '' || self.split('.').all(s, size(s) <= 63)"
}
]
},
"enableBindingCookie": {
"default": false,
"description": "EnableBindingCookie enables binding cookies for sticky sessions.",
"type": "boolean"
},
"httpOnlyCookieAttribute": {
"default": true,
"description": "HttpOnlyCookieAttribute adds HttpOnly to session cookies.",
"type": "boolean"
},
"logoUrl": {
"description": "LogoURL is the application logo in dashboard.",
"maxLength": 1024,
"type": "string"
},
"name": {
"description": "Name is the display name in Cloudflare dashboard.\nDefaults to CR name if omitted.",
"maxLength": 255,
"type": "string"
},
"optionsPreflightBypass": {
"description": "OptionsPreflightBypass allows OPTIONS preflight requests to bypass\nAccess authentication and go directly to the origin. Enabling this\nremoves all CORS header settings. Mutually exclusive with corsHeaders.",
"type": "boolean"
},
"path": {
"description": "Path restricts protection to a specific absolute path prefix.\nCloudflare Access paths must not include query strings or fragments.",
"maxLength": 1024,
"pattern": "^/[^?#]*$",
"type": "string"
},
"pathCookieAttribute": {
"description": "PathCookieAttribute scopes the Access JWT cookie to the application\npath instead of the hostname. When enabled, users must re-authenticate\nfor different paths on the same hostname.",
"type": "boolean"
},
"readServiceTokensFromHeader": {
"description": "ReadServiceTokensFromHeader enables reading service tokens from a\nsingle custom HTTP header instead of the standard CF-Access-Client-Id\nand CF-Access-Client-Secret header pair. The value is the header name.\nThe header value must contain a JSON object with \"cf-access-client-id\"\nand \"cf-access-client-secret\" keys.",
"maxLength": 256,
"type": "string"
},
"sameSiteCookieAttribute": {
"default": "lax",
"description": "SameSiteCookieAttribute controls cross-site cookie behavior.",
"enum": [
"strict",
"lax",
"none"
],
"type": "string"
},
"serviceAuth401Redirect": {
"description": "ServiceAuth401Redirect returns a 401 status code instead of\nredirecting to the Access login page when a request is blocked by a\nService Auth (non_identity) policy. Enable for API consumers.",
"type": "boolean"
},
"sessionDuration": {
"default": "24h",
"description": "SessionDuration controls session cookie lifetime.",
"pattern": "^([0-9]+(ns|us|ms|s|m|h))+$",
"type": "string"
},
"skipInterstitial": {
"default": false,
"description": "SkipInterstitial bypasses the Access login page for API requests.",
"type": "boolean"
},
"type": {
"default": "self_hosted",
"description": "Type is the application type.",
"enum": [
"self_hosted"
],
"type": "string"
}
},
"type": "object",
"x-kubernetes-validations": [
{
"message": "corsHeaders and optionsPreflightBypass are mutually exclusive",
"rule": "!(has(self.corsHeaders) && has(self.optionsPreflightBypass) && self.optionsPreflightBypass)"
}
],
"additionalProperties": false
},
"cloudflareRef": {
"description": "CloudflareRef references Cloudflare credentials. When omitted, credentials\nare inherited from each target's route -> Gateway -> CloudflareTunnel chain.\nMultiple targets must inherit the same Cloudflare account.",
"properties": {
"accountId": {
"description": "AccountID is the Cloudflare account ID.",
"maxLength": 32,
"type": "string"
},
"accountName": {
"description": "AccountName is the Cloudflare account name (looked up via API).",
"maxLength": 255,
"type": "string"
},
"name": {
"description": "Name of the secret containing credentials.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace of the secret (defaults to policy namespace).",
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
},
"policyRefs": {
"description": "PolicyRefs lists reusable CloudflareAccessPolicy resources to attach.",
"items": {
"description": "AccessPolicyReference references a reusable CloudflareAccessPolicy.",
"properties": {
"name": {
"description": "Name is the CloudflareAccessPolicy name.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"default": "",
"description": "Namespace is the CloudflareAccessPolicy namespace. Empty defaults to application namespace.\nCross-namespace references require ReferenceGrant.",
"maxLength": 253,
"type": "string"
},
"precedence": {
"description": "Precedence determines policy evaluation order for the application. Lower values run first.\nWhen omitted, the controller uses list order starting at 1.",
"maximum": 9999,
"minimum": 1,
"type": "integer"
}
},
"required": [
"name",
"namespace"
],
"type": "object",
"additionalProperties": false
},
"maxItems": 16,
"minItems": 1,
"type": "array",
"x-kubernetes-list-map-keys": [
"name",
"namespace"
],
"x-kubernetes-list-type": "map"
},
"targetRef": {
"description": "TargetRef identifies a single Gateway API target.",
"properties": {
"group": {
"default": "gateway.networking.k8s.io",
"description": "Group is the API group of the target resource.",
"maxLength": 253,
"type": "string"
},
"kind": {
"description": "Kind is the kind of the target resource.",
"enum": [
"Gateway",
"HTTPRoute"
],
"maxLength": 63,
"type": "string"
},
"name": {
"description": "Name is the name of the target resource.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace is the namespace of the target resource.\nCross-namespace targeting requires ReferenceGrant.",
"maxLength": 253,
"type": "string"
},
"sectionName": {
"description": "SectionName targets specific listener (Gateway) or rule (Route).",
"maxLength": 253,
"type": "string"
}
},
"required": [
"group",
"kind",
"name"
],
"type": "object",
"x-kubernetes-validations": [
{
"message": "group must be gateway.networking.k8s.io",
"rule": "self.group == 'gateway.networking.k8s.io'"
},
{
"message": "kind must be Gateway or HTTPRoute",
"rule": "self.kind in ['Gateway', 'HTTPRoute']"
}
],
"additionalProperties": false
},
"targetRefs": {
"description": "TargetRefs identifies multiple Gateway API targets.",
"items": {
"description": "PolicyTargetReference identifies a Gateway API resource for Access application attachment.\n\nPolicyTargetReference follows the Gateway API LocalPolicyTargetReferenceWithSectionName\npattern. It targets Gateway API Gateway and HTTPRoute resources and extracts\nhostnames and paths from those resources to create corresponding Cloudflare Access\napplications.\n\nCross-namespace references require a ReferenceGrant in the target namespace that permits\nCloudflareAccessApplication resources from the application's namespace.",
"properties": {
"group": {
"default": "gateway.networking.k8s.io",
"description": "Group is the API group of the target resource.",
"maxLength": 253,
"type": "string"
},
"kind": {
"description": "Kind is the kind of the target resource.",
"enum": [
"Gateway",
"HTTPRoute"
],
"maxLength": 63,
"type": "string"
},
"name": {
"description": "Name is the name of the target resource.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace is the namespace of the target resource.\nCross-namespace targeting requires ReferenceGrant.",
"maxLength": 253,
"type": "string"
},
"sectionName": {
"description": "SectionName targets specific listener (Gateway) or rule (Route).",
"maxLength": 253,
"type": "string"
}
},
"required": [
"group",
"kind",
"name"
],
"type": "object",
"x-kubernetes-validations": [
{
"message": "group must be gateway.networking.k8s.io",
"rule": "self.group == 'gateway.networking.k8s.io'"
},
{
"message": "kind must be Gateway or HTTPRoute",
"rule": "self.kind in ['Gateway', 'HTTPRoute']"
}
],
"additionalProperties": false
},
"maxItems": 16,
"minItems": 1,
"type": "array"
}
},
"required": [
"policyRefs"
],
"type": "object",
"x-kubernetes-validations": [
{
"message": "either targetRef or targetRefs must be specified",
"rule": "has(self.targetRef) || has(self.targetRefs)"
},
{
"message": "targetRef and targetRefs are mutually exclusive",
"rule": "!(has(self.targetRef) && has(self.targetRefs))"
},
{
"message": "policyRefs must either all omit precedence or all specify precedence",
"rule": "self.policyRefs.all(p, !has(p.precedence)) || self.policyRefs.all(p, has(p.precedence))"
},
{
"message": "policyRefs precedence values must be unique",
"rule": "self.policyRefs.all(p, !has(p.precedence)) || self.policyRefs.all(p, has(p.precedence) && self.policyRefs.exists_one(q, has(q.precedence) && q.precedence == p.precedence))"
}
],
"additionalProperties": false
},
"status": {
"description": "CloudflareAccessApplicationStatus defines observed Access application state.",
"properties": {
"accountId": {
"description": "AccountID is the resolved Cloudflare account ID used for Access application cleanup.",
"maxLength": 32,
"type": "string"
},
"ancestors": {
"description": "Ancestors contains status for each targetRef.",
"items": {
"description": "PolicyAncestorStatus describes the policy attachment status for a specific target.\n\nPolicyAncestorStatus follows the Gateway API PolicyAncestorStatus pattern to report\nper-target attachment status. Each target reference in the spec has a corresponding\nancestor status entry showing whether the policy was successfully attached.",
"properties": {
"ancestorRef": {
"description": "AncestorRef identifies the target.",
"properties": {
"group": {
"default": "gateway.networking.k8s.io",
"description": "Group is the API group of the target resource.",
"maxLength": 253,
"type": "string"
},
"kind": {
"description": "Kind is the kind of the target resource.",
"enum": [
"Gateway",
"HTTPRoute"
],
"maxLength": 63,
"type": "string"
},
"name": {
"description": "Name is the name of the target resource.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace is the namespace of the target resource.\nCross-namespace targeting requires ReferenceGrant.",
"maxLength": 253,
"type": "string"
},
"sectionName": {
"description": "SectionName targets specific listener (Gateway) or rule (Route).",
"maxLength": 253,
"type": "string"
}
},
"required": [
"group",
"kind",
"name"
],
"type": "object",
"x-kubernetes-validations": [
{
"message": "group must be gateway.networking.k8s.io",
"rule": "self.group == 'gateway.networking.k8s.io'"
},
{
"message": "kind must be Gateway or HTTPRoute",
"rule": "self.kind in ['Gateway', 'HTTPRoute']"
}
],
"additionalProperties": false
},
"conditions": {
"description": "Conditions for this specific target.",
"items": {
"description": "Condition contains details for one aspect of the current state of this API Resource.",
"properties": {
"lastTransitionTime": {
"description": "lastTransitionTime is the last time the condition transitioned from one status to another.\nThis should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.",
"format": "date-time",
"type": "string"
},
"message": {
"description": "message is a human readable message indicating details about the transition.\nThis may be an empty string.",
"maxLength": 32768,
"type": "string"
},
"observedGeneration": {
"description": "observedGeneration represents the .metadata.generation that the condition was set based upon.\nFor instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date\nwith respect to the current state of the instance.",
"format": "int64",
"minimum": 0,
"type": "integer"
},
"reason": {
"description": "reason contains a programmatic identifier indicating the reason for the condition's last transition.\nProducers of specific condition types may define expected values and meanings for this field,\nand whether the values are considered a guaranteed API.\nThe value should be a CamelCase string.\nThis field may not be empty.",
"maxLength": 1024,
"minLength": 1,
"pattern": "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$",
"type": "string"
},
"status": {
"description": "status of the condition, one of True, False, Unknown.",
"enum": [
"True",
"False",
"Unknown"
],
"type": "string"
},
"type": {
"description": "type of condition in CamelCase or in foo.example.com/CamelCase.",
"maxLength": 316,
"pattern": "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$",
"type": "string"
}
},
"required": [
"lastTransitionTime",
"message",
"reason",
"status",
"type"
],
"type": "object",
"additionalProperties": false
},
"maxItems": 8,
"type": "array"
},
"controllerName": {
"description": "ControllerName identifies the controller managing this attachment.",
"maxLength": 253,
"type": "string"
}
},
"required": [
"ancestorRef",
"controllerName"
],
"type": "object",
"additionalProperties": false
},
"maxItems": 64,
"type": "array"
},
"applications": {
"description": "Applications are Cloudflare Access Applications managed by this resource.",
"items": {
"description": "AccessApplicationObserved records a Cloudflare Access Application created for one host/path target.",
"properties": {
"aud": {
"description": "AUD is the Application Audience Tag.",
"maxLength": 255,
"type": "string"
},
"domain": {
"description": "Domain is the protected hostname/path in Cloudflare.",
"maxLength": 1024,
"type": "string"
},
"id": {
"description": "ID is the Cloudflare Access Application ID.",
"maxLength": 36,
"type": "string"
},
"targetRef": {
"description": "TargetRef identifies the Gateway API target that produced this application.",
"properties": {
"group": {
"default": "gateway.networking.k8s.io",
"description": "Group is the API group of the target resource.",
"maxLength": 253,
"type": "string"
},
"kind": {
"description": "Kind is the kind of the target resource.",
"enum": [
"Gateway",
"HTTPRoute"
],
"maxLength": 63,
"type": "string"
},
"name": {
"description": "Name is the name of the target resource.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace is the namespace of the target resource.\nCross-namespace targeting requires ReferenceGrant.",
"maxLength": 253,
"type": "string"
},
"sectionName": {
"description": "SectionName targets specific listener (Gateway) or rule (Route).",
"maxLength": 253,
"type": "string"
}
},
"required": [
"group",
"kind",
"name"
],
"type": "object",
"x-kubernetes-validations": [
{
"message": "group must be gateway.networking.k8s.io",
"rule": "self.group == 'gateway.networking.k8s.io'"
},
{
"message": "kind must be Gateway or HTTPRoute",
"rule": "self.kind in ['Gateway', 'HTTPRoute']"
}
],
"additionalProperties": false
}
},
"type": "object",
"additionalProperties": false
},
"maxItems": 64,
"type": "array"
},
"attachedTargets": {
"description": "AttachedTargets is the count of successfully attached Gateway API targets.",
"format": "int32",
"type": "integer"
},
"conditions": {
"description": "Conditions describe current state.",
"items": {
"description": "Condition contains details for one aspect of the current state of this API Resource.",
"properties": {
"lastTransitionTime": {
"description": "lastTransitionTime is the last time the condition transitioned from one status to another.\nThis should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.",
"format": "date-time",
"type": "string"
},
"message": {
"description": "message is a human readable message indicating details about the transition.\nThis may be an empty string.",
"maxLength": 32768,
"type": "string"
},
"observedGeneration": {
"description": "observedGeneration represents the .metadata.generation that the condition was set based upon.\nFor instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date\nwith respect to the current state of the instance.",
"format": "int64",
"minimum": 0,
"type": "integer"
},
"reason": {
"description": "reason contains a programmatic identifier indicating the reason for the condition's last transition.\nProducers of specific condition types may define expected values and meanings for this field,\nand whether the values are considered a guaranteed API.\nThe value should be a CamelCase string.\nThis field may not be empty.",
"maxLength": 1024,
"minLength": 1,
"pattern": "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$",
"type": "string"
},
"status": {
"description": "status of the condition, one of True, False, Unknown.",
"enum": [
"True",
"False",
"Unknown"
],
"type": "string"
},
"type": {
"description": "type of condition in CamelCase or in foo.example.com/CamelCase.",
"maxLength": 316,
"pattern": "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$",
"type": "string"
}
},
"required": [
"lastTransitionTime",
"message",
"reason",
"status",
"type"
],
"type": "object",
"additionalProperties": false
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
},
"credentialSecretRef": {
"description": "CredentialSecretRef is the resolved credentials Secret used for cleanup.\nThe namespace is always stored explicitly.",
"properties": {
"name": {
"description": "Name of the secret.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace of the secret. Defaults to the resource's namespace if empty.",
"maxLength": 63,
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
},
"observedGeneration": {
"description": "ObservedGeneration is the last generation processed.",
"format": "int64",
"type": "integer"
}
},
"type": "object",
"additionalProperties": false
}
},
"type": "object"
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,591 @@
{
"description": "CloudflareDNS is the Schema for the cloudflarednses API.\n\nCloudflareDNS manages DNS record synchronization independently from CloudflareTunnel resources.\nIt supports two target modes: tunnel references (for tunnel-based CNAME records) and external\ntargets (for non-tunnel DNS management). DNS records can be sourced from Gateway API routes\nor explicitly defined.\n\nCloudflareDNS implements ownership tracking via TXT records (aligned with external-dns patterns)\nto enable safe multi-cluster deployments and prevent accidental deletion of records created\nby other installations.\n\nStatus conditions:\n - Ready: DNS sync is fully operational\n - CredentialsValid: Cloudflare credentials have been validated\n - ZonesResolved: All configured zones have been resolved via API\n - RecordsSynced: DNS records have been synchronized to Cloudflare\n - OwnershipVerified: TXT ownership records have been verified, when enabled",
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"metadata": {
"type": "object"
},
"spec": {
"description": "CloudflareDNSSpec defines the desired state of a CloudflareDNS resource.\n\nCloudflareDNSSpec configures DNS record synchronization, including the target\n(tunnel or external), zones to manage, hostname sources, and ownership tracking.\nEither tunnelRef or externalTarget must be specified (mutually exclusive).",
"properties": {
"cleanupPolicy": {
"description": "CleanupPolicy defines cleanup behavior for records.",
"properties": {
"deleteOnResourceRemoval": {
"description": "DeleteOnResourceRemoval deletes records when CloudflareDNS resource is deleted.\nnil defaults to true.",
"type": "boolean"
},
"deleteOnRouteRemoval": {
"description": "DeleteOnRouteRemoval deletes records when the source route is deleted.\nnil defaults to true.",
"type": "boolean"
},
"onlyManaged": {
"description": "OnlyManaged only deletes records that were created by cfgate (verified via ownership).\nnil defaults to true.",
"type": "boolean"
}
},
"type": "object",
"additionalProperties": false
},
"cloudflare": {
"description": "Cloudflare API credentials (required when using externalTarget).\nWhen using tunnelRef, credentials are inherited from the tunnel.",
"properties": {
"accountId": {
"description": "AccountID is the Cloudflare Account ID.",
"maxLength": 32,
"type": "string"
},
"accountName": {
"description": "AccountName is the Cloudflare Account name. Will be looked up via API.",
"maxLength": 255,
"type": "string"
},
"secretKeys": {
"description": "SecretKeys defines the key mappings within the secret.",
"properties": {
"apiToken": {
"default": "CLOUDFLARE_API_TOKEN",
"description": "APIToken is the key name for the Cloudflare API token.",
"maxLength": 253,
"type": "string"
}
},
"type": "object",
"additionalProperties": false
},
"secretRef": {
"description": "SecretRef references the Secret containing Cloudflare API credentials.\nThe secret must contain an API token (not tunnel token).",
"properties": {
"name": {
"description": "Name of the secret.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace of the secret. Defaults to the tunnel's namespace.",
"maxLength": 63,
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
}
},
"required": [
"secretRef"
],
"type": "object",
"x-kubernetes-validations": [
{
"message": "either accountId or accountName must be specified",
"rule": "has(self.accountId) || has(self.accountName)"
},
{
"message": "accountId must be a 32-character hex string",
"rule": "!has(self.accountId) || self.accountId.matches('^[a-f0-9]{32}$')"
}
],
"additionalProperties": false
},
"defaults": {
"description": "Defaults defines default settings for DNS records.",
"properties": {
"proxied": {
"default": true,
"description": "Proxied enables Cloudflare proxy by default.",
"type": "boolean"
},
"ttl": {
"default": 1,
"description": "TTL is the default DNS record TTL in seconds.\nValid values: 1 (auto) or 60-86400 (explicit).",
"format": "int32",
"maximum": 86400,
"minimum": 1,
"type": "integer"
}
},
"type": "object",
"x-kubernetes-validations": [
{
"message": "TTL must be 1 (auto) or between 60 and 86400 seconds",
"rule": "!has(self.ttl) || self.ttl == 1 || (self.ttl >= 60 && self.ttl <= 86400)"
}
],
"additionalProperties": false
},
"externalTarget": {
"description": "ExternalTarget specifies a non-tunnel DNS target.",
"properties": {
"type": {
"allOf": [
{
"enum": [
"CNAME",
"A",
"AAAA"
]
},
{
"enum": [
"CNAME",
"A",
"AAAA"
]
}
],
"description": "Type is the DNS record type.",
"type": "string"
},
"value": {
"description": "Value is the target value (domain for CNAME, IP for A/AAAA).\nMax 253: RFC 1035 section 2.3.4 FQDN presentation-format limit.",
"maxLength": 253,
"minLength": 1,
"type": "string"
}
},
"required": [
"type",
"value"
],
"type": "object",
"additionalProperties": false
},
"fallbackCredentialsRef": {
"description": "FallbackCredentialsRef references fallback Cloudflare API credentials.\nUsed during deletion when primary credentials are unavailable.",
"properties": {
"name": {
"description": "Name of the secret.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace of the secret. Defaults to the resource's namespace if empty.",
"maxLength": 63,
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
},
"ownership": {
"description": "Ownership defines how to track record ownership.",
"properties": {
"comment": {
"description": "Comment configures comment-based ownership.\n\nDeprecated: since v0.1.0-alpha.13. All fields are ignored. Will be removed in a future cleanup release.",
"properties": {
"enabled": {
"default": false,
"description": "Enabled enables comment-based ownership tracking.\n\nDeprecated: since v0.1.0-alpha.13. This field is ignored. The controller always\nwrites a \"managed by cfgate\" comment. Will be removed in a future cleanup release.",
"type": "boolean"
},
"template": {
"default": "managed by cfgate",
"description": "Template is the comment template.\n\nDeprecated: since v0.1.0-alpha.13. This field is ignored. The controller always\nuses \"managed by cfgate\" as the comment. Will be removed in a future cleanup release.",
"maxLength": 255,
"type": "string"
}
},
"type": "object",
"additionalProperties": false
},
"ownerId": {
"description": "OwnerID is the cluster/installation identifier used in TXT ownership records.\nUsed to distinguish records created by different cfgate installations.\nDefaults to the CloudflareDNS resource's namespace/name if not specified.",
"maxLength": 253,
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?(/[a-z0-9]([-a-z0-9]*[a-z0-9])?)?$",
"type": "string"
},
"txtRecord": {
"description": "TXTRecord configures TXT record-based ownership.",
"properties": {
"enabled": {
"description": "Enabled enables TXT record ownership tracking.\nnil defaults to true.",
"type": "boolean"
},
"prefix": {
"default": "_cfgate",
"description": "Prefix is the prefix for TXT record names.",
"maxLength": 63,
"type": "string"
}
},
"type": "object",
"additionalProperties": false
}
},
"type": "object",
"additionalProperties": false
},
"policy": {
"allOf": [
{
"enum": [
"sync",
"upsert-only",
"create-only"
]
},
{
"enum": [
"sync",
"upsert-only",
"create-only"
]
}
],
"default": "sync",
"description": "Policy controls DNS record lifecycle.",
"type": "string"
},
"source": {
"description": "Source defines where to get hostnames to sync.",
"properties": {
"explicit": {
"description": "Explicit defines explicit hostnames to sync.",
"items": {
"description": "DNSExplicitHostname defines an explicit hostname to sync with optional per-hostname configuration.\n\nDNSExplicitHostname provides direct specification of DNS hostnames without depending on\nGateway API route discovery. The Target field supports the template\nvariable for dynamic resolution when using tunnelRef.",
"properties": {
"hostname": {
"description": "Hostname is the DNS hostname to create.\nMax 253: RFC 1035 section 2.3.4 FQDN presentation-format limit.",
"maxLength": 253,
"minLength": 1,
"type": "string",
"x-kubernetes-validations": [
{
"message": "each DNS label must not exceed 63 octets (RFC 1035 section 2.3.4)",
"rule": "self.split('.').all(s, size(s) <= 63)"
}
]
},
"proxied": {
"description": "Proxied enables Cloudflare proxy for this record.\nnil inherits from zone or defaults.",
"type": "boolean"
},
"target": {
"description": "Target overrides the resolved record target for this hostname.\nSupports template variable when tunnelRef is used.\nDefaults to the resource-level resolved target when omitted.\nMax 253: RFC 1035 section 2.3.4 FQDN presentation-format limit.",
"maxLength": 253,
"type": "string"
},
"ttl": {
"default": 1,
"description": "TTL is the DNS record TTL in seconds. 1 means auto (Cloudflare managed).\nValid values: 1 (auto) or 60-86400 (explicit).",
"format": "int32",
"maximum": 86400,
"minimum": 1,
"type": "integer"
}
},
"required": [
"hostname"
],
"type": "object",
"x-kubernetes-validations": [
{
"message": "TTL must be 1 (auto) or between 60 and 86400 seconds",
"rule": "!has(self.ttl) || self.ttl == 1 || (self.ttl >= 60 && self.ttl <= 86400)"
}
],
"additionalProperties": false
},
"maxItems": 100,
"type": "array"
},
"gatewayRoutes": {
"description": "GatewayRoutes configures watching Gateway API routes.",
"properties": {
"annotationFilter": {
"description": "AnnotationFilter only syncs routes with this annotation.",
"maxLength": 255,
"type": "string"
},
"enabled": {
"default": true,
"description": "Enabled enables watching Gateway API routes.",
"type": "boolean"
},
"namespaceSelector": {
"description": "NamespaceSelector limits route discovery to specific namespaces.",
"properties": {
"matchLabels": {
"additionalProperties": {
"type": "string"
},
"description": "MatchLabels selects namespaces with matching labels.",
"maxProperties": 10,
"type": "object"
},
"matchNames": {
"description": "MatchNames selects namespaces by name.",
"items": {
"type": "string"
},
"maxItems": 50,
"type": "array"
}
},
"type": "object",
"x-kubernetes-validations": [
{
"message": "at least one selector must be specified",
"rule": "has(self.matchLabels) || has(self.matchNames)"
}
],
"additionalProperties": false
}
},
"type": "object",
"additionalProperties": false
}
},
"type": "object",
"additionalProperties": false
},
"tunnelRef": {
"description": "TunnelRef references a CloudflareTunnel for CNAME target resolution.",
"properties": {
"name": {
"description": "Name is the name of the CloudflareTunnel.",
"maxLength": 63,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace is the namespace of the CloudflareTunnel.\nDefaults to the CloudflareDNS's namespace.",
"maxLength": 63,
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
},
"zones": {
"description": "Zones defines the DNS zones to manage.",
"items": {
"description": "DNSZoneConfig defines a DNS zone where records will be managed.\n\nDNSZoneConfig identifies a Cloudflare DNS zone either by name (requiring API lookup)\nor by explicit zone ID. The optional Proxied field sets the default proxy behavior\nfor all records in this zone.",
"properties": {
"id": {
"description": "ID is the optional explicit zone ID (skips API lookup).",
"maxLength": 32,
"pattern": "^[a-f0-9]{32}$",
"type": "string"
},
"name": {
"description": "Name is the zone domain name (e.g., example.com).\nMax 253: RFC 1035 section 2.3.4 FQDN presentation-format limit.",
"maxLength": 253,
"minLength": 1,
"type": "string",
"x-kubernetes-validations": [
{
"message": "each DNS label must not exceed 63 octets (RFC 1035 section 2.3.4)",
"rule": "self.split('.').all(s, size(s) <= 63)"
}
]
},
"proxied": {
"description": "Proxied sets the default proxied setting for this zone.\nnil inherits from spec.defaults.proxied.",
"type": "boolean"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
},
"maxItems": 10,
"minItems": 1,
"type": "array"
}
},
"required": [
"zones"
],
"type": "object",
"x-kubernetes-validations": [
{
"message": "either tunnelRef or externalTarget must be specified",
"rule": "has(self.tunnelRef) || has(self.externalTarget)"
},
{
"message": "tunnelRef and externalTarget are mutually exclusive",
"rule": "!(has(self.tunnelRef) && has(self.externalTarget))"
},
{
"message": "cloudflare credentials required when using externalTarget",
"rule": "has(self.tunnelRef) || has(self.cloudflare)"
}
],
"additionalProperties": false
},
"status": {
"description": "CloudflareDNSStatus defines the observed state of a CloudflareDNS resource.\n\nCloudflareDNSStatus captures the synchronization state of all DNS records, including\ncounts of synced, pending, and failed records. The ResolvedTarget field shows the\nactual CNAME target being used (either from tunnel or external target).",
"properties": {
"conditions": {
"description": "Conditions represent the latest available observations.",
"items": {
"description": "Condition contains details for one aspect of the current state of this API Resource.",
"properties": {
"lastTransitionTime": {
"description": "lastTransitionTime is the last time the condition transitioned from one status to another.\nThis should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.",
"format": "date-time",
"type": "string"
},
"message": {
"description": "message is a human readable message indicating details about the transition.\nThis may be an empty string.",
"maxLength": 32768,
"type": "string"
},
"observedGeneration": {
"description": "observedGeneration represents the .metadata.generation that the condition was set based upon.\nFor instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date\nwith respect to the current state of the instance.",
"format": "int64",
"minimum": 0,
"type": "integer"
},
"reason": {
"description": "reason contains a programmatic identifier indicating the reason for the condition's last transition.\nProducers of specific condition types may define expected values and meanings for this field,\nand whether the values are considered a guaranteed API.\nThe value should be a CamelCase string.\nThis field may not be empty.",
"maxLength": 1024,
"minLength": 1,
"pattern": "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$",
"type": "string"
},
"status": {
"description": "status of the condition, one of True, False, Unknown.",
"enum": [
"True",
"False",
"Unknown"
],
"type": "string"
},
"type": {
"description": "type of condition in CamelCase or in foo.example.com/CamelCase.",
"maxLength": 316,
"pattern": "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$",
"type": "string"
}
},
"required": [
"lastTransitionTime",
"message",
"reason",
"status",
"type"
],
"type": "object",
"additionalProperties": false
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
},
"failedRecords": {
"description": "FailedRecords is the number of records that failed to sync.",
"format": "int32",
"type": "integer"
},
"lastSyncTime": {
"description": "LastSyncTime is the last time records were synced.",
"format": "date-time",
"type": "string"
},
"observedGeneration": {
"description": "ObservedGeneration is the generation observed by the controller.",
"format": "int64",
"type": "integer"
},
"pendingRecords": {
"description": "PendingRecords is the number of records pending sync.",
"format": "int32",
"type": "integer"
},
"records": {
"description": "Records contains the status of individual DNS records.",
"items": {
"description": "DNSRecordSyncStatus represents the synchronization status of a single DNS record.\n\nDNSRecordSyncStatus tracks individual DNS record state including the Cloudflare record ID,\ncurrent configuration, and sync status. The Status field indicates: Synced (successfully\nsynchronized), Pending (awaiting sync), or Failed (sync failed, see Error field).",
"properties": {
"error": {
"description": "Error contains the error message if status is Failed.",
"type": "string"
},
"hostname": {
"description": "Hostname is the DNS hostname.",
"type": "string"
},
"proxied": {
"description": "Proxied indicates if Cloudflare proxy is enabled.",
"type": "boolean"
},
"recordId": {
"description": "RecordID is the Cloudflare record ID.",
"type": "string"
},
"status": {
"description": "Status is the sync status: Synced, Pending, Failed.",
"type": "string"
},
"target": {
"description": "Target is the record target/content.",
"type": "string"
},
"ttl": {
"description": "TTL is the record TTL.",
"format": "int32",
"type": "integer"
},
"type": {
"description": "Type is the DNS record type (CNAME, A, AAAA).",
"type": "string"
},
"zoneId": {
"description": "ZoneID is the Cloudflare zone ID where the record was created.",
"type": "string"
}
},
"required": [
"hostname",
"proxied",
"status",
"target",
"type"
],
"type": "object",
"additionalProperties": false
},
"maxItems": 1000,
"type": "array"
},
"resolvedTarget": {
"description": "ResolvedTarget is the resolved CNAME target (tunnel domain or external value).",
"type": "string"
},
"syncedRecords": {
"description": "SyncedRecords is the number of successfully synced records.",
"format": "int32",
"type": "integer"
}
},
"type": "object",
"additionalProperties": false
}
},
"type": "object"
}
@@ -0,0 +1,483 @@
{
"description": "CloudflareTunnel is the Schema for the cloudflaretunnels API.\n\nCloudflareTunnel manages the lifecycle of a Cloudflare Tunnel and its cloudflared daemon\ndeployment. It handles tunnel creation or adoption, credential management, and deploys\ncloudflared pods that establish secure connections to Cloudflare's edge network.\n\nCloudflareTunnel follows a composable architecture where tunnel lifecycle is separate from\nDNS management. Use CloudflareDNS with a tunnelRef to create DNS records pointing to this\ntunnel's domain.\n\nStatus conditions:\n - Ready: tunnel is fully operational\n - CredentialsValid: API credentials have been validated\n - TunnelReady: tunnel exists in Cloudflare\n - ConfigurationSynced: ingress configuration is synced\n - CloudflaredDeployed: cloudflared pods are running",
"properties": {
"apiVersion": {
"description": "APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
"type": "string"
},
"kind": {
"description": "Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
"type": "string"
},
"metadata": {
"type": "object"
},
"spec": {
"description": "CloudflareTunnelSpec defines the desired state of a CloudflareTunnel resource.\n\nCloudflareTunnelSpec configures the tunnel identity, Cloudflare credentials, cloudflared\ndeployment settings, and origin connection defaults. The tunnel manages lifecycle only;\nDNS records are managed separately via CloudflareDNS resources.",
"properties": {
"cloudflare": {
"description": "Cloudflare defines the Cloudflare API credentials.",
"properties": {
"accountId": {
"description": "AccountID is the Cloudflare Account ID.",
"maxLength": 32,
"type": "string"
},
"accountName": {
"description": "AccountName is the Cloudflare Account name. Will be looked up via API.",
"maxLength": 255,
"type": "string"
},
"secretKeys": {
"description": "SecretKeys defines the key mappings within the secret.",
"properties": {
"apiToken": {
"default": "CLOUDFLARE_API_TOKEN",
"description": "APIToken is the key name for the Cloudflare API token.",
"maxLength": 253,
"type": "string"
}
},
"type": "object",
"additionalProperties": false
},
"secretRef": {
"description": "SecretRef references the Secret containing Cloudflare API credentials.\nThe secret must contain an API token (not tunnel token).",
"properties": {
"name": {
"description": "Name of the secret.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace of the secret. Defaults to the tunnel's namespace.",
"maxLength": 63,
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
}
},
"required": [
"secretRef"
],
"type": "object",
"x-kubernetes-validations": [
{
"message": "either accountId or accountName must be specified",
"rule": "has(self.accountId) || has(self.accountName)"
},
{
"message": "accountId must be a 32-character hex string",
"rule": "!has(self.accountId) || self.accountId.matches('^[a-f0-9]{32}$')"
}
],
"additionalProperties": false
},
"cloudflared": {
"description": "Cloudflared defines the cloudflared deployment configuration.",
"properties": {
"extraArgs": {
"description": "ExtraArgs are additional arguments to pass to cloudflared.",
"items": {
"type": "string"
},
"maxItems": 20,
"type": "array"
},
"image": {
"default": "ghcr.io/inherent-design/cloudflared:2026.5.0-h2c.1",
"description": "Image is the cloudflared container image.",
"maxLength": 255,
"type": "string"
},
"imagePullPolicy": {
"default": "IfNotPresent",
"description": "ImagePullPolicy is the pull policy for the cloudflared image.",
"enum": [
"Always",
"Never",
"IfNotPresent"
],
"type": "string"
},
"metrics": {
"description": "Metrics configures the cloudflared metrics endpoint.",
"properties": {
"enabled": {
"default": true,
"description": "Enabled enables the metrics endpoint.",
"type": "boolean"
},
"port": {
"default": 44483,
"description": "Port is the port for the metrics endpoint.",
"format": "int32",
"maximum": 65535,
"minimum": 1,
"type": "integer"
}
},
"type": "object",
"additionalProperties": false
},
"nodeSelector": {
"additionalProperties": {
"type": "string"
},
"description": "NodeSelector is a selector for nodes to run cloudflared on.",
"maxProperties": 50,
"type": "object"
},
"podAnnotations": {
"additionalProperties": {
"type": "string"
},
"description": "PodAnnotations are annotations to add to cloudflared pods.",
"maxProperties": 50,
"type": "object"
},
"protocol": {
"default": "auto",
"description": "Protocol is the tunnel transport protocol: auto, quic, http2.",
"enum": [
"auto",
"quic",
"http2"
],
"type": "string"
},
"replicas": {
"default": 2,
"description": "Replicas is the number of cloudflared replicas.",
"format": "int32",
"maximum": 10,
"minimum": 1,
"type": "integer"
},
"resources": {
"description": "Resources are the resource requirements for cloudflared containers.",
"properties": {
"claims": {
"description": "Claims lists the names of resources, defined in spec.resourceClaims,\nthat are used by this container.\n\nThis field depends on the\nDynamicResourceAllocation feature gate.\n\nThis field is immutable. It can only be set for containers.",
"items": {
"description": "ResourceClaim references one entry in PodSpec.ResourceClaims.",
"properties": {
"name": {
"description": "Name must match the name of one entry in pod.spec.resourceClaims of\nthe Pod where this field is used. It makes that resource available\ninside a container.",
"type": "string"
},
"request": {
"description": "Request is the name chosen for a request in the referenced claim.\nIf empty, everything from the claim is made available, otherwise\nonly the result of this request.",
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
},
"type": "array",
"x-kubernetes-list-map-keys": [
"name"
],
"x-kubernetes-list-type": "map"
},
"limits": {
"additionalProperties": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"x-kubernetes-int-or-string": true
},
"description": "Limits describes the maximum amount of compute resources allowed.\nMore info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
"type": "object"
},
"requests": {
"additionalProperties": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"x-kubernetes-int-or-string": true
},
"description": "Requests describes the minimum amount of compute resources required.\nIf Requests is omitted for a container, it defaults to Limits if that is explicitly specified,\notherwise to an implementation-defined value. Requests cannot exceed Limits.\nMore info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/",
"type": "object"
}
},
"type": "object",
"additionalProperties": false
},
"tolerations": {
"description": "Tolerations are tolerations for the cloudflared pods.",
"items": {
"description": "The pod this Toleration is attached to tolerates any taint that matches\nthe triple <key,value,effect> using the matching operator <operator>.",
"properties": {
"effect": {
"description": "Effect indicates the taint effect to match. Empty means match all taint effects.\nWhen specified, allowed values are NoSchedule, PreferNoSchedule and NoExecute.",
"type": "string"
},
"key": {
"description": "Key is the taint key that the toleration applies to. Empty means match all taint keys.\nIf the key is empty, operator must be Exists; this combination means to match all values and all keys.",
"type": "string"
},
"operator": {
"description": "Operator represents a key's relationship to the value.\nValid operators are Exists, Equal, Lt, and Gt. Defaults to Equal.\nExists is equivalent to wildcard for value, so that a pod can\ntolerate all taints of a particular category.\nLt and Gt perform numeric comparisons (requires feature gate TaintTolerationComparisonOperators).",
"type": "string"
},
"tolerationSeconds": {
"description": "TolerationSeconds represents the period of time the toleration (which must be\nof effect NoExecute, otherwise this field is ignored) tolerates the taint. By default,\nit is not set, which means tolerate the taint forever (do not evict). Zero and\nnegative values will be treated as 0 (evict immediately) by the system.",
"format": "int64",
"type": "integer"
},
"value": {
"description": "Value is the taint value the toleration matches to.\nIf the operator is Exists, the value should be empty, otherwise just a regular string.",
"type": "string"
}
},
"type": "object",
"additionalProperties": false
},
"maxItems": 20,
"type": "array"
}
},
"type": "object",
"additionalProperties": false
},
"fallbackCredentialsRef": {
"description": "FallbackCredentialsRef references a secret containing fallback Cloudflare API credentials.\nUsed during deletion when primary credentials (in Cloudflare.SecretRef) are unavailable.\nThis enables cleanup of Cloudflare resources even if the per-tunnel secret is deleted.\nThe secret must contain the same keys as the primary credentials secret.",
"properties": {
"name": {
"description": "Name of the secret.",
"maxLength": 253,
"minLength": 1,
"type": "string"
},
"namespace": {
"description": "Namespace of the secret. Defaults to the resource's namespace if empty.",
"maxLength": 63,
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
},
"fallbackTarget": {
"default": "http_status:404",
"description": "FallbackTarget is the service for unmatched requests.",
"maxLength": 255,
"type": "string"
},
"originDefaults": {
"description": "OriginDefaults defines default settings for origin connections.",
"properties": {
"caPoolSecretRef": {
"description": "CAPoolSecretRef references a Secret containing CA certificates for origin verification.",
"properties": {
"key": {
"default": "ca.crt",
"description": "Key is the key within the secret data.",
"maxLength": 253,
"type": "string"
},
"name": {
"description": "Name of the secret.",
"maxLength": 253,
"minLength": 1,
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
},
"connectTimeout": {
"default": "30s",
"description": "ConnectTimeout is the timeout for connecting to the origin.",
"pattern": "^[0-9]+(s|m|h)$",
"type": "string"
},
"h2cOrigin": {
"default": false,
"description": "H2cOrigin enables HTTP/2 cleartext (h2c) for origin connections.\nUse this for origins that speak HTTP/2 without TLS (e.g., gRPC services).\nMutually exclusive with http2Origin (TLS-based HTTP/2).",
"type": "boolean"
},
"http2Origin": {
"default": false,
"description": "HTTP2Origin enables HTTP/2 for origin connections.",
"type": "boolean"
},
"noTLSVerify": {
"default": false,
"description": "NoTLSVerify disables TLS verification for origin connections.",
"type": "boolean"
}
},
"type": "object",
"x-kubernetes-validations": [
{
"message": "http2Origin and h2cOrigin are mutually exclusive",
"rule": "!(self.http2Origin && self.h2cOrigin)"
}
],
"additionalProperties": false
},
"tunnel": {
"description": "Tunnel defines the tunnel identity configuration.",
"properties": {
"name": {
"description": "Name is the tunnel name in Cloudflare. If tunnel with this name exists, adopt it.\nIf not, create it. Tunnel ID is stored in status after resolution/creation.",
"maxLength": 63,
"minLength": 1,
"pattern": "^[a-z0-9]([-a-z0-9]*[a-z0-9])?$",
"type": "string"
}
},
"required": [
"name"
],
"type": "object",
"additionalProperties": false
}
},
"required": [
"cloudflare",
"tunnel"
],
"type": "object",
"additionalProperties": false
},
"status": {
"description": "CloudflareTunnelStatus defines the observed state of a CloudflareTunnel resource.\n\nCloudflareTunnelStatus captures the tunnel's Cloudflare-assigned identifiers, deployment\nstatus, and reconciliation state. The TunnelDomain field provides the CNAME target\n({tunnelId}.cfargotunnel.com) that CloudflareDNS uses for DNS record creation.",
"properties": {
"accountId": {
"description": "AccountID is the resolved Cloudflare account ID.",
"type": "string"
},
"conditions": {
"description": "Conditions represent the latest available observations of the tunnel's state.",
"items": {
"description": "Condition contains details for one aspect of the current state of this API Resource.",
"properties": {
"lastTransitionTime": {
"description": "lastTransitionTime is the last time the condition transitioned from one status to another.\nThis should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.",
"format": "date-time",
"type": "string"
},
"message": {
"description": "message is a human readable message indicating details about the transition.\nThis may be an empty string.",
"maxLength": 32768,
"type": "string"
},
"observedGeneration": {
"description": "observedGeneration represents the .metadata.generation that the condition was set based upon.\nFor instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date\nwith respect to the current state of the instance.",
"format": "int64",
"minimum": 0,
"type": "integer"
},
"reason": {
"description": "reason contains a programmatic identifier indicating the reason for the condition's last transition.\nProducers of specific condition types may define expected values and meanings for this field,\nand whether the values are considered a guaranteed API.\nThe value should be a CamelCase string.\nThis field may not be empty.",
"maxLength": 1024,
"minLength": 1,
"pattern": "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$",
"type": "string"
},
"status": {
"description": "status of the condition, one of True, False, Unknown.",
"enum": [
"True",
"False",
"Unknown"
],
"type": "string"
},
"type": {
"description": "type of condition in CamelCase or in foo.example.com/CamelCase.",
"maxLength": 316,
"pattern": "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$",
"type": "string"
}
},
"required": [
"lastTransitionTime",
"message",
"reason",
"status",
"type"
],
"type": "object",
"additionalProperties": false
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
},
"connectedRouteCount": {
"description": "ConnectedRouteCount is the number of routes connected to this tunnel.",
"format": "int32",
"type": "integer"
},
"lastSyncTime": {
"description": "LastSyncTime is the last time the configuration was synced to Cloudflare.",
"format": "date-time",
"type": "string"
},
"observedGeneration": {
"description": "ObservedGeneration is the generation observed by the controller.",
"format": "int64",
"type": "integer"
},
"readyReplicas": {
"description": "ReadyReplicas is the number of ready cloudflared replicas.",
"format": "int32",
"type": "integer"
},
"replicas": {
"description": "Replicas is the total number of cloudflared replicas.",
"format": "int32",
"type": "integer"
},
"tunnelDomain": {
"description": "TunnelDomain is the tunnel's CNAME target domain (e.g., {tunnelId}.cfargotunnel.com).",
"type": "string"
},
"tunnelId": {
"description": "TunnelID is the Cloudflare tunnel ID.",
"type": "string"
},
"tunnelName": {
"description": "TunnelName is the Cloudflare tunnel name.",
"type": "string"
}
},
"type": "object",
"additionalProperties": false
}
},
"type": "object"
}