What is RBAC?
RBAC (role based access control) is a security approach to restricting system resources to specific subjects via access lists. RBAC in Kubernetes allows admins to delegate specific types of permissions against specific API objects to other specific API objects. This is accomplished via interactions between three specific object types:
- Role/ClusterRole
- RoleBinding/ClusterRoleBinding
- ServiceAccount/User/Group
ServiceAccounts, Users, and Groups are ways to assign an identity to a subject. ServiceAccounts are basically proxy actors that can be assigned to pods to help grant them a concrete RBAC identity and thus a way to be granted access to specific resources. These are the primary identity object that Kubernetes admins interact with. ServiceAccounts are explicitly namespaced, so multiple ServiceAccounts with the same name can be created across different namespaces and each is actually a distinct identity. Along with User and Groups objects, these guys define the who.
Note: User and Group objects are kind of weird and annoying because there’s not a way to directly query them and see which Users and Groups exist and which permissions they have. So I’m going to effectively ignore them from here on out.
Roles and ClusterRoles are effectively buckets for rules that define the specific API objects (resources
) within different API groups (apiGroups
) that are able to be acted upon and describe how (verbs
) they can be acted upon. Roles are scoped to specific namespaces, while ClusterRoles are scoped to entire clusters. These guys define the what.
RoleBindings and ClusterRolesBindings act like the glue that connect the who to the what.
Give me an example
Alright sure, let’s talk specifics. Argocd is a popular gitops project that provides automation mechanisms to make deployments in kubernetes truly declarative and continuous.
Really awesome stuff.
Anyways, it has a few different installation flavors (which is not really relevant to this discussion so we won’t go into the details here), but for this example we are specifically looking at the server component’s installation manifests.
With our reference material in place to follow along with, the overall process for creating RBAC components and associating them to workloads looks like this:
- Give your workload an identity via a
ServiceAccount
- Attach the identity to your workload
- Figure out what API resources you want your workload to be able to access, and define those interactions in a
Role
- Explicitly connect the permissions to the workload identity using a
RoleBinding
1. I’m gonna need to see some ID
A ServiceAccount is pretty straightforward, it acts as an authentication identity the same way your drivers license does when you’re trying to get into that exclusive club that just opened up downtown (“Alright you can come in”). It also acts as a thing to attach specific permissions to (“Yes you’re granted access to the VIP lounge”).
apiVersion: v1
kind: ServiceAccount
metadata:
labels:
app.kubernetes.io/name: argocd-server
app.kubernetes.io/part-of: argocd
app.kubernetes.io/component: server
name: argocd-server
2. I’m rubber and you’re glue
A workload can only be associated with a single ServiceAccount identity at any given time (although the inverse is not true), which is done by explicitly declaring the ServiceAccount to load in the workload manifest.
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app.kubernetes.io/name: argocd-server
app.kubernetes.io/part-of: argocd
app.kubernetes.io/component: server
name: argocd-server
spec:
selector:
matchLabels:
app.kubernetes.io/name: argocd-server
template:
metadata:
labels:
app.kubernetes.io/name: argocd-server
spec:
serviceAccountName: argocd-server
#<.... Truncated for brevity ....>
Any workload that does not define a ServiceAccount actually just inherits the namespaces default ServiceAccount.
3. Role-o oh Role-o, wherefore art thou Role-o
The argocd server needs to operate on specific Kubernetes API objects in order to do its job and provide value to the team who wants to use it, but we want to give the argocd server only as much permission as it needs and nothing extra. So we create a Role
object and inside describe exactly what API objects the argocd server can interface with and what actions the workload can take on them.
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
labels:
app.kubernetes.io/name: argocd-server
app.kubernetes.io/part-of: argocd
app.kubernetes.io/component: server
name: argocd-server
rules:
- apiGroups:
- ""
resources:
- secrets
- configmaps
verbs:
- create
- get
- list
- watch
- update
- patch
- delete
- apiGroups:
- argoproj.io
resources:
- applications
- appprojects
verbs:
- create
- get
- list
- watch
- update
- delete
- patch
- apiGroups:
- ""
resources:
- events
verbs:
- create
- list
This looks like a lot is going on here but once you break it down it’s actually quite straightforward. Here we are defining a list of permissions named argocd-server
and this list of permissions is granting:
- full permissions to argocd for
Secret
andConfigMap
API objects - full permissions on some new argocd-specific custom resource objects
Applications
andAppProjects
- the ability to create and list Kubernetes
Events
4. That rug RoleBinding
really tied the room together
RoleBindings act as pure glue in this loosely coupled system, connecting a Role
to a ServiceAccount
subject (or multiple ServiceAccounts if need be!).
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
labels:
app.kubernetes.io/name: argocd-server
app.kubernetes.io/part-of: argocd
app.kubernetes.io/component: server
name: argocd-server
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: argocd-server
subjects:
- kind: ServiceAccount
name: argocd-server
In this case we are assigning the argocd-server Role
to the argocd-server ServiceAccount
subject, and the link between the two is the argocd-server RoleBinding
.