diff --git a/src/courses/beginner/10.md b/src/courses/beginner/10.md index 9d390d5f0..6eb1f1e4d 100644 --- a/src/courses/beginner/10.md +++ b/src/courses/beginner/10.md @@ -10,9 +10,9 @@ headerDepth: 3 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. +When a profile depends on controls from other profiles, it can be referred to as an *overlay profile*. We will call the profiles that the overlay depends on the "baseline" profiles (there is no industry-standard term for this relationship; for example, Chef calls a baseline profile a "to-be-included" profile or a "child" profile in different parts of the InSpec documentation). -An overlay can include all, select specific, skip some, or modify controls it uses from the profiles it is depending on. +An overlay can include all, select specific, skip some, or modify controls it uses from the baseline profiles on which it depends. ```flow overlay=>operation: my_nginx_overlay @@ -21,7 +21,7 @@ e=>end: my_nginx e->overlay ``` -To recap, here are the controls that are in the `my_nginx` profile that will test our five original requirements: +To recap, here are the controls that are in the baseline `my_nginx` profile that will test our five original requirements: ```ruby control 'nginx-version' do @@ -107,17 +107,17 @@ tree my_nginx_overlay ``` Which should look like: ```sh - my_nginx_overlay - ├── README.md - ├── controls - │ └── overlay.rb - └── inspec.yml +my_nginx_overlay +├── README.md +├── controls +│ └── overlay.rb +└── inspec.yml - 1 directory, 3 files +1 directory, 3 files ``` -### Defining the Profile Dependency +### Defining the Baseline 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: +For a profile to use controls from another profile, the dependency needs to be listed in the `depends` section of the overlay's `inspec.yml` file. For example, you can indicate that `my_nginx_overlay` should use controls from the baseline `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: ::: code-tabs @tab my_nginx_overlay/inspec.yml @@ -127,10 +127,12 @@ name: my_nginx_overlay depends: - name: my_nginx - path: my_nginx # {path relative to the overlay} + path: ../my_nginx # {path relative to the overlay} ``` ::: +We use a relative path that includes the shorthand `..` for jumping up a directory (`../my_nginx`) to jump up one level because dependencies are resolved relative to the root of the overlay profile (e.g., inside the `my_nginx_overlay` directory). + Once defined in the `inspec.yml` file, controls from the included profiles can be used! ::: info More dependency info @@ -150,7 +152,7 @@ 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`: +In the example above, every time `my_nginx_overlay` profile is executed, all the controls from the baseline `my_nginx` profile are also executed. Therefore, the following controls would be executed for `my_nginx_overlay`: | Controls | Executed | | ------------- | ------------- | @@ -159,10 +161,11 @@ In the example above, every time `my_nginx_overlay` profile is executed, all the | nginx-conf-file | ✓ | | nginx-conf-perms | ✓ | | nginx-shell-access | ✓ | +| nginx-interview | ✓ | ### 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. +What if you don't want to run one of the controls from the baseline 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. ::: code-tabs @tab my_nginx_overlay/controls/overlay.rb @@ -173,7 +176,7 @@ 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`: +In the above example, all controls from the baseline `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 | | ------------- | ------------- | @@ -182,10 +185,11 @@ In the above example, all controls from `my_nginx` profile will be executed, **e | nginx-conf-file | ✓ | | nginx-conf-perms | ✗ | | nginx-shell-access | ✓ | +| nginx-interview | ✓ | ### 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. Instead, use the `require_controls` command. +If there are only a handful of controls that should be executed from a baseline 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. @@ -211,6 +215,7 @@ Whenever `my_nginx_overlay` is executed, it will run only the controls from `my_ | nginx-conf-file | ✗ | | nginx-conf-perms | ✗ | | nginx-shell-access | ✗ | +| nginx-interview | ✗ | 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. @@ -241,10 +246,9 @@ require_controls 'my_nginx' do 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`. +In the above example, all included or required controls from the baseline `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. @@ -252,6 +256,91 @@ Any fields that you *do not* explicitly modify in an included control will not b 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. ::: +### Adding On Brand-New Controls + +You can even, if you so choose, tack on brand-new controls inside of an InSpec overlay profile. (If you want to add more than a handful of extra controls, it's usually worthwhile to simply create another profile, to ensure that your tests are reuseable in other contexts.) + +::: code-tabs +@tab include_controls +```ruby +include_controls 'my_nginx' do + control 'nginx-modules' do + impact 0.5 + end +end +# note that we do have to define the entire control if we want to make a new one +# this one is NOT defined inside the include_controls block +control 'this-one-is-not-in-the-baseline-profile' do + title 'New control' + desc "Check if nginx has a '/tmp' directory" + impact 0.5 + describe file('/tmp') do + it { should be_directory } + end +end +``` +::: + +You can also simply create more Ruby files in the `controls` directory with control code in them. + +### Overlays and Inputs + +You may be wondering why we have a concept of both inputs and overlays in InSpec. Recall that inputs allow for users to change the *data* that passess to the profile at runtime -- what parameters are present in a codebase -- but do not modify test logic. Overlays, meanwhile, can modify test logic where necessary. Sometimes both use cases are necessary. + +#### What about defining new inputs in `inspec.yml` in the new overlay file? + +An overlay profile is, ultimately, just another profile. You can define new inputs in `my_nginx_overlay/inspec.yml` if you want to, but be careful when redefining an input default that is given in a baseline profile. + +If you define the same input name twice in an overlay and in the baseline, InSpec assumes that you want to use the value given in the `baseline/inspec.yml` file for controls defined in the baseline, and the `overlay/inspec.yml` file for controls defined in the overlay. If you want the overlay's default values to override what was in the `baseline/inspec.yml` file, you have to add a scope attribute to the overlay's `inspec.yml`: + +```ruby +inputs: + - name: nginx_version + value: 1.44.0 + profile: my_nginx +``` +See the [docs](https://docs.chef.io/inspec/profiles/inputs/#profile-inheritance) on profile inheritance for details. + +### Vendoring Overlays + +Since a baseline profile might be in a different network location entirely from the overlay profile, InSpec will cache dependencies locally before executing. This happens implicitly when `inspec exec` runs on any profile that includes the `depends` attribute, but can be done manually by running `inspec vendor `. + +In both cases, an `inspec.lock` file is generated, which tells InSpec where the cache is on the local system so that it can avoid re-doing a network call if the profile runs again. This lockfile can sometimes trip people up if they are working on both the overlay and the baseline at the same time -- the overlay might still be referencing an old copy of the baseline. + +You can force InSpec to re-vendor by running `inspec exec vendor --overwrite`. + +### Wrappers + +You may have noticed that the `depends` attribute in InSpec accepts a list of dependencies. Overlays can also be used as "wrapper" profiles, which include multiple upstream baseline profiles. This is a common use case where a given system is under multiple sets of security requirements. + +For example, consider a virtual machine that is a node in a Kubernetes cluster. To properly validate the security of the cluster, we will need to validate the Kubernetes software itself, which runs on several hosts, as well as the underlying host OS as well. + +We can run a simple wrapper profile that does both of these things in one call to `inspec exec`. + +::: code-tabs +@tab inspec.yml +```yaml +depends: + - name: k8s-node + git: https://github.com/mitre/k8s-node-stig-baseline.git + tag: v1.0.0 + - name: os + git: https://github.com/mitre/canonical-ubuntu-24.04-lts-stig-baseline + tag: v1.1.0 +``` +@tab wrapper.rb +```ruby +include_controls 'k8s-node' +include_controls 'os' +``` +::: + +::: note Chaining Profiles +You can chain overlays. An overlay profile can, itself, be declared as a dependency for another profile. + +We don't recommend doing this outside of niche use cases, since figuring out input inheritance and vendoring becomes very difficult very quickly. +::: + ### Additional Examples Let's poke around a few more examples of inheritance. @@ -263,4 +352,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`). -::: \ No newline at end of file +::: diff --git a/src/resources/README.md b/src/resources/README.md index c9f86f971..82e49cdcc 100644 --- a/src/resources/README.md +++ b/src/resources/README.md @@ -35,7 +35,6 @@ headerDepth: 3 ### STIG resources 1. [Vendor STIG Process Guide](../assets/downloads/U_Vendor_STIG_Process_Guide_V4R1_20220815.pdf) 2. DISA's [Vendor STIG Intent Form](https://dl.dod.cyber.mil/wp-content/uploads/stigs/pdf/U_Vendor_STIG_Intent_Form.pdf). Used to formally start the Vendor STIG process. -3. VMWare's [STIG Program Overview](https://www.vmware.com/content/dam/digitalmarketing/vmware/en/pdf/docs/vmware-stig-program-overview.pdf). A good primer on terms and process for STIGs. ## Code Background & Primers