Chef InSpec: Where security and compliance meet devops

How to use InSpec, Chef's open source audit and test framework, to define and enforce security standards

Chef InSpec: Where security and compliance meet devops
Thinkstock

As our applications become more complex and the number of systems we manage grows, it’s only natural to worry that the risks to our environments are also increasing. This worry is profoundly felt in industries that must adhere to regulatory compliance standards.

Regulatory frameworks like PCI, HIPAA, FedRAMP, and the forthcoming GDPR mandate rigid security requirements for computing environments, but they introduce a new concern: that they will slow the pace of development for organizations that aren’t equipped to rapidly and effectively validate the compliance of their environments. Even in organizations that don’t need to adhere to any specific regulatory standard, the ability to reliably validate security is no less important, as frequent headlines about vulnerabilities and security breaches are keen to remind us.

To evaluate our readiness to adapt to these challenges, there are a few questions we must ask. Can we accurately determine which servers, in a fleet of thousands, are in need of software patching? Can we validate that the new feature developed adheres to our organization’s security requirements? Can we ensure our environments comply with regulatory standards, even when not actively under audit?

InSpec is an open source testing framework that can help you answer “yes” to all of those questions by providing an easy to understand, yet deeply customizable, framework to define expectations for the systems managed and detect deviations from those expectations wherever they arise.

InSpec provides a great amount of flexibility in how to go about that detection process. For ad hoc point-in-time scans, the InSpec command line utility allows users to evaluate any system reachable over SSH or WinRM. For testing infrastructure changes in development, InSpec can be used natively in Test Kitchen to validate that Chef Cookbooks behave as expected and that the resulting system remains compliant with existing policies. Finally, for continuous compliance evaluation, InSpec can be invoked as part of a Chef Client run via the audit cookbook, which can be configured to send data to a Chef Automate server, providing an aggregated, searchable, and filterable view into the health of the environment.

Below we will walk through a step-by-step guide to using InSpec to define and enforce security standards.

InSpec controls

InSpec code is made up of “controls” that define a single expectation, or group of expectations, for your systems. Here is a simple example:

control ‘sample-1.0’ do
  impact 1.0
  title ‘Hello, World’
  desc ‘You have to start somewhere’
  describe command(‘echo “Hello, World”‘) do
    its(‘exit_status’) { should eq 0 }
    its(‘stdout’) { should cmp /Hello, World/ }
  end
end

Let’s take a look at the “sample-1.0” control and see what makes it tick.

impact 1.0

Every control is rated on its impact, a weighted value from 0 to 1 describing its criticality. Most InSpec profiles will consist of many controls, which allow users to categorize results as minor (0.0 - 0.3), major (0.4 - 0.6), or critical (0.7 - 1.0).

title ‘Hello, World’
desc ‘You have to start somewhere’

The title and desc parameters allow users to define a human-readable title and description for a control. When using the Chef Automate UI, these will be the first elements to show up when selecting a control, with a click-through to the source code in full. This provides an excellent way for different teams to get the context they need about systems, whether they need only high-level information about controls or to dig into the methodologies to craft a remediation.

inspec profile details Chef Software
  describe command(‘echo “Hello, World”‘) do
    its(‘exit_status’) { should eq 0 }
    its(‘stdout’) { should cmp /Hello, World/ }
  end

Now we get to the meat of the control. The InSpec DSL is made up of “resources” that provide a variety of built-in validations for common infrastructure components. Above you see the “command” resource, which tells InSpec to run the command specified—in this case “echo ‘Hello, World’”—and evaluate what it returns.

Each resource contains one or more “matchers,” the definition of an expected result when evaluating the resource. In this case, two expectations are set:

  1. The command’s exit status should be 0
  2. The command’s stdout should contain “Hello, World”

The InSpec executable

Now that we’ve covered the makeup of InSpec controls, how can we start using them? The Chef development kit includes the InSpec command line utility, which will allow us to evaluate our example control. If we take the full text of the control in the previous section and save it to a file called sample_control.rb, we can run it on our local machine with this command:

inspec exec sample_control.rb

Our output should look something like this:

Profile: tests from sample_control.rb
Version: (not specified)
Target:  local://
  ✔  sample-1.0: Hello, World
     ✔  Command echo “Hello, World” exit_status should eq 0
     ✔  Command echo “Hello, World” stdout should eq “Hello, World\n”

Profile Summary: 1 successful, 0 failures, 0 skipped
Test Summary: 2 successful, 0 failures, 0 skipped

The InSpec executable can also scan remote systems, provided they’re available via SSH or WinRM. The syntax for doing so with the above example would look like so:

