Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add chef-server-visibility collector #157

Merged
merged 2 commits into from
Nov 16, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 25 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Please ensure that `chef-cookbooks` is the parent directory of `audit` cookbook.

#### Reporting to Chef Compliance via Chef Server

If you want the audit cookbook to converge and retrieve compliance profiles through the Chef Server, set the `collector` and `profiles` attribute.
If you want the audit cookbook to converge and retrieve compliance profiles through the Chef Server, set the `collector` and `profiles` attributes.

This requires your Chef Server to be integrated with the Chef Compliance server using this [guide](https://docs.chef.io/integrate_compliance_chef_server.html).

Expand Down Expand Up @@ -153,9 +153,9 @@ node.default['audit']['profiles'].push("path": "#{PROFILES_PATH}/mylinux-failure
You can also configure in a policyfile like this:

```ruby
default['audit'] = {
'collector' => 'chef-server',
'profiles' => [
default["audit"] = {
"collector" => "chef-server",
"profiles" => [
{
"name": "linux",
"compliance": "base/linux"
Expand All @@ -168,6 +168,24 @@ default['audit'] = {
}
```

#### Reporting to Chef Visibility via Chef Server

If you want the audit cookbook to retrieve compliance profiles and report to Chef Automate(Visibility) through Chef Server, set the `collector` and `profiles` attributes. For example:


```ruby
"audit": {
"collector": "chef-server-visibility",
"insecure": false,
"profiles": [
{
"name": "windows",
"compliance": "base/windows"
}
]
}
```


#### Direct reporting to Chef Compliance

Expand Down Expand Up @@ -246,7 +264,7 @@ Simply include the `upload` recipe in the run_list, with attribute overrides for

```ruby
audit: {
server: 'https://compliance-server.test/api'
server: 'https://compliance-server.test/api',
collector: 'chef-compliance',
refresh_token: '21/XMEK3...',
profiles: [
Expand Down Expand Up @@ -302,8 +320,8 @@ This will allow the audit cookbook to fetch the profile from chef-compliance. F

```ruby
"audit": {
"fetcher": 'chef-server'
"collector": 'chef-visibility'
"fetcher": "chef-server",
"collector": "chef-visibility",
"profiles": [
{
"name": "ssh",
Expand Down
7 changes: 4 additions & 3 deletions attributes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@
# root of location must host the *specs.4.8.gz source index
default['audit']['inspec_gem_source'] = nil

# collector possible values: chef-server, chef-compliance, chef-visibility, json-file
# collector possible values: chef-server, chef-compliance, chef-visibility, chef-server-visibility, json-file
# chef-visibility requires inspec version 0.27.1 or above
default['audit']['collector'] = 'chef-server'

# It will use an InSpec fetcher that fetches compliance profiles via Chef Server
# from Chef Compliance. Will be activated by default if the 'chef-server' collector
# from Chef Compliance or Chef Automate. Will be activated by default if the collectors
# 'chef-server' or 'chef-server-visibility' are used
# is used
# fetcher possible values: chef-server
default['audit']['fetcher'] = nil
Expand All @@ -46,7 +47,7 @@
default['audit']['insecure'] = nil

# Chef Compliance organization to post the report to. Defaults to Chef Server org if not defined
# needed for the 'chef-compliance' collector, optional for 'chef-server' collector
# needed for the 'chef-compliance' collector, optional for 'chef-server' and 'chef-server-visibility' collectors
default['audit']['owner'] = nil

# raise exception if Compliance API endpoint is unreachable
Expand Down
13 changes: 11 additions & 2 deletions files/default/handler/audit_report.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def report
load_needed_dependencies

# detect if we run in a chef client with chef server
load_chef_fetcher if reporters.include?('chef-server') || node['audit']['fetcher'] == 'chef-server'
load_chef_fetcher if reporters.include?('chef-server') || reporters.include?('chef-server-visibility') || node['audit']['fetcher'] == 'chef-server'

# iterate through reporters
reporters.each do |reporter|
Expand Down Expand Up @@ -74,7 +74,7 @@ def load_chef_fetcher

# sets format to json-min when chef-compliance, json when chef-visibility
def get_opts(reporter, quiet)
format = reporter == 'chef-visibility' ? 'json' : 'json-min'
format = ['chef-visibility', 'chef-server-visibility'].include?(reporter) ? 'json' : 'json-min'
output = quiet ? ::File::NULL : $stdout

Chef::Log.warn "Format is #{format}"
Expand Down Expand Up @@ -152,6 +152,15 @@ def send_report(reporter, server, user, profiles, report)
else
Chef::Log.warn "'server' and 'token' properties required by inspec report collector #{reporter}. Skipping..."
end
elsif reporter == 'chef-server-visibility'
chef_url = server || base_chef_server_url
chef_org = Chef::Config[:chef_server_url].split('/').last
if chef_url
url = construct_url(chef_url, File.join('organizations', chef_org, 'data-collector'))
Collector::ChefServerVisibility.new(entity_uuid, run_id, gather_nodeinfo, insecure, report).send_report(url)
else
Chef::Log.warn "unable to determine chef-server url required by inspec report collector '#{reporter}'. Skipping..."
end
elsif reporter == 'chef-server'
chef_url = server || base_chef_server_url
chef_org = Chef::Config[:chef_server_url].split('/').last
Expand Down
21 changes: 21 additions & 0 deletions libraries/collector_classes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,27 @@ def send_report
end
end

#
# Used to send inspec reports to Chef Visibility server via Chef Server
#
class ChefServerVisibility < ChefVisibility
def send_report(url)
content = @report
json_report = enriched_report(JSON.parse(content))

if @insecure
Chef::Config[:verify_api_cert] = false
Chef::Config[:ssl_verify_mode] = :verify_none
end

Chef::Log.info "Report to Visibility via Chef Server: #{url}"
rest = Chef::ServerAPI.new(url, Chef::Config)
with_http_rescue do
rest.post(url, JSON.parse(json_report))
end
end
end

#
# Used to write report to file on disk
#
Expand Down
2 changes: 1 addition & 1 deletion metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
license 'Apache 2.0'
description 'Allows for fetching and executing compliance profiles, and reporting its results'
long_description IO.read(File.join(File.dirname(__FILE__), 'README.md'))
version '2.1.0'
version '2.2.0'

source_url 'https://github.com/chef-cookbooks/audit'
issues_url 'https://github.com/chef-cookbooks/audit/issues'
Expand Down
25 changes: 23 additions & 2 deletions spec/unit/report/audit_report_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# encoding: utf-8
#
# Cookbook Name:: compliance
# Cookbook Name:: audit
# Spec:: default
#
# Copyright 2016 Chef Software, Inc.
Expand All @@ -18,6 +18,7 @@
# limitations under the License.

require 'spec_helper'
require 'json'
require_relative '../../../files/default/handler/audit_report'
require_relative '../../data/mock.rb'

Expand Down Expand Up @@ -62,16 +63,28 @@
opts = @audit_report.get_opts(reporter, quiet)
expect(opts).to eq({'report' => true, 'format' => 'json-min', 'output' => '/dev/null', 'logger' => Chef::Log})
end
it 'given chef-server sets the format to json-min' do
reporter = 'chef-server'
quiet = true
opts = @audit_report.get_opts(reporter, quiet)
expect(opts).to eq({'report' => true, 'format' => 'json-min', 'output' => '/dev/null', 'logger' => Chef::Log})
end
it 'given chef-visibility sets the format to json' do
reporter = 'chef-visibility'
quiet = true
opts = @audit_report.get_opts(reporter, quiet)
expect(opts).to eq({'report' => true, 'format' => 'json', 'output' => '/dev/null', 'logger' => Chef::Log})
end
it 'given chef-server-visibility sets the format to json' do
reporter = 'chef-server-visibility'
quiet = true
opts = @audit_report.get_opts(reporter, quiet)
expect(opts).to eq({'report' => true, 'format' => 'json', 'output' => '/dev/null', 'logger' => Chef::Log})
end
end

describe 'call' do
it 'given a profile returns a report' do
it 'given a profile, returns a json report' do
require 'inspec'
opts = {'report' => true, 'format' => 'json', 'output' => '/dev/null'}
path = File.expand_path('../../../data/mock_profile.rb', __FILE__)
Expand All @@ -80,5 +93,13 @@
expected_report = /^.*version.*profiles.*controls.*statistics.*duration.*/
expect(report).to match(expected_report)
end
it 'given a profile, returns a json-min report' do
require 'inspec'
opts = {'report' => true, 'format' => 'json-min', 'output' => '/dev/null'}
path = File.expand_path('../../../data/mock_profile.rb', __FILE__)
profiles = [{'name': 'example', 'path': path }]
report = JSON.parse(@audit_report.call(opts, profiles))
expect(report['controls'].length).to eq(2)
end
end
end