ABAC In Kubernetes

Attribute-based access control (ABAC) is a powerful feature in Kubernetes that allows administrators to define fine-grained permissions for users and groups. Unlike traditional role-based access control (RBAC), ABAC enables a more granular approach to specify what actions can and cannot be performed. However, implementing ABAC can become complex as modifications increase over time. This blog delves into configuring the Kubernetes API server to manage ABAC effectively and explores the trade-offs of using ABAC alongside other identity and access management tools. We’ll also discuss how ABAC compares to RBAC, customized roles, and policies such as OPA, Azure Policy, and SCPs in cloud environments.

Getting Started

For the use of ABAC in Kubernetes a few feature flags need to be introduced to the Kube API Server specifically this is going to cause a restart of the API server you’ll see some of the aspects of how we plan for this as well.

To navigate and check our settings in our Kube API Server we will use the following.

cd /etc/kubernetes/manifests

Assuming we are using the underlying cluster as “Kubeadm” this is typically located in the /etc/kubernetes/manifests. Fairly similar in other distributions however just noting if you’re following along.

Under our Spec we should see the following.

The flags we are going to alter from this are –authorization-mode (adding to our cluster ABAC), –authorization-file-policy (where we store the file reference)

Before we alter anything required for this to be restarted its important we create a copy of our original kube-apiserver.yaml to revert if needed.

cp kube-apiserver.yaml kube-apiserver.yaml.ori

Now that we’ve created our copy of our manifest we will need to create a JSONL file of our policy.

nano abac-policy.jsonl
{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "system:serviceaccount:johndoe", "namespace": "default", "resource": "pods", "readonly": true}}

Then we make a directory next to our /etc/manifests for our policy to stay as a static file.

cd /etc/kubernetes
mkdir abac
mv abac-policy.jsonl /etc/kubernetes/abac

Now once we have done this we are prepared to update our Kube API Server as shown in this image we start with telling our cluster we want the mode to expand to “ABAC” then where our policy file resides.

Now we also have to add our volume and volume mounts so the cluster is aware of where the policy is located and that it can be accessed so if we navigate to volume mounts we add the following

volumeMounts:
- name: abac-policy
  mountPath: /etc/kubernetes/abac
  readOnly: true
volumes:
- name: abac-policy
  hostPath:
    path: /etc/kubernetes/abac
    type: DirectoryOrCreate

Once we’ve made these updates we can now save our file this should reset the clusters API server we can monitor running the following.

crictl ps
docker ps (if using docker)

Now we are running again we can issue out normal command via kubectl we first will start with creating a service account as follows.

apiVersion: v1
kind: ServiceAccount
metadata:
  name: johndoe
  namespace: default
secrets: 
  - name: john-secret
---
apiVersion: v1
kind: Secret
metadata:
  name: john-secret
  namespace: default
  annotations:
    kubernetes.io/service-account.name: johndoe
type: kubernetes.io/service-account-token

Save this in a .yaml file such as serviceaccount.yaml then run the following.

kubectl apply -f serviceaccount.yaml

Now to retrieve any token from this we will use the following commands to help us.

# Get the secret name
kubectl get serviceaccount johndoe -o jsonpath='{.secrets[0].name}'

# Retrieve the token
kubectl get secret john-secret -o jsonpath='{.data.token}' | base64 --decode

# Save the token to a file
kubectl get secret john-secret -o jsonpath='{.data.token}' | base64 --decode > john-secret.txt

So now we have our secret with our token saved to a local file now we have to switch context of our policy we’ve referenced and applied to our cluster to test it out.

We start with setting up our new credentials as below.

# Use of SA Token
export SA_TOKEN=$(cat john-secret.txt)
kubectl config set-credentials johndoe --token=$SA_TOKEN

Then we set up our next context.

kubectl config set-context john-context --cluster=kubernetes --namespace=default --user=johndoe
kubectl config use-context john-context

So we should be only allowed as johndoe to access the listing of our Pods in the cluster.

kubectl get pods # For some reason this failed
kubectl run test-pod --image=nginx # Fails as intended

So now we switch back to our cluster-admin role (highly privileged and edit our policy)

kubectl config use-context kubernetes-admin@kubernetes

I believe once I didn’t include the syntax of the following for the service account it acted erroneously in applying the ABAC so for the edit its as follows.

{"apiVersion": "abac.authorization.kubernetes.io/v1beta1", "kind": "Policy", "spec": {"user": "system:serviceaccount:johndoe", "namespace": "default", "resource": "pods", "apiGroup": "*" "readonly": false}}

After I’ve edited this abac-policy.jsonl and change our syntax of readonly to false. We also edit the user to reference our system:serviceaccount.

So now we switch back to our user context and attempt to create a pod.

kubectl config use-context john-context
kubectl run test-pod --image=nginx -n default

Some of the notes to understand when using the ABAC functionality and limitations

  • You’ll have to add permissions to a file specifically in the .jsonl format
  • Assuming your cluster is being used in multi-tenancy such as SaaS operations this can likely be hard to maintain or restrict
  • The operation does require when a update occurs the control plane or API Server will have to update to reference the changes in the file (this could depending on how you configure High Availability could cause delays).

Summary

Authorization and Authentication are pivotal decisions in a cluster specifically with how abstractions in kubernetes work the fundamentals of Roles, ClusterRoles are relatively mature and offer a way for us to consolidate Developers (with relative permissions they’ll need) such as ConfigMaps, Secrets, Read/List Pods Etc. The use of ABAC while a way to use and restrict permissions should be used with caution given it does take some configuration, I’ve looked further as you use a service account token to mount and change context this is denoted when you create a service account as follows. Important to note each Service Account has a corresponding ABAC username according to documentation as shown below.

system:serviceaccount:<namespace>:<name>

Reference:

https://kubernetes.io/docs/reference/access-authn-authz/abac