Writing InSpec Wrapper Profiles

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

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:

# `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:

# `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:

# `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:

depends:
  - name: 'cis-centos7-level2'
    compliance: 'secteam/cis-centos7-level2'

Then add the following to one of your controls:

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