Management InSpec Lead image: Lead Image © Gunnar Pippel, 123RF
Lead Image © Gunnar Pippel, 123RF
 

Automated compliance testing with InSpec

Strictly Managed

Don't equate compliance through certification with security, because compliance and security are not the same. We look at automated compliance testing with InSpec for the secure operation of enterprise IT. By Martin Loschwitz

Compliance is a valid tool for enabling or facilitating secure operation of any type of IT organization, which is what ISO 27001 [1], BSI Base Protection [2], and various other certification bodies claim for their customers. However, corporations often need to implement compliance rules for certification that are contrary to existing business practices. To keep the promises made to the certification authority, regular systems checks are needed. Thus, a corporation needs to verify whether the rules laid down in its statutes are in fact implemented on all relevant systems, as the instructions require. The question is, how can you implement this kind of check?

One way would be to employ admins who do nothing but handle this task, but that would be inefficient; moreover, it would cause a worrying situation in which regular administrators feel they are being watched. Infinitely preferable is automated compliance tests: InSpec to the rescue.

Audits and Tests

Those who have been involved in converting a home-grown system to one in which strict compliance rules are observed knows the pain involved. Whereas previously a laissez-faire atmosphere ruled the day, all of a sudden, a rigid structure with many requirements and conditions regulate the administrator's work, often with far-reaching consequences. The sheer volume of regulations alone can make moving forward difficult. If a quick fix is needed in an emergency, compliance rules often provide for exceptions, but they do need to be replaced by the right solutions looking forward.

InSpec, from the developer of Chef, promises to run compliance tests automatically and regularly on target systems with tests you define in a human-readable language that avoids the need to learn an overly elaborate syntax. InSpec describes itself as a framework for auditing and testing. First and foremost, it's all about acid testing the existing automated system to determine whether the system and the services running on it are configured in line with policies. The slogan is "Compliance as Code."

Having a tool for automated compliance testing come from the same company that also has the Chef automation tool in its portfolio makes a lot of sense. If you run configuration management, you probably do so because you want the same status on all your managed systems. Tools such as Chef, if in doubt, simply write over configuration deviations and thus enforce uniformity, but what about the system components that Chef does not touch? How can you tell whether individual systems have been deliberately, manually manipulated so that they no longer comply with the standard? Chef's response to these questions is InSpec.

InSpec is available under a free license and is free for download from Chef's GitHub directory [3]. It supports a variety of target systems: Linux is represented by CentOS, RHEL, SLES, Debian, Ubuntu, and others. Windows is also covered – InSpec claims to be executable on all relevant and recent Windows versions. The BSD family is represented by FreeBSD, and even more exotic operating systems like AIX, HP-UX, and Solaris can be tested with InSpec. Additionally, InSpec works on Mac OS X. The supported systems are so diverse, almost all applications in the server room can be supported by the current version.

Hello World!

The example in this article is, what else but, the famous "Hello World!" program. To use InSpec, you first have to install it. The developers offer an installation script for RHEL, Ubuntu, Mac OS, and Windows in the form of finished packages that install InSpec on your system. The command in Ubuntu is:

curl https://omnitruck.chef.io/install.sh | sudo bash -s -- -P inspec

If you are working on a different system, either install InSpec via RubyGems.org [4] or download a Docker container in which InSpec is already running.

In theory, the above command also works on Mac OS, but some caution is advisable: Like Chef, InSpec relies on Ruby, but the version of Ruby included in Mac OS is prehistoric. You can remedy this by installing Homebrew [5] and from there procure a current Ruby for your system. If the installation goes well, a simple call to inspec at the command line displays the program help text.

First Test

Similar to Chef Cookbooks, InSpec tests also follow a strict syntax. It is therefore recommended to create a folder first; I named it helloworld in the example. In the next step, create a file named helloworld.txt by opening it in an editor. For the moment, the file should include a single line of text that reads "Helo World!" (retain the typo; read on to discover why).

Then comes the actual InSpec test or, as it is referred to in InSpec-speak, the Specification. Open the helloworld_spec.rb file in a text editor. It makes sense to use an editor that supports syntax highlighting for Ruby. The first test looks like Listing 1. If you are not familiar with Ruby syntax, you will probably not understand every line right away. However, the majority of the instructions can be decrypted with the kind of basic knowledge any administrator has.

Listing 1: Hello World!

01 control "hello-world-1" do
02   impact 1.0
03   title "Hello World"
04   desc "Text should include the words 'hello world'."
05   describe file('helloworld.txt') do
06    its('content') { should match 'Hello World' }
07   end
08 end

