Skip to content

Commit

Permalink
Merge pull request #50 from jeremymv2/support_other_sources
Browse files Browse the repository at this point in the history
Support other sources
  • Loading branch information
chris-rock committed May 23, 2016
2 parents 93a9b46 + ef4cce8 commit b044160
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 25 deletions.
37 changes: 28 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,32 @@ Please ensure that `chef-cookbooks` is the parent directory of `audit` cookbook.

Once the cookbook is available in Chef Server, you need to add the `audit::default` recipe to the run-list of each node. The profiles are selected via the `node['audit']['profiles']` attribute. For example, to run the `base/ssh` and `base/linux` profiles, you can define the attribute in a JSON-based role or environment file like this:

```json
"audit": {
"inspec_version": "0.22.0",
"profiles": {
"base/ssh": true,
"base/linux": true
}
```ruby
audit = {
"inspec_version" => "0.22.0",
"profiles" => {
# org / profile name
'base/linux' => true,
'brewinc/ssh-hardening' => {
# where inspec will fetch from
'source' => 'supermarket://hardening/ssh-hardening',
'key' => 'value',
},
# Windows path
'brewinc/win2012_audit' => {
# filesystem path
'source' => 'E:/profiles/win2012_audit',
},
'brewinc/tmp_compliance_profile' => {
# github
'source' => 'https://github.com/nathenharvey/tmp_compliance_profile',
},
# disable profile
'brewinc/tmp_compliance_profile-master' => {
'source' => '/tmp/tmp_compliance_profile-master',
'disabled' => true,
},
},
}
```

Expand Down Expand Up @@ -100,7 +119,7 @@ can be re-written in InSpec as follows:

```
# rename `control_group` to `control` and use a unique identifier
control "blog-1" do
control "blog-1" do
title 'Check SSH Port' # add the title from `control_group`
# rename the old `control` to `describe`
describe 'SSH' do
Expand All @@ -114,7 +133,7 @@ end
or even simplified to:

