diff --git a/lib/bundles/inspec-compliance.rb b/lib/bundles/inspec-compliance.rb index 036278e5d..76d38e5a5 100644 --- a/lib/bundles/inspec-compliance.rb +++ b/lib/bundles/inspec-compliance.rb @@ -8,6 +8,7 @@ $LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir) module Compliance autoload :Configuration, 'inspec-compliance/configuration' autoload :HTTP, 'inspec-compliance/http' + autoload :Support, 'inspec-compliance/support' autoload :API, 'inspec-compliance/api' end diff --git a/lib/bundles/inspec-compliance/.kitchen.yml b/lib/bundles/inspec-compliance/.kitchen.yml new file mode 100644 index 000000000..ce146a66e --- /dev/null +++ b/lib/bundles/inspec-compliance/.kitchen.yml @@ -0,0 +1,21 @@ +--- +driver: + name: vagrant + synced_folders: + - ['../../../', '/inspec'] + network: + - ['private_network', {ip: '192.168.251.2'}] + +provisioner: + name: shell + +verifier: + name: inspec + sudo: true + +platforms: + - name: ubuntu-14.04 +suites: + - name: default + run_list: + attributes: diff --git a/lib/bundles/inspec-compliance/README.md b/lib/bundles/inspec-compliance/README.md index 053caca8e..a6ede6e00 100644 --- a/lib/bundles/inspec-compliance/README.md +++ b/lib/bundles/inspec-compliance/README.md @@ -19,3 +19,27 @@ Compliance profiles can be executed in two mays: - via compliance exec: `inspec compliance exec profile` - via compliance scheme: `inspec exec compliance://profile` + +## Integration Tests + +At this point of time, InSpec is not able to pick up the token directly, therefore the integration test is semi-automatic at this point of time: + + * run `kitchen converge` + * open https://192.168.251.2 and log in with user `admin` and password `admin` + * click on user->about and obtain the refresh token + * run `kitchen verify` with the required env variables: + +``` +COMPLIANCE_REFRESH_TOKEN=myrefreshtoken COMPLIANCE_ACCESS_TOKEN=mycompliancetoken b kitchen verify +-----> Starting Kitchen (v1.7.3) +-----> Verifying ... + Search `/Users/chartmann/Development/compliance/inspec/lib/bundles/inspec-compliance/test/integration/default` for tests +.................................. + +Finished in 6.35 seconds (files took 0.40949 seconds to load) +34 examples, 0 failures + + Finished verifying (0m6.62s). +-----> Kitchen is finished. (0m7.02s) +zlib(finalizer): the stream was freed prematurely. +``` diff --git a/lib/bundles/inspec-compliance/bootstrap.sh b/lib/bundles/inspec-compliance/bootstrap.sh new file mode 100644 index 000000000..f037e5ce4 --- /dev/null +++ b/lib/bundles/inspec-compliance/bootstrap.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +echo "Installing Chef Compliance $deb" +# select latest package from cache directory +# deb=$(find /inspec/.cache -name '*.deb' | tail -1) +# sudo dpkg -i $deb + +# use chef compliance package repository +sudo apt-get install -y apt-transport-https +sudo apt-get install wget +wget -qO - https://downloads.chef.io/packages-chef-io-public.key | sudo apt-key add - +CHANNEL=${CHANNEL:-stable} +DISTRIBUTION=$(lsb_release --codename | cut -f2) +echo "found $DISTRIBUTION" +echo "use $CHANNEL channel" +echo "deb https://packages.chef.io/$CHANNEL-apt $DISTRIBUTION main" > /etc/apt/sources.list.d/chef-$CHANNEL.list +sudo apt-get update +sudo apt-get install chef-compliance + +sudo chef-compliance-ctl reconfigure --accept-license +sudo chef-compliance-ctl restart + +# build master version of inspec +sudo /opt/chef-compliance/embedded/bin/gem list inspec + +cd /inspec +sudo /opt/chef-compliance/embedded/bin/gem build *.gemspec +sudo /opt/chef-compliance/embedded/bin/gem install inspec*.gem +sudo /opt/chef-compliance/embedded/bin/inspec version +sudo /opt/chef-compliance/embedded/bin/gem list inspec + +# finalize setup +cd / +/opt/chef-compliance/embedded/service/core/bin/core setup --endpoint "http://127.0.0.1:10500/setup" --login "admin" --password "admin" --name "John Doe" --accept-eula + +# wget --no-check-certificate http://127.0.0.1/api/version +# cat version diff --git a/lib/bundles/inspec-compliance/cli.rb b/lib/bundles/inspec-compliance/cli.rb index 9c459f226..d229913e5 100644 --- a/lib/bundles/inspec-compliance/cli.rb +++ b/lib/bundles/inspec-compliance/cli.rb @@ -23,9 +23,9 @@ module Compliance desc: 'Chef Compliance access token' option :refresh_token, type: :string, required: false, desc: 'Chef Compliance refresh token' - def login(server) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/AbcSize, PerceivedComplexity + def login(server) # rubocop:disable Metrics/AbcSize, PerceivedComplexity # show warning if the Compliance Server does not support - if !Compliance::Configuration.new.supported?(:oidc) && (!options['token'].nil? || !options['refresh_token'].nil?) + if !Compliance::Configuration.new.supported?(:oidc) puts 'Your server supports --user and --password only' end diff --git a/lib/bundles/inspec-compliance/support.rb b/lib/bundles/inspec-compliance/support.rb new file mode 100644 index 000000000..fc65945d7 --- /dev/null +++ b/lib/bundles/inspec-compliance/support.rb @@ -0,0 +1,36 @@ +# encoding: utf-8 +# author: Christoph Hartmann +# author: Dominik Richter + +module Compliance + # is a helper that provides information which version of compliance supports + # which feature + class Support + # for a feature, returns either: + # - a version v0: v supports v0 iff v0 <= v + # - an array [v0, v1] of two versions: v supports [v0, v1] iff v0 <= v < v1 + def self.version_with_support(feature) + case feature.to_sym + when :oidc # open id connect authentication + Gem::Version.new('0.16.19') + else + Gem::Version.new('0.0.0') + end + end + + # determines if the given version support a certain feature + def self.supported?(feature, version) + sup = version_with_support(feature) + + if sup.is_a?(Array) + Gem::Version.new(version) >= sup[0] && + Gem::Version.new(version) < sup[1] + else + Gem::Version.new(version) >= sup + end + end + + # we do not know the version, therefore we do not know if its possible to use the feature + # return if self['version'].nil? || self['version']['version'].nil? + end +end diff --git a/lib/bundles/inspec-compliance/test/integration/default/cli.rb b/lib/bundles/inspec-compliance/test/integration/default/cli.rb new file mode 100644 index 000000000..64d9777bd --- /dev/null +++ b/lib/bundles/inspec-compliance/test/integration/default/cli.rb @@ -0,0 +1,56 @@ +# encoding: utf-8 + +# options +inspec_bin = '/opt/chef-compliance/embedded/bin/inspec' +api_url = 'https://0.0.0.0' +profile = '/inspec/examples/profile' + +# TODO: determine tokens automatically, define in kitchen yml +access_token = ENV['COMPLIANCE_ACCESS_TOKEN'] +refresh_token = ENV['COMPLIANCE_REFRESH_TOKEN'] + +%w{refresh_token access_token}.each do |type| + case type + when 'access_token' + token_options = "--token '#{access_token}'" + when 'refresh_token' + token_options = "--refresh_token '#{refresh_token}'" + end + + # verifies that the help command works + describe command("#{inspec_bin} compliance help") do + its('stdout') { should include 'inspec compliance help [COMMAND]' } + its('stderr') { should eq '' } + its('exit_status') { should eq 0 } + end + + # login via access token token + describe command("#{inspec_bin} compliance login #{api_url} --insecure --user admin #{token_options}") do + its('stdout') { should include 'Successfully authenticated' } + its('stderr') { should eq '' } + its('exit_status') { should eq 0 } + end + + # see available resources + describe command("#{inspec_bin} compliance profiles") do + its('stdout') { should include 'base/ssh' } + its('stderr') { should eq '' } + its('exit_status') { should eq 0 } + end + + # upload a compliance profile + describe command("#{inspec_bin} compliance upload #{profile} --overwrite") do + its('stdout') { should include 'Profile is valid' } + its('stdout') { should include 'Successfully uploaded profile' } + its('stdout') { should_not include 'error(s)' } + its('stderr') { should eq '' } + its('exit_status') { should eq 0 } + end + + # logout + describe command("#{inspec_bin} compliance logout") do + its('stdout') { should include 'Successfully logged out' } + its('stderr') { should eq '' } + its('exit_status') { should eq 0 } + end +end