Getting Started

Measuring Docker Security


It is an open source auditing and testing framework, created by Chef. It is Ruby-based domain specific Langauge, for defining the controls for testing infrastructure.

It has profiles to define test and controls used for testing and auditing.

Community provided profiles are available at ChefSupermarket(

To audit Docker platform, an InSpec profile is available which has CIS Docker Benchmark.

Link: InSpec

Docker Bench for Security

  • Its also a script based auditing system.
  • Open Source implementation
  • It is maintained by Docker
  • Conforms to the CIS Docker CE Benchmark
  • Also available as a container

Link: CIS

Auditing a Docker Platform against CIS


Lets start by auditing a docker platform against CIS Docker Benchmark using Docker Bench for Security.

It does not matter which flavour operating system we are using as the Bench mark is platform agnosting means equally well on most linux distributions. Lets check our flavour of OS.

$ lsb_release -a

So, in order to run Docker Bench for security we first need to clone it from Docker's Git hub repository.

It is located at :

$ git clone

once cloned, move inside cloned directory.

To get help about how to use docker bench security.

$ ./ -h

help bench

Execute file to run an audit againts your docker platform

$ ./

Test are categorized

Configuration Optmization

Choosing Host Operation System

Various variations available are :

Auditing Docker Artifacts

Getting back to your "docker benchmark logs" under Host Configuration.

Here, we see the highlighted [WARN] section having message

[WARN] 1.2.3  - Ensure auditing is configured for the Docker daemon
[WARN] 1.2.4  - Ensure auditing is configured for Docker files and directories - /var/lib/docker
[WARN] 1.2.5  - Ensure auditing is configured for Docker files and directories - /etc/docker
[WARN] 1.2.6  - Ensure auditing is configured for Docker files and directories - docker.service
[WARN] 1.2.7  - Ensure auditing is configured for Docker files and directories - docker.socket
[WARN] 1.2.8  - Ensure auditing is configured for Docker files and directories - /etc/default/docker

lets fix this [WARN] to [PASS] but first lets understand the Audit Framework which is used to do the auditing that is "The Linux Audit Framework"

  • It simply monitors and logs events that occur on host system to an auditing log.
  • It does not prevent attacks.
  • It can be used to identify potential security weakness.

Architechture of Audit Framework

Audit Framework

Once the audit framework is properly installed in linux, then the actual auditing take place in linux kernal.

  1. When the user process makes a call to kernel's system call, an audit event is generated according to the audit policy defined.

  2. Auditd is the user space system process that writes audit event to the audit log.

  3. In order to view the events logged to disk that is audit.log. Audit framework has two utitlities to query the audit log that is aureport and ausearch.

  4. What and how to perform an action by auditd is governed by audit.conf .

  5. aureport : It enables us to view the summary report of all the event stored in audit log. It can provide us report for specific time period or for a specific area of concern for example authentication attempts.

  6. ausearch : It gives us the ability to search about an audit event in more detail.

  7. At the end, auditctl is the user space method for controlling the audit framework. It can be used to alter the configuration of system itself as well as you can apply audit rules when you need.

  8. Audit.rules file define what is monitored and under what conditions. The audit daemon reads this file on startup.

Now we know what is Linux Audit Framework and hence its time we understand what it is for ? On the basis of CIS Docker CE benchmark, we need following things to audit.


- /usr/bin/docker 
- /usr/bin/docker-conatinerd
- /usr/bin/docker-runc

Config Files

- /etc/default/docker
- /etc/docker/daemon.json

Systemd Unit Files

- docker.service
- docker.socket

Execution Root

- /var/lib/docker

TLS Artifacts

- /etc/docker

Lets install audit framework and do the audit in order to pass the result obtained by docker bench security.

To check already running any auditd process in host system

$ pidof auditd

lets check if audit framework is already installed in our system.

$ command -v auditd

if not installed then, its time we install it.

$ sudo apt update
$ sudo apt install auditd

To verify installation we can check for running daemon of auditd by pidof.

$ pidof auditd

Before doing anything, check aureport for events or summary of all events stored inside audit.log

$ sudo aureport

Now lets add an audit rule and start with Binaries that is docker.

I hope till now you know how to find locate of particular command.

$ command -v docker

We will make use of audit's CTL utitlity to add the rule.

$ sudo auditctl -w /usr/bin/docker -k docker

-w : Insert watch at
-k : set filter key on audit rule

Using auditctl again to list rules using -l

$ sudo auditctl -l

-l : List rules


-w /usr/bin/docker -p rwxa -k docker

Now dockerd is on our watch list by our audit framework any event generated at dockerd will be logged under audit event. Lets generate ab audit event.

$ docker -v

we can check audit report summary by aureport.

$ sudo aureport -k

We see a number of key based reports, ignoring all the above and focus on last highlighted key report. You may also filter out by using below commmand.

$ sudo aureport -k --start 09/21/19 21:32:09

In order to check detailed information over this event with id 304

$ sudo ausearch --event 304

It will give you verbose information having type, time and many other things

But to smplify and get it as human readable we can pipe the verbose detail given by ausearch to aureport.

$ sudo ausearch --event 304 | sudo aureport -f -i

Also if you go back to Docker benchmark logs. You will see we have changed status to passed of docker daemon that is from [WARN] to [PASS].

We can follow same step to change other audit bencharks to pass.

we may write an array to add all the rules in one go

$ files=("/var/lib/docker" "/etc/docker" "/lib/systemd/system/docker.service" "/lib/systemd/system/docker.socket" "/etc/default/docker" "/etc/docker/daemon.json" "/usr/bin/containerd" "/usr/sbin/runc")
$ echo "${files[*]}"
/var/lib/docker /etc/docker /lib/systemd/system/docker.service /lib/systemd/system/docker.socket /etc/default/docker /etc/docker/daemon.json /usr/bin/containerd /usr/sbin/runc
$ for i in "${files[@]}"; do sudo auditctl -w $i -k docker; done

now check new watch rules by

$ sudo auditctl -l


-w /usr/bin/docker -p rwxa -k docker
-w /var/lib/docker -p rwxa -k docker
-w /etc/docker -p rwxa -k docker
-w /lib/systemd/system/docker.service -p rwxa -k docker
-w /lib/systemd/system/docker.socket -p rwxa -k docker
-w /etc/default/docker -p rwxa -k docker
-w /etc/docker/daemon.json -p rwxa -k docker
-w /usr/bin/containerd -p rwxa -k docker
-w /usr/sbin/runc -p rwxa -k docker

when you check CIS Docker benchmark again for Host Configuration.

The audit rules which we just created above are not permanent, if the system restarts or the audit daemon then, all the created rules will be lost. So we need to save the audit rules in /etc/audit/audit.rules

Note: Audit.rules is an empty file waiting for rules to be added.

You may mannually save all the rules or use below command to save it in one go.

$ sudo sh -c "auditctl -l >> /etc/audit/audit.rules"

lets see out audit.rules files

$ sudo cat /etc/audit/audit.rules

Lets finally check our docker bench for security for our host configuration.

$ sudo ./ -c host_configuration

Here, configuration 1.2.1 is still not [PASS] because I am using an aws instance so it is little messy to create partition and also out of scope of my talk.

Minimizing Risk of Docker Daemon Config

By using User Namespaces

We also have one important benchmark check under Docker Daemon Configuration that is

[WARN] 2.8 - Enable user namespace support
  • User Namespaces allow to remap the set of user and group IDs from one namespace to another.
  • By using namepspace, an unprivileged user can run a workload as a privileged user.

Subordinate User and Group IDs

For detailed explanation, follow Subordinate

To know about Namespaces in linux, follow Namespaces

Remember when docker creates a container, the default behaviour is that the container process is not created in a new user namespace. To do so we need to set explicitly.

In order to set user/group for re-mapping. We have two approaches for the same

If we want Docker to handle this then we need to set userns-remap to default.




If we want our run our container without mapping then we can set --userns=host



  • Some linux distribution automatically create subordinate mapping for users of the hosts, but some do not.
  • Content already pulled for a particular image can't be used again if user namespace is enabled.


Lets see how exactly user namespaces works.

We start by running a simple container say alpine in our example

docker container run -it --name owasp alpine sh

You will see that docker client pulls a new image from docker hub and stores it locally. It will give you a shell. Once you are in lets check all the process by ps

$ ps

Here, we have single user root which is default user with process ID 1.

Now detach the container using ctrl P+Q and return to host operating system to check subordinate userIDs.

$ docker container top owasp -eo user,pid,comm

Here, it looks like both are different process but these are same. The root user with PID 2590 is same as root inside the alpine container with PID 1. Hence even our sh shell is running inside the container its owner root is same.

If somehow, anyone is able to escape through the contained environment that is alpine. He/She can conduct serious damage to host system as root system has highest privileges.

In order to solve this, we need to implement User Namespaces feature. But before that have a look at our subordinate user IDs.

$ cat /etc/subuid

We have another container platform lxd , local users and root users listed in subuid file.

Before doing any configuration, stop the running alpine container.

$ docker container rm -f owasp

Now open file /etc/docker/daemon.json for implementing Default user namespaces as I mentioned that earlier as two approaches for setting user and Group mapping.

$ sudo nano /etc/docker/daemon.json

This file may or may not be present on host system depending upon the kind of Linux OS been used.

Once opened, add following line of code in it.


Restart docker to save changes

$ sudo systemctl restart docker.service

It will create a new subordinate user dockremap.

$ cat /etc/subuid

Hence, our configuration for user namespace completed.

Now lets run again, alpine container and see the difference.

$ docker container run -it --name newowasp alpine sh

while the image is locally present, it still pulls the image from dockerhub, because once User namespace is enabled. it can not use the image of root other user.

Also when you detach from container and check the userID and PID

$ docker container top newowasp -eo uid,pid,comm

Now the process has privileged owner inside the container but non-privileged user in host default user namespace.

Also we can see the subdirectory created for mapping

$ sudo ls -l /var/lib/docker

This is where content of alpine is resides. Now if somehow even if the someone escapes the container but it will still have lowest privileges.

results matching ""

    No results matching ""