In Kubernetes, before you can do anything, you need to log in. Think of it like needing a key to start a car. The person in charge of the Kubernetes “parking lot” (the cluster administrator) can go anywhere and do anything, much like having a master key. The simplest way to get things moving is to give everyone their own master key. However, just like in real life, if everyone has a master key, things can go wrong quickly.
Someone might accidentally throw away something important (like a Secret object in Kubernetes), which can cause a lot of trouble, breaking apps and upsetting users. This is why giving everyone full control is not smart, especially when you’re running important apps
Just like any other serious system, only a few people should have the keys to the kingdom, while most should only be able to look but not touch (or make small changes, depending on their job). For instance, the people making the apps don’t need to worry about the big-picture stuff like managing the servers. They just need to focus on their piece of the puzzle.
To manage who can do what, Kubernetes uses something called RBAC (Role-Based Access Control). It’s like setting rules about who can enter certain rooms or use certain tools. Turning on RBAC and setting it up right is a must-do for any group that cares about keeping things secure. For tests and real-life use, you’ll need to know what kinds of rules you can make (the RBAC resources) and how to put them into action in various situations.
RBAC High-Level Overview
Creating a Subject
Within RBAC, the entities that can be granted permissions include user accounts, service accounts, and groups, known as subjects. User accounts and groups, designed for operations external to the Kubernetes cluster, are not kept in Kubernetes’ etcd database. On the other hand, service accounts are created as Kubernetes objects for use by internal cluster processes. This section will guide you on setting up these subjects.
User accounts and groups
Kubernetes does not represent a user as with an API resource. The user is meant to be managed by the administrator of a Kubernetes cluster, which then distributes the credentials of the account to the real person or to be used by an external process.
Calls to the API server with a user need to be authenticated. Kubernetes offers a variety of authentication methods for those API requests. Table 2-1 shows different ways of authenticating RBAC subjects.
Authentication strategy | Description |
---|---|
X.509 client certificate | Uses an OpenSSL client certificate to authenticate |
Basic authentication | Uses username and password to authenticate |
Bearer tokens | Uses OpenID (a flavor of OAuth2) or webhooks as a way to authenticate |
For authentication via OpenSSL client certificates, such tasks need to be executed under the cluster-admin Role. During your exam, it’s assumed that user creation has been pre-configured for you, so learning the below steps by heart is unnecessary:
- Access the Kubernetes control plane node and initiate a new directory for storing keys. Enter this directory with the commands:
mkdir cert && cd cert
- Generate a private key using the openssl tool. Name the file thoughtfully, like
<username>.key
:openssl genrsa -out johndoe.key 2048
This creates a 2048-bit RSA private key, confirmed by a message and the file’s presence in the directory. - Craft a Certificate Signing Request (CSR) with the
.csr
file extension, incorporating the previously created private key. Use-subj
to specify the username (CN) and group (O), like so:openssl req -new -key johndoe.key -out johndoe.csr -subj "/CN=johndoe/O=cka-study-guide"
Omit the/O
part if not associating the user with a group. - Sign the CSR using the Kubernetes cluster’s Certificate Authority (CA), typically found at
/etc/kubernetes/pki
or~/.minikube
for minikube users. This validates the CSR for 364 days:openssl x509 -req -in johndoe.csr -CA ~/.minikube/ca.crt -CAkey ~/.minikube/ca.key -CAcreateserial -out johndoe.crt -days 364
This process confirms the signature and retrieves the CA private key. - In Kubernetes, create a user profile for johndoe in kubeconfig, linking to the CRT and key. Also, set up a specific context for this user :
kubectl config set-credentials johndoe --client-certificate=johndoe.crt --client-key=johndoe.key kubectl config set-context johndoe-context --cluster=minikube --user=johndoe
This modifies the context to johndoe-context. - Switch to the newly created user by activating the johndoe-context. Verify the current context with:
kubectl config use-context johndoe-context kubectl config current-context johndoe-context
This completes the switch to the “johndoe-context”.
ServiceAccount
In Kubernetes, users are individuals who interact with the cluster via tools like kubectl
or the dashboard. Service applications, such as Helm within Pods, communicate with the Kubernetes API through RESTful HTTP calls. Kubernetes authenticates these applications using a ServiceAccount, which also links to specific permissions via RBAC rules. Every cluster includes a default ServiceAccount in the default namespace, used by Pods that don’t specify a different ServiceAccount.
To create a custom ServiceAccount imperatively, run the create serviceaccount
command:
$ kubectl create serviceaccount build-bot serviceaccount/build-bot created
The declarative way to create a ServiceAccount looks very straightforward. You simply provide the appropriate kind
and a name, as shown in Example 2-1.
Example 2-1. A YAML manifest defining a ServiceAccount
apiVersion
:
v1
kind
:
ServiceAccount
metadata
:
name
:
build-bot
Listing ServiceAccounts
Listing the ServiceAccounts can be achieved with the get serviceaccounts
command. As you can see in the following output, the default
namespace lists the default
ServiceAccount and the custom ServiceAccount we just created:
$ kubectl get serviceaccounts NAME SECRETS AGE build-bot 1 78s default 1 93d
Rendering ServiceAccount Details
Upon object creation, the API server creates a Secret holding the API token and assigns it to the ServiceAccount. The Secret and token names use the ServiceAccount name as a prefix. You can discover the details of a ServiceAccount using the describe serviceaccount
command, as shown here:
$ kubectl describe serviceaccount build-bot Name: build-bot Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: build-bot-token-rvjnz Tokens: build-bot-token-rvjnz Events: <none>
Consequently, you should be able to find a Secret object for the default
and the build-bot
ServiceAccount:
$ kubectl get secrets NAME TYPE DATA AGE build-bot-token-rvjnz kubernetes.io/service-account-token 3 20m default-token-qgh5n kubernetes.io/service-account-token 3 93d
Assigning a ServiceAccount to a Pod
For a ServiceAccount to take effect, it needs to be assigned to a Pod running the application intended to make API calls. Upon Pod creation, you can use the command-line option --serviceaccount
in conjunction with the run
command:
$ kubectl run build-observer --image=alpine --restart=Never \ --serviceaccount=build-bot pod/build-observer created
Alternatively, you can directly assign the ServiceAccount in the YAML manifest of a Pod, Deployment, Job, or CronJob using the field serviceAccountName
. Example 2-2 shows the definition of a ServiceAccount to a Pod.
Example 2-2. A YAML manifest assigning a ServiceAccount to a Pod
apiVersion
:
v1
kind
:
Pod
metadata
:
name
:
build-observer
spec
:
serviceAccountName
:
build-bot
...
Understanding RBAC API Primitives
In Kubernetes, RBAC is managed through two main API objects:
- Role: Defines permissions for specific actions on resources, like allowing pod listings or log watches. Anything not explicitly allowed is denied.
- RoleBinding: Links Roles to subjects (users, groups, or service accounts), activating the permissions specified in the Role.
These elements work together to set and enforce access controls within Kubernetes, ensuring subjects have only the permissions they need.
Default User-Facing Roles
Kubernetes defines a set of default Roles. You can assign them to a subject via a RoleBinding or define your own, custom Roles depending on your needs. Table 2-2 describes the default user-facing Roles.
Default ClusterRole | Description |
---|---|
cluster-admin | Allows read and write access to resources across all namespaces. |
admin | Allows read and write access to resources in namespace including Roles and RoleBindings. |
edit | Allows read and write access to resources in namespace except Roles and RoleBindings. Provides access to Secrets. |
view | Allows read-only access to resources in namespace except Roles, RoleBindings, and Secrets. |
To define new Roles and RoleBindings, you will have to use a context that allows for creating or modifying them, that is, cluster-admin or admin.
Creating Roles
Roles can be created imperatively with the create role
command. The most important options for the command are --verb
for defining the verbs aka operations, and --resource
for declaring a list of API resources. The following command creates a new Role for the resources Pod, Deployment, and Service with the verbs list
, get
, and watch
:
$ kubectl create role read-only --verb=list,get,watch \ --resource=pods,deployments,services role.rbac.authorization.k8s.io/read-only created
For a single kubectl create role
command, you can specify multiple verbs and resources either as a comma-separated list within the same option or as separate instances of the option. For instance, --verb=list,get,watch
is equivalent to specifying --verb=list --verb=get --verb=watch
. The wildcard symbol *
can be used to indicate all verbs or resources.
The --resource-name
option allows you to specify one or several specific object names to which the role’s permissions should apply, like a Pod named nginx
. Listing resource names is optional; omitting them means the permissions apply to all instances of the resource type.
In a declarative setup, defining resources and verbs might get a bit verbose. For resources belonging to a specific API group, such as Deployments in apps/v1
, you must declare the group under apiGroups
. Resources without an API group, like Pods and Services, use an empty string for their API version. Note that when creating a Role through the command line, the API group is automatically identified.
Example 2-3. A YAML manifest defining a Role
apiVersion
:
rbac.authorization.k8s.io/v1
kind
:
Role
metadata
:
name
:
read-only
rules
:
-
apiGroups
:
-
""
resources
:
-
pods
-
services
verbs
:
-
list
-
get
-
watch
-
apiGroups
:
-
apps
resources
:
-
deployments
verbs
:
-
list
-
get
-
watch
Listing Roles
Once the Role has been created, its object can be listed. The list of Roles renders only the name and the creation timestamp. Each of the listed roles does not give away any of its details:
$ kubectl get roles NAME CREATED AT read-only 2021-06-23T19:46:48Z
Rendering Role Details
You can inspect the details of a Role using the describe
command. The output renders a table that maps a resource to its permitted verbs. This cluster has no resources created, so the list of resource names in the following console output is empty:
$ kubectl describe role read-only Name: read-only Labels: <none> Annotations: <none> PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- pods [] [] [list get watch] services [] [] [list get watch] deployments.apps [] [] [list get watch]
Creating RoleBindings
The imperative command creating a RoleBinding object is create rolebinding
. To bind a Role to the RoleBinding, use the --role
command-line option. The subject type can be assigned by declaring the options --user
, --group
, or --serviceaccount
. The following command creates the RoleBinding with the name read-only-binding
to the user called johndoe
:
$ kubectl create rolebinding read-only-binding --role=read-only --user=johndoe rolebinding.rbac.authorization.k8s.io/read-only-binding created
Example 2-4 shows a YAML manifest representing the RoleBinding. You can see from the structure that a role can be mapped to one or many subjects. The data type is an array indicated by the dash character under the attribute subjects
. At this time, only the user johndoe
has been assigned.
Example 2-4. A YAML manifest defining a RoleBinding
apiVersion
:
rbac.authorization.k8s.io/v1
kind
:
RoleBinding
metadata
:
name
:
read-only-binding
roleRef
:
apiGroup
:
rbac.authorization.k8s.io
kind
:
Role
name
:
read-only
subjects
:
-
apiGroup
:
rbac.authorization.k8s.io
kind
:
User
name
:
johndoe
Listing RoleBindings
The most important information the list of RoleBindings gives away is the associated Role. The following command shows that the RoleBinding read-only-binding
has been mapped to the Role read-only
:
$ kubectl get rolebindings NAME ROLE AGE read-only-binding Role/read-only 24h
The output does not provide an indication of the subjects. You will need to render the details of the object for more information, as described in the next section.
Rendering RoleBinding Details
RoleBindings can be inspected using the describe
command. The output renders a table of subjects and the assigned role. The following example renders the descriptive representation of the RoleBinding named read-only-binding
:
$ kubectl describe rolebinding read-only-binding Name: read-only-binding Labels: <none> Annotations: <none> Role: Kind: Role Name: read-only Subjects: Kind Name Namespace ---- ---- --------- User johndoe
Seeing the RBAC Rules in Effect
Let’s see how Kubernetes enforces the RBAC rules for the scenario we set up so far. First, we’ll create a new Deployment with the cluster-admin
credentials. In Minikube, this user is assigned to the context minikube
:
$ kubectl config current-context minikube $ kubectl create deployment myapp --image=nginx --port=80 --replicas=2 deployment.apps/myapp created
Now, we’ll switch the context for the user johndoe
:
$ kubectl config use-context johndoe-context Switched to context "johndoe-context".
Remember that the user johndoe
is permitted to list deployments. We’ll verify that by using the get deployments
command:
$ kubectl get deployments NAME READY UP-TO-DATE AVAILABLE AGE myapp 2/2 2 2 8s
The RBAC rules only allow listing Deployments, Pods, and Services. The following command tries to list the ReplicaSets, which results in an error:
$ kubectl get replicasets Error from server (Forbidden): replicasets.apps is forbidden: User "johndoe" \ cannot list resource "replicasets" in API group "apps" in the namespace "default"
A similar behavior can be observed when trying to use other verbs than list
, get
, or watch
. The following command tries to delete a Deployment:
$ kubectl delete deployment myapp Error from server (Forbidden): deployments.apps "myapp" is forbidden: User \ "johndoe" cannot delete resource "deployments" in API group "apps" in the \ namespace "default"
At any given time, you can check a user’s permissions with the auth can-i
command. The command gives you the option to list all permissions or check a specific permission:
$ kubectl auth can-i --list --as johndoe Resources Non-Resource URLs Resource Names Verbs ... pods [] [] [list get watch] services [] [] [list get watch] deployments.apps [] [] [list get watch] $ kubectl auth can-i list pods --as johndoe yes
Namespace-wide and Cluster-wide RBAC
Roles and RoleBindings are tied to specific namespaces, requiring you to define the namespace during their setup. However, for scenarios where you need these roles and bindings to cover multiple namespaces or the entire cluster, Kubernetes introduces ClusterRole
and ClusterRoleBinding
. These cluster-level resources mirror the configuration structure of their namespace-specific versions, with the distinction lying in their kind
attribute:
- To create a Role with cluster-wide scope, either employ the
clusterrole
imperative command or useClusterRole
as the kind in a YAML document. - For establishing a RoleBinding that spans the whole cluster, utilize the
clusterrolebinding
command or specifyClusterRoleBinding
as the kind in a YAML document.
Aggregating RBAC Rules
Existing ClusterRoles can be aggregated to avoid having to redefine a new, composed set of rules that likely leads to duplication of instructions. For example, say you wanted to combine a user-facing role with a custom Role. An aggregated ClusterRule can merge rules via label selection without having to copy-paste the existing rules into one.
Say we defined two ClusterRoles shown in Examples 2-5 and 2-6. The ClusterRole list-pods
allows for listing Pods and the ClusterRole delete-services
allows for deleting Services.
Example 2-5. A YAML manifest defining a ClusterRole for listing Pods
apiVersion
:
rbac.authorization.k8s.io/v1
kind
:
ClusterRole
metadata
:
name
:
list-pods
namespace
:
rbac-example
labels
:
rbac-pod-list
:
"true"
rules
:
-
apiGroups
:
-
""
resources
:
-
pods
verbs
:
-
list
Example 2-6. A YAML manifest defining a ClusterRole for deleting Services
apiVersion
:
rbac.authorization.k8s.io/v1
kind
:
ClusterRole
metadata
:
name
:
delete-services
namespace
:
rbac-example
labels
:
rbac-service-delete
:
"true"
rules
:
-
apiGroups
:
-
""
resources
:
-
services
verbs
:
-
delete
To aggregate those rules, ClusterRoles can specify an aggregationRule
. This attribute describes the label selection rules. Example 2-7 shows an aggregated ClusterRole defined by an array of matchLabels
criteria. The ClusterRole does not add its own rules as indicated by rules:
[]
; however, there’s no limiting factor that would disallow it.
Example 2-7. A YAML manifest defining a ClusterRole with aggregated rules
apiVersion
:
rbac.authorization.k8s.io/v1
kind
:
ClusterRole
metadata
:
name
:
pods-services-aggregation-rules
namespace
:
rbac-example
aggregationRule
:
clusterRoleSelectors
:
-
matchLabels
:
rbac-pod-list
:
"true"
-
matchLabels
:
rbac-service-delete
:
"true"
rules
:
[]
We can verify the proper aggregation behavior of the ClusterRole by describing the object. You can see in the following output that both ClusterRoles, list-pods
and delete-services
, have been taken into account:
$ kubectl describe clusterroles pods-services-aggregation-rules -n rbac-example Name: pods-services-aggregation-rules Labels: <none> Annotations: <none> PolicyRule: Resources Non-Resource URLs Resource Names Verbs --------- ----------------- -------------- ----- services [] [] [delete] pods [] [] [list]
For more information on ClusterRole label selection rules, see the official documentation. The page also explains how to aggregate the default user-facing ClusterRoles.