Authenticating

This page provides an overview of authentication.

Users in Kubernetes

All Kubernetes clusters have two categories of users: service accounts managed by Kubernetes, and normal users.

It is assumed that a cluster-independent service manages normal users in the following ways:

  • an administrator distributing private keys
  • a user store like Keystone or Google Accounts
  • a file with a list of usernames and passwords

In this regard, Kubernetes does not have objects which represent normal user accounts. Normal users cannot be added to a cluster through an API call.

Even though a normal user cannot be added via an API call, any user that presents a valid certificate signed by the cluster’s certificate authority (CA) is considered authenticated. In this configuration, Kubernetes determines the username from the common name field in the ‘subject’ of the cert (e.g., “/CN=bob”). From there, the role based access control (RBAC) sub-system would determine whether the user is authorized to perform a specific operation on a resource. For more details, refer to the normal users topic in certificate request for more details about this.

In contrast, service accounts are users managed by the Kubernetes API. They are bound to specific namespaces, and created automatically by the API server or manually through API calls. Service accounts are tied to a set of credentials stored as Secrets, which are mounted into pods allowing in-cluster processes to talk to the Kubernetes API.

API requests are tied to either a normal user or a service account, or are treated as anonymous requests. This means every process inside or outside the cluster, from a human user typing kubectl on a workstation, to kubelets on nodes, to members of the control plane, must authenticate when making requests to the API server, or be treated as an anonymous user.

Authentication strategies

Kubernetes uses client certificates, bearer tokens, or an authenticating proxy to authenticate API requests through authentication plugins. As HTTP requests are made to the API server, plugins attempt to associate the following attributes with the request:

  • Username: a string which identifies the end user. Common values might be kube-admin or jane@example.com.
  • UID: a string which identifies the end user and attempts to be more consistent and unique than username.
  • Groups: a set of strings, each of which indicates the user’s membership in a named logical collection of users. Common values might be system:masters or devops-team.
  • Extra fields: a map of strings to list of strings which holds additional information authorizers may find useful.

All values are opaque to the authentication system and only hold significance when interpreted by an authorizer.

You can enable multiple authentication methods at once. You should usually use at least two methods:

  • service account tokens for service accounts
  • at least one other method for user authentication.

When multiple authenticator modules are enabled, the first module to successfully authenticate the request short-circuits evaluation. The API server does not guarantee the order authenticators run in.

The system:authenticated group is included in the list of groups for all authenticated users.

Integrations with other authentication protocols (LDAP, SAML, Kerberos, alternate x509 schemes, etc) can be accomplished using an authenticating proxy or the authentication webhook.

X509 client certificates

Client certificate authentication is enabled by passing the --client-ca-file=SOMEFILE option to API server. The referenced file must contain one or more certificate authorities to use to validate client certificates presented to the API server. If a client certificate is presented and verified, the common name of the subject is used as the user name for the request. As of Kubernetes 1.4, client certificates can also indicate a user’s group memberships using the certificate’s organization fields. To include multiple group memberships for a user, include multiple organization fields in the certificate.

For example, using the openssl command line tool to generate a certificate signing request:

  1. openssl req -new -key jbeda.pem -out jbeda-csr.pem -subj "/CN=jbeda/O=app1/O=app2"

This would create a CSR for the username “jbeda”, belonging to two groups, “app1” and “app2”.

See Managing Certificates for how to generate a client cert.

Static token file

The API server reads bearer tokens from a file when given the --token-auth-file=SOMEFILE option on the command line. Currently, tokens last indefinitely, and the token list cannot be changed without restarting the API server.

The token file is a csv file with a minimum of 3 columns: token, user name, user uid, followed by optional group names.

Note:

If you have more than one group the column must be double quoted e.g.

  1. token,user,uid,"group1,group2,group3"

Putting a bearer token in a request

When using bearer token authentication from an http client, the API server expects an Authorization header with a value of Bearer <token>. The bearer token must be a character sequence that can be put in an HTTP header value using no more than the encoding and quoting facilities of HTTP. For example: if the bearer token is 31ada4fd-adec-460c-809a-9e56ceb75269 then it would appear in an HTTP header as shown below.

  1. Authorization: Bearer 31ada4fd-adec-460c-809a-9e56ceb75269

Bootstrap tokens

FEATURE STATE: Kubernetes v1.18 [stable]

To allow for streamlined bootstrapping for new clusters, Kubernetes includes a dynamically-managed Bearer token type called a Bootstrap Token. These tokens are stored as Secrets in the kube-system namespace, where they can be dynamically managed and created. Controller Manager contains a TokenCleaner controller that deletes bootstrap tokens as they expire.

The tokens are of the form [a-z0-9]{6}.[a-z0-9]{16}. The first component is a Token ID and the second component is the Token Secret. You specify the token in an HTTP header as follows:

  1. Authorization: Bearer 781292.db7bc3a58fc5f07e

You must enable the Bootstrap Token Authenticator with the --enable-bootstrap-token-auth flag on the API Server. You must enable the TokenCleaner controller via the --controllers flag on the Controller Manager. This is done with something like --controllers=*,tokencleaner. kubeadm will do this for you if you are using it to bootstrap a cluster.

The authenticator authenticates as system:bootstrap:<Token ID>. It is included in the system:bootstrappers group. The naming and groups are intentionally limited to discourage users from using these tokens past bootstrapping. The user names and group can be used (and are used by kubeadm) to craft the appropriate authorization policies to support bootstrapping a cluster.

Please see Bootstrap Tokens for in depth documentation on the Bootstrap Token authenticator and controllers along with how to manage these tokens with kubeadm.

Service account tokens

A service account is an automatically enabled authenticator that uses signed bearer tokens to verify requests. The plugin takes two optional flags:

  • --service-account-key-file File containing PEM-encoded x509 RSA or ECDSA private or public keys, used to verify ServiceAccount tokens. The specified file can contain multiple keys, and the flag can be specified multiple times with different files. If unspecified, —tls-private-key-file is used.
  • --service-account-lookup If enabled, tokens which are deleted from the API will be revoked.

Service accounts are usually created automatically by the API server and associated with pods running in the cluster through the ServiceAccount Admission Controller. Bearer tokens are mounted into pods at well-known locations, and allow in-cluster processes to talk to the API server. Accounts may be explicitly associated with pods using the serviceAccountName field of a PodSpec.

Note: serviceAccountName is usually omitted because this is done automatically.

  1. apiVersion: apps/v1 # this apiVersion is relevant as of Kubernetes 1.9
  2. kind: Deployment
  3. metadata:
  4. name: nginx-deployment
  5. namespace: default
  6. spec:
  7. replicas: 3
  8. template:
  9. metadata:
  10. # ...
  11. spec:
  12. serviceAccountName: bob-the-bot
  13. containers:
  14. - name: nginx
  15. image: nginx:1.14.2

Service account bearer tokens are perfectly valid to use outside the cluster and can be used to create identities for long standing jobs that wish to talk to the Kubernetes API. To manually create a service account, use the kubectl create serviceaccount (NAME) command. This creates a service account in the current namespace.

  1. kubectl create serviceaccount jenkins
  1. serviceaccount/jenkins created

Create an associated token:

  1. kubectl create token jenkins
  1. eyJhbGciOiJSUzI1NiIsImtp...

The created token is a signed JSON Web Token (JWT).

The signed JWT can be used as a bearer token to authenticate as the given service account. See above for how the token is included in a request. Normally these tokens are mounted into pods for in-cluster access to the API server, but can be used from outside the cluster as well.

Service accounts authenticate with the username system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT), and are assigned to the groups system:serviceaccounts and system:serviceaccounts:(NAMESPACE).

Warning: Because service account tokens can also be stored in Secret API objects, any user with write access to Secrets can request a token, and any user with read access to those Secrets can authenticate as the service account. Be cautious when granting permissions to service accounts and read or write capabilities for Secrets.

OpenID Connect Tokens

OpenID Connect is a flavor of OAuth2 supported by some OAuth2 providers, notably Microsoft Entra ID, Salesforce, and Google. The protocol’s main extension of OAuth2 is an additional field returned with the access token called an ID Token. This token is a JSON Web Token (JWT) with well known fields, such as a user’s email, signed by the server.

To identify the user, the authenticator uses the id_token (not the access_token) from the OAuth2 token response as a bearer token. See above for how the token is included in a request.

sequenceDiagram participant user as User participant idp as Identity Provider participant kube as Kubectl participant api as API Server user ->> idp: 1. Log in to IdP activate idp idp —>> user: 2. Provide access_token,
id_token, and refresh_token deactivate idp activate user user ->> kube: 3. Call Kubectl
with —token being the id_token
OR add tokens to .kube/config deactivate user activate kube kube ->> api: 4. Authorization: Bearer… deactivate kube activate api api ->> api: 5. Is JWT signature valid? api ->> api: 6. Has the JWT expired? (iat+exp) api ->> api: 7. User authorized? api —>> kube: 8. Authorized: Perform
action and return result deactivate api activate kube kube —x user: 9. Return result deactivate kube

