Skip to content

Commit

Permalink
Add a section guiding through the process of rule divergence
Browse files Browse the repository at this point in the history
  • Loading branch information
matejak committed Jun 27, 2023
1 parent c006f0e commit 6213898
Showing 1 changed file with 226 additions and 0 deletions.
226 changes: 226 additions & 0 deletions docs/manual/developer/03_creating_content.md
Original file line number Diff line number Diff line change
Expand Up @@ -1301,3 +1301,229 @@ Each rule in the benchmark in the `/linux_os/guide` directory must be a member o
Products specify a path to the directory with component files by the `components_root` key in the `product.yml`.

The mappings of components to rules can be converted to HTML by the `utils/render_components.py` script.


## Preventing problems

This section outlines some situations that are uncontrollably recurring, and that could cause problems in the project if they are handled impulsively.
Following subsections aim to provide a helping hand to navigate through the risks consciously, and to prevent taking of completely wrong decisions.

### Handling rule updates

Handling rule updates and changes is an essential aspect of maintaining a project's rules and ensuring their relevance and effectiveness along the time.
As products evolve and components undergo modifications, it becomes necessary to update the rules that configure them within the project.
These updates may involve addressing divergences in rule behavior, which can arise due to differences between products, versions, or architectures.

In this document, we will explore the approaches and considerations involved in handling rule divergences, including the use of conditionals and spawning new rules.

By effectively managing these updates, we can ensure that the project's rules remain up to date and aligned with the evolving landscape of supported products.

#### Updates and changes

By design it is expected that the rules in the project will be shared and used by the supported products. And during the lifespan of a product a component may change and require that one or more rules be updated.

When a component supported by CaC undergoes changes, it is essential to update and align the rules configuring it in the project accordingly. This is necessary to keep the rules in the project up to date and relevant.

But some changes may not apply to all products, sometimes a change is specific to a linux distro, or a specific minor version or architecture of that distro. In these situations the behavior of the rule needs to be different for a product or one of its versions. The rule behavior needs to diverge according to the product and version.

A rule can diverge in two ways:

- Cross-product wise
- In-product wise

A *cross-product divergence* is a difference in rule behavior stemming from product differences.

In other words, a rule that configures a different key, or value, in different products.

These divergences are the most common in the project since we support a wide range of products, examples of these kinds of divergences are:

- Package names that differ between products. e.g.: In rule `package_audit_installed`, some products have the "audit" package and others have the "auditd" package.
- File paths that differ between products. e.g.: The faillock directory, some products use `/var/run/faillock` and others use `/var/log/faillock`

These divergences are handled in the content

