diff --git a/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml b/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml index bfba61eb894..e94ca33f18a 100644 --- a/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml +++ b/inventory/sample/group_vars/k8s_cluster/k8s-cluster.yml @@ -40,9 +40,9 @@ 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/ ## To use OpenID you have to deploy additional an OpenID Provider (e.g Dex, Keycloak, ...) @@ -55,6 +55,13 @@ 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://... @@ -118,7 +125,8 @@ kube_network_node_prefix_ipv6: 120 # The port the API Server will be listening on. kube_apiserver_ip: "{{ kube_service_addresses | ansible.utils.ipaddr('net') | ansible.utils.ipaddr(1) | ansible.utils.ipaddr('address') }}" -kube_apiserver_port: 6443 # (https) +# https port +kube_apiserver_port: 6443 # Kube-proxy proxyMode configuration. # Can be ipvs, iptables diff --git a/roles/kubernetes/control-plane/defaults/main/main.yml b/roles/kubernetes/control-plane/defaults/main/main.yml index dbc0f239618..4c298311659 100644 --- a/roles/kubernetes/control-plane/defaults/main/main.yml +++ b/roles/kubernetes/control-plane/defaults/main/main.yml @@ -139,6 +139,7 @@ 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 @@ -162,14 +163,19 @@ 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_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 ae7b7506fe5..bce42423328 100644 --- a/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml +++ b/roles/kubernetes/control-plane/tasks/kubeadm-setup.yml @@ -82,6 +82,29 @@ mode: "0640" when: kube_apiserver_tracing +- name: Kubeadm | Configure Structured Authentication + when: + - kube_oidc_auth + - kube_oidc_structured_auth + 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: Kubeadm | Write apiserver authentication config yaml + template: + src: apiserver-authentication-config.yaml.j2 + dest: "{{ kube_config_dir }}/authentication/apiserver-authentication-config.yaml" + mode: "0640" + # Nginx LB(default), If kubeadm_config_api_fqdn is defined, use other LB by kubeadm controlPlaneEndpoint. - name: Set kubeadm_config_api_fqdn define set_fact: diff --git a/roles/kubernetes/control-plane/templates/apiserver-authentication-config.yaml.j2 b/roles/kubernetes/control-plane/templates/apiserver-authentication-config.yaml.j2 new file mode 100644 index 00000000000..6faa62beab6 --- /dev/null +++ b/roles/kubernetes/control-plane/templates/apiserver-authentication-config.yaml.j2 @@ -0,0 +1,31 @@ +--- +apiVersion: apiserver.config.k8s.io/v1beta1 +kind: AuthenticationConfiguration +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 }}" +{% endif %} + url: "{{ kube_oidc_url }}" + claimMappings: +{% if kube_oidc_username_claim is defined %} + username: + claim: "{{ kube_oidc_username_claim }}" + prefix: "{{ kube_oidc_username_prefix }}" +{% endif %} +{% if kube_oidc_groups_claim is defined %} + groups: + claim: "{{ kube_oidc_groups_claim }}" + prefix: "{{ kube_oidc_groups_prefix }}" +{% endif %} +{% if kube_oidc_uid_claim is defined %} + uid: + claim: "{{ kube_oidc_uid_claim }}" +{% endif %} + userValidationRules: +{% for rule in kube_oidc_user_validation_rules + kube_oidc_additional_user_validation_rules %} + - expression: "{{ rule.expression }}" + message: "{{ rule.message }}" +{% endfor %} 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 123a68ad579..0b1d24cb655 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 | 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 and not kube_oidc_structured_auth %} oidc-issuer-url: "{{ kube_oidc_url }}" oidc-client-id: "{{ kube_oidc_client_id }}" {% if kube_oidc_ca_file is defined %} @@ -173,6 +173,9 @@ 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 %} + authentication-config: {{ kube_config_dir }}/authentication/apiserver-authentication-config.yaml +{% endif %} {% if kube_webhook_token_auth | default(false) %} authentication-token-webhook-config-file: {{ kube_config_dir }}/webhook-token-auth-config.yaml {% endif %} @@ -261,6 +264,13 @@ apiServer: readOnly: false pathType: DirectoryOrCreate {% endif %} +{% if kube_oidc_structured_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 263ee0b4569..498bdf127fa 100644 --- a/roles/kubernetes/control-plane/vars/main.yaml +++ b/roles/kubernetes/control-plane/vars/main.yaml @@ -7,3 +7,9 @@ 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"