JavaScript must be enabled to view this content

  1. Log in to your identity provider
  2. Your identity provider will provide you with an access_token, id_token and a refresh_token
  3. When using kubectl, use your id_token with the --token flag or add it directly to your kubeconfig
  4. kubectl sends your id_token in a header called Authorization to the API server
  5. The API server will make sure the JWT signature is valid
  6. Check to make sure the id_token hasn’t expired
    1. Perform claim and/or user validation if CEL expressions are configured with AuthenticationConfiguration.
  7. Make sure the user is authorized
  8. Once authorized the API server returns a response to kubectl
  9. kubectl provides feedback to the user

Since all of the data needed to validate who you are is in the id_token, Kubernetes doesn’t need to “phone home” to the identity provider. In a model where every request is stateless this provides a very scalable solution for authentication. It does offer a few challenges:

  1. Kubernetes has no “web interface” to trigger the authentication process. There is no browser or interface to collect credentials which is why you need to authenticate to your identity provider first.
  2. The id_token can’t be revoked, it’s like a certificate so it should be short-lived (only a few minutes) so it can be very annoying to have to get a new token every few minutes.
  3. To authenticate to the Kubernetes dashboard, you must use the kubectl proxy command or a reverse proxy that injects the id_token.

Configuring the API Server

Using flags

To enable the plugin, configure the following flags on the API server:

ParameterDescriptionExampleRequired
—oidc-issuer-urlURL of the provider that allows the API server to discover public signing keys. Only URLs that use the https:// scheme are accepted. This is typically the provider’s discovery URL, changed to have an empty pathIf the issuer’s OIDC discovery URL is https://accounts.provider.example/.well-known/openid-configuration, the value should be https://accounts.provider.exampleYes
—oidc-client-idA client id that all tokens must be issued for.kubernetesYes
—oidc-username-claimJWT claim to use as the user name. By default sub, which is expected to be a unique identifier of the end user. Admins can choose other claims, such as email or name, depending on their provider. However, claims other than email will be prefixed with the issuer URL to prevent naming clashes with other plugins.subNo
—oidc-username-prefixPrefix prepended to username claims to prevent clashes with existing names (such as system: users). For example, the value oidc: will create usernames like oidc:jane.doe. If this flag isn’t provided and —oidc-username-claim is a value other than email the prefix defaults to ( Issuer URL )# where ( Issuer URL ) is the value of —oidc-issuer-url. The value - can be used to disable all prefixing.oidc:No
—oidc-groups-claimJWT claim to use as the user’s group. If the claim is present it must be an array of strings.groupsNo
—oidc-groups-prefixPrefix prepended to group claims to prevent clashes with existing names (such as system: groups). For example, the value oidc: will create group names like oidc:engineering and oidc:infra.oidc:No
—oidc-required-claimA key=value pair that describes a required claim in the ID Token. If set, the claim is verified to be present in the ID Token with a matching value. Repeat this flag to specify multiple claims.claim=valueNo
—oidc-ca-fileThe path to the certificate for the CA that signed your identity provider’s web certificate. Defaults to the host’s root CAs./etc/kubernetes/ssl/kc-ca.pemNo
—oidc-signing-algsThe signing algorithms accepted. Default is “RS256”.RS512No
Authentication configuration from a file

FEATURE STATE: Kubernetes v1.30 [beta]

JWT Authenticator is an authenticator to authenticate Kubernetes users using JWT compliant tokens. The authenticator will attempt to parse a raw ID token, verify it’s been signed by the configured issuer. The public key to verify the signature is discovered from the issuer’s public endpoint using OIDC discovery.

The minimum valid JWT payload must contain the following claims:

  1. {
  2. "iss": "https://example.com", // must match the issuer.url
  3. "aud": ["my-app"], // at least one of the entries in issuer.audiences must match the "aud" claim in presented JWTs.
  4. "exp": 1234567890, // token expiration as Unix time (the number of seconds elapsed since January 1, 1970 UTC)
  5. "<username-claim>": "user" // this is the username claim configured in the claimMappings.username.claim or claimMappings.username.expression
  6. }

The configuration file approach allows you to configure multiple JWT authenticators, each with a unique issuer.url and issuer.discoveryURL. The configuration file even allows you to specify CEL expressions to map claims to user attributes, and to validate claims and user information. The API server also automatically reloads the authenticators when the configuration file is modified. You can use apiserver_authentication_config_controller_automatic_reload_last_timestamp_seconds metric to monitor the last time the configuration was reloaded by the API server.

You must specify the path to the authentication configuration using the --authentication-config flag on the API server. If you want to use command line flags instead of the configuration file, those will continue to work as-is. To access the new capabilities like configuring multiple authenticators, setting multiple audiences for an issuer, switch to using the configuration file.

For Kubernetes v1.30, the structured authentication configuration file format is beta-level, and the mechanism for using that configuration is also beta. Provided you didn’t specifically disable the StructuredAuthenticationConfiguration feature gate for your cluster, you can turn on structured authentication by specifying the --authentication-config command line argument to the kube-apiserver. An example of the structured authentication configuration file is shown below.

