10. Profile Dependencies (Overlays)
Profile Dependencies (Overlays)
In addition to its own controls, an InSpec profile can leverage controls from one or more other InSpec profiles.
When a profile depends on controls from other profiles, it can be referred to as an "overlay" or "wrapper" profile. We'll use the term overlay profile in this section.
An overlay can include all, select specific, skip some, or modify controls it uses from the profiles it is depending on.
overlay=>operation: my_nginx_overlay
e=>end: my_nginx
e->overlay
To recap, here are the controls that are in the my_nginx
profile:
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.'
describe users.shells(/bash/).usernames do
it { should be_in input('admin_users')}
end
end
Creating the Overlay
Mini Quiz: Do you remember how to initialize a profile?
How to initialize the overlay
inspec init profile my_nginx_overlay
The terminal output should look like the following:
Create new profile at /<pwd>/my_nginx_overlay
* Create directory controls
* Create file controls/example.rb
* Create file inspec.yml
* Create file README.md
In this example, we will rename the example.rb
to overlay.rb
to avoid confusion.
tree my_nginx_overlay
Which should look like:
my_nginx_overlay
βββ README.md
βββ controls
β βββ overlay.rb
βββ inspec.yml
1 directory, 3 files
Defining the Profile Dependency
For a profile to use controls from another profile, the dependency needs to be included in the depends
section of the overlay's inspec.yml
file. For example, you can develop my_nginx_overlay
that uses controls from the my_nginx
profile. In this case, the depends
section of inspec.yml
of my_nginx_overlay
should list the name and location of my_nginx
. One way of declaring the dependency is:
name: my_nginx_overlay
# Metadata
depends:
- name: my_nginx
path: ../my_nginx # {path relative to the overlay}
Once defined in the inspec.yml
file, controls from the included profiles can be used!
More dependency info
Note that a dependency definition can lead to an HTTP/S path or a .git link, just like how inspec exec
can reference profiles that live on the network.
See the docs' Profile Dependencies section for details.
Including All Controls
After defining the dependency in the inspec.yml
of my_nginx_overlay
, controls from my_nginx
are available to be used in the overlay. By using include_controls <profile>
in the overlay.rb
of the overlay profile, all controls from the named profile will be executed every time the overlay is executed. Below you can see an example of an overlay.rb
file in the controls
folder of the overlay.
include_controls 'my_nginx'
In the example above, every time my_nginx_overlay
profile is executed, all the controls from my_nginx
profile are also executed. Therefore, the following controls would be executed for my_nginx_overlay
:
Controls | Executed |
---|---|
nginx-version | β |
nginx-modules | β |
nginx-conf-file | β |
nginx-conf-perms | β |
nginx-shell-access | β |
Skipping a Control
What if you don't want to run one of the controls from the included profile? Luckily, it is not necessary to maintain a slightly-modified copy of the included profile just to delete a control. The skip_control
command tells InSpec not to run a particular control.
include_controls 'my_nginx' do
skip_control 'nginx-conf-perms'
end
In the above example, all controls from my_nginx
profile will be executed, except for control nginx-conf-perms
, every time my_nginx_overlay
is executed. Therefore, the following controls will be executed for my_nginx_overlay
:
Controls | Executed |
---|---|
nginx-version | β |
nginx-modules | β |
nginx-conf-file | β |
nginx-conf-perms | β |
nginx-shell-access | β |
Selectively Including Controls
If there are only a handful of controls that should be executed from an included profile, itβs not necessary to skip all the unneeded controls, or worse, copy/paste those controls bit-for-bit into your profile[1]. Instead, use the require_controls
command.
require_controls 'my_nginx' do
control 'nginx-version'
control 'nginx-modules'
end
Whenever my_nginx_overlay
is executed, it will run only the controls from my_nginx
that are specified in the require_controls
block. In the case, the following controls would be executed:
Controls | Executed |
---|---|
nginx-version | β |
nginx-modules | β |
nginx-conf-file | β |
nginx-conf-perms | β |
nginx-shell-access | β |
Controls nginx-conf-file
, nginx-conf-perms
, and nginx-shell-access
would not be executed, just as if they were manually skipped. This method of including specific controls ensures only the controls specified are executed.
Warning
If new controls are added to a later version of my_nginx
, they would not be executed unless explicitly required in this scenario.
Modifying a Control
Letβs say a particular control from an included profile should still run, but the impact level set in the control isnβt appropriate. When a control is included or required, it can also be modified!
include_controls 'my_nginx' do
control 'nginx-modules' do
impact 0.5
end
end
require_controls 'my_nginx' do
control 'nginx-modules' do
impact 0.5
end
control 'nginx-conf-file'
end
In the above example, all included or required controls from my_nginx
profile are executed. However, should control nginx-modules
fail, it will be raised with an impact of 0.5
instead of the originally-intended impact of 1.0
.
Note
Any fields that you do not explicitly modify in an included control will not be changed from the baseline.
Therefore, you can import a control and only override a single field like the impact
while leaving the actual control code and the rest of the metadata tags untouched.
Additional Examples
Let's poke around a few more examples of inheritance.
Cloud environment overlays
MITRE SAF's Validation library includes several AWS RDS overlays for common databases. Cloud environment deployment often requires an overlay to be written for the underlying technology. For example, suddenly every system has a CSP-default account (e.g. ec2-user
).
Copying and pasting controls from a profile, instead of creating an overlay, can lead to important updates not being reflected. Overlays keep the profile changes in sync as they pull the latest updates. β©οΈ