Granting permissions to users with Kubernetes
In the previous article, we discussed how to create users in Kubernetes so we don’t have the problem of everyone having the same level of access across all namespaces. However, we didn’t elaborate on how we can actually grant those permissions.
Let’s understand more about how RBAC (Role Based Access Control) works and how we can take advantage of it to use our cluster more securely.
RBAC#
Kubernetes supports several authorization methods, the most famous by far is RBAC which stands for Role Based Access Control. Basically, RBAC limits access to cluster resources through four Kubernetes resources: Roles, RoleBindings, ClusterRoles, and ClusterRoleBindings.
Having role-based access instead of profile-based access lets you share these Roles with multiple users, distributing permissions across the cluster so everyone has the correct access when they need it. A basic use case for this model is allowing, for example, coordinators and area managers to access their own namespaces to manage their own teams, thus avoiding escalating issues to the cluster operations team.
To create these access policies, we’ll use a very special API group in Kubernetes, rbac.authorization.k8s.io. This means you can create and configure these access policies dynamically through the Kubernetes API and kubectl.
Most clusters come with RBAC enabled by default, but it’s possible to start a new kube-apiserver with the --authorization-mode=RBAC flag for manual clusters, and in Azure, you can enable RBAC in an AKS cluster directly from the portal:

Terminology#
In RBAC, users are called subjects, the APIs and resources that users may or may not have access to are called resources. We also have verbs, which are the actions and operations that can be executed on a resource by a subject.
Roles and ClusterRoles#
The foundation of all RBAC access policies is a native object called Role. Roles are the rules that define access to a resource, meaning they are not the rules applied to a subject but rather a set of rules that can be reused for multiple users.
In addition to Roles, there are ClusterRoles which, like Roles, are access rules for one or more resources, but while the Role object is limited to its own namespace, the ClusterRole applies to the entire cluster regardless of which namespace it’s in. This allows you to define global policies that apply to the entire cluster and to define policies for Kubernetes resources that don’t depend on namespaces, like Nodes.
There are some important rules to know before we get started:
- There are no rules to “deny” user access to a resource. As we mentioned before, like Ingresses, Kubernetes works on a
deny-allmodel, meaning all permissions are denied by default and everything you create will be an exception to the deny list. - Roles always depend on a namespace, so when you create a new Role, you need to set which namespace it belongs to.
- Conversely, ClusterRoles don’t need a namespace because they’re above that separation.
Defining our permissions#
In the previous article we created a user named Lucas who is part of the development team. Let’s imagine we also created other users in the system to complete our team:
- Ana, who is the development area coordinator (leader of the
devsgroup, which Lucas is part of) - Thiago, who is the BI area manager (leader of the
bigroup) - Fernanda, who is a BI analyst (part of the
bigroup) - Amanda, who is one of the DevOps area coordinators and one of the cluster operators responsible for maintaining several development projects (part of the
devsanddevopsgroups)
For our scenario, let’s imagine Lucas works on one of many projects in the company’s cluster. To allow the work to be done, he needs access to his namespace. And because the company has a more mature DevOps culture, all developers are responsible for deploying their applications, so he needs full access to creating resources.
Ana, being the development leader, needs full access to all development area projects.
Thiago, likewise, needs full read access to all objects in the cluster, but Fernanda is working on the same project as Lucas, so she can only have read access in this namespace.
Amanda is the cluster coordinator, so she needs full access to all objects in the cluster to be able to perform actions on them.
Creating Roles#
Now that we have our scenarios defined, let’s move on to creating our permissions. We’ll create a base object called developer that will apply to every developer in the projeto1 namespace, which is the project for Lucas and Fernanda’s team:
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: developer namespace: projeto1So far we’re defining that this Role will be within the project’s namespace, so all permissions will be restricted to this namespace. Now let’s define the rules:
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: developer namespace: projeto1rules:- apiGroups: ["", "autoscaling", "apps", "networking.k8s.io"] verbs: ["get", "list", "create", "watch", "update"] resources: ["*"]What we’re doing here is creating a Role that will grant access to all resources present in the following APIs:
"": Means the KubernetescoreAPI, that is, when we don’t use any FQDN before the/when creating a new workload. For example, Pods are part of this API; when we create a new Pod we useapiVersion: v1.autoscaling: The group that controls application scalability;HorizontalPodAutoscalersare part of this groupapps: The group forDeployments,DaemonSets, and othersnetworking.k8s.io: The group forIngresses
You can find all API groups in the official documentation and also using the
kubectl api-resources -o widecommand, which will show not only the groups but also the available names and verbs.
In addition, we’re granting access to almost all verbs except delete for all resources described by these APIs through the * wildcard.
For the read-only Role, we’ll make a copy of this Role and call it developer-readonly:
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: developer-readonly namespace: projeto1rules:- apiGroups: ["*"] verbs: ["get", "list"] resources: ["*"]For the development area management Role, we need to create a permission that allows full access to all resources and verbs within the namespaces specific to the development area. Let’s assume this area has only two projects called projeto1 and projeto2. In this case we need to create two identical Roles, one for each namespace, and we’ll name them developer-admin:
apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: developer-admin namespace: projeto1rules:- apiGroups: ["*"] verbs: ["*"] resources: ["*"]---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: developer-admin namespace: projeto2rules:- apiGroups: ["*"] verbs: ["*"] resources: ["*"]We can also create Roles interactively with
kubectl create role <name> -n <namespace> --verb=verb1,verb2,verb3 --resource=resource1,resource2
Creating ClusterRoles#
For the Role that will be applied to Thiago, we need to allow him to read and list any resource in any namespace of the cluster. This would be complicated if we were creating a common Role, so we’ll create a ClusterRole so it can be applied automatically. This ClusterRole will be called readonly:
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: readonlyrules:- apiGroups: ["*"] verbs: ["get", "list", "watch"] resources: ["*"]For the last permission, we’ll create the object that will be given to Amanda. Since she’s the cluster operator, she needs full access to all resources in all cluster namespaces. We’ll call this ClusterRole cluster-operator:
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: cluster-operatorrules:- apiGroups: ["*"] verbs: ["*"] resources: ["*"]Similarly to Roles, we can create ClusterRoles with
kubectlusing the commandkubectl create clusterrole <name> --verb=verb1,verb2 --resource=resource1,resource2
Applying permissions with bindings#
As we mentioned before, Roles and ClusterRoles are just permission definitions. These definitions need to be applied to users through other “sibling” objects called RoleBindings and ClusterRoleBindings.
Bindings grant the permissions defined in Roles and ClusterRoles to subjects and groups. In our case, we have some groups but also some individual users we want to grant access. We can also grant access to a ServiceAccount.
The differences between the two are basically the same as between Role and ClusterRole. While a RoleBinding can be applied to a Role or a ClusterRole (although when done that way, it will apply the ClusterRole’s rules only to the namespace to which that RoleBinding belongs), a ClusterRoleBinding can only be applied to a ClusterRole.
All bindings need a reference to an existing Role or ClusterRole. RoleBindings can only reference Roles within the same namespace, while ClusterRoleBindings can reference any ClusterRoles.
For our first binding, let’s look at the Lucas user, who will have the developer Role associated with him:
apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: developer namespace: projeto1subjects: - kind: Group name: devs apiGroup: rbac.authorization.k8s.ioroleRef: - kind: Role name: developer apiGroup: rbac.authorization.k8s.ioWhat we’re doing here is creating a RoleBinding that will be applied to all subjects in the subjects array. In this case, a subject can have several kinds, such as User, Group, and ServiceAccount.
Additionally, in the roleRef key, we have the name of the Role we’ll apply to these subjects. Here we have two possible kind values: Role or ClusterRole. And we’re essentially saying we want the Role called developer to be applied to the subject whose kind is a Group, that is, a group of users called devs.
Now, for Fernanda’s binding, we’ll do the same thing, but change the name of the roleRef:
apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: business-intelligence namespace: projeto1subjects: - kind: Group name: bi apiGroup: rbac.authorization.k8s.ioroleRef: - kind: Role name: readonly apiGroup: rbac.authorization.k8s.ioNow let’s create the binding for Ana. Just as we created two different roles, we’ll create two more bindings to ensure we grant her access directly:
apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: developer-admin namespace: projeto1subjects: - kind: User name: ana apiGroup: rbac.authorization.k8s.ioroleRef: - kind: Role name: developer-admin apiGroup: rbac.authorization.k8s.io---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: developer-admin namespace: projeto2subjects: - kind: User name: ana apiGroup: rbac.authorization.k8s.ioroleRef: - kind: Role name: developer-admin apiGroup: rbac.authorization.k8s.ioNow we need to create the last two bindings, which are ClusterRoleBindings, because we’ll be granting access to the entire cluster. In Thiago’s case, we need to be careful because we can’t apply the ClusterRole to the bi group since Fernanda is also part of that group, so we’ll apply it only to the user:
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: readonlysubjects: - kind: User name: thiago apiGroup: rbac.authorization.k8s.ioroleRef: - kind: ClusterRole name: readonly apiGroup: rbac.authorization.k8s.ioAnd similarly, we’ll create Amanda’s binding:
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: cluster-operatorsubjects: - kind: User name: amanda apiGroup: rbac.authorization.k8s.ioroleRef: - kind: ClusterRole name: cluster-operator apiGroup: rbac.authorization.k8s.ioWe can also create RoleBindings and ClusterRoleBindings interactively with
kubectl create rolebinding <name> --[user|group|serviceaccount] <subject> --[role|clusterrole] <roleRef>
Now with the bindings created, we can execute commands as each user and each of them will have the permissions needed for their teams.
Conclusion#
Now we know how to create users and how to assign permissions to them. In the next articles, we’ll see how we can further improve the authentication model using Azure Managed AD with AKS!
See you there!