Note: If you specify --authentication-config along with any of the --oidc-* command line arguments, this is a misconfiguration. In this situation, the API server reports an error and then immediately exits. If you want to switch to using structured authentication configuration, you have to remove the --oidc-* command line arguments, and use the configuration file instead.

  1. ---
  2. #
  3. # CAUTION: this is an example configuration.
  4. # Do not use this for your own cluster!
  5. #
  6. apiVersion: apiserver.config.k8s.io/v1beta1
  7. kind: AuthenticationConfiguration
  8. # list of authenticators to authenticate Kubernetes users using JWT compliant tokens.
  9. # the maximum number of allowed authenticators is 64.
  10. jwt:
  11. - issuer:
  12. # url must be unique across all authenticators.
  13. # url must not conflict with issuer configured in --service-account-issuer.
  14. url: https://example.com # Same as --oidc-issuer-url.
  15. # discoveryURL, if specified, overrides the URL used to fetch discovery
  16. # information instead of using "{url}/.well-known/openid-configuration".
  17. # The exact value specified is used, so "/.well-known/openid-configuration"
  18. # must be included in discoveryURL if needed.
  19. #
  20. # The "issuer" field in the fetched discovery information must match the "issuer.url" field
  21. # in the AuthenticationConfiguration and will be used to validate the "iss" claim in the presented JWT.
  22. # This is for scenarios where the well-known and jwks endpoints are hosted at a different
  23. # location than the issuer (such as locally in the cluster).
  24. # discoveryURL must be different from url if specified and must be unique across all authenticators.
  25. discoveryURL: https://discovery.example.com/.well-known/openid-configuration
  26. # PEM encoded CA certificates used to validate the connection when fetching
  27. # discovery information. If not set, the system verifier will be used.
  28. # Same value as the content of the file referenced by the --oidc-ca-file flag.
  29. certificateAuthority: <PEM encoded CA certificates>
  30. # audiences is the set of acceptable audiences the JWT must be issued to.
  31. # At least one of the entries must match the "aud" claim in presented JWTs.
  32. audiences:
  33. - my-app # Same as --oidc-client-id.
  34. - my-other-app
  35. # this is required to be set to "MatchAny" when multiple audiences are specified.
  36. audienceMatchPolicy: MatchAny
  37. # rules applied to validate token claims to authenticate users.
  38. claimValidationRules:
  39. # Same as --oidc-required-claim key=value.
  40. - claim: hd
  41. requiredValue: example.com
  42. # Instead of claim and requiredValue, you can use expression to validate the claim.
  43. # expression is a CEL expression that evaluates to a boolean.
  44. # all the expressions must evaluate to true for validation to succeed.
  45. - expression: 'claims.hd == "example.com"'
  46. # Message customizes the error message seen in the API server logs when the validation fails.
  47. message: the hd claim must be set to example.com
  48. - expression: 'claims.exp - claims.nbf <= 86400'
  49. message: total token lifetime must not exceed 24 hours
  50. claimMappings:
  51. # username represents an option for the username attribute.
  52. # This is the only required attribute.
  53. username:
  54. # Same as --oidc-username-claim. Mutually exclusive with username.expression.
  55. claim: "sub"
  56. # Same as --oidc-username-prefix. Mutually exclusive with username.expression.
  57. # if username.claim is set, username.prefix is required.
  58. # Explicitly set it to "" if no prefix is desired.
  59. prefix: ""
  60. # Mutually exclusive with username.claim and username.prefix.
  61. # expression is a CEL expression that evaluates to a string.
  62. #
  63. # 1. If username.expression uses 'claims.email', then 'claims.email_verified' must be used in
  64. # username.expression or extra[*].valueExpression or claimValidationRules[*].expression.
  65. # An example claim validation rule expression that matches the validation automatically
  66. # applied when username.claim is set to 'email' is 'claims.?email_verified.orValue(true)'.
  67. # 2. If the username asserted based on username.expression is the empty string, the authentication
  68. # request will fail.
  69. expression: 'claims.username + ":external-user"'
  70. # groups represents an option for the groups attribute.
  71. groups:
  72. # Same as --oidc-groups-claim. Mutually exclusive with groups.expression.
  73. claim: "sub"
  74. # Same as --oidc-groups-prefix. Mutually exclusive with groups.expression.
  75. # if groups.claim is set, groups.prefix is required.
  76. # Explicitly set it to "" if no prefix is desired.
  77. prefix: ""
  78. # Mutually exclusive with groups.claim and groups.prefix.
  79. # expression is a CEL expression that evaluates to a string or a list of strings.
  80. expression: 'claims.roles.split(",")'
  81. # uid represents an option for the uid attribute.
  82. uid:
  83. # Mutually exclusive with uid.expression.
  84. claim: 'sub'
  85. # Mutually exclusive with uid.claim
  86. # expression is a CEL expression that evaluates to a string.
  87. expression: 'claims.sub'
  88. # extra attributes to be added to the UserInfo object. Keys must be domain-prefix path and must be unique.
  89. extra:
  90. - key: 'example.com/tenant'
  91. # valueExpression is a CEL expression that evaluates to a string or a list of strings.
  92. valueExpression: 'claims.tenant'
  93. # validation rules applied to the final user object.
  94. userValidationRules:
  95. # expression is a CEL expression that evaluates to a boolean.
  96. # all the expressions must evaluate to true for the user to be valid.
  97. - expression: "!user.username.startsWith('system:')"
  98. # Message customizes the error message seen in the API server logs when the validation fails.
  99. message: 'username cannot used reserved system: prefix'
  100. - expression: "user.groups.all(group, !group.startsWith('system:'))"
  101. message: 'groups cannot used reserved system: prefix'
  • Claim validation rule expression

    jwt.claimValidationRules[i].expression represents the expression which will be evaluated by CEL. CEL expressions have access to the contents of the token payload, organized into claims CEL variable. claims is a map of claim names (as strings) to claim values (of any type).

  • User validation rule expression

    jwt.userValidationRules[i].expression represents the expression which will be evaluated by CEL. CEL expressions have access to the contents of userInfo, organized into user CEL variable. Refer to the UserInfo API documentation for the schema of user.

  • Claim mapping expression

    jwt.claimMappings.username.expression, jwt.claimMappings.groups.expression, jwt.claimMappings.uid.expression jwt.claimMappings.extra[i].valueExpression represents the expression which will be evaluated by CEL. CEL expressions have access to the contents of the token payload, organized into claims CEL variable. claims is a map of claim names (as strings) to claim values (of any type).

    To learn more, see the Documentation on CEL

    Here are examples of the AuthenticationConfiguration with different token payloads.

    1. apiVersion: apiserver.config.k8s.io/v1beta1
    2. kind: AuthenticationConfiguration
    3. jwt:
    4. - issuer:
    5. url: https://example.com
    6. audiences:
    7. - my-app
    8. claimMappings:
    9. username:
    10. expression: 'claims.username + ":external-user"'
    11. groups:
    12. expression: 'claims.roles.split(",")'
    13. uid:
    14. expression: 'claims.sub'
    15. extra:
    16. - key: 'example.com/tenant'
    17. valueExpression: 'claims.tenant'
    18. userValidationRules:
    19. - expression: "!user.username.startsWith('system:')" # the expression will evaluate to true, so validation will succeed.
    20. message: 'username cannot used reserved system: prefix'
    1. TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJpYXQiOjE3MDExMDcyMzMsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJqdGkiOiI3YzMzNzk0MjgwN2U3M2NhYTJjMzBjODY4YWMwY2U5MTBiY2UwMmRkY2JmZWJlOGMyM2I4YjVmMjdhZDYyODczIiwibmJmIjoxNzAxMTA3MjMzLCJyb2xlcyI6InVzZXIsYWRtaW4iLCJzdWIiOiJhdXRoIiwidGVuYW50IjoiNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjRhIiwidXNlcm5hbWUiOiJmb28ifQ.TBWF2RkQHm4QQz85AYPcwLxSk-VLvQW-mNDHx7SEOSv9LVwcPYPuPajJpuQn9C_gKq1R94QKSQ5F6UgHMILz8OfmPKmX_00wpwwNVGeevJ79ieX2V-__W56iNR5gJ-i9nn6FYk5pwfVREB0l4HSlpTOmu80gbPWAXY5hLW0ZtcE1JTEEmefORHV2ge8e3jp1xGafNy6LdJWabYuKiw8d7Qga__HxtKB-t0kRMNzLRS7rka_SfQg0dSYektuxhLbiDkqhmRffGlQKXGVzUsuvFw7IGM5ZWnZgEMDzCI357obHeM3tRqpn5WRjtB8oM7JgnCymaJi-P3iCd88iu1xnzA

    where the token payload is:

    1. {
    2. "aud": "kubernetes",
    3. "exp": 1703232949,
    4. "iat": 1701107233,
    5. "iss": "https://example.com",
    6. "jti": "7c337942807e73caa2c30c868ac0ce910bce02ddcbfebe8c23b8b5f27ad62873",
    7. "nbf": 1701107233,
    8. "roles": "user,admin",
    9. "sub": "auth",
    10. "tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a",
    11. "username": "foo"
    12. }

    The token with the above AuthenticationConfiguration will produce the following UserInfo object and successfully authenticate the user.

    1. {
    2. "username": "foo:external-user",
    3. "uid": "auth",
    4. "groups": [
    5. "user",
    6. "admin"
    7. ],
    8. "extra": {
    9. "example.com/tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a"
    10. }
    11. }
    1. apiVersion: apiserver.config.k8s.io/v1beta1
    2. kind: AuthenticationConfiguration
    3. jwt:
    4. - issuer:
    5. url: https://example.com
    6. audiences:
    7. - my-app
    8. claimValidationRules:
    9. - expression: 'claims.hd == "example.com"' # the token below does not have this claim, so validation will fail.
    10. message: the hd claim must be set to example.com
    11. claimMappings:
    12. username:
    13. expression: 'claims.username + ":external-user"'
    14. groups:
    15. expression: 'claims.roles.split(",")'
    16. uid:
    17. expression: 'claims.sub'
    18. extra:
    19. - key: 'example.com/tenant'
    20. valueExpression: 'claims.tenant'
    21. userValidationRules:
    22. - expression: "!user.username.startsWith('system:')" # the expression will evaluate to true, so validation will succeed.
    23. message: 'username cannot used reserved system: prefix'
    1. TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJpYXQiOjE3MDExMDcyMzMsImlzcyI6Imh0dHBzOi8vZXhhbXBsZS5jb20iLCJqdGkiOiI3YzMzNzk0MjgwN2U3M2NhYTJjMzBjODY4YWMwY2U5MTBiY2UwMmRkY2JmZWJlOGMyM2I4YjVmMjdhZDYyODczIiwibmJmIjoxNzAxMTA3MjMzLCJyb2xlcyI6InVzZXIsYWRtaW4iLCJzdWIiOiJhdXRoIiwidGVuYW50IjoiNzJmOTg4YmYtODZmMS00MWFmLTkxYWItMmQ3Y2QwMTFkYjRhIiwidXNlcm5hbWUiOiJmb28ifQ.TBWF2RkQHm4QQz85AYPcwLxSk-VLvQW-mNDHx7SEOSv9LVwcPYPuPajJpuQn9C_gKq1R94QKSQ5F6UgHMILz8OfmPKmX_00wpwwNVGeevJ79ieX2V-__W56iNR5gJ-i9nn6FYk5pwfVREB0l4HSlpTOmu80gbPWAXY5hLW0ZtcE1JTEEmefORHV2ge8e3jp1xGafNy6LdJWabYuKiw8d7Qga__HxtKB-t0kRMNzLRS7rka_SfQg0dSYektuxhLbiDkqhmRffGlQKXGVzUsuvFw7IGM5ZWnZgEMDzCI357obHeM3tRqpn5WRjtB8oM7JgnCymaJi-P3iCd88iu1xnzA

    where the token payload is:

    1. {
    2. "aud": "kubernetes",
    3. "exp": 1703232949,
    4. "iat": 1701107233,
    5. "iss": "https://example.com",
    6. "jti": "7c337942807e73caa2c30c868ac0ce910bce02ddcbfebe8c23b8b5f27ad62873",
    7. "nbf": 1701107233,
    8. "roles": "user,admin",
    9. "sub": "auth",
    10. "tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a",
    11. "username": "foo"
    12. }

    The token with the above AuthenticationConfiguration will fail to authenticate because the hd claim is not set to example.com. The API server will return 401 Unauthorized error.

    1. apiVersion: apiserver.config.k8s.io/v1beta1
    2. kind: AuthenticationConfiguration
    3. jwt:
    4. - issuer:
    5. url: https://example.com
    6. audiences:
    7. - my-app
    8. claimValidationRules:
    9. - expression: 'claims.hd == "example.com"'
    10. message: the hd claim must be set to example.com
    11. claimMappings:
    12. username:
    13. expression: '"system:" + claims.username' # this will prefix the username with "system:" and will fail user validation.
    14. groups:
    15. expression: 'claims.roles.split(",")'
    16. uid:
    17. expression: 'claims.sub'
    18. extra:
    19. - key: 'example.com/tenant'
    20. valueExpression: 'claims.tenant'
    21. userValidationRules:
    22. - expression: "!user.username.startsWith('system:')" # the username will be system:foo and expression will evaluate to false, so validation will fail.
    23. message: 'username cannot used reserved system: prefix'
    1. TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6ImY3dF9tOEROWmFTQk1oWGw5QXZTWGhBUC04Y0JmZ0JVbFVpTG5oQkgxdXMiLCJ0eXAiOiJKV1QifQ.eyJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNzAzMjMyOTQ5LCJoZCI6ImV4YW1wbGUuY29tIiwiaWF0IjoxNzAxMTEzMTAxLCJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwianRpIjoiYjViMDY1MjM3MmNkMjBlMzQ1YjZmZGZmY2RjMjE4MWY0YWZkNmYyNTlhYWI0YjdlMzU4ODEyMzdkMjkyMjBiYyIsIm5iZiI6MTcwMTExMzEwMSwicm9sZXMiOiJ1c2VyLGFkbWluIiwic3ViIjoiYXV0aCIsInRlbmFudCI6IjcyZjk4OGJmLTg2ZjEtNDFhZi05MWFiLTJkN2NkMDExZGI0YSIsInVzZXJuYW1lIjoiZm9vIn0.FgPJBYLobo9jnbHreooBlvpgEcSPWnKfX6dc0IvdlRB-F0dCcgy91oCJeK_aBk-8zH5AKUXoFTlInfLCkPivMOJqMECA1YTrMUwt_IVqwb116AqihfByUYIIqzMjvUbthtbpIeHQm2fF0HbrUqa_Q0uaYwgy8mD807h7sBcUMjNd215ff_nFIHss-9zegH8GI1d9fiBf-g6zjkR1j987EP748khpQh9IxPjMJbSgG_uH5x80YFuqgEWwq-aYJPQxXX6FatP96a2EAn7wfPpGlPRt0HcBOvq5pCnudgCgfVgiOJiLr_7robQu4T1bis0W75VPEvwWtgFcLnvcQx0JWg

    where the token payload is:

    1. {
    2. "aud": "kubernetes",
    3. "exp": 1703232949,
    4. "hd": "example.com",
    5. "iat": 1701113101,
    6. "iss": "https://example.com",
    7. "jti": "b5b0652372cd20e345b6fdffcdc2181f4afd6f259aab4b7e35881237d29220bc",
    8. "nbf": 1701113101,
    9. "roles": "user,admin",
    10. "sub": "auth",
    11. "tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a",
    12. "username": "foo"
    13. }

    The token with the above AuthenticationConfiguration will produce the following UserInfo object:

    1. {
    2. "username": "system:foo",
    3. "uid": "auth",
    4. "groups": [
    5. "user",
    6. "admin"
    7. ],
    8. "extra": {
    9. "example.com/tenant": "72f988bf-86f1-41af-91ab-2d7cd011db4a"
    10. }
    11. }

    which will fail user validation because the username starts with system:. The API server will return 401 Unauthorized error.

