1. Users in Kubernetes

Users access the Kubernetes API using kubectl, client libraries, or by making REST requests, through several stages, illustrated in the following diagram: [1]

Components of Kubernetes Diagram of request handling steps for Kubernetes API request

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

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

Kubernetes does not have objects which represent normal user accounts.

Any user that presents a valid certificate signed by the cluster’s certificate authority (CA) is considered authenticated. API requests are tied to either a normal user or a service account, or are treated as anonymous requests.

  • Kubernetes determines the username from the common name and groups from organization name in the 'subject' of the cert (e.g., "/CN=bob/O=group1/O=group2").

  • The role based access control (RBAC) sub-system would determine whether the user or group is authorized to perform a specific operation on a resource.

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, mounted into pods allowing in-cluster processes to talk to the Kubernetes API.

2. Authentication

Kubernetes uses client certificates, bearer tokens, an authenticating proxy to authenticate API requests through authentication plugins. [2]

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.

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

2.1. 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.

  • Client certificates can also indicate a user’s group memberships using the certificate’s organization fields.

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

openssl req \
    -nodes -newkey rsa -keyout 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".

2.2. 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.

    $ kubectl get -n kube-system po coredns-7b44686977-vlt44 -oyaml
    ...
    spec:
      containers:
      - args:
        volumeMounts:
        - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
          name: kube-api-access-7xmwj
          readOnly: true
      serviceAccount: coredns
      serviceAccountName: coredns
      volumes:
      - name: kube-api-access-7xmwj
        projected:
          defaultMode: 420
          sources:
          - serviceAccountToken:
              expirationSeconds: 3607
              path: token
          - configMap:
              items:
              - key: ca.crt
                path: ca.crt
              name: kube-root-ca.crt
          - downwardAPI:
              items:
              - fieldRef:
                  apiVersion: v1
                  fieldPath: metadata.namespace
                path: namespace
    ...

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.

# creates a service account in the current namespace.
kubectl create serviceaccount jenkins
# create an associated token, a signed JSON Web Token (JWT).
kubectl create token jenkins

The signed JWT can be used as a bearer token (i.e., Authorization: Bearer <token>) to authenticate as the given service account.

JSON Web Tokens consist of three parts separated by dots (.), which are: <Header>.<Payload>.<Signature>.

$ kubectl create token jenkins | cut -d '.' -f2 | base64 -d | jq
{
  "aud": [
    "https://kubernetes.default.svc.cluster.local"
  ],
  "exp": 1709617047,
  "iat": 1709613447,
  "iss": "https://kubernetes.default.svc.cluster.local",
  "kubernetes.io": {
    "namespace": "default",
    "serviceaccount": {
      "name": "jenkins",
      "uid": "7456ed0e-5b31-444e-85c0-d9db42f3984a"
    }
  },
  "nbf": 1709613447,
  "sub": "system:serviceaccount:default:jenkins"
}
Service accounts authenticate with the username system:serviceaccount:(NAMESPACE):(SERVICEACCOUNT), and are assigned to the groups system:serviceaccounts and system:serviceaccounts:(NAMESPACE).

2.3. User impersonation

A user can act as another user through impersonation headers, which can 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.

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

$ kubectl get pod --as nobody
Error from server (Forbidden): pods is forbidden: User "nobody" cannot list resource "pods" in API group "" in the namespace "default"

2.4. 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.

POST /apis/authentication.k8s.io/v1/selfsubjectreviews

{
  "apiVersion": "authentication.k8s.io/v1",
  "kind": "SelfSubjectReview"
}

For convenience, the kubectl auth whoami command is present.

$ kubectl auth whoami -oyaml
apiVersion: authentication.k8s.io/v1
kind: SelfSubjectReview
metadata:
  creationTimestamp: "2024-03-05T05:31:48Z"
status:
  userInfo:
    groups:
    - kubeadm:cluster-admins
    - system:authenticated
    username: kubernetes-admin

