Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 135 additions & 3 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,122 @@ template:
pkgname@ubuntu2204: avahi-daemon # Platform-specific overrides
```

## CEL Checking Engine (Kubernetes/OpenShift)

CEL (Common Expression Language) provides native Kubernetes resource evaluation without requiring shell access or OVAL checks. Rules using the CEL checking engine are used by the compliance-operator for Kubernetes and OpenShift compliance scanning.

**Important:** Rules with CEL checks are **excluded** from XCCDF/OVAL DataStreams and are generated as a separate `${PRODUCT}-cel-content.yaml` file.

### Rule Structure for CEL Checks

Rules using CEL checks use a **split-file structure** to separate metadata from CEL-specific content:

- **`rule.yml`** - Contains metadata (title, description, rationale, severity, references, etc.)
- **`cel/shared.yml`** - Contains CEL-specific fields (check_type, inputs, expression)

This allows rules to support **both CEL and OVAL** checks during migration from OVAL to CEL.

**Example rule.yml:**
```yaml
documentation_complete: true

title: 'Rule Title'

description: |-
Description of what the rule checks.

rationale: |-
Why this rule matters.

severity: medium

ocil: |- # Optional: Manual check instructions
Run the following command:
<pre>$ oc get pods</pre>

failure_reason: |- # Optional: Custom failure message
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be moved to cel folder in the docs as well.

The resource is not properly configured.

references: # Optional: Same as regular rules
cis@ocp4: 1.2.3
nist: CM-6
```

**Example cel/shared.yml:**
```yaml
check_type: Platform # Usually Platform for K8s checks

expression: |- # REQUIRED: CEL expression (must evaluate to boolean)
resource.spec.enabled == true

inputs: # REQUIRED: Kubernetes resources to evaluate
- name: resource
kubernetes_input_spec:
api_version: v1
resource: pods
resource_name: my-pod # Optional: specific resource
resource_namespace: default # Optional: specific namespace
```

**Note:** The build system automatically detects rules with CEL checks by the presence of the `cel/` directory.

### CEL Expression Examples

Simple boolean check:
```yaml
expression: resource.spec.enabled == true
```

Check for field absence:
```yaml
expression: !has(resource.spec.insecureField)
```

Multiple conditions:
```yaml
expression: |-
resource.spec.replicas >= 3 &&
has(resource.spec.securityContext) &&
resource.spec.securityContext.runAsNonRoot == true
```

### CEL Profile Format

Profiles that select rules using CEL checks must have `scanner_type: CEL`:

```yaml
documentation_complete: true

title: 'CIS VM Extension Benchmark'

description: |-
Profile description.

scanner_type: CEL # REQUIRED: Marks this as a CEL profile (excluded from XCCDF)

selections:
- kubevirt-nonroot-feature-gate-is-enabled
- kubevirt-no-permitted-host-devices
```

**Note:**
- Profiles use `scanner_type: CEL` to indicate they target the CEL checking engine and should be excluded from XCCDF/datastream builds.
- A rule can have both `cel/` (for CEL checks) and `template:` (for OVAL checks) to support both checking engines during migration.

**Important:** Use hyphens rule IDs (Kubernetes naming convention), not underscores.

### Enabling CEL Content for a Product

In `products/${PRODUCT}/CMakeLists.txt`:

```cmake
set(PRODUCT "ocp4")
set(PRODUCT_CEL_ENABLED TRUE) # Enable CEL content generation
ssg_build_product(${PRODUCT})
```

See `docs/manual/developer/12_cel_content.md` for complete CEL documentation.

## Common Jinja2 Macros

Used in rule descriptions, OCIL, fixtext, and warnings fields:
Expand Down Expand Up @@ -279,19 +395,35 @@ Common selection patterns:
## Build Instructions

```bash
# Build a single product (full build)
# Build a single product (full build, includes CEL content if PRODUCT_CEL_ENABLED)
./build_product ocp4

# Build data stream only (faster, skips guides and tables)
# Build data stream only (faster, skips guides, tables, and CEL content)
./build_product ocp4 --datastream
# Short form (only builds datastream):
./build_product ocp4 -d
# Legacy form (still supported):
./build_product ocp4 --datastream-only

# Build data stream and CEL content
./build_product ocp4 --datastream --cel-content=ocp4

# Build only CEL content (no data stream)
./build_product --cel-content=ocp4

# Build CEL content for multiple products
./build_product --cel-content=ocp4,rhel9

