Creating Content

Directory Structure/Layout

Top Level Structure/Layout

Under the top level directory, there are directories and/or files for different products, shared content, documentation, READMEs, Licenses, build files/configuration, etc.

Important Top Level Directory Descriptions

Directory

Description

linux_os

Contains security content for Linux operating systems. Contains rules, OVAL checks, Ansible tasks, Bash remediations, etc.

applications

Contains security content for applications such as OpenShift or OpenStack. Contains rules, OVAL checks, Ansible tasks, Bash remediations, etc.

shared

Contains templates which can generate, Jinja macros, Bash remediation functions.

tests

Contains the test suite for content validation and testing, contains also unit tests.

build

Can be used to build the content using CMake.

build-scripts

Scripts used by the build system.

cmake

Contains the CMake build configuration files.

Dockerfiles

Contains Dockerfiles to build content test suite container backends.

docs

Contains the User Guide and Developer Guide, manual page template, etc.

ssg

Contains Python ssg module which is used by most of the scripts in this repository.

utils

Miscellaneous scripts used for development but not used by the build system.

The remaining directories such as fedora, rhel7, etc. are product directories.

Important Top Level File Descriptions

File

Description

CMakeLists.txt

Top-level CMake build configuration file

Contributors.md

DO NOT MANUALLY EDIT script-generated file

Contributors.xml

DO NOT MANUALLY EDIT script-generated file

DISCLAIMER

Disclaimer for usage of content

Dockerfile

CentOS7 Docker build file

LICENSE

Content license

README.md

Project README file

Benchmark Structure/Layout

Benchmarks are directories that contain benchmark.yml file. We have multiple benchmarks in our project:

Name

Location

Linux OS

/linux_os/guide

Applications

/applications (Notice no guide subdirectory there!)

Java Runtime Environment

/jre/guide

Fuse 6

/fuse6/guide

Firefox

/firefox/guide

Chromium

/chromium/guide

The Linux OS benchmark describes Linux Operating System in general. This benchmark is used by multiple ComplianceAsCode products, eg. rhel7, fedora, ubuntu1604, sle15 etc. The benchmark is located in /linux_os/guide.

The products specify which benchmark they use as a source of content in their product.yml file using benchmark_root key. For example, rhel7 product specifies that it uses the Linux OS benchmark.

$ cat rhel7/product.yml
product: rhel7
full_name: Red Hat Enterprise Linux 7
type: platform

benchmark_root: "../linux_os/guide"

.....

Rules from multiple locations can be used for a single Benchmark. There is an optional key additional_content_directories for a list of paths to some arbitrary Groups of Rules to be included in the benchmark. Note that the additional directories cannot contain a benchmark file (benchmark.yml), otherwise it fails to build the content. Of all the rules collected only the following would become a part of the benchmark:

  • rules that have the prodtype specified in correspondence with the benchmark;

  • rules that have no prodtype metadata.

.....

benchmark_root: "../applications"
additional_content_directories:
    - "../linux_os/guide/services"
    - "../linux_os/guide/system"

.....

The Benchmarks are organized into directory structure. The directories represent either groups or rules. The group directories contain group.yml and rule directories rule.yml. The name of the group directory is the group ID, without the prefix. Similarly, the name of the rule directory if the rule ID, without the prefix.

For example, the Linux OS Benchmark is structured in this way:

.
├── benchmark.yml
├── intro
│   ├── general-principles
│   ├── group.yml
│   └── how-to-use
├── services
│   ├── apt
│   ├── avahi
│   ├── cron_and_at
│   ├── deprecated
│   ├── dhcp
│   ├── dns
│   ├── ftp
│   ├── group.yml
│   ├── http
│   ├── imap
│   ├── ldap
│   ├── mail
│   ├── nfs_and_rpc
│   .......
│   .......
└── system
    ├── accounts
    ├── auditing
    ├── bootloader-grub2
    ├── bootloader-grub-legacy
    ├── entropy
    ├── group.yml
    ├── logging
......

Product Structure/Layout

When creating a new product, use the guidelines below for the directory layout:

  • Do not use capital letters

  • If product versions are required, use major versions only. For example, rhel7, ubuntu16, etc.

  • If the content to be produced does not matter on versions, do not add version numbers. For example: fedora, firefox, etc.

  • In addition, use only a maxdepth of 3 directories.

  • See the README for more information about the changes needed.