Limitations
  1. Distributed claims do not work via CEL expressions.
  2. Egress selector configuration is not supported for calls to issuer.url and issuer.discoveryURL.

Kubernetes does not provide an OpenID Connect Identity Provider. You can use an existing public OpenID Connect Identity Provider (such as Google, or others). Or, you can run your own Identity Provider, such as dex, Keycloak, CloudFoundry UAA, or Tremolo Security’s OpenUnison.

For an identity provider to work with Kubernetes it must:

  1. Support OpenID connect discovery

    The public key to verify the signature is discovered from the issuer’s public endpoint using OIDC discovery. If you’re using the authentication configuration file, the identity provider doesn’t need to publicly expose the discovery endpoint. You can host the discovery endpoint at a different location than the issuer (such as locally in the cluster) and specify the issuer.discoveryURL in the configuration file.

  2. Run in TLS with non-obsolete ciphers

  3. Have a CA signed certificate (even if the CA is not a commercial CA or is self signed)

A note about requirement #3 above, requiring a CA signed certificate. If you deploy your own identity provider (as opposed to one of the cloud providers like Google or Microsoft) you MUST have your identity provider’s web server certificate signed by a certificate with the CA flag set to TRUE, even if it is self signed. This is due to GoLang’s TLS client implementation being very strict to the standards around certificate validation. If you don’t have a CA handy, you can use the gencert script from the Dex team to create a simple CA and a signed certificate and key pair. Or you can use this similar script that generates SHA256 certs with a longer life and larger key size.

Setup instructions for specific systems:

Using kubectl

Option 1 - OIDC Authenticator

The first option is to use the kubectl oidc authenticator, which sets the id_token as a bearer token for all requests and refreshes the token once it expires. After you’ve logged into your provider, use kubectl to add your id_token, refresh_token, client_id, and client_secret to configure the plugin.

Providers that don’t return an id_token as part of their refresh token response aren’t supported by this plugin and should use “Option 2” below.

  1. kubectl config set-credentials USER_NAME \
  2. --auth-provider=oidc \
  3. --auth-provider-arg=idp-issuer-url=( issuer url ) \
  4. --auth-provider-arg=client-id=( your client id ) \
  5. --auth-provider-arg=client-secret=( your client secret ) \
  6. --auth-provider-arg=refresh-token=( your refresh token ) \
  7. --auth-provider-arg=idp-certificate-authority=( path to your ca certificate ) \
  8. --auth-provider-arg=id-token=( your id_token )

As an example, running the below command after authenticating to your identity provider:

  1. kubectl config set-credentials mmosley \
  2. --auth-provider=oidc \
  3. --auth-provider-arg=idp-issuer-url=https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP \
  4. --auth-provider-arg=client-id=kubernetes \
  5. --auth-provider-arg=client-secret=1db158f6-177d-4d9c-8a8b-d36869918ec5 \
  6. --auth-provider-arg=refresh-token=q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXqHega4GAXlF+ma+vmYpFcHe5eZR+slBFpZKtQA= \
  7. --auth-provider-arg=idp-certificate-authority=/root/ca.pem \
  8. --auth-provider-arg=id-token=eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw

Which would produce the below configuration:

  1. users:
  2. - name: mmosley
  3. user:
  4. auth-provider:
  5. config:
  6. client-id: kubernetes
  7. client-secret: 1db158f6-177d-4d9c-8a8b-d36869918ec5
  8. id-token: eyJraWQiOiJDTj1vaWRjaWRwLnRyZW1vbG8ubGFuLCBPVT1EZW1vLCBPPVRybWVvbG8gU2VjdXJpdHksIEw9QXJsaW5ndG9uLCBTVD1WaXJnaW5pYSwgQz1VUy1DTj1rdWJlLWNhLTEyMDIxNDc5MjEwMzYwNzMyMTUyIiwiYWxnIjoiUlMyNTYifQ.eyJpc3MiOiJodHRwczovL29pZGNpZHAudHJlbW9sby5sYW46ODQ0My9hdXRoL2lkcC9PaWRjSWRQIiwiYXVkIjoia3ViZXJuZXRlcyIsImV4cCI6MTQ4MzU0OTUxMSwianRpIjoiMm96US15TXdFcHV4WDlHZUhQdy1hZyIsImlhdCI6MTQ4MzU0OTQ1MSwibmJmIjoxNDgzNTQ5MzMxLCJzdWIiOiI0YWViMzdiYS1iNjQ1LTQ4ZmQtYWIzMC0xYTAxZWU0MWUyMTgifQ.w6p4J_6qQ1HzTG9nrEOrubxIMb9K5hzcMPxc9IxPx2K4xO9l-oFiUw93daH3m5pluP6K7eOE6txBuRVfEcpJSwlelsOsW8gb8VJcnzMS9EnZpeA0tW_p-mnkFc3VcfyXuhe5R3G7aa5d8uHv70yJ9Y3-UhjiN9EhpMdfPAoEB9fYKKkJRzF7utTTIPGrSaSU6d2pcpfYKaxIwePzEkT4DfcQthoZdy9ucNvvLoi1DIC-UocFD8HLs8LYKEqSxQvOcvnThbObJ9af71EwmuE21fO5KzMW20KtAeget1gnldOosPtz1G5EwvaQ401-RPQzPGMVBld0_zMCAwZttJ4knw
  9. idp-certificate-authority: /root/ca.pem
  10. idp-issuer-url: https://oidcidp.tremolo.lan:8443/auth/idp/OidcIdP
  11. refresh-token: q1bKLFOyUiosTfawzA93TzZIDzH2TNa2SMm0zEiPKTUwME6BkEo6Sql5yUWVBSWpKUGphaWpxSVAfekBOZbBhaEW+VlFUeVRGcluyVF5JT4+haZmPsluFoFu5XkpXk5BXq
  12. name: oidc

