From 480fdf27a3e49915d8454efe4e524ad3d76ae2ec Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Mon, 2 May 2016 16:23:50 +0300
Subject: [PATCH 01/30] migrating shell scripts to bats testing framework

---
 helper_lib.sh                   |   3 +
 tests/1_host_configuration.bats | 111 ++++++++++++++++++++++++++++++++
 2 files changed, 114 insertions(+)
 create mode 100644 tests/1_host_configuration.bats

diff --git a/helper_lib.sh b/helper_lib.sh
index 74c8849..2b6f1b0 100644
--- a/helper_lib.sh
+++ b/helper_lib.sh
@@ -1,5 +1,8 @@
 #!/bin/sh
 
+# echo to stderr
+echoerr() { echo "$@" 1>&2; }
+
 # Returns the absolute path of a given string
 abspath () { case "$1" in /*)printf "%s\n" "$1";; *)printf "%s\n" "$PWD/$1";; esac; }
 
diff --git a/tests/1_host_configuration.bats b/tests/1_host_configuration.bats
new file mode 100644
index 0000000..2944f5f
--- /dev/null
+++ b/tests/1_host_configuration.bats
@@ -0,0 +1,111 @@
+#!/usr/bin/env bats
+
+setup() {
+  . "$BATS_TEST_DIRNAME/../helper_lib.sh"
+}
+
+# 1.1
+@test "1.1  - Create a separate partition for containers" {
+  grep /var/lib/docker /etc/fstab
+  [ $status -eq 0 ]
+}
+
+# 1.2
+@test "1.2  - Use an updated Linux Kernel" {
+  kernel_version=$(uname -r | cut -d "-" -f 1)
+  run do_version_check 3.10 "$kernel_version"
+  [ $status -eq 9 ] || [ $status -eq 10 ]
+}
+
+# 1.4
+@test "1.4  - Remove all non-essential services from the host - Network" {
+  # Check for listening network services.
+  listening_services=$(netstat -na | grep -v tcp6 | grep -v unix | grep -c LISTEN)
+  if [ "$listening_services" -eq 0 ]; then
+    echoerr "1.4  - Failed to get listening services for check: $BATS_TEST_NAME"
+  else
+    if [ "$listening_services" -gt 5 ]; then
+      echoerr "     * Host listening on: $listening_services ports"
+    fi
+  fi
+  [ "$listening_services" -ne 0 ] && [ "$listening_services" -le 5 ]
+}
+
+# 1.5
+@test "1.5  - Keep Docker up to date" {
+  docker_version=$(docker version | grep -i -A1 '^server' | grep -i 'version:' \
+    | awk '{print $NF; exit}' | tr -d '[:alpha:]-,')
+  docker_current_version="1.11.1"
+  docker_current_date="2016-04-27"
+  run do_version_check "$docker_current_version" "$docker_version"
+  if [ $status -eq 11 ]; then
+    echoerr "      * Using $docker_version, when $docker_current_version is current as of $docker_current_date"
+    echoerr "      * Your operating system vendor may provide support and security maintenance for docker"
+  else
+    pass "$check_1_5"
+    echoerr "      * Using $docker_version which is current as of $docker_current_date"
+    echoerr "      * Check with your operating system vendor for support and security maintenance for docker"
+  fi
+  [ $status -eq 9 ] || [ $status -eq 10 ]
+}
+
+# 1.6
+@test "1.6  - Only allow trusted users to control Docker daemon" {
+  docker_users=$(getent group docker)
+  echoerr "$BATS_TEST_NAME"
+  for u in $docker_users; do
+    echoerr "     * $u"
+  done
+}
+
+# 1.7
+@test "1.7  - Audit docker daemon - /usr/bin/docker" {
+  file="/usr/bin/docker"
+  run command -v auditctl
+  if [ $status -eq 0 ]; then
+    auditctl -l | grep "$file" >/dev/null 2>&1
+  else
+    echoerr "      * Failed to inspect: auditctl command not found."
+  fi
+  [ $status -eq 0 ]
+}
+
+# 1.8
+@test "1.8  - Audit Docker files and directories - /var/lib/docker" {
+  skip "TODO: need to implement"
+}
+
+# 1.9
+@test "1.9  - Audit Docker files and directories - /etc/docker" {
+  skip "TODO: need to implement"
+}
+
+# 1.10
+@test "1.10 - Audit Docker files and directories - docker.service" {
+  skip "TODO: need to implement"
+}
+
+# 1.11
+@test "1.11 - Audit Docker files and directories - docker.socket" {
+  skip "TODO: need to implement"
+}
+
+# 1.12
+@test "1.12 - Audit Docker files and directories - /etc/default/docker" {
+  skip "TODO: need to implement"
+}
+
+# 1.13
+@test "1.13 - Audit Docker files and directories - /etc/docker/daemon.json" {
+  skip "TODO: need to implement"
+}
+
+# 1.14
+@test "1.14 - Audit Docker files and directories - /usr/bin/docker-containerd" {
+  skip "TODO: need to implement"
+}
+
+# 1.15
+@test "1.15 - Audit Docker files and directories - /usr/bin/docker-runc" {
+  skip "TODO: need to implement"
+}

From 5c34a4108efe7308f108bf93dcee46d15c2da749 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Thu, 5 May 2016 16:17:32 +0300
Subject: [PATCH 02/30] Add bats-support library

---
 .gitmodules                   | 3 +++
 test/test_helper/bats-support | 1 +
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 test/test_helper/bats-support

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..c527cf1
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "test/test_helper/bats-support"]
+	path = test/test_helper/bats-support
+	url = https://github.com/ztombol/bats-support
diff --git a/test/test_helper/bats-support b/test/test_helper/bats-support
new file mode 160000
index 0000000..d0a1318
--- /dev/null
+++ b/test/test_helper/bats-support
@@ -0,0 +1 @@
+Subproject commit d0a131831c487a1f1141e76d3ab386c89642cdff

From 78fdb9a3a0b9b17e385d3e3633953418f3cb10bf Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Thu, 5 May 2016 16:18:15 +0300
Subject: [PATCH 03/30] Add bats-assert library

---
 .gitmodules                  | 3 +++
 test/test_helper/bats-assert | 1 +
 2 files changed, 4 insertions(+)
 create mode 160000 test/test_helper/bats-assert

diff --git a/.gitmodules b/.gitmodules
index c527cf1..dc270e5 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
 [submodule "test/test_helper/bats-support"]
 	path = test/test_helper/bats-support
 	url = https://github.com/ztombol/bats-support
+[submodule "test/test_helper/bats-assert"]
+	path = test/test_helper/bats-assert
+	url = https://github.com/ztombol/bats-assert
diff --git a/test/test_helper/bats-assert b/test/test_helper/bats-assert
new file mode 160000
index 0000000..9f88b42
--- /dev/null
+++ b/test/test_helper/bats-assert
@@ -0,0 +1 @@
+Subproject commit 9f88b4207da750093baabc4e3f41bf68f0dd3630

From 370e3315f1db283da1bc034dff62d740c531af90 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Thu, 5 May 2016 18:50:47 +0300
Subject: [PATCH 04/30] fighting with bats

---
 helper_lib.sh                             |  3 -
 {tests => test}/1_host_configuration.bats | 75 +++++++++++++++--------
 2 files changed, 50 insertions(+), 28 deletions(-)
 rename {tests => test}/1_host_configuration.bats (53%)

diff --git a/helper_lib.sh b/helper_lib.sh
index 2b6f1b0..74c8849 100644
--- a/helper_lib.sh
+++ b/helper_lib.sh
@@ -1,8 +1,5 @@
 #!/bin/sh
 
-# echo to stderr
-echoerr() { echo "$@" 1>&2; }
-
 # Returns the absolute path of a given string
 abspath () { case "$1" in /*)printf "%s\n" "$1";; *)printf "%s\n" "$PWD/$1";; esac; }
 
diff --git a/tests/1_host_configuration.bats b/test/1_host_configuration.bats
similarity index 53%
rename from tests/1_host_configuration.bats
rename to test/1_host_configuration.bats
index 2944f5f..05b7c94 100644
--- a/tests/1_host_configuration.bats
+++ b/test/1_host_configuration.bats
@@ -1,34 +1,31 @@
 #!/usr/bin/env bats
 
+load 'test_helper/bats-support/load'
+load 'test_helper/bats-assert/load'
+
 setup() {
   . "$BATS_TEST_DIRNAME/../helper_lib.sh"
 }
 
 # 1.1
 @test "1.1  - Create a separate partition for containers" {
-  grep /var/lib/docker /etc/fstab
-  [ $status -eq 0 ]
+  run grep /var/lib/docker /etc/fstab
+  assert_success
 }
 
 # 1.2
 @test "1.2  - Use an updated Linux Kernel" {
   kernel_version=$(uname -r | cut -d "-" -f 1)
   run do_version_check 3.10 "$kernel_version"
-  [ $status -eq 9 ] || [ $status -eq 10 ]
+  assert [ $status -eq 9 -o $status -eq 10 ]
 }
 
 # 1.4
 @test "1.4  - Remove all non-essential services from the host - Network" {
   # Check for listening network services.
   listening_services=$(netstat -na | grep -v tcp6 | grep -v unix | grep -c LISTEN)
-  if [ "$listening_services" -eq 0 ]; then
-    echoerr "1.4  - Failed to get listening services for check: $BATS_TEST_NAME"
-  else
-    if [ "$listening_services" -gt 5 ]; then
-      echoerr "     * Host listening on: $listening_services ports"
-    fi
-  fi
-  [ "$listening_services" -ne 0 ] && [ "$listening_services" -le 5 ]
+  refute [ "$listening_services" -eq 0 ] "1.4  - Failed to get listening services for check: $BATS_TEST_NAME"
+  refute [ "$listening_services" -gt 5 ] "Host listening on: $listening_services ports"
 }
 
 # 1.5
@@ -39,22 +36,26 @@ setup() {
   docker_current_date="2016-04-27"
   run do_version_check "$docker_current_version" "$docker_version"
   if [ $status -eq 11 ]; then
-    echoerr "      * Using $docker_version, when $docker_current_version is current as of $docker_current_date"
-    echoerr "      * Your operating system vendor may provide support and security maintenance for docker"
-  else
-    pass "$check_1_5"
-    echoerr "      * Using $docker_version which is current as of $docker_current_date"
-    echoerr "      * Check with your operating system vendor for support and security maintenance for docker"
+    fail "Using $docker_version, when $docker_current_version is current as of $docker_current_date. Your operating system vendor may provide support and security maintenance for docker."
   fi
-  [ $status -eq 9 ] || [ $status -eq 10 ]
+  assert [ $status -eq 9 -o $status -eq 10 ]
 }
 
 # 1.6
 @test "1.6  - Only allow trusted users to control Docker daemon" {
-  docker_users=$(getent group docker)
-  echoerr "$BATS_TEST_NAME"
-  for u in $docker_users; do
-    echoerr "     * $u"
+  declare -a trusted_users=("vagrant" "docker" "ubuntu")
+  users_string=$(awk -F':' '/^docker/{print $4}' /etc/group)
+  docker_users=(${users_string//,/ })
+  for u in ${docker_users[@]}; do
+    local found=1
+    for tu in ${trusted_users[@]}; do
+      if [ "$u" = "$tu" ]; then
+        found=0
+      fi
+    done
+    if [ $found -eq 1 ]; then
+      fail "User $u is not a trusted user!"
+    fi
   done
 }
 
@@ -65,19 +66,43 @@ setup() {
   if [ $status -eq 0 ]; then
     auditctl -l | grep "$file" >/dev/null 2>&1
   else
-    echoerr "      * Failed to inspect: auditctl command not found."
+    fail "Failed to inspect: auditctl command not found."
   fi
   [ $status -eq 0 ]
 }
 
 # 1.8
 @test "1.8  - Audit Docker files and directories - /var/lib/docker" {
-  skip "TODO: need to implement"
+  directory="/var/lib/docker"
+  if [ -d "$directory" ]; then
+    run command -v auditctl >/dev/null
+    if [ $status -eq 0 ]; then
+      auditctl -l | grep $directory >/dev/null 2>&1
+    else
+      fail "1.8  - Failed to inspect: auditctl command not found."
+    fi
+    [ $status -eq 0 ]
+  else
+    fail "     * '$directory' Directory not found"
+    [ -d "$directory" ]
+  fi
 }
 
 # 1.9
 @test "1.9  - Audit Docker files and directories - /etc/docker" {
-  skip "TODO: need to implement"
+  directory="/etc/docker"
+  if [ -d "$directory" ]; then
+    run command -v auditctl >/dev/null
+    if [ $status -eq 0 ]; then
+      auditctl -l | grep $directory >/dev/null 2>&1
+    else
+      fail "1.9  - Failed to inspect: auditctl command not found."
+    fi
+    [ $status -eq 0 ]
+  else
+    fail "'$directory' Directory not found"
+    [ -d "$directory" ]
+  fi
 }
 
 # 1.10

From 09f19482e8277fbf30fb4653bcae6cd6032e2cb7 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Sat, 7 May 2016 15:56:35 +0300
Subject: [PATCH 05/30] convert second CIS section to bats

---
 test/1_host_configuration.bats          |  94 ++++++++++++----------
 test/2_docker_daemon_configuration.bats | 102 ++++++++++++++++++++++++
 2 files changed, 154 insertions(+), 42 deletions(-)
 create mode 100644 test/2_docker_daemon_configuration.bats

diff --git a/test/1_host_configuration.bats b/test/1_host_configuration.bats
index 05b7c94..dfa283d 100644
--- a/test/1_host_configuration.bats
+++ b/test/1_host_configuration.bats
@@ -1,11 +1,8 @@
 #!/usr/bin/env bats
 
-load 'test_helper/bats-support/load'
-load 'test_helper/bats-assert/load'
-
-setup() {
-  . "$BATS_TEST_DIRNAME/../helper_lib.sh"
-}
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 
 # 1.1
 @test "1.1  - Create a separate partition for containers" {
@@ -63,74 +60,87 @@ setup() {
 @test "1.7  - Audit docker daemon - /usr/bin/docker" {
   file="/usr/bin/docker"
   run command -v auditctl
-  if [ $status -eq 0 ]; then
-    auditctl -l | grep "$file" >/dev/null 2>&1
-  else
-    fail "Failed to inspect: auditctl command not found."
-  fi
-  [ $status -eq 0 ]
+  assert_success
+  run auditctl -l | grep "$file"
+  assert_success
 }
 
 # 1.8
 @test "1.8  - Audit Docker files and directories - /var/lib/docker" {
   directory="/var/lib/docker"
-  if [ -d "$directory" ]; then
-    run command -v auditctl >/dev/null
-    if [ $status -eq 0 ]; then
-      auditctl -l | grep $directory >/dev/null 2>&1
-    else
-      fail "1.8  - Failed to inspect: auditctl command not found."
-    fi
-    [ $status -eq 0 ]
-  else
-    fail "     * '$directory' Directory not found"
-    [ -d "$directory" ]
-  fi
+  refute [ -d "$directory" ] "'$directory' Directory not found"
+  run command -v auditctl >/dev/null
+  assert_success
+  run auditctl -l | grep $directory
+  assert_success
 }
 
 # 1.9
 @test "1.9  - Audit Docker files and directories - /etc/docker" {
   directory="/etc/docker"
-  if [ -d "$directory" ]; then
-    run command -v auditctl >/dev/null
-    if [ $status -eq 0 ]; then
-      auditctl -l | grep $directory >/dev/null 2>&1
-    else
-      fail "1.9  - Failed to inspect: auditctl command not found."
-    fi
-    [ $status -eq 0 ]
-  else
-    fail "'$directory' Directory not found"
-    [ -d "$directory" ]
-  fi
+  refute [ -d "$directory" ] "'$directory' Directory not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep $directory
+  assert_success
 }
 
 # 1.10
 @test "1.10 - Audit Docker files and directories - docker.service" {
-  skip "TODO: need to implement"
+  file="$(get_systemd_service_file docker.service)"
+  refute [ -f "$file" ] "'docker.service' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep "$file"
+  assert_success
 }
 
 # 1.11
 @test "1.11 - Audit Docker files and directories - docker.socket" {
-  skip "TODO: need to implement"
+  file="$(get_systemd_service_file docker.socket)"
+  refute [ -e "$file" ] "'docker.socket' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep "$file"
+  assert_success
 }
 
 # 1.12
 @test "1.12 - Audit Docker files and directories - /etc/default/docker" {
-  skip "TODO: need to implement"
+  file="/etc/default/docker"
+  refute [ -f "$file" ] "'$file' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep $file
+  assert_success
 }
 
 # 1.13
 @test "1.13 - Audit Docker files and directories - /etc/docker/daemon.json" {
-  skip "TODO: need to implement"
+  file="/etc/docker/daemon.json"
+  refute [ -f "$file" ] "'$file' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep $file
+  assert_success
 }
 
 # 1.14
 @test "1.14 - Audit Docker files and directories - /usr/bin/docker-containerd" {
-  skip "TODO: need to implement"
+  file="/usr/bin/docker-containerd"
+  refute [ -f "$file" ] "'$file' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep $file
+  assert_success
 }
 
 # 1.15
 @test "1.15 - Audit Docker files and directories - /usr/bin/docker-runc" {
-  skip "TODO: need to implement"
+  file="/usr/bin/docker-runc"
+  refute [ -f "$file" ] "'$file' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep $file
+  assert_success
 }
diff --git a/test/2_docker_daemon_configuration.bats b/test/2_docker_daemon_configuration.bats
new file mode 100644
index 0000000..776c500
--- /dev/null
+++ b/test/2_docker_daemon_configuration.bats
@@ -0,0 +1,102 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
+
+# 2.1
+@test "2.1  - Restrict network traffic between containers" {
+  result=$(get_docker_effective_command_line_args '--icc')
+  run grep "false" <<< "$result"
+  assert_success
+}
+
+# 2.2
+@test "2.2  - Set the logging level" {
+  result=$(get_docker_effective_command_line_args '-l')
+  run grep 'debug' <<< "$result"
+  assert_failure
+}
+
+# 2.3
+@test "2.3  - Allow Docker to make changes to iptables" {
+  result=$(get_docker_effective_command_line_args '--iptables')
+  run grep "false" <<< "$result"
+  assert_failure
+}
+
+# 2.4
+@test "2.4  - Do not use insecure registries" {
+  result=$(get_docker_effective_command_line_args '--insecure-registry')
+  run grep "insecure-registry" <<< "$result"
+  assert_failure
+}
+
+# 2.5
+@test "2.5  - Do not use the aufs storage driver" {
+  result=$(docker info 2>/dev/null)
+  run grep -e "^Storage Driver:\s*aufs\s*$" <<< "$result"
+  assert_failure
+}
+
+# 2.6
+@test "2.6  - Configure TLS authentication for Docker daemon" {
+  result=$(get_docker_cumulative_command_line_args '-H')
+  run grep -vE '(unix|fd)://' <<< "$result"
+  if [ $status -eq 0 ]; then
+    result=$(get_command_line_args docker)
+    run $(grep "tlsverify" <<< "$result" | grep "tlskey")
+    assert_success
+  fi
+}
+
+# 2.7
+@test "2.7 - Set default ulimit as appropriate" {
+  result=$(get_docker_effective_command_line_args '--default-ulimit')
+  run grep "default-ulimit" <<< "$result"
+  assert_success
+}
+
+# 2.8
+@test "2.8  - Enable user namespace support" {
+  result=$(get_docker_effective_command_line_args '--userns-remap')
+  run grep "userns-remap" <<< "$result"
+  assert_success
+}
+
+# 2.9
+@test "2.9  - Confirm default cgroup usage" {
+  result=$(get_docker_effective_command_line_args '--cgroup-parent')
+  run grep "cgroup-parent" <<< "$result"
+  if [ $status -eq 0 ]; then
+    refute_output_contains "docker"
+  fi
+}
+
+# 2.10
+@test "2.10 - Do not change base device size until needed" {
+  result=$(get_docker_effective_command_line_args '--storage-opt')
+  run grep "dm.basesize" <<< "$result"
+  assert_failure
+}
+
+# 2.11
+@test "2.11 - Use authorization plugin" {
+  result=$(get_docker_effective_command_line_args '--authorization-plugin')
+  run grep "authorization-plugin" <<< "$result"
+  assert_success
+}
+
+# 2.12
+@test "2.12 - Configure centralized and remote logging" {
+  result=$(get_docker_effective_command_line_args '--log-driver')
+  run grep "log-driver" <<< "$result"
+  assert_success
+}
+
+# 2.13
+@test "2.13 - Disable operations on legacy registry (v1)" {
+  result=$(get_docker_effective_command_line_args '--disable-legacy-registry')
+  run grep "disable-legacy-registry" <<< "$result"
+  assert_success
+}

From a91d99164f915cee0ff10041214c47cf07cdd2ec Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Fri, 13 May 2016 18:20:50 +0300
Subject: [PATCH 06/30] Implemented tests for CIS 3.x section

---
 README.md                                     |   2 +-
 bats.Dockerfile                               |  34 +++
 test/1_host_configuration.bats                |  25 +-
 test/2_docker_daemon_configuration.bats       |   2 +-
 test/3_docker_daemon_configuration_files.bats | 223 ++++++++++++++++++
 5 files changed, 274 insertions(+), 12 deletions(-)
 create mode 100644 bats.Dockerfile
 create mode 100644 test/3_docker_daemon_configuration_files.bats

diff --git a/README.md b/README.md
index 8689545..38ffd23 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ docker run -it --net host --pid host --cap-add audit_control \
 
 Docker bench requires Docker 1.6.2 or later in order to run, since it depends on the `--label` to exclude the current container from being inspected. If you can't upgrade to 1.6.2, feel free to remove the `--label` flag or run the shell script locally (see below).
 
-Additionally, there was a bug in Docker 1.6.0 that would not allow mounting `-v /dev:/dev`. If you are getting an error while accessing `resolv.conf`, please update your docker to 1.6.2.  
+Additionally, there was a bug in Docker 1.6.0 that would not allow mounting `-v /dev:/dev`. If you are getting an error while accessing `resolv.conf`, please update your docker to 1.6.2.
 Also note that the default image and `Dockerfile` uses `FROM: alpine` which doesn't contain `auditctl`, this will generate errors in section 1.8 to 1.18. Distribution specific Dockerfiles that fixes this issue are available in the [distros directory](https://github.com/docker/docker-bench-security/tree/master/distros).
 
 ## Building Docker Bench for Security
diff --git a/bats.Dockerfile b/bats.Dockerfile
new file mode 100644
index 0000000..376746a
--- /dev/null
+++ b/bats.Dockerfile
@@ -0,0 +1,34 @@
+# REPOSITORY https://github.com/docker/docker-bench-securit
+FROM alpine:3.3
+
+MAINTAINER dockerbench.com
+MAINTAINER Alexei Ledenev <alexei.led@gmail.com>
+
+ENV VERSION 1.10.0
+ENV BATS_VERSION 0.4.0
+
+WORKDIR /usr/bin
+
+RUN apk update && \
+    apk upgrade && \
+    apk --update add curl bash && \
+    curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION > docker-$VERSION && \
+    curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.sha256 > docker-$VERSION.sha256 && \
+    sha256sum -c docker-$VERSION.sha256 && \
+    ln -s docker-$VERSION docker && \
+    chmod u+x docker-$VERSION && \
+    rm -rf /var/cache/apk/*
+
+RUN curl -o "/tmp/v${BATS_VERSION}.tar.gz" -L \
+		"https://github.com/sstephenson/bats/archive/v${BATS_VERSION}.tar.gz" \
+	&& tar -x -z -f "/tmp/v${BATS_VERSION}.tar.gz" -C /tmp/ \
+	&& bash "/tmp/bats-${BATS_VERSION}/install.sh" /usr/local \
+	&& rm -rf /tmp/*
+
+RUN mkdir /docker-bench-security
+
+COPY . /docker-bench-security
+
+WORKDIR /docker-bench-security
+
+ENTRYPOINT ["/usr/local/bin/bats", "/docker-bench-security/test"]
diff --git a/test/1_host_configuration.bats b/test/1_host_configuration.bats
index dfa283d..62ad15b 100644
--- a/test/1_host_configuration.bats
+++ b/test/1_host_configuration.bats
@@ -21,8 +21,13 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 @test "1.4  - Remove all non-essential services from the host - Network" {
   # Check for listening network services.
   listening_services=$(netstat -na | grep -v tcp6 | grep -v unix | grep -c LISTEN)
-  refute [ "$listening_services" -eq 0 ] "1.4  - Failed to get listening services for check: $BATS_TEST_NAME"
-  refute [ "$listening_services" -gt 5 ] "Host listening on: $listening_services ports"
+  if [ "$listening_services" -eq 0 ]; then
+    fail "Failed to get listening services for check: $BATS_TEST_NAME"
+  else
+    if [ "$listening_services" -gt 5 ]; then
+      fail "Host listening on: $listening_services ports"
+    fi
+  fi
 }
 
 # 1.5
@@ -68,7 +73,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.8
 @test "1.8  - Audit Docker files and directories - /var/lib/docker" {
   directory="/var/lib/docker"
-  refute [ -d "$directory" ] "'$directory' Directory not found"
+  assert [ -d "$directory" ]
   run command -v auditctl >/dev/null
   assert_success
   run auditctl -l | grep $directory
@@ -78,7 +83,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.9
 @test "1.9  - Audit Docker files and directories - /etc/docker" {
   directory="/etc/docker"
-  refute [ -d "$directory" ] "'$directory' Directory not found"
+  assert [ -d "$directory" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep $directory
@@ -88,7 +93,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.10
 @test "1.10 - Audit Docker files and directories - docker.service" {
   file="$(get_systemd_service_file docker.service)"
-  refute [ -f "$file" ] "'docker.service' file not found"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep "$file"
@@ -98,7 +103,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.11
 @test "1.11 - Audit Docker files and directories - docker.socket" {
   file="$(get_systemd_service_file docker.socket)"
-  refute [ -e "$file" ] "'docker.socket' file not found"
+  assert [ -e "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep "$file"
@@ -108,7 +113,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.12
 @test "1.12 - Audit Docker files and directories - /etc/default/docker" {
   file="/etc/default/docker"
-  refute [ -f "$file" ] "'$file' file not found"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep $file
@@ -118,7 +123,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.13
 @test "1.13 - Audit Docker files and directories - /etc/docker/daemon.json" {
   file="/etc/docker/daemon.json"
-  refute [ -f "$file" ] "'$file' file not found"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep $file
@@ -128,7 +133,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.14
 @test "1.14 - Audit Docker files and directories - /usr/bin/docker-containerd" {
   file="/usr/bin/docker-containerd"
-  refute [ -f "$file" ] "'$file' file not found"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep $file
@@ -138,7 +143,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.15
 @test "1.15 - Audit Docker files and directories - /usr/bin/docker-runc" {
   file="/usr/bin/docker-runc"
-  refute [ -f "$file" ] "'$file' file not found"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep $file
diff --git a/test/2_docker_daemon_configuration.bats b/test/2_docker_daemon_configuration.bats
index 776c500..de46c62 100644
--- a/test/2_docker_daemon_configuration.bats
+++ b/test/2_docker_daemon_configuration.bats
@@ -69,7 +69,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
   result=$(get_docker_effective_command_line_args '--cgroup-parent')
   run grep "cgroup-parent" <<< "$result"
   if [ $status -eq 0 ]; then
-    refute_output_contains "docker"
+    assert_output_contains "docker"
   fi
 }
 
diff --git a/test/3_docker_daemon_configuration_files.bats b/test/3_docker_daemon_configuration_files.bats
new file mode 100644
index 0000000..de99861
--- /dev/null
+++ b/test/3_docker_daemon_configuration_files.bats
@@ -0,0 +1,223 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
+
+# 3.1
+@test "3.1  - Verify that docker.service file ownership is set to root:root" {
+  file="$(get_systemd_service_file docker.service)"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %u%g $file)" -ne 00 ]; then
+      fail "Wrong ownership for $file"
+    fi
+  fi
+}
+
+# 3.2
+@test "3.2  - Verify that docker.service file permissions are set to 644" {
+  file="$(get_systemd_service_file docker.service)"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %a $file)" -ne 644 ]; then
+      fail "Wrong permissions for $file"
+    fi
+  fi
+}
+
+# 3.3
+@test "3.3  - Verify that docker.socket file ownership is set to root:root" {
+  file="$(get_systemd_service_file docker.socket)"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %u%g $file)" -ne 00 ]; then
+      fail "Wrong ownership for $file"
+    fi
+  fi
+}
+
+# 3.4
+@test "3.4  - Verify that docker.socket file permissions are set to 644" {
+  file="$(get_systemd_service_file docker.socket)"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %a $file)" -ne 644 ]; then
+      fail "Wrong permissions for $file"
+    fi
+  fi
+}
+
+# 3.5
+@test "3.5  - Verify that /etc/docker directory ownership is set to root:root" {
+  directory="/etc/docker"
+  if [ -d "$directory" ]; then
+    if [ "$(stat -c %u%g $directory)" -ne 00 ]; then
+      fail "Wrong ownership for $directory"
+    fi
+  fi
+}
+
+# 3.6
+@test "3.6  - Verify that /etc/docker directory permissions are set to 755 or 700" {
+  directory="/etc/docker"
+  if [ -d "$directory" ]; then
+    if [ "$(stat -c %a $directory)" -ne 755 -a "$(stat -c %a $directory)" -ne 700 ]; then
+      fail "Wrong permissions for $directory : $(stat -c %a $directory)"
+    fi
+  fi
+}
+
+# 3.7
+@test "3.7  - Verify that registry certificate file ownership is set to root:root" {
+  directory="/etc/docker/certs.d/"
+  if [ -d "$directory" ]; then
+    fail=0
+    owners=$(ls -lL $directory | grep ".crt" | awk '{print $3, $4}')
+    for p in $owners; do
+      printf "%s" "$p" | grep "root" >/dev/null 2>&1
+      if [ $? -ne 0 ]; then
+        fail=1
+      fi
+    done
+    if [ $fail -eq 1 ]; then
+      fail "Wrong ownership for $directory"
+    fi
+  fi
+}
+
+# 3.8
+@test "3.8  - Verify that registry certificate file permissions are set to 444" {
+  directory="/etc/docker/certs.d/"
+  if [ -d "$directory" ]; then
+    fail=0
+    perms=$(ls -lL $directory | grep ".crt" | awk '{print $1}')
+    for p in $perms; do
+      if [ "$p" != "-r--r--r--." -a "$p" = "-r--------." ]; then
+        fail=1
+      fi
+    done
+    if [ $fail -eq 1 ]; then
+      fail "Wrong permissions for $directory"
+    fi
+  fi
+}
+
+# 3.9
+@test "3.9  - Verify that TLS CA certificate file ownership is set to root:root" {
+  tlscacert=$(get_docker_effective_command_line_args '--tlscacert' | sed -n 's/.*tlscacert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlscacert" ]; then
+    if [ "$(stat -c %u%g "$tlscacert")" -ne 00 ]; then
+      fail "Wrong ownership for $tlscacert : $(stat -c %u%g "$tlscacert")"
+    fi
+  fi
+}
+
+# 3.10
+@test "3.10 - Verify that TLS CA certificate file permissions are set to 444" {
+  tlscacert=$(get_docker_effective_command_line_args '--tlscacert' | sed -n 's/.*tlscacert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlscacert" ]; then
+    perms=$(ls -ld "$tlscacert" | awk '{print $1}')
+    if [ "$perms" != "-r--r--r--" ]; then
+      fail "Wrong permissions for $tlscacert"
+    fi
+  fi
+}
+
+# 3.11
+@test "3.11 - Verify that Docker server certificate file ownership is set to root:root" {
+  tlscert=$(get_docker_effective_command_line_args '--tlscert' | sed -n 's/.*tlscert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlscert" ]; then
+    if [ "$(stat -c %u%g "$tlscert")" -ne 00 ]; then
+      fail "Wrong ownership for $tlscert : $(stat -c %u%g "$tlscert")"
+    fi
+  fi
+}
+
+# 3.12
+@test "3.12 - Verify that Docker server certificate file permissions are set to 444" {
+  tlscert=$(get_docker_effective_command_line_args '--tlscert' | sed -n 's/.*tlscert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlscert" ]; then
+    perms=$(ls -ld "$tlscert" | awk '{print $1}')
+    if [ "$perms" != "-r--r--r--" ]; then
+      fail "Wrong permissions for $tlscert"
+    fi
+  fi
+}
+
+# 3.13
+@test "3.13 - Verify that Docker server key file ownership is set to root:root" {
+  tlskey=$(get_docker_effective_command_line_args '--tlskey' | sed -n 's/.*tlskey=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlskey" ]; then
+    if [ "$(stat -c %u%g "$tlskey")" -ne 00 ]; then
+      fail "Wrong ownership for $tlskey : $(stat -c %u%g "$tlskey")"
+    fi
+  fi
+}
+
+# 3.14
+@test "3.14 - Verify that Docker server key file permissions are set to 400" {
+  tlskey=$(get_docker_effective_command_line_args '--tlskey' | sed -n 's/.*tlskey=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlskey" ]; then
+    perms=$(ls -ld "$tlskey" | awk '{print $1}')
+    if [ "$perms" != "-r--------" ]; then
+      fail "Wrong permissions for $tlskey"
+    fi
+  fi
+}
+
+# 3.15
+@test "3.15 - Verify that Docker socket file ownership is set to root:docker" {
+  file="/var/run/docker.sock"
+  if [ -S "$file" ]; then
+    if [ "$(stat -c %U:%G $file)" != 'root:docker' ]; then
+      fail "Wrong ownership for $file"
+    fi
+  fi
+}
+
+# 3.16
+@test "3.16 - Verify that Docker socket file permissions are set to 660" {
+  file="/var/run/docker.sock"
+  if [ -S "$file" ]; then
+    if [ "$(stat -c %a $file)" -ne 660 ]; then
+      fail "Wrong permissions for $file"
+    fi
+  fi
+}
+
+# 3.17
+@test "3.17 - Verify that daemon.json file ownership is set to root:root" {
+  file="/etc/docker/daemon.json"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %U:%G $file)" != 'root:root' ]; then
+      fail "Wrong ownership for $file"
+    fi
+  fi
+}
+
+# 3.18
+@test "3.18 - Verify that daemon.json file permissions are set to 644" {
+  file="/etc/docker/daemon.json"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %a $file)" -ne 644 ]; then
+      fail "Wrong permissions for $file"
+    fi
+  fi
+}
+
+# 3.19
+@test "3.19 - Verify that /etc/default/docker file ownership is set to root:root" {
+  file="/etc/default/docker"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %U:%G $file)" != 'root:root' ]; then
+      fail "Wrong ownership for $file"
+    fi
+  fi
+}
+
+# 3.20
+@test "3.20 - Verify that /etc/default/docker file permissions are set to 644" {
+  file="/etc/default/docker"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %a $file)" -ne 644 ]; then
+      fail "Wrong permissions for $file"
+    fi
+  fi
+}

From 0e5cada34d61cd9cd2ac6c054e504ad32646ac4e Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Sun, 15 May 2016 18:01:08 +0300
Subject: [PATCH 07/30] run tests with run_test.sh; automatically generates
 tests per each running container, if needed

---
 bats.Dockerfile                               |  10 +-
 generate_tests.sh                             |  46 +++++++
 run_tests.sh                                  | 114 ++++++++++++++++++
 test/1_host_configuration.bats                |  66 ++++------
 ...4_1_create_user_in_container.bats.template |  18 +++
 test/4_container_images.bats                  |   9 ++
 6 files changed, 217 insertions(+), 46 deletions(-)
 create mode 100644 generate_tests.sh
 create mode 100755 run_tests.sh
 create mode 100644 test/4_1_create_user_in_container.bats.template
 create mode 100644 test/4_container_images.bats

diff --git a/bats.Dockerfile b/bats.Dockerfile
index 376746a..f681f12 100644
--- a/bats.Dockerfile
+++ b/bats.Dockerfile
@@ -7,11 +7,13 @@ MAINTAINER Alexei Ledenev <alexei.led@gmail.com>
 ENV VERSION 1.10.0
 ENV BATS_VERSION 0.4.0
 
+LABEL docker_bench_security=true
+
 WORKDIR /usr/bin
 
 RUN apk update && \
     apk upgrade && \
-    apk --update add curl bash && \
+    apk --update add curl bash ncurses ncurses-terminfo && \
     curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION > docker-$VERSION && \
     curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.sha256 > docker-$VERSION.sha256 && \
     sha256sum -c docker-$VERSION.sha256 && \
@@ -28,7 +30,11 @@ RUN curl -o "/tmp/v${BATS_VERSION}.tar.gz" -L \
 RUN mkdir /docker-bench-security
 
 COPY . /docker-bench-security
+RUN chmod +x /docker-bench-security/run_tests.sh
 
 WORKDIR /docker-bench-security
 
-ENTRYPOINT ["/usr/local/bin/bats", "/docker-bench-security/test"]
+VOLUME /var/docker-bench
+
+CMD ["-r"]
+ENTRYPOINT ["./run_tests.sh"]
diff --git a/generate_tests.sh b/generate_tests.sh
new file mode 100644
index 0000000..b13d9b2
--- /dev/null
+++ b/generate_tests.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+. ./helper_lib.sh
+
+TEST_SRC=./test
+BENCH_ROOT=/var/docker-bench
+TEST_ROOT=$BENCH_ROOT/test
+
+prepare_tests_directory()
+{
+  mkdir -p $BENCH_ROOT
+  if [ -d "$TEST_ROOT" ]; then
+    rm -rf $TEST_ROOT
+  fi
+  if [ ! -f "$BENCH_ROOT/helper_lib.sh" ]; then
+    cp helper_lib.sh $BENCH_ROOT
+  fi
+  cp -r $TEST_SRC $TEST_ROOT
+}
+
+list_running_containers() {
+    # List all running containers
+  local containers=$(docker ps | sed '1d' | awk '{print $NF}')
+  # If there is a container with label docker_bench_security, memorize it:
+  local benchcont="nil"
+  for c in $containers; do
+    labels=$(docker inspect --format '{{ .Config.Labels }}' "$c")
+    contains "$labels" "docker_bench_security" && benchcont="$c"
+  done
+  # List all running containers except docker-bench (use names to improve readability in logs)
+  docker ps -aq --format="{{.Names}}" | grep -v "$benchcont" | tr "\n" " "
+}
+
+generate_all_tests() {
+  # prepare test direcory: copy tests and templates
+  prepare_tests_directory
+  # generate tests from templates for running containers
+  local containers=$(list_running_containers)
+  ( cd $TEST_ROOT
+  for c in ${containers[@]}; do
+    for t in *.bats.template; do
+      sed -e "s/{{c}}/$c/g" "${t}" > "${t%.*.*}_${c}.bats"
+    done
+  done
+  )
+}
diff --git a/run_tests.sh b/run_tests.sh
new file mode 100755
index 0000000..42afa39
--- /dev/null
+++ b/run_tests.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+
+. ./generate_tests.sh
+
+TERMINFO=/usr/share/terminfo
+
+TEST_RESULTS=$BENCH_ROOT/results
+
+# make result folder (inside VOLUME)
+# mkdir -p $TEST_RESULTS
+# generate all tests: copy host and daemon level tests and generate container level tests for running containers
+# generate_all_tests
+# run bats with all tests or passed tests
+# bats $TEST_ROOT >
+
+#Set Script Name variable
+SCRIPT="run_tests.sh"
+
+#Initialize variables to default values.
+OPT_FORMAT="t"
+OPT_OUTPUT=$TEST_RESULTS
+OPT_RESULTS=1
+
+#Set fonts for Help.
+BOLD=`tput bold`
+REV=`tput smso`
+NORM=`tput sgr0`
+
+#Help function
+HELP() {
+  echo -e \\n"Help documentation for ${BOLD}${SCRIPT}${NORM}"\\n
+  echo -e "Basic usage: ${BOLD}$SCRIPT [-c] [-p|-t] [-o path] <test> [<test> ...]${NORM}"\\n
+  echo -e "Command line switches are optional. The following switches are recognized."
+  echo -e "${REV}-c${NORM}  --Displays number of tests. No further functions are performed."
+  echo -e "${REV}-g${NORM}  --Generates all CIS Bats tests without execution. No further functions are performed."
+  echo -e "${REV}-p${NORM}  --Show results in pretty format."
+  echo -e "${REV}-t${NORM}  --Show results in TAP format. This is the default format."
+  echo -e "${REV}-t${NORM}  --Create test results files: ${BOLD}tests_<timestamp>.tap${NORM} in test result folder."
+  echo -e "${REV}-o${NORM}  --Specify test result folder. Default to ${BOLD}$TEST_RESULTS${NORM}."
+  echo -e "${REV}-h${NORM}  --Displays this help message. No further functions are performed."\\n
+  echo -e "Example: ${BOLD}$SCRIPT -t -o $TEST_RESULTS${NORM}"\\n
+  exit 1
+}
+
+#Check the number of arguments. If none are passed, print help and exit.
+NUMARGS=$#
+if [ $NUMARGS -eq 0 ]; then
+  HELP
+fi
+
+### Start getopts code ###
+
+#Parse command line flags
+while getopts o:rptcgh FLAG; do
+  case $FLAG in
+    o)  # output test results into specified folder
+      OPT_OUTPUT=$OPTARG
+      ;;
+    p)  # output test results in TAP format
+      OPT_FORMAT="p"
+      ;;
+    t)  # output test results in pretty format
+      OPT_FORMAT="t"
+      ;;
+    r)  # save test results into file
+      OPT_RESULTS=0
+      ;;
+    c)  # count tests
+      if [ -d "$TEST_ROOT" ]; then
+        echo -e "There are ${BOLD}$(bats $TEST_ROOT -c)${NORM} tests in ${BOLD}$TEST_ROOT${NORM}"
+      else
+        echo -e "No tests found, run ${BOLD}${SCRIPT}${NORM} with ${REV}-g${NORM} option first."
+      fi
+      exit 1
+      ;;
+    g)  # genetate all Bats tests: copy tests and generate tests (per container) from templates
+      generate_all_tests
+      exit 1
+      ;;
+    h)  #show help
+      HELP
+      ;;
+    \?) #unrecognized option - show help
+      echo -e \\n"Option -${BOLD}$OPTARG${NORM} not allowed."
+      HELP
+      ;;
+  esac
+done
+
+shift $((OPTIND-1))  #This tells getopts to move on to the next argument.
+
+### End getopts code ###
+
+### Run Bats tests ###
+
+TESTS=$TEST_ROOT
+if [ ! -d $TEST_ROOT ]; then # generate tests if needed
+  generate_all_tests
+fi
+
+if [ $# -ne 0 ]; then # get tests from command line
+  TESTS=$*
+fi
+
+if [ $OPT_RESULTS -eq 0 ]; then # run tests and [create test result file]
+  if [ ! -d "$OPT_OUTPUT" ]; then
+    mkdir -p "$OPT_OUTPUT"
+  fi
+  bats $TESTS -${OPT_FORMAT} > "${OPT_OUTPUT}/tests_$(date +%s).tap"
+else
+  bats $TESTS -${OPT_FORMAT}
+fi
+
+exit 0
diff --git a/test/1_host_configuration.bats b/test/1_host_configuration.bats
index 62ad15b..97eee29 100644
--- a/test/1_host_configuration.bats
+++ b/test/1_host_configuration.bats
@@ -70,9 +70,8 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
   assert_success
 }
 
-# 1.8
-@test "1.8  - Audit Docker files and directories - /var/lib/docker" {
-  directory="/var/lib/docker"
+test_audit_directory() {
+  local directory="$1"
   assert [ -d "$directory" ]
   run command -v auditctl >/dev/null
   assert_success
@@ -80,72 +79,51 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
   assert_success
 }
 
-# 1.9
-@test "1.9  - Audit Docker files and directories - /etc/docker" {
-  directory="/etc/docker"
-  assert [ -d "$directory" ]
+test_audit_file() {
+  file="$1"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
-  run auditctl -l | grep $directory
+  run auditctl -l | grep "$file"
   assert_success
 }
 
+# 1.8
+@test "1.8  - Audit Docker files and directories - /var/lib/docker" {
+  test_audit_directory "/var/lib/docker"
+}
+
+# 1.9
+@test "1.9  - Audit Docker files and directories - /etc/docker" {
+  test_audit_directory "/etc/docker"
+}
+
 # 1.10
 @test "1.10 - Audit Docker files and directories - docker.service" {
-  file="$(get_systemd_service_file docker.service)"
-  assert [ -f "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep "$file"
-  assert_success
+  test_audit_file "$(get_systemd_service_file docker.service)"
 }
 
 # 1.11
 @test "1.11 - Audit Docker files and directories - docker.socket" {
-  file="$(get_systemd_service_file docker.socket)"
-  assert [ -e "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep "$file"
-  assert_success
+  test_audit_file "$(get_systemd_service_file docker.socket)"
 }
 
 # 1.12
 @test "1.12 - Audit Docker files and directories - /etc/default/docker" {
-  file="/etc/default/docker"
-  assert [ -f "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep $file
-  assert_success
+  test_audit_file "/etc/default/docker"
 }
 
 # 1.13
 @test "1.13 - Audit Docker files and directories - /etc/docker/daemon.json" {
-  file="/etc/docker/daemon.json"
-  assert [ -f "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep $file
-  assert_success
+  test_audit_file "/etc/docker/daemon.json"
 }
 
 # 1.14
 @test "1.14 - Audit Docker files and directories - /usr/bin/docker-containerd" {
-  file="/usr/bin/docker-containerd"
-  assert [ -f "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep $file
-  assert_success
+  test_audit_file "/usr/bin/docker-containerd"
 }
 
 # 1.15
 @test "1.15 - Audit Docker files and directories - /usr/bin/docker-runc" {
-  file="/usr/bin/docker-runc"
-  assert [ -f "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep $file
-  assert_success
+  test_audit_file "/usr/bin/docker-runc"
 }
diff --git a/test/4_1_create_user_in_container.bats.template b/test/4_1_create_user_in_container.bats.template
new file mode 100644
index 0000000..ff67ab2
--- /dev/null
+++ b/test/4_1_create_user_in_container.bats.template
@@ -0,0 +1,18 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
+
+
+# 4.1
+@test "4.1  - Create a user for the container {{c}}" {
+  local user=$(docker inspect --format 'User={{.Config.User}}' "{{c}}")
+  if [ "$user" = "User=" -o "$user" = "User=[]" -o "$user" = "User=<no value>" ]; then
+    # get PID 1 and check if it's running as root (uid=0)
+    local uid=$(docker exec {{c}} awk '/^Uid:/{print $2}' /proc/1/status)
+    if [ $uid -eq 0 ]; then
+      fail "Running as root: {{c}}"
+    fi
+  fi
+}
diff --git a/test/4_container_images.bats b/test/4_container_images.bats
new file mode 100644
index 0000000..6f2de10
--- /dev/null
+++ b/test/4_container_images.bats
@@ -0,0 +1,9 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+
+# 4.5
+@test "4.5  - Enable Content trust for Docker" {
+  assert [ "x$DOCKER_CONTENT_TRUST" = "x1" ]
+}

From 28d3a4b4564ce66b880fd6e133b9e80831c68b55 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Mon, 16 May 2016 17:44:58 +0300
Subject: [PATCH 08/30] fix/ignore non working xterm (due to /etc volume);
 download latest docker

---
 bats.Dockerfile | 30 ++++++++++++++----------------
 run_tests.sh    | 12 +++++++++---
 2 files changed, 23 insertions(+), 19 deletions(-)

diff --git a/bats.Dockerfile b/bats.Dockerfile
index f681f12..af980b9 100644
--- a/bats.Dockerfile
+++ b/bats.Dockerfile
@@ -4,28 +4,26 @@ FROM alpine:3.3
 MAINTAINER dockerbench.com
 MAINTAINER Alexei Ledenev <alexei.led@gmail.com>
 
-ENV VERSION 1.10.0
+ENV VERSION 1.11.1
 ENV BATS_VERSION 0.4.0
 
 LABEL docker_bench_security=true
 
-WORKDIR /usr/bin
+RUN apk --update add curl bash \
+    && rm -rf /var/lib/apt/lists/* \
+    && rm /var/cache/apk/*
 
-RUN apk update && \
-    apk upgrade && \
-    apk --update add curl bash ncurses ncurses-terminfo && \
-    curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION > docker-$VERSION && \
-    curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.sha256 > docker-$VERSION.sha256 && \
-    sha256sum -c docker-$VERSION.sha256 && \
-    ln -s docker-$VERSION docker && \
-    chmod u+x docker-$VERSION && \
-    rm -rf /var/cache/apk/*
+RUN curl -o "/tmp/docker-$VERSION.tgz" -LS "https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.tgz" && \
+    curl -o "/tmp/docker-$VERSION.tgz.sha256" -LS "https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.tgz.sha256" && \
+    cd /tmp && sha256sum -c docker-$VERSION.tgz.sha256 && \
+    tar -xvzf "/tmp/docker-$VERSION.tgz" -C /tmp/ && \
+    chmod u+x /tmp/docker/docker && mv /tmp/docker/docker /usr/bin/ && \
+    rm -rf /tmp/*
 
-RUN curl -o "/tmp/v${BATS_VERSION}.tar.gz" -L \
-		"https://github.com/sstephenson/bats/archive/v${BATS_VERSION}.tar.gz" \
-	&& tar -x -z -f "/tmp/v${BATS_VERSION}.tar.gz" -C /tmp/ \
-	&& bash "/tmp/bats-${BATS_VERSION}/install.sh" /usr/local \
-	&& rm -rf /tmp/*
+RUN curl -o "/tmp/v${BATS_VERSION}.tar.gz" -LS "https://github.com/sstephenson/bats/archive/v${BATS_VERSION}.tar.gz" && \
+    tar -xvzf "/tmp/v${BATS_VERSION}.tar.gz" -C /tmp/ && \
+    bash "/tmp/bats-${BATS_VERSION}/install.sh" /usr/local && \
+    rm -rf /tmp/*
 
 RUN mkdir /docker-bench-security
 
diff --git a/run_tests.sh b/run_tests.sh
index 42afa39..b1c4afd 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -22,9 +22,15 @@ OPT_OUTPUT=$TEST_RESULTS
 OPT_RESULTS=1
 
 #Set fonts for Help.
-BOLD=`tput bold`
-REV=`tput smso`
-NORM=`tput sgr0`
+if [ -e "/usr/bin/tput" ]; then
+  BOLD=`tput bold`
+  REV=`tput smso`
+  NORM=`tput sgr0`
+else
+  BOLD=""
+  REV=""
+  NORM=""
+fi
 
 #Help function
 HELP() {

From 100b33ceda65bb777abf57c8812882c40cfa944f Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Mon, 16 May 2016 18:05:06 +0300
Subject: [PATCH 09/30] update README.md file with info about running Bats
 tests

---
 README.md | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)

diff --git a/README.md b/README.md
index 38ffd23..057423c 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,57 @@ Docker bench requires Docker 1.6.2 or later in order to run, since it depends on
 Additionally, there was a bug in Docker 1.6.0 that would not allow mounting `-v /dev:/dev`. If you are getting an error while accessing `resolv.conf`, please update your docker to 1.6.2.
 Also note that the default image and `Dockerfile` uses `FROM: alpine` which doesn't contain `auditctl`, this will generate errors in section 1.8 to 1.18. Distribution specific Dockerfiles that fixes this issue are available in the [distros directory](https://github.com/docker/docker-bench-security/tree/master/distros).
 
+## Running Docker Bench Bats tests
+
+[Bats](https://github.com/sstephenson/bats) is a [TAP](http://testanything.org/)-compliant testing framework for Bash. It provides a simple way to verify that the UNIX programs you write behave as expected.
+
+All Docker Bench scipts are also available as Bats tests. Also container level (and image level) tests are automatically generated for all containers avaiable on host. It's possible to run all or only selected test(s), if you like.
+
+By default TAP test results are reported, but it's possible to produce a "pretty" printed output too.
+
+Use the following command to run Docker Bench Bats tests:
+
+```
+Help documentation for run_tests.sh
+
+Basic usage: run_tests.sh [-c] [-p|-t] [-o path] <test> [<test> ...]
+
+Command line switches are optional. The following switches are recognized.
+-c  --Displays number of tests. No further functions are performed.
+-g  --Generates all CIS Bats tests without execution. No further functions are performed.
+-p  --Show results in pretty format.
+-t  --Show results in TAP format. This is the default format.
+-t  --Create test results files: tests_<timestamp>.tap in test result folder.
+-o  --Specify test result folder. Default to /var/docker-bench/results.
+-h  --Displays this help message. No further functions are performed.
+
+Example: run_tests.sh -t -o /var/docker-bench/results
+```
+
+You need to run `run_tests.sh` on Docker host as `root` user.
+
+### Running Docker Bench Bats tests from Docker image
+
+First, clone and compile your `docker-bench-tests` Docker image.
+
+```sh
+git clone https://github.com/gaia-adm/docker-bench-security.git
+cd docker-bench-security
+docker build -t docker-bench-tests -f bats.Dockerfile .
+```
+
+Then run `docker-bench-tests` container (as bellow). Test results will be saved into `/var/docker-bench` folder in TAP format. Test results file is named accoring to the `test_<timestamp>.tap` pattern.
+
+```sh
+docker run -it --net host --pid host --cap-add audit_control \
+    -v /var/lib:/var/lib \
+    -v /var/run/docker.sock:/var/run/docker.sock \
+    -v /usr/lib/systemd:/usr/lib/systemd \
+    -v /var/docker-bench:/var/docker-bench
+    -v /etc:/etc --label docker_bench_security \
+    docker-bench-tests
+```
+
 ## Building Docker Bench for Security
 
 If you wish to build and run this container yourself, you can follow the following steps:

From 1f856a743aa51459c95039cd9b0bbd40a580877f Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Tue, 17 May 2016 13:17:03 +0300
Subject: [PATCH 10/30] converted tests for section 5 and 6

---
 .gitmodules                                   |   6 -
 .../1_host_configuration.bats                 |   0
 .../2_docker_daemon_configuration.bats        |   0
 .../3_docker_daemon_configuration_files.bats  |   0
 ...4_1_create_user_in_container.bats.template |   2 +-
 {test => bats_tests}/4_container_images.bats  |   0
 bats_tests/5_container_runtime.bats.template  | 202 ++++++++++++++++++
 bats_tests/6_docker_security_operations.bats  |  36 ++++
 generate_tests.sh                             |   4 +-
 test/test_helper/bats-assert                  |   1 -
 test/test_helper/bats-support                 |   1 -
 11 files changed, 241 insertions(+), 11 deletions(-)
 delete mode 100644 .gitmodules
 rename {test => bats_tests}/1_host_configuration.bats (100%)
 rename {test => bats_tests}/2_docker_daemon_configuration.bats (100%)
 rename {test => bats_tests}/3_docker_daemon_configuration_files.bats (100%)
 rename {test => bats_tests}/4_1_create_user_in_container.bats.template (90%)
 rename {test => bats_tests}/4_container_images.bats (100%)
 create mode 100644 bats_tests/5_container_runtime.bats.template
 create mode 100644 bats_tests/6_docker_security_operations.bats
 delete mode 160000 test/test_helper/bats-assert
 delete mode 160000 test/test_helper/bats-support

diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index dc270e5..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,6 +0,0 @@
-[submodule "test/test_helper/bats-support"]
-	path = test/test_helper/bats-support
-	url = https://github.com/ztombol/bats-support
-[submodule "test/test_helper/bats-assert"]
-	path = test/test_helper/bats-assert
-	url = https://github.com/ztombol/bats-assert
diff --git a/test/1_host_configuration.bats b/bats_tests/1_host_configuration.bats
similarity index 100%
rename from test/1_host_configuration.bats
rename to bats_tests/1_host_configuration.bats
diff --git a/test/2_docker_daemon_configuration.bats b/bats_tests/2_docker_daemon_configuration.bats
similarity index 100%
rename from test/2_docker_daemon_configuration.bats
rename to bats_tests/2_docker_daemon_configuration.bats
diff --git a/test/3_docker_daemon_configuration_files.bats b/bats_tests/3_docker_daemon_configuration_files.bats
similarity index 100%
rename from test/3_docker_daemon_configuration_files.bats
rename to bats_tests/3_docker_daemon_configuration_files.bats
diff --git a/test/4_1_create_user_in_container.bats.template b/bats_tests/4_1_create_user_in_container.bats.template
similarity index 90%
rename from test/4_1_create_user_in_container.bats.template
rename to bats_tests/4_1_create_user_in_container.bats.template
index ff67ab2..8e1fbaf 100644
--- a/test/4_1_create_user_in_container.bats.template
+++ b/bats_tests/4_1_create_user_in_container.bats.template
@@ -6,7 +6,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 
 
 # 4.1
-@test "4.1  - Create a user for the container {{c}}" {
+@test "4.1  - Create a user for the container: {{c}}" {
   local user=$(docker inspect --format 'User={{.Config.User}}' "{{c}}")
   if [ "$user" = "User=" -o "$user" = "User=[]" -o "$user" = "User=<no value>" ]; then
     # get PID 1 and check if it's running as root (uid=0)
diff --git a/test/4_container_images.bats b/bats_tests/4_container_images.bats
similarity index 100%
rename from test/4_container_images.bats
rename to bats_tests/4_container_images.bats
diff --git a/bats_tests/5_container_runtime.bats.template b/bats_tests/5_container_runtime.bats.template
new file mode 100644
index 0000000..3bbf13f
--- /dev/null
+++ b/bats_tests/5_container_runtime.bats.template
@@ -0,0 +1,202 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
+
+# 5.1
+@test "5.1  - Verify AppArmor Profile, if applicable: {{c}}" {
+  policy=$(docker inspect --format 'AppArmorProfile={{ .AppArmorProfile }}' "{{c}}")
+  if [ "$policy" = "AppArmorProfile=" -o "$policy" = "AppArmorProfile=[]" -o "$policy" = "AppArmorProfile=<no value>" ]; then
+    fail "No AppArmorProfile Found: {{c}}"
+  fi
+}
+
+# 5.2
+@test "5.2  - Verify SELinux security options, if applicable: {{c}}" {
+  policy=$(docker inspect --format 'SecurityOpt={{ .HostConfig.SecurityOpt }}' "{{c}}")
+  if [ "$policy" = "SecurityOpt=" -o "$policy" = "SecurityOpt=[]" -o "$policy" = "SecurityOpt=<no value>" ]; then
+    fail "No SecurityOptions Found: {{c}}"
+  fi
+}
+
+# 5.3
+@test "5.3  - Restrict Linux Kernel Capabilities within containers: {{c}}" {
+  caps=$(docker inspect --format 'CapAdd={{ .HostConfig.CapAdd}}' "{{c}}")
+  if [ "$caps" != 'CapAdd=' -a "$caps" != 'CapAdd=[]' -a "$caps" != 'CapAdd=<no value>' -a "$caps" != 'CapAdd=<nil>' ]; then
+    fail "Capabilities added: $caps to {{c}}"
+  fi
+}
+
+# 5.4
+@test "5.4  - Do not use privileged containers: {{c}}" {
+  privileged=$(docker inspect --format '{{ .HostConfig.Privileged }}' "{{c}}")
+  if [ "$privileged" = "true" ]; then
+    fail "Container running in Privileged mode: {{c}}"
+  fi
+}
+
+# 5.5
+@test "5.5  - Do not mount sensitive host system directories on containers: {{c}}" {
+  # List of sensitive directories to test for. Script uses new-lines as a separator.
+  # Note the lack of identation. It needs it for the substring comparison.
+  sensitive_dirs=(/boot /dev /etc /lib /proc /sys /usr)
+  run docker inspect --format '{{ .VolumesRW }}' "{{c}}"
+  if [ $status -eq 0 ]; then
+    volumes=$(docker inspect --format '{{ .VolumesRW }}' "{{c}}")
+  else
+    volumes=$(docker inspect --format '{{ .Mounts }}' "{{c}}")
+  fi
+  # Go over each directory in sensitive dir and see if they exist in the volumes
+  for v in ${sensitive_dirs[@]}; do
+    run contains "$volumes" "$v"
+    if [ $status -eq 0 ]; then
+      fail "Sensitive directory $v mounted in: {{c}}"
+    fi
+  done
+}
+
+# 5.7
+@test "5.7  - Do not map privileged ports within containers: {{c}}" {
+  # Port format is private port -> ip: public port
+  ports=$(docker port "{{c}}" | awk '{print $3}' | cut -d ':' -f2 | tr "\n" " ")
+  # iterate through port range (line delimited)
+  for port in $ports; do
+    if [ ! -z "$port" ] && [ "0$port" -lt 1024 ]; then
+      fail "Privileged Port in use: $port in {{c}}"
+    fi
+  done
+}
+
+# 5.9
+@test "5.9 - Do not share the host's network namespace: {{c}}" {
+  mode=$(docker inspect --format 'NetworkMode={{ .HostConfig.NetworkMode }}' "{{c}}")
+  if [ "$mode" = "NetworkMode=host" ]; then
+    fail "Container running with networking mode 'host': {{c}}"
+  fi
+}
+
+# 5.10
+@test "5.10 - Limit memory usage for container: {{c}}" {
+  run docker inspect --format '{{ .Config.Memory }}' "{{c}}"
+  if [ $status -eq 0 ]; then
+    memory=$(docker inspect --format '{{ .Config.Memory }}' "{{c}}")
+  else
+    memory=$(docker inspect --format '{{ .HostConfig.Memory }}' "{{c}}")
+  fi
+  if [ "$memory" = "0" ]; then
+    fail "Container running without memory restrictions: {{c}}"
+  fi
+}
+
+# 5.11
+@test "5.11 - Set container CPU priority appropriately: {{c}}" {
+  run docker inspect --format '{{ .Config.CpuShares }}' "{{c}}"
+  if [ $status -eq 0 ]; then
+    shares=$(docker inspect --format '{{ .Config.CpuShares }}' "{{c}}")
+  else
+    shares=$(docker inspect --format '{{ .HostConfig.CpuShares }}' "{{c}}")
+  fi
+  if [ "$shares" = "0" ]; then
+    fail "Container running without CPU restrictions: {{c}}"
+  fi
+}
+
+# 5.12
+@test "5.12 - Mount container's root filesystem as read only: {{c}}" {
+  read_status=$(docker inspect --format '{{ .HostConfig.ReadonlyRootfs }}' "{{c}}")
+  if [ "$read_status" = "false" ]; then
+    fail "Container running with root FS mounted R/W: {{c}}"
+  fi
+}
+
+# 5.13
+@test "5.13 - Bind incoming container traffic to a specific host interface: {{c}}" {
+  for ip in $(docker port "{{c}}" | awk '{print $3}' | cut -d ':' -f1 | tr "\n" " "); do
+    if [ "$ip" = "0.0.0.0" ]; then
+      fail "Port being bound to wildcard IP: $ip in {{c}}"
+    fi
+  done
+}
+
+# 5.14
+@test "5.14 - Set the 'on-failure' container restart policy to 5: {{c}}" {
+  policy=$(docker inspect --format MaximumRetryCount='{{ .HostConfig.RestartPolicy.MaximumRetryCount }}' "{{c}}")
+  if [ "$policy" != "MaximumRetryCount=5" ]; then
+    fail "MaximumRetryCount is not set to 5: {{c}}"
+  fi
+}
+
+# 5.15
+@test "5.15 - Do not share the host's process namespace: {{c}}" {
+  mode=$(docker inspect --format 'PidMode={{.HostConfig.PidMode }}' "{{c}}")
+  if [ "$mode" = "PidMode=host" ]; then
+    fail "Host PID namespace being shared with: {{c}}"
+  fi
+}
+
+# 5.16
+@test "5.16 - Do not share the host's IPC namespace: {{c}}" {
+  mode=$(docker inspect --format 'IpcMode={{.HostConfig.IpcMode }}' "{{c}}")
+  if [ "$mode" = "IpcMode=host" ]; then
+    fail "Host IPC namespace being shared with: {{c}}"
+  fi
+}
+
+# 5.17
+@test "5.17 - Do not directly expose host devices to containers: {{c}}" {
+  devices=$(docker inspect --format 'Devices={{ .HostConfig.Devices }}' "{{c}}")
+  if [ "$devices" != "Devices=" -a "$devices" != "Devices=[]" -a "$devices" != "Devices=<no value>" ]; then
+    fail "Container has devices exposed directly: {{c}}"
+  fi
+}
+
+# 5.18
+@test "5.18 - Override default ulimit at runtime only if needed: {{c}}" {
+  ulimits=$(docker inspect --format 'Ulimits={{ .HostConfig.Ulimits }}' "{{c}}")
+  if [ "$ulimits" = "Ulimits=" -o "$ulimits" = "Ulimits=[]" -o "$ulimits" = "Ulimits=<no value>" ]; then
+    fail "Container no default ulimit override: {{c}}"
+  fi
+}
+
+# 5.19
+@test "5.19 - Do not set mount propagation mode to shared: {{c}}" {
+  mode=$(docker inspect --format 'Propagation={{range $mnt := .Mounts}} {{json $mnt.Propagation}} {{end}}' "{{c}}")
+  if [ "$mode" = "Propagation=shared" ]; then
+    fail "Mount propagation mode is shared: {{c}}"
+  fi
+}
+
+# 5.20
+@test "5.20 - Do not share the host's UTS namespace: {{c}}" {
+  mode=$(docker inspect --format 'UTSMode={{.HostConfig.UTSMode }}' "{{c}}")
+  if [ "$mode" = "UTSMode=host" ]; then
+    fail "Host UTS namespace being shared with: {{c}}"
+  fi
+}
+
+# 5.21
+@test "5.21 - Do not disable default seccomp profile: {{c}}" {
+  result=$(docker inspect --format 'SecurityOpt={{.HostConfig.SecurityOpt }}' "{{c}}")
+  run grep "seccomp:unconfined" <<< "$result"
+  if [ $status -eq 0 ]; then
+    fail "Default seccomp profile disabled: {{c}}"
+  fi
+}
+
+# 5.24
+@test "5.24 - Confirm cgroup usage: {{c}}" {
+  mode=$(docker inspect --format 'CgroupParent={{.HostConfig.CgroupParent }}x' "{{c}}")
+  if [ "$mode" != "CgroupParent=x" ]; then
+    fail "Confirm cgroup usage: {{c}}"
+  fi
+}
+
+# 5.25
+@test "5.25 - Restrict container from acquiring additional privileges: {{c}}" {
+  result=$(docker inspect --format 'SecurityOpt={{.HostConfig.SecurityOpt }}' "{{c}}")
+  run grep "no-new-privileges" <<< "$result"
+  if [ $status -ne 0 ]; then
+    fail "Privileges not restricted: {{c}}"
+  fi
+}
diff --git a/bats_tests/6_docker_security_operations.bats b/bats_tests/6_docker_security_operations.bats
new file mode 100644
index 0000000..c4d9f43
--- /dev/null
+++ b/bats_tests/6_docker_security_operations.bats
@@ -0,0 +1,36 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
+
+
+# 6.4
+@test "6.4 - Avoid image sprawl" {
+  images=$(docker images -q | sort -u | wc -l | awk '{print $1}')
+  active_images=0
+
+  for c in $(docker inspect -f "{{.Image}}" $(docker ps -qa)); do
+    if docker images --no-trunc -a | grep "$c" > /dev/null ; then
+      active_images=$(( active_images += 1 ))
+    fi
+  done
+
+  if [ "$images" -gt 100 ]; then
+    fail "There are currently: $images images"
+  fi
+
+  if [ "$active_images" -lt "$((images / 2))" ]; then
+    fail "Only $active_images out of $images are in use"
+  fi
+}
+
+# 6.5
+@test "6.5 - Avoid container sprawl" {
+  total_containers=$(docker info 2>/dev/null | grep "Containers" | awk '{print $2}')
+  running_containers=$(docker ps -q | wc -l | awk '{print $1}')
+  diff="$((total_containers - running_containers))"
+  if [ "$diff" -gt 25 ]; then
+    fail "There are currently a total of $total_containers containers, with only $running_containers of them currently running"
+  fi
+}
diff --git a/generate_tests.sh b/generate_tests.sh
index b13d9b2..908cbfa 100644
--- a/generate_tests.sh
+++ b/generate_tests.sh
@@ -2,9 +2,9 @@
 
 . ./helper_lib.sh
 
-TEST_SRC=./test
+TEST_SRC=./bats_tests
 BENCH_ROOT=/var/docker-bench
-TEST_ROOT=$BENCH_ROOT/test
+TEST_ROOT=$BENCH_ROOT/bats_tests
 
 prepare_tests_directory()
 {
diff --git a/test/test_helper/bats-assert b/test/test_helper/bats-assert
deleted file mode 160000
index 9f88b42..0000000
--- a/test/test_helper/bats-assert
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 9f88b4207da750093baabc4e3f41bf68f0dd3630
diff --git a/test/test_helper/bats-support b/test/test_helper/bats-support
deleted file mode 160000
index d0a1318..0000000
--- a/test/test_helper/bats-support
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit d0a131831c487a1f1141e76d3ab386c89642cdff

From a888600cbb2c08e85ce279b335345e136d529f9b Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Tue, 17 May 2016 13:20:08 +0300
Subject: [PATCH 11/30] fix submodules path rename

---
 .gitmodules                         | 6 ++++++
 bats_tests/test_helper/bats-assert  | 1 +
 bats_tests/test_helper/bats-support | 1 +
 3 files changed, 8 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 bats_tests/test_helper/bats-assert
 create mode 160000 bats_tests/test_helper/bats-support

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..b18fe2f
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "bats_tests/test_helper/bats-assert"]
+	path = bats_tests/test_helper/bats-assert
+	url = https://github.com/ztombol/bats-assert
+[submodule "bats_tests/test_helper/bats-support"]
+	path = bats_tests/test_helper/bats-support
+	url = https://github.com/ztombol/bats-support
diff --git a/bats_tests/test_helper/bats-assert b/bats_tests/test_helper/bats-assert
new file mode 160000
index 0000000..9f88b42
--- /dev/null
+++ b/bats_tests/test_helper/bats-assert
@@ -0,0 +1 @@
+Subproject commit 9f88b4207da750093baabc4e3f41bf68f0dd3630
diff --git a/bats_tests/test_helper/bats-support b/bats_tests/test_helper/bats-support
new file mode 160000
index 0000000..d0a1318
--- /dev/null
+++ b/bats_tests/test_helper/bats-support
@@ -0,0 +1 @@
+Subproject commit d0a131831c487a1f1141e76d3ab386c89642cdff

From b86b18aa1e5e55c81971b24714dcdfef740f1c4d Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Mon, 2 May 2016 16:23:50 +0300
Subject: [PATCH 12/30] migrating shell scripts to bats testing framework

---
 helper_lib.sh                   |   3 +
 tests/1_host_configuration.bats | 111 ++++++++++++++++++++++++++++++++
 2 files changed, 114 insertions(+)
 create mode 100644 tests/1_host_configuration.bats

diff --git a/helper_lib.sh b/helper_lib.sh
index 74c8849..2b6f1b0 100644
--- a/helper_lib.sh
+++ b/helper_lib.sh
@@ -1,5 +1,8 @@
 #!/bin/sh
 
+# echo to stderr
+echoerr() { echo "$@" 1>&2; }
+
 # Returns the absolute path of a given string
 abspath () { case "$1" in /*)printf "%s\n" "$1";; *)printf "%s\n" "$PWD/$1";; esac; }
 
diff --git a/tests/1_host_configuration.bats b/tests/1_host_configuration.bats
new file mode 100644
index 0000000..2944f5f
--- /dev/null
+++ b/tests/1_host_configuration.bats
@@ -0,0 +1,111 @@
+#!/usr/bin/env bats
+
+setup() {
+  . "$BATS_TEST_DIRNAME/../helper_lib.sh"
+}
+
+# 1.1
+@test "1.1  - Create a separate partition for containers" {
+  grep /var/lib/docker /etc/fstab
+  [ $status -eq 0 ]
+}
+
+# 1.2
+@test "1.2  - Use an updated Linux Kernel" {
+  kernel_version=$(uname -r | cut -d "-" -f 1)
+  run do_version_check 3.10 "$kernel_version"
+  [ $status -eq 9 ] || [ $status -eq 10 ]
+}
+
+# 1.4
+@test "1.4  - Remove all non-essential services from the host - Network" {
+  # Check for listening network services.
+  listening_services=$(netstat -na | grep -v tcp6 | grep -v unix | grep -c LISTEN)
+  if [ "$listening_services" -eq 0 ]; then
+    echoerr "1.4  - Failed to get listening services for check: $BATS_TEST_NAME"
+  else
+    if [ "$listening_services" -gt 5 ]; then
+      echoerr "     * Host listening on: $listening_services ports"
+    fi
+  fi
+  [ "$listening_services" -ne 0 ] && [ "$listening_services" -le 5 ]
+}
+
+# 1.5
+@test "1.5  - Keep Docker up to date" {
+  docker_version=$(docker version | grep -i -A1 '^server' | grep -i 'version:' \
+    | awk '{print $NF; exit}' | tr -d '[:alpha:]-,')
+  docker_current_version="1.11.1"
+  docker_current_date="2016-04-27"
+  run do_version_check "$docker_current_version" "$docker_version"
+  if [ $status -eq 11 ]; then
+    echoerr "      * Using $docker_version, when $docker_current_version is current as of $docker_current_date"
+    echoerr "      * Your operating system vendor may provide support and security maintenance for docker"
+  else
+    pass "$check_1_5"
+    echoerr "      * Using $docker_version which is current as of $docker_current_date"
+    echoerr "      * Check with your operating system vendor for support and security maintenance for docker"
+  fi
+  [ $status -eq 9 ] || [ $status -eq 10 ]
+}
+
+# 1.6
+@test "1.6  - Only allow trusted users to control Docker daemon" {
+  docker_users=$(getent group docker)
+  echoerr "$BATS_TEST_NAME"
+  for u in $docker_users; do
+    echoerr "     * $u"
+  done
+}
+
+# 1.7
+@test "1.7  - Audit docker daemon - /usr/bin/docker" {
+  file="/usr/bin/docker"
+  run command -v auditctl
+  if [ $status -eq 0 ]; then
+    auditctl -l | grep "$file" >/dev/null 2>&1
+  else
+    echoerr "      * Failed to inspect: auditctl command not found."
+  fi
+  [ $status -eq 0 ]
+}
+
+# 1.8
+@test "1.8  - Audit Docker files and directories - /var/lib/docker" {
+  skip "TODO: need to implement"
+}
+
+# 1.9
+@test "1.9  - Audit Docker files and directories - /etc/docker" {
+  skip "TODO: need to implement"
+}
+
+# 1.10
+@test "1.10 - Audit Docker files and directories - docker.service" {
+  skip "TODO: need to implement"
+}
+
+# 1.11
+@test "1.11 - Audit Docker files and directories - docker.socket" {
+  skip "TODO: need to implement"
+}
+
+# 1.12
+@test "1.12 - Audit Docker files and directories - /etc/default/docker" {
+  skip "TODO: need to implement"
+}
+
+# 1.13
+@test "1.13 - Audit Docker files and directories - /etc/docker/daemon.json" {
+  skip "TODO: need to implement"
+}
+
+# 1.14
+@test "1.14 - Audit Docker files and directories - /usr/bin/docker-containerd" {
+  skip "TODO: need to implement"
+}
+
+# 1.15
+@test "1.15 - Audit Docker files and directories - /usr/bin/docker-runc" {
+  skip "TODO: need to implement"
+}

From 47fab8795e1472d257b8e3dcfcb75c41fc45043c Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Thu, 5 May 2016 16:17:32 +0300
Subject: [PATCH 13/30] Add bats-support library

---
 .gitmodules                   | 3 +++
 test/test_helper/bats-support | 1 +
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 test/test_helper/bats-support

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..c527cf1
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "test/test_helper/bats-support"]
+	path = test/test_helper/bats-support
+	url = https://github.com/ztombol/bats-support
diff --git a/test/test_helper/bats-support b/test/test_helper/bats-support
new file mode 160000
index 0000000..d0a1318
--- /dev/null
+++ b/test/test_helper/bats-support
@@ -0,0 +1 @@
+Subproject commit d0a131831c487a1f1141e76d3ab386c89642cdff

From fc42e548fedaf63edc5d4a60d6bbdca1c57822fe Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Thu, 5 May 2016 16:18:15 +0300
Subject: [PATCH 14/30] Add bats-assert library

---
 .gitmodules                  | 3 +++
 test/test_helper/bats-assert | 1 +
 2 files changed, 4 insertions(+)
 create mode 160000 test/test_helper/bats-assert

diff --git a/.gitmodules b/.gitmodules
index c527cf1..dc270e5 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
 [submodule "test/test_helper/bats-support"]
 	path = test/test_helper/bats-support
 	url = https://github.com/ztombol/bats-support
+[submodule "test/test_helper/bats-assert"]
+	path = test/test_helper/bats-assert
+	url = https://github.com/ztombol/bats-assert
diff --git a/test/test_helper/bats-assert b/test/test_helper/bats-assert
new file mode 160000
index 0000000..9f88b42
--- /dev/null
+++ b/test/test_helper/bats-assert
@@ -0,0 +1 @@
+Subproject commit 9f88b4207da750093baabc4e3f41bf68f0dd3630

From bd567925236855b05176e90b13b765404da09aeb Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Thu, 5 May 2016 18:50:47 +0300
Subject: [PATCH 15/30] fighting with bats

---
 helper_lib.sh                             |  3 -
 {tests => test}/1_host_configuration.bats | 75 +++++++++++++++--------
 2 files changed, 50 insertions(+), 28 deletions(-)
 rename {tests => test}/1_host_configuration.bats (53%)

diff --git a/helper_lib.sh b/helper_lib.sh
index 2b6f1b0..74c8849 100644
--- a/helper_lib.sh
+++ b/helper_lib.sh
@@ -1,8 +1,5 @@
 #!/bin/sh
 
-# echo to stderr
-echoerr() { echo "$@" 1>&2; }
-
 # Returns the absolute path of a given string
 abspath () { case "$1" in /*)printf "%s\n" "$1";; *)printf "%s\n" "$PWD/$1";; esac; }
 
diff --git a/tests/1_host_configuration.bats b/test/1_host_configuration.bats
similarity index 53%
rename from tests/1_host_configuration.bats
rename to test/1_host_configuration.bats
index 2944f5f..05b7c94 100644
--- a/tests/1_host_configuration.bats
+++ b/test/1_host_configuration.bats
@@ -1,34 +1,31 @@
 #!/usr/bin/env bats
 
+load 'test_helper/bats-support/load'
+load 'test_helper/bats-assert/load'
+
 setup() {
   . "$BATS_TEST_DIRNAME/../helper_lib.sh"
 }
 
 # 1.1
 @test "1.1  - Create a separate partition for containers" {
-  grep /var/lib/docker /etc/fstab
-  [ $status -eq 0 ]
+  run grep /var/lib/docker /etc/fstab
+  assert_success
 }
 
 # 1.2
 @test "1.2  - Use an updated Linux Kernel" {
   kernel_version=$(uname -r | cut -d "-" -f 1)
   run do_version_check 3.10 "$kernel_version"
-  [ $status -eq 9 ] || [ $status -eq 10 ]
+  assert [ $status -eq 9 -o $status -eq 10 ]
 }
 
 # 1.4
 @test "1.4  - Remove all non-essential services from the host - Network" {
   # Check for listening network services.
   listening_services=$(netstat -na | grep -v tcp6 | grep -v unix | grep -c LISTEN)
-  if [ "$listening_services" -eq 0 ]; then
-    echoerr "1.4  - Failed to get listening services for check: $BATS_TEST_NAME"
-  else
-    if [ "$listening_services" -gt 5 ]; then
-      echoerr "     * Host listening on: $listening_services ports"
-    fi
-  fi
-  [ "$listening_services" -ne 0 ] && [ "$listening_services" -le 5 ]
+  refute [ "$listening_services" -eq 0 ] "1.4  - Failed to get listening services for check: $BATS_TEST_NAME"
+  refute [ "$listening_services" -gt 5 ] "Host listening on: $listening_services ports"
 }
 
 # 1.5
@@ -39,22 +36,26 @@ setup() {
   docker_current_date="2016-04-27"
   run do_version_check "$docker_current_version" "$docker_version"
   if [ $status -eq 11 ]; then
-    echoerr "      * Using $docker_version, when $docker_current_version is current as of $docker_current_date"
-    echoerr "      * Your operating system vendor may provide support and security maintenance for docker"
-  else
-    pass "$check_1_5"
-    echoerr "      * Using $docker_version which is current as of $docker_current_date"
-    echoerr "      * Check with your operating system vendor for support and security maintenance for docker"
+    fail "Using $docker_version, when $docker_current_version is current as of $docker_current_date. Your operating system vendor may provide support and security maintenance for docker."
   fi
-  [ $status -eq 9 ] || [ $status -eq 10 ]
+  assert [ $status -eq 9 -o $status -eq 10 ]
 }
 
 # 1.6
 @test "1.6  - Only allow trusted users to control Docker daemon" {
-  docker_users=$(getent group docker)
-  echoerr "$BATS_TEST_NAME"
-  for u in $docker_users; do
-    echoerr "     * $u"
+  declare -a trusted_users=("vagrant" "docker" "ubuntu")
+  users_string=$(awk -F':' '/^docker/{print $4}' /etc/group)
+  docker_users=(${users_string//,/ })
+  for u in ${docker_users[@]}; do
+    local found=1
+    for tu in ${trusted_users[@]}; do
+      if [ "$u" = "$tu" ]; then
+        found=0
+      fi
+    done
+    if [ $found -eq 1 ]; then
+      fail "User $u is not a trusted user!"
+    fi
   done
 }
 
@@ -65,19 +66,43 @@ setup() {
   if [ $status -eq 0 ]; then
     auditctl -l | grep "$file" >/dev/null 2>&1
   else
-    echoerr "      * Failed to inspect: auditctl command not found."
+    fail "Failed to inspect: auditctl command not found."
   fi
   [ $status -eq 0 ]
 }
 
 # 1.8
 @test "1.8  - Audit Docker files and directories - /var/lib/docker" {
-  skip "TODO: need to implement"
+  directory="/var/lib/docker"
+  if [ -d "$directory" ]; then
+    run command -v auditctl >/dev/null
+    if [ $status -eq 0 ]; then
+      auditctl -l | grep $directory >/dev/null 2>&1
+    else
+      fail "1.8  - Failed to inspect: auditctl command not found."
+    fi
+    [ $status -eq 0 ]
+  else
+    fail "     * '$directory' Directory not found"
+    [ -d "$directory" ]
+  fi
 }
 
 # 1.9
 @test "1.9  - Audit Docker files and directories - /etc/docker" {
-  skip "TODO: need to implement"
+  directory="/etc/docker"
+  if [ -d "$directory" ]; then
+    run command -v auditctl >/dev/null
+    if [ $status -eq 0 ]; then
+      auditctl -l | grep $directory >/dev/null 2>&1
+    else
+      fail "1.9  - Failed to inspect: auditctl command not found."
+    fi
+    [ $status -eq 0 ]
+  else
+    fail "'$directory' Directory not found"
+    [ -d "$directory" ]
+  fi
 }
 
 # 1.10

From ccfcf009adfff20b3843d4346a0d87d79262e3ce Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Sat, 7 May 2016 15:56:35 +0300
Subject: [PATCH 16/30] convert second CIS section to bats

---
 test/1_host_configuration.bats          |  94 ++++++++++++----------
 test/2_docker_daemon_configuration.bats | 102 ++++++++++++++++++++++++
 2 files changed, 154 insertions(+), 42 deletions(-)
 create mode 100644 test/2_docker_daemon_configuration.bats

diff --git a/test/1_host_configuration.bats b/test/1_host_configuration.bats
index 05b7c94..dfa283d 100644
--- a/test/1_host_configuration.bats
+++ b/test/1_host_configuration.bats
@@ -1,11 +1,8 @@
 #!/usr/bin/env bats
 
-load 'test_helper/bats-support/load'
-load 'test_helper/bats-assert/load'
-
-setup() {
-  . "$BATS_TEST_DIRNAME/../helper_lib.sh"
-}
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 
 # 1.1
 @test "1.1  - Create a separate partition for containers" {
@@ -63,74 +60,87 @@ setup() {
 @test "1.7  - Audit docker daemon - /usr/bin/docker" {
   file="/usr/bin/docker"
   run command -v auditctl
-  if [ $status -eq 0 ]; then
-    auditctl -l | grep "$file" >/dev/null 2>&1
-  else
-    fail "Failed to inspect: auditctl command not found."
-  fi
-  [ $status -eq 0 ]
+  assert_success
+  run auditctl -l | grep "$file"
+  assert_success
 }
 
 # 1.8
 @test "1.8  - Audit Docker files and directories - /var/lib/docker" {
   directory="/var/lib/docker"
-  if [ -d "$directory" ]; then
-    run command -v auditctl >/dev/null
-    if [ $status -eq 0 ]; then
-      auditctl -l | grep $directory >/dev/null 2>&1
-    else
-      fail "1.8  - Failed to inspect: auditctl command not found."
-    fi
-    [ $status -eq 0 ]
-  else
-    fail "     * '$directory' Directory not found"
-    [ -d "$directory" ]
-  fi
+  refute [ -d "$directory" ] "'$directory' Directory not found"
+  run command -v auditctl >/dev/null
+  assert_success
+  run auditctl -l | grep $directory
+  assert_success
 }
 
 # 1.9
 @test "1.9  - Audit Docker files and directories - /etc/docker" {
   directory="/etc/docker"
-  if [ -d "$directory" ]; then
-    run command -v auditctl >/dev/null
-    if [ $status -eq 0 ]; then
-      auditctl -l | grep $directory >/dev/null 2>&1
-    else
-      fail "1.9  - Failed to inspect: auditctl command not found."
-    fi
-    [ $status -eq 0 ]
-  else
-    fail "'$directory' Directory not found"
-    [ -d "$directory" ]
-  fi
+  refute [ -d "$directory" ] "'$directory' Directory not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep $directory
+  assert_success
 }
 
 # 1.10
 @test "1.10 - Audit Docker files and directories - docker.service" {
-  skip "TODO: need to implement"
+  file="$(get_systemd_service_file docker.service)"
+  refute [ -f "$file" ] "'docker.service' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep "$file"
+  assert_success
 }
 
 # 1.11
 @test "1.11 - Audit Docker files and directories - docker.socket" {
-  skip "TODO: need to implement"
+  file="$(get_systemd_service_file docker.socket)"
+  refute [ -e "$file" ] "'docker.socket' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep "$file"
+  assert_success
 }
 
 # 1.12
 @test "1.12 - Audit Docker files and directories - /etc/default/docker" {
-  skip "TODO: need to implement"
+  file="/etc/default/docker"
+  refute [ -f "$file" ] "'$file' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep $file
+  assert_success
 }
 
 # 1.13
 @test "1.13 - Audit Docker files and directories - /etc/docker/daemon.json" {
-  skip "TODO: need to implement"
+  file="/etc/docker/daemon.json"
+  refute [ -f "$file" ] "'$file' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep $file
+  assert_success
 }
 
 # 1.14
 @test "1.14 - Audit Docker files and directories - /usr/bin/docker-containerd" {
-  skip "TODO: need to implement"
+  file="/usr/bin/docker-containerd"
+  refute [ -f "$file" ] "'$file' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep $file
+  assert_success
 }
 
 # 1.15
 @test "1.15 - Audit Docker files and directories - /usr/bin/docker-runc" {
-  skip "TODO: need to implement"
+  file="/usr/bin/docker-runc"
+  refute [ -f "$file" ] "'$file' file not found"
+  run command -v auditctl
+  assert_success
+  run auditctl -l | grep $file
+  assert_success
 }
diff --git a/test/2_docker_daemon_configuration.bats b/test/2_docker_daemon_configuration.bats
new file mode 100644
index 0000000..776c500
--- /dev/null
+++ b/test/2_docker_daemon_configuration.bats
@@ -0,0 +1,102 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
+
+# 2.1
+@test "2.1  - Restrict network traffic between containers" {
+  result=$(get_docker_effective_command_line_args '--icc')
+  run grep "false" <<< "$result"
+  assert_success
+}
+
+# 2.2
+@test "2.2  - Set the logging level" {
+  result=$(get_docker_effective_command_line_args '-l')
+  run grep 'debug' <<< "$result"
+  assert_failure
+}
+
+# 2.3
+@test "2.3  - Allow Docker to make changes to iptables" {
+  result=$(get_docker_effective_command_line_args '--iptables')
+  run grep "false" <<< "$result"
+  assert_failure
+}
+
+# 2.4
+@test "2.4  - Do not use insecure registries" {
+  result=$(get_docker_effective_command_line_args '--insecure-registry')
+  run grep "insecure-registry" <<< "$result"
+  assert_failure
+}
+
+# 2.5
+@test "2.5  - Do not use the aufs storage driver" {
+  result=$(docker info 2>/dev/null)
+  run grep -e "^Storage Driver:\s*aufs\s*$" <<< "$result"
+  assert_failure
+}
+
+# 2.6
+@test "2.6  - Configure TLS authentication for Docker daemon" {
+  result=$(get_docker_cumulative_command_line_args '-H')
+  run grep -vE '(unix|fd)://' <<< "$result"
+  if [ $status -eq 0 ]; then
+    result=$(get_command_line_args docker)
+    run $(grep "tlsverify" <<< "$result" | grep "tlskey")
+    assert_success
+  fi
+}
+
+# 2.7
+@test "2.7 - Set default ulimit as appropriate" {
+  result=$(get_docker_effective_command_line_args '--default-ulimit')
+  run grep "default-ulimit" <<< "$result"
+  assert_success
+}
+
+# 2.8
+@test "2.8  - Enable user namespace support" {
+  result=$(get_docker_effective_command_line_args '--userns-remap')
+  run grep "userns-remap" <<< "$result"
+  assert_success
+}
+
+# 2.9
+@test "2.9  - Confirm default cgroup usage" {
+  result=$(get_docker_effective_command_line_args '--cgroup-parent')
+  run grep "cgroup-parent" <<< "$result"
+  if [ $status -eq 0 ]; then
+    refute_output_contains "docker"
+  fi
+}
+
+# 2.10
+@test "2.10 - Do not change base device size until needed" {
+  result=$(get_docker_effective_command_line_args '--storage-opt')
+  run grep "dm.basesize" <<< "$result"
+  assert_failure
+}
+
+# 2.11
+@test "2.11 - Use authorization plugin" {
+  result=$(get_docker_effective_command_line_args '--authorization-plugin')
+  run grep "authorization-plugin" <<< "$result"
+  assert_success
+}
+
+# 2.12
+@test "2.12 - Configure centralized and remote logging" {
+  result=$(get_docker_effective_command_line_args '--log-driver')
+  run grep "log-driver" <<< "$result"
+  assert_success
+}
+
+# 2.13
+@test "2.13 - Disable operations on legacy registry (v1)" {
+  result=$(get_docker_effective_command_line_args '--disable-legacy-registry')
+  run grep "disable-legacy-registry" <<< "$result"
+  assert_success
+}

From 72a4a79a36870451dd5447555c5e5bc74db3f380 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Fri, 13 May 2016 18:20:50 +0300
Subject: [PATCH 17/30] Implemented tests for CIS 3.x section

---
 bats.Dockerfile                               |  34 +++
 bats_tests/test_helper/bats-assert            |   1 +
 bats_tests/test_helper/bats-support           |   1 +
 test/1_host_configuration.bats                |  25 +-
 test/2_docker_daemon_configuration.bats       |   2 +-
 test/3_docker_daemon_configuration_files.bats | 223 ++++++++++++++++++
 6 files changed, 275 insertions(+), 11 deletions(-)
 create mode 100644 bats.Dockerfile
 create mode 160000 bats_tests/test_helper/bats-assert
 create mode 160000 bats_tests/test_helper/bats-support
 create mode 100644 test/3_docker_daemon_configuration_files.bats

diff --git a/bats.Dockerfile b/bats.Dockerfile
new file mode 100644
index 0000000..376746a
--- /dev/null
+++ b/bats.Dockerfile
@@ -0,0 +1,34 @@
+# REPOSITORY https://github.com/docker/docker-bench-securit
+FROM alpine:3.3
+
+MAINTAINER dockerbench.com
+MAINTAINER Alexei Ledenev <alexei.led@gmail.com>
+
+ENV VERSION 1.10.0
+ENV BATS_VERSION 0.4.0
+
+WORKDIR /usr/bin
+
+RUN apk update && \
+    apk upgrade && \
+    apk --update add curl bash && \
+    curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION > docker-$VERSION && \
+    curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.sha256 > docker-$VERSION.sha256 && \
+    sha256sum -c docker-$VERSION.sha256 && \
+    ln -s docker-$VERSION docker && \
+    chmod u+x docker-$VERSION && \
+    rm -rf /var/cache/apk/*
+
+RUN curl -o "/tmp/v${BATS_VERSION}.tar.gz" -L \
+		"https://github.com/sstephenson/bats/archive/v${BATS_VERSION}.tar.gz" \
+	&& tar -x -z -f "/tmp/v${BATS_VERSION}.tar.gz" -C /tmp/ \
+	&& bash "/tmp/bats-${BATS_VERSION}/install.sh" /usr/local \
+	&& rm -rf /tmp/*
+
+RUN mkdir /docker-bench-security
+
+COPY . /docker-bench-security
+
+WORKDIR /docker-bench-security
+
+ENTRYPOINT ["/usr/local/bin/bats", "/docker-bench-security/test"]
diff --git a/bats_tests/test_helper/bats-assert b/bats_tests/test_helper/bats-assert
new file mode 160000
index 0000000..9f88b42
--- /dev/null
+++ b/bats_tests/test_helper/bats-assert
@@ -0,0 +1 @@
+Subproject commit 9f88b4207da750093baabc4e3f41bf68f0dd3630
diff --git a/bats_tests/test_helper/bats-support b/bats_tests/test_helper/bats-support
new file mode 160000
index 0000000..d0a1318
--- /dev/null
+++ b/bats_tests/test_helper/bats-support
@@ -0,0 +1 @@
+Subproject commit d0a131831c487a1f1141e76d3ab386c89642cdff
diff --git a/test/1_host_configuration.bats b/test/1_host_configuration.bats
index dfa283d..62ad15b 100644
--- a/test/1_host_configuration.bats
+++ b/test/1_host_configuration.bats
@@ -21,8 +21,13 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 @test "1.4  - Remove all non-essential services from the host - Network" {
   # Check for listening network services.
   listening_services=$(netstat -na | grep -v tcp6 | grep -v unix | grep -c LISTEN)
-  refute [ "$listening_services" -eq 0 ] "1.4  - Failed to get listening services for check: $BATS_TEST_NAME"
-  refute [ "$listening_services" -gt 5 ] "Host listening on: $listening_services ports"
+  if [ "$listening_services" -eq 0 ]; then
+    fail "Failed to get listening services for check: $BATS_TEST_NAME"
+  else
+    if [ "$listening_services" -gt 5 ]; then
+      fail "Host listening on: $listening_services ports"
+    fi
+  fi
 }
 
 # 1.5
@@ -68,7 +73,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.8
 @test "1.8  - Audit Docker files and directories - /var/lib/docker" {
   directory="/var/lib/docker"
-  refute [ -d "$directory" ] "'$directory' Directory not found"
+  assert [ -d "$directory" ]
   run command -v auditctl >/dev/null
   assert_success
   run auditctl -l | grep $directory
@@ -78,7 +83,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.9
 @test "1.9  - Audit Docker files and directories - /etc/docker" {
   directory="/etc/docker"
-  refute [ -d "$directory" ] "'$directory' Directory not found"
+  assert [ -d "$directory" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep $directory
@@ -88,7 +93,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.10
 @test "1.10 - Audit Docker files and directories - docker.service" {
   file="$(get_systemd_service_file docker.service)"
-  refute [ -f "$file" ] "'docker.service' file not found"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep "$file"
@@ -98,7 +103,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.11
 @test "1.11 - Audit Docker files and directories - docker.socket" {
   file="$(get_systemd_service_file docker.socket)"
-  refute [ -e "$file" ] "'docker.socket' file not found"
+  assert [ -e "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep "$file"
@@ -108,7 +113,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.12
 @test "1.12 - Audit Docker files and directories - /etc/default/docker" {
   file="/etc/default/docker"
-  refute [ -f "$file" ] "'$file' file not found"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep $file
@@ -118,7 +123,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.13
 @test "1.13 - Audit Docker files and directories - /etc/docker/daemon.json" {
   file="/etc/docker/daemon.json"
-  refute [ -f "$file" ] "'$file' file not found"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep $file
@@ -128,7 +133,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.14
 @test "1.14 - Audit Docker files and directories - /usr/bin/docker-containerd" {
   file="/usr/bin/docker-containerd"
-  refute [ -f "$file" ] "'$file' file not found"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep $file
@@ -138,7 +143,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 1.15
 @test "1.15 - Audit Docker files and directories - /usr/bin/docker-runc" {
   file="/usr/bin/docker-runc"
-  refute [ -f "$file" ] "'$file' file not found"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
   run auditctl -l | grep $file
diff --git a/test/2_docker_daemon_configuration.bats b/test/2_docker_daemon_configuration.bats
index 776c500..de46c62 100644
--- a/test/2_docker_daemon_configuration.bats
+++ b/test/2_docker_daemon_configuration.bats
@@ -69,7 +69,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
   result=$(get_docker_effective_command_line_args '--cgroup-parent')
   run grep "cgroup-parent" <<< "$result"
   if [ $status -eq 0 ]; then
-    refute_output_contains "docker"
+    assert_output_contains "docker"
   fi
 }
 
diff --git a/test/3_docker_daemon_configuration_files.bats b/test/3_docker_daemon_configuration_files.bats
new file mode 100644
index 0000000..de99861
--- /dev/null
+++ b/test/3_docker_daemon_configuration_files.bats
@@ -0,0 +1,223 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
+
+# 3.1
+@test "3.1  - Verify that docker.service file ownership is set to root:root" {
+  file="$(get_systemd_service_file docker.service)"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %u%g $file)" -ne 00 ]; then
+      fail "Wrong ownership for $file"
+    fi
+  fi
+}
+
+# 3.2
+@test "3.2  - Verify that docker.service file permissions are set to 644" {
+  file="$(get_systemd_service_file docker.service)"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %a $file)" -ne 644 ]; then
+      fail "Wrong permissions for $file"
+    fi
+  fi
+}
+
+# 3.3
+@test "3.3  - Verify that docker.socket file ownership is set to root:root" {
+  file="$(get_systemd_service_file docker.socket)"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %u%g $file)" -ne 00 ]; then
+      fail "Wrong ownership for $file"
+    fi
+  fi
+}
+
+# 3.4
+@test "3.4  - Verify that docker.socket file permissions are set to 644" {
+  file="$(get_systemd_service_file docker.socket)"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %a $file)" -ne 644 ]; then
+      fail "Wrong permissions for $file"
+    fi
+  fi
+}
+
+# 3.5
+@test "3.5  - Verify that /etc/docker directory ownership is set to root:root" {
+  directory="/etc/docker"
+  if [ -d "$directory" ]; then
+    if [ "$(stat -c %u%g $directory)" -ne 00 ]; then
+      fail "Wrong ownership for $directory"
+    fi
+  fi
+}
+
+# 3.6
+@test "3.6  - Verify that /etc/docker directory permissions are set to 755 or 700" {
+  directory="/etc/docker"
+  if [ -d "$directory" ]; then
+    if [ "$(stat -c %a $directory)" -ne 755 -a "$(stat -c %a $directory)" -ne 700 ]; then
+      fail "Wrong permissions for $directory : $(stat -c %a $directory)"
+    fi
+  fi
+}
+
+# 3.7
+@test "3.7  - Verify that registry certificate file ownership is set to root:root" {
+  directory="/etc/docker/certs.d/"
+  if [ -d "$directory" ]; then
+    fail=0
+    owners=$(ls -lL $directory | grep ".crt" | awk '{print $3, $4}')
+    for p in $owners; do
+      printf "%s" "$p" | grep "root" >/dev/null 2>&1
+      if [ $? -ne 0 ]; then
+        fail=1
+      fi
+    done
+    if [ $fail -eq 1 ]; then
+      fail "Wrong ownership for $directory"
+    fi
+  fi
+}
+
+# 3.8
+@test "3.8  - Verify that registry certificate file permissions are set to 444" {
+  directory="/etc/docker/certs.d/"
+  if [ -d "$directory" ]; then
+    fail=0
+    perms=$(ls -lL $directory | grep ".crt" | awk '{print $1}')
+    for p in $perms; do
+      if [ "$p" != "-r--r--r--." -a "$p" = "-r--------." ]; then
+        fail=1
+      fi
+    done
+    if [ $fail -eq 1 ]; then
+      fail "Wrong permissions for $directory"
+    fi
+  fi
+}
+
+# 3.9
+@test "3.9  - Verify that TLS CA certificate file ownership is set to root:root" {
+  tlscacert=$(get_docker_effective_command_line_args '--tlscacert' | sed -n 's/.*tlscacert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlscacert" ]; then
+    if [ "$(stat -c %u%g "$tlscacert")" -ne 00 ]; then
+      fail "Wrong ownership for $tlscacert : $(stat -c %u%g "$tlscacert")"
+    fi
+  fi
+}
+
+# 3.10
+@test "3.10 - Verify that TLS CA certificate file permissions are set to 444" {
+  tlscacert=$(get_docker_effective_command_line_args '--tlscacert' | sed -n 's/.*tlscacert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlscacert" ]; then
+    perms=$(ls -ld "$tlscacert" | awk '{print $1}')
+    if [ "$perms" != "-r--r--r--" ]; then
+      fail "Wrong permissions for $tlscacert"
+    fi
+  fi
+}
+
+# 3.11
+@test "3.11 - Verify that Docker server certificate file ownership is set to root:root" {
+  tlscert=$(get_docker_effective_command_line_args '--tlscert' | sed -n 's/.*tlscert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlscert" ]; then
+    if [ "$(stat -c %u%g "$tlscert")" -ne 00 ]; then
+      fail "Wrong ownership for $tlscert : $(stat -c %u%g "$tlscert")"
+    fi
+  fi
+}
+
+# 3.12
+@test "3.12 - Verify that Docker server certificate file permissions are set to 444" {
+  tlscert=$(get_docker_effective_command_line_args '--tlscert' | sed -n 's/.*tlscert=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlscert" ]; then
+    perms=$(ls -ld "$tlscert" | awk '{print $1}')
+    if [ "$perms" != "-r--r--r--" ]; then
+      fail "Wrong permissions for $tlscert"
+    fi
+  fi
+}
+
+# 3.13
+@test "3.13 - Verify that Docker server key file ownership is set to root:root" {
+  tlskey=$(get_docker_effective_command_line_args '--tlskey' | sed -n 's/.*tlskey=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlskey" ]; then
+    if [ "$(stat -c %u%g "$tlskey")" -ne 00 ]; then
+      fail "Wrong ownership for $tlskey : $(stat -c %u%g "$tlskey")"
+    fi
+  fi
+}
+
+# 3.14
+@test "3.14 - Verify that Docker server key file permissions are set to 400" {
+  tlskey=$(get_docker_effective_command_line_args '--tlskey' | sed -n 's/.*tlskey=\([^s]\)/\1/p' | sed 's/--/ --/g' | cut -d " " -f 1)
+  if [ -f "$tlskey" ]; then
+    perms=$(ls -ld "$tlskey" | awk '{print $1}')
+    if [ "$perms" != "-r--------" ]; then
+      fail "Wrong permissions for $tlskey"
+    fi
+  fi
+}
+
+# 3.15
+@test "3.15 - Verify that Docker socket file ownership is set to root:docker" {
+  file="/var/run/docker.sock"
+  if [ -S "$file" ]; then
+    if [ "$(stat -c %U:%G $file)" != 'root:docker' ]; then
+      fail "Wrong ownership for $file"
+    fi
+  fi
+}
+
+# 3.16
+@test "3.16 - Verify that Docker socket file permissions are set to 660" {
+  file="/var/run/docker.sock"
+  if [ -S "$file" ]; then
+    if [ "$(stat -c %a $file)" -ne 660 ]; then
+      fail "Wrong permissions for $file"
+    fi
+  fi
+}
+
+# 3.17
+@test "3.17 - Verify that daemon.json file ownership is set to root:root" {
+  file="/etc/docker/daemon.json"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %U:%G $file)" != 'root:root' ]; then
+      fail "Wrong ownership for $file"
+    fi
+  fi
+}
+
+# 3.18
+@test "3.18 - Verify that daemon.json file permissions are set to 644" {
+  file="/etc/docker/daemon.json"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %a $file)" -ne 644 ]; then
+      fail "Wrong permissions for $file"
+    fi
+  fi
+}
+
+# 3.19
+@test "3.19 - Verify that /etc/default/docker file ownership is set to root:root" {
+  file="/etc/default/docker"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %U:%G $file)" != 'root:root' ]; then
+      fail "Wrong ownership for $file"
+    fi
+  fi
+}
+
+# 3.20
+@test "3.20 - Verify that /etc/default/docker file permissions are set to 644" {
+  file="/etc/default/docker"
+  if [ -f "$file" ]; then
+    if [ "$(stat -c %a $file)" -ne 644 ]; then
+      fail "Wrong permissions for $file"
+    fi
+  fi
+}

From 1fffe849d3d877f8434c7249504fe0e90922da18 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Sun, 15 May 2016 18:01:08 +0300
Subject: [PATCH 18/30] run tests with run_test.sh; automatically generates
 tests per each running container, if needed

---
 bats.Dockerfile                               |  10 +-
 generate_tests.sh                             |  46 +++++++
 run_tests.sh                                  | 114 ++++++++++++++++++
 test/1_host_configuration.bats                |  66 ++++------
 ...4_1_create_user_in_container.bats.template |  18 +++
 test/4_container_images.bats                  |   9 ++
 6 files changed, 217 insertions(+), 46 deletions(-)
 create mode 100644 generate_tests.sh
 create mode 100755 run_tests.sh
 create mode 100644 test/4_1_create_user_in_container.bats.template
 create mode 100644 test/4_container_images.bats

diff --git a/bats.Dockerfile b/bats.Dockerfile
index 376746a..f681f12 100644
--- a/bats.Dockerfile
+++ b/bats.Dockerfile
@@ -7,11 +7,13 @@ MAINTAINER Alexei Ledenev <alexei.led@gmail.com>
 ENV VERSION 1.10.0
 ENV BATS_VERSION 0.4.0
 
+LABEL docker_bench_security=true
+
 WORKDIR /usr/bin
 
 RUN apk update && \
     apk upgrade && \
-    apk --update add curl bash && \
+    apk --update add curl bash ncurses ncurses-terminfo && \
     curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION > docker-$VERSION && \
     curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.sha256 > docker-$VERSION.sha256 && \
     sha256sum -c docker-$VERSION.sha256 && \
@@ -28,7 +30,11 @@ RUN curl -o "/tmp/v${BATS_VERSION}.tar.gz" -L \
 RUN mkdir /docker-bench-security
 
 COPY . /docker-bench-security
+RUN chmod +x /docker-bench-security/run_tests.sh
 
 WORKDIR /docker-bench-security
 
-ENTRYPOINT ["/usr/local/bin/bats", "/docker-bench-security/test"]
+VOLUME /var/docker-bench
+
+CMD ["-r"]
+ENTRYPOINT ["./run_tests.sh"]
diff --git a/generate_tests.sh b/generate_tests.sh
new file mode 100644
index 0000000..b13d9b2
--- /dev/null
+++ b/generate_tests.sh
@@ -0,0 +1,46 @@
+#!/bin/bash
+
+. ./helper_lib.sh
+
+TEST_SRC=./test
+BENCH_ROOT=/var/docker-bench
+TEST_ROOT=$BENCH_ROOT/test
+
+prepare_tests_directory()
+{
+  mkdir -p $BENCH_ROOT
+  if [ -d "$TEST_ROOT" ]; then
+    rm -rf $TEST_ROOT
+  fi
+  if [ ! -f "$BENCH_ROOT/helper_lib.sh" ]; then
+    cp helper_lib.sh $BENCH_ROOT
+  fi
+  cp -r $TEST_SRC $TEST_ROOT
+}
+
+list_running_containers() {
+    # List all running containers
+  local containers=$(docker ps | sed '1d' | awk '{print $NF}')
+  # If there is a container with label docker_bench_security, memorize it:
+  local benchcont="nil"
+  for c in $containers; do
+    labels=$(docker inspect --format '{{ .Config.Labels }}' "$c")
+    contains "$labels" "docker_bench_security" && benchcont="$c"
+  done
+  # List all running containers except docker-bench (use names to improve readability in logs)
+  docker ps -aq --format="{{.Names}}" | grep -v "$benchcont" | tr "\n" " "
+}
+
+generate_all_tests() {
+  # prepare test direcory: copy tests and templates
+  prepare_tests_directory
+  # generate tests from templates for running containers
+  local containers=$(list_running_containers)
+  ( cd $TEST_ROOT
+  for c in ${containers[@]}; do
+    for t in *.bats.template; do
+      sed -e "s/{{c}}/$c/g" "${t}" > "${t%.*.*}_${c}.bats"
+    done
+  done
+  )
+}
diff --git a/run_tests.sh b/run_tests.sh
new file mode 100755
index 0000000..42afa39
--- /dev/null
+++ b/run_tests.sh
@@ -0,0 +1,114 @@
+#!/bin/bash
+
+. ./generate_tests.sh
+
+TERMINFO=/usr/share/terminfo
+
+TEST_RESULTS=$BENCH_ROOT/results
+
+# make result folder (inside VOLUME)
+# mkdir -p $TEST_RESULTS
+# generate all tests: copy host and daemon level tests and generate container level tests for running containers
+# generate_all_tests
+# run bats with all tests or passed tests
+# bats $TEST_ROOT >
+
+#Set Script Name variable
+SCRIPT="run_tests.sh"
+
+#Initialize variables to default values.
+OPT_FORMAT="t"
+OPT_OUTPUT=$TEST_RESULTS
+OPT_RESULTS=1
+
+#Set fonts for Help.
+BOLD=`tput bold`
+REV=`tput smso`
+NORM=`tput sgr0`
+
+#Help function
+HELP() {
+  echo -e \\n"Help documentation for ${BOLD}${SCRIPT}${NORM}"\\n
+  echo -e "Basic usage: ${BOLD}$SCRIPT [-c] [-p|-t] [-o path] <test> [<test> ...]${NORM}"\\n
+  echo -e "Command line switches are optional. The following switches are recognized."
+  echo -e "${REV}-c${NORM}  --Displays number of tests. No further functions are performed."
+  echo -e "${REV}-g${NORM}  --Generates all CIS Bats tests without execution. No further functions are performed."
+  echo -e "${REV}-p${NORM}  --Show results in pretty format."
+  echo -e "${REV}-t${NORM}  --Show results in TAP format. This is the default format."
+  echo -e "${REV}-t${NORM}  --Create test results files: ${BOLD}tests_<timestamp>.tap${NORM} in test result folder."
+  echo -e "${REV}-o${NORM}  --Specify test result folder. Default to ${BOLD}$TEST_RESULTS${NORM}."
+  echo -e "${REV}-h${NORM}  --Displays this help message. No further functions are performed."\\n
+  echo -e "Example: ${BOLD}$SCRIPT -t -o $TEST_RESULTS${NORM}"\\n
+  exit 1
+}
+
+#Check the number of arguments. If none are passed, print help and exit.
+NUMARGS=$#
+if [ $NUMARGS -eq 0 ]; then
+  HELP
+fi
+
+### Start getopts code ###
+
+#Parse command line flags
+while getopts o:rptcgh FLAG; do
+  case $FLAG in
+    o)  # output test results into specified folder
+      OPT_OUTPUT=$OPTARG
+      ;;
+    p)  # output test results in TAP format
+      OPT_FORMAT="p"
+      ;;
+    t)  # output test results in pretty format
+      OPT_FORMAT="t"
+      ;;
+    r)  # save test results into file
+      OPT_RESULTS=0
+      ;;
+    c)  # count tests
+      if [ -d "$TEST_ROOT" ]; then
+        echo -e "There are ${BOLD}$(bats $TEST_ROOT -c)${NORM} tests in ${BOLD}$TEST_ROOT${NORM}"
+      else
+        echo -e "No tests found, run ${BOLD}${SCRIPT}${NORM} with ${REV}-g${NORM} option first."
+      fi
+      exit 1
+      ;;
+    g)  # genetate all Bats tests: copy tests and generate tests (per container) from templates
+      generate_all_tests
+      exit 1
+      ;;
+    h)  #show help
+      HELP
+      ;;
+    \?) #unrecognized option - show help
+      echo -e \\n"Option -${BOLD}$OPTARG${NORM} not allowed."
+      HELP
+      ;;
+  esac
+done
+
+shift $((OPTIND-1))  #This tells getopts to move on to the next argument.
+
+### End getopts code ###
+
+### Run Bats tests ###
+
+TESTS=$TEST_ROOT
+if [ ! -d $TEST_ROOT ]; then # generate tests if needed
+  generate_all_tests
+fi
+
+if [ $# -ne 0 ]; then # get tests from command line
+  TESTS=$*
+fi
+
+if [ $OPT_RESULTS -eq 0 ]; then # run tests and [create test result file]
+  if [ ! -d "$OPT_OUTPUT" ]; then
+    mkdir -p "$OPT_OUTPUT"
+  fi
+  bats $TESTS -${OPT_FORMAT} > "${OPT_OUTPUT}/tests_$(date +%s).tap"
+else
+  bats $TESTS -${OPT_FORMAT}
+fi
+
+exit 0
diff --git a/test/1_host_configuration.bats b/test/1_host_configuration.bats
index 62ad15b..97eee29 100644
--- a/test/1_host_configuration.bats
+++ b/test/1_host_configuration.bats
@@ -70,9 +70,8 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
   assert_success
 }
 
-# 1.8
-@test "1.8  - Audit Docker files and directories - /var/lib/docker" {
-  directory="/var/lib/docker"
+test_audit_directory() {
+  local directory="$1"
   assert [ -d "$directory" ]
   run command -v auditctl >/dev/null
   assert_success
@@ -80,72 +79,51 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
   assert_success
 }
 
-# 1.9
-@test "1.9  - Audit Docker files and directories - /etc/docker" {
-  directory="/etc/docker"
-  assert [ -d "$directory" ]
+test_audit_file() {
+  file="$1"
+  assert [ -f "$file" ]
   run command -v auditctl
   assert_success
-  run auditctl -l | grep $directory
+  run auditctl -l | grep "$file"
   assert_success
 }
 
+# 1.8
+@test "1.8  - Audit Docker files and directories - /var/lib/docker" {
+  test_audit_directory "/var/lib/docker"
+}
+
+# 1.9
+@test "1.9  - Audit Docker files and directories - /etc/docker" {
+  test_audit_directory "/etc/docker"
+}
+
 # 1.10
 @test "1.10 - Audit Docker files and directories - docker.service" {
-  file="$(get_systemd_service_file docker.service)"
-  assert [ -f "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep "$file"
-  assert_success
+  test_audit_file "$(get_systemd_service_file docker.service)"
 }
 
 # 1.11
 @test "1.11 - Audit Docker files and directories - docker.socket" {
-  file="$(get_systemd_service_file docker.socket)"
-  assert [ -e "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep "$file"
-  assert_success
+  test_audit_file "$(get_systemd_service_file docker.socket)"
 }
 
 # 1.12
 @test "1.12 - Audit Docker files and directories - /etc/default/docker" {
-  file="/etc/default/docker"
-  assert [ -f "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep $file
-  assert_success
+  test_audit_file "/etc/default/docker"
 }
 
 # 1.13
 @test "1.13 - Audit Docker files and directories - /etc/docker/daemon.json" {
-  file="/etc/docker/daemon.json"
-  assert [ -f "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep $file
-  assert_success
+  test_audit_file "/etc/docker/daemon.json"
 }
 
 # 1.14
 @test "1.14 - Audit Docker files and directories - /usr/bin/docker-containerd" {
-  file="/usr/bin/docker-containerd"
-  assert [ -f "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep $file
-  assert_success
+  test_audit_file "/usr/bin/docker-containerd"
 }
 
 # 1.15
 @test "1.15 - Audit Docker files and directories - /usr/bin/docker-runc" {
-  file="/usr/bin/docker-runc"
-  assert [ -f "$file" ]
-  run command -v auditctl
-  assert_success
-  run auditctl -l | grep $file
-  assert_success
+  test_audit_file "/usr/bin/docker-runc"
 }
diff --git a/test/4_1_create_user_in_container.bats.template b/test/4_1_create_user_in_container.bats.template
new file mode 100644
index 0000000..ff67ab2
--- /dev/null
+++ b/test/4_1_create_user_in_container.bats.template
@@ -0,0 +1,18 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
+
+
+# 4.1
+@test "4.1  - Create a user for the container {{c}}" {
+  local user=$(docker inspect --format 'User={{.Config.User}}' "{{c}}")
+  if [ "$user" = "User=" -o "$user" = "User=[]" -o "$user" = "User=<no value>" ]; then
+    # get PID 1 and check if it's running as root (uid=0)
+    local uid=$(docker exec {{c}} awk '/^Uid:/{print $2}' /proc/1/status)
+    if [ $uid -eq 0 ]; then
+      fail "Running as root: {{c}}"
+    fi
+  fi
+}
diff --git a/test/4_container_images.bats b/test/4_container_images.bats
new file mode 100644
index 0000000..6f2de10
--- /dev/null
+++ b/test/4_container_images.bats
@@ -0,0 +1,9 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+
+# 4.5
+@test "4.5  - Enable Content trust for Docker" {
+  assert [ "x$DOCKER_CONTENT_TRUST" = "x1" ]
+}

From c29c5ed566882bc84362dca022632f8de2b67bbb Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Mon, 16 May 2016 17:44:58 +0300
Subject: [PATCH 19/30] fix/ignore non working xterm (due to /etc volume);
 download latest docker

---
 bats.Dockerfile | 30 ++++++++++++++----------------
 run_tests.sh    | 12 +++++++++---
 2 files changed, 23 insertions(+), 19 deletions(-)

diff --git a/bats.Dockerfile b/bats.Dockerfile
index f681f12..af980b9 100644
--- a/bats.Dockerfile
+++ b/bats.Dockerfile
@@ -4,28 +4,26 @@ FROM alpine:3.3
 MAINTAINER dockerbench.com
 MAINTAINER Alexei Ledenev <alexei.led@gmail.com>
 
-ENV VERSION 1.10.0
+ENV VERSION 1.11.1
 ENV BATS_VERSION 0.4.0
 
 LABEL docker_bench_security=true
 
-WORKDIR /usr/bin
+RUN apk --update add curl bash \
+    && rm -rf /var/lib/apt/lists/* \
+    && rm /var/cache/apk/*
 
-RUN apk update && \
-    apk upgrade && \
-    apk --update add curl bash ncurses ncurses-terminfo && \
-    curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION > docker-$VERSION && \
-    curl -sS https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.sha256 > docker-$VERSION.sha256 && \
-    sha256sum -c docker-$VERSION.sha256 && \
-    ln -s docker-$VERSION docker && \
-    chmod u+x docker-$VERSION && \
-    rm -rf /var/cache/apk/*
+RUN curl -o "/tmp/docker-$VERSION.tgz" -LS "https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.tgz" && \
+    curl -o "/tmp/docker-$VERSION.tgz.sha256" -LS "https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.tgz.sha256" && \
+    cd /tmp && sha256sum -c docker-$VERSION.tgz.sha256 && \
+    tar -xvzf "/tmp/docker-$VERSION.tgz" -C /tmp/ && \
+    chmod u+x /tmp/docker/docker && mv /tmp/docker/docker /usr/bin/ && \
+    rm -rf /tmp/*
 
-RUN curl -o "/tmp/v${BATS_VERSION}.tar.gz" -L \
-		"https://github.com/sstephenson/bats/archive/v${BATS_VERSION}.tar.gz" \
-	&& tar -x -z -f "/tmp/v${BATS_VERSION}.tar.gz" -C /tmp/ \
-	&& bash "/tmp/bats-${BATS_VERSION}/install.sh" /usr/local \
-	&& rm -rf /tmp/*
+RUN curl -o "/tmp/v${BATS_VERSION}.tar.gz" -LS "https://github.com/sstephenson/bats/archive/v${BATS_VERSION}.tar.gz" && \
+    tar -xvzf "/tmp/v${BATS_VERSION}.tar.gz" -C /tmp/ && \
+    bash "/tmp/bats-${BATS_VERSION}/install.sh" /usr/local && \
+    rm -rf /tmp/*
 
 RUN mkdir /docker-bench-security
 
diff --git a/run_tests.sh b/run_tests.sh
index 42afa39..b1c4afd 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -22,9 +22,15 @@ OPT_OUTPUT=$TEST_RESULTS
 OPT_RESULTS=1
 
 #Set fonts for Help.
-BOLD=`tput bold`
-REV=`tput smso`
-NORM=`tput sgr0`
+if [ -e "/usr/bin/tput" ]; then
+  BOLD=`tput bold`
+  REV=`tput smso`
+  NORM=`tput sgr0`
+else
+  BOLD=""
+  REV=""
+  NORM=""
+fi
 
 #Help function
 HELP() {

From ce64bc1cf39f381b610a167486f92bf16b9e24a8 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Mon, 16 May 2016 18:05:06 +0300
Subject: [PATCH 20/30] update README.md file with info about running Bats
 tests

---
 README.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 52 insertions(+)

diff --git a/README.md b/README.md
index 9819c8b..aa1bb9c 100644
--- a/README.md
+++ b/README.md
@@ -28,6 +28,58 @@ Also note that the default image and `Dockerfile` uses `FROM: alpine` which does
 
 The [distribution specific Dockerfiles](https://github.com/docker/docker-bench-security/tree/master/distros) may also help if the distribution you're using haven't yet shipped Docker version 1.10.0 or later.
 
+## Running Docker Bench Bats tests
+
+[Bats](https://github.com/sstephenson/bats) is a [TAP](http://testanything.org/)-compliant testing framework for Bash. It provides a simple way to verify that the UNIX programs you write behave as expected.
+
+All Docker Bench scipts are also available as Bats tests. Also container level (and image level) tests are automatically generated for all containers avaiable on host. It's possible to run all or only selected test(s), if you like.
+
+By default TAP test results are reported, but it's possible to produce a "pretty" printed output too.
+
+Use the following command to run Docker Bench Bats tests:
+
+```
+Help documentation for run_tests.sh
+
+Basic usage: run_tests.sh [-c] [-p|-t] [-o path] <test> [<test> ...]
+
+Command line switches are optional. The following switches are recognized.
+-c  --Displays number of tests. No further functions are performed.
+-g  --Generates all CIS Bats tests without execution. No further functions are performed.
+-p  --Show results in pretty format.
+-t  --Show results in TAP format. This is the default format.
+-t  --Create test results files: tests_<timestamp>.tap in test result folder.
+-o  --Specify test result folder. Default to /var/docker-bench/results.
+-h  --Displays this help message. No further functions are performed.
+
+Example: run_tests.sh -t -o /var/docker-bench/results
+```
+
+You need to run `run_tests.sh` on Docker host as `root` user.
+
+### Running Docker Bench Bats tests from Docker image
+
+First, clone and compile your `docker-bench-tests` Docker image.
+
+```sh
+git clone https://github.com/gaia-adm/docker-bench-security.git
+cd docker-bench-security
+docker build -t docker-bench-tests -f bats.Dockerfile .
+```
+
+Then run `docker-bench-tests` container (as bellow). Test results will be saved into `/var/docker-bench` folder in TAP format. Test results file is named accoring to the `test_<timestamp>.tap` pattern.
+
+```sh
+docker run -it --net host --pid host --cap-add audit_control \
+    -v /var/lib:/var/lib \
+    -v /var/run/docker.sock:/var/run/docker.sock \
+    -v /usr/lib/systemd:/usr/lib/systemd \
+    -v /var/docker-bench:/var/docker-bench
+    -v /etc:/etc --label docker_bench_security \
+    docker-bench-tests
+```
+>>>>>>> update README.md file with info about running Bats tests
+
 ## Building Docker Bench for Security
 
 If you wish to build and run this container yourself, you can follow the following steps:

From 25adacbe5cd1dfcd454ed2317794d6cbb58d2330 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Tue, 17 May 2016 13:17:03 +0300
Subject: [PATCH 21/30] converted tests for section 5 and 6

---
 .gitmodules                                   |   6 -
 .../1_host_configuration.bats                 |   0
 .../2_docker_daemon_configuration.bats        |   0
 .../3_docker_daemon_configuration_files.bats  |   0
 ...4_1_create_user_in_container.bats.template |   2 +-
 {test => bats_tests}/4_container_images.bats  |   0
 bats_tests/5_container_runtime.bats.template  | 202 ++++++++++++++++++
 bats_tests/6_docker_security_operations.bats  |  36 ++++
 generate_tests.sh                             |   4 +-
 test/test_helper/bats-assert                  |   1 -
 test/test_helper/bats-support                 |   1 -
 11 files changed, 241 insertions(+), 11 deletions(-)
 delete mode 100644 .gitmodules
 rename {test => bats_tests}/1_host_configuration.bats (100%)
 rename {test => bats_tests}/2_docker_daemon_configuration.bats (100%)
 rename {test => bats_tests}/3_docker_daemon_configuration_files.bats (100%)
 rename {test => bats_tests}/4_1_create_user_in_container.bats.template (90%)
 rename {test => bats_tests}/4_container_images.bats (100%)
 create mode 100644 bats_tests/5_container_runtime.bats.template
 create mode 100644 bats_tests/6_docker_security_operations.bats
 delete mode 160000 test/test_helper/bats-assert
 delete mode 160000 test/test_helper/bats-support

diff --git a/.gitmodules b/.gitmodules
deleted file mode 100644
index dc270e5..0000000
--- a/.gitmodules
+++ /dev/null
@@ -1,6 +0,0 @@
-[submodule "test/test_helper/bats-support"]
-	path = test/test_helper/bats-support
-	url = https://github.com/ztombol/bats-support
-[submodule "test/test_helper/bats-assert"]
-	path = test/test_helper/bats-assert
-	url = https://github.com/ztombol/bats-assert
diff --git a/test/1_host_configuration.bats b/bats_tests/1_host_configuration.bats
similarity index 100%
rename from test/1_host_configuration.bats
rename to bats_tests/1_host_configuration.bats
diff --git a/test/2_docker_daemon_configuration.bats b/bats_tests/2_docker_daemon_configuration.bats
similarity index 100%
rename from test/2_docker_daemon_configuration.bats
rename to bats_tests/2_docker_daemon_configuration.bats
diff --git a/test/3_docker_daemon_configuration_files.bats b/bats_tests/3_docker_daemon_configuration_files.bats
similarity index 100%
rename from test/3_docker_daemon_configuration_files.bats
rename to bats_tests/3_docker_daemon_configuration_files.bats
diff --git a/test/4_1_create_user_in_container.bats.template b/bats_tests/4_1_create_user_in_container.bats.template
similarity index 90%
rename from test/4_1_create_user_in_container.bats.template
rename to bats_tests/4_1_create_user_in_container.bats.template
index ff67ab2..8e1fbaf 100644
--- a/test/4_1_create_user_in_container.bats.template
+++ b/bats_tests/4_1_create_user_in_container.bats.template
@@ -6,7 +6,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 
 
 # 4.1
-@test "4.1  - Create a user for the container {{c}}" {
+@test "4.1  - Create a user for the container: {{c}}" {
   local user=$(docker inspect --format 'User={{.Config.User}}' "{{c}}")
   if [ "$user" = "User=" -o "$user" = "User=[]" -o "$user" = "User=<no value>" ]; then
     # get PID 1 and check if it's running as root (uid=0)
diff --git a/test/4_container_images.bats b/bats_tests/4_container_images.bats
similarity index 100%
rename from test/4_container_images.bats
rename to bats_tests/4_container_images.bats
diff --git a/bats_tests/5_container_runtime.bats.template b/bats_tests/5_container_runtime.bats.template
new file mode 100644
index 0000000..3bbf13f
--- /dev/null
+++ b/bats_tests/5_container_runtime.bats.template
@@ -0,0 +1,202 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
+
+# 5.1
+@test "5.1  - Verify AppArmor Profile, if applicable: {{c}}" {
+  policy=$(docker inspect --format 'AppArmorProfile={{ .AppArmorProfile }}' "{{c}}")
+  if [ "$policy" = "AppArmorProfile=" -o "$policy" = "AppArmorProfile=[]" -o "$policy" = "AppArmorProfile=<no value>" ]; then
+    fail "No AppArmorProfile Found: {{c}}"
+  fi
+}
+
+# 5.2
+@test "5.2  - Verify SELinux security options, if applicable: {{c}}" {
+  policy=$(docker inspect --format 'SecurityOpt={{ .HostConfig.SecurityOpt }}' "{{c}}")
+  if [ "$policy" = "SecurityOpt=" -o "$policy" = "SecurityOpt=[]" -o "$policy" = "SecurityOpt=<no value>" ]; then
+    fail "No SecurityOptions Found: {{c}}"
+  fi
+}
+
+# 5.3
+@test "5.3  - Restrict Linux Kernel Capabilities within containers: {{c}}" {
+  caps=$(docker inspect --format 'CapAdd={{ .HostConfig.CapAdd}}' "{{c}}")
+  if [ "$caps" != 'CapAdd=' -a "$caps" != 'CapAdd=[]' -a "$caps" != 'CapAdd=<no value>' -a "$caps" != 'CapAdd=<nil>' ]; then
+    fail "Capabilities added: $caps to {{c}}"
+  fi
+}
+
+# 5.4
+@test "5.4  - Do not use privileged containers: {{c}}" {
+  privileged=$(docker inspect --format '{{ .HostConfig.Privileged }}' "{{c}}")
+  if [ "$privileged" = "true" ]; then
+    fail "Container running in Privileged mode: {{c}}"
+  fi
+}
+
+# 5.5
+@test "5.5  - Do not mount sensitive host system directories on containers: {{c}}" {
+  # List of sensitive directories to test for. Script uses new-lines as a separator.
+  # Note the lack of identation. It needs it for the substring comparison.
+  sensitive_dirs=(/boot /dev /etc /lib /proc /sys /usr)
+  run docker inspect --format '{{ .VolumesRW }}' "{{c}}"
+  if [ $status -eq 0 ]; then
+    volumes=$(docker inspect --format '{{ .VolumesRW }}' "{{c}}")
+  else
+    volumes=$(docker inspect --format '{{ .Mounts }}' "{{c}}")
+  fi
+  # Go over each directory in sensitive dir and see if they exist in the volumes
+  for v in ${sensitive_dirs[@]}; do
+    run contains "$volumes" "$v"
+    if [ $status -eq 0 ]; then
+      fail "Sensitive directory $v mounted in: {{c}}"
+    fi
+  done
+}
+
+# 5.7
+@test "5.7  - Do not map privileged ports within containers: {{c}}" {
+  # Port format is private port -> ip: public port
+  ports=$(docker port "{{c}}" | awk '{print $3}' | cut -d ':' -f2 | tr "\n" " ")
+  # iterate through port range (line delimited)
+  for port in $ports; do
+    if [ ! -z "$port" ] && [ "0$port" -lt 1024 ]; then
+      fail "Privileged Port in use: $port in {{c}}"
+    fi
+  done
+}
+
+# 5.9
+@test "5.9 - Do not share the host's network namespace: {{c}}" {
+  mode=$(docker inspect --format 'NetworkMode={{ .HostConfig.NetworkMode }}' "{{c}}")
+  if [ "$mode" = "NetworkMode=host" ]; then
+    fail "Container running with networking mode 'host': {{c}}"
+  fi
+}
+
+# 5.10
+@test "5.10 - Limit memory usage for container: {{c}}" {
+  run docker inspect --format '{{ .Config.Memory }}' "{{c}}"
+  if [ $status -eq 0 ]; then
+    memory=$(docker inspect --format '{{ .Config.Memory }}' "{{c}}")
+  else
+    memory=$(docker inspect --format '{{ .HostConfig.Memory }}' "{{c}}")
+  fi
+  if [ "$memory" = "0" ]; then
+    fail "Container running without memory restrictions: {{c}}"
+  fi
+}
+
+# 5.11
+@test "5.11 - Set container CPU priority appropriately: {{c}}" {
+  run docker inspect --format '{{ .Config.CpuShares }}' "{{c}}"
+  if [ $status -eq 0 ]; then
+    shares=$(docker inspect --format '{{ .Config.CpuShares }}' "{{c}}")
+  else
+    shares=$(docker inspect --format '{{ .HostConfig.CpuShares }}' "{{c}}")
+  fi
+  if [ "$shares" = "0" ]; then
+    fail "Container running without CPU restrictions: {{c}}"
+  fi
+}
+
+# 5.12
+@test "5.12 - Mount container's root filesystem as read only: {{c}}" {
+  read_status=$(docker inspect --format '{{ .HostConfig.ReadonlyRootfs }}' "{{c}}")
+  if [ "$read_status" = "false" ]; then
+    fail "Container running with root FS mounted R/W: {{c}}"
+  fi
+}
+
+# 5.13
+@test "5.13 - Bind incoming container traffic to a specific host interface: {{c}}" {
+  for ip in $(docker port "{{c}}" | awk '{print $3}' | cut -d ':' -f1 | tr "\n" " "); do
+    if [ "$ip" = "0.0.0.0" ]; then
+      fail "Port being bound to wildcard IP: $ip in {{c}}"
+    fi
+  done
+}
+
+# 5.14
+@test "5.14 - Set the 'on-failure' container restart policy to 5: {{c}}" {
+  policy=$(docker inspect --format MaximumRetryCount='{{ .HostConfig.RestartPolicy.MaximumRetryCount }}' "{{c}}")
+  if [ "$policy" != "MaximumRetryCount=5" ]; then
+    fail "MaximumRetryCount is not set to 5: {{c}}"
+  fi
+}
+
+# 5.15
+@test "5.15 - Do not share the host's process namespace: {{c}}" {
+  mode=$(docker inspect --format 'PidMode={{.HostConfig.PidMode }}' "{{c}}")
+  if [ "$mode" = "PidMode=host" ]; then
+    fail "Host PID namespace being shared with: {{c}}"
+  fi
+}
+
+# 5.16
+@test "5.16 - Do not share the host's IPC namespace: {{c}}" {
+  mode=$(docker inspect --format 'IpcMode={{.HostConfig.IpcMode }}' "{{c}}")
+  if [ "$mode" = "IpcMode=host" ]; then
+    fail "Host IPC namespace being shared with: {{c}}"
+  fi
+}
+
+# 5.17
+@test "5.17 - Do not directly expose host devices to containers: {{c}}" {
+  devices=$(docker inspect --format 'Devices={{ .HostConfig.Devices }}' "{{c}}")
+  if [ "$devices" != "Devices=" -a "$devices" != "Devices=[]" -a "$devices" != "Devices=<no value>" ]; then
+    fail "Container has devices exposed directly: {{c}}"
+  fi
+}
+
+# 5.18
+@test "5.18 - Override default ulimit at runtime only if needed: {{c}}" {
+  ulimits=$(docker inspect --format 'Ulimits={{ .HostConfig.Ulimits }}' "{{c}}")
+  if [ "$ulimits" = "Ulimits=" -o "$ulimits" = "Ulimits=[]" -o "$ulimits" = "Ulimits=<no value>" ]; then
+    fail "Container no default ulimit override: {{c}}"
+  fi
+}
+
+# 5.19
+@test "5.19 - Do not set mount propagation mode to shared: {{c}}" {
+  mode=$(docker inspect --format 'Propagation={{range $mnt := .Mounts}} {{json $mnt.Propagation}} {{end}}' "{{c}}")
+  if [ "$mode" = "Propagation=shared" ]; then
+    fail "Mount propagation mode is shared: {{c}}"
+  fi
+}
+
+# 5.20
+@test "5.20 - Do not share the host's UTS namespace: {{c}}" {
+  mode=$(docker inspect --format 'UTSMode={{.HostConfig.UTSMode }}' "{{c}}")
+  if [ "$mode" = "UTSMode=host" ]; then
+    fail "Host UTS namespace being shared with: {{c}}"
+  fi
+}
+
+# 5.21
+@test "5.21 - Do not disable default seccomp profile: {{c}}" {
+  result=$(docker inspect --format 'SecurityOpt={{.HostConfig.SecurityOpt }}' "{{c}}")
+  run grep "seccomp:unconfined" <<< "$result"
+  if [ $status -eq 0 ]; then
+    fail "Default seccomp profile disabled: {{c}}"
+  fi
+}
+
+# 5.24
+@test "5.24 - Confirm cgroup usage: {{c}}" {
+  mode=$(docker inspect --format 'CgroupParent={{.HostConfig.CgroupParent }}x' "{{c}}")
+  if [ "$mode" != "CgroupParent=x" ]; then
+    fail "Confirm cgroup usage: {{c}}"
+  fi
+}
+
+# 5.25
+@test "5.25 - Restrict container from acquiring additional privileges: {{c}}" {
+  result=$(docker inspect --format 'SecurityOpt={{.HostConfig.SecurityOpt }}' "{{c}}")
+  run grep "no-new-privileges" <<< "$result"
+  if [ $status -ne 0 ]; then
+    fail "Privileges not restricted: {{c}}"
+  fi
+}
diff --git a/bats_tests/6_docker_security_operations.bats b/bats_tests/6_docker_security_operations.bats
new file mode 100644
index 0000000..c4d9f43
--- /dev/null
+++ b/bats_tests/6_docker_security_operations.bats
@@ -0,0 +1,36 @@
+#!/usr/bin/env bats
+
+load "test_helper/bats-support/load"
+load "test_helper/bats-assert/load"
+load "$BATS_TEST_DIRNAME/../helper_lib.sh"
+
+
+# 6.4
+@test "6.4 - Avoid image sprawl" {
+  images=$(docker images -q | sort -u | wc -l | awk '{print $1}')
+  active_images=0
+
+  for c in $(docker inspect -f "{{.Image}}" $(docker ps -qa)); do
+    if docker images --no-trunc -a | grep "$c" > /dev/null ; then
+      active_images=$(( active_images += 1 ))
+    fi
+  done
+
+  if [ "$images" -gt 100 ]; then
+    fail "There are currently: $images images"
+  fi
+
+  if [ "$active_images" -lt "$((images / 2))" ]; then
+    fail "Only $active_images out of $images are in use"
+  fi
+}
+
+# 6.5
+@test "6.5 - Avoid container sprawl" {
+  total_containers=$(docker info 2>/dev/null | grep "Containers" | awk '{print $2}')
+  running_containers=$(docker ps -q | wc -l | awk '{print $1}')
+  diff="$((total_containers - running_containers))"
+  if [ "$diff" -gt 25 ]; then
+    fail "There are currently a total of $total_containers containers, with only $running_containers of them currently running"
+  fi
+}
diff --git a/generate_tests.sh b/generate_tests.sh
index b13d9b2..908cbfa 100644
--- a/generate_tests.sh
+++ b/generate_tests.sh
@@ -2,9 +2,9 @@
 
 . ./helper_lib.sh
 
-TEST_SRC=./test
+TEST_SRC=./bats_tests
 BENCH_ROOT=/var/docker-bench
-TEST_ROOT=$BENCH_ROOT/test
+TEST_ROOT=$BENCH_ROOT/bats_tests
 
 prepare_tests_directory()
 {
diff --git a/test/test_helper/bats-assert b/test/test_helper/bats-assert
deleted file mode 160000
index 9f88b42..0000000
--- a/test/test_helper/bats-assert
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 9f88b4207da750093baabc4e3f41bf68f0dd3630
diff --git a/test/test_helper/bats-support b/test/test_helper/bats-support
deleted file mode 160000
index d0a1318..0000000
--- a/test/test_helper/bats-support
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit d0a131831c487a1f1141e76d3ab386c89642cdff

From 96d841d414f8a1d4289cfe549a0129b22cf6b21f Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Tue, 17 May 2016 13:20:08 +0300
Subject: [PATCH 22/30] fix submodules path rename

---
 .gitmodules | 6 ++++++
 1 file changed, 6 insertions(+)
 create mode 100644 .gitmodules

diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..b18fe2f
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,6 @@
+[submodule "bats_tests/test_helper/bats-assert"]
+	path = bats_tests/test_helper/bats-assert
+	url = https://github.com/ztombol/bats-assert
+[submodule "bats_tests/test_helper/bats-support"]
+	path = bats_tests/test_helper/bats-support
+	url = https://github.com/ztombol/bats-support

From a913b25ed347c0d3557057da3d1b6d95ee760721 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.ledenev@hp.com>
Date: Tue, 17 May 2016 16:02:25 +0300
Subject: [PATCH 23/30] shellcheck cleaning

Signed-off-by: Alexei Ledenev <alexei.ledenev@hp.com>
---
 bats_tests/1_host_configuration.bats          |  6 ++---
 .../3_docker_daemon_configuration_files.bats  | 12 +++++-----
 ...4_1_create_user_in_container.bats.template |  6 ++---
 bats_tests/5_container_runtime.bats.template  | 12 +++++-----
 generate_tests.sh                             | 10 ++++-----
 run_tests.sh                                  | 22 +++++++++----------
 6 files changed, 33 insertions(+), 35 deletions(-)

diff --git a/bats_tests/1_host_configuration.bats b/bats_tests/1_host_configuration.bats
index 97eee29..6ed2e6e 100644
--- a/bats_tests/1_host_configuration.bats
+++ b/bats_tests/1_host_configuration.bats
@@ -48,9 +48,9 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
   declare -a trusted_users=("vagrant" "docker" "ubuntu")
   users_string=$(awk -F':' '/^docker/{print $4}' /etc/group)
   docker_users=(${users_string//,/ })
-  for u in ${docker_users[@]}; do
+  for u in "${docker_users[@]}"; do
     local found=1
-    for tu in ${trusted_users[@]}; do
+    for tu in "${trusted_users[@]}"; do
       if [ "$u" = "$tu" ]; then
         found=0
       fi
@@ -75,7 +75,7 @@ test_audit_directory() {
   assert [ -d "$directory" ]
   run command -v auditctl >/dev/null
   assert_success
-  run auditctl -l | grep $directory
+  run auditctl -l | grep "$directory"
   assert_success
 }
 
diff --git a/bats_tests/3_docker_daemon_configuration_files.bats b/bats_tests/3_docker_daemon_configuration_files.bats
index de99861..cec8963 100644
--- a/bats_tests/3_docker_daemon_configuration_files.bats
+++ b/bats_tests/3_docker_daemon_configuration_files.bats
@@ -8,7 +8,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 @test "3.1  - Verify that docker.service file ownership is set to root:root" {
   file="$(get_systemd_service_file docker.service)"
   if [ -f "$file" ]; then
-    if [ "$(stat -c %u%g $file)" -ne 00 ]; then
+    if [ "$(stat -c %u%g "$file")" -ne 00 ]; then
       fail "Wrong ownership for $file"
     fi
   fi
@@ -18,7 +18,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 @test "3.2  - Verify that docker.service file permissions are set to 644" {
   file="$(get_systemd_service_file docker.service)"
   if [ -f "$file" ]; then
-    if [ "$(stat -c %a $file)" -ne 644 ]; then
+    if [ "$(stat -c %a "$file")" -ne 644 ]; then
       fail "Wrong permissions for $file"
     fi
   fi
@@ -28,7 +28,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 @test "3.3  - Verify that docker.socket file ownership is set to root:root" {
   file="$(get_systemd_service_file docker.socket)"
   if [ -f "$file" ]; then
-    if [ "$(stat -c %u%g $file)" -ne 00 ]; then
+    if [ "$(stat -c %u%g "$file")" -ne 00 ]; then
       fail "Wrong ownership for $file"
     fi
   fi
@@ -38,7 +38,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 @test "3.4  - Verify that docker.socket file permissions are set to 644" {
   file="$(get_systemd_service_file docker.socket)"
   if [ -f "$file" ]; then
-    if [ "$(stat -c %a $file)" -ne 644 ]; then
+    if [ "$(stat -c %a "$file")" -ne 644 ]; then
       fail "Wrong permissions for $file"
     fi
   fi
@@ -58,7 +58,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 @test "3.6  - Verify that /etc/docker directory permissions are set to 755 or 700" {
   directory="/etc/docker"
   if [ -d "$directory" ]; then
-    if [ "$(stat -c %a $directory)" -ne 755 -a "$(stat -c %a $directory)" -ne 700 ]; then
+    if [ "$(stat -c %a $directory)" -ne 755 ] && [ "$(stat -c %a $directory)" -ne 700 ]; then
       fail "Wrong permissions for $directory : $(stat -c %a $directory)"
     fi
   fi
@@ -89,7 +89,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
     fail=0
     perms=$(ls -lL $directory | grep ".crt" | awk '{print $1}')
     for p in $perms; do
-      if [ "$p" != "-r--r--r--." -a "$p" = "-r--------." ]; then
+      if [ "$p" != "-r--r--r--." ] && [ "$p" = "-r--------." ]; then
         fail=1
       fi
     done
diff --git a/bats_tests/4_1_create_user_in_container.bats.template b/bats_tests/4_1_create_user_in_container.bats.template
index 8e1fbaf..f3a2be8 100644
--- a/bats_tests/4_1_create_user_in_container.bats.template
+++ b/bats_tests/4_1_create_user_in_container.bats.template
@@ -7,10 +7,10 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 
 # 4.1
 @test "4.1  - Create a user for the container: {{c}}" {
-  local user=$(docker inspect --format 'User={{.Config.User}}' "{{c}}")
-  if [ "$user" = "User=" -o "$user" = "User=[]" -o "$user" = "User=<no value>" ]; then
+  user=$(docker inspect --format 'User={{.Config.User}}' "{{c}}")
+  if [ "$user" = "User=" ] || [ "$user" = "User=[]" ] || [ "$user" = "User=<no value>" ]; then
     # get PID 1 and check if it's running as root (uid=0)
-    local uid=$(docker exec {{c}} awk '/^Uid:/{print $2}' /proc/1/status)
+    uid=$(docker exec {{c}} awk '/^Uid:/{print $2}' /proc/1/status)
     if [ $uid -eq 0 ]; then
       fail "Running as root: {{c}}"
     fi
diff --git a/bats_tests/5_container_runtime.bats.template b/bats_tests/5_container_runtime.bats.template
index 3bbf13f..22f73e0 100644
--- a/bats_tests/5_container_runtime.bats.template
+++ b/bats_tests/5_container_runtime.bats.template
@@ -7,7 +7,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 5.1
 @test "5.1  - Verify AppArmor Profile, if applicable: {{c}}" {
   policy=$(docker inspect --format 'AppArmorProfile={{ .AppArmorProfile }}' "{{c}}")
-  if [ "$policy" = "AppArmorProfile=" -o "$policy" = "AppArmorProfile=[]" -o "$policy" = "AppArmorProfile=<no value>" ]; then
+  if [ "$policy" = "AppArmorProfile=" ] || [ "$policy" = "AppArmorProfile=[]" ] || [ "$policy" = "AppArmorProfile=<no value>" ]; then
     fail "No AppArmorProfile Found: {{c}}"
   fi
 }
@@ -15,7 +15,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 5.2
 @test "5.2  - Verify SELinux security options, if applicable: {{c}}" {
   policy=$(docker inspect --format 'SecurityOpt={{ .HostConfig.SecurityOpt }}' "{{c}}")
-  if [ "$policy" = "SecurityOpt=" -o "$policy" = "SecurityOpt=[]" -o "$policy" = "SecurityOpt=<no value>" ]; then
+  if [ "$policy" = "SecurityOpt=" ] || [ "$policy" = "SecurityOpt=[]" ] || [ "$policy" = "SecurityOpt=<no value>" ]; then
     fail "No SecurityOptions Found: {{c}}"
   fi
 }
@@ -23,7 +23,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 5.3
 @test "5.3  - Restrict Linux Kernel Capabilities within containers: {{c}}" {
   caps=$(docker inspect --format 'CapAdd={{ .HostConfig.CapAdd}}' "{{c}}")
-  if [ "$caps" != 'CapAdd=' -a "$caps" != 'CapAdd=[]' -a "$caps" != 'CapAdd=<no value>' -a "$caps" != 'CapAdd=<nil>' ]; then
+  if [ "$caps" != 'CapAdd=' ] && [ "$caps" != 'CapAdd=[]' ] && [ "$caps" != 'CapAdd=<no value>' ] && [ "$caps" != 'CapAdd=<nil>' ]; then
     fail "Capabilities added: $caps to {{c}}"
   fi
 }
@@ -48,7 +48,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
     volumes=$(docker inspect --format '{{ .Mounts }}' "{{c}}")
   fi
   # Go over each directory in sensitive dir and see if they exist in the volumes
-  for v in ${sensitive_dirs[@]}; do
+  for v in "${sensitive_dirs[@]}"; do
     run contains "$volumes" "$v"
     if [ $status -eq 0 ]; then
       fail "Sensitive directory $v mounted in: {{c}}"
@@ -146,7 +146,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 5.17
 @test "5.17 - Do not directly expose host devices to containers: {{c}}" {
   devices=$(docker inspect --format 'Devices={{ .HostConfig.Devices }}' "{{c}}")
-  if [ "$devices" != "Devices=" -a "$devices" != "Devices=[]" -a "$devices" != "Devices=<no value>" ]; then
+  if [ "$devices" != "Devices=" ] && [ "$devices" != "Devices=[]" ] && [ "$devices" != "Devices=<no value>" ]; then
     fail "Container has devices exposed directly: {{c}}"
   fi
 }
@@ -154,7 +154,7 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 # 5.18
 @test "5.18 - Override default ulimit at runtime only if needed: {{c}}" {
   ulimits=$(docker inspect --format 'Ulimits={{ .HostConfig.Ulimits }}' "{{c}}")
-  if [ "$ulimits" = "Ulimits=" -o "$ulimits" = "Ulimits=[]" -o "$ulimits" = "Ulimits=<no value>" ]; then
+  if [ "$ulimits" = "Ulimits=" ] || [ "$ulimits" = "Ulimits=[]" ] || [ "$ulimits" = "Ulimits=<no value>" ]; then
     fail "Container no default ulimit override: {{c}}"
   fi
 }
diff --git a/generate_tests.sh b/generate_tests.sh
index 908cbfa..5ef4ac3 100644
--- a/generate_tests.sh
+++ b/generate_tests.sh
@@ -20,10 +20,10 @@ prepare_tests_directory()
 
 list_running_containers() {
     # List all running containers
-  local containers=$(docker ps | sed '1d' | awk '{print $NF}')
+  containers=($(docker ps | sed '1d' | awk '{print $NF}' | tr "\n" " "))
   # If there is a container with label docker_bench_security, memorize it:
   local benchcont="nil"
-  for c in $containers; do
+  for c in "${containers[@]}"; do
     labels=$(docker inspect --format '{{ .Config.Labels }}' "$c")
     contains "$labels" "docker_bench_security" && benchcont="$c"
   done
@@ -35,9 +35,9 @@ generate_all_tests() {
   # prepare test direcory: copy tests and templates
   prepare_tests_directory
   # generate tests from templates for running containers
-  local containers=$(list_running_containers)
-  ( cd $TEST_ROOT
-  for c in ${containers[@]}; do
+  containers=($(list_running_containers))
+  ( cd $TEST_ROOT || exit 1
+  for c in "${containers[@]}"; do
     for t in *.bats.template; do
       sed -e "s/{{c}}/$c/g" "${t}" > "${t%.*.*}_${c}.bats"
     done
diff --git a/run_tests.sh b/run_tests.sh
index b1c4afd..214490b 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -2,8 +2,6 @@
 
 . ./generate_tests.sh
 
-TERMINFO=/usr/share/terminfo
-
 TEST_RESULTS=$BENCH_ROOT/results
 
 # make result folder (inside VOLUME)
@@ -23,9 +21,9 @@ OPT_RESULTS=1
 
 #Set fonts for Help.
 if [ -e "/usr/bin/tput" ]; then
-  BOLD=`tput bold`
-  REV=`tput smso`
-  NORM=`tput sgr0`
+  BOLD=$(tput bold)
+  REV=$(tput smso)
+  NORM=$(tput sgr0)
 else
   BOLD=""
   REV=""
@@ -50,7 +48,7 @@ HELP() {
 
 #Check the number of arguments. If none are passed, print help and exit.
 NUMARGS=$#
-if [ $NUMARGS -eq 0 ]; then
+if [ "$NUMARGS" -eq 0 ]; then
   HELP
 fi
 
@@ -73,7 +71,7 @@ while getopts o:rptcgh FLAG; do
       ;;
     c)  # count tests
       if [ -d "$TEST_ROOT" ]; then
-        echo -e "There are ${BOLD}$(bats $TEST_ROOT -c)${NORM} tests in ${BOLD}$TEST_ROOT${NORM}"
+        echo -e "There are ${BOLD}$(bats "${TEST_ROOT}" -c)${NORM} tests in ${BOLD}${TEST_ROOT}${NORM}"
       else
         echo -e "No tests found, run ${BOLD}${SCRIPT}${NORM} with ${REV}-g${NORM} option first."
       fi
@@ -99,8 +97,8 @@ shift $((OPTIND-1))  #This tells getopts to move on to the next argument.
 
 ### Run Bats tests ###
 
-TESTS=$TEST_ROOT
-if [ ! -d $TEST_ROOT ]; then # generate tests if needed
+TESTS="${TEST_ROOT}"
+if [ ! -d "${TEST_ROOT}" ]; then # generate tests if needed
   generate_all_tests
 fi
 
@@ -108,13 +106,13 @@ if [ $# -ne 0 ]; then # get tests from command line
   TESTS=$*
 fi
 
-if [ $OPT_RESULTS -eq 0 ]; then # run tests and [create test result file]
+if [ ${OPT_RESULTS} -eq 0 ]; then # run tests and [create test result file]
   if [ ! -d "$OPT_OUTPUT" ]; then
     mkdir -p "$OPT_OUTPUT"
   fi
-  bats $TESTS -${OPT_FORMAT} > "${OPT_OUTPUT}/tests_$(date +%s).tap"
+  bats "${TESTS}" -${OPT_FORMAT} > "${OPT_OUTPUT}/tests_$(date +%s).tap"
 else
-  bats $TESTS -${OPT_FORMAT}
+  bats "${TESTS}" -${OPT_FORMAT}
 fi
 
 exit 0

From e074995a2c8572dbd70817bf809919f7a54588c8 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Wed, 18 May 2016 10:53:43 +0300
Subject: [PATCH 24/30] spellcheck error fix

---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 650d3e0..457fbd9 100644
--- a/README.md
+++ b/README.md
@@ -32,7 +32,7 @@ The [distribution specific Dockerfiles](https://github.com/docker/docker-bench-s
 
 [Bats](https://github.com/sstephenson/bats) is a [TAP](http://testanything.org/)-compliant testing framework for Bash. It provides a simple way to verify that the UNIX programs you write behave as expected.
 
-All Docker Bench scipts are also available as Bats tests. Also container level (and image level) tests are automatically generated for all containers avaiable on host. It's possible to run all or only selected test(s), if you like.
+All Docker Bench scipts are also available as Bats tests. Also container level (and image level) tests are automatically generated for all containers available on host. It's possible to run all or only selected test(s), if you like.
 
 By default TAP test results are reported, but it's possible to produce a "pretty" printed output too.
 

From 3775700d05378445c16f959e163562e83bd43583 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Wed, 18 May 2016 10:56:46 +0300
Subject: [PATCH 25/30] cleanup merge confict comment

---
 README.md | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/README.md b/README.md
index 457fbd9..80dbf42 100644
--- a/README.md
+++ b/README.md
@@ -55,7 +55,7 @@ Command line switches are optional. The following switches are recognized.
 Example: run_tests.sh -t -o /var/docker-bench/results
 ```
 
-You need to run `run_tests.sh` on Docker host as `root` user.
+**Note:**: You need to run `run_tests.sh` on Docker host as `root` user.
 
 ### Running Docker Bench Bats tests from Docker image
 
@@ -78,10 +78,6 @@ docker run -it --net host --pid host --cap-add audit_control \
     -v /etc:/etc --label docker_bench_security \
     docker-bench-tests
 ```
-<<<<<<< HEAD
->>>>>>> update README.md file with info about running Bats tests
-=======
->>>>>>> a888600cbb2c08e85ce279b335345e136d529f9b
 
 ## Building Docker Bench for Security
 

From 7ae92494d1a1e84977a5b3d2658dba673e35d275 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Wed, 18 May 2016 12:10:39 +0300
Subject: [PATCH 26/30] verify hash for downloaded Bats archive

---
 bats.Dockerfile | 12 ++++++++----
 1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/bats.Dockerfile b/bats.Dockerfile
index af980b9..224bebe 100644
--- a/bats.Dockerfile
+++ b/bats.Dockerfile
@@ -6,6 +6,7 @@ MAINTAINER Alexei Ledenev <alexei.led@gmail.com>
 
 ENV VERSION 1.11.1
 ENV BATS_VERSION 0.4.0
+ENV BATS_SHA_256 480d8d64f1681eee78d1002527f3f06e1ac01e173b761bc73d0cf33f4dc1d8d7
 
 LABEL docker_bench_security=true
 
@@ -13,6 +14,13 @@ RUN apk --update add curl bash \
     && rm -rf /var/lib/apt/lists/* \
     && rm /var/cache/apk/*
 
+RUN curl -o "/tmp/v${BATS_VERSION}.tar.gz" -LS "https://github.com/sstephenson/bats/archive/v${BATS_VERSION}.tar.gz" && \
+    echo "${BATS_SHA_256}  v${BATS_VERSION}.tar.gz" > /tmp/v${BATS_VERSION}.tar.gz.sha256 && \
+    cd /tmp && sha256sum -c v${BATS_VERSION}.tar.gz.sha256 && \
+    tar -xvzf "/tmp/v${BATS_VERSION}.tar.gz" -C /tmp/ && \
+    bash "/tmp/bats-${BATS_VERSION}/install.sh" /usr/local && \
+    rm -rf /tmp/*
+
 RUN curl -o "/tmp/docker-$VERSION.tgz" -LS "https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.tgz" && \
     curl -o "/tmp/docker-$VERSION.tgz.sha256" -LS "https://get.docker.com/builds/Linux/x86_64/docker-$VERSION.tgz.sha256" && \
     cd /tmp && sha256sum -c docker-$VERSION.tgz.sha256 && \
@@ -20,10 +28,6 @@ RUN curl -o "/tmp/docker-$VERSION.tgz" -LS "https://get.docker.com/builds/Linux/
     chmod u+x /tmp/docker/docker && mv /tmp/docker/docker /usr/bin/ && \
     rm -rf /tmp/*
 
-RUN curl -o "/tmp/v${BATS_VERSION}.tar.gz" -LS "https://github.com/sstephenson/bats/archive/v${BATS_VERSION}.tar.gz" && \
-    tar -xvzf "/tmp/v${BATS_VERSION}.tar.gz" -C /tmp/ && \
-    bash "/tmp/bats-${BATS_VERSION}/install.sh" /usr/local && \
-    rm -rf /tmp/*
 
 RUN mkdir /docker-bench-security
 

From f6cdaa3d3d37ad4063ab25ea7476e3756270e3f5 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Wed, 18 May 2016 12:25:26 +0300
Subject: [PATCH 27/30] external test configuration can be specified in
 0_config file; it's regular bash file (ignored by git), see
 '0_config.example'

---
 .gitignore                           | 1 +
 bats_tests/0_config.example          | 4 ++++
 bats_tests/1_host_configuration.bats | 4 ++--
 3 files changed, 7 insertions(+), 2 deletions(-)
 create mode 100644 bats_tests/0_config.example

diff --git a/.gitignore b/.gitignore
index 397b4a7..cfd3b2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,2 @@
 *.log
+bats_tests/0_config
diff --git a/bats_tests/0_config.example b/bats_tests/0_config.example
new file mode 100644
index 0000000..d95a242
--- /dev/null
+++ b/bats_tests/0_config.example
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+# trusted users that can control Docker daemon
+config_trusted_users=(vagrant docker ubuntu)
diff --git a/bats_tests/1_host_configuration.bats b/bats_tests/1_host_configuration.bats
index 6ed2e6e..034c9ef 100644
--- a/bats_tests/1_host_configuration.bats
+++ b/bats_tests/1_host_configuration.bats
@@ -1,5 +1,6 @@
 #!/usr/bin/env bats
 
+load "0_config"
 load "test_helper/bats-support/load"
 load "test_helper/bats-assert/load"
 load "$BATS_TEST_DIRNAME/../helper_lib.sh"
@@ -45,12 +46,11 @@ load "$BATS_TEST_DIRNAME/../helper_lib.sh"
 
 # 1.6
 @test "1.6  - Only allow trusted users to control Docker daemon" {
-  declare -a trusted_users=("vagrant" "docker" "ubuntu")
   users_string=$(awk -F':' '/^docker/{print $4}' /etc/group)
   docker_users=(${users_string//,/ })
   for u in "${docker_users[@]}"; do
     local found=1
-    for tu in "${trusted_users[@]}"; do
+    for tu in "${config_trusted_users[@]}"; do
       if [ "$u" = "$tu" ]; then
         found=0
       fi

From e68b554e6601acbddfe2d19604f5d636a9148d28 Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Wed, 18 May 2016 12:51:10 +0300
Subject: [PATCH 28/30] fix flag in help (-r for creating test results folder);
 also do not mount whole /etc (in README.md example), but only required sub
 directoris (otherwise term does not work as expected in Alpine)

---
 README.md       | 10 +++++++---
 bats.Dockerfile |  2 +-
 run_tests.sh    |  2 +-
 3 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/README.md b/README.md
index 80dbf42..c93e36a 100644
--- a/README.md
+++ b/README.md
@@ -48,7 +48,7 @@ Command line switches are optional. The following switches are recognized.
 -g  --Generates all CIS Bats tests without execution. No further functions are performed.
 -p  --Show results in pretty format.
 -t  --Show results in TAP format. This is the default format.
--t  --Create test results files: tests_<timestamp>.tap in test result folder.
+-r  --Create test results files: tests_<timestamp>.tap in test result folder.
 -o  --Specify test result folder. Default to /var/docker-bench/results.
 -h  --Displays this help message. No further functions are performed.
 
@@ -74,8 +74,12 @@ docker run -it --net host --pid host --cap-add audit_control \
     -v /var/lib:/var/lib \
     -v /var/run/docker.sock:/var/run/docker.sock \
     -v /usr/lib/systemd:/usr/lib/systemd \
-    -v /var/docker-bench:/var/docker-bench
-    -v /etc:/etc --label docker_bench_security \
+    -v /var/docker-bench:/var/docker-bench \
+    -v /etc/fstab:/etc/fstab \
+    -v /etc/docker:/etc/docker \
+    -v /etc/default/docker:/etc/default/docker \
+    -v /etc/group:/etc/group \
+    --label docker_bench_security \
     docker-bench-tests
 ```
 
diff --git a/bats.Dockerfile b/bats.Dockerfile
index 224bebe..8ae749a 100644
--- a/bats.Dockerfile
+++ b/bats.Dockerfile
@@ -10,7 +10,7 @@ ENV BATS_SHA_256 480d8d64f1681eee78d1002527f3f06e1ac01e173b761bc73d0cf33f4dc1d8d
 
 LABEL docker_bench_security=true
 
-RUN apk --update add curl bash \
+RUN apk --update add curl bash ncurses \
     && rm -rf /var/lib/apt/lists/* \
     && rm /var/cache/apk/*
 
diff --git a/run_tests.sh b/run_tests.sh
index 214490b..cb8ac8a 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -39,7 +39,7 @@ HELP() {
   echo -e "${REV}-g${NORM}  --Generates all CIS Bats tests without execution. No further functions are performed."
   echo -e "${REV}-p${NORM}  --Show results in pretty format."
   echo -e "${REV}-t${NORM}  --Show results in TAP format. This is the default format."
-  echo -e "${REV}-t${NORM}  --Create test results files: ${BOLD}tests_<timestamp>.tap${NORM} in test result folder."
+  echo -e "${REV}-r${NORM}  --Create test results files: ${BOLD}tests_<timestamp>.tap${NORM} in test result folder."
   echo -e "${REV}-o${NORM}  --Specify test result folder. Default to ${BOLD}$TEST_RESULTS${NORM}."
   echo -e "${REV}-h${NORM}  --Displays this help message. No further functions are performed."\\n
   echo -e "Example: ${BOLD}$SCRIPT -t -o $TEST_RESULTS${NORM}"\\n

From ff1e347fc7db5bbb5459156598b254e6b476a2bb Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Thu, 19 May 2016 16:35:40 +0300
Subject: [PATCH 29/30] move test configuration into separate directory

---
 bats_tests/1_host_configuration.bats              | 1 -
 bats_tests/0_config.example => config/0_config.sh | 0
 config/0_config.sh.example                        | 4 ++++
 run_tests.sh                                      | 1 +
 4 files changed, 5 insertions(+), 1 deletion(-)
 rename bats_tests/0_config.example => config/0_config.sh (100%)
 create mode 100644 config/0_config.sh.example

diff --git a/bats_tests/1_host_configuration.bats b/bats_tests/1_host_configuration.bats
index 034c9ef..3625695 100644
--- a/bats_tests/1_host_configuration.bats
+++ b/bats_tests/1_host_configuration.bats
@@ -1,6 +1,5 @@
 #!/usr/bin/env bats
 
-load "0_config"
 load "test_helper/bats-support/load"
 load "test_helper/bats-assert/load"
 load "$BATS_TEST_DIRNAME/../helper_lib.sh"
diff --git a/bats_tests/0_config.example b/config/0_config.sh
similarity index 100%
rename from bats_tests/0_config.example
rename to config/0_config.sh
diff --git a/config/0_config.sh.example b/config/0_config.sh.example
new file mode 100644
index 0000000..d95a242
--- /dev/null
+++ b/config/0_config.sh.example
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+# trusted users that can control Docker daemon
+config_trusted_users=(vagrant docker ubuntu)
diff --git a/run_tests.sh b/run_tests.sh
index cb8ac8a..cea0227 100755
--- a/run_tests.sh
+++ b/run_tests.sh
@@ -1,6 +1,7 @@
 #!/bin/bash
 
 . ./generate_tests.sh
+. ./config/0_config.sh
 
 TEST_RESULTS=$BENCH_ROOT/results
 

From 010b4a20ff3427e61ed662223824ed825176980d Mon Sep 17 00:00:00 2001
From: Alexei Ledenev <alexei.led@gmail.com>
Date: Thu, 19 May 2016 16:38:34 +0300
Subject: [PATCH 30/30] update .gitignore to ignore user config files

---
 .gitignore | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.gitignore b/.gitignore
index cfd3b2d..c94e0b9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,2 @@
 *.log
-bats_tests/0_config
+config/*_config.sh