Following these guidelines help with the usability and browsability of using and navigating the content.

For example:

$ tree -d rhel7
rhel7
├── kickstart
├── overlays
├── profiles
└── transforms

7 directories

Product Level Directory Descriptions

Directory

Description

kickstart

Optional Contains product kickstart or build files to be used in testing, development, or production (not recommended) of compliance content.

overlays

Required Contains overlay files for specific standards organizations such as NIST, DISA STIG, PCI-DSS, etc.

profiles

Required Contains profiles that are created and tailored to meet government or commercial compliance standards.

transforms

Required Contains XSLT files and scripts that are used to transform the content into the expected compliance document such as XCCDF, OVAL, Datastream, etc.

Important

For any of the Required directories that may not yet add content, add a .gitkeep file for any empty directories.

Profiles

Profiles define the set of rules and variables aligned to a compliance standard.

Structurally, a profile is a YAML file that represents a dictionary. A profile YAML file has one implied attribute:

  • id: The primary identifier for the profile, to be referenced during evaluations. This is inferred from the file name.

A profile YAML file can, optionally, include metadata about the implemented policy and experts in the field, called Subject Matter Experts (SMEs). The SMEs usually are people familiar with the policy requirements or how it is applied.

  • metadata: Dictionary for profile metadata.

  • reference: URL pointing to page or organization that publishes the policy.

  • version: Version of the policy implemented by the profile.

  • SMEs: List of people experienced with the profile, or how they are applied. The preferred method is the GitHub handle, but email is also accepted.

A profile should define these attributes:

  • title: Human-readable title of the profile.

  • description: Human-readable HTML description, which provides broader context for non-experts than the rationale.

  • extends: The id of a profile to be extended. A profile can make incremental changes based on another profile, via extends attribute. The extendee can then, via the selections attribute, select/unselect rules and change XCCDF Value selectors.

  • selections: List composed of items of these types:

  • id`s of rules to be included in the profile, e.g. `accounts_tmout, or

  • id`s of rules to be excluded from the profile prefixed by an exclamation mark, e.g. `!accounts_tmout, or

  • changes to XCCDF Value selectors, e.g. var_accounts_tmout=10_min, or

  • rule refinements, e.g. accounts_tmout.severity=high.

Controls

The controls adds another layer on top of profiles. Controls files store the metadata for security controls and, more importantly, concentrate the mapping from requirement to rule at a single place.

We will explain the format using the expected workflow and we will show examples of the format.

Storing controls data

When we develop a new SCAP profile, we usually base it off an external standard. Standard documents define a policy that consists of controls (or requirements). For example, the rhel8/profiles/cis.profile profile was based off of the CIS Benchmark for RHEL 8, published as a PDF document by the CIS organization. This document is organized by sections, with each “rule” being a specific piece of guidance. For instance, CIS for RHEL 8 1.1.1.1 “Ensure mounting of cramfs filesystems is disabled” would map to our kernel_module_cramfs_disabled rule. Other benchmarks like NIST 800-53 controls, ANSSI requirements, and STIG have different organization, specific to their benchmark document.

To add the policy to our project repository, we will create a YAML file that represents this policy. There is a special directory, called controls, in the repository root to store these files. These files serve as a database of controls (requirements). They are independent from profiles and products. We can extract the relevant data from the standard document and save them in a YAML file. Presently, control files are created manually. In the future, automatic conversions from XML or OpenControl formats into the YAML control file format could be used.

For example, a YAML control file would look like this:

$ cat controls/abcd.yml

id: abcd
title: ABCD Benchmark for securing Linux systems
version: 1.2.3
source: https://www.abcd.com/linux.pdf
controls:
  - id: R1
    title: User session timeout
    description: |-
      Remote user sessions must be closed after a certain
      period of inactivity.
  - id: R2
    title: Minimization of configuration
    description: |-
      The features configured at the level of launched services
      should be limited to the strict minimum.
  - id: R3
    title: Enabling SELinux targeted Policy
    description: |-
      It is recommended to enable SELinux in enforcing mode
      and to use the targeted policy.

In the real world, controls (requirements) can be nested. For example, PCI-DSS has a tree-like structure, within requirement 2.3, we can find 2.3.a, 2.3.b, etc. Therefore, each item in controls list can contain a controls list.

Once we have the initial file, we can read through the policy requirements and assess each requirement. For each control, we will have to identify whether it can be automated by SCAP. If so, we should look if we already have existing XCCDF rules in our project.