Line 1 initiates the specification with the control directive; the character string in quotes is a mechanically processable, unique designation of the test. The impact directive in line 2 establishes a point system to indicate how bad it is if the test in question fails. The points level and the actions resulting from them can be configured individually for InSpec. In this example, 1.0 was chosen arbitrarily.

The title and desc strings give the test a name and description. Both values should be understandable for people without any knowledge of Ruby. Finally, describe in line 5 initiates the condition: In the specific example, InSpec opens the helloworld.txt file and checks whether the string contains the words Hello World.

Call with inspec

A file created in this way can then be passed in directly to inspec as a parameter:

inspec exec helloworld_spec.rb

As expected, the test fails. After all, helloworld.txt has the string Helo World!. InSpec is verbose: It not only notes that the test failed but also uses diff to suggest the changes required to pass the test. After making the corresponding change, the problem disappears and calling inspec again reports no errors.

Command Resource

Granted, the above example is not particularly useful. It only checks the content of a single file and looks to see if a certain string occurs. However, in everyday life, admins are responsible for making sure that certain commands run on their systems to verify compliance values.

InSpec uses a command resource for this kind of task that runs various command-line commands and then lets you examine the output of the commands.

In the example in Listing 2, InSpec checks whether the official GPG keys of the Ubuntu FTP teams (Figure 1) are installed on an Ubuntu system: The control, impact, title, and desc directives work here just as they did in the previous example.

Listing 2: Verify FTP Key Installed

01 control "apt-key-1" do
02   impact 1.0
03   title "Is the Ubuntu FTP archive key installed?"
04   desc "Ubuntu cryptographically signs its package lists to verify they are valid."
05   describe command('apt-key list') do
06    its('stdout') { should match /Ubuntu Archive Automatic Signing Key/ }
07   end
08 end
The test checks to see whether the output of apt-key list also contains Ubuntu Archive Automatic Signing Key.
Figure 1: The test checks to see whether the output of apt-key list also contains Ubuntu Archive Automatic Signing Key.

What is again interesting is the part that follows describe (line 5): Using command, InSpec calls apt-key list to check the output on the program's standard output channel for a specific string. Between the slashes is a regular expression that could also be far more complex. Everything allowed in regular expressions is also possible here.

Yet More Resources

The good news is that the syntax of a single test does not change, no matter what condition you link to the respective test. The even better news is that InSpec provides a large number of complete resources that you can use and that come with their own parameters. Resources can be used as needed within the describe block. The InSpec reference manual provides a list [6].

The apt resource is especially interesting on Ubuntu systems: It helps check whether certain repositories are enabled in /etc/apt/sources.list.d in the Apt configuration. In contrast, the iptables resource checks the local iptables rules and alerts you if the rule specified in the test does not exist.

A parse_config resource lets you interpret configuration formats and look for certain values therein. If a specific parser resource does not already exist, mysql_conf, postgresql_conf, and inetd_conf let you browse the configuration files of the respective services.

The package resource is exciting: It looks to see if a specific package is installed and, if so, in what version. Depending on the desired result, InSpec sounds an alarm. Using user and group, you can check in InSpec whether files have the appropriate permissions. The kernel_module resource lets you check whether a particular kernel module is loaded at a given time.

Incidentally, inside the describe block, the syntax of individual tests is very flexible. Because you are not restricted to a single test, you can specify more its lines in each block. Additionally, the should logic can be inverted with should_not instead of should to specify that the defined criterion should not apply.

If you want the alarm bells to ring if a particular package is installed on the target system, you could add:

describe package('tcpwrapper') do
  it {should_not be_installed}
end

If the mentioned tcpwrapper package were installed, the test would fail, and an error message would be visible.

If you get down to work and check various parameters in the form of InSpec tests, sooner or later you will discover that you have compiled a large set of test files that would be difficult to call individually with inspec exec.

More Profiles

InSpec has a solution for this in the form of a profile function: Many InSpec tests can be combined as a profile and then run together. The highlight of this method is that it is easy to build an InSpec profile (Figure 2) from a collection of InSpec tests. Assuming InSpec is installed, the following command is all you need to get started:

inspec init profile lm-example --overwrite
A simple description for a profile in InSpec that can be executed in the block on servers.
Figure 2: A simple description for a profile in InSpec that can be executed in the block on servers.