- Jinja conditionals (e.g.: [{{{% if product in ... }}}](/~https://github.com/ComplianceAsCode/content/blob/328eac5d78ee756d158c389a91633f5dd74a5d60/linux_os/guide/system/software/integrity/fips/enable_fips_mode/rule.yml#L8)) - commonly used in rule descriptions and remediations.
- Product identifiers (e.g.: [attribute@ubuntu1604](/~https://github.com/ComplianceAsCode/content/blob/328eac5d78ee756d158c389a91633f5dd74a5d60/linux_os/guide/system/auditing/package_audit_installed/rule.yml#LL62C9-L62C9)) - commonly used in templated rules and when defining references.
- Product properties (in [product.yml](/~https://github.com/ComplianceAsCode/content/blob/328eac5d78ee756d158c389a91633f5dd74a5d60/products/rhel8/product.yml#LL32C35-L32C35) file or `product_properties` directory) - useful for more generic properties, applicable to different rules.
- Product-specific files (e.g.: [sle12.yml](/~https://github.com/ComplianceAsCode/content/blob/master/linux_os/guide/system/auditing/auditd_configure_rules/audit_privileged_commands/audit_rules_privileged_commands_kmod/ansible/sle12.yml)) - Less common option which is usually used when the differences are drastic and it is not worth using the other options.

An *in-product divergence* is a difference in rule behavior stemming from component changes between a product’s minor versions or supported architectures.

In other words, a rule that configures a different key, or value, in different versions of the same product from the standpoint of ComplianceAsCode, typically because

- a component changed between minor product versions, or
- behaves differently on different architectures.

These divergences emerge as the result of continued support of a minor version, in other words, once a new minor version of a product is released the previous version doesn’t go End Of Life immediately and needs support.

Examples of in-product divergences are:

- Configuration of [SSHD Compression](/~https://github.com/ComplianceAsCode/content/blob/328eac5d78ee756d158c389a91633f5dd74a5d60/linux_os/guide/services/ssh/ssh_server/sshd_disable_compression/rule.yml#L52), which makes sense only on rhel < 7.4
- Configuration of [systemd's StopIdleSessionSec](/~https://github.com/ComplianceAsCode/content/blob/328eac5d78ee756d158c389a91633f5dd74a5d60/linux_os/guide/system/accounts/accounts-physical/logind_session_timeout/rule.yml#L22), which are available on rhel >= 8.7 and rhel > 9.0
- Different configurations for different architectures. e.g. [audit_access_failed](/~https://github.com/ComplianceAsCode/content/blob/328eac5d78ee756d158c389a91633f5dd74a5d60/linux_os/guide/system/auditing/policy_rules/audit_access_failed/rule.yml#L34), [audit_access_failed_aarch64](/~https://github.com/ComplianceAsCode/content/blob/328eac5d78ee756d158c389a91633f5dd74a5d60/linux_os/guide/system/auditing/policy_rules/audit_access_failed_aarch64/rule.yml#L31) and [audit_access_failed_ppc64le](/~https://github.com/ComplianceAsCode/content/blob/328eac5d78ee756d158c389a91633f5dd74a5d60/linux_os/guide/system/auditing/policy_rules/audit_access_failed_ppc64le/rule.yml#L29) configure different audit rules in each architecture.

Note: The fist two examples above didn't require a new rule to be spawned because they are only limiting the applicability to specific minor versions.
The divergence is the absence of configuration in the complementary set of minor versions.
The third example differs in one single syscall (`open`) not present in aarch64 and ppc64le but present in x86.
In that case there is no mechanism currently available to dynamically update the rule description or the rule parameters, limiting the options to rule spawning.

### Approaches how to handle rule divergence

Basically rule’s divergence can be handled in two ways: Either by

- expanding the affected rule, typically by adding a conditional; or by
- spawning a new rule.

Expansion of a rule allows it to handle the divergence, whereas spawning creates a new rule according to the new behavior, resulting in two separate single-purpose rules.
Each approach has its benefits and drawbacks that need to be evaluated, and they are discussed in the next section.


#### Adding conditionals to a rule

One way to handle divergences in a rule is to add conditionals to it.
The conditionals need to be explained in the rule description and added to the checks and remediations, so that each divergence is identified and checked correctly.
In general, the remediation process should be capable of identifying all compliant states, ensuring idempotence, while also favoring a specific approach in cases where the system is incompliant.

If the divergence is cross-product, the conditionals can be handled at build-time through the techniques mentioned previously.
An in-product divergence will require the use of conditionals that can be evaluated at scan-time.

#### Spawning a new rule

Another way to handle divergences is to create rules that will handle the newly required behavior.
Each rule will describe what they check for and remediate, they will be pretty similar but different regarding their specific divergence.

If the divergence is cross-product, the rule only needs to have the appropriate prototype.
If the divergence is in-product, the rules will need to have disjoint applicabilities in their platforms.

### Aspects to consider when picking one approach

There is no direct guidance on how to handle every case of divergence.
Evaluate the following aspects against the rule update you are dealing, and pick your poison.

#### Granularity and reusability

##### Conditionals

Pros

- Expected behavior of the rule is maintained, no expectation is violated.
- Rules may already be expected to handle different conditions, now it’s only one more such condition.
- Lower granularity means less rules in the project without compromising on the abilities of the project.

Cons

- Profiles or policies that allow or require only one of the behaviors don’t align well with polymorphic rules.

##### Spawning

Pros

- The original rule and the spawned one are clear and direct about what they do.
- The increased rule granularity promotes reusability, making such rules great blocks for profiles and controls.

Cons

- Too much granularity in profiles and controls may create noise and impact the user experience.
- Spawning may result in creation of almost identical rules without any real benefit of granularity.
- Bugs affecting multiple spawned rules may be more laborious to be consistently fixed along the time.

Profile and control selections

##### Conditionals

Pros

- Unlike with spawning, no changes in profile selections, controls or tailoring.


##### Spawning

Remarks

- The ability to define extendable controls may mitigate the shortcomings mentioned right above.

Content Clarity

##### Conditionals

Cons

- Difficulty knowing what the rule is checking for and what it configures.
- Difficulty to write a concise rule description.
- Difficulty to interpret a rule check (why it passed/failed?)
- Tendency to have an opinionated approach to remediations, or remediations full of conditionals that need to be checked for coverage and consistency.

Remarks

- The Conditionals approach works better when the individual rule conditions are equivalent or it doesn’t matter which approach is applied on the system.
- Analysis of results can be facilitated by oscap-report as an interface to ARF/results files.

##### Spawning

Cons

- Need to ensure that new and existing rules have distinct titles and descriptions, so the difference between them is clear, specially in tiny changes, like a single syscall in a list of multiple syscalls in a rule.

Code duplication

Reducing code duplication is important to keep the maintenance costs low.

Both of the update approaches are susceptible to code duplication.


A rule that spawns another rule will lead to duplication of code, especially if templates or macros are not used in the new rule.

Similarly, rule updates solved with conditionals can also lead to code duplication if the behavior handled by the new conditional is similar and doesn’t use templates or macros.


In summary, code duplication is mitigated by refactoring or using templates and macros. Transform the existing code into a macro or template, and use it in the new rule or conditional.

Stability

##### Conditionals

Pros

- Customers expect rules to be capable of dealing with new behaviors, like compatibility with RainerScript syntax in rsyslog rules, for example.

Cons

- Rule may break expectations from customers, especially if they use it in a tailoring to handle a specific case - a rule that once accepted just one posture as compliant, will start accepting multiple postures as compliant.

##### Spawning

Pros

- Rule doesn’t change, it just slowly erodes. But less changes means less surface for emerging problems.

Cons

- Customers will have to actively select spawned rules in case of e.g. extending profiles by tailoring.

Deprecation and Removal

The need to configure a component can appear and disappear, just as the component itself.



Once a rule is deemed obsolete or unnecessary it is deprecated so that new products don’t pick it up into their content. The rule deprecation process is detailed here.

Once no product is including the deprecated rule in their content the rule can be removed.

##### Conditionals

Pros

- Opportunity to remove “dead” code sooner than a “dead” rule, since a rule cannot be removed from a product's data stream, but unnecessary code can.

Cons

- Getting rid of “dead” conditionals requires refactoring.
- Unless actively pruned, the conditionals in the rule might stay there indefinitely.

##### Spawning

Pros

- Spawn that is not needed any more can just be unselected in the profile, control or tailoring.
- Eventually, it can be removed from the project by removing files without closer examination.

0 comments on commit 6213898

Please sign in to comment.