```
control "blog-1" do
control "blog-1" do
title 'SSH should be listening on port 22'
describe port(22) do
it { should be_listening }
Expand Down
10 changes: 6 additions & 4 deletions libraries/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ def with_http_rescue(&block)
begin
return yield
rescue Net::HTTPServerException => e
case e.message
case e.response.code
when /401/
Chef::Log.error "#{e} Possible time/date issue on the client."
Chef::Log.error "Possible time/date issue on the client."
when /403/
Chef::Log.error "#{e} Possible offline Compliance Server or chef_gate auth issue."
Chef::Log.error "Possible offline Compliance Server or chef_gate auth issue."
when /404/
Chef::Log.error "Object does not exist on remote server."
end
Chef::Log.error 'Profile NOT downloaded. Will use cached version if available.'
Chef::Log.error e.message
raise e if run_context.node.audit.raise_if_unreachable
end
end
Expand Down
29 changes: 20 additions & 9 deletions libraries/profile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,23 @@ class ComplianceProfile < Chef::Resource # rubocop:disable Metrics/ClassLength
end

require 'inspec'
# load the supermarket plugin
require 'bundles/inspec-supermarket/api'
require 'bundles/inspec-supermarket/target'
check_inspec
end

converge_by 'create cache directory' do
directory(::File.join(Chef::Config[:file_cache_path], 'compliance')).run_action(:create)
end

converge_by 'fetch compliance profile' do
return if path # will be fetched from other source during execute phase

o, p = normalize_owner_profile
Chef::Log.info "Fetch compliance profile #{o}/#{p}"

path = tar_path
directory(::Pathname.new(path).dirname.to_s).run_action(:create)

if token # go direct
reqpath ="owners/#{o}/compliance/#{p}/tar"
Expand Down Expand Up @@ -92,19 +100,22 @@ class ComplianceProfile < Chef::Resource # rubocop:disable Metrics/ClassLength
check_inspec
end

converge_by 'create/verify cache directory' do
directory(::File.join(Chef::Config[:file_cache_path], 'compliance')).run_action(:create)
end

converge_by 'execute compliance profile' do
path = tar_path
path ||= tar_path
report_file = report_path

unless ::File.exist?(path)
Chef::Log.warn "No such file: #{path}"
supported_schemes = %w{http https supermarket compliance chefserver}
if !supported_schemes.include?(URI(path).scheme) && !::File.exist?(path)
Chef::Log.warn "No such path! Skipping: #{path}"
fail "Aborting since profile is not present here: #{path}" if run_context.node.audit.fail_if_not_present
return
end

report_file = report_path

o, p = normalize_owner_profile
Chef::Log.info "Execute compliance profile #{o}/#{p}"
Chef::Log.info "Executing: #{path}"

# TODO: flesh out inspec's report CLI interface,
# make this an execute[inspec check ...]
Expand Down Expand Up @@ -136,7 +147,7 @@ def check_inspec

def normalize_owner_profile
if profile.include?('/')
profile.split('/')
profile.split('/').last(2)
else
[owner || 'base', profile]
end
Expand Down
13 changes: 10 additions & 3 deletions recipes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,23 @@
server = node['audit']['server']

# iterate over all selected profiles
node['audit']['profiles'].each do |owner_profile, enabled|
next unless enabled
node['audit']['profiles'].each do |owner_profile, value|
case value
when Hash
next if value['disabled']
path = value['source']
else
next if value == false
end
fail "Invalid profile name '#{owner_profile}'. "\
"Must contain /, e.g. 'john/ssh'" if owner_profile !~ %r{\/}
o, p = owner_profile.split('/')
o, p = owner_profile.split('/').last(2)

compliance_profile p do
owner o
server server
token token
path path unless path.nil?
inspec_version node['audit']['inspec_version']
quiet node['audit']['quiet'] unless node['audit']['quiet'].nil?
action [:fetch, :execute]
Expand Down
53 changes: 53 additions & 0 deletions spec/unit/recipes/default_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,4 +85,57 @@
expect { chef_run }.to raise_error("Invalid profile name 'myprofile'. Must contain /, e.g. 'john/ssh'")
end
end

context 'When specifying profiles with alternate sources' do
let(:chef_run) do
runner = ChefSpec::ServerRunner.new(platform: 'centos', version: '6.5')
runner.node.set['audit']['profiles'] = {
'base/linux' => true,
'base/apache' => false,
'brewinc/ssh-hardening' => {
'source' => 'supermarket://hardening/ssh-hardening',
},
'brewinc/tmp_compliance_profile' => {
'source' => 'https://github.com/nathenharvey/tmp_compliance_profile',
},
'brewinc/tmp_compliance_profile-master' => {
'source' => '/tmp/tmp_compliance_profile-master',
},
'exampleorg/myprofile' => {
'disabled' => true,
},
}
runner.converge(described_recipe)
end
it 'executes base/linux in backward compatible mode' do
expect(chef_run).to execute_compliance_profile('linux').with(
path: nil,
)
end
it 'executes brewinc/ssh-hardening from supermarket' do
expect(chef_run).to execute_compliance_profile('ssh-hardening').with(
path: 'supermarket://hardening/ssh-hardening',
)
end
it 'executes brewinc/tmp_compliance_profile from github' do
expect(chef_run).to execute_compliance_profile('tmp_compliance_profile').with(
path: 'https://github.com/nathenharvey/tmp_compliance_profile',
)
end
it 'executes brewinc/tmp_compliance_profile-master from filesystem' do
expect(chef_run).to execute_compliance_profile('tmp_compliance_profile-master').with(
path: '/tmp/tmp_compliance_profile-master',
)
end
it 'does not execute disabled exampleorg/myprofile' do
expect(chef_run).to_not execute_compliance_profile('myprofile')
end
it 'executes execute_compliance_report[chef-server]' do
expect(chef_run).to execute_compliance_report('chef-server')
end

it 'converges successfully' do
expect { chef_run }.to_not raise_error
end
end
end

0 comments on commit b044160

Please sign in to comment.