forked from openshift/compliance-operator
-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Include section about: - parametrizing rules - node rules - common types of rules and their templates
- Loading branch information
Showing
4 changed files
with
375 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
189 changes: 189 additions & 0 deletions
189
doc/tutorials/workshop/content/exercises/10-rule-parametrization.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,189 @@ | ||
--- | ||
Title: Rule parametrization | ||
PrevPage: 09-writing-rules | ||
NextPage: 11-node-rules | ||
--- | ||
|
||
Rule parametrization | ||
==================== | ||
|
||
In this section we will learn how one can make their rules more flexible with | ||
parametrization, which allows us to slightly alter what a rule is checking for. | ||
We will leverage `TailoredProfiless` to create our own `Profile` that enables a | ||
`Rule` and tailors a `Variable`. | ||
|
||
Additionally, we will go through more advanced testing procedures which will | ||
be required to test our `TailoredProfile`. | ||
|
||
## Variable parametrization | ||
|
||
The rule we created initially accepted only `yep` as a compliant value, and | ||
later we extended it to accept anything that matched a regular expression. | ||
|
||
What if we had two different sets of clusters with distinct compliance needs. | ||
One cluster needs to have a `compliant` value of `yep`, and the other the | ||
value of `yes`. | ||
|
||
The usual way of checking for different needs is to have different profiles. | ||
So in our scenarios we would have one profile for each cluster. | ||
|
||
But our rule, with its regular expression, accepts both values as compliant. | ||
We cannot simply use the rule for both profiles and be sure that compliance | ||
requirements for each cluster is met, as both values lead the rule to a | ||
`COMPLIANT` result. | ||
|
||
We could have two separate rules, each checking for one of the values, and | ||
add them to their respective profiles. | ||
But this creates two seemingly identical rules and increases our maintenance | ||
costs. What if we had more clusters each with its different compliance needs? | ||
|
||
The better approach is to parametrize the rule with a variable, and tailor | ||
the variable to the value we want to assess in each profile. | ||
|
||
### Creating the variable and testing basic functionality | ||
|
||
So lets first create a variable named `var-my-compliant-value` as follows: | ||
``` | ||
$ cat << EOF > applications/openshift/var-my-compliant-value.var | ||
documentation_complete: true | ||
title: 'My compliant value' | ||
description: |- | ||
The value that 'my-compliant-configmap' should have in the key 'compliant'. | ||
Default value is 'yep'. | ||
Other possible values are 'yes' and 'definitely. | ||
type: string | ||
operator: equals | ||
options: | ||
default: yep | ||
yep: yep | ||
yes: yes | ||
definitely: definitely | ||
EOF | ||
``` | ||
|
||
Then run the following command to change the rule to use the variable | ||
we just created: | ||
``` | ||
$ ./utils/add_platform_rule.py create \ | ||
--rule must_have_compliant_cm \ | ||
--name my-compliance-configmap --namespace openshift --type configmap \ | ||
--title "Must have compliant CM" \ | ||
--description "The deployment must have a CM that's compliant with.... life!" \ | ||
--match-entity "at least one" \ | ||
--yamlpath '.data.compliant' \ | ||
--variable "var-my-compliant-value" | ||
``` | ||
|
||
Our `ConfigMap` still has an incompliant value, if we test the rule right now | ||
it will evaluate to `NON-COMPLIANT`. | ||
|
||
Let's update it to one of the selections available in the variable. | ||
The default value is `yep`, and that is the variable's value if used without any tailoring. | ||
``` | ||
$ oc patch -n openshift configmap my-compliance-configmap \ | ||
-p '{"data": {"compliant": "yep"}}' --type=merge | ||
``` | ||
``` | ||
$ ./utils/add_platform_rule.py cluster-test --rule must_have_compliant_cm | ||
... | ||
* The result is 'COMPLIANT' | ||
``` | ||
|
||
### Testing Rules with Profile Tailorings | ||
|
||
So far we have been using the `./utils/add_platform_rule.py` script to test | ||
our rule. It creates very specific `ComplianceScans` that cannot cover all the use | ||
cases. | ||
|
||
For that reason we will now leverage the `ProfileBundles` created by the | ||
`utils/build_ds_container.py` to test the rule customization with | ||
`TailoredProfiles`. | ||
|
||
Build and push the content to you cluster while creating `ProfileBundles` with | ||
the following command: | ||
``` | ||
$ ./utils/build_ds_container.py -p | ||
``` | ||
|
||
One aspect to note though, is that `Profiles`, `Rules`, `Variables` and `Remediations` | ||
created by this command will have the `upstream` prefix. | ||
For example, our rule will be named `upstream-ocp4-must-have-compliant-cm`. | ||
|
||
First, let's update the `ConfigMap` to one of the compliant values from the | ||
variable: | ||
``` | ||
$ oc patch -n openshift configmap my-compliance-configmap \ | ||
-p '{"data": {"compliant": "definitely"}}' --type=merge | ||
``` | ||
|
||
Create a `TailoredProfile` that enables only our rule and changes the value of | ||
the variable to check for `definitely`: | ||
``` | ||
$ cat << EOF > my-own-profile.yaml | ||
apiVersion: compliance.openshift.io/v1alpha1 | ||
kind: TailoredProfile | ||
metadata: | ||
name: my-own-profile | ||
namespace: openshift-compliance | ||
spec: | ||
description: My compliance profile for OCP4 | ||
title: OCP4 profile with customized compliance value | ||
enableRules: | ||
- name: upstream-ocp4-must-have-compliant-cm | ||
rationale: Scan with our new rule. | ||
setValues: | ||
- name: upstream-ocp4-var-my-compliant-value | ||
rationale: The cluster scanned needs to be definitely compliant. | ||
value: definitely | ||
EOF | ||
``` | ||
|
||
In the `TailoredProfile` above we are enabling rule `upstream-ocp4-must-have-compliant-cm` | ||
and customizing the value of variable `upstream-ocp4-var-my-compiant-value`. | ||
|
||
Then, to enable scans with our `TailoredProfile` bind it to a `ScanSetting` of your | ||
choice, we will use the `default` `ScanSetting` which is available right after | ||
install. | ||
``` | ||
$ cat << EOF > default-own-profile.yaml | ||
apiVersion: compliance.openshift.io/v1alpha1 | ||
kind: ScanSettingBinding | ||
metadata: | ||
name: default-own-profile | ||
namespace: openshift-compliance | ||
profiles: | ||
- name: my-own-profile | ||
kind: TailoredProfile | ||
apiGroup: compliance.openshift.io/v1alpha1 | ||
settingsRef: | ||
name: default | ||
kind: ScanSetting | ||
apiGroup: compliance.openshift.io/v1alpha1 | ||
EOF | ||
``` | ||
|
||
``` | ||
$ oc create -f my-own-profile.yaml | ||
tailoredprofile.compliance.openshift.io/my-own-profile created | ||
$ oc create -f default-own-profile.yaml | ||
scansettingbinding.compliance.openshift.io/default-own-profile created | ||
``` | ||
|
||
After the `ScanSettingBinding` creation is processed a `ComplianceSuite` | ||
will be created and evaluation with our `TailoredProfile` started. | ||
|
||
After a few minutes check that the scan finished with result `COMPLIANT` | ||
``` | ||
$ oc get compliancescan | ||
NAME PHASE RESULT | ||
my-own-profile DONE COMPLIANT | ||
``` | ||
|
||
Our rule is ready to be enabled in multiple profiles checking different values | ||
in each `Profile`. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,86 @@ | ||
--- | ||
Title: Node Rules | ||
PrevPage: 10-rule-parametrization | ||
NextPage: 12-common-rules | ||
--- | ||
|
||
Node Rules | ||
=================== | ||
|
||
## Platform vs Node rules | ||
|
||
One important distinction to make is whether the configuration being checked is | ||
a Kubernetes resource (a Platform check) or a setting in the node of the | ||
cluster (a node check). | ||
|
||
The Platform rules check the Kubernetes API Resources. | ||
While the node rules check configuration of the node's operating system. | ||
|
||
So far the rules we created were Platform rules, they checked a Kubernetes | ||
configuration. But one can also check configurations of the operating | ||
system at the node level, and that is what we'll be doing in this section. | ||
|
||
## Creating Node rules | ||
|
||
Let's create a rule that checks whether the file `/etc/system-release` is | ||
owned by root in our cluster's nodes. | ||
|
||
To create this node rule, execute the following `create node` command: | ||
``` | ||
$ ./utils/add_platform_rule.py create node \ | ||
--rule file_owner_etc_system_release \ | ||
--title "File /etc/system-release must be owned by root" \ | ||
--description "We need to ensure that root owns the system release file" \ | ||
--template file_owner \ | ||
--template-vars "filepath: /etc/system-release, fileuid: '0'" | ||
``` | ||
|
||
We already know the `rule`, `title` and `description` are for, they are | ||
the same arguments passed when creating a `Platform` rule. | ||
The `template` argument is used to specify which template to use, and | ||
`template-vars` is a comma separated string with the values to be used. | ||
|
||
If you are curious about what templates are available, don't worry, | ||
in the next section we will go through the most used templates and their | ||
input arguments. | ||
|
||
Here is another example of how to quickly generate a a node rule that checks | ||
the sysctl `kernel.randomize_va_space` value: | ||
``` | ||
$ ./utils/add_platform_rule.py create node \ | ||
--rule sysctl_kernel_randomize_va_space \ | ||
--title "Ensure ASLR is fully enabled" \ | ||
--description "Make it harder to exploit vulnerabilities by employing full address space layout randomization" \ | ||
--template sysctl \ | ||
--template-vars "sysctlvar: kernel.randomize_va_space, sysctlval: '2', datatype: int" | ||
``` | ||
|
||
### Selecting the nodes to check | ||
|
||
When we created our node rule wi`--scan-type option. | ||
This, by default, creates a rule that is applicable to all nodes in the cluster, | ||
i.e.: `worker` and `master` nodes. | ||
To restrict a node rule to scan only on master nodes, you need to change the | ||
`platform` key in the `rule.yml` to `ocp4-master-node`. | ||
|
||
To set a node check to run on all nodes, set the rule's platform to: | ||
`platform: {{{ product }}}-node`, or more explicitly `platform: ocp4-node` | ||
|
||
To set a node check to run on only on master nodes, set the rule's platform to: | ||
`platform: {{{ product }}}-master-node` | ||
|
||
### Use of yamlfile_value on node rules | ||
|
||
The `yamlfile_value` template is a template like any other in CaC/content | ||
project, its purpose is to assert whether a yaml key's value satisfiyes a | ||
certain criteria. | ||
|
||
The use of this template is not limited to platform rules, it is very handy | ||
to check configurations defined in yaml file on the node. | ||
|
||
One such example is the Kubelet configuration in each node. The | ||
`/etc/kubernetes/kubelet.conf` is a yaml that can be checked using the | ||
`yamlfile_value` template. | ||
|
||
Check the rule [kubelet_enable_cert_rotation](https://github.com/ComplianceAsCode/content/blob/master/applications/openshift/kubelet/kubelet_enable_cert_rotation/rule.yml) | ||
for an example of how the `yamlfile_value` template is used. |
98 changes: 98 additions & 0 deletions
98
doc/tutorials/workshop/content/exercises/12-common-rules.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
--- | ||
Title: All types of rules | ||
PrevPage: 11-node-rules | ||
NextPage: ../finish | ||
--- | ||
Common types of rules | ||
=================== | ||
|
||
The rules we created in the previous chapters leveraged the `yamlfile_value`, | ||
`file_permissions` and `sysctl` template from CaC/content project. There are | ||
many more templates available in the project and each one enable us to quickly | ||
create rules with a specific behavior. | ||
|
||
In this section we will go through the most common types of checks when | ||
assessing the security posture of a cluster and the templates used to | ||
implement them. | ||
|
||
## Platform checks | ||
|
||
### Checking Kubernetes resources | ||
|
||
Checking the configuration of a Kubernetes resource is the obvious task when | ||
one wants to check the cluster's security posture. | ||
This type of check is handled by the [yamlfile_value](https://complianceascode.readthedocs.io/en/latest/templates/template_reference.html#yamlfile-value) | ||
template. | ||
|
||
You can write a rule and make use of the template by yourself, or | ||
use the `./utils/add_platform_rule.py` script showcased in past sections. | ||
Note that more advanced uses of the template will require you to write the input data by yourself. | ||
|
||
One example of this type of rule is a checking whether any registry configured | ||
for import allow use of insecure protocols. The rule checks the Cluster API and | ||
assess whether any `allowedRegistriesForImport` has `'{"insecure": true}': | ||
[ocp_insecure_allowed_registries_for_import](https://github.com/ComplianceAsCode/content/blob/master/applications/openshift/registry/ocp_insecure_allowed_registries_for_import/rule.yml) | ||
|
||
Another example is checking whether RBAC roles are defined. | ||
[rbac_roles_defined](https://github.com/ComplianceAsCode/content/blob/master/applications/openshift/rbac/rbac_roles_defined/rule.yml) | ||
|
||
## Node checks | ||
|
||
### Checking for a KubeletConfig setting | ||
|
||
With Kubelet being the primary agent on the node, its configuration becomes | ||
important to assess. | ||
|
||
You can also use the [yamlfile_value](https://complianceascode.readthedocs.io/en/latest/templates/template_reference.html#yamlfile-value) | ||
template for this. Note though that these checks are Node checks, not Platform checks. | ||
|
||
For example, this rule ensures that the node does not have a `kubelet.conf` that defines | ||
a `KubeletConfiguration` with an `authorization.mode` with value equal to `AllowAll`: | ||
|
||
[kubelet_authorization_mode](https://github.com/ComplianceAsCode/content/blob/master/applications/openshift/kubelet/kubelet_authorization_mode/rule.yml) | ||
|
||
|
||
### Checking ownership and permissions of files | ||
|
||
A very common requirement is to ensure that files in the cluster node have | ||
appropriate ownership and permissions. | ||
|
||
To check the owner, group owner or permissions in files use the following | ||
templates, | ||
[file_owner](https://complianceascode.readthedocs.io/en/latest/templates/template_reference.html#file-owner), | ||
[file_groupowner](https://complianceascode.readthedocs.io/en/latest/templates/template_reference.html#file-groupowner), | ||
[file_permissions](https://complianceascode.readthedocs.io/en/latest/templates/template_reference.html#file-permissions) | ||
respectivetly | ||
|
||
For example, to ensure the permissions of file `/etc/passwd` in the node is | ||
'0644' write a new rule and use the template `file_permissions`: | ||
|
||
``` | ||
template: | ||
name: file_permissions | ||
vars: | ||
filepath: /etc/passwd | ||
filemode: '0644' | ||
``` | ||
|
||
Check rule [file_permissions_etc_passwd](https://github.com/ComplianceAsCode/content/blob/cc4375ca0cb7f8aa3a789ba619504c7590e7af21/linux_os/guide/system/permissions/files/permissions_important_account_files/file_permissions_etc_passwd/rule.yml) for a complete example. | ||
|
||
### Checking kernel parameters | ||
|
||
The Kernel parameters are also an important setting that can be checked. | ||
|
||
### sysctl | ||
To check for sysctl parameters on the node use the the | ||
[sysctl](https://complianceascode.readthedocs.io/en/latest/templates/template_reference.html#sysctl) template. | ||
For example, to ensure that `kernel.panic_on_oops` is set to one, write a rule | ||
with the following template. | ||
|
||
``` | ||
template: | ||
name: sysctl | ||
vars: | ||
sysctlvar: kernel.panic_on_oops | ||
sysctlval: '1' | ||
datatype: int | ||
``` | ||
Check rule [kubelet_enable_protect_kernel_sysctl_kernel_panic_on_oops](https://github.com/ComplianceAsCode/content/blob/master/applications/openshift/kubelet/kubelet_enable_protect_kernel_sysctl_kernel_panic_on_oops/rule.yml) for a complete example. |