From 18d5a13240a1fe65e80d83d4341f98a471e2264a Mon Sep 17 00:00:00 2001 From: Diogo Monica Date: Sun, 10 May 2015 21:08:28 -0700 Subject: [PATCH] First version of the CIS Docker Benchmark v1.0.0 --- .gitignore | 1 + Dockerfile | 11 + README | 0 README.md | 36 ++ docker_security_benchmark.sh | 80 +++ helper_lib.sh | 38 ++ output_lib.sh | 25 + tests/1_host_configuration.sh | 208 ++++++++ tests/2_docker_daemon_configuration.sh | 105 ++++ tests/3_docker_daemon_configuration_files.sh | 435 ++++++++++++++++ tests/4_container_images.sh | 43 ++ tests/5_container_runtime.sh | 499 +++++++++++++++++++ tests/6_docker_security_operations.sh | 64 +++ 13 files changed, 1545 insertions(+) create mode 100644 .gitignore create mode 100644 Dockerfile delete mode 100644 README create mode 100644 README.md create mode 100644 docker_security_benchmark.sh create mode 100644 helper_lib.sh create mode 100644 output_lib.sh create mode 100644 tests/1_host_configuration.sh create mode 100644 tests/2_docker_daemon_configuration.sh create mode 100644 tests/3_docker_daemon_configuration_files.sh create mode 100644 tests/4_container_images.sh create mode 100644 tests/5_container_runtime.sh create mode 100644 tests/6_docker_security_operations.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..397b4a7 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.log diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..21eda8d --- /dev/null +++ b/Dockerfile @@ -0,0 +1,11 @@ +FROM gliderlabs/alpine:3.1 + +RUN apk --update add docker + +RUN mkdir /docker_security_benchmark + +COPY . /docker_security_benchmark + +WORKDIR /docker_security_benchmark + +ENTRYPOINT ["/bin/sh", "docker_security_benchmark.sh"] diff --git a/README b/README deleted file mode 100644 index e69de29..0000000 diff --git a/README.md b/README.md new file mode 100644 index 0000000..f128e06 --- /dev/null +++ b/README.md @@ -0,0 +1,36 @@ +# Docker Security Benchmark Checker + +The Docker Security Benchmark Checker is a script that checks for all the automatable tests included in the [CIS Docker 1.6 Benchmark](https://benchmarks.cisecurity.org/tools2/docker/CIS_Docker_1.6_Benchmark_v1.0.0.pdf). We are releasing this as a follow-up to our [Understanding Docker Security and Best Practices](https://blog.docker.com/2015/05/understanding-docker-security-and-best-practices/) blog post. + +We are making this available as an open-source utility so the Docker community can have an easy way to self-assess their hosts and docker containers against this benchmark. + +## Running the benchmark + +We packaged this benchmark as a small container for your convenience. Note that this container is being run with a *lot* of privilege -- sharing the host's filesystem, pid and network namespaces, due to portions of the benchmark applying to the running host. + +The easiest way to run your hosts against the CIS Docker 1.6 benchmark is by running our pre-built container: + + +``` +docker run -it --net host --pid host -v /var/run/docker.sock:/var/run/docker.sock \ +-v /usr/lib/systemd:/usr/lib/systemd -v /etc:/etc diogomonica/docker-security-benchmark +``` + +## Building the benchmark + +If you wish to build and run this container yourself, you can follow the following steps: + +``` +# git clone https://github.com/diogomonica/docker-security-benchmark.git +# cd docker-security-benchmark; docker build -t docker-security-benchmark . +# docker run run -it --net host --pid host -v /var/run/docker.sock:/var/run/docker.sock -v /usr/lib/systemd:/usr/lib/systemd -v /etc:/etc docker-security-benchmark +``` + +Also, this script can also be simply run from your base host by running: + +``` +# git clone https://github.com/diogomonica/docker-security-benchmark.git +# cd docker-security-benchmark; sh docker_security_benchmark.sh +``` + +This script was build to be POSIX 2004 compliant, so it should be portable across any Unix platform. diff --git a/docker_security_benchmark.sh b/docker_security_benchmark.sh new file mode 100644 index 0000000..e0bbe79 --- /dev/null +++ b/docker_security_benchmark.sh @@ -0,0 +1,80 @@ +#!/bin/sh +# ------------------------------------------------------------------------------ +# CIS Docker 1.6 Benchmark v1.0.0 checker +# +# Docker, Inc. (c) 2015 +# +# Provides automated tests for the CIS Docker 1.6 Benchmark: +# https://benchmarks.cisecurity.org/tools2/docker/CIS_Docker_1.6_Benchmark_v1.0.0.pdf +# +# ------------------------------------------------------------------------------ + +# Load dependencies +. ./output_lib.sh +. ./helper_lib.sh + +# Setup the paths +this_path=$(abspath $0) ## Path of this file including filenamel +dir_name=`dirname ${this_path}` ## Dir where this file is +myname=`basename ${this_path}` ## file name of this script. +logger="${myname}.log" + + +# Check for required program(s) +req_progs='docker netstat grep awk' +for p in $req_progs; do + command -v $p >/dev/null 2>&1 || { printf "$p command not found.\n"; exit 1; } +done + +# Ensure we can connect to docker daemon +`docker ps -q >/dev/null 2>&1` +if [ $? -ne 0 ]; then + printf "Error connecting to docker daemon (does docker ps work?)\n" + exit 1 +fi + +usage () { + printf " + usage: $myname [options] + + -h optional Print this help message\n" + exit 1 +} + +yell "# ------------------------------------------------------------------------------ +# CIS Docker 1.6 Benchmark v1.0.0 checker +# +# Docker, Inc. (c) 2015 +# +# Provides automated tests for the CIS Docker 1.6 Benchmark: +# https://benchmarks.cisecurity.org/tools2/docker/CIS_Docker_1.6_Benchmark_v1.0.0.pdf +# ------------------------------------------------------------------------------" + +logit "Initializing `date`\n" + +# Warn if not root +ID=`id -u` +if test "x$ID" != "x0"; then + warn "Some tests might require root to run" + sleep 3 +fi + +# Get the flags +while getopts :hlfi: args +do + case $args in + h) usage ;; + l) logger="$OPTARG" ;; + *) usage ;; + esac +done + +# Load all the tests from tests/ and run them +main () { + for test in tests/*.sh + do + . ./$test + done +} + +main "$@" diff --git a/helper_lib.sh b/helper_lib.sh new file mode 100644 index 0000000..3dc3a7b --- /dev/null +++ b/helper_lib.sh @@ -0,0 +1,38 @@ +#!/bin/sh + +# Returns the absolute path of a given string +abspath () { case "$1" in /*)printf "%s\n" "$1";; *)printf "%s\n" "$PWD/$1";; esac; } + +# Compares versions of software of the format X.Y.Z +do_version_check() { + [ "$1" = "$2" ] && return 10 + + ver1front=`printf $1 | cut -d "." -f -1` + ver1back=`printf $1 | cut -d "." -f 2-` + ver2front=`printf $2 | cut -d "." -f -1` + ver2back=`printf $2 | cut -d "." -f 2-` + + if [ "$ver1front" != "$1" ] || [ "$ver2front" != "$2" ]; then + [ "$ver1front" -gt "$ver2front" ] && return 11 + [ "$ver1front" -lt "$ver2front" ] && return 9 + + [ "$ver1front" = "$1" ] || [ -z "$ver1back" ] && ver1back=0 + [ "$ver2front" = "$2" ] || [ -z "$ver2back" ] && ver2back=0 + do_version_check "$ver1back" "$ver2back" + return $? + else + [ "$1" -gt "$2" ] && return 11 || return 9 + fi +} + +# Compares two strings and returns 0 if the second is a substring of the first +contains() { + string="$1" + substring="$2" + if test "${string#*$substring}" != "$string" + then + return 0 # $substring is in $string + else + return 1 # $substring is not in $string + fi +} diff --git a/output_lib.sh b/output_lib.sh new file mode 100644 index 0000000..1a5c423 --- /dev/null +++ b/output_lib.sh @@ -0,0 +1,25 @@ +bldred='\033[1;31m' +bldgrn='\033[1;32m' +bldblu='\033[1;34m' +bldylw='\033[1;33m' # Yellow +txtrst='\033[0m' + +logit () { + printf "$1\n" | tee -a $logger +} + +info () { + printf '%b' "${bldblu}[INFO]${txtrst} $1\n" | tee -a $logger +} + +pass () { + printf '%b' "${bldgrn}[PASS]${txtrst} $1\n" | tee -a $logger +} + +warn () { + printf '%b' "${bldred}[WARN]${txtrst} $1\n" | tee -a $logger +} + +yell () { + printf '%b' "${bldylw}$1${txtrst}\n" +} diff --git a/tests/1_host_configuration.sh b/tests/1_host_configuration.sh new file mode 100644 index 0000000..c103f9c --- /dev/null +++ b/tests/1_host_configuration.sh @@ -0,0 +1,208 @@ +#!/bin/sh + +logit "" +info "1 - Host Configuration" + +# 1.1 +check_1_1="1.1 - Create a separate partition for containers" +grep /var/lib/docker /etc/fstab >/dev/null 2>&1 +if [ $? -eq 0 ]; then + pass "$check_1_1" +else + warn "$check_1_1" +fi + +# 1.2 +check_1_2="1.2 - Use an updated Linux Kernel" +kernel_version=`uname -r | cut -d "-" -f 1` +do_version_check 3.10 $kernel_version +if [ $? -eq 11 ]; then + warn "$check_1_2" +else + pass "$check_1_2" +fi + +# 1.5 +check_1_5="1.5 - 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 LISTEN | wc -l` +if [ $listening_services -eq 0 ]; then + warn "1.5 - Failed to get listening services for check: $check_1_5" +else + if [ $listening_services -gt 5 ]; then + warn "$check_1_5" + else + pass "$check_1_5" + fi +fi + +# 1.6 +check_1_6="1.6 - Keep Docker up to date" +docker_version=`docker version | grep 'Server version' | awk '{print $3}'` +if [ $? -eq 11 ]; then + warn "$check_1_6" +else + pass "$check_1_6" +fi + +# 1.7 +check_1_7="1.7 - Only allow trusted users to control Docker daemon" +docker_users=`cat /etc/group | grep docker` +info "$check_1_7" +for u in $docker_users; do + info " * $u" +done + +# 1.8 +check_1_8="1.8 - Audit docker daemon" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /usr/bin/docker >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_8" + else + warn "$check_1_8" + fi +else + warn "1.8 - Failed to inspect: auditctl command not found." +fi + +# 1.9 +check_1_9="1.9 - Audit Docker files and directories - /var/lib/docker" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /var/lib/docker >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_9" + else + warn "$check_1_9" + fi +else + warn "1.9 - Failed to inspect: auditctl command not found." +fi + +# 1.10 +check_1_10="1.10 - Audit Docker files and directories - /etc/docker" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /etc/docker >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_10" + else + warn "$check_1_10" + fi +else + warn "1.10 - Failed to inspect: auditctl command not found." +fi + +# 1.11 +check_1_11="1.11 - Audit Docker files and directories - docker-registry.service" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /usr/lib/systemd/system/docker-registry.service >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_11" + else + warn "$check_1_11" + fi +else + warn "1.11 - Failed to inspect: auditctl command not found." +fi + +# 1.12 +check_1_12="1.12 - Audit Docker files and directories - docker.service" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /usr/lib/systemd/system/docker.service >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_12" + else + warn "$check_1_12" + fi +else + warn "1.12 - Failed to inspect: auditctl command not found." +fi + +# 1.13 +check_1_13="1.13 - Audit Docker files and directories - /var/run/docker.sock" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /var/run/docker.sock >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_13" + else + warn "$check_1_13" + fi +else + warn "1.13 - Failed to inspect: auditctl command not found." +fi + +# 1.14 +check_1_14="1.14 - Audit Docker files and directories - /etc/sysconfig/docker" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /etc/sysconfig/docker >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_14" + else + warn "$check_1_14" + fi +else + warn "1.14 - Failed to inspect: auditctl command not found." +fi + +# 1.15 +check_1_15="1.15 - Audit Docker files and directories - /etc/sysconfig/docker-network" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /etc/sysconfig/docker-network >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_15" + else + warn "$check_1_15" + fi +else + warn "1.15 - Failed to inspect: auditctl command not found." +fi + +# 1.16 +check_1_16="1.16 - Audit Docker files and directories - /etc/sysconfig/docker-registry" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /etc/sysconfig/docker-registry >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_16" + else + warn "$check_1_16" + fi +else + warn "1.16 - Failed to inspect: auditctl command not found." +fi + +# 1.17 +check_1_17="1.17 - Audit Docker files and directories - /etc/sysconfig/docker-storage" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /etc/sysconfig/docker-storage >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_17" + else + warn "$check_1_17" + fi +else + warn "1.17 - Failed to inspect: auditctl command not found." +fi + +# 1.18 +check_1_18="1.18 - Audit Docker files and directories - /etc/default/docker" +command -v auditctl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + auditctl -l | grep /etc/default/docker >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_1_18" + else + warn "$check_1_18" + fi +else + warn "1.18 - Failed to inspect: auditctl command not found." +fi diff --git a/tests/2_docker_daemon_configuration.sh b/tests/2_docker_daemon_configuration.sh new file mode 100644 index 0000000..11a0ea9 --- /dev/null +++ b/tests/2_docker_daemon_configuration.sh @@ -0,0 +1,105 @@ +#!/bin/sh + +logit "\n" +info "2 - Docker Daemon Configuration" + +# 2.1 +check_2_1="2.1 - Do not use lxc execution driver" +ps -ef | grep docker | grep lxc >/dev/null 2>&1 +if [ $? -eq 0 ]; then + warn "$check_2_1" +else + pass "$check_2_1" +fi + +# 2.2 +check_2_2="2.2 - Restrict network traffic between containers" +ps -ef | grep docker | grep "icc=false" >/dev/null 2>&1 +if [ $? -eq 0 ]; then + pass "$check_2_2" +else + warn "$check_2_2" +fi + +# 2.3 +check_2_3="2.3 - Set the logging level" +ps -ef | grep docker | grep "log-level=\"debug\"" >/dev/null 2>&1 +if [ $? -eq 0 ]; then + warn "$check_2_3" +else + pass "$check_2_3" +fi + +# 2.4 +check_2_4="2.4 - Allow Docker to make changes to iptables" +ps -ef | grep docker | grep "iptables=false" >/dev/null 2>&1 +if [ $? -eq 0 ]; then + warn "$check_2_4" +else + pass "$check_2_4" +fi + +# 2.5 +check_2_5="2.5 - Do not use insecure registries" +ps -ef | grep docker | grep "insecure-registry" >/dev/null 2>&1 +if [ $? -eq 0 ]; then + warn "$check_2_5" +else + pass "$check_2_5" +fi + +# 2.6 +check_2_6="2.6 - Setup a local registry mirror" +ps -ef | grep docker | grep "registry-mirror" >/dev/null 2>&1 +if [ $? -eq 0 ]; then + pass "$check_2_6" +else + info "$check_2_6" + info " * No local registry currently configured" +fi + +# 2.7 +check_2_7="2.7 - Do not use the aufs storage driver" +storage_driver=`docker info 2>/dev/null| grep -e "^Storage Driver:\s*aufs\s*$"` +if [ $? -eq 0 ]; then + warn "$check_2_7" +else + pass "$check_2_7" +fi + +# 2.8 +check_2_8="2.8 - Do not bind Docker to another IP/Port or a Unix socket" +ps -ef | grep docker | grep "\-H" >/dev/null 2>&1 +if [ $? -eq 0 ]; then + info "$check_2_8" + info " * Docker daemon running with -H" +else + pass "$check_2_8" +fi + +# 2.9 +check_2_9="2.9 - Configure TLS authentication for Docker daemon" +ps -ef | grep docker | grep "tcp://" >/dev/null 2>&1 +if [ $? -eq 0 ]; then + ps -ef | grep docker | grep "tlsverify" | grep "tlskey" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_2_9" + info " * Docker daemon currently listening on TCP" + else + warn "$check_2_9" + warn " * Docker daemon currently listening on TCP without --tlsverify" + fi +else + info "$check_2_9" + info " * Docker daemon not listening on TCP" +fi + +# 2.10 +check_2_10="2.10 - Set default ulimit as appropriate" +ps -ef | grep docker | grep "default-ulimit" >/dev/null 2>&1 +if [ $? -eq 0 ]; then + pass "$check_2_10" +else + info "$check_2_10" + info " * Default ulimit doesn't appear to be set" +fi diff --git a/tests/3_docker_daemon_configuration_files.sh b/tests/3_docker_daemon_configuration_files.sh new file mode 100644 index 0000000..9f83d0b --- /dev/null +++ b/tests/3_docker_daemon_configuration_files.sh @@ -0,0 +1,435 @@ +#!/bin/sh + +logit "\n" +info "3 - Docker Daemon Configuration Files" + +# 3.1 +check_3_1="3.1 - Verify that docker.service file ownership is set to root:root" +file="/usr/lib/systemd/system/docker.service" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_1" + else + warn "$check_3_1" + warn " * Wrong ownership for $file" + fi +else + info "$check_3_1" + info " * File not found" +fi + +# 3.2 +check_3_2="3.2 - Verify that docker.service file permissions are set to 644" +file="/usr/lib/systemd/system/docker.service" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $1}' | grep "rw-r--r--" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_2" + else + warn "$check_3_2" + warn " * Wrong permissions for $file" + fi +else + info "$check_3_2" + info " * File not found" +fi + +# 3.3 +check_3_3="3.3 - Verify that docker-registry.service file ownership is set to root:root" +file="/usr/lib/systemd/system/docker-registry.service" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_3" + else + warn "$check_3_3" + warn " * Wrong ownership for $file" + fi +else + info "$check_3_3" + info " * File not found" +fi + +# 3.4 +check_3_4="3.4 - Verify that docker-registry.service file permissions are set to 644" +file="/usr/lib/systemd/system/docker-registry.service" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $1}' | grep "rw-r--r--" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_4" + else + warn "$check_3_4" + warn " * Wrong permissions for $file" + fi +else + info "$check_3_4" + info " * File not found" +fi + +# 3.5 +check_3_5="3.5 - Verify that docker.socket file ownership is set to root:root" +file="/usr/lib/systemd/system/docker.socket" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_5" + else + warn "$check_3_5" + warn " * Wrong ownership for $file" + fi +else + info "$check_3_5" + info " * File not found" +fi + +# 3.6 +check_3_6="3.6 - Verify that docker.socket file permissions are set to 644" +file="/usr/lib/systemd/system/docker.socket" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $1}' | grep "rw-r--r--" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_6" + else + warn "$check_3_6" + warn " * Wrong permissions for $file" + fi +else + info "$check_3_6" + info " * File not found" +fi + +# 3.7 +check_3_7="3.7 - Verify that Docker environment file ownership is set to root:root " +file="/etc/sysconfig/docker" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_7" + else + warn "$check_3_7" + warn " * Wrong ownership for $file" + fi +else + info "$check_3_7" + info " * File not found" +fi + +# 3.8 +check_3_8="3.8 - Verify that Docker environment file permissions are set to 644" +file="/etc/sysconfig/docker" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $1}' | grep "rw-r--r--" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_8" + else + warn "$check_3_8" + warn " * Wrong permissions for $file" + fi +else + info "$check_3_8" + info " * File not found" +fi + +# 3.9 +check_3_9="3.9 - Verify that docker-network environment file ownership is set to root:root" +file="/etc/sysconfig/docker-network" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_9" + else + warn "$check_3_9" + warn " * Wrong ownership for $file" + fi +else + info "$check_3_9" + info " * File not found" +fi + +# 3.10 +check_3_10="3.10 - Verify that docker-network environment file permissions are set to 644" +file="/etc/sysconfig/docker-network" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $1}' | grep "rw-r--r--" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_10" + else + warn "$check_3_10" + warn " * Wrong permissions for $file" + fi +else + info "$check_3_10" + info " * File not found" +fi + +# 3.11 +check_3_11="3.11 - Verify that docker-registry environment file ownership is set to root:root" +file="/etc/sysconfig/docker-registry" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_11" + else + warn "$check_3_11" + warn " * Wrong ownership for $file" + fi +else + info "$check_3_11" + info " * File not found" +fi + +# 3.12 +check_3_12="3.12 - Verify that docker-registry environment file permissions are set to 644" +file="/etc/sysconfig/docker-registry" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $1}' | grep "rw-r--r--" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_12" + else + warn "$check_3_12" + warn " * Wrong permissions for $file" + fi +else + info "$check_3_12" + info " * File not found" +fi + +# 3.13 +check_3_13="3.13 - Verify that docker-storage environment file ownership is set to root:root" +file="/etc/sysconfig/docker-storage" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_13" + else + warn "$check_3_13" + warn " * Wrong ownership for $file" + fi +else + info "$check_3_13" + info " * File not found" +fi + +# 3.14 +check_3_14="3.14 - Verify that docker-storage environment file permissions are set to 644" +file="/etc/sysconfig/docker-storage" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $1}' | grep "rw-r--r--" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_14" + else + warn "$check_3_14" + warn " * Wrong permissions for $file" + fi +else + info "$check_3_14" + info " * File not found" +fi + +# 3.15 +check_3_15="3.15 - Verify that /etc/docker directory ownership is set to root:root" +directory="/etc/docker" +if [ -d "$directory" ]; then + ls -ld "$directory" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_15" + else + warn "$check_3_15" + warn " * Wrong ownership for $directory" + fi +else + info "$check_3_15" + info " * Directory not found" +fi + +# 3.16 +check_3_16="3.16 - Verify that /etc/docker directory permissions are set to 755" +directory="/etc/docker" +if [ -d "$directory" ]; then + perms=`ls -ld $directory | awk '{print $1}'` + if [ $perms = "drwxr-xr-x." ]; then + pass "$check_3_16" + elif [ $perms = "drwx------" ]; then + pass "$check_3_16" + else + warn "$check_3_16" + warn " * Wrong permissions for $directory" + fi +else + info "$check_3_16" + info " * Directory not found" +fi + +# 3.17 +check_3_17="3.17 - 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 "$p" | grep "root" >/dev/null 2>&1 + if [ $? -ne 0 ]; then + fail=1 + fi + done + if [ $fail -eq 1 ]; then + warn "$check_3_17" + warn " * Wrong ownership for $directory" + else + pass "$check_3_17" + fi +else + info "$check_3_17" + info " * Directory not found" +fi + +# 3.18 +check_3_18="3.18 - 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 test "$p" != "-rw-r--r--." && test "$p" = "-rw-------."; then + fail=1 + fi + done + if [ $fail -eq 1 ]; then + warn "$check_3_18" + warn " * Wrong permissions for $directory" + else + pass "$check_3_18" + fi +else + info "$check_3_18" + info " * Directory not found" +fi + +# 3.19 +check_3_19="3.19 - Verify that TLS CA certificate file ownership is set to root:root" +tlscacert=`ps -ef | grep docker | sed -n 's/.*tlscacert=\([^s]\)/\1/p' | cut -d " " -f 1` +if [ -f "$tlscacert" ]; then + ls -ld "$tlscacert" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_19" + else + warn "$check_3_19" + warn " * Wrong ownership for $tlscacert" + fi +else + info "$check_3_19" + info " * No TLS CA certificate found" +fi + +# 3.20 +check_3_20="3.20 - Verify that TLS CA certificate file permissions are set to 444" +tlscacert=`ps -ef | grep docker | sed -n 's/.*tlscacert=\([^s]\)/\1/p' | cut -d " " -f 1` +if [ -f "$tlscacert" ]; then + perms=`ls -ld "$tlscacert" | awk '{print $1}'` + if test "$perms" = "-rw-r--r--"; then + pass "$check_3_20" + else + warn "$check_3_20" + warn " * Wrong permissions for $tlscacert" + fi +else + info "$check_3_20" + info " * No TLS CA certificate found" +fi + +# 3.21 +check_3_21="3.21 - Verify that Docker server certificate file ownership is set to root:root" +tlscert=`ps -ef | grep docker | sed -n 's/.*tlscert=\([^s]\)/\1/p' | cut -d " " -f 1` +if [ -f "$tlscert" ]; then + ls -ld "$tlscert" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_21" + else + warn "$check_3_21" + warn " * Wrong ownership for $tlscert" + fi +else + info "$check_3_21" + info " * No TLS Server certificate found" +fi + +# 3.22 +check_3_22="3.22 - Verify that Docker server certificate file permissions are set to 444" +tlscacert=`ps -ef | grep docker | sed -n 's/.*tlscert=\([^s]\)/\1/p' | cut -d " " -f 1` +if [ -f "$tlscert" ]; then + perms=`ls -ld "$tlscert" | awk '{print $1}'` + if test "$perms" = "-rw-r--r--"; then + pass "$check_3_22" + else + warn "$check_3_22" + warn " * Wrong permissions for $tlscert" + fi +else + info "$check_3_22" + info " * No TLS Server certificate found" +fi + +# 3.23 +check_3_23="3.23 - Verify that Docker server key file ownership is set to root:root" +tlskey=`ps -ef | grep docker | sed -n 's/.*tlskey=\([^s]\)/\1/p' | cut -d " " -f 1` +if [ -f "$tlskey" ]; then + ls -ld "$tlskey" | awk '{print $3, $4}' | grep "root root" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_23" + else + warn "$check_3_23" + warn " * Wrong ownership for $tlskey" + fi +else + info "$check_3_23" + info " * No TLS Key found" +fi + +# 3.24 +check_3_24="3.24 - Verify that Docker server key file permissions are set to 400" +tlskey=`ps -ef | grep docker | sed -n 's/.*tlskey=\([^s]\)/\1/p' | cut -d " " -f 1` +if [ -f "$tlskey" ]; then + perms=`ls -ld "$tlskey" | awk '{print $1}'` + if test "$perms" = "-r--------"; then + pass "$check_3_24" + else + warn "$check_3_24" + warn " * Wrong permissions for $tlskey" + fi +else + info "$check_3_24" + info " * No TLS Key found" +fi + +# 3.25 +check_3_25="3.25 - Verify that Docker socket file ownership is set to root:docker" +file="/var/run/docker.sock" +if [ -f "$file" ]; then + ls -ld "$file" | awk '{print $3, $4}' | grep "root docker" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + pass "$check_3_25" + else + warn "$check_3_25" + warn " * Wrong ownership for $file" + fi +else + info "$check_3_25" + info " * File not found" +fi + +# 3.26 +check_3_26="3.26 - Verify that Docker socket file permissions are set to 660" +file="/var/run/docker.sock" +if [ -f "$file" ]; then + perms=`ls -ld "$file" | awk '{print $1}'` + if test "$perms" = "srw-rw----"; then + pass "$check_3_26" + else + warn "$check_3_26" + warn " * Wrong permissions for $file" + fi +else + info "$check_3_26" + info " * File not found" +fi diff --git a/tests/4_container_images.sh b/tests/4_container_images.sh new file mode 100644 index 0000000..cd849f6 --- /dev/null +++ b/tests/4_container_images.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +logit "\n" +info "4 - Container Images and Build Files" + +# 4.1 +check_4_1="4.1 - Create a user for the container" + +containers=`docker ps -q` +# If container_users is empty, there are no running containers +if test "$containers" = ""; then + info "$check_4_1" + info " * No containers running" +else + # List all the running containers, ouput their ID and USER + containers=`docker ps -q | xargs docker inspect --format '{{ .Id }}:User={{.Config.User}}' 2>/dev/null` + # We have some containers running, set failure flag to 0. Check for Users. + fail=0 + # Make the loop separator be a new-line in POSIX compliant fashion + set -f; IFS=$' +' + for c in $containers; do + user=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + + if test $user = "User=" || test $user = "User="; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_4_1" + warn " * Running as root: $container_id" + fail=1 + else + warn " * Running as root: $container_id" + fi + fi + done + # We went through all the containers and found none running as root + if [ $fail -eq 0 ]; then + pass "$check_4_1" + fi +fi +# Make the loop separator go back to space +set +f; unset IFS diff --git a/tests/5_container_runtime.sh b/tests/5_container_runtime.sh new file mode 100644 index 0000000..f9a3dcd --- /dev/null +++ b/tests/5_container_runtime.sh @@ -0,0 +1,499 @@ +#!/bin/sh + +logit "\n" +info "5 - Container Runtime" + +# If containers is empty, there are no running containers +if test "$containers" = ""; then + info " * No containers running, skipping Section 5" +else + # List all running containers + containers=`docker ps -q` + # Make the loop separator be a new-line in POSIX compliant fashion + set -f; IFS=$' +' + # 5.1 + check_5_1="5.1 - Verify AppArmor Profile, if applicable" + + # List all the running containers, ouput their ID and AppArmorProfile + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:AppArmorProfile={{.AppArmorProfile }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $cont_inspect; do + policy=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + + if test $policy = "AppArmorProfile=" || test $policy = "AppArmorProfile="; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_1" + warn " * No AppArmorProfile Found: $container_id" + fail=1 + else + warn " * No AppArmorProfile Found: $container_id" + fi + fi + done + # We went through all the containers and found none without AppArmor + if [ $fail -eq 0 ]; then + pass "$check_5_1" + fi + + # 5.2 + check_5_2="5.2 - Verify SELinux security options, if applicable" + + # List all the running containers, ouput their ID and SecurityOptions + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:SecurityOpt={{.HostConfig.SecurityOpt }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $cont_inspect; do + policy=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + + if test $policy = "SecurityOpt=" || test $policy = "SecurityOpt="; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_2" + warn " * No SecurityOptions Found: $container_id" + fail=1 + else + warn " * No SecurityOptions Found: $container_id" + fi + fi + done + # We went through all the containers and found none without SELinux + if [ $fail -eq 0 ]; then + pass "$check_5_2" + fi + + # 5.3 + check_5_3="5.3 - Verify that containers are running only a single main process" + + # List all the running containers, ouput their Id + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + + for c in $cont_inspect; do + processes=`docker exec $c ps -el 2>/dev/null | wc -l | awk '{print $1}'` + if [ $processes -gt 5 ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_3" + warn " * Too many proccesses running: $container_id" + fail=1 + else + warn " * Too many proccesses running: $container_id" + fi + fi + done + # We went through all the containers and found none with toom any processes + if [ $fail -eq 0 ]; then + pass "$check_5_3" + fi + + # 5.4 + check_5_4="5.4 - Restrict Linux Kernel Capabilities within containers" + + # List all the running containers, ouput their ID and CapAdd + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:CapAdd={{ .HostConfig.CapAdd}}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + + for c in $cont_inspect; do + caps=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $caps != "CapAdd=" && test $caps != "CapAdd="; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_4" + warn " * Capabilities added: $caps to $container_id" + fail=1 + else + warn " * Capabilities added: $caps to $container_id" + fi + fi + done + # We went through all the containers and found none with extra capabilities + if [ $fail -eq 0 ]; then + pass "$check_5_4" + fi + + # 5.5 + check_5_5="5.5 - Do not use privileged containers" + + # List all the running containers, ouput their ID and privileged status + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:{{.HostConfig.Privileged }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + + for c in $cont_inspect; do + privileged=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $privileged = "true"; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_5" + warn " * Container running in Privileged mode: $container_id" + fail=1 + else + warn " * Container running in Privileged mode: $container_id" + fi + fi + done + # We went through all the containers and found no privileged containers + if [ $fail -eq 0 ]; then + pass "$check_5_5" + fi + + # 5.6 + check_5_6="5.6 - Do not mount sensitive host system directories on containers" + + containers=`docker ps -q` + # List of sensitive directories to test for. Script uses new-lines as a separator + sensitive_dirs='/boot + /dev + /etc + /lib + /proc + /sys + /usr' + # List all the running containers, ouput their ID and R/W Volumes + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:{{ .VolumesRW }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $cont_inspect; do + volumes=`printf "$c" | cut -d ":" -f 2-` + container_id=`printf "$c" | cut -d ":" -f 1` + sensitive=0 + + # Go over each directory in sensitive dir and see if they exist in the volumes + for v in $sensitive_dirs; do + if [ $sensitive -eq 0 ]; then + contains "$volumes" "$v:" && sensitive=1 + fi + done + + if [ $sensitive -eq 1 ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_6" + warn " * Container mounted with sensitive directory: $container_id" + fail=1 + else + warn " * Container mounted with sensitive directory: $container_id" + fi + fi + done + # We went through all the containers and found none with sensitive mounts + if [ $fail -eq 0 ]; then + pass "$check_5_6" + fi + + # 5.7 + check_5_7="5.7 - Do not run ssh within containers" + + # List all the running containers, ouput their Id + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $cont_inspect; do + processes=`docker exec $c ps -el 2>/dev/null | grep sshd | wc -l | awk '{print $1}'` + if [ $processes -gt 1 ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_7" + warn " * Container running sshd: $container_id" + fail=1 + else + warn " * Container running sshd: $container_id" + fi + fi + done + # We went through all the containers and found none with sshd + if [ $fail -eq 0 ]; then + pass "$check_5_7" + fi + + # 5.8 + check_5_8="5.8 - Do not map privileged ports within containers" + + # List all the running containers, ouput their listening ports + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $containers; do + port=`docker port $c | awk '{print $1}' | cut -d '/' -f1` + if test "$port" != "" && [ $port -lt 1025 ]; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_8" + warn " * Privileged Port in use: $port" + fail=1 + else + warn " * Privileged Port in use: $port" + fi + fi + done + # We went through all the containers and found no privileged ports + if [ $fail -eq 0 ]; then + pass "$check_5_8" + fi + + # 5.10 + check_5_10="5.10 - Do not use host network mode on container" + + # List all the running containers, ouput their ID and network mode + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:NetworkMode={{.HostConfig.NetworkMode }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $cont_inspect; do + mode=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $mode = "NetworkMode=host"; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_10" + warn " * Container running with networking mode 'host': $container_id" + fail=1 + else + warn " * Container running with networking mode 'host': $container_id" + fi + fi + done + # We went through all the containers and found no Network Mode host + if [ $fail -eq 0 ]; then + pass "$check_5_10" + fi + + # 5.11 + check_5_11="5.11 - Limit memory usage for container" + + # List all the running containers, ouput their ID and memory limit + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:{{ .Config.Memory }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + # Make the loop separator be a new-line in POSIX compliant fashion + for c in $cont_inspect; do + memory=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $memory = "0"; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_11" + warn " * Container running without memory restrictions: $container_id" + fail=1 + else + warn " * Container running without memory restrictions: $container_id" + fi + fi + done + # We went through all the containers and found no lack of Memory restrictions + if [ $fail -eq 0 ]; then + pass "$check_5_11" + fi + + # 5.12 + check_5_12="5.12 - Set container CPU priority appropriately" + + # List all the running containers, ouput their ID and CPU Shares + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:{{.Config.CpuShares }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $cont_inspect; do + shares=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $shares = "0"; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_12" + warn " * Container running without CPU restrictions: $container_id" + fail=1 + else + warn " * Container running without CPU restrictions: $container_id" + fi + fi + done + # We went through all the containers and found no lack of CPUShare restrictions + if [ $fail -eq 0 ]; then + pass "$check_5_12" + fi + + # 5.13 + check_5_13="5.13 - Mount container's root filesystem as read only" + + # List all the running containers, ouput their ID and status of ReadonlyRootfs + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:{{.HostConfig.ReadonlyRootfs }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $cont_inspect; do + read_status=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $read_status = "false"; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_13" + warn " * Container running with root FS mounted R/W: $container_id" + fail=1 + else + warn " * Container running with root FS mounted R/W: $container_id" + fi + fi + done + # We went through all the containers and found no R/W FS mounts + if [ $fail -eq 0 ]; then + pass "$check_5_13" + fi + + # 5.14 + check_5_14="5.14 - Bind incoming container traffic to a specific host interface" + + # List all the running containers, ouput the IP where ports are being bound + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $containers; do + ip=`docker port $c | awk '{print $3}' | cut -d ':' -f1` + if test "$ip" = "0.0.0.0"; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_14" + warn " * Port being bound to wildcard IP: 0.0.0.0" + fail=1 + else + warn " * Port being bound to wildcard IP: 0.0.0.0" + fi + fi + done + # We went through all the containers and found no ports bound to 0.0.0.0 + if [ $fail -eq 0 ]; then + pass "$check_5_14" + fi + + # 5.15 + check_5_15="5.15 - Do not set the 'on-failure' container restart policy to always" + + # List all the running containers, ouput their ID and Restart Policy Name + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:RestartPolicyName={{.HostConfig.RestartPolicy.Name }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $cont_inspect; do + policy=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + + if test $policy = "RestartPolicyName=always"; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_15" + warn " * Restart Policy set to always: $container_id" + fail=1 + else + warn " * Restart Policy set to always: $container_id" + fi + fi + done + # We went through all the containers and found none with restart policy always + if [ $fail -eq 0 ]; then + pass "$check_5_15" + fi + + # 5.16 + check_5_16="5.16 - Do not share the host's process namespace" + + # List all the running containers, ouput their ID and PidMode + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:PidMode={{.HostConfig.PidMode }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $cont_inspect; do + mode=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $mode = "PidMode=host"; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_16" + warn " * Host PID namespace being shared with: $container_id" + fail=1 + else + warn " * Host PID namespace being shared with: $container_id" + fi + fi + done + # We went through all the containers and found none with PidMode as host + if [ $fail -eq 0 ]; then + pass "$check_5_16" + fi + + # 5.17 + check_5_17="5.17 - Do not share the host's IPC namespace" + + # List all the running containers, ouput their ID and IpcMode + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:IpcMode={{.HostConfig.IpcMode }}'` + # We have some containers running, set failure flag to 0, set failure flag to 0 + fail=0 + for c in $cont_inspect; do + mode=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $mode = "IpcMode=host"; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + warn "$check_5_17" + warn " * Host IPC namespace being shared with: $container_id" + fail=1 + else + warn " * Host IPC namespace being shared with: $container_id" + fi + fi + done + # We went through all the containers and found none with IPCMode as host + if [ $fail -eq 0 ]; then + pass "$check_5_17" + fi + + # 5.18 + check_5_18="5.18 - Do not directly expose host devices to containers" + + # List all the running containers, ouput their ID and host devices + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:Devices={{.HostConfig.Devices }}'` + fail=0 + for c in $cont_inspect; do + mode=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $mode != "Devices=[]" && test $mode != "Devices="; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + info "$check_5_18" + info " * Container has devices exposed directly: $container_id" + fail=1 + else + info " * Container has devices exposed directly: $container_id" + fi + fi + done + # We went through all the containers and found none with devices + if [ $fail -eq 0 ]; then + pass "$check_5_18" + fi + + # 5.19 + check_5_19="5.19 - Override default ulimit at runtime only if needed" + + # List all the running containers, ouput their ID and host devices + cont_inspect=`docker ps -q | xargs docker inspect --format '{{ .Id }}:Ulimits={{.HostConfig.Ulimits }}'` + fail=0 + for c in $cont_inspect; do + mode=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $mode = "Ulimits=" || test $mode = "Ulimits="; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + info "$check_5_19" + info " * Container no default ulimit override: $container_id" + fail=1 + else + info " * Container no default ulimit override: $container_id" + fi + fi + done + # We went through all the containers and found none without Ulimits + if [ $fail -eq 0 ]; then + pass "$check_5_19" + fi +fi diff --git a/tests/6_docker_security_operations.sh b/tests/6_docker_security_operations.sh new file mode 100644 index 0000000..16ea932 --- /dev/null +++ b/tests/6_docker_security_operations.sh @@ -0,0 +1,64 @@ +#!/bin/sh + +logit "\n" +info "6 - Docker Security Operations" + +# 6.5 +check_6_5="6.5 - Use a centralized and remote log collection service" + +containers=`docker ps -q` +# If containers is empty, there are no running containers +if test "$containers" = ""; then + info "$check_6_5" + info " * No containers running" +else + # List all the running containers, ouput their ID and host devices + containers=`docker ps -q | xargs docker inspect --format '{{ .Id}}:{{ .Volumes }}'` + # We have some containers running, set failure flag to 0. + fail=0 + # Make the loop separator be a new-line in POSIX compliant fashion + set -f; IFS=$' +' + for c in $containers; do + mode=`printf "$c" | cut -d ":" -f 2` + container_id=`printf "$c" | cut -d ":" -f 1` + if test $mode = "map[]"; then + # If it's the first container, fail the test + if [ $fail -eq 0 ]; then + info "$check_6_5" + info " * Container has no volumes, ensure centralized logging is enabled : $container_id" + fail=1 + else + info " * Container has no volumes, ensure centralized logging is enabled : $container_id" + fi + fi + done + # Only alert if there are no volumes. If there are volumes, can't know if they + # are used for logs +fi +# Make the loop separator go back to space +set +f; unset IFS + +# 6.6 +check_6_6="6.6 - Avoid image sprawl" +images=`docker images | wc -l | awk '{print $1}'` +if [ $images -gt 200 ]; then + warn "$check_6_6" + warn " * There are currently: $images images" +else + info "$check_6_6" + info " * There are currently: $images images" +fi + +# 6.7 +check_6_7="6.7 - 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=`expr "$total_containers" - "$running_containers"` +if [ $diff -gt 25 ]; then + warn "$check_6_7" + warn " * There are currently a total of $total_containers containers, with only $running_containers of them currently running" +else + info "$check_6_7" + info " * There are currently a total of $total_containers containers, with $running_containers of them currently running" +fi