Once your id_token expires, kubectl will attempt to refresh your id_token using your refresh_token and client_secret storing the new values for the refresh_token and id_token in your .kube/config.

Option 2 - Use the --token Option

The kubectl command lets you pass in a token using the --token option. Copy and paste the id_token into this option:

  1. kubectl --token=eyJhbGciOiJSUzI1NiJ9.eyJpc3MiOiJodHRwczovL21sYi50cmVtb2xvLmxhbjo4MDQzL2F1dGgvaWRwL29pZGMiLCJhdWQiOiJrdWJlcm5ldGVzIiwiZXhwIjoxNDc0NTk2NjY5LCJqdGkiOiI2RDUzNXoxUEpFNjJOR3QxaWVyYm9RIiwiaWF0IjoxNDc0NTk2MzY5LCJuYmYiOjE0NzQ1OTYyNDksInN1YiI6Im13aW5kdSIsInVzZXJfcm9sZSI6WyJ1c2VycyIsIm5ldy1uYW1lc3BhY2Utdmlld2VyIl0sImVtYWlsIjoibXdpbmR1QG5vbW9yZWplZGkuY29tIn0.f2As579n9VNoaKzoF-dOQGmXkFKf1FMyNV0-va_B63jn-_n9LGSCca_6IVMP8pO-Zb4KvRqGyTP0r3HkHxYy5c81AnIh8ijarruczl-TK_yF5akjSTHFZD-0gRzlevBDiH8Q79NAr-ky0P4iIXS8lY9Vnjch5MF74Zx0c3alKJHJUnnpjIACByfF2SCaYzbWFMUNat-K1PaUk5-ujMBG7yYnr95xD-63n8CO8teGUAAEMx6zRjzfhnhbzX-ajwZLGwGUBT4WqjMs70-6a7_8gZmLZb2az1cZynkFRj2BaCkVT3A2RrjeEwZEtGXlMqKJ1_I2ulrOVsYx01_yD35-rw get nodes

Webhook Token Authentication

Webhook authentication is a hook for verifying bearer tokens.

  • --authentication-token-webhook-config-file a configuration file describing how to access the remote webhook service.
  • --authentication-token-webhook-cache-ttl how long to cache authentication decisions. Defaults to two minutes.
  • --authentication-token-webhook-version determines whether to use authentication.k8s.io/v1beta1 or authentication.k8s.io/v1 TokenReview objects to send/receive information from the webhook. Defaults to v1beta1.

The configuration file uses the kubeconfig file format. Within the file, clusters refers to the remote service and users refers to the API server webhook. An example would be:

  1. # Kubernetes API version
  2. apiVersion: v1
  3. # kind of the API object
  4. kind: Config
  5. # clusters refers to the remote service.
  6. clusters:
  7. - name: name-of-remote-authn-service
  8. cluster:
  9. certificate-authority: /path/to/ca.pem # CA for verifying the remote service.
  10. server: https://authn.example.com/authenticate # URL of remote service to query. 'https' recommended for production.
  11. # users refers to the API server's webhook configuration.
  12. users:
  13. - name: name-of-api-server
  14. user:
  15. client-certificate: /path/to/cert.pem # cert for the webhook plugin to use
  16. client-key: /path/to/key.pem # key matching the cert
  17. # kubeconfig files require a context. Provide one for the API server.
  18. current-context: webhook
  19. contexts:
  20. - context:
  21. cluster: name-of-remote-authn-service
  22. user: name-of-api-server
  23. name: webhook

When a client attempts to authenticate with the API server using a bearer token as discussed above, the authentication webhook POSTs a JSON-serialized TokenReview object containing the token to the remote service.

Note that webhook API objects are subject to the same versioning compatibility rules as other Kubernetes API objects. Implementers should check the apiVersion field of the request to ensure correct deserialization, and must respond with a TokenReview object of the same version as the request.

Note: The Kubernetes API server defaults to sending authentication.k8s.io/v1beta1 token reviews for backwards compatibility. To opt into receiving authentication.k8s.io/v1 token reviews, the API server must be started with --authentication-token-webhook-version=v1.

  1. {
  2. "apiVersion": "authentication.k8s.io/v1",
  3. "kind": "TokenReview",
  4. "spec": {
  5. # Opaque bearer token sent to the API server
  6. "token": "014fbff9a07c...",
  7. # Optional list of the audience identifiers for the server the token was presented to.
  8. # Audience-aware token authenticators (for example, OIDC token authenticators)
  9. # should verify the token was intended for at least one of the audiences in this list,
  10. # and return the intersection of this list and the valid audiences for the token in the response status.
  11. # This ensures the token is valid to authenticate to the server it was presented to.
  12. # If no audiences are provided, the token should be validated to authenticate to the Kubernetes API server.
  13. "audiences": ["https://myserver.example.com", "https://myserver.internal.example.com"]
  14. }
  15. }
  1. {
  2. "apiVersion": "authentication.k8s.io/v1beta1",
  3. "kind": "TokenReview",
  4. "spec": {
  5. # Opaque bearer token sent to the API server
  6. "token": "014fbff9a07c...",
  7. # Optional list of the audience identifiers for the server the token was presented to.
  8. # Audience-aware token authenticators (for example, OIDC token authenticators)
  9. # should verify the token was intended for at least one of the audiences in this list,
  10. # and return the intersection of this list and the valid audiences for the token in the response status.
  11. # This ensures the token is valid to authenticate to the server it was presented to.
  12. # If no audiences are provided, the token should be validated to authenticate to the Kubernetes API server.
  13. "audiences": ["https://myserver.example.com", "https://myserver.internal.example.com"]
  14. }
  15. }

The remote service is expected to fill the status field of the request to indicate the success of the login. The response body’s spec field is ignored and may be omitted. The remote service must return a response using the same TokenReview API version that it received. A successful validation of the bearer token would return:

  1. {
  2. "apiVersion": "authentication.k8s.io/v1",
  3. "kind": "TokenReview",
  4. "status": {
  5. "authenticated": true,
  6. "user": {
  7. # Required
  8. "username": "janedoe@example.com",
  9. # Optional
  10. "uid": "42",
  11. # Optional group memberships
  12. "groups": ["developers", "qa"],
  13. # Optional additional information provided by the authenticator.
  14. # This should not contain confidential data, as it can be recorded in logs
  15. # or API objects, and is made available to admission webhooks.
  16. "extra": {
  17. "extrafield1": [
  18. "extravalue1",
  19. "extravalue2"
  20. ]
  21. }
  22. },
  23. # Optional list audience-aware token authenticators can return,
  24. # containing the audiences from the `spec.audiences` list for which the provided token was valid.
  25. # If this is omitted, the token is considered to be valid to authenticate to the Kubernetes API server.
  26. "audiences": ["https://myserver.example.com"]
  27. }
  28. }
  1. {
  2. "apiVersion": "authentication.k8s.io/v1beta1",
  3. "kind": "TokenReview",
  4. "status": {
  5. "authenticated": true,
  6. "user": {
  7. # Required
  8. "username": "janedoe@example.com",
  9. # Optional
  10. "uid": "42",
  11. # Optional group memberships
  12. "groups": ["developers", "qa"],
  13. # Optional additional information provided by the authenticator.
  14. # This should not contain confidential data, as it can be recorded in logs
  15. # or API objects, and is made available to admission webhooks.
  16. "extra": {
  17. "extrafield1": [
  18. "extravalue1",
  19. "extravalue2"
  20. ]
  21. }
  22. },
  23. # Optional list audience-aware token authenticators can return,
  24. # containing the audiences from the `spec.audiences` list for which the provided token was valid.
  25. # If this is omitted, the token is considered to be valid to authenticate to the Kubernetes API server.
  26. "audiences": ["https://myserver.example.com"]
  27. }
  28. }