In the example below we identified that:

  • R1 can be automatically scanned by SCAP and we already have 3 existing rules in our repository. However, we want one of them to be selected only on RHEL 9, but the rule is applicable to all platforms.

  • R2 is up to manual checking, but we have systemd_target_multi_user which is related to this control.

  • R3 can be automatically scanned by SCAP but unfortunately we don’t have any rules implemented yet.

For each control we will add the automated key, which describes whether the control requirement can be automated by SCAP and scanning. Possible values are: yes, no, partially. The automated key is just for informational purposes and does not have any impact on the processing.

When XCCDF rules exist, we will assign them to the controls. We will distinguish between XCCDF rules which directly implement the given controls (represented by rules YAML key) and rules that are only related or relevant to the control (represented by related_rules YAML key).

The rules and related_rules keys consist of a list of rule IDs and variable selections.

If a rule needs to be chosen only in some of products despite its prodtype we can use Jinja macros inside the controls file to choose products.

After we finish our analysis, we will insert our findings to the controls file, the file will look like this:

$ cat controls/abcd.yml
 
id: abcd
title: ABCD Benchmark for securing Linux systems
version: 1.2.3
source: https://www.abcd.com/linux.pdf
controls:
  - id: R1
    title: User session timeout
    description: |-
      Remote user sessions must be closed after a certain
      period of inactivity.
    automated: yes
    rules:
    - sshd_set_idle_timeout
    - accounts_tmout
    - var_accounts_tmout=10_min
{{% if product == "rhel9" %}}
    - cockpit_session_timeout
{{% endif %}}
  - id: R2
    title: Minimization of configuration
    description: |-
      The features configured at the level of launched services
      should be limited to the strict minimum.
    automated: no
    note: |-
      This is individual depending on the system workload
      therefore needs to be audited manually.
    related_rules:
       - systemd_target_multi_user
  - id: R3
    title: Enabling SELinux targeted Policy
    description: |-
      It is recommended to enable SELinux in enforcing mode
      and to use the targeted policy.
    automated: yes

Notice that each section identifier is a reference in the standard’s benchmark. Each of the values under the rules key maps onto a rule identifier in the project. In the future, we could automatically assign references to rules via this control file.

Defining levels

Some real world policies, eg. ANSSI, have a concept of levels. This means that for some use cases a certain set of requirements is required and for other use cases a superset of the previous set is required.

Control files can work with the concept of levels.

For example, let’s say that ABCD benchmark would define 2 levels: low and high. The low level would contain R1 and R2. The high level would contain everything from the low level and R3, ie. the high level would contain R1, R2 and R3.

First, add the levels key to the YAML file and list the level names from lowest to highest. The tools working with the controls file assume that every control included in a level are also included in all subsequent levels listed in the levels list.

Second, add level key to every control ID to specify level the control belongs to.

$ cat controls/abcd.yml

id: abcd
title: ABCD Benchmark for securing Linux systems
version: 1.2.3
source: https://www.abcd.com/linux.pdf
levels:
  - low
  - high
controls:
  - id: R1
    level: low
    title: User session timeout
    description: |-
      Remote user sessions must be closed after a certain
      period of inactivity.
  - id: R2
    level: low
    title: Minimization of configuration
    description: |-
      The features configured at the level of launched services
      should be limited to the strict minimum.
  - id: R3
    level: high
    title: Enabling SELinux targeted Policy
    description: |-
      It is recommended to enable SELinux in enforcing mode
      and to use the targeted policy.

Controls file format

This is a complete schema of the YAML file format.

id: policy ID (required key)
title: short title (required key)
source: a link to the original policy, eg. a URL of a PDF document
levels: a list of levels, sorted from lowest to highest
controls: a list of controls (required key)
  - id: control ID (required key)
    title: control title
    description: description of the control in a few sentences
    automated: Can be one of: ["yes", "no", "partially"]. Default value: "yes".
    level: The policy level that the control belongs to.
    notes: a short paragraph of text
    rules: a list of rule IDs that cover this control
    related_rules: a list of related rules
    note: a short paragraph of text
    controls: a nested list of controls

Full example of a controls file:

id: abcd
title: ABCD Benchmark for securing Linux systems
source: https://www.abcd.com/linux.pdf
levels:
  - low
  - high
