--- 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