The aim of this doc is, as the title suggests, to load an apparmor profile to a Kubernetes node and see that it is loaded. What it is not is an explainer of AppArmor or how to create profiles. For that there are a few resources at the bottom which will be more useful.

Install the tools

I’m using the AppArmor utils to manipulate AppArmor here. First thing you will want to do is open a shell on the kubernetes node. An important thing to note here is if there are multiple nodes on your cluster then you will have to do this individually for each node so it is easiest to scale your cluster down to a single node if possible. Opening the shell is different depending on where you cluster is, I’m using GKE on Google’s cloud so I use this command to connect.

gcloud beta compute ssh --zone "<zone>" "<node_name>" --project "<project_name>"

The tools themselves are installed using the following command.

apt-get update && apt-get install -y apparmor apparmor-utils

Note: An important thing to note is this node is running ubuntu. You can use the toolbox command if it is available to use apt-get below.

Check it is working using the following which will list the profiles that already exist on the node. You may need root access using sudo su

aa-status

Setting up the Profile

AppArmor profiles are stored in the /etc/apparmor.d directory so cd into that folder and create a new file. I am using the name k8s-apparmor-deny-write. As the name suggests the profile will block all writes to the filesystem while allowing reads to still happen. Line 3 below blocks all writes to the filesystem.

profile k8s-apparmor-deny-write flags=(attach_disconnected) {
  file,
  # Deny all file writes.
  deny /** w,
}

Enforce the profile

Use the following to set the above profile into enforce mode.

aa-enforce k8s-apparmor-deny-write

Use the aa-status command again to check this has been loaded correctly. The profile should appear with the enforced profiles now.

Check it works

AppArmor logs can be tailed using one of the two following commands. Anything that tries to write on a pod will cause a new entry in the logs.

sudo journalctl -fx | grep audit
tail -f /var/log/audit/audit.log

To test it works, create a pod, exec into it and try creating a new file using the following.

apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor
  annotations:
    # Tell Kubernetes to apply the AppArmor profile "k8s-apparmor-example-deny-write".
    # Note that this is ignored if the Kubernetes node is not running version 1.4 or greater.
    container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
spec:
  containers:
  - name: hello
    image: busybox
    command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]

Open a shell with the following and then try using touch newfile.txt. You should get an error and a new entry in the logs above.

k exec <pod_name> -- sh

Summary

Here we’ve loaded an AppArmor policy to a node.

For automating this process there are a few resources. The first is the security-profiles-operator which loads apparmor profiles among other things on every node.

Another tool is the apparmor-loader to load profiles on all nodes of the cluster. There is a helm chart for it too.

References