Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion api/clusters/v1alpha1/accessrequest_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,16 @@ type AccessRequestSpec struct {
RequestRef *commonapi.ObjectReference `json:"requestRef,omitempty"`

// Permissions are the requested permissions.
Permissions []PermissionsRequest `json:"permissions"`
// If not empty, corresponding Roles and ClusterRoles will be created in the target cluster, potentially also creating namespaces for Roles.
// For token-based access, the serviceaccount will be bound to the created Roles and ClusterRoles.
// +optional
Permissions []PermissionsRequest `json:"permissions,omitempty"`

// OIDCProvider is a configuration for an OIDC provider that should be used for authentication and associated role bindings.
// If set, the handling ClusterProvider will create an OIDC-based access for the AccessRequest, if supported.
// Otherwise, a serviceaccount with a token will be created and bound to the requested permissions.
// +optional
OIDCProvider *commonapi.OIDCProviderConfig `json:"oidcProvider,omitempty"`
}

type PermissionsRequest struct {
Expand Down
11 changes: 11 additions & 0 deletions api/clusters/v1alpha1/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,14 @@ const (
// RequestFinalizerOnClusterPrefix is the prefix for the finalizers that mark a Cluster as being referenced by a ClusterRequest.
RequestFinalizerOnClusterPrefix = "request." + GroupName + "/"
)

const (
// SecretKeyKubeconfig is the name of the key in the AccessRequest secret that contains the kubeconfig.
SecretKeyKubeconfig = "kubeconfig"
// SecretKeyExpirationTimestamp is the name of the key in the AccessRequest secret that contains the expiration timestamp.
// This value is optional and must not be set for non-expiring authentication methods.
SecretKeyExpirationTimestamp = "expirationTimestamp"
// SecretKeyCreationTimestamp is the name of the key in the AccessRequest secret that contains the creation timestamp.
// This value is optional and must not be set for non-expiring authentication methods.
SecretKeyCreationTimestamp = "creationTimestamp"
)
5 changes: 5 additions & 0 deletions api/clusters/v1alpha1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions api/common/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// +kubebuilder:object:generate=true
package common
99 changes: 99 additions & 0 deletions api/common/oidc_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
package common

import (
"strings"

rbacv1 "k8s.io/api/rbac/v1"
)

type OIDCProviderConfig struct {
// Name is the name of the OIDC provider.
// May be used in k8s resources, therefore has to be a valid k8s name.
// +kubebuilder:validation:MinLength=1
// +kubebuilder:validation:MaxLength=253
// +kubebuilder:validation:Pattern=`[a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*`
Name string `json:"name"`

// Issuer is the issuer URL of the OIDC provider.
Issuer string `json:"issuer"`

// ClientID is the client ID to use for the OIDC provider.
ClientID string `json:"clientID"`

// GroupsClaim is the claim in the OIDC token that contains the groups.
// If empty, the default claim "groups" will be used.
// +kubebuilder:default="groups"
// +optional
GroupsClaim string `json:"groupsClaim"`

// GroupsPrefix is a prefix that will be added to all group names when referenced in RBAC rules.
// This is required to avoid conflicts with Kubernetes built-in groups.
// If the prefix does not end with a colon (:), it will be added automatically.
// +kubebuilder:validation:MinLength=1
GroupsPrefix string `json:"groupsPrefix"`

// UsernameClaim is the claim in the OIDC token that contains the username.
// If empty, the default claim "sub" will be used.
// +kubebuilder:default="sub"
// +optional
UsernameClaim string `json:"usernameClaim"`

// UsernamePrefix is a prefix that will be added to all usernames when referenced in RBAC rules.
// This is required to avoid conflicts with Kubernetes built-in users.
// If the prefix does not end with a colon (:), it will be added automatically.
// +kubebuilder:validation:MinLength=1
UsernamePrefix string `json:"usernamePrefix"`

// RoleBindings is a list of subjects with (cluster) role bindings that should be created for them.
// Note that the username prefix is added automatically to the subjects' names, it must not be explicitly specified here.
RoleBindings []RoleBindings `json:"roleBindings"`
}

type RoleBindings struct {
// Subjects is a list of subjects that should be bound to the specified roles.
// The subjects' names will be prefixed with the username prefix of the OIDC provider.
Subjects []rbacv1.Subject `json:"subjects"`

// RoleRefs is a list of (cluster) role references that the subjects should be bound to.
// Note that existence of the roles is not checked and missing (cluster) roles will result in ineffective (cluster) role bindings.
RoleRefs []RoleRef `json:"roleRefs"`
}

// +kubebuilder:validation:XValidation:rule="self.kind == 'Role' && has(self.namespace) && self.namespace != ”", message="namespace must be set if kind is 'Role'"
// +kubebuilder:validation:XValidation:rule="self.kind == 'ClusterRole' && (!has(self.namespace) || self.namespace == ”)", message="namespace must not be set if kind is 'ClusterRole'"
type RoleRef struct {
// Name is the name of the role or cluster role to bind to the subjects.
// +kubebuilder:validation:MinLength=1
Name string `json:"name"`

// Namespace is the namespace of the role to bind to the subjects.
// It must be set if the kind is 'Role' and may not be set if the kind is 'ClusterRole'.
// +optional
Namespace string `json:"namespace,omitempty"`

// Kind is the kind of the role to bind to the subjects.
// It must be 'Role' or 'ClusterRole'.
// +kubebuilder:validation:Enum=Role;ClusterRole
Kind string `json:"kind"`
}

// Default sets default values for the OIDCProviderConfig.
// Modifies in-place and returns the receiver for chaining.
func (o *OIDCProviderConfig) Default() *OIDCProviderConfig {
if o == nil {
return nil
}
if o.GroupsClaim == "" {
o.GroupsClaim = "groups"
}
if !strings.HasSuffix(o.GroupsPrefix, ":") {
o.GroupsPrefix += ":"
}
if o.UsernameClaim == "" {
o.UsernameClaim = "sub"
}
if !strings.HasSuffix(o.UsernamePrefix, ":") {
o.UsernamePrefix += ":"
}
return o
}
2 changes: 0 additions & 2 deletions api/common/status_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ const (
StatusPhaseTerminating = "Terminating"
)

// +kubebuilder:object:generate=true

// Status represents the status of an openMCP resource.
type Status struct {
// ObservedGeneration is the generation of this resource that was last reconciled by the controller.
Expand Down
129 changes: 127 additions & 2 deletions api/common/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions api/core/v2alpha1/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package v2alpha1

const (
// DefaultOIDCProviderName is the identifier for the default OIDC provider.
DefaultOIDCProviderName = "default"
)
19 changes: 19 additions & 0 deletions api/core/v2alpha1/groupversion_info.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// +kubebuilder:object:generate=true
// +groupName=core.openmcp.cloud
package v2alpha1

import (
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/scheme"
)

var (
// GroupVersion is group version used to register these objects
GroupVersion = schema.GroupVersion{Group: "core.openmcp.cloud", Version: "v2alpha1"}

// SchemeBuilder is used to add go types to the GroupVersionKind scheme
SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}

// AddToScheme adds the types in this group-version to the given scheme.
AddToScheme = SchemeBuilder.AddToScheme
)
62 changes: 62 additions & 0 deletions api/core/v2alpha1/managedcontrolplane_types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package v2alpha1

import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"

commonapi "github.com/openmcp-project/openmcp-operator/api/common"
)

