Skip to main content

3. Practice the Fundamentals

Aaron LippoldAbout 3 min

Revisiting the NGINX Web Server InSpec Profile

In the beginner class, we wrote and ran an InSpec profile against a test container. We then generated a report on our results and loaded them into Heimdall for analysis. Let's recap this process with some practice.

The Target

InSpec is a framework which is used to validate the security configuration of a certain target. In this case, we are interested in validating that an NGINX server complies with our requirements.

First let's find our nginx container id using the docker ps command:

docker ps

Which will return something like:

CONTAINER ID   IMAGE         COMMAND      CREATED      STATUS     PORTS   NAMES
8bs80z6b5n9s   redhat/ubi8   "/bin/bash"  2 weeks ago  Up 1 hour          redhat8
8ba6b8av5n7s   nginx:latest  "/docker.…"  2 weeks ago  Up 1 hour  80/tcp  nginx

We can then use the container name of our nginx container nginx to target the inspec validation scans at that container.

The Requirements

InSpec profiles are a set of automated tests that relate back to a security requirements benchmark, so the controls are always motivated by the requirements.

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

The Controls

InSpec profiles consist of automated tests, that align to security requirements, written in ruby files inside the controls directory.

Review

If you don't have my_nginx profile, run the following command to initialize your InSpec profile.

inspec init profile my_nginx

Append the inputs sections in your profile at my_nginx/inspec.yml

name: my_nginx
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
supports:
  platform: os

inputs:
  - name: nginx_version
    type: String
    value: 1.10.3

  - name: nginx_modules
    type: Array
    value:
      - http_ssl
      - stream_ssl
      - mail_ssl

  - name: admin_users
    type: Array
    value:
      - admin

Create an inputs file in your profile at inputs-linux.yml

admin_users:
  - admin
  - root

Paste the following controls in your profile at my_nginx/controls/example.rb

control 'nginx-version' do
  impact 1.0
  title 'NGINX version'
  desc 'The required version of NGINX should be installed.'
  describe nginx do
    its('version') { should cmp >= input('nginx_version') }
  end
end

control 'nginx-modules' do
  impact 1.0
  title 'NGINX modules'
  desc 'The required NGINX modules should be installed.'
  required_modules = input('nginx_modules')
  describe nginx do
    required_modules.each do |required_module|
      its('modules') { should include required_module }
    end
  end
end

control 'nginx-conf-file' do
  impact 1.0
  title 'NGINX configuration file'
  desc 'The NGINX config file should exist.'
  describe file('/etc/nginx/nginx.conf') do
    it { should be_file }
  end
end

control 'nginx-conf-perms' do
  impact 1.0
  title 'NGINX configuration permissions'
  desc 'The NGINX config file should owned by root, be writable only by owner, and not writeable or and readable by others.'
  describe file('/etc/nginx/nginx.conf') do
    it { should be_owned_by 'root' }
    it { should be_grouped_into 'root' }
    it { should_not be_readable.by('others') }
    it { should_not be_writable.by('others') }
    it { should_not be_executable.by('others') }
  end
end

control 'nginx-shell-access' do
  impact 1.0
  title 'NGINX shell access'
  desc 'The NGINX shell access should be restricted to admin users.'
  non_admin_users = users.shells(/bash/).usernames
  describe "Shell access for non-admin users" do
    it "should be removed." do
      failure_message = "These non-admin should not have shell access: #{non_admin_users.join(", ")}"
      expect(non_admin_users).to be_in(input('admin_users')), failure_message
    end
  end
end

Running the Controls

To run inspec exec on the target, ensure that you are in the directory that has my_nginx profile.

command
inspec exec my_nginx -t docker://nginx --input-file inputs-linux.yml 

Reporting Results

In the beginner class, we mentioned that you can specify an InSpec reporter to indicate the format in which you desire the results. If you want to read the results on the command line as well as save them in a JSON file, you can run this command.

inspec exec my_nginx -t docker://nginx --input-file inputs-linux.yml --reporter cli json:my_nginx_results.json

Visualizing Results

You can use this output file to upload and visualize your results in Heimdall open in new window.

NGINX Heimdall Report View
NGINX Heimdall Report View