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

1 2 Page 2
Page 2 of 2

InSpec baseline profiles

So far, we’ve written InSpec code, created a profile, and seen examples of ways to scan our systems. But we’ve yet to ask our system anything particularly meaningful. Thankfully, there is a library of prewritten InSpec profiles available on the Chef Supermarket to get baseline security information quickly.

In particular, the Security Baseline profile, which is available for both Linux and Windows, gives a more thorough accounting of how well the systems are configured. Download the compliance profiles from the above links and apply them the same way we did with our example profile. We can also run a remote profile directly by providing a URL instead of a path to our InSpec exec command:

inspec exec https://github.com/dev-sec/linux-baseline/archive/master.tar.gz

That said, InSpec profiles have a feature that provides extra flexibility in handling whatever issues we find: profile dependencies.

Let’s revisit our example_profile, and this time take a look at inspec.yml:

name: example_profile
title: InSpec Profile
maintainer: The Authors
copyright: The Authors
copyright_email: you@example.com
license: Apache-2.0
summary: An InSpec Compliance Profile
version: 0.1.0

The inspec.yml file is where the project metadata is defined. It’s where we can set a profile title, maintainer and license information, and the version that got appended to our tarball when we ran the InSpec archive command. This is also where we can specify dependencies on other profiles. Depending on the system being scanned, we can add either of the profiles called out previously. I’ll use the Linux baseline in this example:

name: example_profile
title: Simple Baseline Security Test
maintainer: The Authors
copyright: The Authors
copyright_email: you@example.com
license: Apache-2.0
summary: An InSpec Compliance Profile
version: 0.2.0
depends:
- name: linux-baseline
  url: https://github.com/dev-sec/linux-baseline/archive/master.tar.gz

The depends items can now be loaded and referenced in controls. To do so, let’s create another new file in our controls directory, baseline.rb, with the following content:

include_controls ‘linux-baseline’

Now if we run our updated profile against a Linux server—or Windows if using the other profile—we’ll see a lot more is going on. I ran it on one of my base images, and as I suspected, I may have some work ahead of me!

Test Summary: 66 successful, 55 failures, 2 skipped

Note that some controls in the baseline profiles will require superuser privileges to execute properly. If you encounter issues, you can use the —sudo flag with InSpec to invoke commands as root without needing to open up direct access for that user.

Using InSpec profile dependencies

Unless we have already been applying stringent security standards to our servers, we’ll likely see at least a few failures after running this profile. It can be scary seeing a big wall of failed tests, but this is a positive thing! We now have a roadmap of the issues we need to evaluate, and each control’s “impact” score can help us determine which issues to address first. Each of our failures becomes a decision point—when a control fails, generally speaking, we’ll want to fix it. We can do so with a tool like Chef, and in the case of the security baseline profiles, there are corresponding “hardening” Chef recipes available for Linux and Windows that can give us a leg up.

That said, when using a third-party profile, we may encounter situations where a failure isn’t actually one we care to fix—every organization is unique, and will have different requirements. We can always just fork an open source profile and modify it directly, but in so doing, we might lose access to future improvements made to that profile. Instead, we can use the dependency behavior we’ve already covered to specify which controls we do or don’t want to include from a profile, without needing to recreate it entirely.

Let’s take a look at an example from the Linux security baseline:

control ‘package-01’ do
  impact 1.0
  title ‘Do not run deprecated inetd or xinetd’
  desc ‘http://www.nsa.gov/ia/_files/os/redhat/rhel5-guide-i731.pdf, Chapter 3.2.1’
  describe package(‘inetd’) do
    it { should_not be_installed }
  end
  describe package(‘xinetd’) do
    it { should_not be_installed }
  end
end

This control, package-01, specifies that the packages inetd and xinetd not be installed on our systems, per the guidelines specified in the NSA document provided in the description. Indeed, this control is failing on my test instance:

  ×  package-01: Do not run deprecated inetd or xinetd (1 failed)
     ✔  System Package inetd should not be installed
     ×  System Package xinetd should not be installed
     expected System Package xinetd not to be installed

Let’s say, for the sake of argument, that our server makes use of xinetd in some fashion, and we don’t wish to uninstall it. Rather than rewrite the Linux security baseline profile outright, we can modify our baseline.rb to selectively omit it like so:

