Dockle - Simple Security Auditing and helping build the Best Docker Images
Dockle
helps you
- Build secure Docker images
- Checkpoints includes CIS Benchmarks
- Build Best Practice Docker images
You can check a docker image only run...
$ brew install goodwithtech/dockle/dockle
$ dockle [YOUR_IMAGE_NAME]
- Features
- Comparison
- Installation
- Checkpoint Summary
- Quick Start
- Examples
- Continuous Integration
- Checkpoint Detail
- Credits
- Roadmap
- Detect container's vulnerabilities
- Helping build Dockerfile best-practice
- Simple
- Specify only an image name
- See Quick Start and Examples
- Support CIS Benchmarks
- High accuracy
- DevSecOps
- Suitable for CI such as Travis CI, CircleCI, Jenkins, etc.
- See CI Example
Dockle | Hadolint | Docker Bench for Security | |
---|---|---|---|
Target | BuildImage | Dockerfile | Host DockerDaemon BuildImage ContainerRuntime |
How to run | Binary | Binary | ShellScript |
Dependency | No | No | Some dependencies |
Suitable CI | ✓ | ✓ | x |
Purpose | SecurityAudit DockerfileLint |
DockerfileLint | SecurityAudit DockerfileLint |
Covered CIS Benchmarks(Docker Image and Build File) | 7 | 3 | 5 |
Detail of CIS Benchmark
Dockle | Docker Bench for Security | Hadolint | |
---|---|---|---|
1. Create a user for the container | ✓ | ✓ | ✓ |
2. Use trusted base images for containers | - | – | - |
3. Do not install unnecessary packages in the container | - | - | - |
4. Scan and rebuild the images to include security patches | - | - | - |
5. Enable Content trust for Docker | ✓ | ✓ | - |
6. Add HEALTHCHECK instruction to the container image | ✓ | ✓ | - |
7. Do not use update instructions alone in the Dockerfile | ✓ | ✓ | ✓ |
8. Remove setuid and setgid permissions in the images | ✓ | - | - |
9. Use COPY instead of ADD in Dockerfile | ✓ | ✓ | ✓ |
10. Do not store secrets in Dockerfiles | ✓ | - | - |
11. Install verified packages only | - | - | - |
7 | 5 | 3 |
All checkpoints here!
$ brew install goodwithtech/dockle/dockle
$ VERSION=$(
curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
grep '"tag_name":' | \
sed -E 's/.*"v([^"]+)".*/\1/' \
) && rpm -ivh https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.rpm
$ VERSION=$(
curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
grep '"tag_name":' | \
sed -E 's/.*"v([^"]+)".*/\1/' \
) && curl -L -o dockle.deb https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.deb
$ sudo dpkg -i dockle.deb && rm dockle.deb
You can use homebrew on Mac OS.
$ brew install goodwithtech/dockle/dockle
$ VERSION=$(
curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
grep '"tag_name":' | \
sed -E 's/.*"v([^"]+)".*/\1/' \
) && curl -L -o dockle.zip https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Windows-64bit.zip
$ unzip dockle.zip && rm dockle.zip
$ ./dockle.exe [IMAGE_NAME]
Get the latest version from this page, and download the archive file for your operating system/architecture. Unpack the archive, and put the binary somewhere in your $PATH
(on UNIX-y systems, /usr/local/bin or the like). Make sure it has execution bits turned on.
$ go get -u github.com/goodwithtech/dockle
Simply specify an image name (and a tag).
$ dockle [YOUR_IMAGE_NAME]
Result
FATAL - Create a user for the container
* Last user should not be root
WARN - Enable Content trust for Docker
* export DOCKER_CONTENT_TRUST=1 before docker pull/build
FATAL - Add HEALTHCHECK instruction to the container image
* not found HEALTHCHECK statement
FATAL - Do not use update instructions alone in the Dockerfile
* Use 'Always combine RUN apt-get update with apt-get install' : /bin/sh -c apt-get update && apt-get install -y git
PASS - Remove setuid and setgid permissions in the images
FATAL - Use COPY instead of ADD in Dockerfile
* Use COPY : /bin/sh -c #(nop) ADD file:81c0a803075715d1a6b4f75a29f8a01b21cc170cfc1bff6702317d1be2fe71a3 in /app/credentials.json
FATAL - Do not store secrets in ENVIRONMENT variables
* Suspicious ENV key found : MYSQL_PASSWD
FATAL - Do not store secret files
* Suspicious filename found : app/credentials.json
PASS - Avoid sudo command
FATAL - Avoid sensitive directory mounting
* Avoid mounting sensitive dirs : /usr
PASS - Avoid apt-get/apk/dist-upgrade
PASS - Use apk add with --no-cache
FATAL - Clear apt-get caches
* Use 'apt-get clean && rm -rf /var/lib/apt/lists/*' : /bin/sh -c apt-get update && apt-get install -y git
PASS - Avoid latest tag
FATAL - Avoid empty password
* No password user found! username : nopasswd
PASS - Be unique UID
PASS - Be unique GROUP
Replace [YOUR_CACHE_DIR] with the cache directory on your machine.
$ docker run --rm -v [YOUR_CACHE_DIR]:/root/.cache/ goodwithtech/dockle [YOUR_IMAGE_NAME]
Example for macOS:
$ docker run --rm -v $HOME/Library/Caches:/root/.cache/ goodwithtech/dockle [YOUR_IMAGE_NAME]
If you would like to scan the image on your host machine, you need to mount docker.sock
.
$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock ...
Please re-pull latest goodwithtech/dockle
if an error occured.
CODE | DESCRIPTION | LEVEL※ |
---|---|---|
CIS's Docker Image Checkpoints | ||
CIS-DI-0001 | Create a user for the container | WARN |
CIS-DI-0002 | Use trusted base images for containers | FATAL |
CIS-DI-0003 | Do not install unnecessary packages in the container | FATAL |
CIS-DI-0004 | Scan and rebuild the images to include security patches | FATAL |
CIS-DI-0006 | Add HEALTHCHECK instruction to the container image | WARN |
CIS-DI-0007 | Do not use update instructions alone in the Dockerfile | FATAL |
CIS-DI-0008 | Remove setuid and setgid permissions in the images | INFO |
CIS-DI-0009 | Use COPY instead of ADD in Dockerfile | FATAL |
CIS-DI-0010 | Do not store secrets in Dockerfiles | FATAL |
CIS-DI-0011 | Install verified packages only | INFO |
Dockle Checkpoints for Docker | ||
DKL-DI-0001 | Avoid sudo command |
FATAL |
DKL-DI-0002 | Avoid sensitive directory mounting | FATAL |
DKL-DI-0003 | Avoid apt-get upgrade , apk upgrade , dist-upgrade |
FATAL |
DKL-DI-0004 | Use apk add with --no-cache |
FATAL |
DKL-DI-0005 | Clear apt-get caches | FATAL |
DKL-DI-0006 | Avoid latest tag |
WARN |
Dockle Checkpoints for Linux | ||
DKL-LI-0001 | Avoid empty password | FATAL |
DKL-LI-0002 | Be unique UID/GROUPs | FATAL |
Dockle
has 5 check levels
LEVEL | DESCRIPTION |
---|---|
FATAL | Be practical and prudent |
WARN | Be practical and prudent, but limited uses (official docker image ) |
INFO | May negatively inhibit the utility or performance |
SKIP | Not found target files |
PASS | Not found any problems |
Simply specify an image name (and a tag).
$ dockle goodwithtech/test-image:v1
Result
FATAL - CIS-DI-0001: Create a user for the container
* Last user should not be root
WARN - CIS-DI-0005: Enable Content trust for Docker
* export DOCKER_CONTENT_TRUST=1 before docker pull/build
FATAL - CIS-DI-0006: Add HEALTHCHECK instruction to the container image
* not found HEALTHCHECK statement
FATAL - CIS-DI-0007: Do not use update instructions alone in the Dockerfile
* Use 'Always combine RUN 'apt-get update' with 'apt-get install' : /bin/sh -c apt-get update && apt-get install -y git
FATAL - CIS-DI-0008: Remove setuid and setgid permissions in the images
* Found setuid file: etc/passwd grw-r--r--
* Found setuid file: usr/lib/openssh/ssh-keysign urwxr-xr-x
* Found setuid file: app/hoge.txt ugrw-r--r--
* Found setuid file: app/hoge.txt ugrw-r--r--
* Found setuid file: etc/shadow urw-r-----
FATAL - CIS-DI-0009: Use COPY instead of ADD in Dockerfile
* Use COPY : /bin/sh -c #(nop) ADD file:81c0a803075715d1a6b4f75a29f8a01b21cc170cfc1bff6702317d1be2fe71a3 in /app/credentials.json
FATAL - CIS-DI-0010: Do not store secrets in ENVIRONMENT variables
* Suspicious ENV key found : MYSQL_PASSWD
FATAL - CIS-DI-0010: Do not store secret files
* Suspicious filename found : app/credentials.json
PASS - DKL-DI-0001: Avoid sudo command
FATAL - DKL-DI-0002: Avoid sensitive directory mounting
* Avoid mounting sensitive dirs : /usr
PASS - DKL-DI-0003: Avoid apt-get/apk/dist-upgrade
PASS - DKL-DI-0004: Use apk add with --no-cache
FATAL - DKL-DI-0005: Clear apt-get caches
* Use 'apt-get clean && rm -rf /var/lib/apt/lists/*' : /bin/sh -c apt-get update && apt-get install -y git
PASS - DKL-DI-0006: Avoid latest tag
FATAL - DKL-LI-0001: Avoid empty password
* No password user found! username : nopasswd
PASS - DKL-LI-0002: Be unique UID
PASS - DKL-LI-0002: Be unique GROUP
$ docker save alpine:latest -o alpine.tar
$ dockle --input alpine.tar
$ dockle -f json goodwithtech/test-image:v1
$ dockle -f json -o results.json goodwithtech/test-image:v1
Result
{
"summary": {
"fatal": 6,
"warn": 2,
"info": 2,
"pass": 7
},
"details": [
{
"code": "CIS-DI-0001",
"title": "Create a user for the container",
"level": "WARN",
"alerts": [
"Last user should not be root"
]
},
{
"code": "CIS-DI-0005",
"title": "Enable Content trust for Docker",
"level": "INFO",
"alerts": [
"export DOCKER_CONTENT_TRUST=1 before docker pull/build"
]
},
{
"code": "CIS-DI-0006",
"title": "Add HEALTHCHECK instruction to the container image",
"level": "WARN",
"alerts": [
"not found HEALTHCHECK statement"
]
},
{
"code": "CIS-DI-0008",
"title": "Remove setuid and setgid permissions in the images",
"level": "INFO",
"alerts": [
"Found setuid file: usr/lib/openssh/ssh-keysign urwxr-xr-x"
]
},
{
"code": "CIS-DI-0009",
"title": "Use COPY instead of ADD in Dockerfile",
"level": "FATAL",
"alerts": [
"Use COPY : /bin/sh -c #(nop) ADD file:81c0a803075715d1a6b4f75a29f8a01b21cc170cfc1bff6702317d1be2fe71a3 in /app/credentials.json "
]
},
{
"code": "CIS-DI-0010",
"title": "Do not store secrets in ENVIRONMENT variables",
"level": "FATAL",
"alerts": [
"Suspicious ENV key found : MYSQL_PASSWD"
]
},
{
"code": "CIS-DI-0010",
"title": "Do not store secret files",
"level": "FATAL",
"alerts": [
"Suspicious filename found : app/credentials.json "
]
},
{
"code": "DKL-DI-0002",
"title": "Avoid sensitive directory mounting",
"level": "FATAL",
"alerts": [
"Avoid mounting sensitive dirs : /usr"
]
},
{
"code": "DKL-DI-0005",
"title": "Clear apt-get caches",
"level": "FATAL",
"alerts": [
"Use 'rm -rf /var/lib/apt/lists' after 'apt-get install' : /bin/sh -c apt-get update \u0026\u0026 apt-get install -y git"
]
},
{
"code": "DKL-LI-0001",
"title": "Avoid empty password",
"level": "FATAL",
"alerts": [
"No password user found! username : nopasswd"
]
}
]
}
By default, Dockle
exits with code 0 even if there are some problems.
Use the --exit-code option if you may want to exit with a non-zero exit code.
$ dockle -exist-code 1 [IMAGE_NAME]
Use .dockleignore
.
$ cat .dockleignore
# set root to default user because we want to run nginx
CIS-DI-0001
# Use latest tag because only check for image inside
DKL-DI-0006
The --clear-cache
option removes image caches. This option is useful if the image which has the same tag is updated (such as when using latest
tag).
$ dockle --clear-cache python:3.7
Scan your image built in Travis CI/CircleCI.
The test will fail with if a error is found.
You can skip target checkpoint if you use .dockleignore
.
When you don't would like to fail the test, specify --exit-code 0
.
services:
- docker
env:
global:
- COMMIT=${TRAVIS_COMMIT::8}
before_install:
- docker build -t dockle-ci-test:${COMMIT} .
- export VERSION=$(curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | grep '"tag_name":' | sed -E 's/.*"v([^"]+)".*/\1/')
- wget https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.tar.gz
- tar zxvf dockle_${VERSION}_Linux-64bit.tar.gz
script:
- ./dockle dockle-ci-test:${COMMIT}
- ./dockle --exit-code 1 dockle-ci-test:${COMMIT}
Example: https://travis-ci.org/goodwithtech/dockle-ci-test
Repository: https://github.com/goodwithtech/dockle-ci-test
jobs:
build:
docker:
- image: docker:18.09-git
steps:
- checkout
- setup_remote_docker
- run:
name: Build image
command: docker build -t dockle-ci-test:${CIRCLE_SHA1} .
- run:
name: Install dockle
command: |
apk add --update curl
VERSION=$(
curl --silent "https://api.github.com/repos/goodwithtech/dockle/releases/latest" | \
grep '"tag_name":' | \
sed -E 's/.*"v([^"]+)".*/\1/'
)
wget https://github.com/goodwithtech/dockle/releases/download/v${VERSION}/dockle_${VERSION}_Linux-64bit.tar.gz
tar zxvf dockle_${VERSION}_Linux-64bit.tar.gz
mv dockle /usr/local/bin
- run:
name: Scan the local image with dockle
command: dockle --exit-code 1 dockle-ci-test:${CIRCLE_SHA1}
workflows:
version: 2
release:
jobs:
- build
Example : https://circleci.com/gh/goodwithtech/dockle-ci-test
Repository: https://github.com/goodwithtech/dockle-ci-test
Dockle
can download images from private registry, without installing Docker
and any 3rd party tools.
That's because it's easy to run in a CI process.
All you have to do is install Dockle
and set ENVIRONMENT variables.
But, I can't recommend using ENV vars in your local machine to you.
Docker Hub needs DOCKLE_AUTH_URL
, DOCKLE_USERNAME
and DOCKLE_PASSWORD
.
You don't need to set ENV vars when download from public repository.
export DOCKLE_AUTH_URL=https://registry.hub.docker.com
export DOCKLE_USERNAME={DOCKERHUB_USERNAME}
export DOCKLE_PASSWORD={DOCKERHUB_PASSWORD}
Dockle
uses AWS SDK. You don't need to install aws
CLI tool.
You can use AWS CLI's ENVIRONMENT variables.
Dockle
uses Google Cloud SDK. So, you don't need to install gcloud
command.
If you would like to use target project's repository, you can settle via GOOGLE_APPLICATION_CREDENTIAL
.
# must set DOCKLE_USERNAME empty char
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/credential.json
BasicAuth server needs DOCKLE_USERNAME
and DOCKLE_PASSWORD
.
export DOCKLE_USERNAME={USERNAME}
export DOCKLE_PASSWORD={PASSWORD}
# if you'd like to use 80 port, use NonSSL
export DOCKLE_NON_SSL=true
These checkpoints refered to CIS Docker 1.13.0 Benchmark v1.0.0.
Create a non-root user for the container in the Dockerfile for the container image.
It is a good practice to run the container as a non-root user, if possible.
# Dockerfile
RUN useradd -d /home/dockle -m -s /bin/bash dockle
USER dockle
or
RUN addgroup -S dockle && adduser -S -G dockle dockle
USER dockle
Dockle checks Content Trust. Please check with Trivy.
Not supported.
Not supported. Please check with Trivy.
Content trust is disabled by default. You should enable it.
$ export DOCKER_CONTENT_TRUST=1
https://docs.docker.com/engine/security/trust/content_trust/#about-docker-content-trust-dct
Docker Content Trust (DCT) provides the ability to use digital signatures for data sent to and received from remote Docker registries. Engine Signature Verification prevents the following:
$ docker container run
of an unsigned image.$ docker pull
of an unsigned image.$ docker build
where the FROM image is not signed or is not scratch.
Add
HEALTHCHECK
instruction in your docker container images to perform the health check on running containers.
Based on the reported health status, the docker engine could then exit non-working containers and instantiate new ones.
# Dockerfile
HEALTHCHECK --interval=5m --timeout=3s \
CMD curl -f http://localhost/ || exit 1
Do not use update instructions such as apt-get update alone or in a single line in the Dockerfile.
Adding the update instructions in a single line on the Dockerfile will cache the update layer.
RUN apt-get update && apt-get install -y package-a
Removing setuid and setgid permissions in the images would prevent privilege escalation attacks in the containers.
setuid and setgid permissions could be used for elevating privileges.
chmod u-s setuid-file
chmod u-g setgid-file
Use COPY instruction instead of ADD instruction in the Dockerfile.
ADD
instruction introduces risks such as adding malicious files from URLs without scanning and unpacking procedure vulnerabilities.
# Dockerfile
ADD test.json /app/test.json
↓
COPY test.json /app/test.json
Do not store any secrets in Dockerfiles.
the secrets within these Dockerfiles could be easily exposed and potentially be exploited.
Dockle
checks ENVIRONMENT variables and credential files.
Not supported. It's better to use Trivy.
These checkpoints refered to Docker Best Practice and so on.
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#user
Avoid installing or using sudo as it has unpredictable TTY and signal-forwarding behavior that can cause problems.
A volume mount makes weakpoints.
This depends on mounting volumes.
Currently, Dockle
checks following directories.
/boot
,/dev
,/etc
,/lib
,/proc
,/sys
, /usr
dockle
only checks VOLUME
statements. We can't check docker run -v /lib:/lib ...
.
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get
Avoid RUN apt-get upgrade and dist-upgrade, as many of the “essential” packages from the parent images cannot upgrade inside an unprivileged container.
https://github.com/gliderlabs/docker-alpine/blob/master/docs/usage.md#disabling-cache
As of Alpine Linux 3.3 there exists a new --no-cache option for apk. It allows users to install packages with an index that is updated and used on-the-fly and not cached locally: This avoids the need to use --update and remove /var/cache/apk/* when done installing packages.
Use “apt-get clearn && rm -rf /var/lib/apt/lists/*` if use apt-get install
https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#apt-get
In addition, when you clean up the apt cache by removing /var/lib/apt/lists it reduces the image size, since the apt cache is not stored in a layer. Since the RUN statement starts with apt-get update, the package cache is always refreshed prior to apt-get install.
https://vsupalov.com/docker-latest-tag/
Docker images tagged with :latest have caused many people a lot of trouble.
These checkpoints refered to Linux Best Practices and so on.
https://blog.aquasec.com/cve-2019-5021-alpine-docker-image-vulnerability
CVE-2019-5021: Alpine Docker Image ‘null root password’ Vulnerability
Contrary to popular belief, it is not necessary that each entry in the UID field be unique. However, non-unique UIDs can cause security problems, and thus UIDs should be kept unique across the entire organization.
Special Thanks to @knqyf263 (Teppei Fukuda) and Trivy
AGPLv3
@tomoyamachi (Tomoya Amachi)
- JSON output
- Check php.ini file
- Check nginx.conf file
- create CI badges
- Check /etc/hosts
- duplicates
- hostname
- localhost
- Packages
- Package managers
- File Permissions
- Insecure permission
- Image Size
- check large size container
- Networking
-
docker port container
if docker running - by file
- /proc/1/net/tcp : openning port (if running)
-
- Volume mount
- mount dangerous
- /boot, /dev, /etc, /lib
- mount dangerous