The command creates a new profile in the lm-example folder. The --overwrite parameter used here ensures that InSpec overwrites any existing files and must therefore be used wisely.

Two subfolders, controls and libraries, can be found in the folder as well as the README.md and inspec.yml files. First, move your tests to the controls folder. Second, ensure that the name of the file ends in .rb. The _spec string does not need to be part of the file name because InSpec identifies tests without it.

Third, edit the self-explanatory fields in the inspec.yml file. Here, you assign a name, a description of the profile, and a license statement. Once that has happened, the check command below can be used to verify the syntax of the profile and the tests (it needs to be executed from the parent directory). If no errors appear, the profile is then executable with the exec command (Figure 3):

inspec check lm-example
inspec exec lm-example
If you run exec against an InSpec profile on a system, all tests are performed only once.
Figure 3: If you run exec against an InSpec profile on a system, all tests are performed only once.

Incidentally, it makes sense to manage your InSpec profiles in a Git repository. On the one hand, you can use Jenkins for a smart approach to installing the profile (see the "Combining InSpec with Jenkins" box), on the other hand, you have a meaningful version management option.

Connection to Other Servers

The finished InSpec profile raises another question: How is it possible to run the tests included in the profile on other computers – ideally via the SSH protocol? InSpec itself provides a solution: The -t parameter tells InSpec to run the tests on a different host.

The tool expects you to state a complete login parameter for the target (i.e., for an SSH connection, e.g., ssh://mloschwitz@server2). If you prefer to use root instead, you can continue doing so in InSpec. It is only important that the selected user can log on to the other host via SSH and that they can become root on the target system.

If, for security reasons, you do not want to work directly as root, InSpec gives you the option of using sudo: The --sudo and --sudo-password=<secret> parameters ensure that the InSpec user first runs the sudo command to become root before running tests against the remote system.

Tests as per Specification

Up to this point, you will have gathered that InSpec is a powerful tool whose directives are suitable for almost any type of testing. Once you understand InSpec and its basic functionality, you will be asking yourself other questions: What are the parameters for which you as an administrator should now test an existing system? Are there any requirements in terms of compliance that describe a meaningfully configured system? The answer is, as is so often the case, "yes and no." Compliance is a specific matter, but there is a baseline of best practices.

Basically, the following applies: If a company has a compliance certificate, such as the previously mentioned ISO 27001 or the BSI Baseline Protection Certificate, those rules specify the line of approach. You first need to check the requirements defined for your systems if you want to test for compliance with InSpec.

If you do not have such requirements or are interested in additional sources of knowledge, you can pick up various "benchmarks" from the Center for Internet Security (CIS). Using Ubuntu 4.16 [7] as an example, the experts establish rules that harden the system. Contrary to what the name suggests, the authors of the document have not tested Ubuntu or other distributions for implicit security. In this case "benchmark" means that you can implement the rules described in the document, and the total number of criteria will ultimately give you a score for the respective system. However, not all of the criteria in the book have an effect on the score.

Unfortunately, you will find only a few prebuilt compliance testing collections for InSpec online that implement, for example, the CIS recommendations. If you want to check your Ubuntu systems for compliance according to CIS recommendations, you need to package the tests from the CIS benchmark that are relevant for your operations as InSpec tests.

If you don't have much time, you will hardly be able to implement tests for the entire CIS document for Ubuntu (Figure 5): It has more than 300 pages of various tests, and it documents the results expected and the justification for those results.

The Center for Internet Security provides benchmarks that illustrate how Linux systems can be hardened.
Figure 5: The Center for Internet Security provides benchmarks that illustrate how Linux systems can be hardened.

Conclusions

InSpec is impressive on several levels. Installing and handling the tool is very easy. Chef keeps its promise that administrators can use InSpec without the need for major programming experience. Although the code is in Ruby, creating tests is not difficult and does not require any in-depth knowledge of Ruby. A further advantage is its easy-to-understand syntax: Meaningful function names help.

If you have to deal with compliance as a manager in your company, InSpec is perfect because it is a standalone tool that can be operated without the Chef automation tool in environments where a competing tool is already in place (e.g., Ansible or Puppet). The option to pair InSpec with automation frameworks, such as Jenkins, is another huge benefit. If you want to automate compliance testing, you should take a very close look at InSpec.

If you already use the Chef tool, you can use InSpec as part of an audit cookbook. Attributes and parameters can be used to transfer additional values to InSpec, which it then references for operations. The InSpec documentation [8] is well worth reading and contains more details on this topic.