An unsuccessful request would return:

  1. {
  2. "apiVersion": "authentication.k8s.io/v1",
  3. "kind": "TokenReview",
  4. "status": {
  5. "authenticated": false,
  6. # Optionally include details about why authentication failed.
  7. # If no error is provided, the API will return a generic Unauthorized message.
  8. # The error field is ignored when authenticated=true.
  9. "error": "Credentials are expired"
  10. }
  11. }
  1. {
  2. "apiVersion": "authentication.k8s.io/v1beta1",
  3. "kind": "TokenReview",
  4. "status": {
  5. "authenticated": false,
  6. # Optionally include details about why authentication failed.
  7. # If no error is provided, the API will return a generic Unauthorized message.
  8. # The error field is ignored when authenticated=true.
  9. "error": "Credentials are expired"
  10. }
  11. }

Authenticating Proxy

The API server can be configured to identify users from request header values, such as X-Remote-User. It is designed for use in combination with an authenticating proxy, which sets the request header value.

  • --requestheader-username-headers Required, case-insensitive. Header names to check, in order, for the user identity. The first header containing a value is used as the username.
  • --requestheader-group-headers 1.6+. Optional, case-insensitive. “X-Remote-Group” is suggested. Header names to check, in order, for the user’s groups. All values in all specified headers are used as group names.
  • --requestheader-extra-headers-prefix 1.6+. Optional, case-insensitive. “X-Remote-Extra-“ is suggested. Header prefixes to look for to determine extra information about the user (typically used by the configured authorization plugin). Any headers beginning with any of the specified prefixes have the prefix removed. The remainder of the header name is lowercased and percent-decoded and becomes the extra key, and the header value is the extra value.

Note: Prior to 1.11.3 (and 1.10.7, 1.9.11), the extra key could only contain characters which were legal in HTTP header labels.

For example, with this configuration:

  1. --requestheader-username-headers=X-Remote-User
  2. --requestheader-group-headers=X-Remote-Group
  3. --requestheader-extra-headers-prefix=X-Remote-Extra-

this request:

  1. GET / HTTP/1.1
  2. X-Remote-User: fido
  3. X-Remote-Group: dogs
  4. X-Remote-Group: dachshunds
  5. X-Remote-Extra-Acme.com%2Fproject: some-project
  6. X-Remote-Extra-Scopes: openid
  7. X-Remote-Extra-Scopes: profile

would result in this user info:

  1. name: fido
  2. groups:
  3. - dogs
  4. - dachshunds
  5. extra:
  6. acme.com/project:
  7. - some-project
  8. scopes:
  9. - openid
  10. - profile

In order to prevent header spoofing, the authenticating proxy is required to present a valid client certificate to the API server for validation against the specified CA before the request headers are checked. WARNING: do not reuse a CA that is used in a different context unless you understand the risks and the mechanisms to protect the CA’s usage.

  • --requestheader-client-ca-file Required. PEM-encoded certificate bundle. A valid client certificate must be presented and validated against the certificate authorities in the specified file before the request headers are checked for user names.
  • --requestheader-allowed-names Optional. List of Common Name values (CNs). If set, a valid client certificate with a CN in the specified list must be presented before the request headers are checked for user names. If empty, any CN is allowed.

Anonymous requests

When enabled, requests that are not rejected by other configured authentication methods are treated as anonymous requests, and given a username of system:anonymous and a group of system:unauthenticated.

For example, on a server with token authentication configured, and anonymous access enabled, a request providing an invalid bearer token would receive a 401 Unauthorized error. A request providing no bearer token would be treated as an anonymous request.

In 1.5.1-1.5.x, anonymous access is disabled by default, and can be enabled by passing the --anonymous-auth=true option to the API server.

In 1.6+, anonymous access is enabled by default if an authorization mode other than AlwaysAllow is used, and can be disabled by passing the --anonymous-auth=false option to the API server. Starting in 1.6, the ABAC and RBAC authorizers require explicit authorization of the system:anonymous user or the system:unauthenticated group, so legacy policy rules that grant access to the * user or * group do not include anonymous users.

User impersonation

A user can act as another user through impersonation headers. These let requests manually override the user info a request authenticates as. For example, an admin could use this feature to debug an authorization policy by temporarily impersonating another user and seeing if a request was denied.

Impersonation requests first authenticate as the requesting user, then switch to the impersonated user info.

  • A user makes an API call with their credentials and impersonation headers.
  • API server authenticates the user.
  • API server ensures the authenticated users have impersonation privileges.
  • Request user info is replaced with impersonation values.
  • Request is evaluated, authorization acts on impersonated user info.

The following HTTP headers can be used to performing an impersonation request:

  • Impersonate-User: The username to act as.
  • Impersonate-Group: A group name to act as. Can be provided multiple times to set multiple groups. Optional. Requires “Impersonate-User”.
  • Impersonate-Extra-( extra name ): A dynamic header used to associate extra fields with the user. Optional. Requires “Impersonate-User”. In order to be preserved consistently, ( extra name ) must be lower-case, and any characters which aren’t legal in HTTP header labels MUST be utf8 and percent-encoded.
  • Impersonate-Uid: A unique identifier that represents the user being impersonated. Optional. Requires “Impersonate-User”. Kubernetes does not impose any format requirements on this string.

Note: Prior to 1.11.3 (and 1.10.7, 1.9.11), ( extra name ) could only contain characters which were legal in HTTP header labels.

Note: Impersonate-Uid is only available in versions 1.22.0 and higher.

An example of the impersonation headers used when impersonating a user with groups:

  1. Impersonate-User: jane.doe@example.com
  2. Impersonate-Group: developers
  3. Impersonate-Group: admins

An example of the impersonation headers used when impersonating a user with a UID and extra fields:

  1. Impersonate-User: jane.doe@example.com
  2. Impersonate-Extra-dn: cn=jane,ou=engineers,dc=example,dc=com
  3. Impersonate-Extra-acme.com%2Fproject: some-project
  4. Impersonate-Extra-scopes: view
  5. Impersonate-Extra-scopes: development
  6. Impersonate-Uid: 06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b

When using kubectl set the --as flag to configure the Impersonate-User header, set the --as-group flag to configure the Impersonate-Group header.

  1. kubectl drain mynode
  1. Error from server (Forbidden): User "clark" cannot get nodes at the cluster scope. (get nodes mynode)

Set the --as and --as-group flag:

  1. kubectl drain mynode --as=superman --as-group=system:masters
  1. node/mynode cordoned
  2. node/mynode drained

Note: kubectl cannot impersonate extra fields or UIDs.

To impersonate a user, group, user identifier (UID) or extra fields, the impersonating user must have the ability to perform the “impersonate” verb on the kind of attribute being impersonated (“user”, “group”, “uid”, etc.). For clusters that enable the RBAC authorization plugin, the following ClusterRole encompasses the rules needed to set user and group impersonation headers:

  1. apiVersion: rbac.authorization.k8s.io/v1
  2. kind: ClusterRole
  3. metadata:
  4. name: impersonator
  5. rules:
  6. - apiGroups: [""]
  7. resources: ["users", "groups", "serviceaccounts"]
  8. verbs: ["impersonate"]

For impersonation, extra fields and impersonated UIDs are both under the “authentication.k8s.io” apiGroup. Extra fields are evaluated as sub-resources of the resource “userextras”. To allow a user to use impersonation headers for the extra field “scopes” and for UIDs, a user should be granted the following role:

  1. apiVersion: rbac.authorization.k8s.io/v1
  2. kind: ClusterRole
  3. metadata:
  4. name: scopes-and-uid-impersonator
  5. rules:
  6. # Can set "Impersonate-Extra-scopes" header and the "Impersonate-Uid" header.
  7. - apiGroups: ["authentication.k8s.io"]
  8. resources: ["userextras/scopes", "uids"]
  9. verbs: ["impersonate"]

