From f2299710221d41261d310bbe84c99d5f8ff639cc Mon Sep 17 00:00:00 2001 From: Chris Werner Rau Date: Tue, 19 May 2026 10:50:11 +0200 Subject: [PATCH] feat(t8s-cluster/management-cluster): add OIDC settings that way we can add the customers' OIDC server --- .../clusterClass/_authenticationConfig.yaml | 35 +++++------ .../clusterClass/_helpers.tpl | 21 ++++--- .../_hostedControlPlaneTemplateSpec.yaml | 3 + charts/t8s-cluster/values.schema.json | 60 +++++++++++++++++++ charts/t8s-cluster/values.yaml | 20 +++++++ 5 files changed, 111 insertions(+), 28 deletions(-) diff --git a/charts/t8s-cluster/templates/management-cluster/clusterClass/_authenticationConfig.yaml b/charts/t8s-cluster/templates/management-cluster/clusterClass/_authenticationConfig.yaml index 62c0e4f198..4277c9747c 100644 --- a/charts/t8s-cluster/templates/management-cluster/clusterClass/_authenticationConfig.yaml +++ b/charts/t8s-cluster/templates/management-cluster/clusterClass/_authenticationConfig.yaml @@ -1,29 +1,24 @@ {{- define "t8s-cluster.clusterClass.apiServer.authenticationConfig" -}} + {{- $_ := mustMerge . (pick .context "Values") -}} apiVersion: apiserver.config.k8s.io/v1beta1 kind: AuthenticationConfiguration jwt: + {{- range $issuerUrl, $provider := .Values.oidcProviders }} - issuer: - url: https://staff-auth.k8s.teuto.net - audiences: - - kubernetes + url: {{ $issuerUrl }} + {{- with $provider.certificateAuthority }} + certificateAuthority: {{ . | quote }} + {{- end }} + audiences: {{- toYaml $provider.audiences | nindent 8 }} claimMappings: username: - expression: claims.email + expression: {{ $provider.claimMappings.username | quote }} + {{- with $provider.claimMappings.groups }} groups: - expression: dyn(claims.groups).map(g, "teuto.net:" + g) - claimValidationRules: - - expression: (has(claims.email_verified) && claims.email_verified) || !has(claims.email_verified) - message: email must be verified - - issuer: - url: https://auth.k8s.teuto.net - audiences: - - kubernetes - claimMappings: - username: - expression: claims.email - groups: - expression: dyn(claims.groups).map(g, "teuto.net:" + g) - claimValidationRules: - - expression: (has(claims.email_verified) && claims.email_verified) || !has(claims.email_verified) - message: email must be verified + expression: {{ . | quote }} + {{- end }} + {{- with $provider.claimValidationRules }} + claimValidationRules: {{- toYaml . | nindent 6 }} + {{- end }} + {{- end }} {{- end -}} diff --git a/charts/t8s-cluster/templates/management-cluster/clusterClass/_helpers.tpl b/charts/t8s-cluster/templates/management-cluster/clusterClass/_helpers.tpl index a7d9a2dcae..983e09e6f6 100644 --- a/charts/t8s-cluster/templates/management-cluster/clusterClass/_helpers.tpl +++ b/charts/t8s-cluster/templates/management-cluster/clusterClass/_helpers.tpl @@ -209,7 +209,9 @@ admission-control-config.yaml {{- define "t8s-cluster.clusterClass.args.apiServer" -}} {{- $args := include "t8s-cluster.clusterClass.args.base" (dict "context" .context) | fromYaml -}} {{- $args = mustMerge (include "t8s-cluster.clusterClass.args.sharedController" (dict "context" .context) | fromYaml) $args -}} - {{- $args = set $args "authentication-config" (include "t8s-cluster.clusterClass.apiServer.authenticationConfigPath" (dict)) -}} + {{- if not .context.Values.controlPlane.hosted -}} + {{- $args = set $args "authentication-config" (include "t8s-cluster.clusterClass.apiServer.authenticationConfigPath" (dict)) -}} + {{- end -}} {{- $args = set $args "admission-control-config-file" (include "t8s-cluster.clusterClass.apiServer.admissionControlConfigPath" (dict)) -}} {{- $args = set $args "enable-admission-plugins" (include "t8s-cluster.clusterClass.apiServer.admissionPlugins" (dict "context" .context) | fromYamlArray | join ",") -}} {{- $args = set $args "event-ttl" "4h" -}} @@ -335,13 +337,16 @@ current-context: webhook {{- define "t8s-cluster.clusterClass.apiServer.dynamicFiles" -}} {{- $_ := mustMerge . (pick .context "Values") -}} - {{- $files := dict - (include "t8s-cluster.clusterClass.apiServer.authenticationConfigFileName" (dict)) (dict - "path" (include "t8s-cluster.clusterClass.apiServer.authenticationConfigPath" (dict)) - "fileName" (include "t8s-cluster.clusterClass.apiServer.authenticationConfigFileName" (dict)) - "content" (include "t8s-cluster.clusterClass.apiServer.authenticationConfig" (dict "context" .context)) - ) - -}} + {{- $files := dict -}} + {{- if not .Values.controlPlane.hosted -}} + {{- $files = set $files + (include "t8s-cluster.clusterClass.apiServer.authenticationConfigFileName" (dict)) (dict + "path" (include "t8s-cluster.clusterClass.apiServer.authenticationConfigPath" (dict)) + "fileName" (include "t8s-cluster.clusterClass.apiServer.authenticationConfigFileName" (dict)) + "content" (include "t8s-cluster.clusterClass.apiServer.authenticationConfig" (dict "context" .context)) + ) + -}} + {{- end -}} {{- if .Values.controlPlane.audit -}} {{- $_ := set $files (include "t8s-cluster.clusterClass.apiServer.auditPolicyFileName" (dict)) (dict "path" (include "t8s-cluster.clusterClass.apiServer.auditPolicyPath" (dict)) diff --git a/charts/t8s-cluster/templates/management-cluster/clusterClass/hostedControlPlaneTemplate/_hostedControlPlaneTemplateSpec.yaml b/charts/t8s-cluster/templates/management-cluster/clusterClass/hostedControlPlaneTemplate/_hostedControlPlaneTemplateSpec.yaml index f54ab8fee0..a5b0c37ea4 100644 --- a/charts/t8s-cluster/templates/management-cluster/clusterClass/hostedControlPlaneTemplate/_hostedControlPlaneTemplateSpec.yaml +++ b/charts/t8s-cluster/templates/management-cluster/clusterClass/hostedControlPlaneTemplate/_hostedControlPlaneTemplateSpec.yaml @@ -45,6 +45,9 @@ etcd: {{- end }} kubeProxy: disabled: {{ .Values.controlPlane.hosted }} +{{- with .Values.oidcProviders }} +oidcProviders: {{- toYaml . | nindent 2 }} +{{- end }} gateway: namespace: capi-hosted-control-plane-system name: controlplane diff --git a/charts/t8s-cluster/values.schema.json b/charts/t8s-cluster/values.schema.json index d76cee020b..1645edeb75 100644 --- a/charts/t8s-cluster/values.schema.json +++ b/charts/t8s-cluster/values.schema.json @@ -181,6 +181,66 @@ "cloud": { "type": "string" }, + "oidcProviders": { + "type": "object", + "description": "OIDC providers for structured JWT authentication. The key is the issuer URL.", + "propertyNames": { + "type": "string", + "pattern": "^https://.+" + }, + "additionalProperties": { + "type": "object", + "properties": { + "certificateAuthority": { + "type": "string", + "description": "PEM-encoded CA bundle to verify the OIDC provider's TLS connection. If empty, the system verifier is used." + }, + "audiences": { + "type": "array", + "description": "Set of acceptable audiences the JWT must be issued to. At least one must match the 'aud' claim.", + "items": { + "type": "string" + }, + "minItems": 1 + }, + "claimMappings": { + "type": "object", + "description": "Maps token claims to Kubernetes user attributes.", + "properties": { + "username": { + "type": "string", + "description": "CEL expression producing the Kubernetes username." + }, + "groups": { + "type": "string", + "description": "CEL expression producing the Kubernetes groups (string or []string)." + } + }, + "required": ["username"], + "additionalProperties": false + }, + "claimValidationRules": { + "type": "array", + "description": "CEL expressions evaluated against token claims. All rules must return true.", + "items": { + "type": "object", + "properties": { + "expression": { + "type": "string" + }, + "message": { + "type": "string" + } + }, + "required": ["expression"], + "additionalProperties": false + } + } + }, + "required": ["audiences", "claimMappings"], + "additionalProperties": false + } + }, "quotas": { "type": "object", "properties": { diff --git a/charts/t8s-cluster/values.yaml b/charts/t8s-cluster/values.yaml index 254f637c86..f7b52b68a8 100644 --- a/charts/t8s-cluster/values.yaml +++ b/charts/t8s-cluster/values.yaml @@ -70,6 +70,26 @@ controlPlane: memory: 4Gi etcdBackup: false +oidcProviders: + https://staff-auth.k8s.teuto.net: + audiences: + - kubernetes + claimMappings: + username: claims.email + groups: dyn(claims.groups).map(g, "teuto.net:" + g) + claimValidationRules: + - expression: (has(claims.email_verified) && claims.email_verified) || !has(claims.email_verified) + message: email must be verified + https://auth.k8s.teuto.net: + audiences: + - kubernetes + claimMappings: + username: claims.email + groups: dyn(claims.groups).map(g, "teuto.net:" + g) + claimValidationRules: + - expression: (has(claims.email_verified) && claims.email_verified) || !has(claims.email_verified) + message: email must be verified + quotas: {} version: