AppArmor security profiles for Docker

AppArmor (Application Armor) is a Linux security module that protects an operating system and its applications from security threats. To use it, a system administrator associates an AppArmor security profile with each program. Docker expects to find an AppArmor policy loaded and enforced.

Docker automatically generates and loads a default profile for containers named docker-default.The Docker binary generates this profile intmpfsand then loads it into the kernel.

Note

This profile is used on containers, not on the Docker daemon.

A profile for the Docker Engine daemon exists but it is not currently installed with thedebpackages. If you are interested in the source for the daemon profile, it is located in contrib/apparmor in the Docker Engine source repository.

Understand the policies

Thedocker-defaultprofile is the default for running containers. It is moderately protective while providing wide application compatibility. The profile is generated from the following template.

When you run a container, it uses thedocker-defaultpolicy unless you override it with thesecurity-optoption. For example, the following explicitly specifies the default policy:

$docker run --rm -it --security-optapparmor=docker-default hello-world

Load and unload profiles

To load a new profile into AppArmor for use with containers:

$apparmor_parser -r -W /path/to/your_profile

Then, run the custom profile with--security-opt:

$docker run --rm -it --security-optapparmor=your_profile hello-world

To unload a profile from AppArmor:

#unload the profile
$apparmor_parser -R /path/to/profile

Resources for writing profiles

The syntax for file globbing in AppArmor is a bit different than some other globbing implementations. It is highly suggested you take a look at some of the below resources with regard to AppArmor profile syntax.

Nginx example profile

In this example, you create a custom AppArmor profile for Nginx. Below is the custom profile.

#include<tunables/global>


