Skip to content

Commit

Permalink
Merge pull request #64 from mhedgpeth/interval-fixes
Browse files Browse the repository at this point in the history
Merged interval functionality into default.rb recipe, updated documentation, gave quiet default
  • Loading branch information
chris-rock authored Jun 15, 2016
2 parents 722d7dc + b7695ea commit 7c9d1eb
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 111 deletions.
40 changes: 22 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ The `audit` support two scenarios:

### Chef Server Integration

The first scenario requires at least **Chef Compliance 1.0** and the **Chef Server extensions for Compliance**. The architecture looks as following:
The first scenario requires at least **Chef Compliance 1.0** and the **[Chef Server extensions for Compliance](https://docs.chef.io/integrate_compliance_chef_server.html)**. The architecture looks as following:

```
┌──────────────────────┐ ┌──────────────────────┐ ┌──────────────────────┐
Expand Down Expand Up @@ -58,20 +58,13 @@ The second scenario support a direct connection with Chef Compliance and support
└──────────────────────┘ └──────────────────────┘
```

```ruby
audit = {
"inspec_version" => "0.22.1",
}
```


## Usage

The audit cookbook needs to be configured for each node where the `chef-client` runs. The `audit` cookbook can be reused for all nodes, all node-specific configuration is done via Chef attributes.

### Upload cookbook to Chef Server

The `audit` cookbook is available at [Chef Supermarket](https://supermarket.chef.io/cookbooks/audit). This allows you to reuse the existing workflow.
The `audit` cookbook is available at [Chef Supermarket](https://supermarket.chef.io/cookbooks/audit). This allows you to reuse your existing workflow for managing cookbooks in your runlist.

If you want to upload the cookbook from git, use the following commands:

Expand All @@ -87,11 +80,10 @@ Please ensure that `chef-cookbooks` is the parent directory of `audit` cookbook.

### Configure node

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:
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 you can define the attribute in a JSON-based role or environment file like this:

```ruby
audit = {
"inspec_version" => "0.22.1",
"profiles" => {
# org / profile name from Chef Compliance
'base/linux' => true,
Expand Down Expand Up @@ -119,6 +111,17 @@ audit = {
}
```

You can also configure in a policyfile like this:

```ruby
default['audit'] = {
profiles: {
'base/linux' => true,
'base/ssh' => true
}
}
```

#### Direct reporting to Chef Compliance

If you want the audit cookbook directly report to Chef Compliance, set the `server` and the `token` attribute.
Expand Down Expand Up @@ -150,7 +153,7 @@ audit: {
}
```

## How does it relate to Chef Audit Mode
## Relationship with Chef Audit Mode

The following tables compares the [Chef Client audit mode](https://docs.chef.io/ctl_chef_client.html#run-in-audit-mode) with this `audit` cookbook.

Expand All @@ -161,7 +164,9 @@ The following tables compares the [Chef Client audit mode](https://docs.chef.io/
| Execute InSpec Compliance Profiles | No | Yes |
| Execute tests embedded in Chef recipes | Yes | No |

### How to migrate from audit mode to audit cookbook:
Eventually the `audit` cookbook will replace audit mode. The only drawback is that you will not be able to execute tests in Chef recipes, but since you will be running these tests in production, you will want to have a straightforward, consistent process by which you include these tests throughout your development lifecycle. Within Chef Compliance, this is a profile.

### Migrating from audit mode to audit cookbook:

We will improve the migration and help to ease the process and to reuse existing audit mode test as much as possible. At this point of time, an existing audit-mode test like:

Expand Down Expand Up @@ -204,12 +209,11 @@ end
## Interval Settings

If you have long running audit profiles that you don't wish to execute on every chef-client run,
you can use the interval recipe instead of the default recipe in your runlist, and set the
following attributes:
you can enable an interval:

```
default['audit']['interval']['enabled'] = false
default['audit']['interval']['time'] = 1440
default['audit']['interval']['enabled'] = true
default['audit']['interval']['time'] = 1440 # once a day, the default value
```

The time attribute is in minutes.
Expand All @@ -225,7 +229,7 @@ You can enable the interval and set the interval time, along with your desired p
"base/linux": true
},
"interval": {
"enabled": "true",
"enabled": true,
"time": 1440
}
}
Expand Down
8 changes: 8 additions & 0 deletions attributes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,3 +35,11 @@

# inspec gem version to install(e.g. '0.22.1') or 'latest'
default['audit']['inspec_version'] = '0.22.1'

# by default run audit every time
default['audit']['interval']['enabled'] = false
# by default run compliance once a day
default['audit']['interval']['time'] = 1440

# quiet mode, on by default because this is testing, resources aren't converged in the normal chef sense
default['audit']['quiet'] = true
22 changes: 0 additions & 22 deletions attributes/interval.rb

This file was deleted.

8 changes: 4 additions & 4 deletions libraries/interval.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# encoding: utf-8

def last_run(profile, interval)
def profile_overdue_to_run?(profile, interval)
# Calculate when the profile was last run so we delay it's next run if necessary
return false unless ::File.exist?("#{compliance_cache_directory}/#{profile}")
compliance_cache_directory = ::File.join(Chef::Config[:file_cache_path], 'compliance')
lastrun = Time.now - ::File.mtime("#{compliance_cache_directory}/#{profile}")
lastrun < interval
return true unless ::File.exist?("#{compliance_cache_directory}/#{profile}")
seconds_since_last_run = Time.now - ::File.mtime("#{compliance_cache_directory}/#{profile}")
seconds_since_last_run > interval
end
19 changes: 17 additions & 2 deletions recipes/default.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
# These two attributes should only be set when connecting directly to Chef Compliance, otherwise they should be nil
token = node['audit']['token']
server = node['audit']['server']
interval_seconds = 0 # always run this by default, unless interval is defined
if !node['audit']['interval'].nil? && node['audit']['interval']['enabled']
interval_seconds = node['audit']['interval']['time'] * 60 # seconds in interval
end
Chef::Log.debug "Auditing this machine every #{interval_seconds} seconds "
compliance_cache_directory = ::File.join(Chef::Config[:file_cache_path], 'compliance')

directory compliance_cache_directory

# iterate over all selected profiles
node['audit']['profiles'].each do |owner_profile, value|
Expand All @@ -34,15 +42,22 @@
"Must contain /, e.g. 'john/ssh'" if owner_profile !~ %r{\/}
o, p = owner_profile.split('/').last(2)

# file that can be used for interval triggering
file "#{compliance_cache_directory}/#{p}" do
action :nothing
end

# execute profile
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?
quiet node['audit']['quiet']
only_if { profile_overdue_to_run?(p, interval_seconds) }
action [:fetch, :execute]
notifies :touch, "file[#{compliance_cache_directory}/#{p}]", :immediately
end
end

Expand All @@ -51,6 +66,6 @@
owner node['audit']['owner']
server server
token token
quiet node['audit']['quiet'] unless node['audit']['quiet'].nil?
quiet node['audit']['quiet']
action :execute
end if node['audit']['profiles'].values.any?
44 changes: 0 additions & 44 deletions recipes/interval.rb

This file was deleted.

22 changes: 22 additions & 0 deletions spec/unit/recipes/default_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,26 @@
expect { chef_run }.to_not raise_error
end
end

context 'when set to run on an interval and not due to run' do
before(:each) do
allow_any_instance_of(Chef::Resource).to receive(:profile_overdue_to_run?).and_return(false)
end

let(:chef_run) do
runner = ChefSpec::ServerRunner.new(platform: 'centos', version: '6.5')
runner.node.set['audit']['profiles'] = { 'admin/myprofile' => true }
runner.node.set['audit']['interval']['enabled'] = true
runner.converge(described_recipe)
end

it 'does not fetch or execute on compliance profile' do
expect(chef_run).to_not fetch_compliance_profile('myprofile')
expect(chef_run).to_not execute_compliance_profile('myprofile')
end

it 'converges successfully' do
expect { chef_run }.to_not raise_error
end
end
end
21 changes: 0 additions & 21 deletions spec/unit/recipes/interval_spec.rb

This file was deleted.

0 comments on commit 7c9d1eb

Please sign in to comment.