3. Authorization

Kubernetes authorizes API requests using the API server, and evaluates all of the authenticated request attributes against all policies and allows or denies the request. [3]

  • user - The user string provided during authentication.

  • group - The list of group names to which the authenticated user belongs.

  • extra - A map of arbitrary string keys to string values, provided by the authentication layer.

  • API - Indicates whether the request is for an API resource.

  • Request path - Path to miscellaneous non-resource endpoints like /api or /healthz.

  • API request verb - API verbs like get, list, create, update, patch, watch, delete, and deletecollection are used for resource requests.

  • HTTP request verb - Lowercased HTTP methods like get, post, put, and delete are used for non-resource requests.

  • Resource - The ID or name of the resource that is being accessed (for resource requests only) — For resource requests using get, update, patch, and delete verbs, you must provide the resource name.

  • Subresource - The subresource that is being accessed (for resource requests only).

  • Namespace - The namespace of the object that is being accessed (for namespaced resource requests only).

  • API group - The API Group being accessed (for resource requests only). An empty string designates the core API group.

3.1. Determine the Request Verb

  • Non-resource requests

    Requests to endpoints other than /api/v1/…​ or /apis/<group>/<version>/…​ are considered "non-resource requests", and use the lower-cased HTTP method of the request as the verb.

    For example, a GET request to endpoints like /api or /healthz would use get as the verb.

  • Resource requests

    To determine the request verb for a resource API endpoint, review the HTTP verb used and whether or not the request acts on an individual resource or a collection of resources:

    HTTP verb request verb

    POST

    create

    GET, HEAD

    get (for individual resources), list (for collections, including full object content), watch (for watching an individual resource or collection of resources)

    PUT

    update

    PATCH

    patch

    DELETE

    delete (for individual resources), deletecollection (for collections)

Kubernetes sometimes checks authorization for additional permissions using specialized verbs. For example:

  • RBAC

    bind and escalate verbs on roles and clusterroles resources in the rbac.authorization.k8s.io API group.

  • Authentication

    impersonate verb on users, groups, and serviceaccounts in the core API group, and the userextras in the authentication.k8s.io API group.

3.2. Authorization Modes

The Kubernetes API server may authorize a request using one of several authorization modes:

  • Node - A special-purpose authorization mode that grants permissions to kubelets based on the pods they are scheduled to run.

  • ABAC - Attribute-based access control (ABAC) defines an access control paradigm whereby access rights are granted to users through the use of policies which combine attributes together.

  • RBAC - Role-based access control (RBAC) is a method of regulating access to computer or network resources based on the roles of individual users within an enterprise.

  • Webhook - A WebHook is an HTTP callback: an HTTP POST that occurs when something happens; a simple event-notification via HTTP POST.

3.3. Checking API Access

The command kubectl provides the auth can-i subcommand for quickly querying the API authorization layer, that uses the SelfSubjectAccessReview API to determine if the current user can perform a given action, and works regardless of the authorization mode used.

kubectl auth can-i create deployments --namespace dev
yes
kubectl auth can-i create deployments --namespace prod
no
kubectl auth can-i list secrets --namespace dev --as dave
no
kubectl auth can-i list pods \
	--namespace target \
	--as system:serviceaccount:dev:dev-sa
yes

These SelfSubjectRulesReview API can be queried by creating normal Kubernetes resources, where the response "status" field of the returned object is the result of the query.

kubectl create -f - -o yaml << EOF
apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
spec:
  resourceAttributes:
    group: apps
    resource: deployments
    verb: create
    namespace: dev
EOF
apiVersion: authorization.k8s.io/v1
kind: SelfSubjectAccessReview
...
status:
  allowed: true
  reason: 'RBAC: allowed by ClusterRoleBinding "kubeadm:cluster-admins" of ClusterRole
    "cluster-admin" to Group "kubeadm:cluster-admins"'