# Build with only specific rules (fastest, for testing individual rules)
./build_product ocp4 --datastream-only --rule-id api_server_tls_security_profile
./build_product ocp4 --datastream --rule-id api_server_tls_security_profile
```

Build output goes to `build/`. The data stream file is at:
`build/ssg-<product>-ds.xml`

For products with CEL content enabled, the CEL content file is at:
`build/<product>-cel-content.yaml`

## Guidelines for Claude

1. **Always show proposals before making changes.** Present the full content of any new or modified file and wait for explicit approval.
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ocp-test-profiles.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ jobs:

- name: Build product OCP and RHCOS content
if: ${{ steps.ctf.outputs.CTF_OUTPUT_SIZE != '0' && (contains(steps.product.outputs.prop, 'ocp4') || contains(steps.product.outputs.prop, 'rhcos4')) }}
run: ./build_product -d ocp4 rhcos4
run: ./build_product --datastream ocp4 rhcos4 --cel-content=ocp4

- name: Process list of rules into a list of product-profiles to test
if: ${{ steps.ctf.outputs.CTF_OUTPUT_SIZE != '0' && (contains(steps.product.outputs.prop, 'ocp4') || contains(steps.product.outputs.prop, 'rhcos4')) }}
Expand Down
5 changes: 3 additions & 2 deletions Dockerfiles/compliance-operator-content-konflux.Containerfile
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ RUN grep -lr 'documentation_complete: false' ./products | xargs -I '{}' \
# Build the OpenShift and RHCOS content for x86, aarch64 and ppc64le architectures.
# Only build OpenShift content for s390x architectures.
RUN if [ "$(uname -m)" = "x86_64" ] || [ "$(uname -m)" = "aarch64" ] || [ "$(uname -m)" = "ppc64le" ]; then \
./build_product ocp4 rhcos4 --datastream-only; \
else ./build_product ocp4 --datastream-only; \
./build_product ocp4 rhcos4 --datastream --cel-content=ocp4; \
else ./build_product ocp4 --datastream --cel-content=ocp4; \
fi

FROM registry.redhat.io/ubi9/ubi-minimal:latest
Expand All @@ -110,3 +110,4 @@ LABEL \
WORKDIR /
COPY --from=builder /go/src/github.com/ComplianceAsCode/content/LICENSE /licenses/LICENSE
COPY --from=builder /go/src/github.com/ComplianceAsCode/content/build/ssg-*-ds.xml .
COPY --from=builder /go/src/github.com/ComplianceAsCode/content/build/*-cel-content.yaml .
8 changes: 5 additions & 3 deletions Dockerfiles/ocp4_content
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ RUN if [ "$(uname -m)" == "x86_64" ] || [ "$(uname -m)" == "aarch64" ]; then \
products/ocp4/profiles/pci-dss-node.profile \
products/ocp4/profiles/pci-dss.profile \
products/ocp4/profiles/cis-node.profile \
products/ocp4/profiles/cis-vm-extension.profile \
products/ocp4/profiles/cis.profile \
products/ocp4/profiles/moderate-node.profile \
products/ocp4/profiles/moderate.profile \
Expand All @@ -41,13 +42,14 @@ RUN if [ "$(uname -m)" == "x86_64" ] || [ "$(uname -m)" == "aarch64" ]; then \
# OpenShift content for ppc64le and s390x architectures since we're not
# including any RHCOS profiles on those architectures right now anyway.
RUN if [ "$(uname -m)" = "x86_64" ] || [ "$(uname -m)" == "aarch64" ]; then \
./build_product ocp4 rhcos4 eks --datastream-only; \
./build_product ocp4 rhcos4 eks --datastream --cel-content=ocp4; \
elif [ "$(uname -m)" = "ppc64le" ]; then \
./build_product ocp4 rhcos4 --datastream-only; \
else ./build_product ocp4 --datastream-only; \
./build_product ocp4 rhcos4 --datastream --cel-content=ocp4; \
else ./build_product ocp4 --datastream --cel-content=ocp4; \
fi

FROM registry.access.redhat.com/ubi8/ubi-micro:latest

WORKDIR /
COPY --from=builder /content/build/ssg-*-ds.xml .
COPY --from=builder /content/build/*-cel-content.yaml .
3 changes: 2 additions & 1 deletion Dockerfiles/quay_publish
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ FROM fedora:38 as builder
RUN dnf -y install cmake make git /usr/bin/python3 python3-pyyaml python3-jinja2 openscap-utils
RUN git clone --depth 1 https://github.com/ComplianceAsCode/content
WORKDIR /content
RUN ./build_product --datastream-only --debug ocp4 rhcos4 eks
RUN ./build_product --datastream --debug ocp4 rhcos4 eks --cel-content=ocp4

FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /
COPY --from=builder /content/build/ssg-ocp4-ds.xml .
COPY --from=builder /content/build/ssg-rhcos4-ds.xml .
COPY --from=builder /content/build/ssg-eks-ds.xml .
COPY --from=builder /content/build/ocp4-cel-content.yaml .
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ profiles. These are meant to be run on machines to put them into
compliance. We recommend using other formats but understand that for
some deployment scenarios bash is the only option.

*"CEL content"* refers to compliance content using the Common Expression Language (CEL)
for Kubernetes and OpenShift platforms. CEL content is generated as YAML files and is
designed for native Kubernetes resource evaluation through the compliance-operator,
without requiring shell access to nodes. This format is used for platform-level
compliance checks on container orchestration systems.

### Why?

We want multiple organizations to be able to efficiently develop security
Expand Down
7 changes: 7 additions & 0 deletions applications/openshift-virtualization/group.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
documentation_complete: true

title: 'OpenShift Virtualization'

description: |-
This section contains security recommendations for OpenShift Virtualization
(KubeVirt) configuration and virtual machine management.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
check_type: Platform

inputs:
- name: hco
kubernetes_input_spec:
api_version: hco.kubevirt.io/v1beta1
resource: hyperconvergeds
resource_name: kubevirt-hyperconverged
resource_namespace: openshift-cnv

expression: |-
!has(hco.spec.storageImport) ||
hco.spec.storageImport.insecureRegistries.size() == 0
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
documentation_complete: true

title: 'Only Trusted Registries Using TLS Can Be Used'

description: |-
By only pulling container images from trusted registries using TLS, organizations
can reduce the risk of introducing unknown vulnerabilities or malicious
software into their systems. This helps ensure that their applications and systems
remain secure and stable. All container image registries used by KubeVirt should
require TLS connections to protect the integrity and authenticity of images.

rationale: |-
When the <tt>.spec.storageImport.insecureRegistries</tt> field contains entries in
the <tt>kubevirt-hyperconverged</tt> resource, KubeVirt is configured to allow
connections to container registries that do not use TLS encryption. This creates
a significant security risk as images could be intercepted or tampered with during
transit. Man-in-the-middle attacks could result in malicious images being pulled
and executed within virtual machines. To maintain security, only registries using
TLS should be permitted, and the insecureRegistries list should be empty.

failure_reason: |-
There are registries not using TLS in '.spec.storageImport.insecureRegistries' in
the 'kubevirt-hyperconverged' resource.

severity: medium

ocil_clause: 'insecure registries are configured'

ocil: |-
Run the following command to check for insecure registries:
<pre>$ oc get hyperconverged kubevirt-hyperconverged -n openshift-cnv -o jsonpath='{.spec.storageImport.insecureRegistries}'</pre>
The output should be empty or the field should not exist.
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
check_type: Platform

inputs:
- name: hcoList
kubernetes_input_spec:
api_version: hco.kubevirt.io/v1beta1
resource: hyperconvergeds
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

followup: could add resource_name and resource_namespace here to simplify the filter (lines 11 - 12) below.


expression: |
hcoList.items.filter(h,
h.metadata.name == 'kubevirt-hyperconverged' &&
h.metadata.namespace == 'openshift-cnv'
).size() == 1 &&
hcoList.items.filter(h,
h.metadata.name == 'kubevirt-hyperconverged' &&
h.metadata.namespace == 'openshift-cnv'
).all(h,
!has(h.spec.permittedHostDevices) ||
h.spec.permittedHostDevices == null ||
(has(h.spec.permittedHostDevices.pciHostDevices) && size(h.spec.permittedHostDevices.pciHostDevices) == 0) &&
(has(h.spec.permittedHostDevices.mediatedDevices) && size(h.spec.permittedHostDevices.mediatedDevices) == 0)
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
documentation_complete: true

title: 'KubeVirt Must Not Permit Host Devices'

description: |-
Host devices should not be permitted to virtualization workloads unless
absolutely necessary for workload execution. Allowing host devices provides
direct access to host hardware, which can introduce security risks including
unauthorized access to sensitive hardware resources, potential for privilege
escalation, and bypass of virtualization security boundaries.

By default, no host devices should be trusted or permitted for use by
virtualization workloads.

rationale: |-
The <tt>.spec.permittedHostDevices</tt> field in the <tt>kubevirt-hyperconverged</tt>
resource controls which host devices can be used by virtualization workloads.
Permitting host devices allows virtual machines to bypass virtualization boundaries
and directly access host hardware, which introduces significant security risks.
This can lead to unauthorized access to sensitive hardware resources, privilege
escalation opportunities, and potential compromise of the host system. Unless
explicitly required, no host devices should be permitted.

failure_reason: |-
The '.spec.permittedHostDevices' field is set in the 'kubevirt-hyperconverged'
resource, allowing host devices to be used by virtualization workloads.

severity: medium

ocil_clause: 'permittedHostDevices are configured in kubevirt-hyperconverged'

ocil: |-
Run the following command to check the HyperConverged configuration:
<pre>$ oc get hyperconverged kubevirt-hyperconverged -n openshift-cnv -o jsonpath='{.spec.permittedHostDevices}'</pre>
The output should be empty or show empty lists for both <tt>pciHostDevices</tt> and <tt>mediatedDevices</tt>.
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
check_type: Platform

inputs:
- name: vms
kubernetes_input_spec:
api_version: kubevirt.io/v1
resource: VirtualMachine
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

followup: virtualmachine to be consistent with the case used in the other rules (hyperconvergeds)


expression: |
vms.all(h,
!has(h.spec.template.spec.domain.resources) ||
!has(h.spec.template.spec.domain.resources.overcommitGuestOverhead) ||
(has(h.spec.template.spec.domain.resources.overcommitGuestOverhead) &&
h.spec.template.spec.domain.resources.overcommitGuestOverhead == false)
)
Loading
Loading