2015-05-11 06:08:28 +02:00
#!/bin/sh
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
2019-08-26 14:11:10 +02:00
version = '1.3.5'
2018-05-10 15:19:10 +02:00
2018-10-25 12:05:48 +02:00
# Load dependencies
. ./functions_lib.sh
. ./helper_lib.sh
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
2015-05-29 13:42:34 +02: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-09 16:50:00 +01:00
req_progs = 'awk docker grep stat tee tail wc xargs truncate'
2015-05-11 06:08:28 +02:00
for p in $req_progs ; do
2015-05-29 13:42:34 +02:00
command -v " $p " >/dev/null 2>& 1 || { printf "%s command not found.\n" " $p " ; exit 1; }
2015-05-11 06:08:28 +02:00
done
2020-05-08 13:09:52 +02:00
if command -v ss >/dev/null 2>& 1; then
netbin = ss
elif command -v netstat >/dev/null 2>& 1; then
netbin = netstat
else
echo "ss or netstat command not found."
exit 1
fi
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
usage: ${ myname } [ options]
2015-05-11 06:08:28 +02:00
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
2018-02-27 15:43:51 +01:00
-c CHECK optional Comma delimited list of specific check( s)
2018-05-10 14:45:59 +02:00
-e CHECK optional Comma delimited list of specific check( s) 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) .
2015-09-05 01:25:13 +02:00
EOF
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.
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
while getopts bhl:c:e:i:x:t:n: 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 " ; ;
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 " ; ;
2015-09-05 01:34:00 +02:00
*) usage; exit 1 ; ;
2015-05-11 06:08:28 +02:00
esac
done
2015-09-05 16:23:34 +02:00
if [ -z " $logger " ] ; then
logger = " ${ myname } .log "
fi
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
if [ -z " $limit " ] ; then
limit = 0
fi
2018-10-25 12:05:48 +02:00
# Load output formating
2018-10-25 11:34:14 +02:00
. ./output_lib.sh
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
ID = $( id -u)
if [ " x $ID " != "x0" ] ; then
2018-05-10 15:41:27 +02:00
warn "Some tests might require root to run"
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-09 12:27:32 +01:00
logit " Initializing $( date +%Y-%m-%dT%H:%m:%S%:z) \n "
2021-03-09 15:06:38 +01:00
appendjson
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 ( ) {
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-01-16 13:44:43 +01:00
if [ -z " $containers " ] ; then
running_containers = 0
else
running_containers = 1
fi
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
check = $( sed -ne "/cis() {/,/}/{/{/d; /}/d; p}" functions_lib.sh)
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
loop_checks = " $( sed -ne " / $c () {/,/}/{/{/d; /}/d; p} " functions_lib.sh) "
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
2017-10-23 15:38:08 +02:00
printf "\n"
info " Checks: $totalChecks "
info " Score: $currentScore "
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 " $@ "