From 68aff6cb33c9a7ea1319d2139efbdf0e229fdb70 Mon Sep 17 00:00:00 2001 From: Christoph Hartmann Date: Sat, 22 Apr 2017 20:15:27 +0200 Subject: [PATCH] use new inspec docker resource Signed-off-by: Christoph Hartmann --- README.md | 3 +- controls/container_images.rb | 6 +- controls/container_runtime.rb | 113 +++++++++--------- controls/docker_daemon_configuration_files.rb | 8 +- controls/host_configuration.rb | 35 +++--- libraries/{docker.rb => docker_helper.rb} | 17 +-- 6 files changed, 85 insertions(+), 97 deletions(-) rename libraries/{docker.rb => docker_helper.rb} (82%) diff --git a/README.md b/README.md index 80ced94..da60df3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ InSpec is an open-source run-time framework and rule language used to specify co ## Requirements -* [InSpec](http://inspec.io/) +* at least [InSpec](http://inspec.io/) version 1.21.0 ### Platform @@ -107,6 +107,7 @@ inspec supermarket exec dev-sec/cis-docker-benchmark -t ssh://user@hostname --ke ## License and Author * Author:: Patrick Muench +* Author:: Christoph Hartmann Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/controls/container_images.rb b/controls/container_images.rb index 283b053..f556b44 100644 --- a/controls/container_images.rb +++ b/controls/container_images.rb @@ -46,10 +46,10 @@ ref url: 'https://github.com/docker/docker/issues/7906' ref url: 'https://www.altiscale.com/blog/making-docker-work-yarn/' - docker.ps.each do |id| - describe docker.inspect(id) do - its(%w(Config User)) { should eq CONTAINER_USER } + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(Config User)) { should_not eq nil } + its(%w(Config User)) { should eq CONTAINER_USER } end end end diff --git a/controls/container_runtime.rb b/controls/container_runtime.rb index 8cf59a5..6811054 100644 --- a/controls/container_runtime.rb +++ b/controls/container_runtime.rb @@ -57,8 +57,8 @@ ref 'http://wiki.apparmor.net/index.php/Main_Page' only_if { %w(ubuntu debian).include? os[:name] } - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(['AppArmorProfile']) { should include(APP_ARMOR_PROFILE) } its(['AppArmorProfile']) { should_not eq nil } end @@ -84,8 +84,8 @@ its(['selinux-enabled']) { should eq(true) } end - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig SecurityOpt)) { should_not eq nil } its(%w(HostConfig SecurityOpt)) { should include(SELINUX_PROFILE) } end @@ -104,8 +104,8 @@ ref url: 'http://man7.org/linux/man-pages/man7/capabilities.7.html' ref url: 'https://github.com/docker/docker/blob/master/oci/defaults_linux.go#L64-L79' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig CapDrop)) { should include(/all/) } its(%w(HostConfig CapDrop)) { should_not eq nil } its(%w(HostConfig CapAdd)) { should eq CONTAINER_CAPADD } @@ -123,8 +123,8 @@ tag level: 1 ref url: 'https://docs.docker.com/engine/reference/commandline/cli/' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig Privileged)) { should eq false } its(%w(HostConfig Privileged)) { should_not eq true } end @@ -141,8 +141,8 @@ tag level: 1 ref url: 'https://docs.docker.com/engine/userguide/containers/dockervolumes/' - docker.ps.each do |id| - info = docker.inspect(id) + docker.containers.running?.ids.each do |id| + info = docker.object(id) info['Mounts'].each do |mounts| describe mounts['Source'] do it { should_not eq '/' } @@ -168,7 +168,7 @@ tag level: 1 ref url: 'https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/' - docker.ps.each do |id| + docker.containers.running?.ids.each do |id| execute_command = 'docker exec ' + id + ' ps -e' describe command(execute_command) do its('stdout') { should_not match(/ssh/) } @@ -187,12 +187,12 @@ ref url: 'https://docs.docker.com/engine/userguide/networking/default_network/binding/' ref url: 'https://www.adayinthelifeof.nl/2012/03/12/why-putting-ssh-on-another-port-than-22-is-bad-idea/' - docker.ps.each do |id| - info = docker.inspect(id) - ports = info['NetworkSettings']['Ports'].keys - ports.each do |item| - info['NetworkSettings']['Ports'][item].each do |hostport| - describe hostport['HostPort'].to_i.between?(1, 1024) do + docker.containers.running?.ids.each do |id| + container_info = docker.object(id) + next unless container_info['NetworkSettings']['Ports'].nil? + container_info['NetworkSettings']['Ports'].each do |_, hosts| + hosts.each do |host| + describe host['HostPort'].to_i.between?(1, 1024) do it { should eq false } end end @@ -222,8 +222,8 @@ ref url: 'https://docs.docker.com/engine/userguide/networking/dockernetworks/' ref url: 'https://github.com/docker/docker/issues/6401' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig NetworkMode)) { should_not eq 'host' } end end @@ -241,8 +241,8 @@ ref url: 'https://docs.docker.com/engine/reference/commandline/cli/#run' ref url: 'https://docs.docker.com/v1.8/articles/runmetrics/' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig Memory)) { should_not eq 0 } end end @@ -260,8 +260,8 @@ ref url: 'https://docs.docker.com/engine/reference/commandline/cli/#run' ref url: 'https://docs.docker.com/v1.8/articles/runmetrics/' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig CpuShares)) { should_not eq 0 } its(%w(HostConfig CpuShares)) { should_not eq 1024 } end @@ -278,8 +278,8 @@ tag level: 1 ref url: 'https://docs.docker.com/engine/reference/commandline/cli/#run' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig ReadonlyRootfs)) { should eq true } end end @@ -295,12 +295,12 @@ tag level: 1 ref url: 'https://docs.docker.com/engine/userguide/networking/default_network/binding/' - docker.ps.each do |id| - info = docker.inspect(id) - ports = info['NetworkSettings']['Ports'].keys - ports.each do |item| - info['NetworkSettings']['Ports'][item].each do |hostip| - describe hostip['HostIp'] do + docker.containers.running?.ids.each do |id| + container_info = docker.object(id) + next unless container_info['NetworkSettings']['Ports'].nil? + container_info['NetworkSettings']['Ports'].each do |_, hosts| + hosts.each do |host| + describe host['HostIp'].to_i.between?(1, 1024) do it { should_not eq '0.0.0.0' } end end @@ -318,14 +318,15 @@ tag level: 1 ref url: 'https://docs.docker.com/engine/reference/commandline/cli/#restart-policies' - docker.ps.each do |id| - info = docker.inspect(id) - only_if { info['HostConfig']['RestartPolicy']['Name'] != 'no' } - describe info do - its(%w(HostConfig RestartPolicy Name)) { should eq 'on-failure' } - end - describe info do - its(%w(HostConfig RestartPolicy MaximumRetryCount)) { should eq 5 } + docker.containers.running?.ids.each do |id| + describe.one do + describe docker.object(id) do + its(%w(HostConfig RestartPolicy Name)) { should eq 'no' } + end + describe docker.object(id) do + its(%w(HostConfig RestartPolicy Name)) { should eq 'on-failure' } + its(%w(HostConfig RestartPolicy MaximumRetryCount)) { should eq 5 } + end end end end @@ -341,8 +342,8 @@ ref url: 'https://docs.docker.com/engine/reference/run/#pid-settings' ref url: 'http://man7.org/linux/man-pages/man7/pid_namespaces.7.html' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig PidMode)) { should_not eq 'host' } end end @@ -359,8 +360,8 @@ ref url: 'https://docs.docker.com/engine/reference/run/#ipc-settings' ref url: 'http://man7.org/linux/man-pages/man7/pid_namespaces.7.html' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig IpcMode)) { should_not eq 'host' } end end @@ -376,8 +377,8 @@ tag level: 1 ref url: 'https://docs.docker.com/engine/reference/commandline/cli/#run' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig Devices)) { should be_empty } end end @@ -393,8 +394,8 @@ tag level: 1 ref url: 'https://docs.docker.com/engine/reference/commandline/cli/#setting-ulimits-in-a-container' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig Ulimits)) { should eq nil } end end @@ -412,7 +413,7 @@ ref url: 'https://docs.docker.com/engine/reference/run/' ref url: 'https://www.kernel.org/doc/Documentation/filesystems/sharedsubtree.txt' - docker.ps.each do |id| + docker.containers.running?.ids.each do |id| raw = command("docker inspect --format '{{range $mnt := .Mounts}} {{json $mnt.Propagation}} {{end}}' #{id}").stdout describe raw.delete("\n").delete('\"').delete(' ') do it { should_not eq 'shared' } @@ -431,8 +432,8 @@ ref url: 'https://docs.docker.com/engine/reference/run/' ref url: 'http://man7.org/linux/man-pages/man7/pid_namespaces.7.html' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig UTSMode)) { should_not eq 'host' } end end @@ -453,8 +454,8 @@ ref url: 'https://www.kernel.org/doc/Documentation/prctl/seccomp_filter.txt' ref url: 'https://github.com/docker/docker/pull/17034' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig SecurityOpt)) { should include(/seccomp/) } its(%w(HostConfig SecurityOpt)) { should_not include(/seccomp[=|:]unconfined/) } end @@ -502,8 +503,8 @@ ref url: 'https://docs.docker.com/engine/reference/run/#specifying-custom-cgroups' ref url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Resource_Management_Guide/ch01.html' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig CgroupParent)) { should be_empty } end end @@ -523,8 +524,8 @@ ref url: 'https://lwn.net/Articles/475678/' ref url: 'https://lwn.net/Articles/475362/' - docker.ps.each do |id| - describe docker.inspect(id) do + docker.containers.running?.ids.each do |id| + describe docker.object(id) do its(%w(HostConfig SecurityOpt)) { should include(/no-new-privileges/) } end end diff --git a/controls/docker_daemon_configuration_files.rb b/controls/docker_daemon_configuration_files.rb index c5555d6..2f82f5b 100644 --- a/controls/docker_daemon_configuration_files.rb +++ b/controls/docker_daemon_configuration_files.rb @@ -55,7 +55,7 @@ tag level: 1 ref url: 'https://docs.docker.com/engine/admin/systemd/' - describe file(docker.path) do + describe file(docker_helper.path) do it { should exist } it { should be_file } it { should be_owned_by 'root' } @@ -73,7 +73,7 @@ tag level: 1 ref url: 'https://docs.docker.com/engine/admin/systemd/' - describe file(docker.path) do + describe file(docker_helper.path) do it { should exist } it { should be_file } it { should be_readable.by('owner') } @@ -98,7 +98,7 @@ ref url: 'https://github.com/YungSang/fedora-atomic-packer/blob/master/oem/docker.socket' ref url: 'https://daviddaeschler.com/2014/12/14/centos-7rhel-7-and-docker-containers-on-boot/' - describe file(docker.socket) do + describe file(docker_helper.socket) do it { should exist } it { should be_file } it { should be_owned_by 'root' } @@ -118,7 +118,7 @@ ref url: 'https://github.com/YungSang/fedora-atomic-packer/blob/master/oem/docker.socket' ref url: 'https://daviddaeschler.com/2014/12/14/centos-7rhel-7-and-docker-containers-on-boot/' - describe file(docker.socket) do + describe file(docker_helper.socket) do it { should exist } it { should be_file } it { should be_readable.by('owner') } diff --git a/controls/host_configuration.rb b/controls/host_configuration.rb index 6fbdb23..f1e290f 100644 --- a/controls/host_configuration.rb +++ b/controls/host_configuration.rb @@ -64,9 +64,9 @@ ref 'Check kernel dependencies', url: 'https://docs.docker.com/engine/installation/binaries/#check-kernel-dependencies' ref 'Installation list', url: 'https://docs.docker.com/engine/installation/#installation-list' + only_if { os.linux? } kernel_version = command('uname -r | grep -o \'^\w\.\w*\.\w*\'').stdout kernel_compare = Gem::Version.new('3.10') <= Gem::Version.new(kernel_version) - describe kernel_compare do it { should eq true } end @@ -111,18 +111,9 @@ ref 'Docker installation', url: 'https://docs.docker.com/installation/' ref 'Docker releases', url: 'https://github.com/docker/docker/releases/latest' - docker_server_version = command('docker version --format \'{{.Server.Version}}\'').stdout - docker_server_compare = Gem::Version.new('17.03') <= Gem::Version.new(docker_server_version) - - docker_client_version = command('docker version --format \'{{.Client.Version}}\'').stdout - docker_client_compare = Gem::Version.new('17.03') <= Gem::Version.new(docker_client_version) - - describe docker_server_compare do - it { should eq true } - end - - describe docker_client_compare do - it { should eq true } + describe docker do + its('version.Client.Version') { should cmp >= '17.03' } + its('version.Server.Version') { should cmp >= '17.03' } end end @@ -156,6 +147,7 @@ tag level: 1 ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' + only_if { os.linux? } describe auditd_rules do its(:lines) { should include('-w /usr/bin/docker -p rwxa -k docker') } end @@ -176,6 +168,7 @@ tag level: 1 ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' + only_if { os.linux? } describe auditd_rules do its(:lines) { should include('-w /var/lib/docker/ -p rwxa -k docker') } end @@ -191,6 +184,7 @@ tag level: 1 ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' + only_if { os.linux? } describe auditd_rules do its(:lines) { should include('-w /etc/docker/ -p rwxa -k docker') } end @@ -206,8 +200,9 @@ tag level: 1 ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' - if docker.path - rule = '-w ' + docker.path + ' -p rwxa -k docker' + only_if { os.linux? } + if docker_helper.path + rule = '-w ' + docker_helper.path + ' -p rwxa -k docker' describe auditd_rules do its(:lines) { should include(rule) } end @@ -228,8 +223,9 @@ tag level: 1 ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' - if docker.socket - rule = '-w ' + docker.socket + ' -p rwxa -k docker' + only_if { os.linux? } + if docker_helper.socket + rule = '-w ' + docker_helper.socket + ' -p rwxa -k docker' describe auditd_rules do its(:lines) { should include(rule) } end @@ -250,7 +246,7 @@ tag level: 1 ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' - only_if { os[:family] != 'centos' } + only_if { os.linux? } describe auditd_rules do its(:lines) { should include('-w /etc/default/docker -p rwxa -k docker') } end @@ -267,6 +263,7 @@ ref 'System auditing', url: 'https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Security_Guide/chap-system_auditing.html' ref 'Daemon configuration', url: 'https://docs.docker.com/engine/reference/commandline/daemon/#daemon-configuration-file' + only_if { os.linux? } describe auditd_rules do its(:lines) { should include('-w /etc/docker/daemon.json -p rwxa -k docker') } end @@ -284,6 +281,7 @@ ref 'Containerd integration', url: 'https://github.com/docker/docker/pull/20662' ref 'Containerd tools', url: 'https://containerd.tools/' + only_if { os.linux? } describe auditd_rules do its(:lines) { should include('-w /usr/bin/docker-containerd -p rwxa -k docker') } end @@ -302,6 +300,7 @@ ref 'Containerd tools', url: 'https://containerd.tools/' ref 'Opencontainers runc repository', url: 'https://github.com/opencontainers/runc' + only_if { os.linux? } describe auditd_rules do its(:lines) { should include('-w /usr/bin/docker-runc -p rwxa -k docker') } end diff --git a/libraries/docker.rb b/libraries/docker_helper.rb similarity index 82% rename from libraries/docker.rb rename to libraries/docker_helper.rb index efd7fdd..18534f3 100644 --- a/libraries/docker.rb +++ b/libraries/docker_helper.rb @@ -18,26 +18,13 @@ # author: Dominik Richter # author: Patrick Muench -require 'yaml' - -class Docker < Inspec.resource(1) - name 'docker' +class DockerHelper < Inspec.resource(1) + name 'docker_helper' desc " A resource to retrieve information about docker " - # return a list on container ids - def ps - inspec.command('docker ps --format "{{.ID}}"').stdout.split - end - - def inspect(id) - raw = inspec.command("docker inspect #{id}").stdout - info = inspec.json('').parse(raw) - info[0] - end - def path cmd = inspec.command('systemctl show -p FragmentPath docker.service') return nil if cmd.exit_status.to_i.nonzero?