Getting Started
Measuring Docker Security
InSpec
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(https://supermarket.chef.io)
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
Demo
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 https://github.com/docker/docker-bench-security.git
once cloned, move inside cloned directory.
To get help about how to use docker bench security.
$ ./docker-bench-security.sh -h
Execute docker-bench-security.sh file to run an audit againts your docker platform
$ ./docker-bench-security.sh
Test are categorized
Configuration Optmization
Choosing Host Operation System
Various variations available are :
- Container Linux - CoreOS
- Atomic Host - ProjectAtomic
- RancherOS - Racher
- Ubuntu Core - Ubuntu
- LinuxKit - Linuxkit
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
Once the audit framework is properly installed in linux, then the actual auditing take place in linux kernal.
When the user process makes a call to kernel's system call, an audit event is generated according to the audit policy defined.
Auditd is the user space system process that writes audit event to the audit log.
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.
What and how to perform an action by auditd is governed by audit.conf .
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.
ausearch : It gives us the ability to search about an audit event in more detail.
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.
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.
Binaries
- /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
/usr/bin/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
Output:
-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
Output
-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 ./docker-bench-security.sh -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.
--userns-remap=default
or
--userns-remap=uid:gid|user:group|user|uid
If we want our run our container without mapping then we can set --userns=host
--userns=host
Note:
- 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.
Demo
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.
{
"userns-remap":"default"
}
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.