The values of impersonation headers can also be restricted by limiting the set of resourceNames a resource can take.

  1. apiVersion: rbac.authorization.k8s.io/v1
  2. kind: ClusterRole
  3. metadata:
  4. name: limited-impersonator
  5. rules:
  6. # Can impersonate the user "jane.doe@example.com"
  7. - apiGroups: [""]
  8. resources: ["users"]
  9. verbs: ["impersonate"]
  10. resourceNames: ["jane.doe@example.com"]
  11. # Can impersonate the groups "developers" and "admins"
  12. - apiGroups: [""]
  13. resources: ["groups"]
  14. verbs: ["impersonate"]
  15. resourceNames: ["developers","admins"]
  16. # Can impersonate the extras field "scopes" with the values "view" and "development"
  17. - apiGroups: ["authentication.k8s.io"]
  18. resources: ["userextras/scopes"]
  19. verbs: ["impersonate"]
  20. resourceNames: ["view", "development"]
  21. # Can impersonate the uid "06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"
  22. - apiGroups: ["authentication.k8s.io"]
  23. resources: ["uids"]
  24. verbs: ["impersonate"]
  25. resourceNames: ["06f6ce97-e2c5-4ab8-7ba5-7654dd08d52b"]

Note: Impersonating a user or group allows you to perform any action as if you were that user or group; for that reason, impersonation is not namespace scoped. If you want to allow impersonation using Kubernetes RBAC, this requires using a ClusterRole and a ClusterRoleBinding, not a Role and RoleBinding.

client-go credential plugins

FEATURE STATE: Kubernetes v1.22 [stable]

k8s.io/client-go and tools using it such as kubectl and kubelet are able to execute an external command to receive user credentials.

This feature is intended for client side integrations with authentication protocols not natively supported by k8s.io/client-go (LDAP, Kerberos, OAuth2, SAML, etc.). The plugin implements the protocol specific logic, then returns opaque credentials to use. Almost all credential plugin use cases require a server side component with support for the webhook token authenticator to interpret the credential format produced by the client plugin.

Note: Earlier versions of kubectl included built-in support for authenticating to AKS and GKE, but this is no longer present.

Example use case

In a hypothetical use case, an organization would run an external service that exchanges LDAP credentials for user specific, signed tokens. The service would also be capable of responding to webhook token authenticator requests to validate the tokens. Users would be required to install a credential plugin on their workstation.

To authenticate against the API:

  • The user issues a kubectl command.
  • Credential plugin prompts the user for LDAP credentials, exchanges credentials with external service for a token.
  • Credential plugin returns token to client-go, which uses it as a bearer token against the API server.
  • API server uses the webhook token authenticator to submit a TokenReview to the external service.
  • External service verifies the signature on the token and returns the user’s username and groups.

Configuration

Credential plugins are configured through kubectl config files as part of the user fields.

  1. apiVersion: v1
  2. kind: Config
  3. users:
  4. - name: my-user
  5. user:
  6. exec:
  7. # Command to execute. Required.
  8. command: "example-client-go-exec-plugin"
  9. # API version to use when decoding the ExecCredentials resource. Required.
  10. #
  11. # The API version returned by the plugin MUST match the version listed here.
  12. #
  13. # To integrate with tools that support multiple versions (such as client.authentication.k8s.io/v1beta1),
  14. # set an environment variable, pass an argument to the tool that indicates which version the exec plugin expects,
  15. # or read the version from the ExecCredential object in the KUBERNETES_EXEC_INFO environment variable.
  16. apiVersion: "client.authentication.k8s.io/v1"
  17. # Environment variables to set when executing the plugin. Optional.
  18. env:
  19. - name: "FOO"
  20. value: "bar"
  21. # Arguments to pass when executing the plugin. Optional.
  22. args:
  23. - "arg1"
  24. - "arg2"
  25. # Text shown to the user when the executable doesn't seem to be present. Optional.
  26. installHint: |
  27. example-client-go-exec-plugin is required to authenticate
  28. to the current cluster. It can be installed:
  29. On macOS: brew install example-client-go-exec-plugin
  30. On Ubuntu: apt-get install example-client-go-exec-plugin
  31. On Fedora: dnf install example-client-go-exec-plugin
  32. ...
  33. # Whether or not to provide cluster information, which could potentially contain
  34. # very large CA data, to this exec plugin as a part of the KUBERNETES_EXEC_INFO
  35. # environment variable.
  36. provideClusterInfo: true
  37. # The contract between the exec plugin and the standard input I/O stream. If the
  38. # contract cannot be satisfied, this plugin will not be run and an error will be
  39. # returned. Valid values are "Never" (this exec plugin never uses standard input),
  40. # "IfAvailable" (this exec plugin wants to use standard input if it is available),
  41. # or "Always" (this exec plugin requires standard input to function). Required.
  42. interactiveMode: Never
  43. clusters:
  44. - name: my-cluster
  45. cluster:
  46. server: "https://172.17.4.100:6443"
  47. certificate-authority: "/etc/kubernetes/ca.pem"
  48. extensions:
  49. - name: client.authentication.k8s.io/exec # reserved extension name for per cluster exec config
  50. extension:
  51. arbitrary: config
  52. this: can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo
  53. you: ["can", "put", "anything", "here"]
  54. contexts:
  55. - name: my-cluster
  56. context:
  57. cluster: my-cluster
  58. user: my-user
  59. current-context: my-cluster
  1. apiVersion: v1
  2. kind: Config
  3. users:
  4. - name: my-user
  5. user:
  6. exec:
  7. # Command to execute. Required.
  8. command: "example-client-go-exec-plugin"
  9. # API version to use when decoding the ExecCredentials resource. Required.
  10. #
  11. # The API version returned by the plugin MUST match the version listed here.
  12. #
  13. # To integrate with tools that support multiple versions (such as client.authentication.k8s.io/v1),
  14. # set an environment variable, pass an argument to the tool that indicates which version the exec plugin expects,
  15. # or read the version from the ExecCredential object in the KUBERNETES_EXEC_INFO environment variable.
  16. apiVersion: "client.authentication.k8s.io/v1beta1"
  17. # Environment variables to set when executing the plugin. Optional.
  18. env:
  19. - name: "FOO"
  20. value: "bar"
  21. # Arguments to pass when executing the plugin. Optional.
  22. args:
  23. - "arg1"
  24. - "arg2"
  25. # Text shown to the user when the executable doesn't seem to be present. Optional.
  26. installHint: |
  27. example-client-go-exec-plugin is required to authenticate
  28. to the current cluster. It can be installed:
  29. On macOS: brew install example-client-go-exec-plugin
  30. On Ubuntu: apt-get install example-client-go-exec-plugin
  31. On Fedora: dnf install example-client-go-exec-plugin
  32. ...
  33. # Whether or not to provide cluster information, which could potentially contain
  34. # very large CA data, to this exec plugin as a part of the KUBERNETES_EXEC_INFO
  35. # environment variable.
  36. provideClusterInfo: true
  37. # The contract between the exec plugin and the standard input I/O stream. If the
  38. # contract cannot be satisfied, this plugin will not be run and an error will be
  39. # returned. Valid values are "Never" (this exec plugin never uses standard input),
  40. # "IfAvailable" (this exec plugin wants to use standard input if it is available),
  41. # or "Always" (this exec plugin requires standard input to function). Optional.
  42. # Defaults to "IfAvailable".
  43. interactiveMode: Never
  44. clusters:
  45. - name: my-cluster
  46. cluster:
  47. server: "https://172.17.4.100:6443"
  48. certificate-authority: "/etc/kubernetes/ca.pem"
  49. extensions:
  50. - name: client.authentication.k8s.io/exec # reserved extension name for per cluster exec config
  51. extension:
  52. arbitrary: config
  53. this: can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo
  54. you: ["can", "put", "anything", "here"]
  55. contexts:
  56. - name: my-cluster
  57. context:
  58. cluster: my-cluster
  59. user: my-user
  60. current-context: my-cluster

Relative command paths are interpreted as relative to the directory of the config file. If KUBECONFIG is set to /home/jane/kubeconfig and the exec command is ./bin/example-client-go-exec-plugin, the binary /home/jane/bin/example-client-go-exec-plugin is executed.

  1. - name: my-user
  2. user:
  3. exec:
  4. # Path relative to the directory of the kubeconfig
  5. command: "./bin/example-client-go-exec-plugin"
  6. apiVersion: "client.authentication.k8s.io/v1"
  7. interactiveMode: Never

Input and output formats

The executed command prints an ExecCredential object to stdout. k8s.io/client-go authenticates against the Kubernetes API using the returned credentials in the status. The executed command is passed an ExecCredential object as input via the KUBERNETES_EXEC_INFO environment variable. This input contains helpful information like the expected API version of the returned ExecCredential object and whether or not the plugin can use stdin to interact with the user.

When run from an interactive session (i.e., a terminal), stdin can be exposed directly to the plugin. Plugins should use the spec.interactive field of the input ExecCredential object from the KUBERNETES_EXEC_INFO environment variable in order to determine if stdin has been provided. A plugin’s stdin requirements (i.e., whether stdin is optional, strictly required, or never used in order for the plugin to run successfully) is declared via the user.exec.interactiveMode field in the kubeconfig (see table below for valid values). The user.exec.interactiveMode field is optional in client.authentication.k8s.io/v1beta1 and required in client.authentication.k8s.io/v1.

