Skip to content

Commit 129b3d8

Browse files
committed
chore: bump version to 0.38.0
1 parent 22aba04 commit 129b3d8

26 files changed

Lines changed: 3335 additions & 469 deletions

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.38.0] - 2026-01-27
11+
12+
### Breaking Changes
13+
- **AccessApplication**: Remove `allowedIdpRefs` field, use `identityProviderRefs` instead
14+
- **AccessApplication**: Remove `destinations[].vnetId` field, use `destinations[].vnetRef` instead
15+
- **AccessGroup**: Remove `identityProviderId` from all rule types (GSuite, GitHub, Azure, Okta, OIDC, SAML, AuthContext, LoginMethod), use `idpRef` instead
16+
- **AccessServiceToken**: Remove `secretRef.namespace` field, Secret is automatically created in the resource namespace
17+
18+
### Added
19+
- **PagesDomain**: Implement `autoConfigureDNS` feature for automatic CNAME record creation
20+
- **PagesDomain**: Extract and expose `validationMethod` and `validationStatus` in Status
21+
22+
### Fixed
23+
- **PagesDomain**: Fix API type conversion to properly extract validation data from Cloudflare response
24+
1025
## [0.27.6] - 2026-01-20
1126

1227
### Changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
# To re-generate a bundle for another specific version without changing the standard setup, you can:
44
# - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2)
55
# - use environment variables to overwrite this value (e.g export VERSION=0.0.2)
6-
VERSION ?= 0.37.0
6+
VERSION ?= 0.38.0
77

88
# CHANNELS define the bundle channels used in the bundle.
99
# Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable")

