diff --git a/Gemfile b/Gemfile index da8596284..9eadff9c2 100644 --- a/Gemfile +++ b/Gemfile @@ -13,8 +13,10 @@ group :test do end group :integration do - gem 'test-kitchen', '~> 1.4' + gem 'berkshelf', '~> 4.0' + gem 'test-kitchen', git: 'https://github.com/chris-rock/test-kitchen', branch: 'test-path' gem 'kitchen-vagrant' + gem 'kitchen-inspec', git: 'git@github.com:chef/kitchen-inspec.git' gem 'concurrent-ruby', '~> 0.9' end diff --git a/Rakefile b/Rakefile index 39a5aa44b..55f588612 100644 --- a/Rakefile +++ b/Rakefile @@ -37,4 +37,10 @@ namespace :test do return if tests.empty? sh(Gem.ruby, 'test/docker_test.rb', *tests) end + + task :vm do + concurrency = ENV['CONCURRENCY'] || 4 + path = File.join(File.dirname(__FILE__), 'test', 'integration') + sh('sh', '-c', "cd #{path} && bundle exec kitchen test -c #{concurrency} -t .") + end end diff --git a/bin/vulcano b/bin/vulcano index 3aeba4d3f..b000a0841 100755 --- a/bin/vulcano +++ b/bin/vulcano @@ -57,7 +57,7 @@ class VulcanoCLI < Thor desc: 'The login user for a remote scan.' option :password, type: :string, default: nil, desc: 'Login password for a remote scan, if required.' - option :key, type: :string, default: nil, + option :key_files, type: :array, default: nil, desc: 'Login key or certificate file for a remote scan.' option :path, type: :string, default: nil, desc: 'Login path to use when connecting to the target.' diff --git a/lib/resources/kernel_module.rb b/lib/resources/kernel_module.rb index 2b7ed5eaa..826829c35 100644 --- a/lib/resources/kernel_module.rb +++ b/lib/resources/kernel_module.rb @@ -15,12 +15,17 @@ class KernelModule < Vulcano.resource(1) @module = modulename # this resource is only supported on Linux - return skip_resource 'The `kernel_module` resource is not supported on your OS.' if !%w{ubuntu debian redhat fedora arch}.include? vulcano.os[:family] + return skip_resource 'The `kernel_parameter` resource is not supported on your OS.' if !vulcano.os.linux? end def loaded? + # default lsmod command + lsmod_cmd = 'lsmod' + # special care for CentOS 5 and sudo + lsmod_cmd = '/sbin/lsmod' if vulcano.os[:family] == 'centos' && vulcano.os[:release].to_i == 5 + # get list of all modules - cmd = vulcano.command('lsmod') + cmd = vulcano.command(lsmod_cmd) return false if cmd.exit_status != 0 # check if module is loaded diff --git a/lib/resources/kernel_parameter.rb b/lib/resources/kernel_parameter.rb index f472cf659..5856af00b 100644 --- a/lib/resources/kernel_parameter.rb +++ b/lib/resources/kernel_parameter.rb @@ -13,7 +13,7 @@ class KernelParameter < Vulcano.resource(1) @parameter = parameter # this resource is only supported on Linux - return skip_resource 'The `kernel_module` resource is not supported on your OS.' if !%w{ubuntu debian redhat fedora arch}.include? vulcano.os[:family] + return skip_resource 'The `kernel_parameter` resource is not supported on your OS.' if !vulcano.os.linux? end def value diff --git a/lib/resources/package.rb b/lib/resources/package.rb index 21907ba60..4a17c589d 100644 --- a/lib/resources/package.rb +++ b/lib/resources/package.rb @@ -21,7 +21,7 @@ class Package < Vulcano.resource(1) case vulcano.os[:family] when 'ubuntu', 'debian' @pkgman = Deb.new(vulcano) - when 'redhat', 'fedora', 'centos' + when 'redhat', 'fedora', 'centos', 'opensuse' @pkgman = Rpm.new(vulcano) when 'arch' @pkgman = Pacman.new(vulcano) @@ -36,7 +36,8 @@ class Package < Vulcano.resource(1) # returns true if the package is installed def installed?(_provider = nil, _version = nil) - !info.nil? + return false if info.nil? + info[:installed] == true end # returns the package description @@ -88,7 +89,9 @@ end class Rpm < PkgManagement def info(package_name) cmd = @vulcano.command("rpm -qia #{package_name}") - return nil if cmd.exit_status.to_i != 0 + # CentOS does not return an error code if the package is not installed, + # therefore we need to check for emptyness + return nil if cmd.exit_status.to_i != 0 || cmd.stdout.chomp.empty? params = SimpleConfig.new( cmd.stdout.chomp, assignment_re: /^\s*([^:]*?)\s*:\s*(.*?)\s*$/, diff --git a/lib/resources/service.rb b/lib/resources/service.rb index c8c30b60d..66aa62957 100644 --- a/lib/resources/service.rb +++ b/lib/resources/service.rb @@ -68,7 +68,7 @@ class Service < Vulcano.resource(1) @service_mgmt = WindowsSrv.new(vulcano) when 'freebsd' @service_mgmt = BSDInit.new(vulcano) - when 'arch' + when 'arch', 'opensuse' @service_mgmt = Systemd.new(vulcano) end @@ -159,10 +159,15 @@ class Upstart < ServiceManager # check if a service is enabled # http://upstart.ubuntu.com/cookbook/#determine-if-a-job-is-disabled # $ initctl show-config $job | grep -q "^ start on" && echo enabled || echo disabled + # Ubuntu 10.04 show-config is not supported + # @see http://manpages.ubuntu.com/manpages/maverick/man8/initctl.8.html config = @vulcano.command("initctl show-config #{service_name}") match_enabled = /^\s*start on/.match(config.stdout) !match_enabled.nil? ? (enabled = true) : (enabled = false) + # implement fallback for Ubuntu 10.04 + enabled = true if @vulcano.os[:family] == 'ubuntu' && @vulcano.os[:release].to_f >= 10.04 && @vulcano.os[:release].to_f < 12.04 && cmd.exit_status == 0 + { name: service_name, description: nil, @@ -199,9 +204,14 @@ class SysV < ServiceManager # check if service is really running # service throws an exit code if the service is not installed or # not enabled - cmd = @vulcano.command("service #{service_name} status") - cmd.exit_status == 0 ? (running = true) : (running = false) + # on debian service is located /usr/sbin/service, on centos it is located here /sbin/service + service_cmd = 'service' + service_cmd = '/usr/sbin/service' if @vulcano.os[:family] == 'debian' + service_cmd = '/sbin/service' if @vulcano.os[:family] == 'centos' + + cmd = @vulcano.command("#{service_cmd} #{service_name} status") + cmd.exit_status == 0 ? (running = true) : (running = false) { name: service_name, description: nil, @@ -227,7 +237,7 @@ class BSDInit < ServiceManager return nil if cmd.exit_status != 0 # search for the service - srv = /(^.*#{service_name}.*)/.match(cmd.stdout) + srv = /(^.*#{service_name}$)/.match(cmd.stdout) return nil if srv.nil? || srv[0].nil? enabled = true diff --git a/lib/resources/user.rb b/lib/resources/user.rb index 36a7a7c67..f3b1a9fbb 100644 --- a/lib/resources/user.rb +++ b/lib/resources/user.rb @@ -47,7 +47,7 @@ class User < Vulcano.resource(1) # select package manager @user_provider = nil case vulcano.os[:family] - when 'ubuntu', 'debian', 'redhat', 'fedora', 'centos', 'arch' + when 'ubuntu', 'debian', 'redhat', 'fedora', 'centos', 'arch', 'opensuse' @user_provider = LinuxUser.new(vulcano) when 'windows' @user_provider = WindowsUser.new(vulcano) diff --git a/test/integration/.kitchen.yml b/test/integration/.kitchen.yml new file mode 100644 index 000000000..8eaee7470 --- /dev/null +++ b/test/integration/.kitchen.yml @@ -0,0 +1,42 @@ +--- +driver: + name: vagrant + +provisioner: + name: chef_solo + +verifier: + name: InSpec + sudo: true + +platforms: + - name: centos-7.1 + - name: centos-6.7 + - name: centos-6.7-i386 + - name: centos-5.11 + - name: centos-5.11-i386 + - name: debian-6.0.10 + - name: debian-6.0.10-i386 + - name: debian-7.8 + - name: debian-7.8-i386 + - name: debian-8.1 + - name: debian-8.1-i386 + - name: fedora-21 + - name: fedora-21-i386 + - name: fedora-22 + - name: freebsd-9.3 + - name: freebsd-10.2 + - name: opensuse-13.2-x86_64 + - name: opensuse-13.2-i386 + - name: ubuntu-14.04 + - name: ubuntu-14.04-i386 + - name: ubuntu-12.04 + - name: ubuntu-12.04-i386 + - name: ubuntu-10.04 + - name: ubuntu-10.04-i386 + +suites: + - name: default + run_list: + - recipe[os_prepare] + attributes: diff --git a/test/integration/Berksfile b/test/integration/Berksfile new file mode 100644 index 000000000..76967d3b7 --- /dev/null +++ b/test/integration/Berksfile @@ -0,0 +1,4 @@ +source 'https://supermarket.chef.io' + +cookbook 'apt' +cookbook 'os_prepare', path: './cookbooks/os_prepare' diff --git a/test/integration/cookbooks/os_prepare/metadata.rb b/test/integration/cookbooks/os_prepare/metadata.rb new file mode 100644 index 000000000..a874318ef --- /dev/null +++ b/test/integration/cookbooks/os_prepare/metadata.rb @@ -0,0 +1,8 @@ +# encoding: utf-8 +name 'os_prepare' +maintainer 'Chef Software, Inc.' +maintainer_email 'support@chef.io' +description 'This cookbook prepares the test operating systems' +version '1.0.0' +depends 'apt' +depends 'yum' diff --git a/test/integration/cookbooks/os_prepare/recipes/default.rb b/test/integration/cookbooks/os_prepare/recipes/default.rb new file mode 100644 index 000000000..316e79549 --- /dev/null +++ b/test/integration/cookbooks/os_prepare/recipes/default.rb @@ -0,0 +1,7 @@ +# encoding: utf-8 +# author: Christoph Hartmann +# author: Dominik Richter +# +# prepare all operating systems with the required configuration + +include_recipe('os_prepare::package') diff --git a/test/integration/cookbooks/os_prepare/recipes/package.rb b/test/integration/cookbooks/os_prepare/recipes/package.rb new file mode 100644 index 000000000..8d8c03ce7 --- /dev/null +++ b/test/integration/cookbooks/os_prepare/recipes/package.rb @@ -0,0 +1,26 @@ +# encoding: utf-8 +# author: Christoph Hartmann +# author: Dominik Richter +# +# installs everything to do the package test + +case node['platform'] +when 'ubuntu' + include_recipe('apt') + + package 'curl' +when 'rhel', 'centos', 'fedora' + include_recipe('yum') + + # TODO: support DNF natively + # Special care for fedora 22, since dnf is not officially supported yet + # https://github.com/chef/chef/issues/3201 + if node['platform_version'] == 22 + execute 'dnf install -y yum' + end + + package 'curl' +when 'freebsd' + # do nothing + # TODO: implement Freebsd packages +end diff --git a/test/integration/default/_debug_spec.rb b/test/integration/default/_debug_spec.rb new file mode 100644 index 000000000..cd3b41f6f --- /dev/null +++ b/test/integration/default/_debug_spec.rb @@ -0,0 +1 @@ +p "You are currently running on OS family: #{os[:family] || 'unknown'}, OS release: #{os[:release] || 'unknown'}" diff --git a/test/integration/default/kernel_module_spec.rb b/test/integration/default/kernel_module_spec.rb new file mode 100644 index 000000000..8744d55fe --- /dev/null +++ b/test/integration/default/kernel_module_spec.rb @@ -0,0 +1,17 @@ +# encoding: utf-8 + +# Test kernel modules on all linux systems +if os.linux? + + describe kernel_module('video') do + it { should be_loaded } + end + + describe kernel_module('bridge') do + it { should_not be_loaded } + end + + describe kernel_module('dhcp') do + it { should_not be_loaded } + end +end diff --git a/test/integration/default/kernel_parameter_spec.rb b/test/integration/default/kernel_parameter_spec.rb new file mode 100644 index 000000000..332902795 --- /dev/null +++ b/test/integration/default/kernel_parameter_spec.rb @@ -0,0 +1,56 @@ +# encoding: utf-8 + +# prepare values +if ['ubuntu', 'centos', 'fedora', 'opensuse', 'debian'].include?(os[:family]) + test_values = { + kernel_panic: 0, + ip_local_port_range: "32768\t61000", + forwarding: 0, + sched_autogroup_enabled: 1, + nf_log: 'NONE', + } + + # configue parameter derivations for different OS + test_values[:sched_autogroup_enabled] = 0 if ['centos', 'debian'].include?(os[:family]) + + if (os[:family] == 'ubuntu' && os[:release].to_f == 10.04) || + (os[:family] == 'debian' && os[:release].to_i == 6) || + (os[:family] == 'centos' && os[:release].to_i == 5) || + (os[:family] == 'opensuse') + test_values[:sched_autogroup_enabled] = nil + end + + test_values[:nf_log] = nil if os[:family] == 'centos' && os[:release].to_i == 5 + test_values[:kernel_panic] = 90 if os[:family] == 'opensuse' + +else + test_values = {} +end + +# test on all linux systems +if os.linux? + describe kernel_parameter('kernel.panic') do + its(:value) { should eq test_values[:kernel_panic] } + end + + describe kernel_parameter('net.netfilter.nf_log.0') do + its(:value) { should eq test_values[:nf_log] } + end + + describe kernel_parameter('kernel.sched_autogroup_enabled') do + its(:value) { should eq test_values[:sched_autogroup_enabled] } + end + + describe kernel_parameter('net.ipv4.ip_local_port_range') do + its(:value) { should eq test_values[:ip_local_port_range] } + end + + describe kernel_parameter('net.ipv4.conf.all.forwarding') do + its(:value) { should eq test_values[:forwarding] } + end + + # serverspec compatability + describe linux_kernel_parameter('net.ipv4.conf.all.forwarding') do + its(:value) { should eq test_values[:forwarding] } + end +end diff --git a/test/integration/default/package_spec.rb b/test/integration/default/package_spec.rb new file mode 100644 index 000000000..aba113e5c --- /dev/null +++ b/test/integration/default/package_spec.rb @@ -0,0 +1,11 @@ +# encoding: utf-8 + +if ['centos', 'fedora', 'opensuse', 'debian', 'ubuntu'].include?(os[:family]) + describe package('curl') do + it { should be_installed } + end + + describe package('nginx') do + it { should_not be_installed } + end +end diff --git a/test/integration/default/service_spec.rb b/test/integration/default/service_spec.rb new file mode 100644 index 000000000..8009d5d44 --- /dev/null +++ b/test/integration/default/service_spec.rb @@ -0,0 +1,28 @@ +# encoding: utf-8 + +# based on operating system we select the available service +if ['centos', 'fedora', 'freebsd', 'opensuse'].include?(os[:family]) + # CentOS, Fedora + unavailable_service = 'ssh' + available_service = 'sshd' +elsif ['debian'].include?(os[:family]) + # Debian + unavailable_service = 'clamav' + available_service = 'ssh' +else + # Ubuntu + unavailable_service = 'sshd' + available_service = 'ssh' +end + +describe service(unavailable_service) do + it { should_not be_enabled } + it { should_not be_installed } + it { should_not be_running } +end + +describe service(available_service) do + it { should be_enabled } + it { should be_installed } + it { should be_running } +end diff --git a/test/integration/default/user_spec.rb b/test/integration/default/user_spec.rb new file mode 100644 index 000000000..2bcd9c2bb --- /dev/null +++ b/test/integration/default/user_spec.rb @@ -0,0 +1,44 @@ +# encoding: utf-8 + +# root test +if ['centos', 'fedora', 'opensuse', 'debian', 'ubuntu'].include?(os[:family]) + + userinfo = { + name: 'root', + group: 'root', + uid: 0, + gid: 0, + groups: ["root"], + home: '/root', + shell: '/bin/bash', + } + + # different groupset for centos 5 + userinfo[:groups] = ["root", "bin", "daemon", "sys", "adm", "disk", "wheel"] if os[:release].to_i == 5 + +elsif ['freebsd'].include?(os[:family]) + + userinfo = { + name: 'root', + group: 'wheel', + uid: 0, + gid: 0, + groups: ["wheel", "operator"], + home: '/root', + shell: '/bin/csh', + } + +else + userinfo = {} +end + +describe user(userinfo[:name]) do + it { should exist } + it { should belong_to_group userinfo[:group] } + its('uid') { should eq userinfo[:uid] } + its('gid') { should eq userinfo[:gid] } + its('group') { should eq userinfo[:group] } + its('groups') { should eq userinfo[:groups] } + its('home') { should eq userinfo[:home] } + its('shell') { should eq userinfo[:shell] } +end