interactiveMode values
interactiveMode ValueMeaning
NeverThis exec plugin never needs to use standard input, and therefore the exec plugin will be run regardless of whether standard input is available for user input.
IfAvailableThis exec plugin would like to use standard input if it is available, but can still operate if standard input is not available. Therefore, the exec plugin will be run regardless of whether stdin is available for user input. If standard input is available for user input, then it will be provided to this exec plugin.
AlwaysThis exec plugin requires standard input in order to run, and therefore the exec plugin will only be run if standard input is available for user input. If standard input is not available for user input, then the exec plugin will not be run and an error will be returned by the exec plugin runner.

To use bearer token credentials, the plugin returns a token in the status of the ExecCredential

  1. {
  2. "apiVersion": "client.authentication.k8s.io/v1",
  3. "kind": "ExecCredential",
  4. "status": {
  5. "token": "my-bearer-token"
  6. }
  7. }
  1. {
  2. "apiVersion": "client.authentication.k8s.io/v1beta1",
  3. "kind": "ExecCredential",
  4. "status": {
  5. "token": "my-bearer-token"
  6. }
  7. }

Alternatively, a PEM-encoded client certificate and key can be returned to use TLS client auth. If the plugin returns a different certificate and key on a subsequent call, k8s.io/client-go will close existing connections with the server to force a new TLS handshake.

If specified, clientKeyData and clientCertificateData must both must be present.

clientCertificateData may contain additional intermediate certificates to send to the server.

  1. {
  2. "apiVersion": "client.authentication.k8s.io/v1",
  3. "kind": "ExecCredential",
  4. "status": {
  5. "clientCertificateData": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
  6. "clientKeyData": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
  7. }
  8. }
  1. {
  2. "apiVersion": "client.authentication.k8s.io/v1beta1",
  3. "kind": "ExecCredential",
  4. "status": {
  5. "clientCertificateData": "-----BEGIN CERTIFICATE-----\n...\n-----END CERTIFICATE-----",
  6. "clientKeyData": "-----BEGIN RSA PRIVATE KEY-----\n...\n-----END RSA PRIVATE KEY-----"
  7. }
  8. }

Optionally, the response can include the expiry of the credential formatted as a RFC 3339 timestamp.

Presence or absence of an expiry has the following impact:

  • If an expiry is included, the bearer token and TLS credentials are cached until the expiry time is reached, or if the server responds with a 401 HTTP status code, or when the process exits.
  • If an expiry is omitted, the bearer token and TLS credentials are cached until the server responds with a 401 HTTP status code or until the process exits.

  • client.authentication.k8s.io/v1

  • client.authentication.k8s.io/v1beta1
  1. {
  2. "apiVersion": "client.authentication.k8s.io/v1",
  3. "kind": "ExecCredential",
  4. "status": {
  5. "token": "my-bearer-token",
  6. "expirationTimestamp": "2018-03-05T17:30:20-08:00"
  7. }
  8. }
  1. {
  2. "apiVersion": "client.authentication.k8s.io/v1beta1",
  3. "kind": "ExecCredential",
  4. "status": {
  5. "token": "my-bearer-token",
  6. "expirationTimestamp": "2018-03-05T17:30:20-08:00"
  7. }
  8. }

To enable the exec plugin to obtain cluster-specific information, set provideClusterInfo on the user.exec field in the kubeconfig. The plugin will then be supplied this cluster-specific information in the KUBERNETES_EXEC_INFO environment variable. Information from this environment variable can be used to perform cluster-specific credential acquisition logic. The following ExecCredential manifest describes a cluster information sample.

  1. {
  2. "apiVersion": "client.authentication.k8s.io/v1",
  3. "kind": "ExecCredential",
  4. "spec": {
  5. "cluster": {
  6. "server": "https://172.17.4.100:6443",
  7. "certificate-authority-data": "LS0t...",
  8. "config": {
  9. "arbitrary": "config",
  10. "this": "can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo",
  11. "you": ["can", "put", "anything", "here"]
  12. }
  13. },
  14. "interactive": true
  15. }
  16. }
  1. {
  2. "apiVersion": "client.authentication.k8s.io/v1beta1",
  3. "kind": "ExecCredential",
  4. "spec": {
  5. "cluster": {
  6. "server": "https://172.17.4.100:6443",
  7. "certificate-authority-data": "LS0t...",
  8. "config": {
  9. "arbitrary": "config",
  10. "this": "can be provided via the KUBERNETES_EXEC_INFO environment variable upon setting provideClusterInfo",
  11. "you": ["can", "put", "anything", "here"]
  12. }
  13. },
  14. "interactive": true
  15. }
  16. }

API access to authentication information for a client

FEATURE STATE: Kubernetes v1.28 [stable]

If your cluster has the API enabled, you can use the SelfSubjectReview API to find out how your Kubernetes cluster maps your authentication information to identify you as a client. This works whether you are authenticating as a user (typically representing a real person) or as a ServiceAccount.

SelfSubjectReview objects do not have any configurable fields. On receiving a request, the Kubernetes API server fills the status with the user attributes and returns it to the user.

Request example (the body would be a SelfSubjectReview):

  1. POST /apis/authentication.k8s.io/v1/selfsubjectreviews
  1. {
  2. "apiVersion": "authentication.k8s.io/v1",
  3. "kind": "SelfSubjectReview"
  4. }

Response example:

  1. {
  2. "apiVersion": "authentication.k8s.io/v1",
  3. "kind": "SelfSubjectReview",
  4. "status": {
  5. "userInfo": {
  6. "name": "jane.doe",
  7. "uid": "b6c7cfd4-f166-11ec-8ea0-0242ac120002",
  8. "groups": [
  9. "viewers",
  10. "editors",
  11. "system:authenticated"
  12. ],
  13. "extra": {
  14. "provider_id": ["token.company.example"]
  15. }
  16. }
  17. }
  18. }

For convenience, the kubectl auth whoami command is present. Executing this command will produce the following output (yet different user attributes will be shown):

  • Simple output example

    1. ATTRIBUTE VALUE
    2. Username jane.doe
    3. Groups [system:authenticated]
  • Complex example including extra attributes

    1. ATTRIBUTE VALUE
    2. Username jane.doe
    3. UID b79dbf30-0c6a-11ed-861d-0242ac120002
    4. Groups [students teachers system:authenticated]
    5. Extra: skills [reading learning]
    6. Extra: subjects [math sports]

By providing the output flag, it is also possible to print the JSON or YAML representation of the result:

  1. {
  2. "apiVersion": "authentication.k8s.io/v1",
  3. "kind": "SelfSubjectReview",
  4. "status": {
  5. "userInfo": {
  6. "username": "jane.doe",
  7. "uid": "b79dbf30-0c6a-11ed-861d-0242ac120002",
  8. "groups": [
  9. "students",
  10. "teachers",
  11. "system:authenticated"
  12. ],
  13. "extra": {
  14. "skills": [
  15. "reading",
  16. "learning"
  17. ],
  18. "subjects": [
  19. "math",
  20. "sports"
  21. ]
  22. }
  23. }
  24. }
  25. }
  1. apiVersion: authentication.k8s.io/v1
  2. kind: SelfSubjectReview
  3. status:
  4. userInfo:
  5. username: jane.doe
  6. uid: b79dbf30-0c6a-11ed-861d-0242ac120002
  7. groups:
  8. - students
  9. - teachers
  10. - system:authenticated
  11. extra:
  12. skills:
  13. - reading
  14. - learning
  15. subjects:
  16. - math
  17. - sports

This feature is extremely useful when a complicated authentication flow is used in a Kubernetes cluster, for example, if you use webhook token authentication or authenticating proxy.

Note: The Kubernetes API server fills the userInfo after all authentication mechanisms are applied, including impersonation. If you, or an authentication proxy, make a SelfSubjectReview using impersonation, you see the user details and properties for the user that was impersonated.

By default, all authenticated users can create SelfSubjectReview objects when the APISelfSubjectReview feature is enabled. It is allowed by the system:basic-user cluster role.

Note:

You can only make SelfSubjectReview requests if:

  • the APISelfSubjectReview feature gate is enabled for your cluster (not needed for Kubernetes 1.30, but older Kubernetes versions might not offer this feature gate, or might default it to be off)
  • (if you are running a version of Kubernetes older than v1.28) the API server for your cluster has the authentication.k8s.io/v1alpha1 or authentication.k8s.io/v1beta1 API group enabled.

What’s next