api/v1alpha2/accessapplication_types.go

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,13 @@ type AccessApplicationSpec struct {
5656
// +kubebuilder:validation:Optional
5757
AllowedIdps []string `json:"allowedIdps,omitempty"`
5858

59-
// AllowedIdpRefs references AccessIdentityProvider resources by name.
59+
// IdentityProviderRefs references AccessIdentityProvider resources with flexible reference modes.
60+
// Each reference can be:
61+
// - K8s AccessIdentityProvider name (via name field)
62+
// - Cloudflare IdP UUID (via cloudflareId field)
63+
// - Cloudflare IdP display name (via cloudflareName field)
6064
// +kubebuilder:validation:Optional
61-
AllowedIdpRefs []AccessIdentityProviderRef `json:"allowedIdpRefs,omitempty"`
65+
IdentityProviderRefs []AccessIdentityProviderRefV2 `json:"identityProviderRefs,omitempty"`
6266

6367
// AutoRedirectToIdentity enables automatic redirect to the identity provider.
6468
// +kubebuilder:validation:Optional
@@ -171,6 +175,15 @@ type AccessApplicationSpec struct {
171175
// +kubebuilder:validation:Optional
172176
ReusablePolicyRefs []ReusablePolicyRef `json:"reusablePolicyRefs,omitempty"`
173177

178+
// ReusableGroupRefs references AccessGroup resources for SCIM group inclusion rules.
179+
// These groups can be used in Include rules for access control.
180+
// Each reference can be:
181+
// - K8s AccessGroup name (via name field)
182+
// - Cloudflare Group UUID (via cloudflareId field)
183+
// - Cloudflare Group display name (via cloudflareName field)
184+
// +kubebuilder:validation:Optional
185+
ReusableGroupRefs []ReusableGroupRef `json:"reusableGroupRefs,omitempty"`
186+
174187
// Cloudflare contains the Cloudflare API credentials and account information.
175188
// +kubebuilder:validation:Required
176189
Cloudflare CloudflareDetails `json:"cloudflare"`
@@ -239,9 +252,10 @@ type AccessDestination struct {
239252
// +kubebuilder:validation:Enum=tcp;udp
240253
L4Protocol string `json:"l4Protocol,omitempty"`
241254

242-
// VnetID is the Virtual Network ID for private destinations.
255+
// VnetRef references a VirtualNetwork with flexible reference modes.
256+
// Can reference by K8s name, Cloudflare UUID, or Cloudflare display name.
243257
// +kubebuilder:validation:Optional
244-
VnetID string `json:"vnetId,omitempty"`
258+
VnetRef *VirtualNetworkRef `json:"vnetRef,omitempty"`
245259
}
246260

247261
// AccessApplicationCorsHeaders represents CORS settings for an Access Application.
@@ -645,13 +659,6 @@ type AccessInfrastructureTargetContext struct {
645659
Protocol string `json:"protocol"`
646660
}
647661

648-
// AccessIdentityProviderRef references an AccessIdentityProvider resource.
649-
type AccessIdentityProviderRef struct {
650-
// Name is the name of the AccessIdentityProvider resource.
651-
// +kubebuilder:validation:Required
652-
Name string `json:"name"`
653-
}
654-
655662
// AccessPolicyRef references an access policy or defines an inline policy.
656663
// You can either:
657664
// 1. Reference an AccessGroup using name, groupId, or cloudflareGroupName (simple mode)
@@ -843,7 +850,7 @@ type AccessApplicationStatus struct {
843850

844851
// +kubebuilder:object:root=true
845852
// +kubebuilder:subresource:status
846-
// +kubebuilder:resource:scope=Cluster,shortName=accessapp
853+
// +kubebuilder:resource:scope=Namespaced,shortName=accessapp
847854
// +kubebuilder:printcolumn:name="Domain",type=string,JSONPath=`.spec.domain`
848855
// +kubebuilder:printcolumn:name="Type",type=string,JSONPath=`.spec.type`
849856
// +kubebuilder:printcolumn:name="AppID",type=string,JSONPath=`.status.applicationId`

api/v1alpha2/accessgroup_types.go

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -185,29 +185,44 @@ type AccessGroupDevicePostureRule struct {
185185

186186
// AccessGroupGSuiteRule matches Google Workspace users.
187187
type AccessGroupGSuiteRule struct {
188-
Email string `json:"email"`
189-
IdentityProviderID string `json:"identityProviderId"`
188+
Email string `json:"email"`
189+
190+
// IdpRef references an AccessIdentityProvider with flexible reference modes.
191+
// Can reference by K8s name, Cloudflare UUID, or Cloudflare display name.
192+
// +kubebuilder:validation:Optional
193+
IdpRef *AccessIdentityProviderRefV2 `json:"idpRef,omitempty"`
190194
}
191195

192196
// AccessGroupGitHubRule matches GitHub users.
193197
type AccessGroupGitHubRule struct {
194-
Name string `json:"name"`
195-
IdentityProviderID string `json:"identityProviderId"`
196-
Teams []string `json:"teams,omitempty"`
198+
Name string `json:"name"`
199+
Teams []string `json:"teams,omitempty"`
200+
201+
// IdpRef references an AccessIdentityProvider with flexible reference modes.
202+
// Can reference by K8s name, Cloudflare UUID, or Cloudflare display name.
203+
// +kubebuilder:validation:Optional
204+
IdpRef *AccessIdentityProviderRefV2 `json:"idpRef,omitempty"`
197205
}
198206

199207
// AccessGroupAzureRule matches Azure AD users.
200208
type AccessGroupAzureRule struct {
201-
ID string `json:"id"`
202-
IdentityProviderID string `json:"identityProviderId"`
209+
ID string `json:"id"`
210+
211+
// IdpRef references an AccessIdentityProvider with flexible reference modes.
212+
// Can reference by K8s name, Cloudflare UUID, or Cloudflare display name.
213+
// +kubebuilder:validation:Optional
214+
IdpRef *AccessIdentityProviderRefV2 `json:"idpRef,omitempty"`
203215
}
204216

205217
// AccessGroupOktaRule matches Okta groups.
206218
type AccessGroupOktaRule struct {
207219
// Name is the Okta group name.
208220
Name string `json:"name"`
209-
// IdentityProviderID is the Cloudflare ID of the Okta identity provider.
210-
IdentityProviderID string `json:"identityProviderId"`
221+
222+
// IdpRef references an AccessIdentityProvider with flexible reference modes.
223+
// Can reference by K8s name, Cloudflare UUID, or Cloudflare display name.
224+
// +kubebuilder:validation:Optional
225+
IdpRef *AccessIdentityProviderRefV2 `json:"idpRef,omitempty"`
211226
}
212227

213228
// AccessGroupAuthMethodRule enforces MFA options.
@@ -222,28 +237,41 @@ type AccessGroupAuthContextRule struct {
222237
ID string `json:"id"`
223238
// AcID is the Azure AD Conditional Access Policy ID.
224239
AcID string `json:"acId"`
225-
// IdentityProviderID is the Cloudflare ID of the Azure identity provider.
226-
IdentityProviderID string `json:"identityProviderId"`
240+
241+
// IdpRef references an AccessIdentityProvider with flexible reference modes.
242+
// Can reference by K8s name, Cloudflare UUID, or Cloudflare display name.
243+
// +kubebuilder:validation:Optional
244+
IdpRef *AccessIdentityProviderRefV2 `json:"idpRef,omitempty"`
227245
}
228246

229247
// AccessGroupLoginMethodRule matches a specific identity provider.
230248
type AccessGroupLoginMethodRule struct {
231-
// ID is the Cloudflare ID of the identity provider.
232-
ID string `json:"id"`
249+
// IdpRef references an AccessIdentityProvider with flexible reference modes.
250+
// Can reference by K8s name, Cloudflare UUID, or Cloudflare display name.
251+
// +kubebuilder:validation:Optional
252+
IdpRef *AccessIdentityProviderRefV2 `json:"idpRef,omitempty"`
233253
}
234254

235255
// AccessGroupOIDCRule matches OIDC claims.
236256
type AccessGroupOIDCRule struct {
237-
ClaimName string `json:"claimName"`
238-
ClaimValue string `json:"claimValue"`
239-
IdentityProviderID string `json:"identityProviderId"`
257+
ClaimName string `json:"claimName"`
258+
ClaimValue string `json:"claimValue"`
259+
260+
// IdpRef references an AccessIdentityProvider with flexible reference modes.
261+
// Can reference by K8s name, Cloudflare UUID, or Cloudflare display name.
262+
// +kubebuilder:validation:Optional
263+
IdpRef *AccessIdentityProviderRefV2 `json:"idpRef,omitempty"`
240264
}
241265

242266
// AccessGroupSAMLRule matches SAML attributes.
243267
type AccessGroupSAMLRule struct {
244-
AttributeName string `json:"attributeName"`
245-
AttributeValue string `json:"attributeValue"`
246-
IdentityProviderID string `json:"identityProviderId"`
268+
AttributeName string `json:"attributeName"`
269+
AttributeValue string `json:"attributeValue"`
270+
271+
// IdpRef references an AccessIdentityProvider with flexible reference modes.
272+
// Can reference by K8s name, Cloudflare UUID, or Cloudflare display name.
273+
// +kubebuilder:validation:Optional
274+
IdpRef *AccessIdentityProviderRefV2 `json:"idpRef,omitempty"`
247275
}
248276

249277
// AccessGroupExternalEvaluationRule calls external endpoint.

api/v1alpha2/accessidentityprovider_types.go

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,6 @@ type IdentityProviderConfig struct {
8989
// +kubebuilder:validation:Optional
9090
IdPPublicCert string `json:"idpPublicCert,omitempty"`
9191

92-
// IdPPublicCerts are the IdP's public certificates for SAML (multiple certs).
93-
//
94-
// Deprecated: Use IdPPublicCert instead.
95-
// +kubebuilder:validation:Optional
96-
IdPPublicCerts []string `json:"idpPublicCerts,omitempty"`
97-
9892
// IssuerURL is the OIDC issuer URL.
9993
// +kubebuilder:validation:Optional
10094
IssuerURL string `json:"issuerUrl,omitempty"`

api/v1alpha2/accessservicetoken_types.go

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,10 @@ type AccessServiceTokenSpec struct {
3131
// ServiceTokenSecretRef defines where to store token credentials.
3232
type ServiceTokenSecretRef struct {
3333
// Name is the name of the Secret to create/update.
34+
// The Secret will be created in the same namespace as the AccessServiceToken resource.
3435
// +kubebuilder:validation:Required
3536
Name string `json:"name"`
3637

37-
// Namespace is the namespace for the Secret.
38-
// +kubebuilder:validation:Required
39-
Namespace string `json:"namespace"`
40-
4138
// ClientIDKey is the key for the Client ID.
4239
// +kubebuilder:validation:Optional
4340
// +kubebuilder:default="CF_ACCESS_CLIENT_ID"
@@ -104,7 +101,7 @@ type AccessServiceTokenStatus struct {
104101

105102
// +kubebuilder:object:root=true
106103
// +kubebuilder:subresource:status
107-
// +kubebuilder:resource:scope=Cluster,shortName=accesstoken
104+
// +kubebuilder:resource:scope=Namespaced,shortName=accesstoken
108105
// +kubebuilder:printcolumn:name="TokenID",type=string,JSONPath=`.status.tokenId`
109106
// +kubebuilder:printcolumn:name="ClientID",type=string,JSONPath=`.status.clientId`
110107
// +kubebuilder:printcolumn:name="ExpiresAt",type=string,JSONPath=`.status.expiresAt`

api/v1alpha2/cloudflare_refs.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// SPDX-License-Identifier: Apache-2.0
2+
// Copyright 2025-2026 The Cloudflare Operator Authors
3+
4+
package v1alpha2
5+
6+
// ============================================================================
7+
// Unified Reference Types
8+
// ============================================================================
9+
// These types provide a consistent way to reference Cloudflare resources.
10+
// All reference types support three modes:
11+
// 1. K8s resource name - References a K8s CRD that manages the Cloudflare resource
12+
// 2. cloudflareId - Direct UUID reference to an existing Cloudflare resource
13+
// 3. cloudflareName - Display name lookup via Cloudflare API
14+
//
15+
// CEL validation ensures exactly one of the three fields is set.
16+
17+
// AccessIdentityProviderRefV2 references an AccessIdentityProvider.
18+
// Supports K8s name, Cloudflare UUID, or Cloudflare display name.
19+
//
20+
// +kubebuilder:validation:XValidation:rule="(has(self.name) ? 1 : 0) + (has(self.cloudflareId) ? 1 : 0) + (has(self.cloudflareName) ? 1 : 0) == 1",message="exactly one of name, cloudflareId, or cloudflareName must be set"
21+
type AccessIdentityProviderRefV2 struct {
22+
// Name is the K8s AccessIdentityProvider resource name.
23+
// The controller will look up the CRD and use its status.providerID.
24+
// +kubebuilder:validation:Optional
25+
// +kubebuilder:validation:MaxLength=253
26+
Name string `json:"name,omitempty"`
27+
28+
// CloudflareID is the Cloudflare IdP UUID.
29+
// Use this to directly reference a Cloudflare-managed IdP
30+
// without creating a corresponding K8s AccessIdentityProvider resource.
31+
// +kubebuilder:validation:Optional
32+
// +kubebuilder:validation:Pattern=`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`
33+
CloudflareID string `json:"cloudflareId,omitempty"`
34+
35+
// CloudflareName is the display name of the IdP in Cloudflare.
36+
// The controller will resolve this name to an ID via the Cloudflare API.
37+
// Use this when you want to reference an IdP by name
38+
// (e.g., IdPs created via Terraform or the Cloudflare dashboard).
39+
// +kubebuilder:validation:Optional
40+
// +kubebuilder:validation:MaxLength=255
41+
CloudflareName string `json:"cloudflareName,omitempty"`
42+
}
43+
44+
// ReusableGroupRef references an AccessGroup.
45+
// Supports K8s name, Cloudflare UUID, or Cloudflare display name.
46+
//
47+
// +kubebuilder:validation:XValidation:rule="(has(self.name) ? 1 : 0) + (has(self.cloudflareId) ? 1 : 0) + (has(self.cloudflareName) ? 1 : 0) == 1",message="exactly one of name, cloudflareId, or cloudflareName must be set"
48+
type ReusableGroupRef struct {
49+
// Name is the K8s AccessGroup resource name.
50+
// The controller will look up the CRD and use its status.groupId.
51+
// +kubebuilder:validation:Optional
52+
// +kubebuilder:validation:MaxLength=253
53+
Name string `json:"name,omitempty"`
54+
55+
// CloudflareID is the Cloudflare Group UUID.
56+
// Use this to directly reference a Cloudflare-managed group
57+
// without creating a corresponding K8s AccessGroup resource.
58+
// +kubebuilder:validation:Optional
59+
// +kubebuilder:validation:Pattern=`^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$`
60+
CloudflareID string `json:"cloudflareId,omitempty"`
61+
62+
// CloudflareName is the display name of the group in Cloudflare.
63+
// The controller will resolve this name to an ID via the Cloudflare API.
64+
// +kubebuilder:validation:Optional
65+
// +kubebuilder:validation:MaxLength=255
66+
CloudflareName string `json:"cloudflareName,omitempty"`
67+
}
68+
69+
// VirtualNetworkRef references a VirtualNetwork.
70+
// Supports K8s name, Cloudflare UUID, or Cloudflare display name.
71+
//
72+
// +kubebuilder:validation:XValidation:rule="(has(self.name) ? 1 : 0) + (has(self.cloudflareId) ? 1 : 0) + (has(self.cloudflareName) ? 1 : 0) == 1",message="exactly one of name, cloudflareId, or cloudflareName must be set"
73+
type VirtualNetworkRef struct {
74+
// Name is the K8s VirtualNetwork resource name.
75+
// The controller will look up the CRD and use its status.virtualNetworkId.
76+
// +kubebuilder:validation:Optional
77+
// +kubebuilder:validation:MaxLength=253
78+
Name string `json:"name,omitempty"`
79+
80+
// CloudflareID is the Cloudflare VNet UUID.
81+
// Use this to directly reference a Cloudflare-managed VNet
82+
// without creating a corresponding K8s VirtualNetwork resource.
83+
// +kubebuilder:validation:Optional
84+
CloudflareID string `json:"cloudflareId,omitempty"`
85+
86+
// CloudflareName is the display name of the VNet in Cloudflare.
87+
// The controller will resolve this name to an ID via the Cloudflare API.
88+
// +kubebuilder:validation:Optional
89+
// +kubebuilder:validation:MaxLength=255
90+
CloudflareName string `json:"cloudflareName,omitempty"`
91+
}

api/v1alpha2/networkroute_types.go

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -51,13 +51,6 @@ type TunnelRef struct {
5151
Namespace string `json:"namespace,omitempty"`
5252
}
5353

54-
// VirtualNetworkRef references a VirtualNetwork resource.
55-
type VirtualNetworkRef struct {
56-
// Name is the name of the VirtualNetwork resource.
57-
// +kubebuilder:validation:Required
58-
Name string `json:"name"`
59-
}
60-
6154
// NetworkRouteStatus defines the observed state of NetworkRoute
6255
type NetworkRouteStatus struct {
6356
// Network is the CIDR from the route in Cloudflare.

api/v1alpha2/types_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,9 +291,9 @@ func TestAccessGroupTypes(t *testing.T) {
291291
t.Run("AccessGroupRule GitHub", func(t *testing.T) {
292292
rule := AccessGroupRule{
293293
GitHub: &AccessGroupGitHubRule{
294-
Name: "my-org",
295-
Teams: []string{"team-a", "team-b"},
296-
IdentityProviderID: "idp-github",
294+
Name: "my-org",
295+
Teams: []string{"team-a", "team-b"},
296+
IdpRef: &AccessIdentityProviderRefV2{CloudflareID: "idp-github"},
297297
},
298298
}
299299
require.NotNil(t, rule.GitHub)
@@ -304,8 +304,8 @@ func TestAccessGroupTypes(t *testing.T) {
304304
t.Run("AccessGroupRule Azure AD", func(t *testing.T) {
305305
rule := AccessGroupRule{
306306
Azure: &AccessGroupAzureRule{
307-
ID: "azure-group-id",
308-
IdentityProviderID: "idp-azure",
307+
ID: "azure-group-id",
308+
IdpRef: &AccessIdentityProviderRefV2{CloudflareID: "idp-azure"},
309309
},
310310
}
311311
require.NotNil(t, rule.Azure)
@@ -315,9 +315,9 @@ func TestAccessGroupTypes(t *testing.T) {
315315
t.Run("AccessGroupRule SAML", func(t *testing.T) {
316316
rule := AccessGroupRule{
317317
SAML: &AccessGroupSAMLRule{
318-
AttributeName: "department",
319-
AttributeValue: "engineering",
320-
IdentityProviderID: "idp-saml",
318+
AttributeName: "department",
319+
AttributeValue: "engineering",
320+
IdpRef: &AccessIdentityProviderRefV2{CloudflareID: "idp-saml"},
321321
},
322322
}
323323
require.NotNil(t, rule.SAML)

0 commit comments

Comments
 (0)