Getting Started with InSpec
InSpec Baseline
- InSpec is an open-source framework for testing and auditing your applications and infrastructure.
- Turn Security and Compliance into code
- Translate compliance into code
- Clearly express statements of policy
- Move risk to build/test from runtime
- Find issues early
- Write code quickly
- Run code anywhere
- Inspect machines, data & APIs
InSpec Shell
- An interactive session that enables you to explore InSpec resources and controls on:
- Your local workstation
- A remote target
- A local docker container
- A cloud provider
- To launch the inspec shell session locally:
inspec shell - To launch the session on an instance:
inspec shell -t ssh://user:password@hostinspec shell -t winrm://user:password@hostinspec shell -t docker://container_idinspec shell -t cloud://region-subscription
InSpec Execution
- InSpec enables you to execute controls that you define
- locally, in a control file or profile
inspec exec [control_file_name.rb]inspec exec [profile_directory_name]
- remotely, in a profile stored at a URL
inspec exec [URL]
- remotely, through the Chef Supermarket
inspec supermarket exec [profile_name]
- remotely, through Chef Compliance
inspec compliance exec [profile_name]
- locally, in a control file or profile
- To see list of profiles available on the supermarket
inspec supermarket profiles
- To see list of profiles available on the compliance
inspec compliance profiles
- Some controls may require root privileges, in such situation add the sudo flag while executing the profile
inspec exec [profile] -t [target] --sudo
- To execute multiple profiles:
inspec exec [profile_1_name] [profile_2_name]
Anatomy of Controls
InSpec expresses expectations about the state of the target system through resources expressed as controls.
- resources with defaults, required, optional parameters, and filters.
- individual tests with properties, conditions, matchers, and expected results.
Resources
InSpec resources map platform concepts to code objects
- default value
- required parameters
- optional parameters
- resources with filters
Controls
InSpec controls express expectations about the state of the resources on our system.
- properties
- conditions
- matchers
- expected results
Sample Control
control "tmp-1.0" do # A unique ID for this control
impact 0.7 # The criticality, if this control fails.
title "Create /tmp directory" # A human-readable title
desc "An optional description..."
describe file("/tmp") do # The actual test
it { should be_directory }
end
end
Components of Controls
Controls has
- Properties like:
baseurl, version, status - Conditions like
should, should_not - Matchers like:
exist, eq, be_installed, cmp - Expected results: the result we expect
Create a Profile
InSpec Profiles organize multiple controls into a reusable artifact that can be described and versionsed
- To create a Profile:
inspec init profile [profile_name]- exmaple:
inspec init profile my_profile
- To check if an instance exist
inspec detect -t [target_instance]- example:
inspec detect -t docker://[container_id]
- To execute the profile on the target
inspec exec -t [target_instance] [profile_name]- example:
inspec exec -t docker://[container_id] my_profile
Profile Dependencies
InSpec profiles can bring in the controls from another InSpec profile. These dependencies are maintained in the metadata and captured in the profile’s lockfile.
Steps:
- Let’s consider we have two profiles viz.
base_profileandchild_profilewhich has different controls executed by them. - If you do not have two profiles, create using
inspec init profile [profile_name]for learning purpose. - We would like to execute
child_profilewhich in turns should execute the controls present in thebase_profile. - Add dependency in the metadata of the
child_profilei.e.inspec.ymlas below ```yml depends:- name: base_profile path: ../base_profile ```
- Edit the control file of the
child_profileand add the following lines:include_controls 'base_profile'This will include the controls present in the
base_profile - Execute the
child_profilewhich should execute the controls present in its own control file as well as thebase_profilecontrol file.inspec exec child_profile
Possible reasons of failure for the child_profile not being able to execute controls present in the base_profile
- The
inspec.lockfile is not updated. To do this delete the file and execute the profile again. Other way to do this without deleting theinspec.lockfile is to use--no-create-lockfilewhile executing the profile. Example:inspec exec child_profile --no-create-lockfile - The path to the profile is not set correctly in the
inspec.ymlfile.
Other ways to add depedency of other profile are as follows:
- via git:
```yaml
depends:
- name: base_profile git: https://gitrepo/profile branch: master ```
- via url:
```yaml
depends:
- name: base_profile url: [url] ```
- via supermarket:
```yaml
depends:
- name: base_profile supermarket: origin/profile_name ```
- via compliance:
```yml
depends:
- name: base_profile compliance: origin/profile_name ```
Conditional Execution
InSpec controls can be conditionally executed based on additional requirements expressed through InSpec helpers and language constructs.
Profile Attributes:
- only_if
only_if do command('git').exist? end - if
if os.linux? ... end - describe.one
describe.one do describe file('primary.cfg) do its('content') { ... } end describe file('seconday.cfg) do its('content') { ... } end end - supports
supports: os-family: OSFAMILY
Activity for Conditional Execution
For this activity I have two docker instances running, a CentOS and a Ubuntu image. The Ubuntu instance has git installed, and in the CentOS instance I have created a file hello.txt in the root directory with a content of “Conditional test” inside of it.
- Execution on CentOS
inspec exec conditional_profile -t docker://86b8cd0c449f --no-create-lockfileOutput:
Profile: InSpec Profile (conditional_profile) Version: 0.1.0 Target: docker://86b8cd0c449fca335313a635461801e1a5c82cf58ec5a71e53ecbbda6a856381 ✔ tmp-1.0: Testing Command on Conditional Execution with if ✔ Command: `cat /hello.txt` stdout is expected to match "Conditional Test" ✔ tmp-2.0: Testing Command on Conditional Execution with describe.one ✔ Command: `cat /hello.txt` stdout is expected to match "Conditional Test" ✔ tmp-3.0: Testing Command on Conditional Execution with only_if ✔ Command: `cat /hello.txt` stdout is expected to match "Conditional Test" File /tmp ✔ is expected to be directory Profile Summary: 3 successful controls, 0 control failures, 0 controls skipped Test Summary: 4 successful, 0 failures, 0 skipped - Execution on Ubuntu:
inspec exec conditional_profile -t docker://2d31e0883f76 --no-create-lockfileOutput:
Profile: InSpec Profile (conditional_profile) Version: 0.1.0 Target: docker://2d31e0883f76ba10891bc9ca5f8bad99ad1291b49fa6b84ff728e34bf189869c ✔ tmp-1.0: Testing Command on Conditional Execution with if ✔ Command: `git --version` stdout is expected to match "git version 2.25.1" ✔ tmp-2.0: Testing Command on Conditional Execution with describe.one ✔ Command: `git --version` stdout is expected to match "git version 2.25.1" ↺ tmp-3.0: Testing Command on Conditional Execution with only_if ↺ Skipped control due to only_if condition. File /tmp ✔ is expected to be directory Profile Summary: 2 successful controls, 0 control failures, 1 control skipped Test Summary: 3 successful, 0 failures, 1 skipped - When I change supports os-family to windows in the
inspec.ymlfile and execute on the linux instances i.e. Ubuntu & CentOS in my case. My inspec.yml file looked something like this:name: conditional_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 supports: platform: os os-family: windowsExecute:
inspec exec conditional_profile -t docker://2d31e0883f76 --no-create-lockfileOutput:
Skipping profile: 'conditional_profile' on unsupported platform: 'ubuntu/20.04'. Test Summary: 0 successful, 0 failures, 0 skippedWatch the video series here on Chef Software