3.4. RBAC Authorization

Role-based access control (RBAC) is a method of regulating access to computer or network resources based on the roles of individual users within your organization.

RBAC authorization uses the rbac.authorization.k8s.io API group to drive authorization decisions, allowing you to dynamically configure policies through the Kubernetes API.

3.4.1. Role and ClusterRole

An RBAC Role or ClusterRole contains rules that represent a set of permissions. Permissions are purely additive (there are no "deny" rules).

  • A Role always sets permissions within a particular namespace; when you create a Role, you have to specify the namespace it belongs in.

  • By contrast, ClusterRole, is a non-namespaced resource.

The resources have different names (Role and ClusterRole) because a Kubernetes object always has to be either namespaced or not namespaced; it can’t be both.

Here’s an example Role in the "default" namespace that can be used to grant read access to pods:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

Because ClusterRoles are cluster-scoped, they can also used to grant access to:

  • cluster-scoped resources (like nodes)

  • non-resource endpoints (like /healthz)

  • namespaced resources (like Pods), across all namespaces

    For example: use a ClusterRole to allow a particular user to run kubectl get pods --all-namespaces.

Here is an example of a ClusterRole that can be used to grant read access to secrets in any particular namespace, or across all namespaces (depending on how it is bound):

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # "namespace" omitted since ClusterRoles are not namespaced
  name: secret-reader
rules:
- apiGroups: [""]
  #
  # at the HTTP level, the name of the resource for accessing Secret
  # objects is "secrets"
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

3.4.2. RoleBinding and ClusterRoleBinding

A role binding grants the permissions defined in a role to a user or set of users.

  • It holds a list of subjects (users, groups, or service accounts), and a reference to the role being granted.

  • A RoleBinding grants permissions within a specific namespace whereas a ClusterRoleBinding grants that access cluster-wide.

  • A RoleBinding may reference any Role in the same namespace.

  • A RoleBinding can also reference a ClusterRole to grant the permissions defined in that ClusterRole to resources inside the RoleBinding’s namespace.

  • To bind a ClusterRole to all the namespaces in a cluster, use a ClusterRoleBinding.

Here is an example of a RoleBinding that grants the "pod-reader" Role to the user "jane" within the "default" namespace.

apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "jane" to read pods in the "default" namespace.
# You need to already have a Role named "pod-reader" in that namespace.
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
# You can specify more than one "subject"
- kind: User
  name: jane # "name" is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  # "roleRef" specifies the binding to a Role / ClusterRole
  kind: Role #this must be Role or ClusterRole
  name: pod-reader # this must match the name of the Role or ClusterRole you wish to bind to
  apiGroup: rbac.authorization.k8s.io

For instance, even though the following RoleBinding refers to a ClusterRole, "dave" (the subject, case sensitive) will only be able to read Secrets in the "dev" namespace, because the RoleBinding’s namespace (in its metadata) is "dev".

apiVersion: rbac.authorization.k8s.io/v1
# This role binding allows "dave" to read secrets in the "dev" namespace.
# You need to already have a ClusterRole named "secret-reader".
kind: RoleBinding
metadata:
  name: read-secrets
  #
  # The namespace of the RoleBinding determines where the permissions are granted.
  # This only grants permissions within the "dev" namespace.
  namespace: dev
subjects:
- kind: User
  name: dave # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

The following ClusterRoleBinding allows any user in the group "manager" to read secrets in any namespace.

apiVersion: rbac.authorization.k8s.io/v1
# This cluster role binding allows anyone in the "manager" group to read secrets in any namespace.
kind: ClusterRoleBinding
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager # Name is case sensitive
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

3.4.3. Referring to Resources

In the Kubernetes API, most resources (including subresources) are represented and accessed using a string representation of their object name, such as pods for a Pod. RBAC refers to resources using exactly the same name that appears in the URL for the relevant API endpoint.