type ManagedControlPlaneSpec struct {
// IAM contains the access management configuration for the ManagedControlPlane.
IAM IAMConfig `json:"iam"`
}

type ManagedControlPlaneStatus struct {
commonapi.Status `json:",inline"`

// Access is a mapping from OIDC provider names to secret references.
// Each referenced secret is expected to contain a 'kubeconfig' key with the kubeconfig that was generated for the respective OIDC provider for the ManagedControlPlane.
// The default OIDC provider, if configured, uses the name "default" in this mapping.
// The "default" key is also used if the ClusterProvider does not support OIDC-based access and created a serviceaccount with a token instead.
Access map[string]commonapi.LocalObjectReference `json:"access"`
}

type IAMConfig struct {
// RoleBindings is a list of subjects with (cluster) role bindings that should be created for them.
// These bindings refer to the standard OIDC provider. If empty, the standard OIDC provider is disabled.
// Note that the username prefix is added automatically to the subjects' names, it must not be explicitly specified here.
// +optional
RoleBindings []commonapi.RoleBindings `json:"roleBindings,omitempty"`

// OIDCProviders is a list of OIDC providers that should be configured for the ManagedControlPlane.
// They are independent of the standard OIDC provider and in addition to it, unless it has been disabled by not specifying any role bindings.
// +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"
// +optional
OIDCProviders []*commonapi.OIDCProviderConfig `json:"oidcProviders,omitempty"`
}

// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:resource:shortName=mcp
// +kubebuilder:metadata:labels="openmcp.cloud/cluster=onboarding"
// +kubebuilder:selectablefield:JSONPath=".status.phase"
// +kubebuilder:printcolumn:JSONPath=".status.phase",name="Phase",type=string

type ManagedControlPlane struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ManagedControlPlaneSpec `json:"spec,omitempty"`
Status ManagedControlPlaneStatus `json:"status,omitempty"`
}

// +kubebuilder:object:root=true

type ManagedControlPlaneList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []ManagedControlPlane `json:"items"`
}

func init() {
SchemeBuilder.Register(&ManagedControlPlane{}, &ManagedControlPlaneList{})
}
Loading