inspec exec sample_control.rb -t ssh://USER@HOSTNAME -i /path/to/identityfile

or

inspec exec sample_control.rb -t winrm://USER@HOSTNAME -p ‘PASSWORD’

It is worth noting that this example should work on most servers, regardless of operating system. InSpec’s execution doesn’t change from platform to platform. As long as the OS is supported, if the system being scanned behaves as described in the control, the tests will pass and the system will “echo” the expected output, regardless of whether that system is Linux, Windows, MacOS, etc.

In circumstances where differences between servers would necessitate different testing, the InSpec DSL allows users to define conditional execution for controls or resources. To illustrate this, we created a new file, called user_test.rb, containing the following code:

control ‘Test Windows Super User’ do
  impact 0.5
  title ‘Superuser Test’
  desc ‘Make sure the Administrator user exists.’

  only_if do
    os.windows?
  end

  describe user(‘Administrator’) do
    it { should exist }
  end
end

control ‘Test *nix Super User’ do
  impact 0.5
  title ‘Superuser Test’
  desc ‘Make sure the root user exists.’

  only_if do
    os.redhat? || os.debian? || os.linux? || os.darwin? || os.bsd?
  end

  describe user(‘root’) do
    it { should exist }
  end
end

Here, two controls are defined and in each we see a block like this:

only_if do
  os.windows?
end

We’re specifying that this particular control should only be run if the system being evaluated is running Windows. To illustrate, let’s try it out first locally on a Windows server, then remotely on a Linux counterpart. Note that the test for the Linux superuser is skipped on the Windows machine, and vice versa:

inspec tests Chef Software

InSpec profiles

Thus a single file can be used to run controls. However, before committing that file to version control, or uploading it to a Chef Automate server, we’ll need to add it to an InSpec profile. InSpec profiles allow users to organize controls to support versioning and dependency management. To demonstrate, we’ll create an InSpec profile using the same CLI as before:

inspec init profile example_profile

This should return output similar to:

Create new profile at /Users/myuser/example_profile
* Create directory controls
* Create file controls/example.rb
* Create file inspec.yml
* Create directory libraries
* Create file README.md
* Create file libraries/.gitkeep

Creating a profile creates a number of files and directories, but for now, turn your attention to the controls directory.

The example.rb file that our command created provides a few more syntax examples for using InSpec controls, with some helpful comments. However, since we have controls of our own to implement, we can delete example.rb and move our sample_control.rb and user_test.rb files into the controls directory instead:

$ rm example_profile/controls/example.rb
$ mv sample_control.rb example_profile/controls/
$ mv user_test.rb example_profile/controls/
$ ls example_profile/controls/

sample_control.rb    user_test.rb

Now that our controls are in place, we can run InSpec exec again, this time pointed at the path to the profile we created, and it will execute all of the contained profiles:

$ inspec exec example_profile

Profile: InSpec Profile (example_profile)
Version: 0.1.0
Target:  local://

  ✔  sample-1.0: Hello, World
     ✔  Command echo “Hello, World” exit_status should eq 0
     ✔  Command echo “Hello, World” stdout should cmp == /Hello, World/
  ↺  Test Windows Super User: Superuser Test
     ↺  Skipped control due to only_if condition.
  ✔  Test *nix Super User: Superuser Test
     ✔  User root should exist

Profile Summary: 2 successful, 0 failures, 1 skipped
Test Summary: 3 successful, 0 failures, 1 skipped

If we’re making use of a Chef Automate server, we can also run the inspec archive command to compress the profile into a tarball that can be uploaded to our server:

$ inspec archive example_profile
I, [2017-08-29T20:28:44.999748 #81989]  INFO—: Checking profile in example_profile
I, [2017-08-29T20:28:45.000973 #81989]  INFO—: Metadata OK.
I, [2017-08-29T20:28:45.006901 #81989]  INFO—: Found 3 controls.
I, [2017-08-29T20:28:45.007360 #81989]  INFO—: Control definitions OK.
I, [2017-08-29T20:28:45.007868 #81989]  INFO—: Generate archive
Users/myuser/example_profile-0.1.0.tar.gz.
I, [2017-08-29T20:28:45.019701 #81989]  INFO—: Finished archive generation.

Note that when we run inspec archive, InSpec will first check that the metadata and controls in the profile are syntactically valid, then generate a tarball that can be invoked directly with InSpec exec, as above, or uploaded to a Chef Automate server. In either case, InSpec runs controls from all contained files, so we’re seeing both of our tests from the previous section run together in all of these examples.

1 2 Page 1
Page 1 of 2