2021-05-25 20:44:18 +02:00
#!/bin/bash
2021-03-10 09:01:18 +01:00
# --------------------------------------------------------------------------------------------
2018-05-10 15:19:10 +02:00
# Docker Bench for Security
2015-05-11 06:08:28 +02:00
#
2021-03-09 20:43:25 +01:00
# Docker, Inc. (c) 2015-2021
2015-05-11 06:08:28 +02:00
#
2015-06-28 20:04:53 +02:00
# Checks for dozens of common best-practices around deploying Docker containers in production.
2021-03-10 09:01:18 +01:00
# --------------------------------------------------------------------------------------------
2015-05-11 06:08:28 +02:00
2021-03-17 13:05:48 +01:00
version = '1.3.6'
2018-05-10 15:19:10 +02:00
2018-10-25 12:05:48 +02:00
# Load dependencies
2021-03-16 09:11:29 +01:00
. ./functions/functions_lib.sh
. ./functions/helper_lib.sh
2018-10-25 12:05:48 +02:00
2015-05-11 06:08:28 +02:00
# Setup the paths
2018-05-10 15:41:27 +02:00
this_path = $( abspath " $0 " ) ## Path of this file including filename
2021-03-27 08:36:10 +01:00
myname = $( basename " ${ this_path %.* } " ) ## file name of this script.
2015-05-11 06:08:28 +02:00
2018-05-10 15:41:27 +02:00
readonly version
readonly this_path
readonly myname
2019-10-07 16:26:20 +02:00
export PATH = " $PATH :/bin:/sbin:/usr/bin:/usr/local/bin:/usr/sbin/ "
2015-05-11 06:08:28 +02:00
# Check for required program(s)
2021-03-29 14:22:14 +02:00
req_programs 'awk docker grep stat tee tail wc xargs truncate sed'
2020-05-08 13:09:52 +02:00
2015-05-11 06:08:28 +02:00
# Ensure we can connect to docker daemon
2017-02-17 15:03:29 +01:00
if ! docker ps -q >/dev/null 2>& 1; then
2015-05-11 06:08:28 +02:00
printf "Error connecting to docker daemon (does docker ps work?)\n"
exit 1
fi
usage ( ) {
2015-09-05 01:25:13 +02:00
cat <<EOF
2021-03-11 09:21:13 +01:00
Docker Bench for Security - Docker, Inc. ( c) 2015-$( date +"%Y" )
Checks for dozens of common best-practices around deploying Docker containers in production.
2021-05-25 20:44:18 +02:00
Based on the CIS Docker Benchmark 1.3.1.
2015-05-11 06:08:28 +02:00
2021-03-27 08:36:10 +01:00
Usage: ${ myname } .sh [ OPTIONS]
2021-03-11 09:21:13 +01:00
Example:
- Only run check "2.2 - Ensure the logging level is set to 'info'" :
sh docker-bench-security.sh -c check_2_2
- Run all available checks except the host_configuration group and "2.8 - Enable user namespace support" :
sh docker-bench-security.sh -e host_configuration,check_2_8
- Run just the container_images checks except "4.5 - Ensure Content trust for Docker is Enabled" :
sh docker-bench-security.sh -c container_images -e check_4_5
Options:
2018-10-25 11:34:14 +02:00
-b optional Do not print colors
2015-09-05 01:25:13 +02:00
-h optional Print this help message
2021-03-10 09:01:18 +01:00
-l FILE optional Log output in FILE, inside container if run using docker
2021-03-16 09:11:29 +01:00
-u USERS optional Comma delimited list of trusted docker user( s)
-c CHECK optional Comma delimited list of specific check( s) id
-e CHECK optional Comma delimited list of specific check( s) id to exclude
2019-07-30 12:25:14 +02:00
-i INCLUDE optional Comma delimited list of patterns within a container or image name to check
-x EXCLUDE optional Comma delimited list of patterns within a container or image name to exclude from check
Limit the number of reported items
In some evironments, there may be a very large number of images,
containers, etc not satisfying a given test. For example, in one
environment, we saw *378k* images not satisfying 4.6, mostly because
the customer was never cleaning up old images.
To avoid overly long lists of items, add a new option "-n LIMIT" that
limits the number of items included in JSON output. When the limit is
reached, the list will be truncated and a trailing (truncated) will be
added. Here's an example:
```
{"id": "5.9", "desc": "Ensure the host's network namespace is not
shared", "result": "WARN", "details": "Containers running with
networking mode 'host': k8s_POD_storage-provisioner_kube-system_ef960ef5-62c5-11e9-802f-08002719228f_0
k8s_POD_kube-proxy-xfln8_kube-system_ee70c4c3-62c5-11e9-802f-08002719228f_0 (truncated)",
"items":
["k8s_POD_storage-provisioner_kube-system_ef960ef5-62c5-11e9-802f-08002719228f_0","k8s_POD_kube-proxy-xfln8_kube-system_ee70c4c3-62c5-11e9-802f-08002719228f_0","(truncated)"]},
```
Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2020-07-10 22:00:29 +02:00
-n LIMIT optional In JSON output, when reporting lists of items ( containers, images, etc.) , limit the number of reported items to LIMIT. Default 0 ( no limit) .
2021-05-25 20:44:18 +02:00
-p PRINT optional Print remediation measures. Default: Don' t print remediation measures.
2021-03-11 09:21:13 +01:00
2021-03-16 09:11:29 +01:00
Complete list of checks: <https://github.com/docker/docker-bench-security/blob/master/tests/>
2021-03-11 09:21:13 +01:00
Full documentation: <https://github.com/docker/docker-bench-security>
Released under the Apache-2.0 License.
2015-09-05 01:25:13 +02:00
EOF
2015-05-11 06:08:28 +02:00
}
2021-03-28 10:08:15 +02:00
# Default values
if [ ! -d log ] ; then
mkdir log
fi
2021-05-25 20:44:18 +02:00
2021-03-28 10:08:15 +02:00
logger = " log/ ${ myname } .log "
limit = 0
2021-05-25 20:44:18 +02:00
printremediation = "0"
2021-03-29 14:22:14 +02:00
globalRemediation = ""
2021-03-28 10:08:15 +02:00
2015-05-11 06:08:28 +02:00
# Get the flags
2015-09-05 01:25:13 +02:00
# If you add an option here, please
# remember to update usage() above.
2021-03-16 09:11:29 +01:00
while getopts bhl:u:c:e:i:x:t:n:p args
2015-05-11 06:08:28 +02:00
do
case $args in
2018-10-25 11:34:14 +02:00
b) nocolor = "nocolor" ; ;
2015-09-05 01:34:00 +02:00
h) usage; exit 0 ; ;
2015-05-11 06:08:28 +02:00
l) logger = " $OPTARG " ; ;
2021-03-16 09:11:29 +01:00
u) dockertrustusers = " $OPTARG " ; ;
2018-01-16 13:44:43 +01:00
c) check = " $OPTARG " ; ;
2018-05-10 14:45:59 +02:00
e) checkexclude = " $OPTARG " ; ;
2018-10-15 16:21:00 +02:00
i) include = " $OPTARG " ; ;
2018-02-27 15:43:51 +01:00
x) exclude = " $OPTARG " ; ;
Limit the number of reported items
In some evironments, there may be a very large number of images,
containers, etc not satisfying a given test. For example, in one
environment, we saw *378k* images not satisfying 4.6, mostly because
the customer was never cleaning up old images.
To avoid overly long lists of items, add a new option "-n LIMIT" that
limits the number of items included in JSON output. When the limit is
reached, the list will be truncated and a trailing (truncated) will be
added. Here's an example:
```
{"id": "5.9", "desc": "Ensure the host's network namespace is not
shared", "result": "WARN", "details": "Containers running with
networking mode 'host': k8s_POD_storage-provisioner_kube-system_ef960ef5-62c5-11e9-802f-08002719228f_0
k8s_POD_kube-proxy-xfln8_kube-system_ee70c4c3-62c5-11e9-802f-08002719228f_0 (truncated)",
"items":
["k8s_POD_storage-provisioner_kube-system_ef960ef5-62c5-11e9-802f-08002719228f_0","k8s_POD_kube-proxy-xfln8_kube-system_ee70c4c3-62c5-11e9-802f-08002719228f_0","(truncated)"]},
```
Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2020-07-10 22:00:29 +02:00
n) limit = " $OPTARG " ; ;
2021-05-25 20:44:18 +02:00
p) printremediation = "1" ; ;
2015-09-05 01:34:00 +02:00
*) usage; exit 1 ; ;
2015-05-11 06:08:28 +02:00
esac
done
2018-10-25 12:05:48 +02:00
# Load output formating
2021-03-16 09:11:29 +01:00
. ./functions/output_lib.sh
2018-10-25 11:34:14 +02:00
2018-05-10 15:19:10 +02:00
yell_info
2015-09-05 16:23:34 +02:00
2015-09-05 15:51:51 +02:00
# Warn if not root
2021-05-25 20:44:18 +02:00
if [ " $( id -u) " != "0" ] ; then
2021-03-16 09:11:29 +01:00
warn " $( yell 'Some tests might require root to run' ) \n "
2018-05-10 15:41:27 +02:00
sleep 3
2015-09-05 15:51:51 +02:00
fi
2017-10-23 15:38:08 +02:00
# Total Score
# Warn Scored -1, Pass Scored +1, Not Score -0
totalChecks = 0
currentScore = 0
2021-03-19 12:51:31 +01:00
logit " Initializing $( date +%Y-%m-%dT%H:%M:%S%:z) \n "
2018-05-10 15:19:10 +02:00
beginjson " $version " " $( date +%s) "
2015-09-05 15:44:44 +02:00
2015-05-11 06:08:28 +02:00
# Load all the tests from tests/ and run them
main ( ) {
2021-05-25 20:44:18 +02:00
logit " \n ${ bldylw } Section A - Check results ${ txtrst } "
2021-03-10 20:47:52 +01:00
2019-12-17 15:03:54 +01:00
# Get configuration location
get_docker_configuration_file
2015-07-25 07:08:14 +02:00
# If there is a container with label docker_bench_security, memorize it:
2015-05-14 02:08:12 +02:00
benchcont = "nil"
2018-11-23 10:50:34 +01:00
for c in $( docker ps | sed '1d' | awk '{print $NF}' ) ; do
2017-02-17 15:03:29 +01:00
if docker inspect --format '{{ .Config.Labels }}' " $c " | \
grep -e 'docker.bench.security' >/dev/null 2>& 1; then
benchcont = " $c "
fi
2015-05-14 02:08:12 +02:00
done
2019-10-16 09:49:18 +02:00
2019-12-09 15:19:17 +01:00
# Get the image id of the docker_bench_security_image, memorize it:
2019-07-29 17:16:14 +02:00
benchimagecont = "nil"
for c in $( docker images | sed '1d' | awk '{print $3}' ) ; do
if docker inspect --format '{{ .Config.Labels }}' " $c " | \
grep -e 'docker.bench.security' >/dev/null 2>& 1; then
benchimagecont = " $c "
fi
done
2018-10-15 16:21:00 +02:00
if [ -n " $include " ] ; then
pattern = $( echo " $include " | sed 's/,/|/g' )
containers = $( docker ps | sed '1d' | awk '{print $NF}' | grep -v " $benchcont " | grep -E " $pattern " )
2019-12-04 12:14:43 +01:00
images = $( docker images | sed '1d' | grep -E " $pattern " | awk '{print $3}' | grep -v " $benchimagecont " )
2018-10-15 16:21:00 +02:00
elif [ -n " $exclude " ] ; then
2018-02-27 15:43:51 +01:00
pattern = $( echo " $exclude " | sed 's/,/|/g' )
2018-10-15 16:21:00 +02:00
containers = $( docker ps | sed '1d' | awk '{print $NF}' | grep -v " $benchcont " | grep -Ev " $pattern " )
2019-12-04 12:14:43 +01:00
images = $( docker images | sed '1d' | grep -Ev " $pattern " | awk '{print $3}' | grep -v " $benchimagecont " )
2018-10-15 16:21:00 +02:00
else
containers = $( docker ps | sed '1d' | awk '{print $NF}' | grep -v " $benchcont " )
2019-07-29 17:16:14 +02:00
images = $( docker images -q | grep -v " $benchcont " )
2018-02-27 15:43:51 +01:00
fi
2015-05-14 04:22:39 +02:00
2018-05-10 15:41:27 +02:00
for test in tests/*.sh; do
. ./" $test "
2015-05-11 06:08:28 +02:00
done
2017-10-10 13:54:59 +02:00
2018-05-10 15:41:27 +02:00
if [ -z " $check " ] && [ ! " $checkexclude " ] ; then
2019-12-09 15:19:17 +01:00
# No options just run
2018-01-16 13:44:43 +01:00
cis
2019-12-09 15:19:17 +01:00
elif [ -z " $check " ] ; then
# No check defined but excludes defined set to calls in cis() function
2021-03-16 09:11:29 +01:00
check = $( sed -ne "/cis() {/,/}/{/{/d; /}/d; p}" functions/functions_lib.sh)
2019-12-09 15:19:17 +01:00
fi
for c in $( echo " $check " | sed "s/,/ /g" ) ; do
if ! command -v " $c " 2>/dev/null 1>& 2; then
echo " Check \" $c \" doesn't seem to exist. "
continue
fi
if [ -z " $checkexclude " ] ; then
# No excludes just run the checks specified
2018-05-10 14:45:59 +02:00
" $c "
2019-12-09 15:19:17 +01:00
else
# Exludes specified and check exists
checkexcluded = " $( echo " , $checkexclude " | sed -e 's/^/\^/g' -e 's/,/\$|/g' -e 's/$/\$/g' ) "
if echo " $c " | grep -E " $checkexcluded " 2>/dev/null 1>& 2; then
# Excluded
2018-02-27 15:43:51 +01:00
continue
2019-12-09 15:19:17 +01:00
elif echo " $c " | grep -vE 'check_[0-9]|check_[a-z]' 2>/dev/null 1>& 2; then
# Function not a check, fill loop_checks with all check from function
2021-03-16 09:11:29 +01:00
loop_checks = " $( sed -ne " / $c () {/,/}/{/{/d; /}/d; p} " functions/functions_lib.sh) "
2019-12-09 15:19:17 +01:00
else
# Just one check
loop_checks = " $c "
2018-02-27 15:43:51 +01:00
fi
2019-12-09 15:19:17 +01:00
for lc in $loop_checks ; do
if echo " $lc " | grep -vE " $checkexcluded " 2>/dev/null 1>& 2; then
# Not excluded
" $lc "
fi
done
fi
done
2018-01-16 13:44:43 +01:00
2021-03-16 09:11:29 +01:00
if [ -n " ${ globalRemediation } " ] && [ " $printremediation " = "1" ] ; then
2021-03-11 08:26:31 +01:00
logit " \n\n ${ bldylw } Section B - Remediation measures ${ txtrst } "
logit " ${ globalRemediation } "
fi
2021-03-10 20:47:52 +01:00
logit " \n\n ${ bldylw } Section C - Score ${ txtrst } \n "
2017-10-23 15:38:08 +02:00
info " Checks: $totalChecks "
2021-03-10 20:47:52 +01:00
info " Score: $currentScore \n "
2017-10-23 15:38:08 +02:00
Improve docker-bench-security json output
Add a test object for each test performed by the script. Each object has
an id N.M, a desc property describing the test, and the result. Some
tests include additional information about the test e.g. "No TLS
Certificate Found". That can be found in an optional details property of
the test object.
Also, some tests might also return a list of containers, images, users,
etc. This is included in an optional items property of the test object.
Instead of having all test results as top-level objects, break the test
results into sections. Each section has an id + description e.g. "1" and
"Host Configuration". The tests for that section are an array below that
object.
All of the additional json output is implemented by adding new functions
startsectionjson(), endsectionjson(), starttestjson(), and
resulttestjson() that take the id/desc/etc as arguments and print the
proper json properties. It also required adding an "end" test to each
script that calls endsectionjson().
Signed-off-by: Mark Stemm <mark.stemm@gmail.com>
2018-07-12 03:02:12 +02:00
endjson " $totalChecks " " $currentScore " " $( date +%s) "
2015-05-11 06:08:28 +02:00
}
main " $@ "