include_controls ‘linux-baseline’ do
  skip_control ‘package-01’
end

Then we can update the version in inspec.yml and re-run our scans. This time, when I run the command against my server, I can see that whereas previously I had 55 failures, now I have 54. If we scroll through the evaluated rules, we can see that our scans skipped right from os-10 to package-02, without us needing to make any other changes!

Test Summary: 65 successful, 54 failures, 2 skipped


×  os-10: CIS: Disable unused filesystems (8 failed)
     ×  File /etc/modprobe.d/dev-sec.conf content should match “install cramfs /bin/true”
     expected nil to match “install cramfs /bin/true”
     ×  File /etc/modprobe.d/dev-sec.conf content should match “install freevxfs /bin/true”
     expected nil to match “install freevxfs /bin/true”
     ×  File /etc/modprobe.d/dev-sec.conf content should match “install jffs2 /bin/true”
     expected nil to match “install jffs2 /bin/true”
     ×  File /etc/modprobe.d/dev-sec.conf content should match “install hfs /bin/true”
     expected nil to match “install hfs /bin/true”
     ×  File /etc/modprobe.d/dev-sec.conf content should match “install hfsplus /bin/true”
     expected nil to match “install hfsplus /bin/true”
     ×  File /etc/modprobe.d/dev-sec.conf content should match “install squashfs /bin/true”
     expected nil to match “install squashfs /bin/true”
     ×  File /etc/modprobe.d/dev-sec.conf content should match “install udf /bin/true”
     expected nil to match “install udf /bin/true”
     ×  File /etc/modprobe.d/dev-sec.conf content should match “install vfat /bin/true”
     expected nil to match “install vfat /bin/true”
  ✔  package-02: Do not install Telnet server
     ✔  System Package telnetd should not be installed

Just as we can selectively omit a control from a profile, we can selectively include a control while skipping the rest. Let’s say that we do want to enforce the package-01 control (i.e., we do not want xinetd installed), but the rest of the rules in the baseline profile don’t apply to our systems. We can selectively include controls by using require_controls instead of include_controls. Let’s update baseline.rb so that our content now reads:

require_controls ‘linux-baseline’ do
  control ‘package-01’
end

Once again, after we update our version, and re-run our profile, we’ll see a very different set of results:

$ inspec exec example_profile-0.2.2.tar.gz -t ssh://USER@192.168.123.45 -i ~/.ssh/id_rsa —sudo

Profile: Simple Baseline Security Test (example_profile)
Version: 0.2.2
Target:  ssh://USER@192.168.123.45:22

  ✔  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: DevSec Linux Security Baseline (linux-baseline)
Version: 2.1.1
Target:  ssh://USER@192.168.123.45:22

  ×  package-01: Do not run deprecated inetd or xinetd (1 failed)
     ✔  System Package inetd should not be installed
     ×  System Package xinetd should not be installed
     expected System Package xinetd not to be installed

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

Now we have only the controls we care about. Success! 

InSpec next steps

Although we’ve covered a lot here, we have only glimpsed the power and flexibility InSpec can bring to environments. To go further with InSpec, you will want to check out the full reference documentation, tutorials, and interactive demonstrations at https://inspec.io. You will likely find these resources helpful as well:  

  • Compliance as Code – learning modules that expand on the skills covered in this article and introduce more of InSpec’s capabilities
  • Local Development and Testing – a step-by-step guide to using InSpec to validate cookbook code in Test Kitchen.
  • Integrated Compliance – a guide to using Chef Automate with InSpec to organize compliance scans into auditable reports in the Automate UI

In this article, we’ve limited our demonstration to the InSpec CLI for simplicity’s sake. If you would like to get hands-on with some of the other ways you can work with InSpec, you’ll find a ton of great resources at Learn Chef Rally.

Nick Rycar is technical product marketing manager at Chef Software

New Tech Forum provides a venue to explore and discuss emerging enterprise technology in unprecedented depth and breadth. The selection is subjective, based on our pick of the technologies we believe to be important and of greatest interest to InfoWorld readers. InfoWorld does not accept marketing collateral for publication and reserves the right to edit all contributed content. Send all inquiries to newtechforum@infoworld.com.

1 2 Page 2
Page 2 of 2