You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
164 lines
5.4 KiB
164 lines
5.4 KiB
---
|
|
layout: post
|
|
title: Writing InSpec Wrapper Profiles
|
|
category: chef_inspec
|
|
tags: [inspec, cis, security]
|
|
summary: No one is fully compliant, here's how you write a profile fits your environment
|
|
---
|
|
|
|
## What is a wrapper profile?
|
|
|
|
Chef InSpec groups controls and tests in a standalone artifact known as a
|
|
profile. These profiles can include other profiles and bundle/vendor those
|
|
profiles to be executed at runtime.
|
|
|
|
A wrapper profile is just that, a profile that includes controls/resources from
|
|
another profile.
|
|
|
|
## Why would I use a wrapper profile?
|
|
|
|
In addition to adding custom resources, wrapping profiles allows you to
|
|
customize the exact set of controls that are executed during runtime.
|
|
Additionally, it is possible to modify the included controls to reduce the
|
|
severity/impact or redefine/modify tests. Most commonly, wrapper profiles are
|
|
used to implement a strict compliance posture in a way that allows you to
|
|
audit/track only the items you care about.
|
|
|
|
For example, it is very difficult to comply entirely with DISA/NIST/CIS
|
|
standards. By using wrapper profiles you can take advantage of the majority of
|
|
the security benefits being offered without cluttering up your reports with
|
|
items that are not relevant to you or your organization.
|
|
|
|
---
|
|
|
|
## Creating a Wrapper Profile
|
|
|
|
Creating a wrapper profile is a simple as adding a dependency to your
|
|
profile's `inspec.yml` file and using `include_controls` or `require_controls`.
|
|
|
|
## Defining dependencies
|
|
|
|
When defining dependencies two things are needed:
|
|
1. A name, this is used when referencing a dependent profile in your profile
|
|
2. A source to fetch the dependent profile
|
|
|
|
As of the time of this writing, sources can be any of the following:
|
|
- A local path on disk (`path:`)
|
|
- A URL to a tar/zip (`url:`)
|
|
- A GitHub repository (`github:`)
|
|
- A Chef Automate server (`compliance:`)
|
|
|
|
For more information on how to use each of these see:
|
|
|
|
[https://www.inspec.io/docs/reference/profiles/#defining-the-dependencies](https://www.inspec.io/docs/reference/profiles/#defining-the-dependencies)
|
|
|
|
## Including/Requiring controls
|
|
|
|
Controls are included from dependant profiles by using either
|
|
`include_controls` or `require_controls`. The main difference between these two
|
|
is that `include_controls` includes all controls from the dependent profile
|
|
whereas `require_controls` only includes the controls specified within the
|
|
`do`/`end` block.
|
|
|
|
### `include_controls`
|
|
|
|
Using `include_controls` will include all controls from the referenced profile.
|
|
Certain controls can be excluded from this inclusion by using: `skip_control`.
|
|
|
|
For example, let's say you wanted to include all controls from `some-profile`
|
|
but skip the `some-strict-control` (defined via `control
|
|
'some-strict-control`).
|
|
|
|
Do the following:
|
|
|
|
```ruby
|
|
# `some_profile` corresponds to `name:` in the `inspec.yml` `depends:` section
|
|
include_controls 'some-profile' do
|
|
skip_control 'some-strict-control'
|
|
end
|
|
```
|
|
|
|
### `require_controls`
|
|
|
|
Using `require_conrols` will only execute the controls you specify from the
|
|
dependant profile.
|
|
|
|
For example, let's say you want to include some specific controls from a
|
|
dependent profile, but not include all the controls from that profile.
|
|
|
|
Do the following:
|
|
|
|
```ruby
|
|
# `some_profile` corresponds to `name:` in the `inspec.yml` `depends:` section
|
|
require_controls 'some-profile' do
|
|
control 'some-strict-control'
|
|
end
|
|
```
|
|
|
|
## Modifying Included Controls
|
|
|
|
Whether you are using `include_controls` or `require_controls`, the included
|
|
controls can be modified.
|
|
|
|
For example, let's say you want to include all the controls from `some-profile`
|
|
but the `telnet` control from the included profile only specifies that the
|
|
package `telnetd` is not installed and does not account for the fact that
|
|
CentOS's package is called `telnet-server`.
|
|
|
|
Do the following:
|
|
|
|
```ruby
|
|
# `some_profile` corresponds to `name:` in the `inspec.yml` `depends:` section
|
|
include_control 'some-profile' do
|
|
control 'telnet' do
|
|
telnet_server_packages = %w(telnetd telnet-server)
|
|
telnet_server_packages do |telnet_package|
|
|
package telnet_package do
|
|
it { should_not be_installed }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
This will overwrite the tests in the `some-profile` control named `telnet` but
|
|
will not modify things such as `impact`, `title`, or `desc`.
|
|
|
|
## Example: Wrapping a CIS Profile
|
|
|
|
Chef Automate includes several built in Chef InSpec profiles for a variety of
|
|
platforms.
|
|
|
|
The example will:
|
|
- Include all controls from the `cis-centos7-level2` profile
|
|
- Modify the impact of the SSHD control to be informational
|
|
- Justification: You would like to have a banner but it needn't be a 'critical' failure
|
|
- Modify the HTTP server control to not execute/fail if a web server is running
|
|
- Justification: You want to run this profile, but don't want web servers to fail
|
|
|
|
First, add the following to your profile's `inspec.yml`:
|
|
|
|
```yaml
|
|
depends:
|
|
- name: 'cis-centos7-level2'
|
|
compliance: 'secteam/cis-centos7-level2'
|
|
```
|
|
|
|
Then add the following to one of your controls:
|
|
|
|
```ruby
|
|
include_controls 'cis-centos7-level2' do
|
|
control 'xccdf_org.cisecurity.benchmarks_rule_6.2.14_Set_SSH_Banner' do
|
|
impact 0.0
|
|
end
|
|
|
|
control 'xccdf_org.cisecurity.benchmarks_rule_3.11_Remove_HTTP_Server' do
|
|
# TODO: Remove me when/if InSpec add support for `not_if`
|
|
only_if('This server is a webserver') do
|
|
!service('httpd').running? || !service('apache2').running?
|
|
end
|
|
end
|
|
end
|
|
```
|
|
|
|
> NOTE: Message after the `only_if` will only show if the control is skipped
|
|
|