Skip to content

Commit ee6ba8f

Browse files
authored
refactor: OIDC validation and defaulting (#157)
* fix oidc config validation * loosen restrictions on default oidc provider name and change default to 'openmcp' * validate oidc provider name uniqueness * adapt docs
1 parent 0b46b9f commit ee6ba8f

File tree

8 files changed

+198
-93
lines changed

8 files changed

+198
-93
lines changed

api/common/oidc_types.go

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,29 @@
11
package common
22

33
import (
4-
"strings"
5-
64
rbacv1 "k8s.io/api/rbac/v1"
75
)
86

97
type OIDCProviderConfig struct {
108
// Name is the name of the OIDC provider.
119
// May be used in k8s resources, therefore has to be a valid k8s name.
10+
// It is also used (with a ':' suffix) as prefix in k8s resources referencing users or groups from this OIDC provider.
11+
// E.g. if the name is 'example', the username 'alice' from this provider will be referenced as 'example:alice' in k8s resources.
12+
// Must be unique among all OIDC providers configured in the same environment.
1213
// +kubebuilder:validation:MinLength=1
1314
// +kubebuilder:validation:MaxLength=253
14-
// +kubebuilder:validation:Pattern=`[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*`
15+
// +kubebuilder:validation:Pattern=`^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$`
16+
// +kubebuilder:validation:XValidation:rule=`self != "system"`, message="'system' is a reserved string and may not be used as OIDC provider name"
1517
Name string `json:"name"`
1618

1719
// Issuer is the issuer URL of the OIDC provider.
20+
// Must be a valid URL.
21+
// +kubebuilder:validation:Pattern=`^https?://[^\s/$.?#].[^\s]*$`
22+
// +kubebuilder:validation:MinLength=1
1823
Issuer string `json:"issuer"`
1924

2025
// ClientID is the client ID to use for the OIDC provider.
26+
// +kubebuilder:validation:MinLength=1
2127
ClientID string `json:"clientID"`
2228

2329
// GroupsClaim is the claim in the OIDC token that contains the groups.
@@ -26,24 +32,12 @@ type OIDCProviderConfig struct {
2632
// +optional
2733
GroupsClaim string `json:"groupsClaim"`
2834

29-
// GroupsPrefix is a prefix that will be added to all group names when referenced in RBAC rules.
30-
// This is required to avoid conflicts with Kubernetes built-in groups.
31-
// If the prefix does not end with a colon (:), it will be added automatically.
32-
// +kubebuilder:validation:MinLength=1
33-
GroupsPrefix string `json:"groupsPrefix"`
34-
3535
// UsernameClaim is the claim in the OIDC token that contains the username.
3636
// If empty, the default claim "sub" will be used.
3737
// +kubebuilder:default="sub"
3838
// +optional
3939
UsernameClaim string `json:"usernameClaim"`
4040

41-
// UsernamePrefix is a prefix that will be added to all usernames when referenced in RBAC rules.
42-
// This is required to avoid conflicts with Kubernetes built-in users.
43-
// If the prefix does not end with a colon (:), it will be added automatically.
44-
// +kubebuilder:validation:MinLength=1
45-
UsernamePrefix string `json:"usernamePrefix"`
46-
4741
// ExtraScopes is a list of extra scopes that should be requested from the OIDC provider.
4842
// +optional
4943
ExtraScopes []string `json:"extraScopes,omitempty"`
@@ -90,14 +84,14 @@ func (o *OIDCProviderConfig) Default() *OIDCProviderConfig {
9084
if o.GroupsClaim == "" {
9185
o.GroupsClaim = "groups"
9286
}
93-
if !strings.HasSuffix(o.GroupsPrefix, ":") {
94-
o.GroupsPrefix += ":"
95-
}
9687
if o.UsernameClaim == "" {
9788
o.UsernameClaim = "sub"
9889
}
99-
if !strings.HasSuffix(o.UsernamePrefix, ":") {
100-
o.UsernamePrefix += ":"
101-
}
10290
return o
10391
}
92+
93+
// UsernameGroupsPrefix returns the prefix for usernames and groups for this OIDC provider.
94+
// It is equivalent to <provider_name> + ":".
95+
func (o *OIDCProviderConfig) UsernameGroupsPrefix() string {
96+
return o.Name + ":"
97+
}

api/core/v2alpha1/constants.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ package v2alpha1
22

33
const (
44
// DefaultOIDCProviderName is the identifier for the default OIDC provider.
5-
DefaultOIDCProviderName = "default"
5+
DefaultOIDCProviderName = "openmcp"
66
// DefaultMCPClusterPurpose is the default purpose for ManagedControlPlane clusters.
77
DefaultMCPClusterPurpose = "mcp"
88
)
@@ -15,6 +15,8 @@ const (
1515

1616
// ManagedPurposeMCPPurposeOverride is used as value for the managed purpose label. It must not be modified.
1717
ManagedPurposeMCPPurposeOverride = "mcp-purpose-override"
18+
// ManagedPurposeOIDCProviderNameUniqueness is used as value for the managed purpose label. It must not be modified.
19+
ManagedPurposeOIDCProviderNameUniqueness = "oidc-provider-name-uniqueness"
1820

1921
MCPFinalizer = GroupName + "/mcp"
2022

api/core/v2alpha1/managedcontrolplane_types.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ type IAMConfig struct {
3131

3232
// OIDCProviders is a list of OIDC providers that should be configured for the ManagedControlPlaneV2.
3333
// They are independent of the standard OIDC provider and in addition to it, unless it has been disabled by not specifying any role bindings.
34-
// +kubebuilder:validation:items:XValidation:rule="self.name != 'default'", message="OIDC provider name must not be 'default' as this is reserved for the standard OIDC provider"
3534
// +optional
3635
OIDCProviders []*commonapi.OIDCProviderConfig `json:"oidcProviders,omitempty"`
3736
}

api/crds/manifests/clusters.openmcp.cloud_accessrequests.yaml

Lines changed: 13 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ spec:
7373
properties:
7474
clientID:
7575
description: ClientID is the client ID to use for the OIDC provider.
76+
minLength: 1
7677
type: string
7778
extraScopes:
7879
description: ExtraScopes is a list of extra scopes that should
@@ -86,24 +87,28 @@ spec:
8687
GroupsClaim is the claim in the OIDC token that contains the groups.
8788
If empty, the default claim "groups" will be used.
8889
type: string
89-
groupsPrefix:
90+
issuer:
9091
description: |-
91-
GroupsPrefix is a prefix that will be added to all group names when referenced in RBAC rules.
92-
This is required to avoid conflicts with Kubernetes built-in groups.
93-
If the prefix does not end with a colon (:), it will be added automatically.
92+
Issuer is the issuer URL of the OIDC provider.
93+
Must be a valid URL.
9494
minLength: 1
95-
type: string
96-
issuer:
97-
description: Issuer is the issuer URL of the OIDC provider.
95+
pattern: ^https?://[^\s/$.?#].[^\s]*$
9896
type: string
9997
name:
10098
description: |-
10199
Name is the name of the OIDC provider.
102100
May be used in k8s resources, therefore has to be a valid k8s name.
101+
It is also used (with a ':' suffix) as prefix in k8s resources referencing users or groups from this OIDC provider.
102+
E.g. if the name is 'example', the username 'alice' from this provider will be referenced as 'example:alice' in k8s resources.
103+
Must be unique among all OIDC providers configured in the same environment.
103104
maxLength: 253
104105
minLength: 1
105-
pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*'
106+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
106107
type: string
108+
x-kubernetes-validations:
109+
- message: '''system'' is a reserved string and may not be used
110+
as OIDC provider name'
111+
rule: self != "system"
107112
roleBindings:
108113
description: |-
109114
RoleBindings is a list of subjects with (cluster) role bindings that should be created for them.
@@ -260,20 +265,11 @@ spec:
260265
UsernameClaim is the claim in the OIDC token that contains the username.
261266
If empty, the default claim "sub" will be used.
262267
type: string
263-
usernamePrefix:
264-
description: |-
265-
UsernamePrefix is a prefix that will be added to all usernames when referenced in RBAC rules.
266-
This is required to avoid conflicts with Kubernetes built-in users.
267-
If the prefix does not end with a colon (:), it will be added automatically.
268-
minLength: 1
269-
type: string
270268
required:
271269
- clientID
272-
- groupsPrefix
273270
- issuer
274271
- name
275272
- roleBindings
276-
- usernamePrefix
277273
type: object
278274
requestRef:
279275
description: |-

api/crds/manifests/core.openmcp.cloud_managedcontrolplanev2s.yaml

Lines changed: 13 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ spec:
5858
clientID:
5959
description: ClientID is the client ID to use for the OIDC
6060
provider.
61+
minLength: 1
6162
type: string
6263
extraScopes:
6364
description: ExtraScopes is a list of extra scopes that
@@ -71,24 +72,28 @@ spec:
7172
GroupsClaim is the claim in the OIDC token that contains the groups.
7273
If empty, the default claim "groups" will be used.
7374
type: string
74-
groupsPrefix:
75+
issuer:
7576
description: |-
76-
GroupsPrefix is a prefix that will be added to all group names when referenced in RBAC rules.
77-
This is required to avoid conflicts with Kubernetes built-in groups.
78-
If the prefix does not end with a colon (:), it will be added automatically.
77+
Issuer is the issuer URL of the OIDC provider.
78+
Must be a valid URL.
7979
minLength: 1
80-
type: string
81-
issuer:
82-
description: Issuer is the issuer URL of the OIDC provider.
80+
pattern: ^https?://[^\s/$.?#].[^\s]*$
8381
type: string
8482
name:
8583
description: |-
8684
Name is the name of the OIDC provider.
8785
May be used in k8s resources, therefore has to be a valid k8s name.
86+
It is also used (with a ':' suffix) as prefix in k8s resources referencing users or groups from this OIDC provider.
87+
E.g. if the name is 'example', the username 'alice' from this provider will be referenced as 'example:alice' in k8s resources.
88+
Must be unique among all OIDC providers configured in the same environment.
8889
maxLength: 253
8990
minLength: 1
90-
pattern: '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*'
91+
pattern: ^[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$
9192
type: string
93+
x-kubernetes-validations:
94+
- message: '''system'' is a reserved string and may not
95+
be used as OIDC provider name'
96+
rule: self != "system"
9297
roleBindings:
9398
description: |-
9499
RoleBindings is a list of subjects with (cluster) role bindings that should be created for them.
@@ -171,25 +176,12 @@ spec:
171176
UsernameClaim is the claim in the OIDC token that contains the username.
172177
If empty, the default claim "sub" will be used.
173178
type: string
174-
usernamePrefix:
175-
description: |-
176-
UsernamePrefix is a prefix that will be added to all usernames when referenced in RBAC rules.
177-
This is required to avoid conflicts with Kubernetes built-in users.
178-
If the prefix does not end with a colon (:), it will be added automatically.
179-
minLength: 1
180-
type: string
181179
required:
182180
- clientID
183-
- groupsPrefix
184181
- issuer
185182
- name
186183
- roleBindings
187-
- usernamePrefix
188184
type: object
189-
x-kubernetes-validations:
190-
- message: OIDC provider name must not be 'default' as this
191-
is reserved for the standard OIDC provider
192-
rule: self.name != 'default'
193185
type: array
194186
roleBindings:
195187
description: |-

0 commit comments

Comments
 (0)