profiledocker-nginxflags=(attach_disconnected,mediate_deleted){
#include<abstractions/base>

networkinettcp,
networkinetudp,
networkineticmp,

denynetworkraw,

denynetworkpacket,

file,
umount,

deny/bin/** wl,
deny /boot/** wl,
deny /dev/** wl,
deny /etc/** wl,
deny /home/** wl,
deny /lib/** wl,
deny /lib64/** wl,
deny /media/** wl,
deny /mnt/** wl,
deny /opt/** wl,
deny /proc/** wl,
deny /root/** wl,
deny /sbin/** wl,
deny /srv/** wl,
deny /tmp/** wl,
deny /sys/** wl,
deny /usr/** wl,

audit /** w,

/var/run/nginx.pid w,

/usr/sbin/nginx ix,

deny /bin/dash mrwklx,
deny /bin/sh mrwklx,
deny /usr/bin/top mrwklx,


capability chown,
capability dac_override,
capability setuid,
capability setgid,
capability net_bind_service,

deny @{PROC}/* w, # deny write for all files directly in /proc (not in a subdir)
# deny write to files not in /proc/<number>/** or /proc/sys/**
deny @{PROC}/{[^1-9],[^1-9][^0-9],[^1-9s][^0-9y][^0-9s],[^1-9][^0-9][^0-9][^0-9]*}/** w,
deny @{PROC}/sys/[^k]** w, # deny /proc/sys except /proc/sys/k* (effectively /proc/sys/kernel)
deny @{PROC}/sys/kernel/{?,??,[^s][^h][^m]**} w, # deny everything except shm* in /proc/sys/kernel/
deny @{PROC}/sysrq-trigger rwklx,
deny @{PROC}/mem rwklx,
deny @{PROC}/kmem rwklx,
deny @{PROC}/kcore rwklx,

deny mount,

deny /sys/[^f]*/**wklx,
deny/sys/f[^s]*/**wklx,
deny/sys/fs/[^c]*/**wklx,
deny/sys/fs/c[^g]*/**wklx,
deny/sys/fs/cg[^r]*/**wklx,
deny/sys/firmware/** rwklx,
deny /sys/kernel/security/** rwklx,
}
  1. Save the custom profile to disk in the /etc/apparmor.d/containers/docker-nginxfile.

    The file path in this example is not a requirement. In production, you could use another.

  2. Load the profile.

    $sudo apparmor_parser -r -W /etc/apparmor.d/containers/docker-nginx
    
  3. Run a container with the profile.

    To run nginx in detached mode:

    $docker run --security-opt"apparmor=docker-nginx"\
    -p 80:80 -d --name apparmor-nginx nginx
    
  4. Exec into the running container.

    $docker containerexec-it apparmor-nginx bash
    
  5. Try some operations to test the profile.

    root@6da5a2a930b9:~# ping 8.8.8.8
    ping: Lacking privilege for raw socket.
    
    root@6da5a2a930b9:/# top
    bash: /usr/bin/top: Permission denied
    
    root@6da5a2a930b9:~# touch ~/thing
    touch: cannot touch 'thing': Permission denied
    
    root@6da5a2a930b9:/# sh
    bash: /bin/sh: Permission denied
    
    root@6da5a2a930b9:/# dash
    bash: /bin/dash: Permission denied
    

You just deployed a container secured with a custom apparmor profile.

Debug AppArmor

You can usedmesgto debug problems andaa-statuscheck the loaded profiles.

Use dmesg

Here are some helpful tips for debugging any problems you might be facing with regard to AppArmor.

AppArmor sends quite verbose messaging todmesg.Usually an AppArmor line looks like the following:

[ 5442.864673] audit: type=1400 audit(1453830992.845:37): apparmor= "ALLOWED" operation= "open" profile= "/usr/bin/docker" name= "/home/jessie/docker/man/man1/docker-attach.1" pid=10923 comm= "docker" requested_mask= "r" denied_mask= "r" fsuid=1000 ouid=0

In the above example, you can seeprofile=/usr/bin/docker.This means the user has thedocker-engine(Docker Engine daemon) profile loaded.

Look at another log line:

[ 3256.689120] type=1400 audit(1405454041.341:73): apparmor= "DENIED" operation= "ptrace" profile= "docker-default" pid=17651 comm= "docker" requested_mask= "receive" denied_mask= "receive"

This time the profile isdocker-default,which is run on containers by default unless inprivilegedmode. This line shows that apparmor has denied ptracein the container. This is exactly as expected.

Use aa-status

If you need to check which profiles are loaded, you can useaa-status.The output looks like:

$sudo aa-status
apparmor module is loaded.
14 profiles are loaded.
1 profiles are in enforce mode.
docker-default
13 profiles are in complain mode.
/usr/bin/docker
/usr/bin/docker///bin/cat
/usr/bin/docker///bin/ps
/usr/bin/docker///sbin/apparmor_parser
/usr/bin/docker///sbin/auplink
/usr/bin/docker///sbin/blkid
/usr/bin/docker///sbin/iptables
/usr/bin/docker///sbin/mke2fs
/usr/bin/docker///sbin/modprobe
/usr/bin/docker///sbin/tune2fs
/usr/bin/docker///sbin/xtables-multi
/usr/bin/docker///sbin/zfs
/usr/bin/docker///usr/bin/xz
38 processes have profiles defined.
37 processes are in enforce mode.
docker-default (6044)
...
docker-default (31899)
1 processes are in complain mode.
/usr/bin/docker (29756)
0 processes are unconfined but have a profile defined.

The above output shows that thedocker-defaultprofile running on various container PIDs is inenforcemode. This means AppArmor is actively blocking and auditing indmesganything outside the bounds of thedocker-default profile.

The output above also shows the/usr/bin/docker(Docker Engine daemon) profile is running incomplainmode. This means AppArmor only logs todmesg activity outside the bounds of the profile. (Except in the case of Ubuntu Trusty, where some interesting behaviors are enforced.)

Contribute to Docker's AppArmor code

Advanced users and package managers can find a profile for/usr/bin/docker (Docker Engine daemon) underneath contrib/apparmor in the Docker Engine source repository.

Thedocker-defaultprofile for containers lives in profiles/apparmor.