AppArmor and Kubernetes

January 22, 2021

Basically, AppArmor is a security enhancement for the Linux kernel, which restricts access to resources. AppArmor can use different profiles and enforce several rules to related components.

Some components which can be used:

  • Linux capabilities
  • Files
  • Network sockets
  • Signals
  • ptrace

In Docker and Kubernetes world, you may want to avoid malicious actions when an attacker gains access to the container. Of course, AppArmor will not be enough without any other prevention.


I’m gonna use Ubuntu 18.04 in this tutorial. In some cloud providers, AppArmor already installed on this version, but if you don’t have it, just run it:

apt-get install apparmor AppArmor-utils -y

You can list the existing profiles and show the current status as shown below:


You can also use:



As you can see in the screenshot, 15 profiles are running in Enforce mode.


AppArmor profiles are based on text files and located under /etc/AppArmor.d/ directory. The following screenshot is showing the rsyslog profile.


The lines which contain “r”, “w”, “rwk” at the end of the line are path entries that provide which files and directories can be used by the application.

Capability lines determine the security privileges like context-based.

Let’s create a profile skeleton to use with Kubernetes later. Profiles can be created by text file or aa-genprof tool.


#include <tunables/global>
profile deny-profile flags=(attach_disconnected) {
  #include <abstractions/base>

  deny /root/** w,

Note: #include <abstractions/base> is applying basic rules.

This profile grants super access on the file system to the application except for executable permission on the /root directory. If you want to specify the entire file system, you can declare deny variable like:

deny /** x,

Even we can use regex patterns to specify any file or directory names. Here is the part of the list of file-level permissions on AppArmor:

  • r is read
  • w is write
  • x is executable
  • m is the map as executable
  • k is a file locking
  • l is hard links

and so on.

Now reload the our profile:

apparmor_parser -r /etc/apparmor.d/kubernetes-apparmor

And enforce it:

aa-enforce /etc/apparmor.d/kubernetes-apparmor


As you can see, our deny-profile is loaded and running as enforce mode.

Let’s create a pod with Alpine image and declare the AppArmor profile. To do this, we must use ” “annotation. Then specify the profile on the local environment. If the profile name doesn’t match in the YAML file, you’ll get an error message about that.

apiVersion: v1
kind: Pod
  name: apparmor-test
  annotations: localhost/deny-profile
  - name: app-test
    image: alpine
    command: [ "sh", "-c", "echo 'AppArmor test |' && sleep 7h" ]

Our expectation is we can’t create anything under the /root directory with “touch” command. Create the pod and try to read somethings on /root path.


As you can see, we couldn’t create a new file via touch under the path. To prove it in another way, now we can try to create a file under the root path.


No any error. You can also check the Kubernetes events that profile assigned to the pod.


It’s possible to create AppArmor profiles with Kubernetes via some third party APIs as a kind.

Note: Deny rules cannot be overridden by an allow rule.

Let’s make another sample to deny network access. We should restrict “inet” and “raw”.

#include <tunables/global>
profile ping flags=(attach_disconnected) {
  deny network inet,
  deny network raw,


To check the “ping” utility inside the container, you can use an image that already includes a ping tool, or you should install net-tools in the container. After applying the AppArmor profile, try ping to

Before restricted rule:


After the rule:


AppArmor profiles can be used with PodSecurityPolicy.

Written by Deniz Parlak