diff --git a/src/courses/advanced/03.md b/src/courses/advanced/03.md index db4309a08..ca34cfa12 100644 --- a/src/courses/advanced/03.md +++ b/src/courses/advanced/03.md @@ -33,7 +33,7 @@ InSpec profiles are a set of automated tests that relate back to a security requ ::: details Review -1. NGINX version 1.10.3 or later. +1. NGINX should be installed as version 1.27.0 or later. 2. The following NGINX modules should be installed: * `http_ssl` * `stream_ssl` @@ -74,7 +74,7 @@ supports: inputs: - name: nginx_version type: String - value: 1.10.3 + value: 1.27.0 - name: nginx_modules type: Array @@ -178,7 +178,7 @@ inspec exec my_nginx -t docker://nginx --input-file inputs-linux.yml Target ID: TARGET_ID ✔ nginx-version: NGINX version - ✔ Nginx Environment version is expected to cmp >= "1.10.3" + ✔ Nginx Environment version is expected to cmp >= "1.27.0" ✔ nginx-modules: NGINX modules ✔ Nginx Environment modules is expected to include "http_ssl" ✔ Nginx Environment modules is expected to include "stream_ssl" diff --git a/src/courses/beginner/02.md b/src/courses/beginner/02.md index 46f5d022b..f71258639 100644 --- a/src/courses/beginner/02.md +++ b/src/courses/beginner/02.md @@ -5,7 +5,8 @@ title: 2. What is an InSpec Profile? author: Aaron Lippold --- -### Check-In +## Check-In + ::: details Have you used InSpec before? There is no right or wrong answer. If this is your first time using InSpec, welcome! If you have used it before, welcome back. If you want more information on the basics of Ruby (InSpec is built on Ruby), take a look at the Ruby materials linked on the [home page](../../). ::: @@ -13,20 +14,25 @@ There is no right or wrong answer. If this is your first time using InSpec, welc ::: details Which goal of the MITRE Security Automation Framework (SAF) is supported by InSpec? InSpec is used in the [Validate pillar](https://saf.mitre.org/#/validate). InSpec is used to do security validation. -_Note that InSpec is not the only validation solution, but the one preferred at the SAF. You are not required to use InSpec to use the rest of the SAF nor are you required to use the rest of the SAF to use InSpec._ +_Note that InSpec is not the only validation solution, but the one preferred by the MITRE SAF team. You are not required to use InSpec to use the rest of MITRE SAF's approach to security._ ::: ::: details Is your Codespace environment set up successfully? -This class will have hands-on activities. We recommend that you use the provided class [development lab environment](https://github.com/mitre/saf-training-lab-environment) to participate in the hands-on activities. All you need is to login with a [GitHub](https://github.com/signup) account (which you can make for free), and follow the directions in the [README.md](https://github.com/mitre/saf-training-lab-environment#readme). To verify that your environment is setup, run `./test-lab.sh`. +This class will have hands-on activities. The class contents are written assuming that you are following along using the recommended [development lab environment](https://github.com/mitre/saf-training-lab-environment), which leverages GitHub Codespaces to give you a stable development VM. All you need is to login with a [GitHub](https://github.com/signup) account (which you can make for free), and follow the directions in the [README.md](https://github.com/mitre/saf-training-lab-environment#readme). + +To setup your environment, run `source ./build-lab.sh`. This script will install all of the dependencies we will be using in this class, not the least of which is the executable InSpec binary itself. You can validate that everything is up and running properly by running `./test-lab.sh`. If anything goes wrong or gets misconfigured, you can just run `source ./build-lab.sh` again. ::: ## What is an InSpec Profile? -### InSpec Profiles -InSpec organizes its code into `profiles`. A `profile` is a set of automated tests that usually relates directly back to a Security Requirements Benchmark -- such as a CIS Benchmark or a Defense Information Security Agency (DISA) Security Technical Implementation Guide (STIGs) - and provides an organized structure to articulate that set of requirements using tests in code. +An InSpec profile is a collection of automated tests. In real-world contexts, if we are testing a system for security, we are running tests based off of some security guidance documentation such as a [Center for Internet Security (CIS) Benchmark](https://www.cisecurity.org/cis-benchmarks) or one of the [Defense Information Security Agency (DISA) Security Technical Implementation Guide (STIGs)](https://public.cyber.mil/stigs/). An InSpec profile provides an organized structure to articulate that set of requirements as code. + +If you have gone through the [MITRE SAF Guidance Class](../../courses/guidance/README.md), you may recall that a security guidance document can often be expressed as a structured, machine-readable dataset. We like to think of the human-friendly security guidance documentation and the programmatic validation of that guidance via a testing tool such as InSpec as ultimately two different ways of formatting the same data about security requirements. + +### The Structure of an InSpec Profile -**Profiles have two (2) required elements:** +An InSpec profile has two required elements: - An `inspec.yml` file - A `controls` directory @@ -38,71 +44,84 @@ InSpec organizes its code into `profiles`. A `profile` is a set of automated tes You can learn all the details here: -We will be going over each of these during our class. -### InSpec Profile Structure +We will be discussing the purpose and function of each of these elements during this class. + ```sh -$ tree nginx - nginx - └── profile - ├── README.md - ├── inputs.yml - ├── controls - │ ├── V-2230.rb - │ └── V-2232.rb - ├── files - │ └── services-and-ports.yml - ├── inspec.yml - └── libraries - └── nginx_helper.rb +# Example profile structure +nginx +└── profile + ├── README.md + ├── inputs.yml + ├── controls + │ ├── V-2230.rb + │ └── V-2232.rb + ├── files + │ └── services-and-ports.yml + ├── inspec.yml + └── libraries + └── nginx_helper.rb ``` --- -### InSpec Controls Structure +### The Structure of an InSpec Control + +Let's start off by taking a glance at what a quality InSpec control looks like when complete. This example is taken from the [InSpec profile](https://github.com/mitre/redhat-enterprise-linux-9-stig-baseline) that the MITRE SAF team developed to assess a Red Hat Enterprise Linux 9 operating system against security guidance documentation coming from a STIG - it could just as well have come from an alternative authority such as a CIS Benchmark. + +[redhat-enterprise-linux-9-stig-baseline/controls/SV-257791.rb](https://github.com/mitre/redhat-enterprise-linux-9-stig-baseline/blob/46fd72d144fcda10b07604df037db7f683b74225/controls/SV-257791.rb) -```ruby -control "V-13727" do - title "The worker_processes StartServers directive must be set properly." +```ruby +control 'SV-257791' do + title 'RHEL 9 /boot/grub2/grub.cfg file must be owned by root.' + desc 'The " /boot/grub2/grub.cfg" file stores sensitive system configuration. Protection of this file is critical for system security.' + desc 'check', 'Verify the ownership of the "/boot/grub2/grub.cfg" file with the following command: - desc "These requirements are set to mitigate the effects of several types of - denial of service attacks. Although there is some latitude concerning the - settings themselves, the requirements attempt to provide reasonable limits - for the protection of the web server. If necessary, these limits can be - adjusted to accommodate the operational requirement of a given system." +$ sudo stat -c "%U %n" /boot/grub2/grub.cfg +root /boot/grub2/grub.cfg + +If "/boot/grub2/grub.cfg" file does not have an owner of "root", this is a finding.' + desc 'fix', 'Change the owner of the file /boot/grub2/grub.cfg to root by running the following command: + +$ sudo chown root /boot/grub2/grub.cfg' impact 0.5 - tag "severity": "medium" - tag "gtitle": "WA000-WWA026" - tag "gid": "V-13727" - tag "rid": "SV-36645r2_rule" - tag "stig_id": "WA000-WWA026 A22" - tag "nist": ["CM-6", "Rev_4"] - - tag "check": "To view the worker_processes directive value enter the - following command: - grep ""worker_processes"" on the nginx.conf file and any separate included - configuration files - If the value of ""worker_processes"" is not set to auto or explicitly set, - this is a finding: - worker_processes auto; - worker_processes defines the number of worker processes. The optimal value - depends on many factors including (but not limited to) the number of CPU - cores, the number of hard disk drives that store data, and load pattern. When - one is in doubt, setting it to the number of available CPU cores would be a - good start (the value “auto” will try to autodetect it)." - - tag "fix": "Edit the configuration file and set the value of - ""worker_processes"" to the value of auto or a value of 1 or higher: - worker_processes auto;" - - describe nginx_conf(NGINX_CONF_FILE).params['worker_processes'] do - it { should cmp [['auto']] } + ref 'DPMS Target Red Hat Enterprise Linux 9' + tag check_id: 'C-61532r925358_chk' + tag severity: 'medium' + tag gid: 'V-257791' + tag rid: 'SV-257791r925360_rule' + tag stig_id: 'RHEL-09-212030' + tag gtitle: 'SRG-OS-000480-GPOS-00227' + tag fix_id: 'F-61456r925359_fix' + tag 'documentable' + tag cci: ['CCI-000366'] + tag nist: ['CM-6 b'] + tag 'host' + + only_if('Control not applicable within a container', impact: 0.0) { + !virtualization.system.eql?('docker') + } + + grubfile = input('grub_conf_path') + + describe file(grubfile) do + it { should exist } + it { should be_owned_by 'root' } end end ``` -### InSpec Results +An InSpec control includes specific keywords like `control` and `describe` that define and logically organize tests. It will likely also reference some InSpec [Resources](https://docs.chef.io/inspec/resources/): pre-defined classes that model sometimes rather complex parts of software systems that we want to test. These resources expose easy to use APIs for a variety of different constructs. This control invokes the `file` resource, which allows test authors to ask questions about the attributes of a given file on the filesystem. + +### What happens when I run a profile? + +InSpec controls can be run singularly, but are more commonly run in a group as an entire profile is executed against the system we want to evaluate. The tool can pass output to a variety of destinations - in simple cases, we will direct the output to the terminal's STDOUT, while if we want to save our results to a report, we can pass the report to a visualization tool such as [Heimdall](https://heimdall-lite.mitre.org). We will discuss reporting and visualizing InSpec's output in more detail later. + +::: warning READONLY +InSpec controls when written correctly should not make changes to your software system as you ought to only be validating that a security requirement is met. You should be utilizing a different technology to correct anything found to be non-compliant - these technologies are frequently labelled as "infrastructure as code". +::: + #### Failure diff --git a/src/courses/beginner/03.md b/src/courses/beginner/03.md index e18563e45..aee42d8a3 100644 --- a/src/courses/beginner/03.md +++ b/src/courses/beginner/03.md @@ -1,19 +1,48 @@ --- order: 3 next: 04.md -title: 3. Study an InSpec Profile - NGINX Example +title: 3. Studying an InSpec Profile - NGINX Example author: Aaron Lippold --- ## Studying an InSpec profile -Let's start by creating a profile that will contain NGINX tests. +The best way to learn how an InSpec profile works is to write our own example. Let's start out with a simple profile and start adding more tests to make it useful! -At your terminal, type: +## Our Use Case + +For the purposes of this class, we will assume the role of an infrastructure engineer who is responsible for the security of a NGINX webserver, which is running in a Docker container named `nginx`. We want a way to automate the process of validating the security of our webserver, and we have settled on InSpec as our assessment tool of choice. We have installed InSpec on the host machine for our NGINX container (your GitHub Codespace virtual machine). + +::: note What if I don't know anything about NGINX or webservers? +Don't worry. This class does not expect you to be an expert in webserver administration. The concepts we introduce here are relevant to any category of software, from end user applications all the way down to the operating system running your apps. +::: + +### Our Requirements + +Let's say that we are attempting to validate that our NGINX webserver complies with the following simple list of requirements. + +```sh +1. NGINX should be installed as version 1.27.0 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. +``` + +### Creating the profile + +We'll start by creating a profile that will contain the tests we want to run against NGINX. + +At your terminal, from your preferred directory, type: ::: code-tabs#shell -@tab InSpec Init Command +@tab Command ```bash inspec init profile my_nginx @@ -32,20 +61,26 @@ Creating new profile at /workspaces/saf-training-lab-environment/my_nginx ``` ::: +::: note `my_nginx_answer_key` +You may notice that you already have a folder named `my_nginx_answer_key` in your lab system environment that looks suspiciously like a complete InSpec profile for testing NGINX. + +That profile is what we will be constructing in the next few sections. Feel free to reference it if you want to see what your code is supposed to look like when we are done. +::: + ### Understanding the profile structure Let's take a look at how the profile is structured. We'll start with how a profile's files are structured and then move to what makes up an InSpec control. -First, run `tree` to see what's in the `my_nginx` profile. +First, run `tree` from the directory that contains your `my_nginx` profile to take a look inside. ::: code-tabs#shell -@tab Tree Command +@tab Command ```sh tree my_nginx ``` -@tab Expected Output - my_nginx folder structure +@tab Expected Output ``` my_nginx ├── README.md @@ -58,15 +93,17 @@ my_nginx Here's the role of each component. - `README.md` provides documentation about the profile, including what it covers and how to run it. -- The `controls` directory contains files which implement the InSpec tests. -- `inspec.yml` provides metadata, or information, about the profile. Metadata includes the profile's description, author, copyright, and version. -- You may optionally have a `libraries` directory. The `libraries` directory contains resource extensions. A resource extension enables you to [define your own resource types](https://www.inspec.io/docs/reference/dsl_resource/). The [Advanced Security Automation Developer Class](../advanced/README.md) teaches about defining your own resources. +- The `controls` directory contains files that define the actual tests. +- `inspec.yml` provides metadata about the profile. Metadata includes the profile's human-friendly description, author, version, and what parameters we can pass to the profile at runtime. +- You may optionally have a `libraries` directory. The `libraries` directory contains resource extensions. A resource extension enables you to [define your own resource types](https://docs.chef.io/inspec/profiles/custom_resources/). The [Advanced Security Automation Developer Class](../advanced/README.md) will discuss in-depth how to define your own custom resources. ### Understand a control's structure Let's take a look at the default control file, `controls/example.rb`. ```ruby +# copyright: 2018, The Authors + title 'sample section' # you can also use plain tests @@ -85,21 +122,17 @@ control 'tmp-1.0' do # A unique ID for this control end ``` -::: tip Tip for developing profiles -When creating new profiles use the existing example file as a template -::: - This example shows two tests. Both tests check for the existence of the `/tmp` directory. The second test provides additional information about the test. Let's break down each component. -- `control` (line 9) is followed by the control's name. Each control in a profile has a unique name. -- `impact` (line 10) measures the relative importance of the test and must be a value between 0.0 and 1.0. -- `title` (line 11) defines the control's purpose. -- `desc` (line 12) provides a more complete description of what the control checks for. -- `describe` (lines 13 — 15) defines the test. Here, the test checks for the existence of the `/tmp` directory. +- `control` (line 11) is followed by the control's name. Each control in a profile must have a unique control identifier which is usually derived from the unique identifier of the security requirement being assessed. +- `impact` (line 12) measures the severity, or relative importance, of the test and must be a value between 0.0 and 1.0 where 0.0 is not applicable and 1.0 is of critical importance. +- `title` (line 13) concisely defines the control's purpose. +- `desc` (line 14) provides a more complete description of what the control is assessing. +- `describe` (lines 15 — 17) defines the test. Here, the test checks for the existence of the `/tmp` directory. -In Ruby, the `do` and `end` keywords define a `block`. An InSpec control always contains at least one `describe` block. However, a control can contain many `describe` blocks. +In Ruby, the `do` and `end` keywords define a `block`. An InSpec control always contains at least one `describe` block. However, a control can contain many `describe` blocks as many items might need to be tested to fully assess the requirement. ::: tip More information on a block in Ruby -[Ruby's block syntax](http://ruby-for-beginners.rubymonstas.org/blocks.html) +[Ruby's block syntax](https://ruby-for-beginners.rubymonstas.org/blocks.html) ::: ### Understand a describe block's structure @@ -144,10 +177,10 @@ The `it` statement validates one of your resource's features. A `describe` block Important! Just like in English grammar, pay attention to the difference between the thing (it) and the possessive word (its). `it` is used to describe an action or the expected behavior of the **subject/resource** in question. -e.g. `it { should be_owned_by 'root' }` +e.g. `it { should exist }` `its` is used to specify the expectation(s) of **an attribute of the subject/resource**. -e.g. `its("signal") { should eq "on" }` +e.g. `its('size') { should eq 64 }` ::: --- @@ -165,7 +198,7 @@ e.g. `its("signal") { should eq "on" }` ### Comprehension Check! -Look at the 4 file structures below. Determine which are valid InSpec Profiles! +Look at the 4 file structures below. Determine which are valid InSpec Profiles that will run the tests! ::: code-tabs#shell diff --git a/src/courses/beginner/04.md b/src/courses/beginner/04.md index e60c4d24e..2882188db 100644 --- a/src/courses/beginner/04.md +++ b/src/courses/beginner/04.md @@ -11,18 +11,17 @@ Before we test our NGINX configuration, let's take a look at the InSpec commands ### 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 InSpec](https://mitre.github.io/saf-training/courses/user/06.html) for the MITRE SAF User course for more information. +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 InSpec](../user/06.md) in the MITRE SAF User course for more information. ```sh inspec exec WHERE_IS_THE_PROFILE -t WHAT_IS_THE_TARGET --more-flags EXTRA_STUFF --reporter WHAT_SHOULD_INSPEC_DO_WITH_THE_RESULTS ``` ::: details 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. - +You can run a simple 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. ::: code-tabs#shell -@tab Run InSpec +@tab Command ```bash inspec exec my_nginx -t docker://nginx --reporter cli ``` @@ -30,8 +29,8 @@ inspec exec my_nginx -t docker://nginx --reporter cli ```bash Profile: InSpec Profile (my_nginx) Version: 0.1.0 -Target: docker://31e4ea1be052a9bcc137003e77cbc7f0a4a3149576a2ce65354092424b0d3a89 -Target ID: 761efa53-ee0a-5ea0-a459-b2a5d287b531 +Target: docker://31e4ea1be052a9bcc13700 +Target ID: 761efa53-ee0a-5ea0-a459-b2a5d28 ✔ tmp-1.0: Create /tmp directory ✔ File /tmp is expected to be directory @@ -45,7 +44,7 @@ Test Summary: 2 successful, 0 failures, 0 skipped ::: -### How to Write InSpec +### InSpec References 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. @@ -60,7 +59,7 @@ When writing profiles, refer back to the documentation for resources. They often :::info Finding Documentation - Explore the [InSpec documentation](https://www.inspec.io/docs/) to see which resources and matchers are available, including descriptions of their attributes and examples of their use. - Examine the [source code](https://github.com/inspec/inspec) to see what's available. For example, you can see how `file` and other InSpec resources are implemented. -- Reference examples, such as profiles provided on [Chef Supermarket](https://supermarket.chef.io/tools-directory), as a guide. +- Reference examples, such as profiles provided on [Chef Supermarket](https://supermarket.chef.io/tools-directory) or [developed by the SAF team](https://saf.mitre.org/libs/validate), as a guide. - Explore InSpec interactively using the [InSpec shell](https://www.inspec.io/docs/reference/shell/). ::: @@ -71,7 +70,7 @@ The [InSpec shell](https://www.inspec.io/docs/reference/shell/) enables you to e 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. ::: details What is the InSpec Shell? -InSpec shell is based on a tool called `pry`. [`pry`](https://github.com/pry/pry) is an interactive debugging environment for Ruby. +InSpec shell is based on a tool called `pry`. [`pry`](https://github.com/pry/pry) is an interactive (or 'runtime') debugging environment for Ruby. There are equivalent tools in most programming languages for examining your code mid-execution. ::: ### Entering the InSpec shell @@ -80,12 +79,12 @@ Run `inspec shell` to enter the interactive session. ::: code-tabs#shell -@tab InSpec Shell Command +@tab Command ```sh inspec shell ``` -@tab Output - Enter the InSpec Shell +@tab Output ```sh Welcome to the interactive InSpec Shell To find out how to use it, type: help @@ -96,6 +95,8 @@ You are currently running on: Families: debian, linux, unix, os Release: 20.04 Arch: x86_64 + +inspec> ``` ::: @@ -103,12 +104,12 @@ Run `help` to see what commands are available. ::: code-tabs#shell -@tab Command - help +@tab Command ```sh -help +inspec> help ``` -@tab Output - help +@tab Output ```sh inspec> help You are currently running on: @@ -139,7 +140,7 @@ Run `help resources` to see which resources are available. @tab Command ```sh -help resources +inspec> help resources ``` @tab Output ```sh @@ -175,7 +176,7 @@ describe file('/tmp') do 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. +The InSpec shell understands the structure of blocks. This enables you to run multiline code from the shell terminal - we can run an entire `describe` block by simply copying it and pasting the whole thing into the InSpec shell. ::: code-tabs#shell @@ -203,14 +204,16 @@ Test Summary: 1 successful, 0 failures, 0 skipped ``` ::: -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. +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. :::tip 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. +Inspec is a [Domain Specific Language (DSL)](https://www.jetbrains.com/mps/concepts/domain-specific-languages/) built on top of Ruby. In other words, InSpec is just the Ruby programming language with some extra syntax and keywords tacked on. For example, InSpec matchers are implemented as Ruby methods. This allows you to have the full power of the Ruby programming language at your hands as you write your tests. ::: -### Exploring Resources +### Exploring Resources + #### `file` example + You can also use the InSpec shell to explore resources, in addition to referencing the [resource documentation](https://docs.chef.io/inspec/resources/). 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. @@ -246,10 +249,10 @@ inspec> 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`. +You can use the arrow keys or Page Up and Page Down keys to scroll through the list. When you're done, press `q`. ::: details 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. +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. ```sh @@ -263,7 +266,7 @@ inspec> file('/tmp').directory? 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`. +To make the tests easier to read, the InSpec language uses "syntactic sugar" to turn methods into English-like phrases. For example, the style conventions for the Ruby language say that methods that return booleans (values that evaluate to `true` or `false` where `nil` is a type of false) ought to have names that end in `?`. InSpec automatically changes the syntax of these methods to include `be_` before the method rather than `?` after the method to make it more readable in a `describe` block. For example, to check if a directory exists, Ruby would traditionally use `directory?` while InSpec uses `be_directory`. ::: info Using Ruby Predicate Methods @@ -277,7 +280,7 @@ The `?` (or `be_` in InSpec) makes your method a Ruby Predicate Method. See [Rub Now's a good time to define the requirements for our NGINX configuration. Let's say that you require: ```sh -1. NGINX version 1.10.3 or later. +1. NGINX should be installed as 1.27.0 or later. 2. The following NGINX modules should be installed: * `http_ssl` * `stream_ssl` @@ -289,9 +292,7 @@ Now's a good time to define the requirements for our NGINX configuration. Let's 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`. +In the next section, we will start writing controls for `my_nginx` profile, but first let's see what resources are available to us to model the NGINX webserver. Run `help resources` a second time to identify InSpec's provided two built-in resources to support NGINX – `nginx` and `nginx_conf`. @@ -355,12 +356,6 @@ nginx.methods Run `nginx.version` to see what result you get. -::: warning Expected Error Ahead - -Recall that you're working on your workstation environment, which does not have NGINX installed. - -::: - ::: code-tabs#shell @tab Command - nginx version @@ -376,7 +371,11 @@ from /opt/inspec/embedded/lib/ruby/gems/2.4.0/gems/inspec-2.0.17/lib/resources/n ``` ::: -We can verify this with Inspec by running the following command: +Uh-oh. We got an error. Why? + +Recall that you're running InSpec from a shell running _on your host machine_, which does not have NGINX installed (NGINX _is_ installed inside your Docker container, but we did not tell InSpec that we are trying to examine a container). Therefore, when the `nginx` resource tries to access information about NGINX, it cannot do so and throws an error when it reads a `nil`. + +We can verify this is the issue by invoking the `package` resource to check if we even have NGINX present on this system: ::: code-tabs#shell @@ -387,11 +386,10 @@ package('nginx').installed? @tab Output - nginx installed ```ruby inspec> package('nginx').installed? - - => false +false ``` ::: -As you can see we get `false` - since nginx is not installed on your `runner`. +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. @@ -420,7 +418,7 @@ d4bcef5bb9e3 nginx:latest "/docker-entrypoint.…" 23 seconds ago Up 22 ::: -We can enter the InSpec shell on the nginx container instead of our lab environment host machine that we did before. +We can enter the InSpec shell on the nginx container instead of our lab environment host machine like before. Run `inspec shell`, this time providing the `-t` argument to connect the shell session to the target container. ::: code-tabs#shell @@ -453,7 +451,7 @@ You are currently running on: 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. +Check that the nginx package is installed, this time on the target container. ::: code-tabs#shell @@ -530,12 +528,32 @@ Use the `file` resource to check whether the NGINX configuration file is owned b ::: code-tabs -@tab Command - file methods +@tab Command +```sh +ls file('/tmp') +``` +@tab Output +```sh +Inspec::Resource#methods: + check_supported! check_supports fail_resource inspec inspect ... +Inspec::Resources::FilePermissionsSelector#methods: select_file_perms_style +Inspec::Utils::LinuxMountParser#methods: includes_whitespaces? parse_mount_options +file#methods: + allowed? content_as_json file? inherited? + path setgid? socket? symlink? + ... + content file immutable? mode? + owner selinux_label size version? +instance variables: + @__backend_runner__ @__resource_name__ @file @path @perms_provider ... +``` + +@tab Alternate Command ```sh file('/tmp').class.superclass.instance_methods(false).sort ``` -@tab Output - file methods +@tab Output ```sh => [:allowed?, :directory?, @@ -564,12 +582,30 @@ To check whether shell access has been provided to non-admin users, because we w ::: code-tabs -@tab Command - users methods +@tab Command +```sh +ls users +``` +@tab Output +```sh +Inspec::Resource#methods: + check_supported! fail_resource inspect + ... + inspec resource_id skip_resource +Inspec::Resources::UserManagementSelector#methods: select_user_manager +users#methods: + count disabled disabled? enabled? entries exist? exists? + gids groupnames groups homes maxdays mindays raw_data + ... + @resource_params @resource_skipped @supports @user_provider +``` + +@tab Alternate Command ```sh users.class.superclass.instance_methods(false).sort ``` -@tab Output - users methods +@tab Output ```sh => [:count, :disabled, @@ -598,4 +634,6 @@ Exit the InSpec shell session with the `exit` command. ```sh exit -``` \ No newline at end of file +``` + +At this point we have some experience playing around with InSpec resources to see what they can tell us about system components. Let's take what we've learned and use it to write more structured, repeatable tests. diff --git a/src/courses/beginner/05.md b/src/courses/beginner/05.md index 5d67e6b12..5b0fb7e25 100644 --- a/src/courses/beginner/05.md +++ b/src/courses/beginner/05.md @@ -11,10 +11,10 @@ Let's work through some example requirements to write InSpec controls. ### Security & Configuration Requirements -We write InSpec controls to test some target based on security guidance. Here, let's verify that the NGINX instance had been configured to meet the following requirements: +Review the requirements we're testing for: ```sh -1. NGINX version 1.10.3 or later. +1. NGINX should be installed as version 1.27.0 or later. 2. The following NGINX modules should be installed: * `http_ssl` * `stream_ssl` @@ -28,7 +28,7 @@ We write InSpec controls to test some target based on security guidance. Here, l ### Requirement (1) - NGINX Version -The first requirement is for the NGINX version to be `1.10.3 or later`. +The first requirement is for the NGINX version to be `1.27.0 or later`. We can check this using the InSpec `cmp` matcher. @@ -40,7 +40,7 @@ control 'nginx-version' do title 'NGINX version' desc 'The required version of NGINX should be installed.' describe nginx do - its('version') { should cmp >= '1.10.3' } + its('version') { should cmp >= '1.27.0' } end end ``` @@ -49,7 +49,7 @@ end [`nginx_conf`](https://www.inspec.io/docs/reference/resources/nginx_conf/) ::: -The test has an impact of 1.0, meaning it is most critical. A failure might indicate to the team that this issue should be resolved as soon as possible, likely by upgrading NGINX to a newer version. The test compares `nginx.version` against version 1.10.3. +The test has an impact of 1.0, meaning it is considered a _critical_ issue if this test fails. A failure might indicate to the team that this issue should be resolved as soon as possible, likely by upgrading NGINX to a newer version. The test compares `nginx.version` against version 1.27.0. `cmp` is one of InSpec's [built-in matchers](https://www.inspec.io/docs/reference/matchers/). `cmp` understands version numbers and can use the operators ==, <, <=, >=, and >. `cmp` compares versions by each segment, not as a string. For example, "7.4" is less than than "7.30". @@ -75,7 +75,7 @@ inspec exec /root/my_nginx -t docker://{DOCKER_CONTAINER_ID or DOCKER_CONTAINER_ Target ID: TARGET_ID ✔ nginx-version: NGINX version - ✔ Nginx Environment version should cmp >= "1.10.3" + ✔ Nginx Environment version should cmp >= "1.27.0" Profile Summary: 1 successful control, 0 control failures, 0 controls skipped @@ -84,7 +84,7 @@ inspec exec /root/my_nginx -t docker://{DOCKER_CONTAINER_ID or DOCKER_CONTAINER_ ::: -You see that the test passes. +You see that the test passes. We get back our test results and some metadata about the target we ran it against. The 'Target ID' in the output is the UUID generated by InSpec to uniquely identify the target system/node. ### Requirement (2) - NGINX Modules are Installed @@ -133,7 +133,7 @@ inspec exec /root/my_nginx -t docker://{DOCKER_CONTAINER_ID or DOCKER_CONTAINER_ Target ID: TARGET_ID ✔ nginx-version: NGINX version - ✔ Nginx Environment version should cmp >= "1.10.3" + ✔ Nginx Environment version should cmp >= "1.27.0" ✔ nginx-modules: NGINX version ✔ Nginx Environment modules should include "http_ssl" ✔ Nginx Environment modules should include "stream_ssl" @@ -187,7 +187,7 @@ Target: docker://DOCKER_CONTAINER_ID Target ID: TARGET_ID ✔ nginx-version: NGINX version - ✔ Nginx Environment version is expected to cmp >= "1.10.3" + ✔ Nginx Environment version is expected to cmp >= "1.27.0" ✔ nginx-modules: NGINX modules ✔ Nginx Environment modules is expected to include "http_ssl" ✔ Nginx Environment modules is expected to include "stream_ssl" @@ -227,10 +227,10 @@ end ``` :::tip The `file` resource docs -[`file`](https://www.inspec.io/docs/reference/resources/file/) +[`file`](https://www.inspec.io/docs/reference/resources/file/) You can also [jump back](https://mitre.github.io/saf-training/courses/beginner/03.html#file) in the course for a refresher. ::: -The first 2 tests use `should` to verify the `root` owner and group. The last 3 tests use `should_not` to verify that the file is not readable, writable, or executable by others. +The first two tests use `should` to verify the `root` owner and group. The last three tests use `should_not` to verify that the file is not readable, writable, or executable by others. Run `inspec exec` on the target. @@ -254,7 +254,7 @@ Target: docker://DOCKER_CONTAINER_ID Target ID: TARGET_ID ✔ nginx-version: NGINX version - ✔ Nginx Environment version is expected to cmp >= "1.10.3" + ✔ Nginx Environment version is expected to cmp >= "1.27.0" ✔ nginx-modules: NGINX modules ✔ Nginx Environment modules is expected to include "http_ssl" ✔ Nginx Environment modules is expected to include "stream_ssl" @@ -317,7 +317,7 @@ Target: docker://DOCKER_CONTAINER_ID Target ID: TARGET_ID ✔ nginx-version: NGINX version - ✔ Nginx Environment version is expected to cmp >= "1.10.3" + ✔ Nginx Environment version is expected to cmp >= "1.27.0" ✔ nginx-modules: NGINX modules ✔ Nginx Environment modules is expected to include "http_ssl" ✔ Nginx Environment modules is expected to include "stream_ssl" @@ -345,13 +345,10 @@ Test Summary: 9 successful, 2 failures, 0 skipped ::: -Remember, the first step, detect, is where you identify where the problems are so that you can accurately assess risk and prioritize remediation actions. +### Fixing Problems -For the second step, correct, you can use a configuration management tool or some other automation framework to correct compliance failures for you. +Recall that what we're doing with InSpec falls under [MITRE SAF's Validate pillar](../beginner/README.md), which is where you identify where problems are with your software component, so that you can accurately assess risk and prioritize remediation actions. -You won't correct this issue in this module, but later you can check out the [Integrated Compliance with Chef](https://learn.chef.io/tracks/integrated-compliance#/) track to learn more about how to correct compliance issues using Chef. +For the second step, Hardening (sometimes variously referred to as implementation or remediation) you can use a configuration management tool or some other automation framework to correct compliance failures for you. - -::: details The Target ID in the InSpec exec output -The target_id in the reporter is the UUID generated by train to uniquely identify the target system/node. Reference the [PR that made this update](https://github.com/inspec/inspec/pull/5895) -::: \ No newline at end of file +We're focused on InSpec right now, so we won't correct this issue right now, but you can check out the Chef [Secure your Infrastructure](https://learn.chef.io/tracks/integrated-compliance#/) tutorial course to learn more about how to correct compliance failures using Chef. \ No newline at end of file diff --git a/src/courses/beginner/06.md b/src/courses/beginner/06.md index 8debd199b..ab891795f 100644 --- a/src/courses/beginner/06.md +++ b/src/courses/beginner/06.md @@ -17,7 +17,7 @@ control 'nginx-version' do title 'NGINX version' desc 'The required version of NGINX should be installed.' describe nginx do - its('version') { should cmp >= '1.10.3' } + its('version') { should cmp >= '1.27.0' } end end @@ -64,13 +64,13 @@ control 'nginx-shell-access' do end ``` -Although these rules do what you expect, imagine your control file contains dozens or hundreds of tests. As the data you check for, such as the version or which modules are installed, evolve, it can become tedious to locate and update your tests. You may also find that you repeat the same data across multiple control files. +Although these controls are straightforward, imagine if your `controls` directory contained dozens or _hundreds_ of tests, like we might see for a real security validation profile. Many of these tests will be referring to the same test values over and over again -- what happens when we need to change them? Or if I want to test to a different standard and want to adjust the values the tests are looking for? For example, what happens when a new major release of NGINX happens, and we want to test to make sure our deployed webserver is still up to date with the new latest version? -One way to improve these tests is to use `inputs`. `Inputs` enable you to separate the **logic of your tests** from the **data of your tests**. `Input files` are typically expressed as a `YAML` file (files ending in `.yaml` or `.yml`). +One way to improve these tests is to use `inputs`. Inputs enable you to separate the _logic of your tests_ from the _data of your tests_. Inputs can be provided in-line with your invocation of `inspec exec` but they can also be stored in `input files` and placed under version control. Input files should be in the `YAML` format (files ending in `.yaml` or `.yml`). -`Profile Inputs` exist in your profile's main directory within the `inspec.yml` for global `inputs` to be used across all the controls in your profile. +Profile inputs are defined in your profile's main directory within the `inspec.yml`. These global `inputs` can be used across all the controls in your profile. -Let's create the `inspec.yml` file for our profile: +Let's add an input to the `inspec.yml` file for our profile: ```yaml name: my_nginx @@ -87,10 +87,10 @@ supports: inputs: - name: nginx_version type: String - value: 1.10.3 + value: 1.27.0 ``` -To access an input you will use the input keyword. You can use this anywhere in your control code. +Now that we have defined the `nginx_version` input, we can access it in any control by using the `input` keyword and passing in its name. For example: @@ -105,7 +105,17 @@ control 'nginx-version' do end ``` -For our next control we require specific modules +Since we gave the input a default value of `1.27.0`, that value will be passed down to the control from `inspec.yml` at runtime and nothing about the control's returned results will change from before. + +We have now swapped out a hard-coded value in the control with a parameter. This is the process of _parameterization_. It is a common practice during InSpec profile development, where profile authors will write their controls and then read through them to look for values that are hardcoded that could be easily replaced with an input. + +Keep in mind that these inputs are **constants**. Their values should not and do not change during the execution of the program. If you want the input to have a different value, change your inputs file accordingly before your next run of the profile. + +::: tip +A good rule of thumb is that any check of a numerical value in an InSpec profile should be replaced with an input. +::: + +The next control checks whether certain NGINX modules are installed. Let's make an input to represent an array of modules we want to check for. Example of adding an array object of servers: @@ -147,7 +157,7 @@ supports: inputs: - name: nginx_version type: String - value: 1.10.3 + value: 1.27.0 - name: nginx_modules type: Array @@ -159,7 +169,7 @@ inputs: ::: -Your control can be changed to look like this: +Your control can now be updated to look like this: ```ruby control 'nginx-modules' do @@ -205,7 +215,7 @@ supports: inputs: - name: nginx_version type: String - value: 1.10.3 + value: 1.27.0 - name: nginx_modules type: Array @@ -237,12 +247,12 @@ end ## Input File Example -To change your inputs for platform specific cases you can setup multiple input files. +To change your inputs for platform specific cases you can set up multiple input files. -For example, an NGINX web server could be run on a Windows or Linux machine, which may require different admin users for each context. The inputs can be tailored for each system. You can create the `inputs-windows.yml` and `inputs-linux.yml` files in your home directory. +For example, an NGINX web server could be run on a Windows or Linux machine, which may require different admin users for each context. The inputs can be tailored for each system. You can create a `inputs-windows.yml` *and* `inputs-linux.yml` file in your home directory and only pass the one you actually need to InSpec at runtime. ::: note -Another example is that a production and development environment may require different inputs. +Another example is that production and development environments may require different inputs. ::: ::: code-tabs#shell @@ -277,7 +287,7 @@ supports: inputs: - name: nginx_version type: String - value: 1.10.3 + value: 1.27.0 - name: nginx_modules type: Array @@ -294,35 +304,18 @@ inputs: ::: - - -The following command runs the tests and applies the inputs specified, first, on the Linux system: +The following command runs the tests and applies the inputs specified on the Linux system underlying the nginx application in the container: ```sh inspec exec ./my_nginx -t docker://nginx --input-file inputs-linux.yml ``` -And, on our Windows systems: +If we'd wanted to do this on a hypothetical Windows system, we would need to create a new inputs file (called `inputs-windows.yml` here) and change our target to an nginx instance hosted on a Windows container or virtual machine: ```sh -inspec exec ./my_nginx -t docker://nginx --input-file inputs-windows.yml +inspec exec ./my_nginx -t docker://windows-nginx --input-file inputs-windows.yml ``` ::: details Best Practice - inputs.yml file It is best practice to create a separate file for inputs when using the provided profile. The exception to this is when working with an overlay profile, which you will see in [Section 10](./10.md). - - ::: diff --git a/src/courses/beginner/07.md b/src/courses/beginner/07.md index 2a5b1bacb..68c2a8e0d 100644 --- a/src/courses/beginner/07.md +++ b/src/courses/beginner/07.md @@ -76,7 +76,7 @@ end Well. . . it *sort of* works. -Notice how much output InSpec printed here to answer the simple question of "did this command return empty?" Imagine if we had done this on a directory with many files in it. We'd just be cluttering up the screen (and our report files). +Notice how much output InSpec printed here to answer the simple question of "did this command return empty?" Imagine if we had done this on a directory with many files in it. We'd just be cluttering up the screen (and our report files) with output that is only tangential to what we really want to know, which is that the directory is _not empty_; we don't care what's in it. ::: warning Wait, couldn't we have just used the directory resource for this? Correct. That would have been a much better way of doing this, and illustrates the general principle of "don't use raw shell commands with the `command` resource unless you have to." @@ -84,7 +84,7 @@ Correct. That would have been a much better way of doing this, and illustrates t We're just doing it this way for the example. ::: -If we would like to have a more detailed and cleaner feedback to our user, we can override the standard title of our describe block with a specific message that describes the intent of the test and use the [Explicit Subject](https://relishapp.com/rspec/rspec-core/docs/subject/explicit-subject) to tell inspec what the "subject" is for the test, then, we could refactor the code like this: +If we would like to have a more detailed and cleaner feedback to our user, we can override the standard title of our describe block with a specific message that describes the intent of the test and use the [`subject` keyword](https://rspec.info/features/3-12/rspec-core/subject/explicit-subject/) to tell InSpec what the "subject" is for the test. This allows us to refactor the code like this: ::: code-tabs @@ -250,7 +250,7 @@ Here's another example -- we have an InSpec test that checks if passwords are SH As we said, when possible, and when there is a high change of a large set only having a few offending items, attempt to find only those items that could be outside our requirements. If there are none -- wonderful! We met our requirement. ```ruby -bad_users = inspec.shadow.where { password != "*" && password != "!" && password !~ /\$6\$/ }.users # note that SHA12-encrypted passwords are marked by starting with '$6$' in /etc/shadow +bad_users = inspec.shadow.where { password != "*" && password != "!" && password !~ /\$6\$/ }.users # note that SHA512-encrypted passwords are marked by starting with '$6$' in /etc/shadow describe 'Password hashes in /etc/shadow' do it 'should only contain SHA512 hashes' do diff --git a/src/courses/beginner/08.md b/src/courses/beginner/08.md index f7788253a..514a3cf9b 100644 --- a/src/courses/beginner/08.md +++ b/src/courses/beginner/08.md @@ -11,8 +11,6 @@ headerDepth: 3 ### Try it out! InSpec allows you to send your test results to one or more *reporters*, or output formats. The following command outputs results to both the terminal (`cli`) and to a JSON report file (`json:baseline_output.json`). - ```sh inspec exec my_nginx -t docker://nginx --reporter cli json:baseline_output.json @@ -32,7 +30,7 @@ inspec exec my_nginx --reporter json:nginx-$(date +"%Y-%m-%d-%H-%M-%S").json Here we add a bash `eval` (the `$(date +"%Y-%m-%d-%H-%M-%S")`) to our filename when we invoke `inspec exec`. Now we can run tests multiple times with the same command and get a different filename each time. ::: danger -Note that if you save InSpec results to a file (such as with the `json` reporter), and then re-run the same command, you will overwrite the original contents of that file with the more recent results. Be sure that all of your reports have unique names. +Note that if you save InSpec results to a file (such as with the `json` reporter), and then re-run the same command, you will overwrite the original contents of that file with the more recent results. This is not always what we want to have happen. Be sure that all of your generated reports have unique names! ::: ### Additional Reporter Examples for Reference @@ -86,14 +84,6 @@ inspec exec my_nginx --reporter json junit2:tmp/junit.xml | tee out.json ``` ::: - - - ### Using a configuration file instead of the --reporter flag InSpec also lets you capture all of these reporter options in a configuration file: @@ -139,7 +129,7 @@ When this flag is passed, the control level status outcomes of the profile execu ::: -### Running Profiles from Network Locations +## Running Profiles from Network Locations So far, we have been executing InSpec profiles that we have written ourselves and saved to the local machine. InSpec also gives you the ability to execute a profile that lives on the other end of an HTTP/S link or a `.git` link. @@ -168,5 +158,5 @@ Let's try running an already-complete profile and generating a report. The following command will run the SAF Validation Library's NGINX baseline profile from MITRE GitHub, and use the reporter to output a json file. You will need this JSON file for the next section, where we'll load our results into Heimdall: ```sh -inspec exec https://github.com/mitre/nginx-stigready-baseline -t docker://nginx --reporter cli json:nginx-full-baseline-$(date +"%Y-%m-%d-%H-%M-%S").json +inspec exec https://github.com/mitre/nginx-stigready-baseline -t docker://nginx --reporter cli json:nginx-full-baseline-$(date +"%Y-%m-%d-%H-%M-%S").json --enhanced-outcomes ``` diff --git a/src/courses/beginner/09.md b/src/courses/beginner/09.md index 332c4d848..7b347d31b 100644 --- a/src/courses/beginner/09.md +++ b/src/courses/beginner/09.md @@ -14,7 +14,7 @@ Let's imagine the case where we have been running InSpec scans -- a *lot* of InS ### Heimdall -Take a look at [Heimdall's page](https://saf.mitre.org/apps/heimdall) on the MITRE SAF website for a quick description of what the tool is and what it does. You can also check out the [Heimdall docs](https://saf.mitre.org/docs/heimdall-install). +Take a look at [Heimdall's page](https://saf.mitre.org/apps/heimdall) on the MITRE SAF website for a quick description of what the tool is and what it does. You can also check out the [Heimdall docs](https://saf.mitre.org/docs/heimdall-install) and the SAF [User Course](../user/README.md). We won't get too far into all the cool things Heimdall does here, because this class is focused on InSpec. Suffice to say that Heimdall is our visualization tool for actually understanding InSpec's reports. @@ -29,11 +29,11 @@ No. Heimdall can ingest security data from an ever-growing list of commercial an ### Heimdall Lite -We're going to use Heimdall Lite, the demo version of Heimdall that is freely accesible to the internet, for this part of the class. +We're going to use Heimdall Lite, the demo version of Heimdall that is freely accessible to the internet, for this part of the class. Navigate to the web page for [Heimdall Lite](https://heimdall-lite.mitre.org/). -Click on the `Upload` button and navigate to your json output file that you saved from your previous step. Select that file then click open. +Click on the `Upload` button and navigate to your JSON output file that you saved from your previous step. Select that file then click open. This will allow you to view the InSpec results in the Heimdall viewer. diff --git a/src/courses/beginner/10.md b/src/courses/beginner/10.md index 04efa5b88..4b639ca96 100644 --- a/src/courses/beginner/10.md +++ b/src/courses/beginner/10.md @@ -1,12 +1,12 @@ --- order: 10 next: 11.md -title: 10. Profile Dependencies (Overlays) +title: 10. Profile Dependencies and Overlays author: Aaron Lippold headerDepth: 3 --- -## Profile Dependencies (Overlays) +## Profile Dependencies and Overlays In addition to its own controls, an InSpec profile can leverage controls from one or more other InSpec profiles. @@ -21,7 +21,7 @@ e=>end: my_nginx e->overlay ``` -To recap, here are the controls that are in the `my_nginx` profile: +To recap, here are the controls that are in the `my_nginx` profile that will test our five original requirements: ```ruby control 'nginx-version' do @@ -185,7 +185,11 @@ In the above example, all controls from `my_nginx` profile will be executed, **e ### 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[^first]. Instead, use the `require_controls` command. +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. Instead, use the `require_controls` command. + +:::warning Keep profiles in sync! +Copying and pasting controls from a profile, instead of creating an overlay, can lead to important updates from the upstream profile not being reflected in the new one. Overlays keep the profile changes in sync as they pull the latest updates from upstream before running. +::: ::: code-tabs @tab my_nginx_overlay/controls/overlay.rb @@ -259,6 +263,4 @@ Let's poke around a few more examples of inheritance. ::: note Cloud environment overlays MITRE SAF's [Validation library](https://saf.mitre.org/libs/validation) 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`). -::: - -[^first]: 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. \ No newline at end of file +::: \ No newline at end of file diff --git a/src/courses/beginner/11.md b/src/courses/beginner/11.md index 698ebae84..bef1582e1 100644 --- a/src/courses/beginner/11.md +++ b/src/courses/beginner/11.md @@ -8,42 +8,56 @@ headerDepth: 3 ## From STIG to Profile -You have seen in some of our examples in this class that a robust profile's controls will include a large number of metadata tags: +Recall from our initial example of an InSpec control that a real control will be tied back to an upstream piece of security guidance documentation, like a CIS Benchmark or a STIG. All the context and metadata for that guidance can be added to the control's code via InSpec specific syntax. ::: details InSpec control with many STIG-related tags -Taken from the [SAF RHEL7 profile](https://github.com/mitre/redhat-enterprise-linux-7-stig-baseline/blob/main/controls/SV-204392.rb): - -``` ruby -control 'SV-204392' do - title 'The Red Hat Enterprise Linux operating system must be configured so that the file permissions, ownership, - and group membership of system files and commands match the vendor values.' - desc 'Discretionary access control is weakened if a user or group has access permissions to system files and - directories greater than the default.' - desc 'check', ... - desc 'fix', ... - impact 0.7 - tag legacy: ['V-71849', 'SV-86473'] - tag severity: 'high' - tag gtitle: 'SRG-OS-000257-GPOS-00098' - tag satisfies: ['SRG-OS-000257-GPOS-00098', 'SRG-OS-000278-GPOS-00108'] - tag gid: 'V-204392' - tag rid: 'SV-204392r880752_rule' - tag stig_id: 'RHEL-07-010010' - tag fix_id: 'F-36302r880751_fix' - tag cci: ['CCI-001494', 'CCI-001496', 'CCI-002165', 'CCI-002235'] - tag nist: ['AU-9', 'AU-9 (3)', 'AC-3 (4)', 'AC-6 (10)'] - tag subsystems: ['permissions', 'package', 'rpm'] +[redhat-enterprise-linux-9-stig-baseline/controls/SV-257791.rb](https://github.com/mitre/redhat-enterprise-linux-9-stig-baseline/blob/46fd72d144fcda10b07604df037db7f683b74225/controls/SV-257791.rb) + +```ruby +control 'SV-257791' do + title 'RHEL 9 /boot/grub2/grub.cfg file must be owned by root.' + desc 'The " /boot/grub2/grub.cfg" file stores sensitive system configuration. Protection of this file is critical for system security.' + desc 'check', 'Verify the ownership of the "/boot/grub2/grub.cfg" file with the following command: + +$ sudo stat -c "%U %n" /boot/grub2/grub.cfg + +root /boot/grub2/grub.cfg + +If "/boot/grub2/grub.cfg" file does not have an owner of "root", this is a finding.' + desc 'fix', 'Change the owner of the file /boot/grub2/grub.cfg to root by running the following command: + +$ sudo chown root /boot/grub2/grub.cfg' + impact 0.5 + ref 'DPMS Target Red Hat Enterprise Linux 9' + tag check_id: 'C-61532r925358_chk' + tag severity: 'medium' + tag gid: 'V-257791' + tag rid: 'SV-257791r925360_rule' + tag stig_id: 'RHEL-09-212030' + tag gtitle: 'SRG-OS-000480-GPOS-00227' + tag fix_id: 'F-61456r925359_fix' + tag 'documentable' + tag cci: ['CCI-000366'] + tag nist: ['CM-6 b'] tag 'host' - tag 'container' - describe the_actual_test do # the actual describe block appears on line 54 of this control! - ... + only_if('Control not applicable within a container', impact: 0.0) { + !virtualization.system.eql?('docker') + } + + grubfile = input('grub_conf_path') + + describe file(grubfile) do + it { should exist } + it { should be_owned_by 'root' } end end ``` -We *really* do not want to stuck with labeling all of these controls by hand. Let's cheat and use the [SAF CLI](https://saf-cli.mitre.org) benchmark generator. +See how many tags we're adding? In fact, best practice is to include the complete text of the requirement we are implementing as `desc` or `tag` fields in the control code itself. We have so much metadata in this control file that the actual `describe` block doesn't show up until 34 lines into the control! + +We *really* do not want to stuck with writing all of these tags by hand. Instead, let's use the [SAF CLI](https://saf-cli.mitre.org) benchmark generator. ### Download STIG Requirements diff --git a/src/courses/beginner/12.md b/src/courses/beginner/12.md index dbb49fe8f..b5305a745 100644 --- a/src/courses/beginner/12.md +++ b/src/courses/beginner/12.md @@ -11,12 +11,12 @@ headerDepth: 3 Let's practice writing a few 'real' controls using a security guidance document. #### The Steps to write an InSpec Control -1. **Read the Control** - Go to the control (you made from the stub generator) and read the check text to understand the goal of the control +1. **Read the Control** - Go to the control (you made from the stub generator) and read the check text to understand the goal of the control. 2. **Look for Key Words and Resources** - Look for key words that would indicate what resource to use. If you can't find it, look at the [resources page](https://docs.chef.io/inspec/resources/) and compare back to the control. -3. **Read the documentation** - Look at the documentation for the resource you need and understand the syntax and checkout some examples. +3. **Read the documentation** - Look at the documentation for the resource you need and understand the syntax and check out some examples. 4. **Write the test** 5. **Run the test** -6. **Troubleshoot errors** If you have syntax errors or unexpected results, it's time to troubleshoot. The best first step in troubleshooting is to _read the error message_ from the command line. +6. **Troubleshoot errors** If you have syntax errors or unexpected results, it's time to troubleshoot! The best first step in troubleshooting is to _read the error message_ from the command line. ### Example Control Using `login_defs` Resource: diff --git a/src/courses/beginner/README.md b/src/courses/beginner/README.md index 1176db5b1..785c4dc46 100644 --- a/src/courses/beginner/README.md +++ b/src/courses/beginner/README.md @@ -1,14 +1,14 @@ --- order: 1 next: 02.md -title: Beginner Security Automation Developer Class -shortTitle: Beginner Profile Development +title: 1. Beginner Security Automation Developer Class +shortTitle: 1. Beginner Profile Development author: Aaron Lippold --- ## Class Objectives -The purpose of this class is to give you the fundamentals of how to develop security validation content, particularly through hands-on examples developing InSpec profiles. This class will also show participants how to visualize and analyze their security results. By the end, you should be able to achieve all of the following objectives. +The purpose of this class is to teach the fundamentals of security validation content development via hands-on examples utilizing InSpec profiles. This class will also show participants how to visualize and analyze the security data produced by the InSpec tool using the Heimdall application. ### Objectives: @@ -23,27 +23,34 @@ The purpose of this class is to give you the fundamentals of how to develop secu ## InSpec 101 -- InSpec is an open-source, community-developed compliance validation framework -- Provides a mechanism for defining machine-readable compliance and security requirements +The MITRE SAF team writes quite a bit of InSpec content for real-world use cases. We prefer InSpec for several reasons. + +- Open-source (see the source code on GitHub) +- Strong developer community +- Allows users to define machine-readable compliance and security requirements - Easy to create, validate, and read content - Cross-platform (Windows, Linux, Mac) - Agnostic to other DevOps tools and techniques - Integrates into multiple configuration management tools +::: note Does this mean InSpec is the only validation tool I should ever learn to use? +No. InSpec is powerful, but it cannot accomplish every type of security testing that we will need to do for a complex system. No single tool can do that. +::: + ### The Lab Environment -This class will use GitHub Codespaces for a consistent environment for all students. See instructions for setting up your own lab environment [here](../../resources/05.md). +This class will use GitHub Codespaces for a consistent environment for all students. See instructions for setting up your own lab environment [here](../../resources/02.md). ## The Road to Security Automation InSpec is one of the primary tools in the Security Automation workflow. It integrates easily with orchestration and configuration management tools found in the DevOps world. -As you can see from the picture below, the process for developing automated security tests is a bit of a journey -- we need to start with a human-language requirements documents like SRGs, STIGs or CIS Benchmark and then implement them as code. We need that code to record test results in a standardized format so that we can easily export our security data somewhere people can use it to make decisions (like the Heimdall visualization app, which we will discuss later). +As you can see from the picture below, the process of developing automated security tests is a bit of a journey -- we need to start with human-language requirements documents like SRGs, STIGs, or CIS Benchmark and then implement them as code. We need that code to record test results in a standardized format so that we can easily export our security data somewhere people can use it to make decisions (like the Heimdall visualization app, which we will discuss later). -This challenge is what the [MITRE Security Automation Framework](https://saf.mitre.org) or MITRE SAF was developed to simplify -- to make the journey from a Requirement Document to an automated test profile and back again a little easier to navigate. +This challenge is what the [MITRE SAF (Security Automation Framework)](https://saf.mitre.org) was developed to simplify -- to make the journey from a security guidance document to an automated test profile to a report on security posture easier to navigate. ![The SAF Lifecycle](../../assets/img/saf-lifecycle.png) -We hope that during this class you will become comfortable with the tools, parts and processes involved in the end-to-end process, and gain the confidence to start automating your compliance journey with the information presented here. +We hope that during this class you will become comfortable with the tools, parts, and processes involved in the end-to-end process, and gain the confidence to start automating your compliance journey with the information presented here. Let's get started! diff --git a/src/courses/guidance/02.md b/src/courses/guidance/02.md index e4fbe9d3d..b6c6b06d8 100644 --- a/src/courses/guidance/02.md +++ b/src/courses/guidance/02.md @@ -30,7 +30,7 @@ This class content will be giving heavy focus to STIGs, since Vulcan was origina ### 2.1.1 Organizational Policy vs. Baselines -Many organizations that use popular secrity guidance documents as their baselines have their own specific organizational security policies which conflict with that baseline. For example, consider the following requirement in the STIG for the Red Hat 9 operating system: +Many organizations that use popular security guidance documents as their baselines have their own specific organizational security policies that conflict with that baseline. For example, consider the following requirement in the STIG for the Red Hat 9 operating system: ``` SV-258055 - RHEL 9 must automatically lock the root account until the root account is released by an administrator when three unsuccessful logon attempts occur during a 15-minute time period. @@ -46,7 +46,7 @@ You can check out the [Beginner Security Automation Developer Class](https://mit ## 2.2 Finding Security Guidance Documentation Baselines -Commonly-used security guidance is often available on the open Internet. +Commonly used security guidance is often available on the open Internet. - CIS publishes its popular [Benchmarks](https://www.cisecurity.org/cis-benchmarks) for free with registration (in PDF form, other formats require a subscription to CIS's SecureSuite) - DISA publishes all STIGs (and all the rest of its security documentation materials) for free on the [DOD Cyber Exchange](https://public.cyber.mil/stigs/downloads/) public web page. @@ -77,4 +77,4 @@ Therefore, the Planning capability of the SAF -- dealing with the selection and ![SAF Plan Capability](../../assets/img/SAFPlan.png) -The Plan capability comes first in the list because every other capability needs to refer back to it! \ No newline at end of file +The Plan capability comes first in the list because every other capability needs to refer back to it! diff --git a/src/courses/guidance/05.md b/src/courses/guidance/05.md index 1e8bd99b8..5ba55379d 100644 --- a/src/courses/guidance/05.md +++ b/src/courses/guidance/05.md @@ -8,7 +8,7 @@ headerDepth: 3 ## 5.1 Using Vulcan -Let's walk though the process for building STIG-ready content from the beginning using the Vulcan application. We will use our [demonstration instance of Vulcan](https://vulcan-br-training.herokuapp.com/) that the MITRE SAF team created for this training class (please note that this instance will be regularly reset for each training course). +Let's walk though the process for building STIG-ready content from the beginning using the Vulcan application. We will use our [demonstration instance of Vulcan](https://mitre-vulcan-staging.herokuapp.com/) that the MITRE SAF team created for this training class (please note that this instance may be regularly reset for each training course). ### 5.1.1 Our Use Case @@ -74,4 +74,4 @@ If you have any colleagues taking the class with you, you may want to add them a Reviewers are able to approve requirements written by other members. Depending on how your team operates, you may want to have many authors with one final reviewer role, or you may want to have every member be a reviewer. It's up to you. Only the Admin role can bypass the peer review process to lock (finalize) their own requirements. Try not to dole out the Admin role too often; it's best practice to force all requirements to undergo peer review. -::: \ No newline at end of file +::: diff --git a/src/courses/guidance/12.md b/src/courses/guidance/12.md index 96c7b22ad..e82efca42 100644 --- a/src/courses/guidance/12.md +++ b/src/courses/guidance/12.md @@ -28,6 +28,7 @@ From left to right, those buttons will: - Lock all Component controls from further editing - Export the Component's InSpec code as an archive of an InSpec profile +- Export the Component as an XCCDF XML file - Export the Component as a comma-separated values file (CSV) - Release the Component -- note that this option is not enabled unless all controls have been locked from further editing - Duplicate the Component -- this creates a copy of the Component within the same Project; useful for creating a new release of the same Component without having to change the original @@ -90,4 +91,4 @@ Duplicating a Component automatically generates empty InSpec stubs inside the ne The Diff Viewer's main use is for comparing completed released components. You can compare releases to tell at a glance what has changed in a piece of guidance, which traditionally would require tracking everything in a changelog manually. ::: -At this point, we have gone over most of the processes you would use in Vulcan to develop your own security guidance content. To close, let's review the process for formally publishing a STIG. \ No newline at end of file +At this point, we have gone over most of the processes you would use in Vulcan to develop your own security guidance content. To close, let's review the process for formally publishing a STIG. diff --git a/src/courses/guidance/README.md b/src/courses/guidance/README.md index 2ba3b35e8..a02990ec0 100644 --- a/src/courses/guidance/README.md +++ b/src/courses/guidance/README.md @@ -1,8 +1,8 @@ --- order: 1 next: 02.md -title: Security Guidance Developer Class -shortTitle: Security Guidance Development +title: 1. Security Guidance Developer Class +shortTitle: 1. Security Guidance Development author: Emily Rodriguez headerDepth: 3 --- @@ -17,12 +17,12 @@ By the end of this class, you should be able to achieve all of the following obj - Create tailored security guidance using Vulcan. - Classify security requirements as Applicable - Configurable, Applicable - Inherently Meets, Applicable - Does Not Meet, Not Applicable, or Not Yet Determined for a given software component. - Export security guidance as InSpec stubs to assist in automated security validation. -- Understand how STIG-ready content can be formally peer reviewed by DISA and published to the security community +- Understand how STIG-ready content can be formally peer-reviewed by DISA and published to the security community - Create guidance with Vulcan to support Authority To Operate (ATO) efforts ## 1.2 The Road to Security Automation -As you can see from the picture below, the process for developing automated security tests starts with requirements documents like SRGs, STIGs or CIS Benchmark that are written in regular, human language and then implemented as code. We need that code to record test results in a standardized format so that we can easily export our security data somewhere people can use it to make decisions (like the Heimdall visualization app). +As you can see from the picture below, the process for developing automated security tests starts with requirements documents like SRGs, STIGs, or CIS Benchmarks that are written in regular, human language and then implemented as code. We need that code to record test results in a standardized format so that we can easily export our security data somewhere people can use it to make decisions (like the Heimdall visualization app). This challenge is what the [MITRE Security Automation Framework](https://saf.mitre.org) or MITRE SAF was developed to simplify -- to make the journey from a Requirement Document to an automated test profile and back again a little easier to navigate.