controls:
  - id: R1
    level: low
    title: User session timeout
    description: >-
      Remote user sessions must be closed after a certain
      period of inactivity.
    automated: yes
    rules:
    - sshd_set_idle_timeout
    - accounts_tmout
    - var_accounts_tmout=10_min
    - configure_crypto_policy
    notes: >-
      Certain period of inactivity is vague.
  - id: R2
    title: Minimization of configuration
    description: >-
      The features configured at the level of launched services
      should be limited to the strict minimum.
    automated: no
    note: >-
      This is individual depending on the system workload
      therefore needs to be audited manually.
    related_rules:
       - systemd_target_multi_user
  - id: R3
    title: Enabling SELinux targeted Policy
    description: >-
      It is recommended to enable SELinux in enforcing mode
      and to use the targeted policy.
    automated: yes
    rules:
      - selinux_state
  - id: R4
    title: Configure authentication
    description: >-
      Ensure authentication methods are functional to prevent
      unauthorized access to the system.
    controls:
      - id: R4.a
        title: Disable administrator accounts
        automated: yes
        level: low
        rules:
          -  accounts_passwords_pam_faillock_deny_root
      - id: R4.b
        title: Enforce password quality standards
        automated: yes
        level: high
        rules:
          - accounts_password_pam_minlen
          - accounts_password_pam_ocredit
          - var_password_pam_ocredit=1

Using controls in profiles

Later, we can use the policy requirements in profile YAML. Let’s say that we will define a “Desktop” profile built from the controls.

To use controls, we add them under selection keys in the profile. The entry has the form policy_id:control_id[:level_id], where level_id is optional.

$ cat rhel8/profiles/abcd-desktop.profile
 
documentation_complete: true
title: ABCD Desktop for Red Hat Enterprise Linux 8
description: |-
  This profile contains configuration checks that align to
  the ABCD benchmark.
selections:
  - abcd:R1
  - abcd:R2
  - abcd:R3
  - security_patches_uptodate

Notice we can mix the controls selections with normal rule selections.

In a similar way, we could define another profile that selects only some of the requirements.

In the example we have selected all controls from controls/abcd.yml by listing them explicitly. It is possible to shorten it using the “all” value which means that all controls will be selected. Let’s show how it will be easier:

$ cat rhel8/profiles/abcd-high.profile
 
documentation_complete: true
title: ABCD High for Red Hat Enterprise Linux 8
description: |-
  This profile contains configuration checks that align to
  the ABCD benchmark.
selections:
  - abcd:all
  - security_patches_uptodate

It is possible to use levels if the levels are defined in the controls file. For example, abcd:all:low selects all rules for the ABCD low level or abcd:all:high selects all rules from the ABCD high level.

Finally, when we build the content, we will automatically get a SCAP profile which contains XCCDF rules and variables from all controls selected in profile YAML.

The build system adds all XCCDF rules listed under rules key in the control to the built profile. The rules listed under related_rules key are not added. Therefore, the related_rules don’t affect the generated source data stream. Also, the selections from selection key in profile file are included.

In our example, the generated profile will contain rules sshd_set_idle_timeout, accounts_tmout, var_accounts_tmout=10_min and security_patches_uptodate. The profile will not contain systemd_target_multi_user even if control R2 is selected because that is listed under related_rules.

The profile will be compiled to a canonical form during the build. The compiled profiles are located in the /build/${PRODUCT_ID}/profiles/ directory.

Example of a compiled profile:

$ cat build/rhel8/profiles/abcd-desktop.profile

documentation_complete: true
title: ABCD Desktop for Red Hat Enterprise Linux 8
description: |-
  This profile contains configuration checks that align to
  the ABCD benchmark.
selections:
  - sshd_set_idle_timeout
  - accounts_tmout
  - var_accounts_tmout=10_min
  - security_patches_uptodate

This profile is similar in content to one we could’ve created manually, but instead is automatically generated during build from a semantic data source. It seamlessly integrates with the build system to include the generated profile in the resulting SCAP source data stream.

Presentation of data

Apart to the build system, the controls files can be also processed by the render-policy.py utility. It creates a HTML file where the controls are resolved in the context of a given product. The file contains links to rule definitions in the upstream repository. The generated file can be distributed to subject matter experts for a review.

$ utils/render-policy.py --output doc.html rhel8 controls/abcd.yml

For more details about the render_policy.py tool, run utils/render-policy.py --help.