From 1f856a743aa51459c95039cd9b0bbd40a580877f Mon Sep 17 00:00:00 2001 From: Alexei Ledenev Date: Tue, 17 May 2016 13:17:03 +0300 Subject: [PATCH] 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=" ]; 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=" ]; 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=" ]; 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=' -a "$caps" != 'CapAdd=' ]; 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=" ]; 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=" ]; 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