From 8296a0689b769260c88ff7f538562bfc70d68b52 Mon Sep 17 00:00:00 2001 From: Payback159 Date: Thu, 2 Jan 2025 03:47:18 +0100 Subject: [PATCH] Move configuration from kube_oidc to own top-level configuration. Expand the entire Struct-Auth configuration and only create the configurations if the feature gate is activated. --- .../group_vars/k8s_cluster/k8s-cluster.yml | 8 --- .../defaults/main/kube-apiserver-auth.yml | 37 +++++++++++ .../control-plane/defaults/main/main.yml | 11 +--- .../control-plane/tasks/kubeadm-setup.yml | 26 +++++--- .../apiserver-authentication-config.yaml.j2 | 61 ++++++++++++------- .../templates/kubeadm-config.v1beta3.yaml.j2 | 6 +- .../templates/kubeadm-config.v1beta4.yaml.j2 | 13 +++- roles/kubernetes/control-plane/vars/main.yaml | 11 ++-- 8 files changed, 117 insertions(+), 56 deletions(-) create mode 100644 roles/kubernetes/control-plane/defaults/main/kube-apiserver-auth.yml diff --git a/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml b/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml index e94ca33f18a..95909da00b1 100644 --- a/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml +++ b/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml @@ -40,7 +40,6 @@ credentials_dir: "{{ inventory_dir }}/credentials" ## It is possible to activate / deactivate selected authentication methods (oidc, static token auth) # kube_oidc_auth: false -# kube_oidc_structured_auth: false # kube_token_auth: false ## Variables for OpenID Connect Configuration https://kubernetes.io/docs/admin/authentication/ @@ -55,13 +54,6 @@ credentials_dir: "{{ inventory_dir }}/credentials" # kube_oidc_groups_claim: groups # kube_oidc_groups_prefix: 'oidc:' -# Variables are only valid if kube_oidc_structured_auth is set to true -# kube_oidc_uid_clam: "sub" -# kube_oidc_audiences: [] -# kube_oidc_additional_user_validation_rules: -# - expression: "!user.username.startsWith('system:')" -# error_message: "username cannot used reserved system: prefix" - ## Variables to control webhook authn/authz # kube_webhook_token_auth: false # kube_webhook_token_auth_url: https://... diff --git a/roles/kubernetes/control-plane/defaults/main/kube-apiserver-auth.yml b/roles/kubernetes/control-plane/defaults/main/kube-apiserver-auth.yml new file mode 100644 index 00000000000..cb215aa3aa2 --- /dev/null +++ b/roles/kubernetes/control-plane/defaults/main/kube-apiserver-auth.yml @@ -0,0 +1,37 @@ +--- +kube_apiserver_structured_auth_jwt_issuers: [] +# Example JWT issuer configuration: +# kube_apiserver_structured_auth_jwt_issuers: +# - issuer: +# url: "https://issuer1.example.com" +# audiences: +# - audience1 +# - audience2 +# audienceMatchPolicy: MatchAny +# claimMappings: +# username: +# expression: "claims.username" +# groups: +# expression: "claims.groups" +# uid: +# expression: "claims.uid" +# userValidationRules: +# - expression: "!user.username.startsWith('system:')" +# message: "username cannot use reserved system: prefix" +# - issuer: +# url: "https://issuer2.example.com" +# discoveryURL: "https://discovery.example.com/.well-known/openid-configuration" +# audiences: +# - audience3 +# - audience4 +# audienceMatchPolicy: MatchAny +# claimMappings: +# username: +# expression: "claims.username" +# groups: +# expression: "claims.groups" +# uid: +# expression: "claims.uid" +# userValidationRules: +# - expression: "!user.username.startsWith('kubespray:')" +# message: "username cannot use reserved kubespray: prefix" diff --git a/roles/kubernetes/control-plane/defaults/main/main.yml b/roles/kubernetes/control-plane/defaults/main/main.yml index 4c298311659..6269cd8d7dd 100644 --- a/roles/kubernetes/control-plane/defaults/main/main.yml +++ b/roles/kubernetes/control-plane/defaults/main/main.yml @@ -139,7 +139,6 @@ kube_api_runtime_config: [] ## Enable/Disable Kube API Server Authentication Methods kube_token_auth: false kube_oidc_auth: false -kube_oidc_structured_auth: false ## Variables for webhook token auth https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication kube_webhook_token_auth: false @@ -163,19 +162,15 @@ kube_apiserver_admission_plugins_podnodeselector_default_node_selector: "" # kube_oidc_client_id: kubernetes ## Optional settings for OIDC # kube_oidc_username_claim: sub -kube_oidc_username_prefix: "oidc:" +# kube_oidc_username_prefix: "oidc:" # kube_oidc_groups_claim: groups -kube_oidc_groups_prefix: "oidc:" -kube_oidc_uid_claim: "sub" +# kube_oidc_groups_prefix: "oidc:" +# kube_oidc_uid_claim: "sub" # Copy oidc CA file to the following path if needed # kube_oidc_ca_file: {{ kube_cert_dir }}/ca.pem # Optionally include a base64-encoded oidc CA cert # kube_oidc_ca_cert: c3RhY2thYnVzZS5jb20... -# Variables are only valid if kube_oidc_structured_auth is true -kube_oidc_audiences: [] -kube_oidc_additional_user_validation_rules: [] - # List of the preferred NodeAddressTypes to use for kubelet connections. kubelet_preferred_address_types: 'InternalDNS,InternalIP,Hostname,ExternalDNS,ExternalIP' diff --git a/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml b/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml index bce42423328..2d97085bb0b 100644 --- a/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml +++ b/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml @@ -83,22 +83,32 @@ when: kube_apiserver_tracing - name: Kubeadm | Configure Structured Authentication + vars: + all_kube_apiserver_feature_gates: "{{ kube_apiserver_feature_gates + kube_feature_gates }}" when: - - kube_oidc_auth - - kube_oidc_structured_auth + - not kube_oidc_auth + - ("StructuredAuthenticationConfiguration=true" in all_kube_apiserver_feature_gates and kube_version | regex_replace("^v", "") is version("1.30.0", "<", version_type="semver")) + or + ("StructuredAuthenticationConfiguration=false" not in all_kube_apiserver_feature_gates and kube_version | regex_replace("^v", "") is version("1.30.0", ">=", version_type="semver")) block: - - name: Kubeadm | Check if Structured Authentication Configuration is supported - fail: - msg: "OIDC structured auth is supported only from Kubernetes 1.30.0" - when: - - kube_version is version('1.30.0', '<') - - name: Kubeadm | Create apiserver authentication config directory file: path: "{{ kube_config_dir }}/authentication" state: directory mode: "0640" + - name: Merge additional userValidationRules + set_fact: + kube_apiserver_structured_auth_jwt_issuers: >- + {{ + kube_apiserver_structured_auth_jwt_issuers | map('combine', { + 'userValidationRules': item.userValidationRules + additional_user_validation_rules + }) | list + }} + loop: "{{ kube_apiserver_structured_auth_jwt_issuers }}" + loop_control: + loop_var: item + - name: Kubeadm | Write apiserver authentication config yaml template: src: apiserver-authentication-config.yaml.j2 diff --git a/roles/kubernetes/control-plane/templates/apiserver-authentication-config.yaml.j2 b/roles/kubernetes/control-plane/templates/apiserver-authentication-config.yaml.j2 index 6faa62beab6..a09a82e2f5e 100644 --- a/roles/kubernetes/control-plane/templates/apiserver-authentication-config.yaml.j2 +++ b/roles/kubernetes/control-plane/templates/apiserver-authentication-config.yaml.j2 @@ -1,31 +1,48 @@ --- apiVersion: apiserver.config.k8s.io/v1beta1 kind: AuthenticationConfiguration +{% if kube_apiserver_structured_auth_jwt_issuers | length > 0 %} jwt: - - issuer: - audiences: {{ ([kube_oidc_client_id] + kube_oidc_audiences) | to_json }} - audienceMatchPolicy: MatchAny -{% if kube_oidc_ca_file is defined %} - certificateAuthority: "{{ kube_oidc_ca_file }}" +{% for issuer in kube_apiserver_structured_auth_jwt_issuers %} +- issuer: + url: "{{ issuer.issuer.url }}" +{% if issuer.issuer.discoveryURL is defined %} + discoveryURL: "{{ issuer.issuer.discoveryURL }}" {% endif %} - url: "{{ kube_oidc_url }}" - claimMappings: -{% if kube_oidc_username_claim is defined %} - username: - claim: "{{ kube_oidc_username_claim }}" - prefix: "{{ kube_oidc_username_prefix }}" + audiences: +{% for audience in issuer.issuer.audiences %} + - {{ audience }} +{% endfor %} + audienceMatchPolicy: {{ issuer.issuer.audienceMatchPolicy }} +{% if issuer.claimValidationRules is defined %} + claimValidationRules: +{% for validationRule in issuer.claimValidationRules %} + expression: "{{ validationRule.expression }}" + message: "{{ validationRule.message }}" +{% endfor %} {% endif %} -{% if kube_oidc_groups_claim is defined %} - groups: - claim: "{{ kube_oidc_groups_claim }}" - prefix: "{{ kube_oidc_groups_prefix }}" + claimMappings: + username: + expression: "{{ issuer.claimMappings.username.expression }}" + groups: + expression: "{{ issuer.claimMappings.groups.expression }}" + uid: + expression: "{{ issuer.claimMappings.uid.expression }}" +{% if issuer.claimMappings.extra is defined %} + extra: +{% for extra in issuer.claimMappings.extra %} + - key: "{{ extra.key }}" + expression: "{{ extra.expression }}" +{% endfor %} {% endif %} -{% if kube_oidc_uid_claim is defined %} - uid: - claim: "{{ kube_oidc_uid_claim }}" +{% if issuer.userValidationRules | length > 0 %} + userValidationRules: +{% for rule in issuer.userValidationRules %} + - expression: "{{ rule.expression }}" + message: "{{ rule.message }}" +{% endfor %} {% endif %} - userValidationRules: -{% for rule in kube_oidc_user_validation_rules + kube_oidc_additional_user_validation_rules %} - - expression: "{{ rule.expression }}" - message: "{{ rule.message }}" {% endfor %} +{% else %} +jwt: +{% endif %} diff --git a/roles/kubernetes/control-plane/templates/kubeadm-config.v1beta3.yaml.j2 b/roles/kubernetes/control-plane/templates/kubeadm-config.v1beta3.yaml.j2 index 0b1d24cb655..b745596b783 100644 --- a/roles/kubernetes/control-plane/templates/kubeadm-config.v1beta3.yaml.j2 +++ b/roles/kubernetes/control-plane/templates/kubeadm-config.v1beta3.yaml.j2 @@ -154,7 +154,7 @@ apiServer: {% if kube_apiserver_service_account_lookup %} service-account-lookup: "{{ kube_apiserver_service_account_lookup }}" {% endif %} -{% if kube_oidc_auth and kube_oidc_url is defined and kube_oidc_client_id is defined and not kube_oidc_structured_auth %} +{% if kube_oidc_auth and kube_oidc_url is defined and kube_oidc_client_id is defined %} oidc-issuer-url: "{{ kube_oidc_url }}" oidc-client-id: "{{ kube_oidc_client_id }}" {% if kube_oidc_ca_file is defined %} @@ -173,7 +173,7 @@ apiServer: oidc-groups-prefix: "{{ kube_oidc_groups_prefix }}" {% endif %} {% endif %} -{% if kube_oidc_auth and kube_oidc_url is defined and kube_oidc_client_id is defined and kube_oidc_structured_auth %} +{% if not kube_oidc_auth %} authentication-config: {{ kube_config_dir }}/authentication/apiserver-authentication-config.yaml {% endif %} {% if kube_webhook_token_auth | default(false) %} @@ -264,7 +264,7 @@ apiServer: readOnly: false pathType: DirectoryOrCreate {% endif %} -{% if kube_oidc_structured_auth %} +{% if not kube_oidc_auth %} - name: structauth hostPath: {{ kube_config_dir }}/authentication mountPath: {{ kube_config_dir }}/authentication diff --git a/roles/kubernetes/control-plane/templates/kubeadm-config.v1beta4.yaml.j2 b/roles/kubernetes/control-plane/templates/kubeadm-config.v1beta4.yaml.j2 index be03b489f54..3284166e4cd 100644 --- a/roles/kubernetes/control-plane/templates/kubeadm-config.v1beta4.yaml.j2 +++ b/roles/kubernetes/control-plane/templates/kubeadm-config.v1beta4.yaml.j2 @@ -182,7 +182,7 @@ apiServer: - name: service-account-lookup value: "{{ kube_apiserver_service_account_lookup }}" {% endif %} -{% if kube_oidc_auth | default(false) and kube_oidc_url is defined and kube_oidc_client_id is defined %} +{% if kube_oidc_auth and kube_oidc_url is defined and kube_oidc_client_id is defined %} - name: oidc-issuer-url value: "{{ kube_oidc_url }}" - name: oidc-client-id @@ -208,6 +208,10 @@ apiServer: value: "{{ kube_oidc_groups_prefix }}" {% endif %} {% endif %} +{% if not kube_oidc_auth %} + - name: authentication-config + value: "{{ kube_config_dir }}/authentication/apiserver-authentication-config.yaml" +{% endif %} {% if kube_webhook_token_auth | default(false) %} - name: authentication-token-webhook-config-file value: "{{ kube_config_dir }}/webhook-token-auth-config.yaml" @@ -317,6 +321,13 @@ apiServer: readOnly: false pathType: DirectoryOrCreate {% endif %} +{% if not kube_oidc_auth %} + - name: structauth + hostPath: {{ kube_config_dir }}/authentication + mountPath: {{ kube_config_dir }}/authentication + readOnly: true + pathType: DirectoryOrCreate +{% endif %} {% if kube_apiserver_tracing %} - name: tracing hostPath: {{ kube_config_dir }}/tracing diff --git a/roles/kubernetes/control-plane/vars/main.yaml b/roles/kubernetes/control-plane/vars/main.yaml index 498bdf127fa..6fe2cfe5f23 100644 --- a/roles/kubernetes/control-plane/vars/main.yaml +++ b/roles/kubernetes/control-plane/vars/main.yaml @@ -7,9 +7,8 @@ kube_apiserver_admission_plugins_needs_configuration: - PodSecurity - PodNodeSelector - ResourceQuota -# default user validation rules for OIDC -kube_oidc_user_validation_rules: - - expression: "!user.username.startsWith('system:')" - message: "username cannot used reserved system: prefix" - - expression: "user.groups.all(group, !group.startsWith('system:'))" - message: "groups cannot used reserved system: prefix" +additional_user_validation_rules: +- expression: "!user.username.startsWith('system:')" + message: "username cannot used reserved system: prefix" +- expression: "user.groups.all(group, !group.startsWith('system:'))" + message: "groups cannot used reserved system: prefix" \ No newline at end of file