A request for a Pod’s logs looks like: GET /api/v1/namespaces/{namespace}/pods/{name}/log.

  • In this case, pods is the namespaced resource for Pod resources, and log is a subresource of pods.

  • To represent this in an RBAC role, use a slash (/) to delimit the resource and subresource.

  • To allow a subject to read pods and also access the log subresource for each of those Pods, write:

    apiVersion: rbac.authorization.k8s.io/v1
    kind: Role
    metadata:
      namespace: default
      name: pod-and-pod-logs-reader
    rules:
    - apiGroups: [""]
      resources: ["pods", "pods/log"]
      verbs: ["get", "list"]

To restrict to individual instances of a resource, refer to resources by name for certain requests through the resourceNames list. Here is an example that restricts its subject to only get or update a ConfigMap named my-configmap:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: configmap-updater
rules:
- apiGroups: [""]
  #
  # at the HTTP level, the name of the resource for accessing ConfigMap
  # objects is "configmaps"
  resources: ["configmaps"]
  resourceNames: ["my-configmap"]
  verbs: ["update", "get"]

Rather than referring to individual resources, apiGroups, and verbs, use the wildcard * symbol to refer to all such objects.

  • For nonResourceURLs, use the wildcard * as a suffix glob match.

  • For resourceNames, an empty set means that everything is allowed.

Here is the definition of the built-in cluster-admin ClusterRole.

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  # ...
  name: cluster-admin
rules:
- apiGroups:
  - '*'
  resources:
  - '*'
  verbs:
  - '*'
- nonResourceURLs:
  - '*'
  verbs:
  - '*'

3.4.4. Aggregated ClusterRoles

A controller, running as part of the cluster control plane, watches for ClusterRole objects with an aggregationRule set that defines a label selector to match other ClusterRole objects that should be combined into the rules field of this one.

Here is an example aggregated ClusterRole:

apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # The control plane automatically fills in the rules

3.4.5. Referring to Subjects

A RoleBinding or ClusterRoleBinding binds a role to subjects which can be groups, users or ServiceAccounts.

The prefix system: is reserved for Kubernetes system use, so you should ensure that you don’t have users or groups with names that start with system: by accident. Other than this special prefix, the RBAC authorization system does not require any format for usernames.

ServiceAccounts have names prefixed with system:serviceaccount:, and belong to groups that have names prefixed with system:serviceaccounts:.

  • system:serviceaccount: (singular) is the prefix for service account usernames.

  • system:serviceaccounts: (plural) is the prefix for service account groups.

For a user named alice@example.com:

subjects:
- kind: User
  name: "alice@example.com"
  apiGroup: rbac.authorization.k8s.io

For a group named frontend-admins:

subjects:
- kind: Group
  name: "frontend-admins"
  apiGroup: rbac.authorization.k8s.io

For the default service account in the "kube-system" namespace:

subjects:
- kind: ServiceAccount
  name: default
  namespace: kube-system

For all service accounts in the "qa" namespace:

subjects:
- kind: Group
  name: system:serviceaccounts:qa
  apiGroup: rbac.authorization.k8s.io

For all service accounts in any namespace:

subjects:
- kind: Group
  name: system:serviceaccounts
  apiGroup: rbac.authorization.k8s.io

For all authenticated users:

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io

For all unauthenticated users:

subjects:
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

For all users:

subjects:
- kind: Group
  name: system:authenticated
  apiGroup: rbac.authorization.k8s.io
- kind: Group
  name: system:unauthenticated
  apiGroup: rbac.authorization.k8s.io

3.4.6. Default Roles and Role Bindings

API servers create a set of default ClusterRole and ClusterRoleBinding objects.

  • Many of these are system: prefixed, which indicates that the resource is directly managed by the cluster control plane.

  • All of the default ClusterRoles and ClusterRoleBindings are labeled with kubernetes.io/bootstrapping=rbac-defaults.

