├── .gitignore ├── .rubocop.yml ├── .travis.yml ├── .github └── workflows │ ├── test.yml │ ├── release.yml │ └── codespell.yml ├── renovate.json ├── Gemfile ├── sample_attributes.yml ├── Rakefile ├── libraries └── docker_helper.rb ├── Vagrantfile ├── inspec.yml ├── README.md ├── controls ├── docker_security_operations.rb ├── container_images.rb ├── host_configuration.rb ├── docker_daemon_configuration_files.rb ├── docker_daemon_configuration.rb └── container_runtime.rb ├── LICENSE └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | Gemfile.lock 2 | .vagrant/ 3 | inspec.lock -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AllCops: 3 | Exclude: 4 | - vendor/**/* 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: ruby 3 | cache: bundler 4 | 5 | rvm: 6 | - 2.3.1 7 | 8 | bundler_args: --without integration 9 | script: bundle exec rake 10 | 11 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | schedule: 9 | - cron: '0 6 * * *' 10 | 11 | jobs: 12 | test: 13 | uses: dev-sec/.github/.github/workflows/baseline-test.yml@main 14 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: New release 3 | 4 | on: # yamllint disable-line rule:truthy 5 | workflow_dispatch: 6 | push: 7 | branches: 8 | - master 9 | 10 | jobs: 11 | generate_changelog: 12 | uses: dev-sec/.github/.github/workflows/baseline-release.yml@main 13 | -------------------------------------------------------------------------------- /.github/workflows/codespell.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Codespell - Spellcheck 3 | 4 | on: # yamllint disable-line rule:truthy 5 | push: 6 | branches: [master] 7 | pull_request: 8 | branches: [master] 9 | 10 | jobs: 11 | codespell: 12 | uses: "dev-sec/.github/.github/workflows/codespell.yml@main" 13 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:base", 5 | ":gitSignOff" 6 | ], 7 | "dependencyDashboard": true, 8 | "dependencyDashboardAutoclose": true, 9 | "packageRules": [ 10 | { 11 | "matchUpdateTypes": ["patch", "minor"], 12 | "automerge": true 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'cookstyle' 6 | gem 'highline' 7 | gem 'rack' 8 | gem 'rake' 9 | gem 'rubocop' 10 | 11 | group :tools do 12 | gem 'github_changelog_generator' 13 | gem 'pry-coolline' 14 | end 15 | 16 | source 'https://packagecloud.io/cinc-project/stable' do 17 | gem 'chef-config' 18 | gem 'cinc-auditor-bin' 19 | end 20 | -------------------------------------------------------------------------------- /sample_attributes.yml: -------------------------------------------------------------------------------- 1 | trusted_user: vagrant 2 | managable_container_number: 25 3 | registry_cert_path: /etc/docker/certs.d 4 | registry_name: /etc/docker/certs.d/registry_hostname:port 5 | registry_ca_file: /etc/docker/certs.d/registry_hostname:port/ca.crt 6 | container_user: vagrant 7 | container_capadd: 'NET_ADMIN' 8 | authorization_plugin: authz-broker 9 | log_driver: syslog 10 | log_opts: /syslog-address/ 11 | app_armor_profile: docker-default 12 | selinux_profile: /label\:level\:s0-s0\:c1023/ 13 | benchmark_version: 1.12.0 14 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'cookstyle' 4 | require 'rake/testtask' 5 | require 'rubocop/rake_task' 6 | 7 | # Rubocop 8 | desc 'Run Rubocop lint checks' 9 | task :rubocop do 10 | RuboCop::RakeTask.new 11 | end 12 | 13 | RuboCop::RakeTask.new(:cookstyle) do |task| 14 | task.options << '--display-cop-names' 15 | end 16 | 17 | # lint the project 18 | desc 'Run robocop linter' 19 | task lint: [:rubocop] 20 | 21 | # run tests 22 | task default: [:lint, 'test:check'] 23 | 24 | namespace :test do 25 | # run inspec check to verify that the profile is properly configured 26 | task :check do 27 | require 'inspec' 28 | puts "Checking profile with InSpec Version: #{Inspec::VERSION}" 29 | profile = Inspec::Profile.for_target('.', backend: Inspec::Backend.create(Inspec::Config.mock)) 30 | pp profile.check 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /libraries/docker_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2016, Christoph Hartmann 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | # 17 | # author: Christoph Hartmann 18 | # author: Dominik Richter 19 | # author: Patrick Muench 20 | 21 | class DockerHelper < Inspec.resource(1) 22 | name 'docker_helper' 23 | 24 | desc " 25 | A resource to retrieve information about docker 26 | " 27 | 28 | def path 29 | cmd = inspec.command('systemctl show -p FragmentPath docker.service') 30 | return if cmd.exit_status.to_i.nonzero? 31 | 32 | # parse data 33 | params = parse_systemd_values(cmd.stdout.chomp) 34 | 35 | # return the value 36 | params['FragmentPath'] 37 | end 38 | 39 | def socket 40 | cmd = inspec.command('systemctl show -p FragmentPath docker.socket') 41 | return if cmd.exit_status.to_i.nonzero? 42 | 43 | # parse data 44 | params = parse_systemd_values(cmd.stdout.chomp) 45 | 46 | # return the value 47 | params['FragmentPath'] 48 | end 49 | 50 | def overlay_networks 51 | cmd = inspec.command('docker network ls -f driver=overlay -q') 52 | return if cmd.exit_status.to_i.nonzero? 53 | 54 | # parse data from docker network ls 55 | params = parse_systemd_values(cmd.stdout.chomp) 56 | 57 | # parse data from docker network inspect output 58 | params.each do |k, _v| 59 | params[k] = parse_network_values(inspec.command("docker network inspect #{k}").stdout.delete('\"').delete(',').chomp) 60 | end 61 | 62 | # return the value 63 | params 64 | end 65 | 66 | private 67 | 68 | # returns parsed params 69 | def parse_systemd_values(stdout) 70 | SimpleConfig.new( 71 | stdout, 72 | assignment_regex: /^\s*([^=]*?)\s*=\s*(.*?)\s*$/, 73 | multiple_values: false 74 | ).params 75 | end 76 | 77 | # returns parsed params 78 | def parse_network_values(stdout) 79 | SimpleConfig.new( 80 | stdout, 81 | assignment_regex: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/, 82 | multiple_values: false 83 | ).params 84 | end 85 | end 86 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # -*- mode: ruby -*- 4 | # vi: set ft=ruby : 5 | 6 | Vagrant.configure(2) do |config| 7 | config.vm.define :ubuntu1604 do |ubuntu1604| 8 | ubuntu1604.vm.box = 'ubuntu/xenial64' # https://atlas.hashicorp.com/ubuntu/boxes/xenial64 9 | # install docker 10 | ubuntu1604.vm.provision :shell, inline: 'curl -fsSL https://get.docker.com/ | sh' 11 | # add vagrant user to docker group 12 | ubuntu1604.vm.provision :shell, inline: 'usermod -aG docker ubuntu' 13 | # reload and restart docker daemon 14 | ubuntu1604.vm.provision :shell, inline: 'systemctl daemon-reload' 15 | ubuntu1604.vm.provision :shell, inline: 'systemctl restart docker.service' 16 | # start one docker container 17 | ubuntu1604.vm.provision :shell, inline: 'docker run -d ubuntu /bin/bash -c "while true; do echo hello world; sleep 1; done"' 18 | ubuntu1604.vm.network 'private_network', ip: '192.168.34.101' 19 | end 20 | 21 | config.vm.define :centos7 do |centos7| 22 | centos7.vm.box = 'centos/7' # https://atlas.hashicorp.com/centos/boxes/7 23 | # install docker 24 | centos7.vm.provision :shell, inline: 'curl -fsSL https://get.docker.com/ | sh' 25 | # add vagrant user to docker group 26 | centos7.vm.provision :shell, inline: 'usermod -aG docker vagrant' 27 | # reload and restart docker daemon 28 | centos7.vm.provision :shell, inline: 'systemctl daemon-reload' 29 | centos7.vm.provision :shell, inline: 'systemctl restart docker.service' 30 | # start one docker container 31 | centos7.vm.provision :shell, inline: 'docker run -d ubuntu /bin/bash -c "while true; do echo hello world; sleep 1; done"' 32 | centos7.vm.network 'private_network', ip: '192.168.34.102' 33 | end 34 | 35 | config.vm.define :debian8 do |debian8| 36 | debian8.vm.box = 'debian/jessie64' # https://atlas.hashicorp.com/debian/boxes/jessie64/ 37 | # install curl 38 | debian8.vm.provision :shell, inline: 'apt-get update' 39 | debian8.vm.provision :shell, inline: 'apt-get install -y curl' 40 | # install docker 41 | debian8.vm.provision :shell, inline: 'curl -fsSL https://get.docker.com/ | sh' 42 | # add vagrant user to docker group 43 | debian8.vm.provision :shell, inline: 'usermod -aG docker vagrant' 44 | # reload and restart docker daemon 45 | debian8.vm.provision :shell, inline: 'systemctl daemon-reload' 46 | debian8.vm.provision :shell, inline: 'systemctl restart docker.service' 47 | # start one docker container 48 | debian8.vm.provision :shell, inline: 'docker run -d ubuntu /bin/bash -c "while true; do echo hello world; sleep 1; done"' 49 | debian8.vm.network 'private_network', ip: '192.168.34.103' 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /inspec.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: cis-docker-benchmark 3 | title: CIS Docker Benchmark Profile 4 | maintainer: DevSec Hardening Framework Team 5 | copyright: DevSec Hardening Framework Team 6 | copyright_email: hello@dev-sec.io 7 | license: Apache-2.0 8 | summary: An InSpec Compliance Profile for the CIS Docker Benchmark 9 | version: 2.1.4 10 | inspec_version: '>= 4.6.3' 11 | attributes: 12 | - name: container_user 13 | required: false 14 | description: 'define user within containers.' 15 | value: 'ubuntu' 16 | type: string 17 | - name: container_capadd 18 | required: true 19 | description: 'define needed capabilities for containers.' 20 | type: string 21 | value: NET_ADMIN,SYS_ADMIN 22 | - name: app_armor_profile 23 | required: false 24 | description: 'define apparmor profile for Docker containers.' 25 | value: 'docker-default' 26 | type: string 27 | - name: selinux_profile 28 | required: false 29 | description: 'define SELinux profile for Docker containers.' 30 | value: label:level:s0-s0:c1023 31 | type: string 32 | - name: trusted_user 33 | required: false 34 | description: 'define trusted user to control Docker daemon.' 35 | value: vagrant 36 | type: string 37 | - name: managable_container_number 38 | required: true 39 | description: 'keep number of containers on a host to a manageable total.' 40 | value: 25 41 | type: numeric 42 | - name: benchmark_version 43 | required: true 44 | description: 'to execute also the old controls from previous benchmarks. to execute the controls, define the value as 1.12.0' 45 | type: string 46 | value: 1.12.0 47 | - name: registry_cert_path 48 | required: true 49 | description: 'directory contains various Docker registry directories.' 50 | value: '/etc/docker/certs.d' 51 | type: string 52 | - name: registry_name 53 | required: true 54 | description: 'directory contain certificate certain Docker registry.' 55 | value: '/etc/docker/certs.d/registry_hostname:port' 56 | type: string 57 | - name: registry_ca_file 58 | required: false 59 | description: 'directory contain certificate certain Docker registry.' 60 | value: '/etc/docker/certs.d/registry_hostname:port/ca.crt' 61 | type: string 62 | - name: daemon_tlscacert 63 | required: false 64 | description: 'Trust certs signed only by this CA' 65 | value: '/etc/docker/ssl/ca.pem' 66 | type: string 67 | - name: daemon_tlscert 68 | required: false 69 | description: 'Path to TLS certificate file' 70 | value: '/etc/docker/ssl/server_cert.pem' 71 | type: string 72 | - name: daemon_tlskey 73 | required: false 74 | description: 'Path to TLS key file' 75 | value: '/etc/docker/ssl/server_key.pem' 76 | type: string 77 | - name: authorization_plugin 78 | required: false 79 | description: 'define authorization plugin to manage access to Docker daemon.' 80 | value: 'authz-broker' 81 | type: string 82 | - name: log_driver 83 | required: false 84 | description: 'define preferable way to store logs.' 85 | value: 'syslog' 86 | type: string 87 | - name: log_opts 88 | required: false 89 | description: 'define Docker daemon log-opts.' 90 | value: syslog-address 91 | type: string 92 | - name: swarm_mode 93 | required: false 94 | description: 'define the swarm mode, `active` or `inactive`' 95 | value: inactive 96 | type: string 97 | - name: swarm_max_manager_nodes 98 | required: false 99 | description: 'number of manager nodes in a swarm' 100 | value: 3 101 | type: numeric 102 | - name: swarm_port 103 | required: false 104 | description: 'port of the swarm node' 105 | value: 2377 106 | type: numeric 107 | - name: seccomp_default_profile 108 | required: false 109 | description: 'define the default seccomp profile' 110 | value: 'default' 111 | type: string 112 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CIS Docker Benchmark - InSpec Profile 2 | 3 | [![Build Status](http://img.shields.io/travis/dev-sec/cis-docker-benchmark.svg)][1] 4 | [![Supermarket](https://img.shields.io/badge/InSpec%20Profile-CIS%20Docker%20Benchmark-brightgreen.svg)](https://supermarket.chef.io/tools/cis-docker-benchmark) 5 | [![Gitter Chat](https://badges.gitter.im/Join%20Chat.svg)][2] 6 | 7 | ## Description 8 | 9 | This [InSpec](https://github.com/chef/inspec) compliance profile implement the [CIS Docker 1.13.0 Benchmark](https://downloads.cisecurity.org/) in an automated way to provide security best-practice tests around Docker daemon and containers in a production environment. 10 | 11 | InSpec is an open-source run-time framework and rule language used to specify compliance, security, and policy requirements for testing any node in your infrastructure. 12 | 13 | ## Requirements 14 | 15 | * at least [InSpec](http://inspec.io/) version 2.3.23 16 | * Docker 1.13+ 17 | 18 | ### Platform 19 | 20 | * Debian 8 21 | * Ubuntu 16.04 22 | * CentOS 7 23 | 24 | ## Attributes 25 | 26 | We use a yml attribute file to steer the configuration, the following options are available: 27 | 28 | * `trusted_user: vagrant` 29 | define trusted user to control Docker daemon. 30 | * `authorization_plugin: authz-broker` 31 | define authorization plugin to manage access to Docker daemon. 32 | * `log_driver: syslog` 33 | define preferable way to store logs. 34 | * `log_opts: /syslog-address/` 35 | define Docker daemon log-opts. 36 | * `registry_cert_path: /etc/docker/certs.d` 37 | directory contains various Docker registry directories. 38 | * `registry_name: /etc/docker/certs.d/registry_hostname:port` 39 | directory contain certificate certain Docker registry. 40 | * `registry_ca_file: /etc/docker/certs.d/registry_hostname:port/ca.crt` 41 | certificate file for a certain Docker registry certificate files. 42 | * `container_user: vagrant` 43 | define user within containers. 44 | * `app_armor_profile: docker-default` 45 | define apparmor profile for Docker containers. 46 | * `selinux_profile: /label\:level\:s0-s0\:c1023/` 47 | define SELinux profile for Docker containers. 48 | * `container_capadd: null` 49 | define needed capabilities for containers. example: `container_capadd: NET_ADMIN,SYS_ADMIN` 50 | * `managable_container_number: 25` 51 | keep number of containers on a host to a manageable total. 52 | * `daemon_tlscacert : /etc/docker/ssl/ca.pem` 53 | configure the certificate authority. 54 | * `daemon_tlscert: /etc/docker/ssl/server_cert.pem` 55 | configure the server certificate. 56 | * `daemon_tlskey: /etc/docker/ssl/server_key.pem` 57 | configure the server key. 58 | * `swarm_mode: inactive` 59 | configure the swarm mode. 60 | * `swarm_max_manager_nodes: 3` 61 | configure the maximum number of swarm leaders. 62 | * `swarm_port: 2377` 63 | configure the swarm port. 64 | * `benchmark_version` 65 | to execute also the old controls from previous benchmarks, e.g. set it to 1.12.0 to execute also the tests from cis-benchmark-1.12.0 (which is the default). 66 | 67 | These settings can be overridden using an attributes file (e.g. --attrs ). See [sample_attributes.yml](sample_attributes.yml) as an example. 68 | 69 | ## Usage 70 | 71 | InSpec makes it easy to run your tests wherever you need. More options listed here: [InSpec cli](http://inspec.io/docs/reference/cli/) 72 | 73 | ```sh 74 | # run profile locally 75 | $ git clone https://github.com/dev-sec/cis-docker-benchmark 76 | $ inspec exec cis-docker-benchmark 77 | 78 | # run profile locally and directly from Github 79 | $ inspec exec https://github.com/dev-sec/cis-docker-benchmark 80 | 81 | # run profile on remote host via SSH 82 | inspec exec cis-docker-benchmark -t ssh://user@hostname -i /path/to/key 83 | 84 | # run profile on remote host via SSH with sudo 85 | inspec exec cis-docker-benchmark -t ssh://user@hostname -i /path/to/key --sudo 86 | 87 | # run profile on remote host via SSH with sudo and define attribute value 88 | inspec exec cis-docker-benchmark --attrs sample_attributes.yml 89 | 90 | # run profile direct from inspec supermarket 91 | inspec supermarket exec dev-sec/cis-docker-benchmark -t ssh://user@hostname --key-files private_key --sudo 92 | ``` 93 | 94 | ### Run individual controls 95 | 96 | In order to verify individual controls, just provide the control ids to InSpec: 97 | 98 | ```sh 99 | inspec exec cis-docker-benchmark --controls 'cis-docker-benchmark-1.4 cis-docker-benchmark-1.5' 100 | ``` 101 | 102 | ## Contributors + Kudos 103 | 104 | * Patrick Muench [atomic111](https://github.com/atomic111) 105 | * Dominik Richter [arlimus](https://github.com/arlimus) 106 | * Christoph Hartmann [chris-rock](https://github.com/chris-rock) 107 | 108 | ## License and Author 109 | 110 | * Author:: Patrick Muench 111 | * Author:: Christoph Hartmann 112 | 113 | Licensed under the Apache License, Version 2.0 (the "License"); 114 | you may not use this file except in compliance with the License. 115 | You may obtain a copy of the License at 116 | 117 | 118 | 119 | Unless required by applicable law or agreed to in writing, software 120 | distributed under the License is distributed on an "AS IS" BASIS, 121 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 122 | See the License for the specific language governing permissions and 123 | limitations under the License. 124 | 125 | [1]: http://travis-ci.org/dev-sec/cis-docker-benchmark 126 | [2]: https://gitter.im/dev-sec/general 127 | [3]: https://downloads.cisecurity.org/ 128 | -------------------------------------------------------------------------------- /controls/docker_security_operations.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2016, Patrick Muench 4 | # Copyright:: 2017, Christoph Hartmann 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # author: Christoph Hartmann 19 | # author: Dominik Richter 20 | # author: Patrick Muench 21 | 22 | title 'Docker Security Operations' 23 | 24 | # check if docker exists 25 | only_if('docker not found') do 26 | command('docker').exist? 27 | end 28 | 29 | control 'docker-6.1' do 30 | impact 1.0 31 | title 'Perform regular security audits of your host system and containers' 32 | desc 'Perform regular security audits of your host system and containers to identify any mis-configurations or vulnerabilities that could expose your system to compromise. 33 | 34 | Rationale: Performing regular and dedicated security audits of your host systems and containers could provide deep security insights that you might not know in your daily course of business. The identified security weaknesses should be then mitigated and this overall improves security posture of your environment.' 35 | 36 | tag 'docker' 37 | tag 'cis-docker-1.12.0': '6.1' 38 | tag 'cis-docker-1.13.0': '6.1' 39 | tag 'level:1' 40 | ref 'IT security auditing: Best practices for conducting audits', url: 'http://searchsecurity.techtarget.com/IT-security-auditing-Best-practices-for-conducting-audits' 41 | 42 | describe 'docker-test' do 43 | skip 'Perform regular security audits of your host system and containers' 44 | end 45 | end 46 | 47 | control 'docker-6.2' do 48 | impact 1.0 49 | title 'Monitor Docker containers usage, performance and metering' 50 | desc 'Containers might run services that are critical for your business. Monitoring their usage, performance and metering would be of paramount importance. 51 | 52 | Rationale: Tracking container usage, performance and having some sort of metering around them would be important as you embrace the containers to run critical services for your business. This would give you 53 | 54 | Capacity Management and Optimization 55 | Performance Management 56 | Comprehensive Visibility 57 | 58 | Such a deep visibility of container performance would help you ensure high availability of containers and minimum downtime.' 59 | 60 | tag 'docker' 61 | tag 'cis-docker-1.12.0': '6.2' 62 | tag 'cis-docker-1.13.0': '6.2' 63 | tag 'level:1' 64 | ref 'Runtime metrics', url: 'https://docs.docker.com/engine/admin/runmetrics/' 65 | ref 'cAdvisor (Container Advisor)', url: 'https://github.com/google/cadvisor' 66 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/' 67 | 68 | describe 'docker-test' do 69 | skip 'Monitor Docker containers usage, performance and metering' 70 | end 71 | end 72 | 73 | control 'docker-6.3' do 74 | impact 1.0 75 | title 'Backup container data' 76 | desc 'Take regular backups of your container data volumes. 77 | 78 | Rationale: Containers might run services that are critical for your business. Taking regular data backups would ensure that if there is ever any loss of data you would still have your data in backup. The loss of data could be devastating for your business.' 79 | 80 | tag 'docker' 81 | tag 'cis-docker-1.12.0': '6.3' 82 | tag 'cis-docker-1.13.0': '6.3' 83 | tag 'level:1' 84 | ref 'Backups and disaster recovery', url: 'https://docs.docker.com/datacenter/ucp/2.2/guides/admin/backups-and-disaster-recovery/' 85 | ref 'How can I backup a Docker-container with its data-volumes?', url: 'https://stackoverflow.com/questions/26331651/how-can-i-backup-a-docker-container-with-its-data-volumes' 86 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/' 87 | 88 | describe 'docker-test' do 89 | skip 'Backup container data' 90 | end 91 | end 92 | 93 | control 'host-6.4' do 94 | impact 1.0 95 | title 'Avoid image sprawl' 96 | desc 'Do not keep a large number of container images on the same host. Use only tagged images as appropriate. 97 | 98 | Rationale: Tagged images are useful to fall back from "latest" to a specific version of an image in production. Images with unused or old tags may contain vulnerabilities that might be exploited, if instantiated. Additionally, if you fail to remove unused images from the system and there are various such redundant and unused images, the host filesystem may become full and could lead to denial of service.' 99 | 100 | tag 'host' 101 | tag 'cis-docker-1.12.0': '6.4' 102 | tag 'cis-docker-1.13.0': '6.4' 103 | tag 'level:1' 104 | ref 'Clean up unused Docker Containers and Images', url: 'http://craiccomputing.blogspot.de/2014/09/clean-up-unused-docker-containers-and.html' 105 | ref 'Command to remove all unused images', url: 'https://forums.docker.com/t/command-to-remove-all-unused-images/20/8' 106 | ref 'docker rmi --unused', url: 'https://github.com/moby/moby/issues/9054' 107 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/' 108 | ref 'Add support for referring to images by digest', url: 'https://github.com/moby/moby/pull/11109' 109 | 110 | instantiated_images = command('docker ps -qa | xargs docker inspect -f \'{{.Image}}\'').stdout.split 111 | all_images = command('docker images -q --no-trunc').stdout.split 112 | diff = all_images - instantiated_images 113 | 114 | describe diff do 115 | it { should be_empty } 116 | end 117 | end 118 | 119 | control 'host-6.5' do 120 | impact 1.0 121 | title 'Avoid container sprawl' 122 | desc 'Do not keep a large number of containers on the same host. 123 | 124 | Rationale: The flexibility of containers makes it easy to run multiple instances of applications and indirectly leads to Docker images that exist at varying security patch levels. It also means that you are consuming host resources that otherwise could have been used for running \'useful\' containers. Having more than just the manageable number of containers on a particular host makes the situation vulnerable to mishandling, misconfiguration and fragmentation. Thus, avoid container sprawl and keep the number of containers on a host to a manageable total.' 125 | 126 | tag 'host' 127 | tag 'cis-docker-1.12.0': '6.5' 128 | tag 'cis-docker-1.13.0': '6.5' 129 | tag 'level:1' 130 | ref 'Security Risks and Benefits of Docker Application Containers', url: 'https://zeltser.com/security-risks-and-benefits-of-docker-application/' 131 | ref 'Docker networking: How Linux containers will change your network', url: 'http://searchsdn.techtarget.com/feature/Docker-networking-How-Linux-containers-will-change-your-network' 132 | 133 | total_on_host = command('docker info').stdout.split[1].to_i 134 | total_running = command('docker ps -q').stdout.split.length 135 | diff = total_on_host - total_running 136 | 137 | describe diff do 138 | it { should be <= MANAGEABLE_CONTAINER_NUMBER } 139 | end 140 | end 141 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [2.1.4](https://github.com/dev-sec/cis-docker-benchmark/tree/2.1.4) (2023-05-02) 4 | 5 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/2.1.3...2.1.4) 6 | 7 | **Implemented enhancements:** 8 | 9 | - use centralised issue templates and workflows [\#77](https://github.com/dev-sec/cis-docker-benchmark/pull/77) ([schurzi](https://github.com/schurzi)) 10 | 11 | **Fixed bugs:** 12 | 13 | - Wrong placement of flag in command [\#72](https://github.com/dev-sec/cis-docker-benchmark/issues/72) 14 | - Wrong placement of flag in command [\#73](https://github.com/dev-sec/cis-docker-benchmark/pull/73) ([nnickie23](https://github.com/nnickie23)) 15 | 16 | **Merged pull requests:** 17 | 18 | - add spellchecking with codespell [\#79](https://github.com/dev-sec/cis-docker-benchmark/pull/79) ([schurzi](https://github.com/schurzi)) 19 | - Configure Renovate [\#78](https://github.com/dev-sec/cis-docker-benchmark/pull/78) ([renovate[bot]](https://github.com/apps/renovate)) 20 | - Change linting to Cookstyle [\#75](https://github.com/dev-sec/cis-docker-benchmark/pull/75) ([schurzi](https://github.com/schurzi)) 21 | 22 | ## [2.1.3](https://github.com/dev-sec/cis-docker-benchmark/tree/2.1.3) (2022-01-12) 23 | 24 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/2.1.2...2.1.3) 25 | 26 | **Merged pull requests:** 27 | 28 | - use input instead of attribute [\#74](https://github.com/dev-sec/cis-docker-benchmark/pull/74) ([micheelengronne](https://github.com/micheelengronne)) 29 | - update release workflow to match other projects [\#71](https://github.com/dev-sec/cis-docker-benchmark/pull/71) ([schurzi](https://github.com/schurzi)) 30 | - Missing words "certificate authority." [\#70](https://github.com/dev-sec/cis-docker-benchmark/pull/70) ([adamoutler](https://github.com/adamoutler)) 31 | 32 | ## [2.1.2](https://github.com/dev-sec/cis-docker-benchmark/tree/2.1.2) (2020-06-18) 33 | 34 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/2.1.1...2.1.2) 35 | 36 | **Merged pull requests:** 37 | 38 | - version alignment [\#68](https://github.com/dev-sec/cis-docker-benchmark/pull/68) ([micheelengronne](https://github.com/micheelengronne)) 39 | 40 | ## [2.1.1](https://github.com/dev-sec/cis-docker-benchmark/tree/2.1.1) (2020-06-18) 41 | 42 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/2.1.0...2.1.1) 43 | 44 | **Closed issues:** 45 | 46 | - Examples not working [\#58](https://github.com/dev-sec/cis-docker-benchmark/issues/58) 47 | - incompatible character encodings: UTF-8 and ASCII-8BIT [\#51](https://github.com/dev-sec/cis-docker-benchmark/issues/51) 48 | 49 | **Merged pull requests:** 50 | 51 | - github release action [\#67](https://github.com/dev-sec/cis-docker-benchmark/pull/67) ([micheelengronne](https://github.com/micheelengronne)) 52 | - Update Inspec.yml [\#66](https://github.com/dev-sec/cis-docker-benchmark/pull/66) ([MoisesTapia](https://github.com/MoisesTapia)) 53 | - Removed trailing slashes in 1.8, 1.9 [\#63](https://github.com/dev-sec/cis-docker-benchmark/pull/63) ([presidenten](https://github.com/presidenten)) 54 | - Remove .gitkeep file [\#62](https://github.com/dev-sec/cis-docker-benchmark/pull/62) ([james-stocks](https://github.com/james-stocks)) 55 | - Simple fix for \#58 [\#61](https://github.com/dev-sec/cis-docker-benchmark/pull/61) ([commjoen](https://github.com/commjoen)) 56 | - Update issue templates [\#57](https://github.com/dev-sec/cis-docker-benchmark/pull/57) ([rndmh3ro](https://github.com/rndmh3ro)) 57 | - unified attributes [\#56](https://github.com/dev-sec/cis-docker-benchmark/pull/56) ([chris-rock](https://github.com/chris-rock)) 58 | - Removed unneeded processing step [\#55](https://github.com/dev-sec/cis-docker-benchmark/pull/55) ([tstuber](https://github.com/tstuber)) 59 | 60 | ## [2.1.0](https://github.com/dev-sec/cis-docker-benchmark/tree/2.1.0) (2018-04-20) 61 | 62 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/2.0.0...2.1.0) 63 | 64 | **Closed issues:** 65 | 66 | - method\_missing: undefined local variable or method docker [\#50](https://github.com/dev-sec/cis-docker-benchmark/issues/50) 67 | - uploading cis docker profile to chef compliance [\#46](https://github.com/dev-sec/cis-docker-benchmark/issues/46) 68 | 69 | **Merged pull requests:** 70 | 71 | - 2.1.0 [\#54](https://github.com/dev-sec/cis-docker-benchmark/pull/54) ([chris-rock](https://github.com/chris-rock)) 72 | - Fix utf8 truncated output [\#53](https://github.com/dev-sec/cis-docker-benchmark/pull/53) ([aschmidt75](https://github.com/aschmidt75)) 73 | - update inspec version to 2.0 [\#52](https://github.com/dev-sec/cis-docker-benchmark/pull/52) ([atomic111](https://github.com/atomic111)) 74 | - Fixes \#37 prevent NoMethodError when no hosts available [\#49](https://github.com/dev-sec/cis-docker-benchmark/pull/49) ([Nowheresly](https://github.com/Nowheresly)) 75 | - name correct minimum inspec version [\#47](https://github.com/dev-sec/cis-docker-benchmark/pull/47) ([chris-rock](https://github.com/chris-rock)) 76 | - update changelog [\#45](https://github.com/dev-sec/cis-docker-benchmark/pull/45) ([chris-rock](https://github.com/chris-rock)) 77 | 78 | ## [2.0.0](https://github.com/dev-sec/cis-docker-benchmark/tree/2.0.0) (2017-11-24) 79 | 80 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/1.3.1...2.0.0) 81 | 82 | **Closed issues:** 83 | 84 | - Verify enable content trust per-shell or per-invocation check [\#44](https://github.com/dev-sec/cis-docker-benchmark/issues/44) 85 | - load_with_context': undefined method `each' for nil:NilClass \(NoMethodError\) exception in cis-docker-benchmark-master/controls/container\_runtime.rb:194 [\#37](https://github.com/dev-sec/cis-docker-benchmark/issues/37) 86 | - use own control number scheme [\#25](https://github.com/dev-sec/cis-docker-benchmark/issues/25) 87 | - Update to CIS 1.13 [\#24](https://github.com/dev-sec/cis-docker-benchmark/issues/24) 88 | 89 | **Merged pull requests:** 90 | 91 | - Update to CIS Docker Benchmark 1.13.0 [\#43](https://github.com/dev-sec/cis-docker-benchmark/pull/43) ([atomic111](https://github.com/atomic111)) 92 | - correct the maintainer and email in inspec.yml [\#42](https://github.com/dev-sec/cis-docker-benchmark/pull/42) ([atomic111](https://github.com/atomic111)) 93 | - update gemfile [\#41](https://github.com/dev-sec/cis-docker-benchmark/pull/41) ([atomic111](https://github.com/atomic111)) 94 | 95 | ## [1.3.1](https://github.com/dev-sec/cis-docker-benchmark/tree/1.3.1) (2017-11-18) 96 | 97 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/1.3.0...1.3.1) 98 | 99 | **Fixed bugs:** 100 | 101 | - undefined method `downcase' for nil:NilClass [\#32](https://github.com/dev-sec/cis-docker-benchmark/issues/32) 102 | 103 | **Closed issues:** 104 | 105 | - docker variable not defined [\#31](https://github.com/dev-sec/cis-docker-benchmark/issues/31) 106 | 107 | **Merged pull requests:** 108 | 109 | - 1.3.1 [\#40](https://github.com/dev-sec/cis-docker-benchmark/pull/40) ([chris-rock](https://github.com/chris-rock)) 110 | - updating check for container\_info networkings port [\#38](https://github.com/dev-sec/cis-docker-benchmark/pull/38) ([nandeshguru](https://github.com/nandeshguru)) 111 | - add required docker cli version [\#35](https://github.com/dev-sec/cis-docker-benchmark/pull/35) ([chris-rock](https://github.com/chris-rock)) 112 | - use recommended spdx license identifier [\#34](https://github.com/dev-sec/cis-docker-benchmark/pull/34) ([chris-rock](https://github.com/chris-rock)) 113 | - Due to inspec deprecation warnings [\#33](https://github.com/dev-sec/cis-docker-benchmark/pull/33) ([alexpop](https://github.com/alexpop)) 114 | 115 | ## [1.3.0](https://github.com/dev-sec/cis-docker-benchmark/tree/1.3.0) (2017-04-28) 116 | 117 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/1.2.0...1.3.0) 118 | 119 | **Closed issues:** 120 | 121 | - rename control titles [\#22](https://github.com/dev-sec/cis-docker-benchmark/issues/22) 122 | - splitt controls in components [\#21](https://github.com/dev-sec/cis-docker-benchmark/issues/21) 123 | - include the inspec docker resource [\#20](https://github.com/dev-sec/cis-docker-benchmark/issues/20) 124 | - Update to CIS Docker 1.12.0 Benchmark [\#11](https://github.com/dev-sec/cis-docker-benchmark/issues/11) 125 | - tag the tests which belongs to a host and to a container [\#8](https://github.com/dev-sec/cis-docker-benchmark/issues/8) 126 | 127 | **Merged pull requests:** 128 | 129 | - fix \#11 implement missing 1.12 controls [\#30](https://github.com/dev-sec/cis-docker-benchmark/pull/30) ([chris-rock](https://github.com/chris-rock)) 130 | - use new inspec docker resource [\#29](https://github.com/dev-sec/cis-docker-benchmark/pull/29) ([chris-rock](https://github.com/chris-rock)) 131 | - split up control files into components [\#26](https://github.com/dev-sec/cis-docker-benchmark/pull/26) ([chris-rock](https://github.com/chris-rock)) 132 | - update tags and refs [\#23](https://github.com/dev-sec/cis-docker-benchmark/pull/23) ([chris-rock](https://github.com/chris-rock)) 133 | 134 | ## [1.2.0](https://github.com/dev-sec/cis-docker-benchmark/tree/1.2.0) (2017-04-18) 135 | 136 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/1.1.1...1.2.0) 137 | 138 | **Merged pull requests:** 139 | 140 | - update to CIS Benchmark 1.12, controls 1.1 to 2.16 [\#19](https://github.com/dev-sec/cis-docker-benchmark/pull/19) ([atomic111](https://github.com/atomic111)) 141 | 142 | ## [1.1.1](https://github.com/dev-sec/cis-docker-benchmark/tree/1.1.1) (2017-03-01) 143 | 144 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/1.1.0...1.1.1) 145 | 146 | **Merged pull requests:** 147 | 148 | - Fix 'or' in controls 5.1 and 5.2 [\#18](https://github.com/dev-sec/cis-docker-benchmark/pull/18) ([emilyay](https://github.com/emilyay)) 149 | - add changelog [\#16](https://github.com/dev-sec/cis-docker-benchmark/pull/16) ([chris-rock](https://github.com/chris-rock)) 150 | 151 | ## [1.1.0](https://github.com/dev-sec/cis-docker-benchmark/tree/1.1.0) (2016-12-13) 152 | 153 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/1.0.0...1.1.0) 154 | 155 | **Merged pull requests:** 156 | 157 | - update Gemfile and fix rubocop issues [\#15](https://github.com/dev-sec/cis-docker-benchmark/pull/15) ([atomic111](https://github.com/atomic111)) 158 | - Edit control "cis-docker-benchmark-3.4" [\#14](https://github.com/dev-sec/cis-docker-benchmark/pull/14) ([emilyay](https://github.com/emilyay)) 159 | - Edit control "cis-docker-benchmark-1.11" [\#13](https://github.com/dev-sec/cis-docker-benchmark/pull/13) ([emilyay](https://github.com/emilyay)) 160 | - Fix README.md [\#12](https://github.com/dev-sec/cis-docker-benchmark/pull/12) ([netflash](https://github.com/netflash)) 161 | 162 | ## [1.0.0](https://github.com/dev-sec/cis-docker-benchmark/tree/1.0.0) (2016-07-05) 163 | 164 | [Full Changelog](https://github.com/dev-sec/cis-docker-benchmark/compare/b7947d9bfea0a7fb961874f94a7fa0375bef31ba...1.0.0) 165 | 166 | **Implemented enhancements:** 167 | 168 | - use new InSpec attributes [\#10](https://github.com/dev-sec/cis-docker-benchmark/pull/10) ([chris-rock](https://github.com/chris-rock)) 169 | - handle nil results for docker.path [\#6](https://github.com/dev-sec/cis-docker-benchmark/pull/6) ([chris-rock](https://github.com/chris-rock)) 170 | - externalize reoccurring calls to docker resource [\#4](https://github.com/dev-sec/cis-docker-benchmark/pull/4) ([chris-rock](https://github.com/chris-rock)) 171 | - determine attribute values at the beginning [\#1](https://github.com/dev-sec/cis-docker-benchmark/pull/1) ([chris-rock](https://github.com/chris-rock)) 172 | 173 | **Merged pull requests:** 174 | 175 | - fix ips for vagrant machines [\#9](https://github.com/dev-sec/cis-docker-benchmark/pull/9) ([chris-rock](https://github.com/chris-rock)) 176 | - change order of InSpec img shield in README.md [\#7](https://github.com/dev-sec/cis-docker-benchmark/pull/7) ([atomic111](https://github.com/atomic111)) 177 | - add ruby 2.3.1 to travis.yml [\#5](https://github.com/dev-sec/cis-docker-benchmark/pull/5) ([atomic111](https://github.com/atomic111)) 178 | - changed link to CIS Docker Benchmark document [\#3](https://github.com/dev-sec/cis-docker-benchmark/pull/3) ([atomic111](https://github.com/atomic111)) 179 | - add Vagrantfile to repo [\#2](https://github.com/dev-sec/cis-docker-benchmark/pull/2) ([atomic111](https://github.com/atomic111)) 180 | 181 | 182 | 183 | \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* 184 | -------------------------------------------------------------------------------- /controls/container_images.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2016, Patrick Muench 4 | # Copyright:: 2017, Christoph Hartmann 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # author: Christoph Hartmann 19 | # author: Dominik Richter 20 | # author: Patrick Muench 21 | 22 | title 'Container Images and Build File' 23 | 24 | # attributes 25 | CONTAINER_USER = input('container_user') 26 | 27 | # check if docker exists 28 | only_if('docker not found') do 29 | command('docker').exist? 30 | end 31 | 32 | control 'docker-4.1' do 33 | impact 1.0 34 | title 'Create a user for the container' 35 | desc 'Create a non-root user for the container in the Dockerfile for the container image. 36 | 37 | Rationale: It is a good practice to run the container as a non-root user, if possible. Though user namespace mapping is now available, if a user is already defined in the container image, the container is run as that user by default and specific user namespace remapping is not required.' 38 | 39 | tag 'docker' 40 | tag 'cis-docker-1.12.0': '4.1' 41 | tag 'cis-docker-1.13.0': '4.1' 42 | tag 'level:1' 43 | ref 'Having non-root privileges on the host and root inside the container', url: 'https://github.com/docker/docker/issues/2918' 44 | ref 'Support for user namespaces', url: 'https://github.com/docker/docker/pull/4572' 45 | ref 'Proposal: Support for user namespaces', url: 'https://github.com/docker/docker/issues/7906' 46 | ref 'Secure Engine', url: 'https://docs.docker.com/engine/security/' 47 | 48 | docker.containers.running?.ids.each do |id| 49 | describe docker.object(id) do 50 | its(%w(Config User)) { should_not eq nil } 51 | its(%w(Config User)) { should eq CONTAINER_USER } 52 | end 53 | end 54 | end 55 | 56 | control 'docker-4.2' do 57 | impact 1.0 58 | title 'Use trusted base images for containers' 59 | desc 'Ensure that the container image is written either from scratch or is based on another established and trusted base image downloaded over a secure channel. 60 | 61 | Rationale: Official repositories are Docker images curated and optimized by the Docker community or the vendor. There could be other potentially unsafe public repositories. You should thus exercise a lot of caution when obtaining container images.' 62 | 63 | tag 'docker' 64 | tag 'cis-docker-1.12.0': '4.2' 65 | tag 'cis-docker-1.13.0': '4.2' 66 | tag 'level:1' 67 | ref 'Docker Image Insecurity', url: 'https://titanous.com/posts/docker-insecurity' 68 | ref 'Docker Hub', url: 'https://hub.docker.com/' 69 | ref 'Docker 1.3: signed images, process injection, security options, Mac shared directories', url: 'https://blog.docker.com/2014/10/docker-1-3-signed-images-process-injection-security-options-mac-shared-directories/' 70 | ref 'Proposal: Provenance step 1 - Transform images for validation and verification', url: 'https://github.com/docker/docker/issues/8093' 71 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/' 72 | ref 'Add support for referring to images by digest', url: 'https://github.com/docker/docker/pull/11109' 73 | ref 'Announcing Docker Trusted Registry 1.4 – New User Interface, Integrated Content Trust and Support for Docker Engine 1.9', url: 'https://blog.docker.com/2015/11/docker-trusted-registry-1-4/' 74 | 75 | describe os_env('DOCKER_CONTENT_TRUST') do 76 | its('content') { should eq '1' } 77 | end 78 | end 79 | 80 | control 'docker-4.3' do 81 | impact 1.0 82 | title 'Do not install unnecessary packages in the container' 83 | desc 'Containers tend to be minimal and slim down versions of the Operating System. Do not install anything that does not justify the purpose of container. 84 | 85 | Rationale: Bloating containers with unnecessary software could possibly increase the attack surface of the container. This also voids the concept of minimal and slim down versions of container images. Hence, do not install anything else apart from what is truly needed for the purpose of the container.' 86 | 87 | tag 'docker' 88 | tag 'cis-docker-1.12.0': '4.3' 89 | tag 'cis-docker-1.13.0': '4.3' 90 | tag 'level:1' 91 | ref 'Get Started, Part 1: Orientation and setup', url: 'https://docs.docker.com/get-started/' 92 | ref 'Slimming down your Docker containers with Alpine Linux', url: 'http://www.livewyer.com/blog/2015/02/24/slimming-down-your-docker-containers-alpine-linux' 93 | ref 'busybox', url: 'https://github.com/progrium/busybox' 94 | 95 | describe 'docker-test' do 96 | skip 'Do not install unnecessary packages in the container' 97 | end 98 | end 99 | 100 | control 'docker-4.4' do 101 | impact 1.0 102 | title 'Rebuild the images to include security patches' 103 | desc 'Instead of patching your containers and images, rebuild the images from scratch and instantiate new containers from it. 104 | 105 | Rationale: Vulnerabilities are loopholes/bugs that can be exploited and security patches are updates to resolve these vulnerabilities. We can use image vulnerability scanning tools to find any kind of vulnerabilities within the images and then check for available patches to mitigate these vulnerabilities. Patches update the system to the most recent code base. Being on the current code base is important because that\'s where vendors focus on fixing problems. Evaluate the security patches before applying and follow the patching best practices. Also, it would be better if, image vulnerability scanning tools could perform binary level analysis or hash based verification instead of just version string matching.' 106 | 107 | tag 'docker' 108 | tag 'cis-docker-1.12.0': '4.4' 109 | tag 'cis-docker-1.13.0': '4.4' 110 | tag 'level:1' 111 | ref 'Get Started, Part 1: Orientation and setup', url: 'https://docs.docker.com/get-started/' 112 | ref 'Docker Security Scan', url: ' https://docs.docker.com/docker-cloud/builds/image-scan/' 113 | ref 'Docker Security Scanning safeguards the container content lifecycle', url: 'https://blog.docker.com/2016/05/docker-security-scanning/' 114 | ref 'Dockerfile reference', url: 'https://docs.docker.com/engine/reference/builder/' 115 | 116 | describe 'docker-test' do 117 | skip 'Rebuild the images to include security patches' 118 | end 119 | end 120 | 121 | control 'docker-4.5' do 122 | impact 1.0 123 | title 'Enable Content trust for Docker' 124 | desc 'Content trust is disabled by default. You should enable it. 125 | 126 | Rationale: Content trust provides the ability to use digital signatures for data sent to and received from remote Docker registries. These signatures allow client-side verification of the integrity and publisher of specific image tags. This ensures provenance of container images.' 127 | 128 | tag 'docker' 129 | tag 'cis-docker-1.12.0': '4.5' 130 | tag 'cis-docker-1.13.0': '4.5' 131 | tag 'level:2' 132 | ref 'Content trust in Docker', url: 'https://docs.docker.com/engine/security/trust/content_trust/' 133 | ref 'Notary', url: 'https://docs.docker.com/engine/reference/commandline/cli/#notary' 134 | ref 'Environment variables', url: 'https://docs.docker.com/engine/reference/commandline/cli/#environment-variables' 135 | 136 | describe os_env('DOCKER_CONTENT_TRUST') do 137 | its('content') { should eq '1' } 138 | end 139 | end 140 | 141 | control 'docker-4.6' do 142 | impact 0.0 143 | title 'Add HEALTHCHECK instruction to the container image' 144 | desc 'Add HEALTHCHECK instruction in your docker container images to perform the health check on running containers. 145 | 146 | Rationale: One of the important security triads is availability. Adding HEALTHCHECK instruction to your container image ensures that the docker engine periodically checks the running container instances against that instruction to ensure that the instances are still working. Based on the reported health status, the docker engine could then exit non-working containers and instantiate new ones.' 147 | 148 | tag 'docker' 149 | tag 'cis-docker-1.12.0': '4.6' 150 | tag 'cis-docker-1.13.0': '4.6' 151 | tag 'level:1' 152 | ref 'Add support for user-defined healthchecks', url: 'https://github.com/moby/moby/pull/22719' 153 | 154 | docker.containers.running?.ids.each do |id| 155 | describe docker.object(id) do 156 | its(%w(Config Healthcheck)) { should_not eq nil } 157 | end 158 | end 159 | end 160 | 161 | control 'docker-4.7' do 162 | impact 1.0 163 | title 'Do not use update instructions alone in the Dockerfile' 164 | desc 'Do not use update instructions such as apt-get update alone or in a single line in the Dockerfile. 165 | 166 | Rationale: Adding the update instructions in a single line on the Dockerfile will cache the update layer. Thus, when you build any image later using the same instruction, previously cached update layer will be used. This could potentially deny any fresh updates to go in the later builds.' 167 | 168 | tag 'docker' 169 | tag 'cis-docker-1.12.0': '4.7' 170 | tag 'cis-docker-1.13.0': '4.7' 171 | tag 'level:1' 172 | ref 'Best practices for writing Dockerfiles', url: 'https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/' 173 | ref 'caching and apt-get update', url: 'https://github.com/moby/moby/issues/3313' 174 | 175 | docker.images.ids.each do |id| 176 | describe command("docker history --no-trunc #{id}| grep -e 'update'") do 177 | its('stdout') { should eq '' } 178 | end 179 | end 180 | end 181 | 182 | control 'docker-4.8' do 183 | impact 1.0 184 | title 'Remove setuid and setgid permissions in the images' 185 | desc 'Removing setuid and setgid permissions in the images would prevent privilege escalation attacks in the containers. 186 | 187 | Rationale: setuid and setgid permissions could be used for elevating privileges. While these permissions are at times legitimately needed, these could potentially be used in privilege escalation attacks. Thus, you should consider dropping these permissions for the packages which do not need them within the images.' 188 | 189 | tag 'docker' 190 | tag 'cis-docker-1.12.0': '4.8' 191 | tag 'cis-docker-1.13.0': '4.8' 192 | tag 'level:2' 193 | ref 'DevSec Linux Baseline', url: 'https://github.com/dev-sec/linux-baseline' 194 | ref 'Docker Security', url: 'http://www.oreilly.com/webops-perf/free/files/docker-security.pdf' 195 | ref 'Docker Security Cheat Sheet', url: 'http://container-solutions.com/content/uploads/2015/06/15.06.15_DockerCheatSheet_A2.pdf' 196 | ref 'setuid - set user identity', url: 'http://man7.org/linux/man-pages/man2/setuid.2.html' 197 | ref 'setgid - set group identity', url: 'http://man7.org/linux/man-pages/man2/setgid.2.html' 198 | 199 | describe 'docker-test' do 200 | skip 'Use DevSec Linux Baseline in Container' 201 | end 202 | end 203 | 204 | control 'docker-4.9' do 205 | impact 1.0 206 | title 'Use COPY instead of ADD in Dockerfile' 207 | desc 'Use COPY instruction instead of ADD instruction in the Dockerfile. 208 | 209 | Rationale: COPY instruction just copies the files from the local host machine to the container file system. ADD instruction potentially could retrieve files from remote URLs and perform operations such as unpacking. Thus, ADD instruction introduces risks such as adding malicious files from URLs without scanning and unpacking procedure vulnerabilities.' 210 | 211 | tag 'docker' 212 | tag 'cis-docker-1.12.0': '4.9' 213 | tag 'cis-docker-1.13.0': '4.9' 214 | tag 'level:1' 215 | ref 'Best practices for writing Dockerfiles', url: 'https://docs.docker.com/engine/userguide/eng-image/dockerfile_best-practices/' 216 | 217 | docker.images.ids.each do |id| 218 | describe command("docker history --no-trunc #{id}| grep 'ADD'") do 219 | its('stdout') { should eq '' } 220 | end 221 | end 222 | end 223 | 224 | control 'docker-4.10' do 225 | impact 1.0 226 | title 'Do not store secrets in Dockerfiles' 227 | desc 'Do not store any secrets in Dockerfiles. 228 | 229 | Rationale: Dockerfiles could be backtracked easily by using native Docker commands such as docker history and various tools and utilities. Also, as a general practice, image publishers provide Dockerfiles to build the credibility for their images. Hence, the secrets within these Dockerfiles could be easily exposed and potentially be exploited.' 230 | 231 | tag 'docker' 232 | tag 'cis-docker-1.12.0': '4.10' 233 | tag 'cis-docker-1.13.0': '4.10' 234 | tag 'level:1' 235 | ref 'Secrets: write-up best practices, do\'s and don\'ts, roadmap', url: 'https://github.com/moby/moby/issues/13490' 236 | ref 'The Twelve-Factor App', url: 'https://12factor.net/config' 237 | ref 'Twitter\'s Vine Source code dump', url: 'https://avicoder.me/2016/07/22/Twitter-Vine-Source-code-dump/' 238 | 239 | describe 'docker-test' do 240 | skip 'Manually verify that you have not used secrets in images' 241 | end 242 | end 243 | 244 | control 'docker-4.11' do 245 | impact 1.0 246 | title 'Install verified packages only' 247 | desc 'Verify authenticity of the packages before installing them in the image. 248 | 249 | Rationale: Verifying authenticity of the packages is essential for building a secure container image. Tampered packages could potentially be malicious or have some known vulnerabilities that could be exploited.' 250 | 251 | tag 'docker' 252 | tag 'cis-docker-1.13.0': '4.11' 253 | tag 'level:1' 254 | ref 'Docker Security', url: 'http://www.oreilly.com/webops-perf/free/files/docker-security.pdf' 255 | ref 'Dockerfile HTTPD', url: 'https://github.com/docker-library/httpd/blob/12bf8c8883340c98b3988a7bade8ef2d0d6dcf8a/2.4/Dockerfile' 256 | ref 'Dockerfile PHP Alpine', url: 'https://github.com/docker-library/php/blob/d8a4ccf4d620ec866d5b42335b699742df08c5f0/7.0/alpine/Dockerfile' 257 | ref 'Product Signing (GPG) Keys', url: 'https://access.redhat.com/security/team/key' 258 | 259 | describe 'docker-test' do 260 | skip 'Manually verify that you installed verified packages' 261 | end 262 | end 263 | -------------------------------------------------------------------------------- /controls/host_configuration.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2016, Patrick Muench 4 | # Copyright:: 2017, Christoph Hartmann 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # author: Christoph Hartmann 19 | # author: Dominik Richter 20 | # author: Patrick Muench 21 | 22 | title 'Host Configuration' 23 | 24 | TRUSTED_USER = input('trusted_user') 25 | MANAGEABLE_CONTAINER_NUMBER = input('managable_container_number') 26 | BENCHMARK_VERSION = input('benchmark_version') 27 | 28 | # check if docker exists 29 | only_if('docker not found') do 30 | command('docker').exist? 31 | end 32 | 33 | control 'host-1.1' do 34 | impact 1.0 35 | title 'Create a separate partition for containers' 36 | desc 'All Docker containers and their data and metadata is stored under /var/lib/docker directory. By default, /var/lib/docker would be mounted under / or /var partitions based on availability. 37 | 38 | Rationale: Docker depends on /var/lib/docker as the default directory where all Docker related files, including the images, are stored. This directory might fill up fast and soon Docker and the host could become unusable. So, it is advisable to create a separate partition (logical volume) for storing Docker files.' 39 | 40 | tag 'host' 41 | tag 'cis-docker-1.12.0': '1.1' 42 | tag 'cis-docker-1.13.0': '1.1' 43 | tag 'level:1' 44 | ref 'Docker storage recommendation', url: 'http://www.projectatomic.io/docs/docker-storage-recommendation/' 45 | 46 | describe mount('/var/lib/docker') do 47 | it { should be_mounted } 48 | end 49 | end 50 | 51 | control 'host-1.2' do 52 | impact 1.0 53 | title 'Use the updated Linux Kernel' 54 | desc 'Docker in daemon mode has specific kernel requirements. A 3.10 Linux kernel is the minimum requirement for Docker.' 55 | 56 | tag 'host' 57 | tag 'cis-docker-1.12.0': '1.2' 58 | tag 'level:1' 59 | ref 'Check kernel dependencies', url: 'https://docs.docker.com/engine/installation/binaries/#check-kernel-dependencies' 60 | ref 'Installation list', url: 'https://docs.docker.com/engine/installation/#installation-list' 61 | 62 | only_if { os.linux? } 63 | kernel_version = command('uname -r | grep -o \'^\w\.\w*\.\w*\'').stdout 64 | kernel_compare = Gem::Version.new('3.10') <= Gem::Version.new(kernel_version) 65 | describe kernel_compare do 66 | it { should eq true } 67 | end 68 | only_if { BENCHMARK_VERSION == '1.12.0' } 69 | end 70 | 71 | control 'host-1.3' do 72 | impact 1.0 73 | title 'Harden the container host' 74 | desc 'Containers run on a Linux host. A container host can run one or more containers. It is of utmost importance to harden the host to mitigate host security misconfiguration. 75 | 76 | Rationale: You should follow infrastructure security best practices and harden your host OS. Keeping the host system hardened would ensure that the host vulnerabilities are mitigated. Not hardening the host system could lead to security exposures and breaches. You can use the dev-sec.io Hardening Framework for this task 77 | 78 | By default, host has factory settings. It is not hardened.' 79 | 80 | tag 'host' 81 | tag 'cis-docker-1.12.0': '1.3' 82 | tag 'cis-docker-1.13.0': '1.2' 83 | tag 'level:1' 84 | ref 'Dev-Sec Hardening Framework', url: 'http://dev-sec.io/' 85 | ref 'Secure Engine', url: 'https://docs.docker.com/engine/security/' 86 | ref 'Center of Internet Security Benchmarks', url: 'https://learn.cisecurity.org/benchmarks' 87 | ref 'Grsecurity', url: 'https://grsecurity.net/' 88 | ref 'Grsecurity Wiki', url: 'https://en.wikibooks.org/wiki/Grsecurity' 89 | ref 'PAX Security', url: 'https://pax.grsecurity.net/' 90 | ref 'PAX Security Wiki', url: 'https://en.wikipedia.org/wiki/PaX' 91 | 92 | describe 'docker-test' do 93 | skip 'Harden the container host. Use the Dev-Sec Hardening Framework' 94 | end 95 | end 96 | 97 | control 'host-1.4' do 98 | impact 1.0 99 | title 'Remove all non-essential services from the host' 100 | desc 'Ensure that the host running the docker daemon is running only the essential services.' 101 | 102 | tag 'host' 103 | tag 'cis-docker-1.12.0': '1.4' 104 | tag 'level:1' 105 | ref 'Containers & Docker: How Secure Are They?', url: 'https://blog.docker.com/2013/08/containers-docker-how-secure-are-they/' 106 | ref 'Dev-Sec Hardening Framework', url: 'http://dev-sec.io/' 107 | 108 | describe 'docker-test' do 109 | skip 'Remove all non-essential services from the host. Use the Dev-Sec Hardening Framework' 110 | end 111 | end 112 | 113 | control 'host-1.5' do 114 | impact 1.0 115 | title 'Keep Docker up to date' 116 | desc 'There are frequent releases for Docker software that address security vulnerabilities,product bugs and bring in new functionality. Keep a tab on these product updates and upgrade as frequently as when new security vulnerabilities are fixed or deemed correct for your organization. 117 | 118 | Rationale: By staying up to date on Docker updates, vulnerabilities in the Docker software can be mitigated. An educated attacker may exploit known vulnerabilities when attempting to attain access or elevate privileges. Not installing regular Docker updates may leave you ith running vulnerable Docker software. It might lead to elevation privileges, unauthorized access or other security breaches. Keep a track of new releases and update as necessary.' 119 | 120 | tag 'host' 121 | tag 'cis-docker-1.12.0': '1.5' 122 | tag 'cis-docker-1.13.0': '1.3' 123 | tag 'level:1' 124 | ref 'Docker installation', url: 'https://docs.docker.com/engine/installation/' 125 | ref 'Docker releases', url: 'https://github.com/moby/moby/releases/tag/v17.03.2-ce' 126 | ref 'About Docker EE', url: 'https://docs.docker.com/enterprise/' 127 | 128 | describe docker do 129 | its('version.Client.Version') { should cmp >= '17.06' } 130 | its('version.Server.Version') { should cmp >= '17.06' } 131 | end 132 | end 133 | 134 | control 'host-1.6' do 135 | impact 1.0 136 | title 'Only allow trusted users to control Docker daemon' 137 | desc 'The Docker daemon currently requires \'root\' privileges. A user added to the \'docker\' group gives him full \'root\' access rights. 138 | 139 | Rationale: Docker allows you to share a directory between the Docker host and a guest container without limiting the access rights of the container. This means that you can start a container and map the / directory on your host to the container. The container will then be able to alter your host file system without any restrictions. In simple terms, it means that you can attain elevated privileges with just being a member of the \'docker\' group and then starting a container with mapped / directory on the host.' 140 | 141 | tag 'host' 142 | tag 'cis-docker-1.12.0': '1.6' 143 | tag 'cis-docker-1.13.0': '1.4' 144 | tag 'level:1' 145 | ref 'Docker Engine Security', url: 'https://docs.docker.com/engine/security/' 146 | ref 'On Docker security: \'docker\' group considered harmful', url: 'https://www.zopyx.com/andreas-jung/contents/on-docker-security-docker-group-considered-harmful' 147 | ref 'Why we don\'t let non-root users run Docker in CentOS, Fedora, or RHEL', url: 'http://www.projectatomic.io/blog/2015/08/why-we-dont-let-non-root-users-run-docker-in-centos-fedora-or-rhel/' 148 | 149 | describe group('docker') do 150 | it { should exist } 151 | end 152 | 153 | describe etc_group.where(group_name: 'docker') do 154 | its('users') { should include TRUSTED_USER } 155 | end 156 | end 157 | 158 | control 'host-1.7' do 159 | impact 1.0 160 | title 'Audit docker daemon' 161 | desc 'Audit all Docker daemon activities. 162 | 163 | Rationale: Apart from auditing your regular Linux file system and system calls, audit Docker daemon as well. Docker daemon runs with \'root\' privileges. It is thus necessary to audit its activities and usage.' 164 | 165 | tag 'host' 166 | tag 'cis-docker-1.12.0': '1.7' 167 | tag 'cis-docker-1.13.0': '1.5' 168 | tag 'level:1' 169 | ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' 170 | 171 | only_if { os.linux? } 172 | describe auditd do 173 | its(:lines) { should include('-w /usr/bin/docker -p rwxa -k docker') } 174 | end 175 | describe service('auditd') do 176 | it { should be_installed } 177 | it { should be_enabled } 178 | it { should be_running } 179 | end 180 | end 181 | 182 | control 'host-1.8' do 183 | impact 1.0 184 | title 'Audit Docker files and directories - /var/lib/docker' 185 | desc 'Audit /var/lib/docker. 186 | 187 | Rationale: Apart from auditing your regular Linux file system and system calls, audit all Docker related files and directories. Docker daemon runs with \'root\' privileges. Its behavior depends on some key files and directories. /var/lib/docker is one such directory. It holds all the information about containers. It must be audited.' 188 | 189 | tag 'host' 190 | tag 'cis-docker-1.12.0': '1.8' 191 | tag 'cis-docker-1.13.0': '1.6' 192 | tag 'level:1' 193 | ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' 194 | 195 | only_if { os.linux? } 196 | describe auditd do 197 | its(:lines) { should include('-w /var/lib/docker -p rwxa -k docker') } 198 | end 199 | end 200 | 201 | control 'host-1.9' do 202 | impact 1.0 203 | title 'Audit Docker files and directories - /etc/docker' 204 | desc 'Audit /etc/docker. 205 | 206 | Rationale: Apart from auditing your regular Linux file system and system calls, audit all Docker related files and directories. Docker daemon runs with \'root\' privileges. Its behavior depends on some key files and directories. /etc/docker is one such directory. It holds various certificates and keys used for TLS communication between Docker daemon and Docker client. It must be audited.' 207 | 208 | tag 'host' 209 | tag 'cis-docker-1.12.0': '1.9' 210 | tag 'cis-docker-1.13.0': '1.7' 211 | tag 'level:1' 212 | ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' 213 | 214 | only_if { os.linux? } 215 | describe auditd do 216 | its(:lines) { should include('-w /etc/docker -p rwxa -k docker') } 217 | end 218 | end 219 | 220 | control 'host-1.10' do 221 | impact 1.0 222 | title 'Audit Docker files and directories - docker.service' 223 | desc 'Audit docker.service, if applicable. 224 | 225 | Rationale: Apart from auditing your regular Linux file system and system calls, audit all Docker related files and directories. Docker daemon runs with \'root\' privileges. Its behavior depends on some key files and directories. docker.service is one such file. The docker.service file might be present if the daemon parameters have been changed by an administrator. It holds various parameters for Docker daemon. It must be audited, if applicable.' 226 | 227 | tag 'host' 228 | tag 'cis-docker-1.12.0': '1.10' 229 | tag 'cis-docker-1.13.0': '1.8' 230 | tag 'level:1' 231 | ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' 232 | 233 | only_if { os.linux? } 234 | if docker_helper.path 235 | rule = "-w #{docker_helper.path} -p rwxa -k docker" 236 | describe auditd do 237 | its(:lines) { should include(rule) } 238 | end 239 | else 240 | describe 'audit docker service' do 241 | skip 'Cannot determine docker path' 242 | end 243 | end 244 | end 245 | 246 | control 'host-1.11' do 247 | impact 1.0 248 | title 'Audit Docker files and directories - docker.socket' 249 | desc 'Audit docker.socket, if applicable. 250 | 251 | Rationale: Apart from auditing your regular Linux file system and system calls, audit all Docker related files and directories. Docker daemon runs with \'root\' privileges. Its behavior depends on some key files and directories. docker.socket is one such file. It holds various parameters for Docker daemon socket. It must be audited, if applicable.' 252 | 253 | tag 'host' 254 | tag 'cis-docker-1.12.0': '1.11' 255 | tag 'cis-docker-1.13.0': '1.9' 256 | tag 'level:1' 257 | ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' 258 | 259 | only_if { os.linux? } 260 | if docker_helper.socket 261 | rule = "-w #{docker_helper.socket} -p rwxa -k docker" 262 | describe auditd do 263 | its(:lines) { should include(rule) } 264 | end 265 | else 266 | describe 'audit docker service' do 267 | skip 'Cannot determine docker socket' 268 | end 269 | end 270 | end 271 | 272 | control 'host-1.12' do 273 | impact 1.0 274 | title 'Audit Docker files and directories - /etc/default/docker' 275 | desc 'Audit /etc/default/docker , if applicable. 276 | 277 | Rationale: Apart from auditing your regular Linux file system and system calls, audit all Docker related files and directories. Docker daemon runs with \'root\' privileges. Its behavior depends on some key files and directories. /etc/default/docker is one such file. It holds various parameters for Docker daemon. It must be audited, if applicable.' 278 | 279 | tag 'host' 280 | tag 'cis-docker-1.12.0': '1.12' 281 | tag 'cis-docker-1.13.0': '1.10' 282 | tag 'level:1' 283 | ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' 284 | 285 | only_if { os.linux? } 286 | describe auditd do 287 | its(:lines) { should include('-w /etc/default/docker -p rwxa -k docker') } 288 | end 289 | end 290 | 291 | control 'host-1.13' do 292 | impact 1.0 293 | title 'Audit Docker files and directories - /etc/docker/daemon.json' 294 | desc 'Audit /etc/docker/daemon.json, if applicable. 295 | 296 | Rationale: Apart from auditing your regular Linux file system and system calls, audit all Docker related files and directories. Docker daemon runs with \'root\' privileges. Its behavior depends on some key files and directories. /etc/docker/daemon.json is one such file. It holds various parameters for Docker daemon. It must be audited, if applicable.' 297 | 298 | tag 'host' 299 | tag 'cis-docker-1.12.0': '1.13' 300 | tag 'cis-docker-1.13.0': '1.11' 301 | tag 'level:1' 302 | ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' 303 | ref 'Daemon configuration', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/#daemon-configuration-file' 304 | 305 | only_if { os.linux? } 306 | describe auditd do 307 | its(:lines) { should include('-w /etc/docker/daemon.json -p rwxa -k docker') } 308 | end 309 | end 310 | 311 | control 'host-1.14' do 312 | impact 1.0 313 | title 'Audit Docker files and directories - /usr/bin/docker-containerd' 314 | desc 'Audit /usr/bin/docker-containerd, if applicable. 315 | 316 | Rationale: Apart from auditing your regular Linux file system and system calls, audit all Docker related files and directories. Docker daemon runs with \'root\' privileges. Its behavior depends on some key files and directories. /usr/bin/docker-containerd is one such file. Docker now relies on containerd and runC to spawn containers. It must be audited, if applicable.' 317 | 318 | tag 'host' 319 | tag 'cis-docker-1.12.0': '1.14' 320 | tag 'cis-docker-1.13.0': '1.12' 321 | tag 'level:1' 322 | ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' 323 | ref 'Containerd integration', url: 'https://github.com/docker/docker/pull/20662' 324 | ref 'Containerd tools', url: 'https://containerd.tools/' 325 | 326 | only_if { os.linux? } 327 | describe auditd do 328 | its(:lines) { should include('-w /usr/bin/docker-containerd -p rwxa -k docker') } 329 | end 330 | end 331 | 332 | control 'host-1.15' do 333 | impact 1.0 334 | title 'Audit Docker files and directories - /usr/bin/docker-runc' 335 | desc 'Audit /usr/bin/docker-runc, if applicable. 336 | 337 | Rationale: Apart from auditing your regular Linux file system and system calls, audit all Docker related files and directories. Docker daemon runs with \'root\' privileges. Its behavior depends on some key files and directories. /usr/bin/docker-runc is one such file. Docker now relies on containerd and runC to spawn containers. It must be audited, if applicable.' 338 | 339 | tag 'host' 340 | tag 'cis-docker-1.12.0': '1.15' 341 | tag 'cis-docker-1.13.0': '1.13' 342 | tag 'level:1' 343 | ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' 344 | ref 'Containerd integration', url: 'https://github.com/docker/docker/pull/20662' 345 | ref 'Containerd tools', url: 'https://containerd.tools/' 346 | ref 'Opencontainers runc repository', url: 'https://github.com/opencontainers/runc' 347 | 348 | only_if { os.linux? } 349 | describe auditd do 350 | its(:lines) { should include('-w /usr/bin/docker-runc -p rwxa -k docker') } 351 | end 352 | end 353 | -------------------------------------------------------------------------------- /controls/docker_daemon_configuration_files.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2016, Patrick Muench 4 | # Copyright:: 2017, Christoph Hartmann 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # author: Christoph Hartmann 19 | # author: Dominik Richter 20 | # author: Patrick Muench 21 | 22 | title 'Docker Daemon Configuration Files' 23 | 24 | # attributes 25 | REGISTRY_CERT_PATH = input('registry_cert_path') 26 | REGISTRY_NAME = input('registry_name') 27 | REGISTRY_CA_FILE = input('registry_ca_file') 28 | 29 | # check if docker exists 30 | only_if('docker not found') do 31 | command('docker').exist? 32 | end 33 | 34 | control 'docker-3.1' do 35 | impact 1.0 36 | title 'Verify that docker.service file ownership is set to root:root' 37 | desc 'Verify that the \'docker.service\' file ownership and group-ownership are correctly set to \'root\'. 38 | 39 | Rationale: \'docker.service\' file contains sensitive parameters that may alter the behavior of Docker daemon. Hence, it should be owned and group-owned by \'root\' to maintain the integrity of the file.' 40 | 41 | tag 'docker' 42 | tag 'cis-docker-1.12.0': '3.1' 43 | tag 'cis-docker-1.13.0': '3.1' 44 | tag 'level:1' 45 | ref 'Control and configure Docker with systemd', url: 'https://docs.docker.com/engine/admin/systemd/' 46 | 47 | describe file(docker_helper.path) do 48 | it { should exist } 49 | it { should be_file } 50 | it { should be_owned_by 'root' } 51 | it { should be_grouped_into 'root' } 52 | end 53 | end 54 | 55 | control 'docker-3.2' do 56 | impact 1.0 57 | title 'Verify that docker.service file permissions are set to 644 or more restrictive' 58 | desc 'Verify that the \'docker.service\' file permissions are correctly set to \'644\' or more restrictive. 59 | 60 | Rationale: \'docker.service\' file contains sensitive parameters that may alter the behavior of Docker daemon. Hence, it should not be writable by any other user other than \'root\' to maintain the integrity of the file.' 61 | 62 | tag 'docker' 63 | tag 'cis-docker-1.12.0': '3.2' 64 | tag 'cis-docker-1.13.0': '3.2' 65 | tag 'level:1' 66 | ref 'Control and configure Docker with systemd', url: 'https://docs.docker.com/engine/admin/systemd/' 67 | 68 | describe file(docker_helper.path) do 69 | it { should exist } 70 | it { should be_file } 71 | it { should be_readable.by('owner') } 72 | it { should be_writable.by('owner') } 73 | it { should be_readable.by('group') } 74 | it { should_not be_writable.by('group') } 75 | it { should be_readable.by('other') } 76 | it { should_not be_writable.by('other') } 77 | it { should_not be_executable } 78 | end 79 | end 80 | 81 | control 'docker-3.3' do 82 | impact 1.0 83 | title 'Verify that docker.socket file ownership is set to root:root' 84 | desc 'Verify that the \'docker.socket\' file ownership and group-ownership are correctly set to \'root\' 85 | 86 | Rationale: \'docker.socket\' file contains sensitive parameters that may alter the behavior of Docker remote API. Hence, it should be owned and group-owned by \'root\' to maintain the integrity of the file.' 87 | 88 | tag 'docker' 89 | tag 'cis-docker-1.12.0': '3.3' 90 | tag 'cis-docker-1.13.0': '3.3' 91 | tag 'level:1' 92 | ref 'Dockerd', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/' 93 | ref 'YungSang/fedora-atomic-packer', url: 'https://github.com/YungSang/fedora-atomic-packer/blob/master/oem/docker.socket' 94 | ref 'CentOS 7/RHEL 7 and docker containers on boot', url: 'https://daviddaeschler.com/2014/12/14/centos-7rhel-7-and-docker-containers-on-boot/' 95 | 96 | describe file(docker_helper.socket) do 97 | it { should exist } 98 | it { should be_file } 99 | it { should be_owned_by 'root' } 100 | it { should be_grouped_into 'root' } 101 | end 102 | end 103 | 104 | control 'docker-3.4' do 105 | impact 1.0 106 | title 'Verify that docker.socket file permissions are set to 644 or more restrictive' 107 | desc 'Verify that the \'docker.socket\' file permissions are correctly set to \'644\' or more restrictive. 108 | 109 | Rationale: \'docker.socket\' file contains sensitive parameters that may alter the behavior of Docker remote API. Hence, it should be writable only by \'root\' to maintain the integrity of the file.' 110 | 111 | tag 'docker' 112 | tag 'cis-docker-1.12.0': '3.4' 113 | tag 'cis-docker-1.13.0': '3.4' 114 | tag 'level:1' 115 | ref 'Dockerd', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/' 116 | ref 'YungSang/fedora-atomic-packer', url: 'https://github.com/YungSang/fedora-atomic-packer/blob/master/oem/docker.socket' 117 | ref 'CentOS 7/RHEL 7 and docker containers on boot', url: 'https://daviddaeschler.com/2014/12/14/centos-7rhel-7-and-docker-containers-on-boot/' 118 | 119 | describe file(docker_helper.socket) do 120 | it { should exist } 121 | it { should be_file } 122 | it { should be_readable.by('owner') } 123 | it { should be_writable.by('owner') } 124 | it { should be_readable.by('group') } 125 | it { should_not be_writable.by('group') } 126 | it { should be_readable.by('other') } 127 | it { should_not be_writable.by('other') } 128 | it { should_not be_executable } 129 | end 130 | end 131 | 132 | control 'docker-3.5' do 133 | impact 1.0 134 | title 'Verify that /etc/docker directory ownership is set to root:root' 135 | desc '\'/etc/docker\' directory contains certificates and keys in addition to various sensitive files. Hence, it should be owned and group-owned by \'root\' to maintain the integrity of the directory. 136 | 137 | Rationale: \'/etc/docker\' directory contains certificates and keys in addition to various sensitive files. Hence, it should be owned and group-owned by \'root\' to maintain the integrity of the directory.' 138 | 139 | tag 'docker' 140 | tag 'cis-docker-1.12.0': '3.5' 141 | tag 'cis-docker-1.13.0': '3.5' 142 | tag 'level:1' 143 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 144 | 145 | describe file('/etc/docker') do 146 | it { should exist } 147 | it { should be_directory } 148 | it { should be_owned_by 'root' } 149 | it { should be_grouped_into 'root' } 150 | end 151 | end 152 | 153 | control 'docker-3.6' do 154 | impact 1.0 155 | title 'Verify that /etc/docker directory permissions are set to 755 or more restrictive' 156 | desc 'Verify that the /etc/docker directory permissions are correctly set to \'755\' or more restrictive. 157 | 158 | Rationale: \'/etc/docker\' directory contains certificates and keys in addition to various sensitive files. Hence, it should only be writable by \'root\' to maintain the integrity of the directory.' 159 | 160 | tag 'docker' 161 | tag 'cis-docker-1.12.0': '3.6' 162 | tag 'cis-docker-1.13.0': '3.6' 163 | tag 'level:1' 164 | ref 'Docker Security', url: 'https://docs.docker.com/engine/security/security/#conclusions' 165 | 166 | describe file('/etc/docker') do 167 | it { should exist } 168 | it { should be_directory } 169 | it { should be_readable.by('owner') } 170 | it { should be_writable.by('owner') } 171 | it { should be_executable.by('owner') } 172 | it { should be_readable.by('group') } 173 | it { should_not be_writable.by('group') } 174 | it { should be_executable.by('group') } 175 | it { should be_readable.by('other') } 176 | it { should_not be_writable.by('other') } 177 | it { should be_executable.by('other') } 178 | end 179 | end 180 | 181 | control 'docker-3.7' do 182 | impact 1.0 183 | title 'Verify that registry certificate file ownership is set to root:root' 184 | desc 'Verify that all the registry certificate files (usually found under /etc/docker/certs.d/ directory) are owned and group-owned by \'root\'. 185 | 186 | Rationale: /etc/docker/certs.d/ directory contains Docker registry certificates. These certificate files must be owned and group-owned by \'root\' to maintain the integrity of the certificates.' 187 | 188 | tag 'docker' 189 | tag 'cis-docker-1.12.0': '3.7' 190 | tag 'cis-docker-1.13.0': '3.7' 191 | tag 'level:1' 192 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 193 | ref 'Verify repository client with certificates', url: 'https://docs.docker.com/engine/security/certificates/' 194 | ref 'Insecure Registry', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/' 195 | 196 | describe file(REGISTRY_CERT_PATH) do 197 | it { should exist } 198 | it { should be_directory } 199 | it { should be_owned_by 'root' } 200 | it { should be_grouped_into 'root' } 201 | end 202 | 203 | describe file(REGISTRY_NAME) do 204 | it { should exist } 205 | it { should be_directory } 206 | it { should be_owned_by 'root' } 207 | it { should be_grouped_into 'root' } 208 | end 209 | 210 | describe file(REGISTRY_CA_FILE) do 211 | it { should exist } 212 | it { should be_file } 213 | it { should be_owned_by 'root' } 214 | it { should be_grouped_into 'root' } 215 | end 216 | end 217 | 218 | control 'docker-3.8' do 219 | impact 1.0 220 | title 'Verify that registry certificate file permissions are set to 444 or more restrictive' 221 | desc 'Verify that all the registry certificate files (usually found under /etc/docker/certs.d/ directory) have permissions of \'444\' or more restrictive. 222 | 223 | Rationale: /etc/docker/certs.d/ directory contains Docker registry certificates. These certificate files must have permissions of \'444\' to maintain the integrity of the certificates.' 224 | 225 | tag 'docker' 226 | tag 'cis-docker-1.12.0': '3.8' 227 | tag 'cis-docker-1.13.0': '3.8' 228 | tag 'level:1' 229 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 230 | ref 'Verify repository client with certificates', url: 'https://docs.docker.com/engine/security/certificates/' 231 | ref 'Insecure Registry', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/' 232 | 233 | describe file(REGISTRY_CA_FILE) do 234 | it { should exist } 235 | it { should be_file } 236 | it { should be_readable } 237 | it { should_not be_executable } 238 | it { should_not be_writable } 239 | end 240 | end 241 | 242 | control 'docker-3.9' do 243 | impact 1.0 244 | title 'Verify that TLS CA certificate file ownership is set to root:root' 245 | desc 'Verify that the TLS CA certificate file (the file that is passed alongwith \'--tlscacert\' parameter) is owned and group-owned by \'root\'. 246 | 247 | Rationale: The TLS CA certificate file should be protected from any tampering. It is used to authenticate Docker server based on given CA certificate. Hence, it must be owned and group-owned by \'root\' to maintain the integrity of the CA certificate.' 248 | 249 | tag 'docker' 250 | tag 'cis-docker-1.12.0': '3.9' 251 | tag 'cis-docker-1.13.0': '3.9' 252 | tag 'level:1' 253 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 254 | ref 'Verify repository client with certificates', url: 'https://docs.docker.com/engine/security/certificates/' 255 | ref 'Insecure Registry', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/' 256 | 257 | describe file(json('/etc/docker/daemon.json').params['tlscacert']) do 258 | it { should exist } 259 | it { should be_file } 260 | it { should be_owned_by 'root' } 261 | it { should be_grouped_into 'root' } 262 | end 263 | end 264 | 265 | control 'docker-3.10' do 266 | impact 1.0 267 | title 'Verify that TLS CA certificate file permissions are set to 444 or more restrictive' 268 | desc 'Verify that the TLS CA certificate file (the file that is passed alongwith \'--tlscacert\' parameter) has permissions of \'444\' or more restrictive. 269 | 270 | Rationale: The TLS CA certificate file should be protected from any tampering. It is used to authenticate Docker server based on given CA certificate. Hence, it must have permissions of \'444\' to maintain the integrity of the CA certificate.' 271 | 272 | tag 'docker' 273 | tag 'cis-docker-1.12.0': '3.10' 274 | tag 'cis-docker-1.13.0': '3.10' 275 | tag 'level:1' 276 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 277 | ref 'Verify repository client with certificates', url: 'https://docs.docker.com/engine/security/certificates/' 278 | ref 'Insecure Registry', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/' 279 | 280 | describe file(json('/etc/docker/daemon.json').params['tlscacert']) do 281 | it { should exist } 282 | it { should be_file } 283 | it { should be_readable } 284 | it { should_not be_executable } 285 | it { should_not be_writable } 286 | end 287 | end 288 | 289 | control 'docker-3.11' do 290 | impact 1.0 291 | title 'Verify that Docker server certificate file ownership is set to root:root' 292 | desc 'Verify that the Docker server certificate file (the file that is passed alongwith \'--tlscert\' parameter) is owned and group-owned by \'root\'. 293 | 294 | Rationale: The Docker server certificate file should be protected from any tampering. It is used to authenticate Docker server based on the given server certificate. Hence, it must be owned and group-owned by \'root\' to maintain the integrity of the certificate.' 295 | 296 | tag 'docker' 297 | tag 'cis-docker-1.12.0': '3.11' 298 | tag 'cis-docker-1.13.0': '3.11' 299 | tag 'level:1' 300 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 301 | ref 'Verify repository client with certificates', url: 'https://docs.docker.com/engine/security/certificates/' 302 | ref 'Insecure Registry', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/' 303 | 304 | describe file(json('/etc/docker/daemon.json').params['tlscert']) do 305 | it { should exist } 306 | it { should be_file } 307 | it { should be_owned_by 'root' } 308 | it { should be_grouped_into 'root' } 309 | end 310 | end 311 | 312 | control 'docker-3.12' do 313 | impact 1.0 314 | title 'Verify that Docker server certificate file permissions are set to 444 or more restrictive' 315 | desc 'Verify that the Docker server certificate file (the file that is passed alongwith \'--tlscert\' parameter) has permissions of \'444\' or more restrictive. 316 | 317 | Rationale: The Docker server certificate file should be protected from any tampering. It is used to authenticate Docker server based on the given server certificate. Hence, it must have permissions of \'444\' to maintain the integrity of the certificate.' 318 | 319 | tag 'docker' 320 | tag 'cis-docker-1.12.0': '3.12' 321 | tag 'cis-docker-1.13.0': '3.12' 322 | tag 'level:1' 323 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 324 | ref 'Verify repository client with certificates', url: 'https://docs.docker.com/engine/security/certificates/' 325 | ref 'Insecure Registry', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/' 326 | 327 | describe file(json('/etc/docker/daemon.json').params['tlscert']) do 328 | it { should exist } 329 | it { should be_file } 330 | it { should be_readable } 331 | it { should_not be_executable } 332 | it { should_not be_writable } 333 | end 334 | end 335 | 336 | control 'docker-3.13' do 337 | impact 1.0 338 | title 'Verify that Docker server certificate key file ownership is set to root:root' 339 | desc 'Verify that the Docker server certificate key file (the file that is passed alongwith \'--tlskey\' parameter) is owned and group-owned by \'root\'. 340 | 341 | Rationale: The Docker server certificate key file should be protected from any tampering or unneeded reads. It holds the private key for the Docker server certificate. Hence, it must be owned and group-owned by \'root\' to maintain the integrity of the Docker server certificate.' 342 | 343 | tag 'docker' 344 | tag 'cis-docker-1.12.0': '3.13' 345 | tag 'cis-docker-1.13.0': '3.13' 346 | tag 'level:1' 347 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 348 | ref 'Verify repository client with certificates', url: 'https://docs.docker.com/engine/security/certificates/' 349 | ref 'Insecure Registry', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/' 350 | 351 | describe file(json('/etc/docker/daemon.json').params['tlskey']) do 352 | it { should exist } 353 | it { should be_file } 354 | it { should be_owned_by 'root' } 355 | it { should be_grouped_into 'root' } 356 | end 357 | end 358 | 359 | control 'docker-3.14' do 360 | impact 1.0 361 | title 'Verify that Docker server certificate key file permissions are set to 444 or more restrictive' 362 | desc 'Verify that the Docker server certificate key file (the file that is passed alongwith \'--tlskey\' parameter) has permissions of \'400\'. 363 | 364 | Rationale: The Docker server certificate key file should be protected from any tampering or unneeded reads. It holds the private key for the Docker server certificate. Hence, it must have permissions of \'400\' to maintain the integrity of the Docker server certificate.' 365 | 366 | tag 'docker' 367 | tag 'cis-docker-1.12.0': '3.14' 368 | tag 'cis-docker-1.13.0': '3.14' 369 | tag 'level:1' 370 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 371 | ref 'Verify repository client with certificates', url: 'https://docs.docker.com/engine/security/certificates/' 372 | ref 'Insecure Registry', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/' 373 | 374 | describe file(json('/etc/docker/daemon.json').params['tlskey']) do 375 | it { should exist } 376 | it { should be_file } 377 | it { should be_readable } 378 | it { should_not be_executable } 379 | it { should_not be_writable } 380 | end 381 | end 382 | 383 | control 'docker-3.15' do 384 | impact 1.0 385 | title 'Verify that Docker socket file ownership is set to root:docker' 386 | desc 'Verify that the Docker socket file is owned by \'root\' and group-owned by \'docker\'. 387 | 388 | Rationale: Docker daemon runs as \'root\'. The default Unix socket hence must be owned by \'root\'. If any other user or process owns this socket, then it might be possible for that non-privileged user or process to interact with Docker daemon. Also, such a non-privileged user or process might interact with containers. This is neither secure nor desired behavior. Additionally, the Docker installer creates a Unix group called \'docker\'. You can add users to this group, and then those users would be able to read and write to default Docker Unix socket. The membership to the \'docker\' group is tightly controlled by the system administrator. If any other group owns this socket, then it might be possible for members of that group to interact with Docker daemon. Also, such a group might not be as tightly controlled as the \'docker\' group. This is neither secure nor desired behavior. Hence, the default Docker Unix socket file must be owned by \'root\' and group-owned by \'docker\' to maintain the integrity of the socket file.' 389 | 390 | tag 'docker' 391 | tag 'cis-docker-1.12.0': '3.15' 392 | tag 'cis-docker-1.13.0': '3.15' 393 | tag 'level:1' 394 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/#daemon-socket-option' 395 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 396 | 397 | describe file('/var/run/docker.sock') do 398 | it { should exist } 399 | it { should be_socket } 400 | it { should be_owned_by 'root' } 401 | it { should be_grouped_into 'docker' } 402 | end 403 | end 404 | 405 | control 'docker-3.16' do 406 | impact 1.0 407 | title 'Verify that Docker socket file permissions are set to 660 or more restrictive' 408 | desc 'Only \'root\' and members of \'docker\' group should be allowed to read and write to default Docker Unix socket. Hence, the Docket socket file must have permissions of \'660\' or more restrictive. 409 | 410 | Rationale: Only \'root\' and members of \'docker\' group should be allowed to read and write to default Docker Unix socket. Hence, the Docket socket file must have permissions of \'660\' or more restrictive.' 411 | 412 | tag 'docker' 413 | tag 'cis-docker-1.12.0': '3.16' 414 | tag 'cis-docker-1.13.0': '3.16' 415 | tag 'level:1' 416 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/#daemon-socket-option' 417 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 418 | 419 | describe file('/var/run/docker.sock') do 420 | it { should exist } 421 | it { should be_socket } 422 | it { should be_readable.by('owner') } 423 | it { should be_writable.by('owner') } 424 | it { should_not be_executable.by('owner') } 425 | it { should be_readable.by('group') } 426 | it { should be_writable.by('group') } 427 | it { should_not be_executable.by('group') } 428 | it { should_not be_readable.by('other') } 429 | it { should_not be_writable.by('other') } 430 | it { should_not be_executable.by('other') } 431 | end 432 | end 433 | 434 | control 'docker-3.17' do 435 | impact 1.0 436 | title 'Verify that daemon.json file ownership is set to root:root' 437 | desc '\'daemon.json\' file contains sensitive parameters that may alter the behavior of docker daemon. Hence, it should be owned and group-owned by \'root\' to maintain the integrity of the file. 438 | 439 | Rationale: \'daemon.json\' file contains sensitive parameters that may alter the behavior of docker daemon. Hence, it should be owned and group-owned by \'root\' to maintain the integrity of the file.' 440 | 441 | tag 'docker' 442 | tag 'cis-docker-1.12.0': '3.17' 443 | tag 'cis-docker-1.13.0': '3.17' 444 | tag 'level:1' 445 | ref 'dockerd', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/#miscellaneous-options' 446 | 447 | describe file('/etc/docker/daemon.json') do 448 | it { should exist } 449 | it { should be_file } 450 | it { should be_owned_by 'root' } 451 | it { should be_grouped_into 'root' } 452 | end 453 | end 454 | 455 | control 'docker-3.18' do 456 | impact 1.0 457 | title 'Verify that /etc/docker/daemon.json file permissions are set to 644 or more restrictive' 458 | desc '\'daemon.json\' file contains sensitive parameters that may alter the behavior of docker daemon. Hence, it should be writable only by \'root\' to maintain the integrity of the file. 459 | 460 | Rationale: \'daemon.json\' file contains sensitive parameters that may alter the behavior of docker daemon. Hence, it should be writable only by \'root\' to maintain the integrity of the file.' 461 | 462 | tag 'docker' 463 | tag 'cis-docker-1.12.0': '3.18' 464 | tag 'cis-docker-1.13.0': '3.18' 465 | tag 'level:1' 466 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/#daemon-socket-option' 467 | ref 'Protect the Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 468 | ref 'dockerd', url: 'https://docs.docker.com/engine/reference/commandline/dockerd/#miscellaneous-options' 469 | 470 | describe file('/etc/docker/daemon.json') do 471 | it { should exist } 472 | it { should be_file } 473 | it { should be_readable.by('owner') } 474 | it { should be_writable.by('owner') } 475 | it { should_not be_executable.by('owner') } 476 | it { should be_readable.by('group') } 477 | it { should_not be_writable.by('group') } 478 | it { should_not be_executable.by('group') } 479 | it { should be_readable.by('other') } 480 | it { should_not be_writable.by('other') } 481 | it { should_not be_executable.by('other') } 482 | end 483 | end 484 | 485 | control 'docker-3.19' do 486 | impact 1.0 487 | title 'Verify that /etc/default/docker file ownership is set to root:root' 488 | desc '\'/etc/default/docker\' file contains sensitive parameters that may alter the behavior of docker daemon. Hence, it should be owned and group-owned by \'root\' to maintain the integrity of the file. 489 | 490 | Rationale: \'/etc/default/docker\' file contains sensitive parameters that may alter the behavior of docker daemon. Hence, it should be owned and group-owned by \'root\' to maintain the integrity of the file.' 491 | 492 | tag 'docker' 493 | tag 'cis-docker-1.12.0': '3.19' 494 | tag 'cis-docker-1.13.0': '3.19' 495 | tag 'level:1' 496 | ref 'Configure and troubleshoot the Docker daemon', url: 'https://docs.docker.com/engine/admin/' 497 | 498 | only_if { os[:family] != 'centos' } 499 | describe file('/etc/default/docker') do 500 | it { should exist } 501 | it { should be_file } 502 | it { should be_owned_by 'root' } 503 | it { should be_grouped_into 'root' } 504 | end 505 | end 506 | 507 | control 'docker-3.20' do 508 | impact 1.0 509 | title 'Verify that /etc/default/docker file permissions are set to 644 or more restrictive' 510 | desc 'Verify that the \'/etc/default/docker\' file permissions are correctly set to \'644\' or more restrictive. 511 | 512 | Rationale: \'/etc/default/docker\' file contains sensitive parameters that may alter the behavior of docker daemon. Hence, it should be writable only by \'root\' to maintain the integrity of the file.' 513 | 514 | tag 'docker' 515 | tag 'cis-docker-1.12.0': '3.20' 516 | tag 'cis-docker-1.13.0': '3.20' 517 | tag 'level:1' 518 | ref 'Configure and troubleshoot the Docker daemon', url: 'https://docs.docker.com/engine/admin/' 519 | 520 | only_if { os[:family] != 'centos' } 521 | describe file('/etc/default/docker') do 522 | it { should exist } 523 | it { should be_file } 524 | it { should be_readable.by('owner') } 525 | it { should be_writable.by('owner') } 526 | it { should_not be_executable.by('owner') } 527 | it { should be_readable.by('group') } 528 | it { should_not be_writable.by('group') } 529 | it { should_not be_executable.by('group') } 530 | it { should be_readable.by('other') } 531 | it { should_not be_writable.by('other') } 532 | it { should_not be_executable.by('other') } 533 | end 534 | end 535 | -------------------------------------------------------------------------------- /controls/docker_daemon_configuration.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2016, Patrick Muench 4 | # Copyright:: 2017, Christoph Hartmann 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # author: Christoph Hartmann 19 | # author: Dominik Richter 20 | # author: Patrick Muench 21 | 22 | title 'Docker Daemon Configuration' 23 | 24 | # attributes 25 | DAEMON_TLSCACERT = input('daemon_tlscacert') 26 | DAEMON_TLSCERT = input('daemon_tlscert') 27 | DAEMON_TLSKEY = input('daemon_tlskey') 28 | AUTHORIZATION_PLUGIN = input('authorization_plugin') 29 | LOG_DRIVER = input('log_driver') 30 | LOG_OPTS = input('log_opts') 31 | SWARM_MODE = input('swarm_mode') 32 | SWARM_MAX_MANAGER_NODES = input('swarm_max_manager_nodes') 33 | SWARM_PORT = input('swarm_port') 34 | SECCOMP_DEFAULT_PROFILE = input('seccomp_default_profile') 35 | 36 | # check if docker exists 37 | only_if('docker not found') do 38 | command('docker').exist? 39 | end 40 | 41 | control 'docker-2.1' do 42 | impact 1.0 43 | title 'Restrict network traffic between containers' 44 | desc 'By default, all network traffic is allowed between containers on the same host. If not desired, restrict all the intercontainer communication. Link specific containers together that require inter communication. 45 | 46 | Rationale: By default, unrestricted network traffic is enabled between all containers on the same host. Thus, each container has the potential of reading all packets across the container network on the same host. This might lead to unintended and unwanted disclosure of information to other containers. Hence, restrict the inter container communication.' 47 | 48 | tag 'docker' 49 | tag 'cis-docker-1.12.0': '2.1' 50 | tag 'cis-docker-1.13.0': '2.1' 51 | tag 'level:1' 52 | ref 'Docker container networking', url: 'https://docs.docker.com/engine/userguide/networking/' 53 | 54 | describe json('/etc/docker/daemon.json') do 55 | its(['icc']) { should eq(false) } 56 | end 57 | end 58 | 59 | control 'docker-2.2' do 60 | impact 1.0 61 | title 'Set the logging level' 62 | desc 'Set Docker daemon log level to \'info\'. 63 | 64 | Rationale: Setting up an appropriate log level, configures the Docker daemon to log events that you would want to review later. A ase log level of \'info\' and above would capture all logs except debug logs. Until and unless required, you should not run docker daemon at \'debug\' log level.' 65 | 66 | tag 'docker' 67 | tag 'cis-docker-1.12.0': '2.2' 68 | tag 'cis-docker-1.13.0': '2.2' 69 | tag 'level:1' 70 | ref 'Docker daemon', url: 'https://docs.docker.com/engine/reference/commandline/daemon/' 71 | 72 | describe json('/etc/docker/daemon.json') do 73 | its(['log-level']) { should eq('info') } 74 | end 75 | end 76 | 77 | control 'docker-2.3' do 78 | impact 1.0 79 | title 'Allow Docker to make changes to iptables' 80 | desc 'Iptables are used to set up, maintain, and inspect the tables of IP packet filter rules in the Linux kernel. Allow the Docker daemon to make changes to the iptables. 81 | 82 | Rationale: Docker will never make changes to your system iptables rules if you choose to do so. Docker server would automatically make the needed changes to iptables based on how you choose your networking options for the containers if it is allowed to do so. It is recommended to let Docker server make changes to iptables automatically to avoid networking misconfiguration that might hamper the communication between containers and to the outside world. Additionally, it would save you hassles of updating iptables every time you choose to run the containers or modify networking options.' 83 | 84 | tag 'docker' 85 | tag 'cis-docker-1.12.0': '2.3' 86 | tag 'cis-docker-1.13.0': '2.3' 87 | tag 'level:1' 88 | ref 'Understand container communication', url: 'https://docs.docker.com/engine/userguide/networking/default_network/container-communication/' 89 | 90 | describe json('/etc/docker/daemon.json') do 91 | its(['iptables']) { should eq(true) } 92 | end 93 | end 94 | 95 | control 'docker-2.4' do 96 | impact 1.0 97 | title 'Do not use insecure registries' 98 | desc 'Docker considers a private registry either secure or insecure. By default, registries are considered secure. 99 | 100 | Rationale: A secure registry uses TLS. A copy of registry\'s CA certificate is placed on the Docker host at \'/etc/docker/certs.d//\' directory. An insecure registry is the one not having either valid registry certificate or is not using TLS. You should not be using any insecure registries in the production environment. Insecure registries can be tampered with leading to possible compromise to your production system. Additionally, If a registry is marked as insecure then \'docker pull\', \'docker push\', and \'docker search\' commands will not result in an error message and the user might be indefinitely working with insecure registries without ever being notified of potential danger.' 101 | 102 | tag 'docker' 103 | tag 'cis-docker-1.12.0': '2.4' 104 | tag 'cis-docker-1.13.0': '2.4' 105 | tag 'level:1' 106 | ref 'Insecure registry', url: 'https://docs.docker.com/registry/insecure/' 107 | 108 | describe json('/etc/docker/daemon.json') do 109 | its(['insecure-registries']) { should be_empty } 110 | end 111 | end 112 | 113 | control 'docker-2.5' do 114 | impact 1.0 115 | title 'Do not use the aufs storage driver' 116 | desc 'Do not use \'aufs\' as storage driver for your Docker instance. 117 | 118 | Rationale: The \'aufs\' storage driver is the oldest storage driver. It is based on a Linux kernel patch-set that is unlikely to be merged into the main Linux kernel. \'aufs\' driver is also known to cause some serious kernel crashes. \'aufs\' just has legacy support from Docker. Most importantly, \'aufs\' is not a supported driver in many Linux distributions using latest Linux kernels.' 119 | 120 | tag 'docker' 121 | tag 'cis-docker-1.12.0': '2.5' 122 | tag 'cis-docker-1.13.0': '2.5' 123 | tag 'level:1' 124 | ref 'Docker daemon storage driver options', url: 'https://docs.docker.com/engine/reference/commandline/cli/#daemon-storage-driver-option' 125 | ref 'Switch from aufs to devicemapper', url: 'http://muehe.org/posts/switching-docker-from-aufs-to-devicemapper/' 126 | ref 'Deep dive into docker storage drivers', url: 'http://jpetazzo.github.io/assets/2015-03-05-deep-dive-into-docker-storage-drivers.html#1' 127 | ref 'Docker storage drivers', url: 'https://docs.docker.com/engine/userguide/storagedriver/' 128 | 129 | describe json('/etc/docker/daemon.json') do 130 | its(['storage-driver']) { should_not eq('aufs') } 131 | end 132 | end 133 | 134 | control 'docker-2.6' do 135 | impact 1.0 136 | title 'Configure TLS authentication for Docker daemon' 137 | desc 'It is possible to make the Docker daemon to listen on a specific IP and port and any other Unix socket other than default Unix socket. Configure TLS authentication to restrict access to Docker daemon via IP and port. 138 | 139 | Rationale: By default, Docker daemon binds to a non-networked Unix socket and runs with \'root\' privileges. If you change the default docker daemon binding to a TCP port or any other Unix socket, anyone with access to that port or socket can have full access to Docker daemon and in turn to the host system. Hence, you should not bind the Docker daemon to another IP/port or a Unix socket. If you must expose the Docker daemon via a network socket, configure TLS authentication for the daemon and Docker Swarm APIs (if using). This would restrict the connections to your Docker daemon over the network to a limited number of clients who could successfully authenticate over TLS.' 140 | 141 | tag 'docker' 142 | tag 'cis-docker-1.12.0': '2.6' 143 | tag 'cis-docker-1.13.0': '2.6' 144 | tag 'level:1' 145 | ref 'Protect Docker daemon socket', url: 'https://docs.docker.com/engine/security/https/' 146 | 147 | describe json('/etc/docker/daemon.json') do 148 | its(['tls']) { should eq(true) } 149 | its(['tlsverify']) { should eq(true) } 150 | its(['tlscacert']) { should eq(DAEMON_TLSCACERT) } 151 | its(['tlscert']) { should eq(DAEMON_TLSCERT) } 152 | its(['tlskey']) { should eq(DAEMON_TLSKEY) } 153 | end 154 | end 155 | 156 | control 'docker-2.7' do 157 | impact 1.0 158 | title 'Set default ulimit as appropriate' 159 | desc 'Set the default ulimit options as appropriate in your environment. 160 | 161 | Rationale: ulimit provides control over the resources available to the shell and to processes started by it. Setting system resource limits judiciously saves you from many disasters such as a fork bomb. Sometimes, even friendly users and legitimate processes can overuse system resources and in-turn can make the system unusable. Setting default ulimit for the Docker daemon would enforce the ulimit for all container instances. You would not need to setup ulimit for each container instance. However, the default ulimit can be overridden during container runtime, if needed. Hence, to control the system resources, define a default ulimit as needed in your environment.' 162 | 163 | tag 'docker' 164 | tag 'cis-docker-1.12.0': '2.7' 165 | tag 'cis-docker-1.13.0': '2.7' 166 | tag 'level:1' 167 | ref 'Docker daemon default ulimits', url: 'https://docs.docker.com/engine/reference/commandline/daemon/#default-ulimits' 168 | 169 | describe json('/etc/docker/daemon.json') do 170 | its(%w(default-ulimits nproc)) { should eq('1024:2408') } 171 | its(%w(default-ulimits nofile)) { should eq('100': '200') } 172 | end 173 | end 174 | 175 | control 'docker-2.8' do 176 | impact 1.0 177 | title 'Enable user namespace support' 178 | desc 'Enable user namespace support in Docker daemon to utilize container user to host user re-mapping. This recommendation is beneficial where containers you are using do not have an explicit container user defined in the container image. If container images that you are using have a pre-defined non-root user, this recommendation may be skipped since this feature is still in its infancy and might give you unpredictable issues and complexities. 179 | 180 | Rationale: The Linux kernel user namespace support in Docker daemon provides additional security for the Docker host system. It allows a container to have a unique range of user and group IDs which are outside the traditional user and group range utilized by the host system. For example, the root user will have expected administrative privilege inside the container but can effectively be mapped to an unprivileged UID on the host system.' 181 | 182 | tag 'docker' 183 | tag 'cis-docker-1.12.0': '2.8' 184 | tag 'cis-docker-1.13.0': '2.8' 185 | tag 'level:2' 186 | ref 'User namespeces', url: 'http://man7.org/linux/man-pages/man7/user_namespaces.7.html' 187 | ref 'Docker daemon configuration', url: 'https://docs.docker.com/engine/reference/commandline/daemon/' 188 | ref 'Routing out root: user namespaces in docker', url: 'http://events.linuxfoundation.org/sites/events/files/slides/User%20Namespaces%20-%20ContainerCon%202015%20-%2016-9-final_0.pdf' 189 | ref 'Docker images vanish when using user namespaces ', url: 'https://github.com/docker/docker/issues/21050' 190 | 191 | describe json('/etc/docker/daemon.json') do 192 | its(['userns-remap']) { should eq('default') } 193 | end 194 | describe file('/etc/subuid') do 195 | it { should exist } 196 | it { should be_file } 197 | end 198 | describe file('/etc/subgid') do 199 | it { should exist } 200 | it { should be_file } 201 | end 202 | end 203 | 204 | control 'docker-2.9' do 205 | impact 1.0 206 | title 'Confirm default cgroup usage' 207 | desc 'The --cgroup-parent option allows you to set the default cgroup parent to use for all the containers. If there is no specific use case, this setting should be left at its default. 208 | 209 | Rationale: System administrators typically define cgroups under which containers are supposed to run. Even if cgroups are not explicitly defined by the system administrators, containers run under docker cgroup by default. It is possible to attach to a different cgroup other than that is the default. This usage should be monitored and confirmed. By attaching to a different cgroup than the one that is a default, it is possible to share resources unevenly and thus might starve the host for resources.' 210 | 211 | tag 'docker' 212 | tag 'cis-docker-1.12.0': '2.9' 213 | tag 'cis-docker-1.13.0': '2.9' 214 | tag 'level:2' 215 | ref 'Docker daemon configuration', url: 'https://docs.docker.com/engine/reference/commandline/daemon/' 216 | 217 | describe json('/etc/docker/daemon.json') do 218 | its(['cgroup-parent']) { should eq('docker') } 219 | end 220 | end 221 | 222 | control 'docker-2.10' do 223 | impact 1.0 224 | title 'Do not change base device size until needed' 225 | desc 'In certain circumstances, you might need containers bigger than 10G in size. In these cases, carefully choose the base device size. 226 | 227 | Rationale: The base device size can be increased at daemon restart. Increasing the base device size allows all future images and containers to be of the new base device size. A user can use this option to expand the base device size however shrinking is not permitted. This value affects the system-wide “base” empty filesystem that may already be initialized and inherited by pulled images. Though the file system does not allot the increased size if it is empty, it will use more space for the empty case depending upon the device size. This may cause a denial of service by ending up in file system being over-allocated or full.' 228 | 229 | tag 'docker' 230 | tag 'cis-docker-1.12.0': '2.10' 231 | tag 'cis-docker-1.13.0': '2.10' 232 | tag 'level:2' 233 | ref 'Docker daemon storage driver options', url: 'https://docs.docker.com/engine/reference/commandline/daemon/#storage-driver-options' 234 | 235 | describe json('/etc/docker/daemon.json') do 236 | its(['storage-opts']) { should eq(['dm.basesize=10G']) } 237 | end 238 | end 239 | 240 | control 'docker-2.11' do 241 | impact 1.0 242 | title 'Use authorization plugin' 243 | desc 'Docker’s out-of-the-box authorization model is all or nothing. Any user with permission to access the Docker daemon can run any Docker client command. The same is true for callers using Docker’s remote API to contact the daemon. If you require greater access control, you can create authorization plugins and add them to your Docker daemon configuration. Using an authorization plugin, a Docker administrator can configure granular access policies for managing access to Docker daemon. 244 | 245 | Rationale: Docker’s out-of-the-box authorization model is all or nothing. Any user with permission to access the Docker daemon can run any Docker client command. The same is true for callers using Docker’s remote API to contact the daemon. If you require greater access control, you can create authorization plugins and add them to your Docker daemon configuration. Using an authorization plugin, a Docker administrator can configure granular access policies for managing access to Docker daemon.' 246 | 247 | tag 'docker' 248 | tag 'cis-docker-1.12.0': '2.11' 249 | tag 'cis-docker-1.13.0': '2.11' 250 | tag 'level:2' 251 | ref 'Access authorization', url: 'https://docs.docker.com/engine/reference/commandline/daemon/#access-authorization' 252 | ref 'Auhtorization plugins', url: 'https://docs.docker.com/engine/extend/plugins_authorization/' 253 | ref 'Twistlock authorization plugin', url: 'https://github.com/twistlock/authz' 254 | 255 | describe json('/etc/docker/daemon.json') do 256 | its(['authorization-plugins']) { should_not be_empty } 257 | its(['authorization-plugins']) { should eq([AUTHORIZATION_PLUGIN]) } 258 | end 259 | end 260 | 261 | control 'docker-2.12' do 262 | impact 1.0 263 | title 'Configure centralized and remote logging' 264 | desc 'Docker now supports various log drivers. A preferable way to store logs is the one that supports centralized and remote logging. 265 | 266 | Ratonale: Centralized and remote logging ensures that all important log records are safe despite catastrophic events. Docker now supports various such logging drivers. Use the one that suits your environment the best.' 267 | 268 | tag 'docker' 269 | tag 'cis-docker-1.12.0': '2.12' 270 | tag 'cis-docker-1.13.0': '2.12' 271 | tag 'level:2' 272 | ref 'Logging overview', url: 'https://docs.docker.com/engine/admin/logging/overview/' 273 | 274 | describe json('/etc/docker/daemon.json') do 275 | its(['log-driver']) { should_not be_empty } 276 | its(['log-driver']) { should eq(LOG_DRIVER) } 277 | its(['log-opts']) { should include(LOG_OPTS) } 278 | end 279 | end 280 | 281 | control 'docker-2.13' do 282 | impact 1.0 283 | title 'Disable operations on legacy registry (v1)' 284 | desc 'The latest Docker registry is v2. All operations on the legacy registry version (v1) should be restricted. 285 | 286 | Rationale: Docker registry v2 brings in many performance and security improvements over v1. It supports container image provenance and other security features such as image signing and verification. Hence, operations on Docker legacy registry should be restricted.' 287 | 288 | tag 'docker' 289 | tag 'cis-docker-1.12.0': '2.13' 290 | tag 'cis-docker-1.13.0': '2.13' 291 | tag 'level:1' 292 | ref 'Docker daemon storage driver options', url: 'https://docs.docker.com/engine/reference/commandline/daemon/#storage-driver-options' 293 | ref 'Proposal: Provenance step 1 - Transform images for validation and verification', url: 'https://github.com/docker/docker/issues/8093' 294 | ref 'Proposal: JSON Registry API V2.1', url: 'https://github.com/docker/docker/issues/9015' 295 | ref 'Registry next generation', url: 'https://github.com/docker/docker-registry/issues/612' 296 | ref 'Docker Registry HTTP API V2', url: 'https://docs.docker.com/registry/spec/api/' 297 | ref 'Creating Private Docker Registry 2.0 with Token Authentication Service', url: 'https://the.binbashtheory.com/creating-private-docker-registry-2-0-with-token-authentication-service/' 298 | ref 'New Tool to Migrate From V1 Registry to Docker Trusted Registry or V2 Open Source Registry', url: 'https://blog.docker.com/2015/07/new-tool-v1-registry-docker-trusted-registry-v2-open-source/' 299 | ref 'Docker Registry V2', url: 'https://www.slideshare.net/Docker/docker-registry-v2' 300 | 301 | describe json('/etc/docker/daemon.json') do 302 | its(['disable-legacy-registry']) { should eq(true) } 303 | end 304 | end 305 | 306 | control 'docker-2.14' do 307 | impact 1.0 308 | title 'Enable live restore' 309 | desc 'The \'--live-restore\' enables full support of daemon-less containers in docker. It ensures that docker does not stop containers on shutdown or restore and properly reconnects to the container when restarted. 310 | 311 | Rationale: One of the important security triads is availability. Setting \'--live-restore\' flag in the docker daemon ensures that container execution is not interrupted when the docker daemon is not available. This also means that it is now easier to update and patch the docker daemon without execution downtime.' 312 | 313 | tag 'docker' 314 | tag 'cis-docker-1.12.0': '2.14' 315 | tag 'cis-docker-1.13.0': '2.14' 316 | tag 'level:1' 317 | ref 'Add --live-restore flag', url: 'https://github.com/docker/docker/pull/23213' 318 | 319 | describe json('/etc/docker/daemon.json') do 320 | its(['live-restore']) { should eq(true) } 321 | end 322 | end 323 | 324 | control 'docker-2.15' do 325 | impact 1.0 326 | title 'Do not enable swarm mode, if not needed' 327 | desc 'Do not enable swarm mode on a docker engine instance unless needed. 328 | 329 | Rationale: By default, a Docker engine instance will not listen on any network ports, with all communications with the client coming over the Unix socket. When Docker swarm mode is enabled on a docker engine instance, multiple network ports are opened on the system and made available to other systems on the network for the purposes of cluster management and node communications. Opening network ports on a system increase its attack surface and this should be avoided unless required.' 330 | 331 | tag 'docker' 332 | tag 'cis-docker-1.12.0': '2.15' 333 | tag 'cis-docker-1.13.0': '2.15' 334 | tag 'level:1' 335 | ref 'docker swarm init', url: 'https://docs.docker.com/engine/reference/commandline/swarm_init/' 336 | 337 | describe docker.info do 338 | its('Swarm.LocalNodeState') { should eq SWARM_MODE } 339 | end 340 | end 341 | 342 | control 'docker-2.16' do 343 | impact 1.0 344 | title 'Control the number of manager nodes in a swarm' 345 | desc 'Ensure that the minimum number of required manager nodes is created in a swarm. 346 | 347 | Rationale: Manager nodes within a swarm have control over the swarm and change its configuration modifying security parameters. Having excessive manager nodes could render the swarm more susceptible to compromise. If fault tolerance is not required in the manager nodes, a single node should be elected as a manager. If fault tolerance is required then the smallest practical odd number to achieve the appropriate level of tolerance should be configured.' 348 | 349 | tag 'docker' 350 | tag 'cis-docker-1.12.0': '2.16' 351 | tag 'cis-docker-1.13.0': '2.16' 352 | tag 'level:1' 353 | ref 'Manage nodes in a swarm', url: 'https://docs.docker.com/engine/swarm/manage-nodes/' 354 | ref 'Administer and maintain a swarm of Docker Engines', url: 'https://docs.docker.com/engine/swarm/admin_guide/' 355 | 356 | only_if { SWARM_MODE == 'active' } 357 | describe docker.info do 358 | its('Swarm.Managers') { should cmp <= SWARM_MAX_MANAGER_NODES } 359 | end 360 | end 361 | 362 | control 'docker-2.17' do 363 | impact 1.0 364 | title 'Bind swarm services to a specific host interface' 365 | desc 'By default, the docker swarm services will listen to all interfaces on the host, which may not be necessary for the operation of the swarm where the host has multiple network interfaces. 366 | 367 | Rationale: When a swarm is initialized the default value for the --listen-addr flag is 0.0.0.0\': \'2377 which means that the swarm services will listen on all interfaces on the host. If a host has multiple network interfaces this may be undesirable as it may expose the docker swarm services to networks which are not involved in the operation of the swarm. By passing a specific IP address to the --listen-addr, a specific network interface can be specified limiting this exposure.' 368 | 369 | tag 'docker' 370 | tag 'cis-docker-1.12.0': '2.17' 371 | tag 'cis-docker-1.13.0': '2.17' 372 | tag 'level:1' 373 | ref 'docker swarm init', url: 'https://docs.docker.com/engine/reference/commandline/swarm_init/' 374 | ref 'Administer and maintain a swarm of Docker Engines', url: 'https://docs.docker.com/engine/swarm/admin_guide/' 375 | 376 | only_if { SWARM_MODE == 'active' } 377 | describe port(SWARM_PORT) do 378 | its('addresses') { should_not include '0.0.0.0' } 379 | its('addresses') { should_not include '::' } 380 | end 381 | end 382 | 383 | control 'docker-2.18' do 384 | impact 1.0 385 | title 'Disable Userland Proxy' 386 | desc 'The docker daemon starts a userland proxy service for port forwarding whenever a port is exposed. Where hairpin NAT is available, this service is generally superfluous to requirements and can be disabled. 387 | 388 | Rationale: Docker engine provides two mechanisms for forwarding ports from the host to containers, hairpin NAT, and a userland proxy. In most circumstances, the hairpin NAT mode is preferred as it improves performance and makes use of native Linux iptables functionality instead of an additional component. Where hairpin NAT is available, the userland proxy should be disabled on startup to reduce the attack surface of the installation.' 389 | 390 | tag 'docker' 391 | tag 'cis-docker-1.12.0': '2.18' 392 | tag 'cis-docker-1.13.0': '2.18' 393 | tag 'level:1' 394 | ref 'The docker-proxy', url: 'http://windsock.io/the-docker-proxy/' 395 | ref 'Disable Userland proxy by default', url: 'https://github.com/docker/docker/issues/14856' 396 | ref 'overlay networking with userland-proxy disabled prevents port exposure', url: 'https://github.com/moby/moby/issues/22741' 397 | ref 'Bind container ports to the host', url: 'https://docs.docker.com/engine/userguide/networking/default_network/binding/' 398 | 399 | describe json('/etc/docker/daemon.json') do 400 | its(['userland-proxy']) { should eq(false) } 401 | end 402 | describe processes('dockerd').commands do 403 | it { should include 'userland-proxy=false' } 404 | end 405 | end 406 | 407 | control 'docker-2.19' do 408 | impact 1.0 409 | title 'Encrypt data exchanged between containers on different nodes on the overlay network' 410 | desc 'Encrypt data exchanged between containers on different nodes on the overlay network. 411 | 412 | Rationale: By default, data exchanged between containers on different nodes on the overlay network is not encrypted. This could potentially expose traffic between the container nodes.' 413 | 414 | tag 'docker' 415 | tag 'cis-docker-1.13.0': '2.19' 416 | tag 'level:1' 417 | ref 'Docker swarm mode overlay network security model', url: 'https://docs.docker.com/engine/userguide/networking/overlay-security-model/' 418 | ref 'Docker swarm container-container traffic not encrypted when inspecting externally with tcpdump', url: 'https://github.com/moby/moby/issues/24253' 419 | 420 | only_if { SWARM_MODE == 'active' } 421 | if docker_helper.overlay_networks 422 | docker_helper.overlay_networks.each do |k, _v| 423 | describe docker_helper.overlay_networks[k] do 424 | its(['encrypted']) { should_not eq(nil) } 425 | end 426 | end 427 | else 428 | describe 'Encrypted overlay networks' do 429 | skip 'Cannot determine overlay networks' 430 | end 431 | end 432 | end 433 | 434 | control 'docker-2.20' do 435 | impact 1.0 436 | title 'Apply a daemon-wide custom seccomp profile, if needed' 437 | desc 'You can choose to apply your custom seccomp profile at the daemon-wide level if needed and override Docker\'s default seccomp profile. 438 | 439 | Rationale: A large number of system calls are exposed to every userland process with many of them going unused for the entire lifetime of the process. Most of the applications do not need all the system calls and thus benefit by having a reduced set of available system calls. The reduced set of system calls reduces the total kernel surface exposed to the application and thus improvises application security. You could apply your own custom seccomp profile instead of Docker\'s default seccomp profile. Alternatively, if Docker\'s default profile is good for your environment, you can choose to ignore this recommendation.' 440 | 441 | tag 'docker' 442 | tag 'cis-docker-1.13.0': '2.20' 443 | tag 'level:2' 444 | ref 'daemon: add a flag to override the default seccomp profile', url: 'https://github.com/moby/moby/pull/26276' 445 | 446 | describe json('/etc/docker/daemon.json') do 447 | its(['seccomp-profile']) { should_not eq(nil) } 448 | its(['seccomp-profile']) { should eq(SECCOMP_DEFAULT_PROFILE) } 449 | end 450 | end 451 | 452 | control 'docker-2.21' do 453 | impact 1.0 454 | title 'Avoid experimental features in production' 455 | desc 'Avoid experimental features in production. 456 | 457 | Rationale: Experimental is now a runtime docker daemon flag instead of a separate build. Passing --experimental as a runtime flag to the docker daemon, activates experimental features. Experimental is now considered a stable release, but with a couple of features which might not have tested and guaranteed API stability.' 458 | 459 | tag 'docker' 460 | tag 'cis-docker-1.13.0': '2.21' 461 | tag 'level:1' 462 | ref 'Changing the definition of experimental', url: 'https://github.com/moby/moby/issues/26713' 463 | ref 'Make experimental a runtime flag', url: 'https://github.com/moby/moby/pull/27223' 464 | 465 | describe command('docker version --format \'{{ .Server.Experimental }}\'').stdout.chomp do 466 | it { should eq('false') } 467 | end 468 | end 469 | 470 | control 'docker-2.22' do 471 | impact 1.0 472 | title 'Use Docker\'s secret management commands for managing secrets in a Swarm cluster' 473 | desc 'Use Docker\'s in-built secret management command. 474 | 475 | Rationale: Docker has various commands for managing secrets in a Swarm cluster. This is the foundation for future secret support in Docker with potential improvements such as Windows support, different backing stores, etc.' 476 | 477 | tag 'docker' 478 | tag 'cis-docker-1.13.0': '2.22' 479 | tag 'level:2' 480 | ref 'Secret Management', url: 'https://github.com/moby/moby/pull/27794' 481 | 482 | only_if { SWARM_MODE == 'active' } 483 | describe command('docker secret ls -q').stdout.split("\n").length do 484 | it { should be > 0 } 485 | end 486 | end 487 | 488 | control 'docker-2.23' do 489 | impact 1.0 490 | title 'Run swarm manager in auto-lock mode' 491 | desc 'Run Docker swarm manager in auto-lock mode. 492 | 493 | Rationale: When Docker restarts, both the TLS key used to encrypt communication among swarm nodes, and the key used to encrypt and decrypt Raft logs on disk, are loaded into each manager node\'s memory. You should protect the mutual TLS encryption key and the key used to encrypt and decrypt Raft logs at rest. This protection could be enabled by initializing swarm with --autolock flag. With --autolock enabled, when Docker restarts, you must unlock the swarm first, using a key encryption key generated by Docker when the swarm was initialized.' 494 | 495 | tag 'docker' 496 | tag 'cis-docker-1.13.0': '2.23' 497 | tag 'level:1' 498 | ref 'Initialize a swarm with autolocking enabled', url: 'https://github.com/mistyhacks/docker.github.io/blob/af7dfdba8504f9b102fb31a78cd08a06c33a8975/engine/swarm/swarm_manager_locking.md' 499 | 500 | only_if { SWARM_MODE == 'active' } 501 | describe command('docker swarm unlock-key -q').stdout.chomp.length do 502 | it { should be > 0 } 503 | end 504 | end 505 | 506 | control 'docker-2.24' do 507 | impact 1.0 508 | title 'Rotate swarm manager auto-lock key periodically' 509 | desc 'Rotate swarm manager auto-lock key periodically. 510 | 511 | Rationale: Swarm manager auto-lock key is not automatically rotated. You should rotate them periodically as a best practice. 512 | 513 | Audit: Currently, there is no mechanism to find out when the key was last rotated on a swarm manager node. You should check with the system administrator if there is a key rotation record and the keys were rotated at a pre-defined frequency.' 514 | 515 | tag 'docker' 516 | tag 'cis-docker-1.13.0': '2.24' 517 | tag 'level:1' 518 | ref 'Swarm Key rotation', url: 'https://github.com/mistyhacks/docker.github.io/blob/af7dfdba8504f9b102fb31a78cd08a06c33a8975/engine/swarm/swarm_manager_locking.md' 519 | end 520 | -------------------------------------------------------------------------------- /controls/container_runtime.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Copyright:: 2016, Patrick Muench 4 | # Copyright:: 2017, Christoph Hartmann 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # author: Christoph Hartmann 19 | # author: Dominik Richter 20 | # author: Patrick Muench 21 | 22 | title 'Container Runtime' 23 | 24 | # attributes 25 | CONTAINER_CAPADD = input('container_capadd') 26 | APP_ARMOR_PROFILE = input('app_armor_profile') 27 | SELINUX_PROFILE = input('selinux_profile') 28 | 29 | # check if docker exists 30 | only_if('docker not found') do 31 | command('docker').exist? 32 | end 33 | 34 | control 'docker-5.1' do 35 | impact 1.0 36 | title 'Verify AppArmor Profile, if applicable' 37 | desc 'AppArmor is an effective and easy-to-use Linux application security system. It is available on quite a few Linux distributions by default such as Debian and Ubuntu. 38 | 39 | Rationale: AppArmor protects the Linux OS and applications from various threats by enforcing security policy which is also known as AppArmor profile. You can create your own AppArmor profile for containers or use the Docker\'s default AppArmor profile. This would enforce security policies on the containers as defined in the profile.' 40 | 41 | tag 'docker' 42 | tag 'cis-docker-1.12.0': '5.1' 43 | tag 'cis-docker-1.13.0': '5.1' 44 | tag 'level:1' 45 | ref 'Docker Security', url: 'https://docs.docker.com/engine/security/security/' 46 | ref 'Secure Engine', url: 'https://docs.docker.com/engine/security/' 47 | ref 'AppArmor security profiles for Docker', url: 'https://docs.docker.com/engine/security/apparmor/' 48 | 49 | only_if { %w(ubuntu debian).include? os[:name] } 50 | docker.containers.running?.ids.each do |id| 51 | describe docker.object(id) do 52 | its(['AppArmorProfile']) { should include(APP_ARMOR_PROFILE) } 53 | its(['AppArmorProfile']) { should_not eq nil } 54 | end 55 | end 56 | end 57 | 58 | control 'docker-5.2' do 59 | impact 1.0 60 | title 'Verify SELinux security options, if applicable' 61 | desc 'SELinux is an effective and easy-to-use Linux application security system. It is available on quite a few Linux distributions by default such as Red Hat and Fedora. 62 | 63 | Rationale: SELinux provides a Mandatory Access Control (MAC) system that greatly augments the default Discretionary Access Control (DAC) model. You can thus add an extra layer of safety by enabling SELinux on your Linux host, if applicable.' 64 | 65 | tag 'docker' 66 | tag 'cis-docker-1.12.0': '5.2' 67 | tag 'cis-docker-1.13.0': '5.2' 68 | tag 'level:2' 69 | ref 'Docker Security', url: 'https://docs.docker.com/engine/security/security/' 70 | ref 'Secure Engine', url: 'https://docs.docker.com/engine/security/' 71 | ref 'AppArmor security profiles for Docker', url: 'https://docs.docker.com/engine/security/apparmor/' 72 | ref 'Bug: Wrong SELinux label for devmapper device', url: 'https://github.com/docker/docker/issues/22826' 73 | ref 'Bug: selinux break docker user namespace', url: 'https://bugzilla.redhat.com/show_bug.cgi?id=1312665' 74 | ref 'Security-Enhanced Linux', url: 'https://docs-old.fedoraproject.org/en-US/Fedora/13/html/Security-Enhanced_Linux/' 75 | 76 | only_if { %w(centos redhat).include? os[:name] } 77 | describe json('/etc/docker/daemon.json') do 78 | its(['selinux-enabled']) { should eq(true) } 79 | end 80 | 81 | docker.containers.running?.ids.each do |id| 82 | describe docker.object(id) do 83 | its(%w(HostConfig SecurityOpt)) { should_not eq nil } 84 | its(%w(HostConfig SecurityOpt)) { should include(SELINUX_PROFILE) } 85 | end 86 | end 87 | end 88 | 89 | control 'docker-5.3' do 90 | impact 1.0 91 | title 'Restrict Linux Kernel Capabilities within containers' 92 | desc 'By default, Docker starts containers with a restricted set of Linux Kernel Capabilities. It means that any process may be granted the required capabilities instead of root access. Using Linux Kernel Capabilities, the processes do not have to run as root for almost all the specific areas where root privileges are usually needed. 93 | 94 | Rationale: Docker supports the addition and removal of capabilities, allowing use of a non-default profile. This may make Docker more secure through capability removal, or less secure through the addition of capabilities. It is thus recommended to remove all capabilities except those explicitly required for your container process. 95 | 96 | For example, capabilities such as below are usually not needed for container process: NET_ADMIN, SYS_ADMIN, SYS_MODULE' 97 | 98 | tag 'docker' 99 | tag 'cis-docker-1.12.0': '5.3' 100 | tag 'cis-docker-1.13.0': '5.3' 101 | tag 'level:1' 102 | ref 'Docker Security', url: 'https://docs.docker.com/engine/security/security/' 103 | ref 'Secure Engine', url: 'https://docs.docker.com/engine/security/' 104 | ref 'capabilities - overview of Linux capabilities', url: 'http://man7.org/linux/man-pages/man7/capabilities.7.html' 105 | ref 'Docker Security Book', url: 'http://www.oreilly.com/webops-perf/free/files/docker-security.pdf' 106 | 107 | docker.containers.running?.ids.each do |id| 108 | describe docker.object(id) do 109 | its(%w(HostConfig CapDrop)) { should include(/all/) } 110 | its(%w(HostConfig CapDrop)) { should_not eq nil } 111 | its(%w(HostConfig CapAdd)) { should eq CONTAINER_CAPADD } 112 | end 113 | end 114 | end 115 | 116 | control 'docker-5.4' do 117 | impact 1.0 118 | title 'Do not use privileged containers' 119 | desc 'Using the --privileged flag gives all Linux Kernel Capabilities to the container thus overwriting the --cap-add and --cap-drop flags. Ensure that it is not used. 120 | 121 | Rationale: The --privileged flag gives all capabilities to the container, and it also lifts all the limitations enforced by the device cgroup controller. In other words, the container can then do almost everything that the host can do. This flag exists to allow special use-cases, like running Docker within Docker.' 122 | 123 | tag 'docker' 124 | tag 'cis-docker-1.12.0': '5.4' 125 | tag 'cis-docker-1.13.0': '5.4' 126 | tag 'level:1' 127 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/' 128 | 129 | docker.containers.running?.ids.each do |id| 130 | describe docker.object(id) do 131 | its(%w(HostConfig Privileged)) { should eq false } 132 | its(%w(HostConfig Privileged)) { should_not eq true } 133 | end 134 | end 135 | end 136 | 137 | control 'docker-5.5' do 138 | impact 1.0 139 | title 'Do not mount sensitive host system directories on containers' 140 | desc 'Sensitive host system directories such as \'/, /boot, /dev, /etc, /lib, /proc, /sys, /usr\' should not be allowed to be mounted as container volumes especially in read-write mode. 141 | 142 | Rationale: If sensitive directories are mounted in read-write mode, it would be possible to make changes to files within those sensitive directories. The changes might bring down security implications or unwarranted changes that could put the Docker host in compromised state.' 143 | 144 | tag 'docker' 145 | tag 'cis-docker-1.12.0': '5.5' 146 | tag 'cis-docker-1.13.0': '5.5' 147 | tag 'level:1' 148 | ref 'Use volumes', url: 'https://docs.docker.com/engine/admin/volumes/volumes/' 149 | 150 | docker.containers.running?.ids.each do |id| 151 | info = docker.object(id) 152 | info['Mounts'].each do |mounts| 153 | describe mounts['Source'] do 154 | it { should_not eq '/' } 155 | it { should_not match(%r{/boot}) } 156 | it { should_not match(%r{/dev}) } 157 | it { should_not match(%r{/etc}) } 158 | it { should_not match(%r{/lib}) } 159 | it { should_not match(%r{/proc}) } 160 | it { should_not match(%r{/sys}) } 161 | it { should_not match(%r{/usr}) } 162 | end 163 | end 164 | end 165 | end 166 | 167 | control 'docker-5.6' do 168 | impact 1.0 169 | title 'Do not run ssh within containers' 170 | desc 'SSH server should not be running within the container. You should SSH into the Docker host, and use nsenter tool to enter a container from a remote host. 171 | 172 | Rationale: Running SSH within the container increases the complexity of security management by making it 173 | 174 | Difficult to manage access policies and security compliance for SSH server 175 | Difficult to manage keys and passwords across various containers 176 | Difficult to manage security upgrades for SSH server 177 | 178 | It is possible to have shell access to a container without using SSH, the needlessly increasing the complexity of security management should be avoided.' 179 | 180 | tag 'docker' 181 | tag 'cis-docker-1.12.0': '5.6' 182 | tag 'cis-docker-1.13.0': '5.6' 183 | tag 'level:1' 184 | ref 'Why you don\'t need to run SSHd in your Docker containers', url: 'https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/' 185 | 186 | docker.containers.running?.ids.each do |id| 187 | execute_command = "docker exec #{id} ps -e" 188 | describe command(execute_command) do 189 | its('stdout') { should_not match(/ssh/) } 190 | end 191 | end 192 | end 193 | 194 | control 'docker-5.7' do 195 | impact 1.0 196 | title 'Do not map privileged ports within containers' 197 | desc 'The TCP/IP port numbers below 1024 are considered privileged ports. Normal users and processes are not allowed to use them for various security reasons. Docker allows a container port to be mapped to a privileged port. 198 | 199 | Rationale: By default, if the user does not specifically declare the container port to host port mapping, Docker automatically and correctly maps the container port to one available in 49153-65535 block on the host. But, Docker allows a container port to be mapped to a privileged port on the host if the user explicitly declared it. This is so because containers are executed with NET_BIND_SERVICE Linux kernel capability that does not restrict the privileged port mapping. The privileged ports receive and transmit various sensitive and privileged data. Allowing containers to use them can bring serious implications.' 200 | 201 | tag 'docker' 202 | tag 'cis-docker-1.12.0': '5.7' 203 | tag 'cis-docker-1.13.0': '5.7' 204 | tag 'level:1' 205 | ref 'Bind container ports to the host', url: 'https://docs.docker.com/engine/userguide/networking/default_network/binding/' 206 | ref 'Why putting SSH on another port than 22 is bad idea', url: 'https://www.adayinthelifeof.nl/2012/03/12/why-putting-ssh-on-another-port-than-22-is-bad-idea/' 207 | 208 | docker.containers.running?.ids.each do |id| 209 | container_info = docker.object(id) 210 | next if container_info['NetworkSettings']['Ports'].nil? 211 | 212 | container_info['NetworkSettings']['Ports'].each do |_, hosts| 213 | next if hosts.nil? 214 | 215 | hosts.each do |host| 216 | describe host['HostPort'].to_i.between?(1, 1024) do 217 | it { should eq false } 218 | end 219 | end 220 | end 221 | end 222 | end 223 | 224 | control 'docker-5.8' do 225 | impact 1.0 226 | title 'Open only needed ports on container' 227 | desc 'Dockerfile for a container image defines the ports to be opened by default on a container instance. The list of ports may or may not be relevant to the application you are running within the container. 228 | 229 | Rationale: A container can be run just with the ports defined in the Dockerfile for its image or can be arbitrarily passed run time parameters to open a list of ports. Additionally, Overtime, Dockerfile may undergo various changes and the list of exposed ports may or may not be relevant to the application you are running within the container. Opening unneeded ports increase the attack surface of the container and the containerized application. As a recommended practice, do not open unneeded ports.' 230 | 231 | tag 'docker' 232 | tag 'cis-docker-1.12.0': '5.8' 233 | tag 'cis-docker-1.13.0': '5.8' 234 | tag 'level:1' 235 | ref 'Bind container ports to the host', url: 'https://docs.docker.com/engine/userguide/networking/default_network/binding/' 236 | end 237 | 238 | control 'docker-5.9' do 239 | impact 1.0 240 | title 'Do not share the host\'s network namespace' 241 | desc 'The networking mode on a container when set to \'--net=host\', skips placing the container inside separate network stack. In essence, this choice tells Docker to not containerize the container\'s networking. This would network-wise mean that the container lives "outside" in the main Docker host and has full access to its network interfaces. 242 | 243 | Rationale: This is potentially dangerous. It allows the container process to open low-numbered ports like any other root process. It also allows the container to access network services like D-bus on the Docker host. Thus, a container process can potentially do unexpected things such as shutting down the Docker host. You should not use this option.' 244 | 245 | tag 'docker' 246 | tag 'cis-docker-1.12.0': '5.9' 247 | tag 'cis-docker-1.13.0': '5.9' 248 | tag 'level:1' 249 | ref 'Docker container networking', url: 'https://docs.docker.com/engine/userguide/networking/' 250 | ref 'Rebooting within docker container actually reboots the host', url: 'https://github.com/docker/docker/issues/6401' 251 | 252 | docker.containers.running?.ids.each do |id| 253 | describe docker.object(id) do 254 | its(%w(HostConfig NetworkMode)) { should_not eq 'host' } 255 | end 256 | end 257 | end 258 | 259 | control 'docker-5.10' do 260 | impact 1.0 261 | title 'Limit memory usage for container' 262 | desc 'By default, all containers on a Docker host share the resources equally. By using the resource management capabilities of Docker host, such as memory limit, you can control the amount of memory that a container may consume. 263 | 264 | Rationale: By default, container can use all of the memory on the host. You can use memory limit mechanism to prevent a denial of service arising from one container consuming all of the host’s resources such that other containers on the same host cannot perform their intended functions. Having no limit on memory can lead to issues where one container can easily make the whole system unstable and as a result unusable.' 265 | 266 | tag 'docker' 267 | tag 'cis-docker-1.12.0': '5.10' 268 | tag 'cis-docker-1.13.0': '5.10' 269 | tag 'level:1' 270 | ref 'Resource management in Docker', url: 'https://goldmann.pl/blog/2014/09/11/resource-management-in-docker/' 271 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/' 272 | ref 'Runtime metrics', url: 'https://docs.docker.com/engine/admin/runmetrics/' 273 | 274 | docker.containers.running?.ids.each do |id| 275 | describe docker.object(id) do 276 | its(%w(HostConfig Memory)) { should_not eq 0 } 277 | end 278 | end 279 | end 280 | 281 | control 'docker-5.11' do 282 | impact 1.0 283 | title 'Set container CPU priority appropriately' 284 | desc 'By default, all containers on a Docker host share the resources equally. By using the resource management capabilities of Docker host, such as CPU shares, you can control the host CPU resources that a container may consume. 285 | 286 | Rationale: By default, CPU time is divided between containers equally. If it is desired, to control the CPU time amongst the container instances, you can use CPU sharing feature. CPU sharing allows to prioritize one container over the other and forbids the lower priority container to claim CPU resources more often. This ensures that the high priority containers are served better.' 287 | 288 | tag 'docker' 289 | tag 'cis-docker-1.12.0': '5.11' 290 | tag 'cis-docker-1.13.0': '5.11' 291 | tag 'level:1' 292 | ref 'Resource management in Docker', url: 'https://goldmann.pl/blog/2014/09/11/resource-management-in-docker/' 293 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/' 294 | ref 'Runtime metrics', url: 'https://docs.docker.com/engine/admin/runmetrics/' 295 | 296 | docker.containers.running?.ids.each do |id| 297 | describe docker.object(id) do 298 | its(%w(HostConfig CpuShares)) { should_not eq 0 } 299 | its(%w(HostConfig CpuShares)) { should_not eq 1024 } 300 | end 301 | end 302 | end 303 | 304 | control 'docker-5.12' do 305 | impact 1.0 306 | title 'Mount container\'s root filesystem as read only' 307 | desc 'The container\'s root file system should be treated as a \'golden image\' and any writes to the root filesystem should be avoided. You should explicitly define a container volume for writing. 308 | 309 | Rationale: You should not be writing data within containers. The data volume belonging to a container should be explicitly defined and administered. This is useful in many cases where the admin controls where they would want developers to write files and errors. Also, this has other advantages such as below: 310 | 311 | This leads to an immutable infrastructure 312 | Since the container instance cannot be written to, there is no need to audit instance divergence 313 | Reduced security attack vectors since the instance cannot be tampered with or written to 314 | Ability to use a purely volume based backup without backing up anything from theinstance' 315 | 316 | tag 'docker' 317 | tag 'cis-docker-1.12.0': '5.12' 318 | tag 'cis-docker-1.13.0': '5.12' 319 | tag 'level:1' 320 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/' 321 | 322 | docker.containers.running?.ids.each do |id| 323 | describe docker.object(id) do 324 | its(%w(HostConfig ReadonlyRootfs)) { should eq true } 325 | end 326 | end 327 | end 328 | 329 | control 'docker-5.13' do 330 | impact 1.0 331 | title 'Bind incoming container traffic to a specific host interface' 332 | desc 'By default, Docker containers can make connections to the outside world, but the outside world cannot connect to containers. Each outgoing connection will appear to originate from one of the host machine\'s own IP addresses. Only allow container services to be contacted through a specific external interface on the host machine. 333 | 334 | Rationale: If you have multiple network interfaces on your host machine, the container can accept connections on the exposed ports on any network interface. This might not be desired and may not be secured. Many a times a particular interface is exposed externally and services such as intrusion detection, intrusion prevention, firewall, load balancing, etc. are run on those interfaces to screen incoming public traffic. Hence, you should not accept incoming connections on any interface. You should only allow incoming connections from a particular external interface.' 335 | 336 | tag 'docker' 337 | tag 'cis-docker-1.12.0': '5.13' 338 | tag 'cis-docker-1.13.0': '5.13' 339 | tag 'level:1' 340 | ref 'Docker container networking', url: 'https://docs.docker.com/engine/userguide/networking/' 341 | 342 | docker.containers.running?.ids.each do |id| 343 | container_info = docker.object(id) 344 | next if container_info['NetworkSettings']['Ports'].nil? 345 | 346 | container_info['NetworkSettings']['Ports'].each do |_, hosts| 347 | next if hosts.nil? 348 | 349 | hosts.each do |host| 350 | describe host['HostIp'].to_i.between?(1, 1024) do 351 | it { should_not eq '0.0.0.0' } 352 | end 353 | end 354 | end 355 | end 356 | end 357 | 358 | control 'docker-5.14' do 359 | impact 1.0 360 | title 'Set the \'on-failure\' container restart policy to 5' 361 | desc 'Using the \'--restart\' flag in \'docker run\' command you can specify a restart policy for how a container should or should not be restarted on exit. You should choose the \'on-failure\' restart policy and limit the restart attempts to 5. 362 | 363 | Rationale: If you indefinitely keep trying to start the container, it could possibly lead to a denial of service on the host. It could be an easy way to do a distributed denial of service attack especially if you have many containers on the same host. Additionally, ignoring the exit status of the container and \'always\' attempting to restart the container leads to non-investigation of the root cause behind containers getting terminated. If a container gets terminated, you should investigate on the reason behind it instead of just attempting to restart it indefinitely. Thus, it is recommended to use \'on-failure\' restart policy and limit it to maximum of 5 restart attempts.' 364 | 365 | tag 'docker' 366 | tag 'cis-docker-1.12.0': '5.14' 367 | tag 'cis-docker-1.13.0': '5.14' 368 | tag 'level:1' 369 | ref 'Start containers automatically', url: 'https://docs.docker.com/engine/admin/start-containers-automatically/' 370 | 371 | docker.containers.running?.ids.each do |id| 372 | describe.one do 373 | describe docker.object(id) do 374 | its(%w(HostConfig RestartPolicy Name)) { should eq 'no' } 375 | end 376 | describe docker.object(id) do 377 | its(%w(HostConfig RestartPolicy Name)) { should eq 'on-failure' } 378 | its(%w(HostConfig RestartPolicy MaximumRetryCount)) { should eq 5 } 379 | end 380 | end 381 | end 382 | end 383 | 384 | control 'docker-5.15' do 385 | impact 1.0 386 | title 'Do not share the host\'s process namespace' 387 | desc 'Process ID (PID) namespaces isolate the process ID number space, meaning that processes in different PID namespaces can have the same PID. This is process level isolation between containers and the host. 388 | 389 | Rationale: PID namespace provides separation of processes. The PID Namespace removes the view of the system processes, and allows process ids to be reused including PID 1. If the host\'s PID namespace is shared with the container, it would basically allow processes within the container to see all of the processes on the host system. This breaks the benefit of process level isolation between the host and the containers. Someone having access to the container can eventually know all the processes running on the host system and can even kill the host system processes from within the container. This can be catastrophic. Hence, do not share the host\'s process namespace with the containers.' 390 | 391 | tag 'docker' 392 | tag 'cis-docker-1.12.0': '5.15' 393 | tag 'cis-docker-1.13.0': '5.15' 394 | tag 'level:1' 395 | ref 'PID settings (–pid)', url: 'https://docs.docker.com/engine/reference/run/#pid-equivalent' 396 | ref 'pid_namespaces - overview of Linux PID namespaces', url: 'http://man7.org/linux/man-pages/man7/pid_namespaces.7.html' 397 | 398 | docker.containers.running?.ids.each do |id| 399 | describe docker.object(id) do 400 | its(%w(HostConfig PidMode)) { should_not eq 'host' } 401 | end 402 | end 403 | end 404 | 405 | control 'docker-5.16' do 406 | impact 1.0 407 | title 'Do not share the host\'s IPC namespace' 408 | desc 'IPC (POSIX/SysV IPC) namespace provides separation of named shared memory segments, semaphores and message queues. IPC namespace on the host thus should not be shared with the containers and should remain isolated. 409 | 410 | Rationale: IPC namespace provides separation of IPC between the host and containers. If the host\'s IPC namespace is shared with the container, it would basically allow processes within the container to see all of the IPC on the host system. This breaks the benefit of IPC level isolation between the host and the containers. Someone having access to the container can eventually manipulate the host IPC. This can be catastrophic. Hence, do not share the host\'s IPC namespace with the containers.' 411 | 412 | tag 'docker' 413 | tag 'cis-docker-1.12.0': '5.16' 414 | tag 'cis-docker-1.13.0': '5.16' 415 | tag 'level:1' 416 | ref 'IPC settings (–ipc)', url: 'https://docs.docker.com/engine/reference/run/#ipc-settings---ipc' 417 | ref 'namespaces - overview of Linux namespaces', url: 'http://man7.org/linux/man-pages/man7/namespaces.7.html' 418 | 419 | docker.containers.running?.ids.each do |id| 420 | describe docker.object(id) do 421 | its(%w(HostConfig IpcMode)) { should_not eq 'host' } 422 | end 423 | end 424 | end 425 | 426 | control 'docker-5.17' do 427 | impact 1.0 428 | title 'Do not directly expose host devices to containers' 429 | desc 'Host devices can be directly exposed to containers at runtime. Do not directly expose host devices to containers especially for containers that are not trusted. 430 | 431 | Rationale: The \'--device\' option exposes the host devices to the containers and consequently the containers can directly access such host devices. You would not require the container to run in \'privileged\' mode to access and manipulate the host devices. By default, the container will be able to read, write and mknod these devices. Additionally, it is possible for containers to remove block devices from the host. Hence, do not expose host devices to containers directly. If at all, you would want to expose the host device to a container, use the sharing permissions appropriately: 432 | 433 | r - read only 434 | w - writable 435 | m - mknod allowed' 436 | 437 | tag 'docker' 438 | tag 'cis-docker-1.12.0': '5.17' 439 | tag 'cis-docker-1.13.0': '5.17' 440 | tag 'level:1' 441 | ref 'Use the Docker command line', url: 'https://docs.docker.com/engine/reference/commandline/cli/' 442 | 443 | docker.containers.running?.ids.each do |id| 444 | describe docker.object(id) do 445 | its(%w(HostConfig Devices)) { should be_empty } 446 | end 447 | end 448 | end 449 | 450 | control 'docker-5.18' do 451 | impact 1.0 452 | title 'Override default ulimit at runtime only if needed' 453 | desc 'The default ulimit is set at the Docker daemon level. However, you may override the default ulimit setting, if needed, during container runtime. 454 | 455 | Rationale: ulimit provides control over the resources available to the shell and to processes started by it. Setting system resource limits judiciously saves you from many disasters such as a fork bomb. Sometimes, even friendly users and legitimate processes can overuse system resources and in-turn can make the system unusable. The default ulimit set at the Docker daemon level should be honored. If the default ulimit settings are not appropriate for a particular container instance, you may override them as an exception. But, do not make this a practice. If most of the container instances are overriding default ulimit settings, consider changing the default ulimit settings to something that is appropriate for your needs.' 456 | 457 | tag 'docker' 458 | tag 'cis-docker-1.12.0': '5.18' 459 | tag 'cis-docker-1.13.0': '5.18' 460 | tag 'level:1' 461 | ref 'docker run', url: 'https://docs.docker.com/engine/reference/commandline/run/' 462 | ref 'Command: man setrlimit' 463 | ref 'Docker Security Book', url: 'http://www.oreilly.com/webops-perf/free/files/docker-security.pdf' 464 | 465 | docker.containers.running?.ids.each do |id| 466 | describe docker.object(id) do 467 | its(%w(HostConfig Ulimits)) { should eq nil } 468 | end 469 | end 470 | end 471 | 472 | control 'docker-5.19' do 473 | impact 1.0 474 | title 'Do not set mount propagation mode to shared' 475 | desc 'Mount propagation mode allows mounting volumes in shared, slave or private mode on a container. Do not use shared mount propagation mode until needed. 476 | 477 | Rationale: A shared mount is replicated at all mounts and the changes made at any mount point are propagated to all mounts. Mounting a volume in shared mode does not restrict any other container to mount and make changes to that volume. This might be catastrophic if the mounted volume is sensitive to changes. Do not set mount propagation mode to shared until needed.' 478 | 479 | tag 'docker' 480 | tag 'cis-docker-1.12.0': '5.19' 481 | tag 'cis-docker-1.13.0': '5.19' 482 | tag 'level:1' 483 | ref 'Capability to specify per volume mount propagation mode', url: 'https://github.com/docker/docker/pull/17034' 484 | ref 'Docker run reference', url: 'https://docs.docker.com/engine/reference/run/' 485 | ref 'Shared Subtrees', url: 'https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt' 486 | 487 | docker.containers.running?.ids.each do |id| 488 | raw = command("docker inspect --format '{{range $mnt := .Mounts}} {{json $mnt.Propagation}} {{end}}' #{id}").stdout 489 | describe raw.delete("\n").delete('\"').delete(' ') do 490 | it { should_not eq 'shared' } 491 | end 492 | end 493 | end 494 | 495 | control 'docker-5.20' do 496 | impact 1.0 497 | title 'Do not share the host\'s UTS namespace' 498 | desc 'UTS namespaces provide isolation of two system identifiers: the hostname and the NIS domain name. It is used for setting the hostname and the domain that is visible to running processes in that namespace. Processes running within containers do not typically require to know hostname and domain name. Hence, the namespace should not be shared with the host. 499 | 500 | Rationale: Sharing the UTS namespace with the host provides full permission to the container to change the hostname of the host. This is insecure and should not be allowed.' 501 | 502 | tag 'docker' 503 | tag 'cis-docker-1.12.0': '5.20' 504 | tag 'cis-docker-1.13.0': '5.20' 505 | tag 'level:1' 506 | ref 'Docker run reference', url: 'https://docs.docker.com/engine/reference/run/' 507 | ref 'namespaces - overview of Linux namespaces', url: ' http://man7.org/linux/man-pages/man7/namespaces.7.html' 508 | 509 | docker.containers.running?.ids.each do |id| 510 | describe docker.object(id) do 511 | its(%w(HostConfig UTSMode)) { should_not eq 'host' } 512 | end 513 | end 514 | end 515 | 516 | control 'docker-5.21' do 517 | impact 1.0 518 | title 'Do not disable default seccomp profile' 519 | desc 'Seccomp filtering provides a means for a process to specify a filter for incoming system calls. The default Docker seccomp profile disables 44 system calls, out of 313. It should not be disabled unless it hinders your container application usage. 520 | 521 | Rationale: A large number of system calls are exposed to every userland process with many of them going unused for the entire lifetime of the process. Most of the applications do not need all the system calls and thus benefit by having a reduced set of available system calls. The reduced set of system calls reduces the total kernel surface exposed to the application and thus improvises application security.' 522 | 523 | tag 'docker' 524 | tag 'cis-docker-1.12.0': '5.21' 525 | tag 'cis-docker-1.13.0': '5.21' 526 | tag 'level:1' 527 | ref 'New Docker Security Features and What They Mean: Seccomp Profiles', url: 'http://blog.aquasec.com/new-docker-security-features-and-what-they-mean-seccomp-profiles' 528 | ref 'Docker run reference', url: 'https://docs.docker.com/engine/reference/run/' 529 | ref 'Seccomp default.json', url: 'https://github.com/moby/moby/blob/master/profiles/seccomp/default.json' 530 | ref 'Seccomp security profiles for Docker', url: 'https://docs.docker.com/engine/security/seccomp/' 531 | ref 'SECure COMPuting with filters', url: 'https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt' 532 | ref 'Capability to specify per volume mount propagation mode', url: 'https://github.com/moby/moby/pull/17034' 533 | 534 | docker.containers.running?.ids.each do |id| 535 | describe docker.object(id) do 536 | its(%w(HostConfig SecurityOpt)) { should include(/seccomp/) } 537 | its(%w(HostConfig SecurityOpt)) { should_not include(/seccomp[=|:]unconfined/) } 538 | end 539 | end 540 | end 541 | 542 | control 'docker-5.22' do 543 | impact 1.0 544 | title 'Do not docker exec commands with privileged option' 545 | desc 'Do not docker exec with --privileged option. 546 | 547 | Rationale: Using --privileged option in docker exec gives extended Linux capabilities to the command. This could potentially be insecure and unsafe to do especially when you are running containers with dropped capabilities or with enhanced restrictions.' 548 | 549 | tag 'docker' 550 | tag 'cis-docker-1.12.0': '5.22' 551 | tag 'cis-docker-1.13.0': '5.22' 552 | tag 'level:2' 553 | ref 'docker exec', url: 'https://docs.docker.com/engine/reference/commandline/exec/' 554 | 555 | describe command('ausearch --input-logs -k docker | grep exec | grep privileged').stdout do 556 | it { should be_empty } 557 | end 558 | end 559 | 560 | control 'docker-5.23' do 561 | impact 1.0 562 | title 'Do not docker exec commands with user option' 563 | desc 'Do not docker exec with --user option. 564 | 565 | Rationale: Using --user option in docker exec executes the command within the container as that user. This could potentially be insecure and unsafe to do especially when you are running containers with dropped capabilities or with enhanced restrictions. For example, suppose your container is running as tomcat user (or any other non-root user), it would be possible to run a command through docker exec as root with --user=root option. This could potentially be dangerous.' 566 | 567 | tag 'docker' 568 | tag 'cis-docker-1.12.0': '5.23' 569 | tag 'cis-docker-1.13.0': '5.23' 570 | tag 'level:2' 571 | ref 'docker exec', url: 'https://docs.docker.com/engine/reference/commandline/exec/' 572 | 573 | describe command('ausearch --input-logs -k docker | grep exec | grep user').stdout do 574 | it { should be_empty } 575 | end 576 | end 577 | 578 | control 'docker-5.24' do 579 | impact 1.0 580 | title 'Confirm cgroup usage' 581 | desc 'It is possible to attach to a particular cgroup on container run. Confirming cgroup usage would ensure that containers are running under defined cgroups. 582 | 583 | Rationale: System administrators typically define cgroups under which containers are supposed to run. Even if cgroups are not explicitly defined by the system administrators, containers run under docker cgroup by default. At run-time, it is possible to attach to a different cgroup other than the one that was expected to be used. This usage should be monitored and confirmed. By attaching to a different cgroup than the one that is expected, excess permissions and resources might be granted to the container and thus, can prove to be unsafe.' 584 | 585 | tag 'docker' 586 | tag 'cis-docker-1.12.0': '5.24' 587 | tag 'cis-docker-1.13.0': '5.24' 588 | tag 'level:1' 589 | ref 'Specify custom cgroups', url: 'https://docs.docker.com/engine/reference/run/' 590 | ref 'Chapter 1. Introduction to Control Groups (Cgroups)', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Resource_Management_Guide/ch01.html' 591 | 592 | docker.containers.running?.ids.each do |id| 593 | describe docker.object(id) do 594 | its(%w(HostConfig CgroupParent)) { should be_empty } 595 | end 596 | end 597 | end 598 | 599 | control 'docker-5.25' do 600 | impact 1.0 601 | title 'Restrict container from acquiring additional privileges' 602 | desc 'Restrict the container from acquiring additional privileges via suid or sgid bits. 603 | 604 | Rationale: A process can set the no_new_priv bit in the kernel. It persists across fork, clone and execve. The no_new_priv bit ensures that the process or its children processes do not gain any additional privileges via suid or sgid bits. This way a lot of dangerous operations become a lot less dangerous because there is no possibility of subverting privileged binaries.' 605 | 606 | tag 'docker' 607 | tag 'cis-docker-1.12.0': '5.25' 608 | tag 'cis-docker-1.13.0': '5.25' 609 | tag 'level:1' 610 | ref 'BLOG: No New Privileges support in docker', url: 'https://github.com/projectatomic/atomic-site/issues/269' 611 | ref 'Add support for NoNewPrivileges in docker', url: 'https://github.com/moby/moby/pull/20727' 612 | ref 'no_new_privs', url: 'https://www.kernel.org/doc/Documentation/prctl/no_new_privs.txt' 613 | ref 'System call filtering and no_new_privs', url: 'https://lwn.net/Articles/475678/' 614 | ref 'Add PR_{GET,SET}_NO_NEW_PRIVS to prevent execve from granting privs', url: 'https://lwn.net/Articles/475362/' 615 | 616 | docker.containers.running?.ids.each do |id| 617 | describe docker.object(id) do 618 | its(%w(HostConfig SecurityOpt)) { should include(/no-new-privileges/) } 619 | end 620 | end 621 | end 622 | 623 | control 'docker-5.26' do 624 | impact 1.0 625 | title 'Check container health at runtime' 626 | desc 'If the container image does not have an HEALTHCHECK instruction defined, use --health-cmd parameter at container runtime for checking container health. 627 | 628 | Rationale: One of the important security triads is availability. If the container image you are using does not have a pre-defined HEALTHCHECK instruction, use the --health-cmd parameter to check container health at runtime. Based on the reported health status, you could take necessary actions.' 629 | 630 | tag 'docker' 631 | tag 'cis-docker-1.12.0': '5.26' 632 | tag 'cis-docker-1.13.0': '5.26' 633 | tag 'level:1' 634 | ref 'Add support for user-defined healthchecks', url: 'https://github.com/moby/moby/pull/22719' 635 | 636 | docker.containers.running?.ids.each do |id| 637 | describe docker.object(id) do 638 | its('State.Health.Status') { should eq 'healthy' } 639 | end 640 | end 641 | end 642 | 643 | control 'docker-5.27' do 644 | impact 1.0 645 | title 'Ensure docker commands always get the latest version of the image' 646 | desc 'Always ensure that you are using the latest version of the image within your repository and not the cached older versions. 647 | 648 | Rationale: Multiple docker commands such as docker pull, docker run, etc. are known to have an issue that by default, they extract the local copy of the image, if present, even though there is an updated version of the image with the "same tag" in the upstream repository. This could lead to using older and vulnerable images.' 649 | 650 | tag 'docker' 651 | tag 'cis-docker-1.12.0': '5.27' 652 | tag 'cis-docker-1.13.0': '5.27' 653 | tag 'level:1' 654 | ref 'Modifying trusted/untrusted pull behavior for create/run/build', url: 'https://github.com/moby/moby/pull/16609' 655 | 656 | describe 'docker-test' do 657 | skip 'Ensure docker commands always get the latest version of the image' 658 | end 659 | end 660 | 661 | control 'docker-5.28' do 662 | impact 1.0 663 | title 'Use PIDs cgroup limit' 664 | desc 'Use --pids-limit flag at container runtime. 665 | 666 | Rationale: Attackers could launch a fork bomb with a single command inside the container. This fork bomb can crash the entire system and requires a restart of the host to make the system functional again. PIDs cgroup --pids-limit will prevent this kind of attacks by restricting the number of forks that can happen inside a container at a given time.' 667 | 668 | tag 'docker' 669 | tag 'cis-docker-1.12.0': '5.28' 670 | tag 'cis-docker-1.13.0': '5.28' 671 | tag 'level:1' 672 | ref 'Add PIDs cgroup support to Docker', url: 'https://github.com/moby/moby/pull/18697' 673 | ref 'docker run', url: 'https://docs.docker.com/engine/reference/commandline/run/' 674 | 675 | docker.containers.running?.ids.each do |id| 676 | describe docker.object(id) do 677 | its('HostConfig.PidsLimit') { should_not cmp 0 } 678 | its('HostConfig.PidsLimit') { should_not cmp(-1) } 679 | end 680 | end 681 | end 682 | 683 | control 'docker-5.29' do 684 | impact 1.0 685 | title 'Do not use Docker\'s default bridge docker0' 686 | desc 'Do not use Docker\'s default bridge docker0. Use docker\'s user-defined networks for container networking. 687 | 688 | Rationale: Docker connects virtual interfaces created in the bridge mode to a common bridge called docker0. This default networking model is vulnerable to ARP spoofing and MAC flooding attacks since there is no filtering applied.' 689 | 690 | tag 'do cker' 691 | tag 'cis-docker-1.12.0': '5.29' 692 | tag 'cis-docker-1.13.0': '5.29' 693 | tag 'level:2' 694 | ref 'narwhal – secure Docker networking', url: 'https://github.com/nyantec/narwhal' 695 | ref 'Analysis of Docker Security', url: 'https://arxiv.org/pdf/1501.02967.pdf' 696 | ref 'Docker container networking', url: 'https://docs.docker.com/engine/userguide/networking/' 697 | 698 | describe 'docker-test' do 699 | skip 'Not implemented yet' 700 | end 701 | end 702 | 703 | control 'docker-5.30' do 704 | impact 1.0 705 | title 'Do not share the host\'s user namespaces' 706 | desc 'Do not share the host\'s user namespaces with the containers. 707 | 708 | Rationale: User namespaces ensure that a root process inside the container will be mapped to a non-root process outside the container. Sharing the user namespaces of the host with the container thus does not isolate users on the host with users on the containers.' 709 | 710 | tag 'docker' 711 | tag 'cis-docker-1.12.0': '5.30' 712 | tag 'cis-docker-1.13.0': '5.30' 713 | tag 'level:1' 714 | ref 'docker run', url: 'https://docs.docker.com/engine/reference/commandline/run/' 715 | ref 'Rooting out Root: User namespaces in Docker', url: 'https://events.linuxfoundation.org/sites/events/files/slides/User%20Namespaces%20-%20ContainerCon%202015%20-%2016-9-final_0.pdf' 716 | ref 'Phase 1 implementation of user namespaces as a remapped container root', url: 'https://github.com/moby/moby/pull/12648' 717 | 718 | docker.containers.running?.ids.each do |id| 719 | describe docker.object(id) do 720 | its('HostConfig.UsernsMode') { should eq '' } 721 | end 722 | end 723 | end 724 | 725 | control 'docker-5.31' do 726 | impact 1.0 727 | title 'Do not mount the Docker socket inside any containers' 728 | desc 'The docker socket (docker.sock) should not be mounted inside a container. 729 | 730 | Rationale: If the docker socket is mounted inside a container it would allow processes running within the container to execute docker commands which effectively allows for full control of the host.' 731 | 732 | tag 'docker' 733 | tag 'cis-docker-1.12.0': '5.31' 734 | tag 'cis-docker-1.13.0': '5.31' 735 | tag 'level:1' 736 | ref 'The Dangers of Docker.sock', url: 'https://raesene.github.io/blog/2016/03/06/The-Dangers-Of-Docker.sock/' 737 | ref 'Docker-in-docker vs mounting /var/run/docker.sock', url: 'https://forums.docker.com/t/docker-in-docker-vs-mounting-var-run-docker-sock/9450/2' 738 | ref 'Is `-v /var/run/docker.sock:/var/run/docker.sock` a ticking time bomb', url: 'https://github.com/moby/moby/issues/21109' 739 | 740 | docker.containers.running?.ids.each do |id| 741 | docker.object(id).Mounts.each do |mount| 742 | describe mount do 743 | its('Source') { should_not include 'docker.sock' } 744 | end 745 | end 746 | end 747 | end 748 | --------------------------------------------------------------------------------