In this tutorial, you will learn how to assign roles to users and groups in Kubernetes cluster. With Kubernetes becoming the de facto standard for container orchestration, managing access control is paramount to ensuring the security and integrity of your clusters. This guide will take you through how you control access to various resources by assigning necessary roles or permissions to users or respective groups in a Kubernetes cluster.
Table of Contents
Assign Roles to Users and Groups in Kubernetes Cluster
Introduction to Role-Based Access Control in Kubernetes
Kubernetes manages user access to its resources through Role-Based Access Control. Check the link below to read more on Kubernetes RBAC.
Introduction to Role-Based Access Control (RBAC) in Kubernetes
Creating User Accounts in Kubernetes
While Kubernetes does not natively support management of normal user accounts, it does support the management of service accounts.
Read more about user management in Kubernetes by following the guide below.
Kubernetes User Management: Creating Users, Groups and Service Accounts
As already stated that Kubernetes does not have objects which represent normal user accounts, we have created two users, alice and bob, on the cluster that depicts the native user accounts using X509 certificate authentication strategy.
kubectl config get-users
NAME
alice
bob
kubernetes-admin
We have also created a service account called monitoring;
kubectl get serviceaccounts
NAME SECRETS AGE
default 0 5d21h
monitoring 0 21h
Kubernetes RBAC API Objects
There are four RBAC API objects in Kubernetes that are used to enforce access restriction in the cluster. These objects include:
- Role: The Role API object defines the rules representing a given set of permissions within a particular namespace. When creating, you need to specify the namespace on which the role apply to, otherwise, default namespace is selected.
- ClusterRole: The ClusterRole on the other hand defines rules that represent a given set of permissions that applies cluster-wide rather than a specific namespace.
- RoleBinding: The RoleBinding object binds (grants permissions defined in a role) the Role to a specific subject (user, group or a service account) within a specific namespace.
- ClusterRoleBinding: The ClusterRoleBinding similarly grants permissions defined in the ClusterRole object to a subject in a cluster-wide manner.
Creating Kubernetes Roles
Switch to Cluster Admin Context
To be able to create roles, you need to use the cluster admin context, or at least a user that has rights to create roles. To list the contexts;
kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
alice kubernetes alice default
bob kubernetes bob default
* kubernetes-admin@kubernetes kubernetes kubernetes-admin default
The * specifies your current context. In the above example, we are using the Cluster administrator context.
You can switch to another context using the command;
kubectl config use-context <context-name>
Create User Role
In Kubernetes, you can create user roles imperatively via the kubectl create role command, or declaratively via the manifests file using kubectl apply command.
To create a role, you need to specify;
- the name of the role itself
- the verb: the API request permissions like get, list, create, update, patch, watch, delete, and deletecollection.
- the resource object such Pod, Deployments, Services, ReplicaSets, PersistentVolume…
To imparatively create a role, that allows users to list pods and services;
kubectl create role <name of the role> --verb=<list of permissions comma seperated> --resource=<list of resources comma seperated>
For example to create a role called ReadOnly that allows users to list or get Pods and Services resource;
kubectl create role ReadOnly --verb=list,get --resource=pods,services
In essence:
- list permission allows you to get a quick inventory of all resources of a specific kind (e.g kubectl get pods).
- get permission allows you to get details of a particular named resource (kubectl get pod <name of the pod>.
For more options, check;
kubectl create role --help
You can also create a role using a manifest yaml file. For example, create a YAML file to define the role, verbs and resources.
vim readonly.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: ReadOnly
namespace: default
rules:
- apiGroups:
- ""
resources:
- pods
- services
verbs:
- list
- get
Pods and Services are core Kubernetes resources do not have API groups associated with them, hence ” “.
To create the role using manifest yaml file, use kubectl apply -f <manifest-file>.
kubectl apply -f readonly.yaml --server-side
Read more on kubectl apply –help.
Listing Roles
You can list namespaced roles using kubectl command as follows;
kubectl get roles
NAME CREATED AT
ReadOnly 2024-05-19T19:55:52Z
If you want to see more details;
kubectl describe roles ReadOnly
Name: ReadOnly
Labels:
Annotations:
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [] [list get]
services [] [] [list get]
You can also get the details in YAML format;
kubectl get roles ReadOnly -o yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
annotations:
kubectl.kubernetes.io/last-applied-configuration: |
{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"Role","metadata":{"name":"ReadOnly","namespace":"default"},"rules":[{"apiGroups":[""],"resources":["pods","services"],"verbs":["list","get"]}]}
creationTimestamp: "2024-05-19T19:55:52Z"
name: ReadOnly
namespace: default
resourceVersion: "1095342"
uid: 6f391085-b7e8-4823-be99-cbfb62a932e4
rules:
- apiGroups:
- ""
resources:
- pods
- services
verbs:
- list
- get
Assigning Roles to Users/Service Accounts
Create Kubernetes RoleBindings
Now that you have roles with respective permissions in place, how can you assign them to users? To assign users to specific roles, use the command kubectl create rolebinding;
To create a rolebinding, you need:
- name of the rolebinding
- role to assign
- Subject to assign the role (user (–user), group (–group) or serviceaccount (–serviceaccount))
In essence, here is the command line syntax of creating a rolebinding
kubectl create rolebinding NAME --clusterrole=NAME|--role=NAME [--user=username] [--group=groupname]
[--serviceaccount=namespace:serviceaccountname] [--dry-run=server|client|none] [options]
For example, we want to assign a user names alice to the ReadOnly role created above;
kubectl create rolebinding ReadOnlyBind --role=ReadOnly --user=alice
You can also define your rolebinding in a manifest YAML file and install using kubectl apply command;
vim readonly-bind.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: ReadOnlyBind
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: ReadOnly
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: bob
You can then apply the manifest;
kubectl apply -f readonly-bind.yaml
To assign a role to a group, for example;
kubectl create rolebinding <bind name> --role=<role> --group=devs
To assign a role to a service account;
kubectl create rolebinding <bind name> --role=<role> --serviceaccount=<service account name>
Listing RoleBindings
You can get a list of rolebindings using the command;
kubectl get rolebindings
NAME ROLE AGE
ReadOnlyBind Role/ReadOnly 5m39s
To get more details;
kubectl describe rolebindings ReadOnlyBind
Name: ReadOnlyBind
Labels:
Annotations:
Role:
Kind: Role
Name: ReadOnly
Subjects:
Kind Name Namespace
---- ---- ---------
User alice
Verify Role Assignment and Access Permissions
As you can see in the command output above, user alice is given ReadOnly permissions (get and list) on all Pods/Services resources.
To verify that the user can access what is given via RBAC, this is how we will do the test!
- Either create a native Linux user account on the cluster, create Kubeconfig for the user and verify access to the cluster resources as per assigned roles. This is not scalable in production environment.
- As a cluster admin, switch to user’s context and check access.
- Create human user account as a service account and assign the roles as shown above, generate the service account token and use it remotely for API access to Kubernetes cluster from another system.
To use the first method of creating user’s native Linux account and check access to cluster resources;
sudo useradd -m -s /bin/bash alice
sudo passwd alice
Next, copy the user’s credentials that were added to the Kubeconfig file. See, we had already created alice credentials based on her X-509 certificate authentication method.
kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://192.168.122.60:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
namespace: default
user: alice
name: alice
- context:
cluster: kubernetes
namespace: default
user: bob
name: bob
- context:
cluster: kubernetes
namespace: default
user: kubernetes-admin
name: kubernetes-admin@kubernetes
current-context: kubernetes-admin@kubernetes
kind: Config
preferences: {}
users:
- name: alice
user:
client-certificate: users/alice/alice.crt
client-key: users/alice/alice.key
- name: bob
user:
client-certificate: users/bob/bob.crt
client-key: users/bob/bob.key
- name: kubernetes-admin
user:
client-certificate-data: DATA+OMITTED
client-key-data: DATA+OMITTED
So, create user’s kubeconfig directory and copy its credentials.
sudo mkdir /home/alice/.kube/
Generate a portable Kubeconfig file for the user.
kubectl config view --flatten | sudo tee /home/alice/.kube/config
Then edit it such that it only has credentials for the specific user, in this case, alice. See my updated kubeconfig file for the user alice.
sudo cat /home/alice/.kube/config
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURCVENDQWUyZ0F3SUJBZ0lJYU14STJpaWEzMDh3RFFZSktvWklodmNOQVFFTEJRQXdGVEVUTUJFR0ExVUUKQXhNS2EzVmlaWEp1WlhSbGN6QWVGdzB5TkRBMU1UTXlNRE0xTlRoYUZ3MHpOREExTVRFeU1EUXdOVGhhTUJVeApFekFSQmdOVkJBTVRDbXQxWW1WeWJtVjBaWE13Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLCkFvSUJBUURCK1JJUm9odVJTb0hBS2U4RXZ1TnpqODJvUEV6YzhxWGFzdlprV3FRZU9PS05zYWpWLy9CNTMxTEkKMFd0blNmaE5MRXhCU2dYdmRxYXI2NGJUWWgzbzV1MExNaVBXTzFWZ25SM0xGcVpuWkNpMUdRUnJwZndSaU45cwpUczdwQWNnMFBISy9zdWF1RUprbnJZeGUwcjZtMHlLSFdBZ2tSN1lHUmIzYmI3YWNTNlRiYWF5RTFlS0NUblhECkpzTlZGU2lzckd5LzJCaVE2NHNDSGRtL21SYm94dnpCZ1EvckszckdQSEdxVWNQd0YwVG9qaVB6UVJkZ242N0gKbXJ3NHRESFo3QzQveU5PZTNWY0hvd0dJZTEybHN2TU5PVkJwZFlhTVNTcUZjL24yNWQ3R2dYcWdGbDdjc3JsdQpkbnBZeENHdUg3c29YZHZMRDZHS2VPNlBlS2piQWdNQkFBR2pXVEJYTUE0R0ExVWREd0VCL3dRRUF3SUNwREFQCkJnTlZIUk1CQWY4RUJUQURBUUgvTUIwR0ExVWREZ1FXQkJSODd2ZHhLV0Q4RWhMK3ozei9CUmdENWVJbEN6QVYKQmdOVkhSRUVEakFNZ2dwcmRXSmxjbTVsZEdWek1BMEdDU3FHU0liM0RRRUJDd1VBQTRJQkFRQ2JZN0hwUVRNdwpFTVNnOWdWbjErTmJVRzUxWmlwUmNuQ2JDRXN3OFhBUFZVV2REallQekI4QitCdE9NU2NBY2l5QU5pZllMU01iClNOb2ZBeG95NnJUT0FtTDBHM2w2VUtvUEZUTExSSWxIR1hqdi9rQllHbTA1MWk0ZUVJdHBmblhuU3BCM0NSVnQKcWN5UFhqQmJVNFhIS2pVTzBibXNsMkVoZ1BPQm1oZ2k0VXRWYXk0eU9jcWN4ZS9MTFJjaE9DcS9DaEhYcmU5dQo1WEthRGVpUTZtZ081SDNYbFBqeGhtaThQcWUyTzJWdWQ3ZmMxeTg2d2lrRWhWU2ZNRTE4dk1ZV0VvLzhhckxmCmFuSGwveUJDY2c1dkg0RTdXYVJRajNPTTJkUTZjY0JiVlphd2w5MGVmTllZNFJ1OUFnbWNDd09qWVVFQWViK04KZlRzTnJDdytFV1lFCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K
server: https://192.168.122.60:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
namespace: default
user: alice
name: alice
current-context: alice
kind: Config
preferences: {}
users:
- name: alice
user:
client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNyRENDQVpRQ0ZIa2FOdkZCczEvUzJTSDhsMWJWUk9OSW05eEJNQTBHQ1NxR1NJYjNEUUVCQ3dVQU1CVXgKRXpBUkJnTlZCQU1UQ210MVltVnlibVYwWlhNd0hoY05NalF3TlRFNE1UY3pNVFUyV2hjTk1qVXdOVEU0TVRjegpNVFUyV2pBUU1RNHdEQVlEVlFRRERBVmhiR2xqWlRDQ0FTSXdEUVlKS29aSWh2Y05BUUVCQlFBRGdnRVBBRENDCkFRb0NnZ0VCQUxMMGFqRk0yRWVuRGhEY3NhQlA4eDBaRDBpV2VqSG1WUk1veEQzejZqa0VKMDZCVnlUZTZlNmIKYmZPVEtRS0daUnh0Q1BsZXFSR3Y5a2tPUXhpYXpZSnpKeGlMaUh5bndvNVZpcFg1STBkNmUzNC92d2NhLzJ3cAorNmM1TDlpZThLWFhNZWcycW5ZT3NLWnZJcmFIbk16SWIvTVFwWmlHM0x6TTdJQUpJeHAzZGllSE1HMU02VWxCClc0YWxNd3FkZTZNY2xuWHdsUm52RFZEdjBta1pZQ3lCQTVTNVVZZkhOL1Nybk0rcVhmYk5qVlQxRG9FNzBEemwKRUowb2dtV1NTcENqR2krQWhqMktWenBacGo2NlhzUkg4dFE5eTdOTG91aGs3TGF4UFduQ2FvbDZaRFhPOUtVawp1eUF1SHRlWHROckhvQVVlNlZzWTJ2eFBPSitBamlrQ0F3RUFBVEFOQmdrcWhraUc5dzBCQVFzRkFBT0NBUUVBCnFBTlBDMkU4eWlBY2toZ2pzZTJlbzJVMXlJRGoxem1Yak9uZnpkdVVjUzVaeXNtU3llUGtqb2JMK2VsVmEzZmUKVktWSnh1Nkp0NWQ5eWtXLzRpWWxKWWpESFAxK3RZN1ZtMmU3Yi9vRkZSQXVVOFIwellHRVVzV2pveTI1a3BQMwpWMzIyYVhQL3hPNTlOTDRVN1ZMRnkyeTZPRWUvbXlUbjhHdHZUVnNOaTZ2WlBMcmEvcXA4S1B6cmZRdWJFYjRjCnpEWmNaOTV3aE95a1FtVEh5aGltaUROVWdnMDMwY2pFNzI1QytRaFhaRXRCd3RhQ2RGYmpzZmJHc1p1WldBREUKbUUzRVZtUE1PTzNjT3cwUEpoUTZuK2lhcml3ak9Ia1NGZThZZi9ldW5YMHhyeHBWUS9xL0ZkWTJQYStlTkEvUQo4U2Z2NUZlSVRwbUpUakc3akc5Vlh3PT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=
client-key-data: LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRQ3k5R294VE5oSHB3NFEKM0xHZ1QvTWRHUTlJbG5veDVsVVRLTVE5OCtvNUJDZE9nVmNrM3VudW0yM3preWtDaG1VY2JRajVYcWtSci9aSgpEa01ZbXMyQ2N5Y1lpNGg4cDhLT1ZZcVYrU05IZW50K1A3OEhHdjlzS2Z1bk9TL1ludkNsMXpIb05xcDJEckNtCmJ5SzJoNXpNeUcvekVLV1lodHk4ek95QUNTTWFkM1luaHpCdFRPbEpRVnVHcFRNS25YdWpISloxOEpVWjd3MVEKNzlKcEdXQXNnUU9VdVZHSHh6ZjBxNXpQcWwzMnpZMVU5UTZCTzlBODVSQ2RLSUpsa2txUW94b3ZnSVk5aWxjNgpXYVkrdWw3RVIvTFVQY3V6UzZMb1pPeTJzVDFwd21xSmVtUTF6dlNsSkxzZ0xoN1hsN1RheDZBRkh1bGJHTnI4ClR6aWZnSTRwQWdNQkFBRUNnZ0VBQWNYS2lQcis4UndEZzBpUGpzaGR5TWpqZWdLOGZPWnkrNE93RUFGRWZnWDYKQmdxQ1YveTV2WkkySFNZMDZMcmswS1luWTBLU0VXcVZnYzlaZUhPakVwWFBwbzdLbHBuNUdDaVZSaE0wSDZpSQpnZExza1hYZzZsZTl1QWxGK3RQZ04zTnBlWVBkT09qelhyZTAzOVY1bVVHeC9Ta1lrVG5tWEd4bmRNNEZHN1hZCjFkNStsaGxYcitCcUxNN2drVjZiSFVUL0hQcURTN3RlZUg3UDh0OTVrUVA0cHlXOVdTNk0zejhvZEJDUXlrYTkKNDhlYWxoVU9LaDRmTWFmZ2lQb056VGlJZS9RQmhRVHQvQ0hQdGh3S2Y0KzVWN2ZycHVzUFNGdC9zTEVCdWpFUgp1WDM3alBkVjhKSk1VeHJMallOcHBVV3BRUld4bmxSVTZDQTJqOVFKYlFLQmdRRHlsV1ZRVnJUcUNmclNxbW9jCnJ1aUVjS09LY3FRV0hyQVc0dDFxZlV0Yms3bzIrTC8vMEF1MytONVNYVlprL3B0bjdsblVPV2RjbXJUODhyMWcKcDJqUUlubENDZ3VEN2dBakRYckZrYWtmZEhSSDA5NzBsOEhMRWhYWUFoR1RBVWEyMFo0S01CZERBTjZpcVhMMApwTnlob0RSQ2Jzdm01ZXFIWTZBY2ZDUWNJd0tCZ1FDODJpSmZMQ3ovczVvamc4NzNZNWZMdUxBZ201TWxML3YzCnBOcUptVU9OV3VrdW1QclROUy9FT3N1MUxxWjRvY0pKMERMWERRNDJBMXJuZGw3R2dNaDgreVFGMjFRNkVxMlUKdGNkYmVVSG9LT2JGRG9ySVNZM3BBMEsvUlZ0SE5vOWN3enRvR3BoaXpCbHduZFpMd1FDK25QbHJGWUZkNmp0cQpldEx3UlN5YlF3S0JnRFluRDVUZHBrbFFyUU4yTTNYdnZjeEM4TjhwTkdRVHVhK0NPWGRhUFFaV2RnMXJma0QzCkNvYXBNY2dsT2ZJVnZFOTVMK2htWUNLV0RxMGc2eEcyalhsWkdNU2JSWExRSUl1eXFLT09Icmo4NERCZ3BiYm8KWWNTWlp2THZrMGpEMGl0aG8rd1dURHNTNktCYlAyUkpvVThiV2s4eU9LWjAwT1FrWTB1NGtyOE5Bb0dBQmtncgpSSWN2cUFITmFza0RwVzhHcVp3bkg2Nk5JbnVLSWg2MXRrWUczVGpjOE5QZDVCQ3MyaFlxbUloSXVWS0lKL1JvCi9JWk9wclZOM00wdk1lTXV5Qm1DaFQ5YWVlUU5LaGt4M0hVWUlDVGNLRW5uaStvR2NtM05WcGQwQmRabXhtc28KR3JwbnYwR1N4eEE1QktRUzVrUktkNmxyZURoR2FiQlVPL0hSSGdrQ2dZRUF4T0RzMEdQNWFUL29kalJWQlZjcgo1QUhBRHQ5a0NNUHhIekJHRm5WM3JBMU9PZnJEeUtkNVNXQ1hBNGpkTmdwWm11QjNmRDYybjFzd2hZYWU1elJ1ClYzdDNVeFZ6cWxrZlF2MDlCZU1kVkxJcThQVGt5WnRhT0J0UVRFdzd4dW16S2ROMUFnS2xQQndWTmRHUmdsTkkKdFdwcUtrMFBna0tVZ2hBQmtYZ2ZzQXc9Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0K
Update ownership of user’s files/directories;
sudo chown -R alice: /home/alice/.kube/
Switch to the user and check access to resources as defined in the roles;
sudo -u alice -i
alice@master:~$ pwd
/home/alice
Confirm your credentials are in place;
alice@master:~$ kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://192.168.122.60:6443
name: kubernetes
contexts:
- context:
cluster: kubernetes
namespace: default
user: alice
name: alice
current-context: alice
kind: Config
preferences: {}
users:
- name: alice
user:
client-certificate-data: DATA+OMITTED
client-key-data: DATA+OMITTED
To check if you can access the resources defined in the role, use the kubectl auth can-i command;
alice@master:~$ kubectl auth can-i get pods
yes
alice@master:~$ kubectl auth can-i list pods
yes
alice@master:~$ kubectl auth can-i list services
yes
alice@master:~$ kubectl auth can-i get services
yes
As you can, yes confirms access to a resource. if no, then no access. See;
alice@master:~$ kubectl auth can-i '*' '*'
no
Read more on kubectl auth can-i –help.
alice@master:~$ kubectl get pods --all-namespaces
Error from server (Forbidden): pods is forbidden: User "alice" cannot list resource "pods" in API group "" at the cluster scope
As you can see, if you have multiple users, managing this will be cumbersome!
You can also switch to the user’s context as Kubernetes cluster administrator and verify access;
kubectl config use-context <context-name>
E.g
kubectl config use-context alice
kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
* alice kubernetes alice default
bob kubernetes bob default
kubernetes-admin@kubernetes kubernetes kubernetes-admin default
Switch to user’s context;
kubectl config use-context alice
kubectl config current-context
$ kubectl auth can-i '*' '*'
no
$ kubectl auth can-i get '*'
no
$ kubectl auth can-i list '*'
no
$ kubectl auth can-i get 'pods'
yes
$ kubectl auth can-i get 'services'
yes
$ kubectl auth can-i list 'pods'
yes
$ kubectl auth can-i list 'services'
yes
Kubernetes Service Accounts and API Access (via Secrets and Tokens)
Similarly, if you want to access the cluster using API calls from another system, you can actually create the human user account as a service account as shown above. See our example;
kubectl get serviceaccounts
NAME SECRETS AGE
alice-svc 0 5h
kubectl describe roles alice-svc
Name: alice-svc
Labels:
Annotations:
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
pods [] [] [get list]
services [] [] [get list]
Next, create a service account secret. A secret is used to store confidential information, such as authentication credentials, tokens, API keys, or TLS certificates, that applications or users need to access securely.
Recent versions of Kubernetes do not automatically generate secrets for a service account when created. You can confirm with the command:
kubectl describe sa alice-svc
Name: alice-svc
Namespace: default
Labels: <none>
Annotations: <none>
Image pull secrets: <none>
Mountable secrets: <none>
Tokens: <none>
Events: <none>
Therefore, if you have a service account you need to use for API access, you can generate a secret and token for it. Note that there are two types of tokens based on their lifespan.
- Long-lived tokens are associated with service accounts and have a prolonged lifespan, ideal for persistent processes needing continuous access to the Kubernetes API. These tokens persist until manually revoked, requiring careful management to mitigate security risks if compromised.
- Time-limited/short-lived tokens are automatically generated with a restricted lifespan. Typically used for temporary access, they’re suitable for short-lived tasks or processes. Once expired, these tokens become invalid, necessitating the generation of new tokens for further access.
For our case, we will generate long-lived token for demo purposes.
You can generate a secret imperatively using a command line or declarative via the manifest file.
To create the secret using a manifest yaml file, simply run the command like below;
kubectl apply -f - <<EOF
apiVersion: v1
kind: Secret
metadata:
name: alice-svc-secret
annotations:
kubernetes.io/service-account.name: alice-svc
type: kubernetes.io/service-account-token
EOF
This will create a Kubernetes secret named alice-svc-secret, which contains a token associated with the service account named alice-svc. This secret can be used for authentication purposes, allowing the service account alice-svc to access the Kubernetes API securely using the token stored in this secret.
To achieve the same using command line, get a token and generate a secret using it.
kubectl create secret generic <name> --from-literal=token=<your-token>
For testing purposes, let’s use a random base64-encoded string of 32 characters;
kubectl create secret generic alice-svc --from-literal=token=$(openssl rand -base64 32)
Note that there are different types of Kubernetes secrets:
- Opaque: This is the most common type of secret in Kubernetes and is used hold arbitrary data, such as usernames, passwords, tokens, or any other sensitive information. Opaque secrets are stored as base64-encoded strings. When generting using kubectl command, you need to specify the generic option.
- Service Account Tokens: These are used to store tokens that identifies a service account. They are used mostly to generate long-lived token, which is no longer recommended as from Kubernetes v1.22, which recommends use of short-live tokens. Note that also from K8S v1.22, these tokens are not automatically generated when you create a service account.
- Docker config: These are the secrets that used to access Docker container image registry. When using kubectl command to generate the Docker config secret, you specify the command option, docker-registry.
- TLS Secrets: These are used to store TLS certificates and private keys.
There are other types of secrets such as basic authentication and ssh authentication secrets.
Read more on kubectl create secret –help.
Once you have created the secret, annotate with the service account name. This basically means to associate with the secret with the service account.
kubectl annotate secret alice-svc-secret kubernetes.io/service-account.name=alice-svc
Check more details about the secret;
kubectl describe secrets alice-svc-secret
Name: alice-svc-secret
Namespace: default
Labels: kubernetes.io/legacy-token-last-used=2024-05-24
Annotations: kubernetes.io/service-account.name: alice-svc
kubernetes.io/service-account.uid: b3ee12b4-175b-460b-8afe-b3bbb884e334
Type: kubernetes.io/service-account-token
Data
====
token: eyJhbGciOiJSUzI1NiIsImtpZCI6InoyeTB4MmZuN0ZnM1Y4a043NXhVWGw0djNaX1VmZDBfN0xXWFVjMFh0em8ifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImFsaWNlLXN2Yy1zZWNyZXQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiYWxpY2Utc3ZjIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiYjNlZTEyYjQtMTc1Yi00NjBiLThhZmUtYjNiYmI4ODRlMzM0Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6YWxpY2Utc3ZjIn0.TnG9j6BVza-WdDxYh4g9sU3AbcMjSdv3x5Cbpug-dd5n04G7B7KtiilcUgpdB6pwrjPvN8QbF0uTDRtAK7p06Cn7eb0NBvRDYoh40Viv504F5MC2cb6nMoCZzlh6iOFiiMtccwfW4QgwM9IP1kSAP042CVn53HgXto8h11n8k6rcymEyniHwL-BmR64RmbEYOsiyiZQB-y9MqwXt8zVU6S_3w80a49PNxWrs-AjS9vtnyFTtaIEj4bC91V3W-7w1aerwY0x6mYjkWodz8dGqzKHWV0hPOra1fN7ult6JjZFkSf2r1_QMGky5myS_mQbViYhqxz302hZsfsGv9eqy1w
ca.crt: 1107 bytes
namespace: 7 bytes
You can now verify access to the cluster resources from another system via API.
For this, you need to generate the user’s token. You can simply copy the value of the token in the command output above. However, you can also run this command to extract it.
kubectl get secret alice-svc-secret -o=jsonpath='{.data.token}' | base64 --decode
eyJhbGciOiJSUzI1NiIsImtpZCI6InoyeTB4MmZuN0ZnM1Y4a043NXhVWGw0djNaX1VmZDBfN0xXWFVjMFh0em8ifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImFsaWNlLXN2Yy1zZWNyZXQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiYWxpY2Utc3ZjIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiYjNlZTEyYjQtMTc1Yi00NjBiLThhZmUtYjNiYmI4ODRlMzM0Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6YWxpY2Utc3ZjIn0.TnG9j6BVza-WdDxYh4g9sU3AbcMjSdv3x5Cbpug-dd5n04G7B7KtiilcUgpdB6pwrjPvN8QbF0uTDRtAK7p06Cn7eb0NBvRDYoh40Viv504F5MC2cb6nMoCZzlh6iOFiiMtccwfW4QgwM9IP1kSAP042CVn53HgXto8h11n8k6rcymEyniHwL-BmR64RmbEYOsiyiZQB-y9MqwXt8zVU6S_3w80a49PNxWrs-AjS9vtnyFTtaIEj4bC91V3W-7w1aerwY0x6mYjkWodz8dGqzKHWV0hPOra1fN7ult6JjZFkSf2r1_QMGky5myS_mQbViYhqxz302hZsfsGv9eqy1w
Once you have the token, you can use it to authenticate your API calls. For example, you can use curl
to make API calls to the Kubernetes API server.
curl -H "Authorization: Bearer $TOKEN" <RESOURCE URI>
For example, to check access from another system using the user’s secret above. Set the TOKEN variable to the token value above.
TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6InoyeTB4MmZuN0ZnM1Y4a043NXhVWGw0djNaX1VmZDBfN0xXWFVjMFh0em8ifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImFsaWNlLXN2Yy1zZWNyZXQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiYWxpY2Utc3ZjIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiYjNlZTEyYjQtMTc1Yi00NjBiLThhZmUtYjNiYmI4ODRlMzM0Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRlZmF1bHQ6YWxpY2Utc3ZjIn0.TnG9j6BVza-WdDxYh4g9sU3AbcMjSdv3x5Cbpug-dd5n04G7B7KtiilcUgpdB6pwrjPvN8QbF0uTDRtAK7p06Cn7eb0NBvRDYoh40Viv504F5MC2cb6nMoCZzlh6iOFiiMtccwfW4QgwM9IP1kSAP042CVn53HgXto8h11n8k6rcymEyniHwL-BmR64RmbEYOsiyiZQB-y9MqwXt8zVU6S_3w80a49PNxWrs-AjS9vtnyFTtaIEj4bC91V3W-7w1aerwY0x6mYjkWodz8dGqzKHWV0hPOra1fN7ult6JjZFkSf2r1_QMGky5myS_mQbViYhqxz302hZsfsGv9eqy1w
curl -H "Authorization: Bearer $TOKEN" https://192.168.122.60:6443/api/v1/pods -k
The command tries to list pods on all namespaces in the Kubernetes cluster. Remeber Alice permissions revolves only around listing Pods and Services in the default namespace.
The command above will be denied! See;
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {},
"status": "Failure",
"message": "pods is forbidden: User \"system:serviceaccount:default:alice-svc\" cannot list resource \"pods\" in API group \"\" at the cluster scope",
"reason": "Forbidden",
"details": {
"kind": "pods"
},
"code": 403
List pods in the default namespace;
curl -H "Authorization: Bearer $TOKEN" https://192.168.122.60:6443/api/v1/namespaces/default/pods -k
{
"kind": "PodList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "1910734"
},
"items": []
curl -H "Authorization: Bearer $TOKEN" https://192.168.122.60:6443/api/v1/namespaces/default/services -k
{
"kind": "ServiceList",
"apiVersion": "v1",
"metadata": {
"resourceVersion": "1910774"
},
"items": [
{
"metadata": {
"name": "kubernetes",
"namespace": "default",
"uid": "70ed9dfa-c4ea-46b4-9874-5a8e170efb16",
"resourceVersion": "234",
"creationTimestamp": "2024-05-13T20:41:04Z",
"labels": {
"component": "apiserver",
"provider": "kubernetes"
},
"managedFields": [
{
"manager": "kube-apiserver",
"operation": "Update",
"apiVersion": "v1",
"time": "2024-05-13T20:41:04Z",
"fieldsType": "FieldsV1",
"fieldsV1": {
"f:metadata": {
"f:labels": {
".": {},
"f:component": {},
"f:provider": {}
}
},
"f:spec": {
"f:clusterIP": {},
"f:internalTrafficPolicy": {},
"f:ipFamilyPolicy": {},
"f:ports": {
".": {},
"k:{\"port\":443,\"protocol\":\"TCP\"}": {
".": {},
"f:name": {},
"f:port": {},
"f:protocol": {},
"f:targetPort": {}
}
},
"f:sessionAffinity": {},
"f:type": {}
}
}
}
]
},
"spec": {
"ports": [
{
"name": "https",
"protocol": "TCP",
"port": 443,
"targetPort": 6443
}
],
"clusterIP": "10.96.0.1",
"clusterIPs": [
"10.96.0.1"
],
"type": "ClusterIP",
"sessionAffinity": "None",
"ipFamilies": [
"IPv4"
],
"ipFamilyPolicy": "SingleStack",
"internalTrafficPolicy": "Cluster"
},
"status": {
"loadBalancer": {}
}
}
]
curl -H "Authorization: Bearer $TOKEN" https://192.168.122.60:6443/api/v1/namespaces/default/services -sk | jq -r '["NAME", "TYPE", "CLUSTER-IP", "EXTERNAL-IP", "PORT(S)", "AGE"], (.items[] | [.metadata.name, .spec.type, .spec.clusterIP, "<none>", (.spec.ports[0].port | tostring) + "/" + .spec.ports[0].protocol, .metadata.creationTimestamp]) | @tsv'
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 443/TCP 2024-05-13T20:41:04Z
And that is it!
Assigning Roles to Groups in Kubernetes
In our previous guide, we discussed how to create groups in Kubernetes.
While there is no native way of creating groups in a Kubernetes cluster itself, we used an example of creating users with X509 certificate authentication.
openssl x509 -in ~/.kube/users/dave/dave.crt -subject -noout
Output;
subject=CN = dave, O = devops-int
openssl x509 -in ~/.kube/users/carol/carol.crt -subject -noout
subject=CN = carol, O = devops-int
As you can see, both users belong to devops-int group.
Create a view-only role on Pods, Services and Deployments for devops-int group;
kubectl create role view-only --verb=get,list,watch --resource=pods,deployments,services
Assign the role to the group via role binding in the default namespace.
kubectl create rolebinding devops-view-only --group=devops-int --role=view-only
Verify that you can list or get pods, services, and deployments in the specified namespace.
Switch to dave context!
kubectl config use-context dave
Verify permissions;
kubectl auth can-i '*' '*'
no
kubectl auth can-i get '*'
no
kubectl auth can-i list '*'
no
kubectl auth can-i list pods
yes
kubectl auth can-i get pods
yes
kubectl auth can-i list services
yes
kubectl auth can-i get services
yes
kubectl auth can-i list deployments
yes
kubectl auth can-i get deployments
yes
Create and Assign ClusterRoles to Users and Groups
If you want to create roles that applies cluster-wide, you simply have to replace role option with clusterrole option in the kubectl create command as shown above.
kubectl create clusterrole <name> --verb=<permissions> --resource=<resources>
Before you can create your custom cluster role, you can view existing roles;
kubectl get clusterroles
NAME CREATED AT
admin 2024-05-13T20:41:04Z
api-clusterrole 2024-05-17T04:45:14Z
calico-cni-plugin 2024-05-13T21:05:22Z
calico-kube-controllers 2024-05-13T21:05:22Z
calico-node 2024-05-13T21:05:22Z
calico-typha 2024-05-13T21:05:22Z
cluster-admin 2024-05-13T20:41:04Z
edit 2024-05-13T20:41:04Z
kubeadm:get-nodes 2024-05-13T20:41:05Z
system:aggregate-to-admin 2024-05-13T20:41:04Z
system:aggregate-to-edit 2024-05-13T20:41:04Z
system:aggregate-to-view 2024-05-13T20:41:04Z
system:auth-delegator 2024-05-13T20:41:04Z
system:basic-user 2024-05-13T20:41:04Z
system:certificates.k8s.io:certificatesigningrequests:nodeclient 2024-05-13T20:41:04Z
system:certificates.k8s.io:certificatesigningrequests:selfnodeclient 2024-05-13T20:41:04Z
system:certificates.k8s.io:kube-apiserver-client-approver 2024-05-13T20:41:04Z
system:certificates.k8s.io:kube-apiserver-client-kubelet-approver 2024-05-13T20:41:04Z
system:certificates.k8s.io:kubelet-serving-approver 2024-05-13T20:41:04Z
system:certificates.k8s.io:legacy-unknown-approver 2024-05-13T20:41:04Z
system:controller:attachdetach-controller 2024-05-13T20:41:04Z
system:controller:certificate-controller 2024-05-13T20:41:04Z
system:controller:clusterrole-aggregation-controller 2024-05-13T20:41:04Z
system:controller:cronjob-controller 2024-05-13T20:41:04Z
system:controller:daemon-set-controller 2024-05-13T20:41:04Z
system:controller:deployment-controller 2024-05-13T20:41:04Z
system:controller:disruption-controller 2024-05-13T20:41:04Z
system:controller:endpoint-controller 2024-05-13T20:41:04Z
system:controller:endpointslice-controller 2024-05-13T20:41:04Z
system:controller:endpointslicemirroring-controller 2024-05-13T20:41:04Z
system:controller:ephemeral-volume-controller 2024-05-13T20:41:04Z
system:controller:expand-controller 2024-05-13T20:41:04Z
system:controller:generic-garbage-collector 2024-05-13T20:41:04Z
system:controller:horizontal-pod-autoscaler 2024-05-13T20:41:04Z
system:controller:job-controller 2024-05-13T20:41:04Z
system:controller:legacy-service-account-token-cleaner 2024-05-13T20:41:04Z
system:controller:namespace-controller 2024-05-13T20:41:04Z
system:controller:node-controller 2024-05-13T20:41:04Z
system:controller:persistent-volume-binder 2024-05-13T20:41:04Z
system:controller:pod-garbage-collector 2024-05-13T20:41:04Z
system:controller:pv-protection-controller 2024-05-13T20:41:04Z
system:controller:pvc-protection-controller 2024-05-13T20:41:04Z
system:controller:replicaset-controller 2024-05-13T20:41:04Z
system:controller:replication-controller 2024-05-13T20:41:04Z
system:controller:resourcequota-controller 2024-05-13T20:41:04Z
system:controller:root-ca-cert-publisher 2024-05-13T20:41:04Z
system:controller:route-controller 2024-05-13T20:41:04Z
system:controller:service-account-controller 2024-05-13T20:41:04Z
system:controller:service-controller 2024-05-13T20:41:04Z
system:controller:statefulset-controller 2024-05-13T20:41:04Z
system:controller:ttl-after-finished-controller 2024-05-13T20:41:04Z
system:controller:ttl-controller 2024-05-13T20:41:04Z
system:controller:validatingadmissionpolicy-status-controller 2024-05-13T20:41:04Z
system:coredns 2024-05-13T20:41:05Z
system:discovery 2024-05-13T20:41:04Z
system:heapster 2024-05-13T20:41:04Z
system:kube-aggregator 2024-05-13T20:41:04Z
system:kube-controller-manager 2024-05-13T20:41:04Z
system:kube-dns 2024-05-13T20:41:04Z
system:kube-scheduler 2024-05-13T20:41:04Z
system:kubelet-api-admin 2024-05-13T20:41:04Z
system:monitoring 2024-05-13T20:41:04Z
system:node 2024-05-13T20:41:04Z
system:node-bootstrapper 2024-05-13T20:41:04Z
system:node-problem-detector 2024-05-13T20:41:04Z
system:node-proxier 2024-05-13T20:41:04Z
system:persistent-volume-provisioner 2024-05-13T20:41:04Z
system:public-info-viewer 2024-05-13T20:41:04Z
system:service-account-issuer-discovery 2024-05-13T20:41:04Z
system:volume-scheduler 2024-05-13T20:41:04Z
tigera-operator 2024-05-13T20:53:51Z
view 2024-05-13T20:41:04Z
To check permissions associated with view role;
kubectl describe clusterrole view
Name: view
Labels: kubernetes.io/bootstrapping=rbac-defaults
rbac.authorization.k8s.io/aggregate-to-edit=true
Annotations: rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
Resources Non-Resource URLs Resource Names Verbs
--------- ----------------- -------------- -----
bindings [] [] [get list watch]
configmaps [] [] [get list watch]
endpoints [] [] [get list watch]
events [] [] [get list watch]
limitranges [] [] [get list watch]
namespaces/status [] [] [get list watch]
namespaces [] [] [get list watch]
persistentvolumeclaims/status [] [] [get list watch]
persistentvolumeclaims [] [] [get list watch]
pods/log [] [] [get list watch]
pods/status [] [] [get list watch]
pods [] [] [get list watch]
replicationcontrollers/scale [] [] [get list watch]
replicationcontrollers/status [] [] [get list watch]
replicationcontrollers [] [] [get list watch]
resourcequotas/status [] [] [get list watch]
resourcequotas [] [] [get list watch]
serviceaccounts [] [] [get list watch]
services/status [] [] [get list watch]
services [] [] [get list watch]
controllerrevisions.apps [] [] [get list watch]
daemonsets.apps/status [] [] [get list watch]
daemonsets.apps [] [] [get list watch]
deployments.apps/scale [] [] [get list watch]
deployments.apps/status [] [] [get list watch]
deployments.apps [] [] [get list watch]
replicasets.apps/scale [] [] [get list watch]
replicasets.apps/status [] [] [get list watch]
replicasets.apps [] [] [get list watch]
statefulsets.apps/scale [] [] [get list watch]
statefulsets.apps/status [] [] [get list watch]
statefulsets.apps [] [] [get list watch]
horizontalpodautoscalers.autoscaling/status [] [] [get list watch]
horizontalpodautoscalers.autoscaling [] [] [get list watch]
cronjobs.batch/status [] [] [get list watch]
cronjobs.batch [] [] [get list watch]
jobs.batch/status [] [] [get list watch]
jobs.batch [] [] [get list watch]
endpointslices.discovery.k8s.io [] [] [get list watch]
daemonsets.extensions/status [] [] [get list watch]
daemonsets.extensions [] [] [get list watch]
deployments.extensions/scale [] [] [get list watch]
deployments.extensions/status [] [] [get list watch]
deployments.extensions [] [] [get list watch]
ingresses.extensions/status [] [] [get list watch]
ingresses.extensions [] [] [get list watch]
networkpolicies.extensions [] [] [get list watch]
replicasets.extensions/scale [] [] [get list watch]
replicasets.extensions/status [] [] [get list watch]
replicasets.extensions [] [] [get list watch]
replicationcontrollers.extensions/scale [] [] [get list watch]
ingresses.networking.k8s.io/status [] [] [get list watch]
ingresses.networking.k8s.io [] [] [get list watch]
networkpolicies.networking.k8s.io [] [] [get list watch]
poddisruptionbudgets.policy/status [] [] [get list watch]
poddisruptionbudgets.policy [] [] [get list watch]
If you want to create your custom role, here is an example that allows you to view all resources.
kubectl create clusterrole view-only --verb=get,list,watch --resource=*
To assign a roles to user/service account or a group, you use clusterrolebinding.
kubectl create clusterrolebinding bob-view-cluster --clusterrole=view --user=bob
For a service account, replace –user with –serviceaccount and –group for group.
To confirm, either switch to user’s context or try API access with tokens.
kubectl config use-context bob
/sbin/ipkubectl auth can-i '*' '*'
no
/sbin/ipkubectl auth can-i list '*'
no
/sbin/ipkubectl auth can-i list services
yes
/sbin/ipkubectl auth can-i list deployments
yes
And that is it on how to create Kubernetes roles, assign them to the users/groups and verify.
Read more on Kubernetes API Access Control.