Take care when modifying ClusterRoles and ClusterRoleBindings with names that have a system: prefix. Modifications to these resources can result in non-functional clusters.

At each start-up, the API server updates default cluster roles with any missing permissions, and updates default cluster role bindings with any missing subjects.

Some of the default ClusterRoles are not system: prefixed which are intended to be user-facing roles that include super-user roles (cluster-admin), roles intended to be granted cluster-wide using ClusterRoleBindings, and roles intended to be granted within particular namespaces using RoleBindings (admin, edit, view).

3.4.7. Command-line utilities

  • Create a Role named "pod-reader" that allows users to perform get, watch and list on pods:

    kubectl create role pod-reader --verb=get --verb=list --verb=watch --resource=pods
  • Create a Role named "pod-reader" with resourceNames specified:

    kubectl create role pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
  • Create a Role named "foo" with apiGroups specified:

    kubectl create role foo --verb=get,list,watch --resource=replicasets.apps
  • Create a Role named "foo" with subresource permissions:

    kubectl create role foo --verb=get,list,watch --resource=pods,pods/status
  • Create a Role named "my-component-lease-holder" with permissions to get/update a resource with a specific name:

    kubectl create role my-component-lease-holder --verb=get,list,watch,update --resource=lease --resource-name=my-component
  • Create a ClusterRole named "pod-reader" that allows user to perform get, watch and list on pods:

    kubectl create clusterrole pod-reader --verb=get,list,watch --resource=pods
  • Create a ClusterRole named "pod-reader" with resourceNames specified:

    kubectl create clusterrole pod-reader --verb=get --resource=pods --resource-name=readablepod --resource-name=anotherpod
  • Create a ClusterRole named "foo" with apiGroups specified:

    kubectl create clusterrole foo --verb=get,list,watch --resource=replicasets.apps
  • Create a ClusterRole named "foo" with subresource permissions:

    kubectl create clusterrole foo --verb=get,list,watch --resource=pods,pods/status
  • Create a ClusterRole named "foo" with nonResourceURL specified:

    kubectl create clusterrole "foo" --verb=get --non-resource-url=/logs/*
  • Create a ClusterRole named "monitoring" with an aggregationRule specified:

    kubectl create clusterrole monitoring --aggregation-rule="rbac.example.com/aggregate-to-monitoring=true"
  • Within the namespace "acme", grant the permissions in the "admin" ClusterRole to a user named "bob":

    kubectl create rolebinding bob-admin-binding --clusterrole=admin --user=bob --namespace=acme
  • Within the namespace "acme", grant the permissions in the "view" ClusterRole to the service account in the namespace "acme" named "myapp":

    kubectl create rolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp --namespace=acme
  • Within the namespace "acme", grant the permissions in the "view" ClusterRole to a service account in the namespace "myappnamespace" named "myapp":

    kubectl create rolebinding myappnamespace-myapp-view-binding --clusterrole=view --serviceaccount=myappnamespace:myapp --namespace=acme
  • Across the entire cluster, grant the permissions in the "cluster-admin" ClusterRole to a user named "root":

    kubectl create clusterrolebinding root-cluster-admin-binding --clusterrole=cluster-admin --user=root
  • Across the entire cluster, grant the permissions in the "system:node-proxier" ClusterRole to a user named "system:kube-proxy":

    kubectl create clusterrolebinding kube-proxy-binding --clusterrole=system:node-proxier --user=system:kube-proxy
  • Across the entire cluster, grant the permissions in the "view" ClusterRole to a service account named "myapp" in the namespace "acme":

    kubectl create clusterrolebinding myapp-view-binding --clusterrole=view --serviceaccount=acme:myapp
  • Test applying a manifest file of RBAC objects, displaying changes that would be made:

    kubectl auth reconcile -f my-rbac-rules.yaml --dry-run=client
  • Apply a manifest file of RBAC objects, preserving any extra permissions (in roles) and any extra subjects (in bindings):

    kubectl auth reconcile -f my-rbac-rules.yaml
  • Apply a manifest file of RBAC objects, removing any extra permissions (in roles) and any extra subjects (in bindings):

    kubectl auth reconcile -f my-rbac-rules.yaml --remove-extra-subjects --remove-extra-permissions

4. Certificates and Certificate Signing Requests

A CertificateSigningRequest (CSR) resource is used to request that a certificate be signed by a denoted signer, after which the request may be approved or denied before finally being signed. [4]

4.1. Signers

Signers abstractly represent the entity or entities that might sign, or have signed, a security certificate.

Any signer that is made available for outside a particular cluster should provide information about how the signer works, so that consumers can understand what that means for CertifcateSigningRequests and (if enabled) ClusterTrustBundles which are cluster-scoped objects for distributing X.509 trust anchors (root certificates) to workloads within the cluster. .

  • Trust distribution: how trust anchors (CA certificates or certificate bundles) are distributed.

  • Permitted subjects: any restrictions on and behavior when a disallowed subject is requested.

  • Permitted x509 extensions: including IP subjectAltNames, DNS subjectAltNames, Email subjectAltNames, URI subjectAltNames etc, and behavior when a disallowed extension is requested.

  • Permitted key usages / extended key usages: any restrictions on and behavior when usages different than the signer-determined usages are specified in the CSR.

  • Expiration/certificate lifetime: whether it is fixed by the signer, configurable by the admin, determined by the CSR spec.expirationSeconds field, etc and the behavior when the signer-determined expiration is different from the CSR spec.expirationSeconds field.

  • CA bit allowed/disallowed: and behavior if a CSR contains a request a for a CA certificate when the signer does not permit it.

Commonly, the status.certificate field of a CertificateSigningRequest contains a single PEM-encoded X.509 certificate once the CSR is approved and the certificate is issued.

4.2. Kubernetes signers

Kubernetes provides built-in signers that each have a well-known signerName:

  • kubernetes.io/kube-apiserver-client: signs certificates that will be honored as client certificates by the API server. Never auto-approved by kube-controller-manager.

    1. Trust distribution: signed certificates must be honored as client certificates by the API server. The CA bundle is not distributed by any other means.

    2. Permitted subjects: no subject restrictions, but approvers and signers may choose not to approve or sign.

    3. Permitted x509 extensions - honors subjectAltName and key usage extensions and discards other extensions.

    4. Permitted key usages: Must include ["client auth"]. Must not include key usages beyond ["digital signature", "key encipherment", "client auth"].

    5. Expiration/certificate lifetime: for the kube-controller-manager implementation of this signer, set to the minimum of the --cluster-signing-duration option or, if specified, the spec.expirationSeconds field of the CSR object.

    6. CA bit allowed/disallowed: not allowed.

  • kubernetes.io/kube-apiserver-client-kubelet: signs client certificates that will be honored as client certificates by the API server. May be auto-approved by kube-controller-manager.

    1. Trust distribution: signed certificates must be honored as client certificates by the API server. The CA bundle is not distributed by any other means.

    2. Permitted subjects: organizations are exactly ["system:nodes"], common name starts with system:node:.

    3. Permitted x509 extensions: honors key usage extensions, forbids subjectAltName extensions and drops other extensions.

    4. Permitted key usages: ["key encipherment", "digital signature", "client auth"] or ["digital signature", "client auth"].

    5. Expiration/certificate lifetime: for the kube-controller-manager implementation of this signer, set to the minimum of the --cluster-signing-duration option or, if specified, the spec.expirationSeconds field of the CSR object.

    6. CA bit allowed/disallowed - not allowed.

  • kubernetes.io/kubelet-serving: signs serving certificates that are honored as a valid kubelet serving certificate by the API server, but has no other guarantees. Never auto-approved by kube-controller-manager.

    1. Trust distribution: signed certificates must be honored by the API server as valid to terminate connections to a kubelet. The CA bundle is not distributed by any other means.

    2. Permitted subjects: organizations are exactly ["system:nodes"], common name starts with system:node:.

    3. Permitted x509 extensions: honors key usage and DNSName/IPAddress subjectAltName extensions, forbids EmailAddress and URI subjectAltName extensions, drops other extensions. At least one DNS or IP subjectAltName must be present.

    4. Permitted key usages: ["key encipherment", "digital signature", "server auth"] or ["digital signature", "server auth"].

    5. Expiration/certificate lifetime: for the kube-controller-manager implementation of this signer, set to the minimum of the --cluster-signing-duration option or, if specified, the spec.expirationSeconds field of the CSR object.

    6. CA bit allowed/disallowed - not allowed.

The kube-controller-manager implements control plane signing for each of the built in signers. Failures for all of these are only reported in kube-controller-manager logs.

A Kubernetes administrator (with appropriate permissions) can manually approve (or deny) CertificateSigningRequests by using the kubectl certificate approve and kubectl certificate deny commands.

4.3. How to issue a certificate for a user

  1. Generate a certificate signing request:

    openssl req \
      -nodes \
      -newkey rsa \
      -subj "/CN=developer/O=developers" \
      -keyout developer.key \
      -out developer-csr.pem
  2. Create a CertificateSigningRequest and submit it to Kubernetes Cluster:

    kubectl apply -f - <<EOF
    apiVersion: certificates.k8s.io/v1
    kind: CertificateSigningRequest
    metadata:
      name: developers
    spec:
      username: developer
      groups: ["developers"]
      request: $(base64 <(cat developer-csr.pem) | tr -d '\n')
      usages: ["digital signature", "key encipherment", "client auth"]
      #expirationSeconds: 7200
      signerName: kubernetes.io/kube-apiserver-client
    EOF
    certificatesigningrequest.certificates.k8s.io/developers created
    • Approve the CertificateSigningRequest via kubectl:

      kubectl get csr developers
      NAME         AGE   SIGNERNAME                            REQUESTOR          REQUESTEDDURATION   CONDITION
      developers   17s   kubernetes.io/kube-apiserver-client   kubernetes-admin   <none>              Pending
      kubectl certificate approve developers
      certificatesigningrequest.certificates.k8s.io/developers approved
    • Retrieve the certificate from the CSR object:

      kubectl get csr developers \
          -ojsonpath='{.status.certificate}' \
          | base64 -d > developer.crt
    • Create a kubeconfig file:

      Below is a script to generate the kubeconfig named developer.config in the current directory.

      # replace the CA file with your cluster
      certificate_authority=/etc/kubernetes/pki/ca.crt
      
      kubectl config set-cluster \
          kubernetes \
          --kubeconfig developer.config \
          --server $(kubectl config view --minify | grep server | awk '{print $NF}') \
          --certificate-authority $certificate_authority \
          --embed-certs
      
      kubectl config set-credentials \
          developer \
          --kubeconfig developer.config \
          --user developer \
          --client-key developer.key \
          --client-certificate developer.crt \
          --embed-certs
      
      kubectl config set-context \
          developer@kubernetes \
          --kubeconfig developer.config \
          --cluster kubernetes \
          --user developer \
          --namespace default
    • Create Role and RoleBinding:

      # create a cluster role named developer:namespace:view
      # and assign the `get` and `list` permissions on `namespace` resources.
      kubectl create clusterrole developer:namespace:view \
          --resource namespace \
          --verb get \
          --verb list
      
      kubectl create clusterrolebinding developer:namespace:view \
          --clusterrole=developer:namespace:view \
          --user developer
      $ kubectl get ns --kubeconfig developer.config
      NAME              STATUS   AGE
      default           Active   152d
      kube-node-lease   Active   152d
      kube-public       Active   152d
      kube-system       Active   152d