Skip to main content

4. How to Get Started - InSpec Commands & Docs

Aaron LippoldAbout 7 min

InSpec Commands and Documentation

Before we test our NGINX configuration, let's take a look at the InSpec commands and documentation we can use to write tests.

How to Run InSpec

Use the InSpec executable with the command inspec exec to run a profile against a system. The generic command is below, but take a look at our breakdown on How To Run InSpecopen in new window for the MITRE SAF User course for more information.

inspec exec WHERE_IS_THE_PROFILE -t WHAT_IS_THE_TARGET --more-flags EXTRA_STUFF --reporter WHAT_SHOULD_INSPEC_DO_WITH_THE_RESULTS
Want to try it out?

You can run a sample InSpec command against the nginx target running in the development lab environment using your my_nginx profile. Remember, this profile only has one sample control right now.

Run InSpec
inspec exec my_nginx -t docker://nginx --reporter cli

How to Write InSpec

The inspec exec command is used to run or execute InSpec profiles. Now, let's talk about InSpec's existing documentation and features to support writing those InSpec profiles.

Good InSpec tests will use resources, many of which are already built in, to easily describe some part of the system. Remember the file resource as an example from the previous section. Additionally, tests should use matchers to implement the logic check of an expected result. The previous example used be_directory as a matcher. There are a number of built-in matchersopen in new window: be_in, be_readable, cmp, include, to list a few.

Make Writing InSpec Easier with Built-in Resources

InSpec features dozens of resources and matchers that come predefined in the language. These resources are a core benefit to using InSpec because they allow you to leverage existing stable code to write simple and consistent tests.

When writing profiles, refer back to the documentation for resources. They often give you an example of how to write a test that matches your current problem.

Finding Documentation

The InSpec shell

The InSpec shellopen in new window enables you to explore InSpec interactively. In this part, you'll use the InSpec shell to discover which resources can be used to test your NGINX configuration.

You are not required to use the InSpec shell to develop your profiles, but some users (including this course's instructors) find the InSpec shell to be a useful way to get immediate feedback and explore what's available. You can also use the InSpec shell to debug your tests; the shell lets you write and execute describe blocks in-line.

What is the InSpec Shell?

InSpec shell is based on a tool called pry. pryopen in new window is an interactive debugging environment for Ruby.

Entering the InSpec shell

Run inspec shell to enter the interactive session.

InSpec Shell Command
inspec shell

Run help to see what commands are available.

Command - help
help

Run help resources to see which resources are available.

Command
help resources

You see file and other resources listed.

Using the InSpec Shell

Earlier, we saw this describe block:

describe file('/tmp') do
  it { should be_directory }
end

The InSpec shell understands the structure of blocks. This enables you to run mutiline code. As an example, run the entire describe block like this which will run the entire block of code in the InSpec Shell and return the result.

Command - describe block
describe file('/tmp') do
  it { should be_directory }
end

In practice, you don't typically run controls interactively this way for day to day use, but it is a great way to test out your ideas, find bugs or validate your approach before running a scan in its entirety on a target of evaluation.

What is the difference between InSpec and Ruby?

Inspec is a Domain Specific Language (DSL) on top of Ruby. In other words, InSpec is built on the Ruby programming language. For example, InSpec matchers are implemented as Ruby methods.

Exploring Resources

file example

You can also use the InSpec shell to explore resources, in addition to referencing the resource documentationopen in new window. Here, we can use the InSpec shell to see how the file resource functions.
Run this command to list which methods are available to the file resource.

Command - file resource
file('/tmp').class.superclass.instance_methods(false).sort

You can use the arrow or Page Up and Page Down keys to scroll through the list. When you're done, press Q.

Exploring Resources in the InSpec Shell

Let's use the InSpec shell to explore some resources in InSpec. We will start with one of the most common elements on the system: a directory. In the InSpec Shell call the file.directory? method.

file('/tmp').directory?
inspec> file('/tmp').directory?
        => true

This will return true, since /tmp is a directory on the system and exists on your workstation container.

To make the tests easier to read, the InSpec language uses "syntactic sugar" to turn methods into English-like phrases. For example, the Ruby language contains boolean methods ending in ? which evaluate to true or false (nil is a type of false). InSpec changes the syntax of these methods to include be_ before the method rather than ? after the method to make it more readable. For example, to check if a directory exists, Ruby would traditionally use directory? while InSpec uses be_directory.

Using Ruby Predicate Methods

Given what we have just learned, the best practice in InSpec is to return something that evaluates to true or false.
The ? (or be_ in InSpec) makes your method a Ruby Predicate Method. See Ruby predicate methodsopen in new window to learn more.

nginx example

Now's a good time to define the requirements for our NGINX configuration. Let's say that you require:

1. NGINX version 1.10.3 or later.
2. The following NGINX modules should be installed:
   * `http_ssl`
   * `stream_ssl`
   * `mail_ssl`
3. The NGINX configuration file - `/etc/nginx/nginx.conf`- should exist as a file.
4. The NGINX configuration file should:
   * be owned by the `root` user and group.
   * not be readable, writeable, or executable by others.
5. The NGINX shell access should be restricted to admin users.

In the next section, we will start writing controls for my_nginx profile.

Let's see what resources are available for nginx.

Run help resources a second time to identify InSpec's provided two built-in resources to support NGINX – nginx and nginx_conf.

Command - resources
help resources

Run nginx.methods. This will list all of the available methods for the nginx resource. You can see the version and modules methods. These will be useful to us in the next section.

Command - nginx methods
nginx.methods

Run nginx.version to see what result you get.

Expected Error Ahead

Recall that you're working on your workstation environment, which does not have NGINX installed.

Command - nginx version
nginx.version

We can verify this with Inspec by running the following command:

Command - nginx installed
package('nginx').installed?

As you can see we get false - since nginx is not installed on your runner.

We can instead run InSpec shell commands against the target that does have NGINX installed to see what results we find.

To do so, first start by exiting your InSpec shell session.

exit

Run docker ps to see the running docker containers in your development lab environment that we can test:

Command - docker
docker ps

We can enter the InSpec shell on the nginx container instead of our lab environment host machine that we did before.
Run inspec shell, this time providing the -t argument to connect the shell session to the target container.

Command - shell into container
inspec shell -t docker://nginx

InSpec is agentless

Remember that the target does not have InSpec installed on it. Your shell session exists on the workstation; InSpec routes commands to the target instance over Docker.

Check that the nginx package is intalled, this time on the target container.

Command - package installed
package('nginx').installed?

Now, let's get the version of NGINX that is installed on the target, run: nginx.version. You can see that version 1.23.3 was installed on our container.

Command - version
nginx.version

To complete the example, let's see which modules are enabled on the nginx container. Run nginx.modules to list the installed NGINX modules.

Command - nginx modules
nginx.modules
Looking at the nginx_conf resource

The nginx_confopen in new window resource examines the contents of the NGINX configuration file, /etc/nginx/nginx.conf.

To check whether the NGINX configuration file exists as a file, we want to test attributes of the file itself, so we use the file resource.

Use the file resource to check whether the NGINX configuration file is owned by root and is not readable, writeable, or executable by others. You saw earlier how the file resource provides the readable, writeable, and executable methods. You would also see that the file resource provides the owned_by and grouped_into methods.

Command - file methods
file('/tmp').class.superclass.instance_methods(false).sort

To check whether shell access has been provided to non-admin users, because we want to test attributes of users, you'll use the users resource.

Command - users methods
users.class.superclass.instance_methods(false).sort

Exit the InSpec shell session with the exit command.

exit