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
125 changes: 122 additions & 3 deletions .claude/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,109 @@ template:
pkgname@ubuntu2204: avahi-daemon # Platform-specific overrides
```

## CEL Rules (Kubernetes/OpenShift)

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

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

### Required Fields for CEL Rules

```yaml
documentation_complete: true

title: 'Rule Title'

description: |-
Description of what the rule checks.

rationale: |-
Why this rule matters.

severity: medium

scannerType: CEL # REQUIRED: Marks this as a CEL rule

checkType: 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
kubernetesInputSpec:
apiVersion: v1
resource: pods
resourceName: my-pod # Optional: specific resource
resourceNamespace: default # Optional: specific namespace

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

failureReason: |- # Optional: Custom failure message
The resource is not properly configured.

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

### 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 use CEL rules must have `scannerType: CEL`:

```yaml
documentation_complete: true

title: 'CIS VM Extension Benchmark'

description: |-
Profile description.

scannerType: CEL # REQUIRED: Marks this as a CEL profile

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

**Note:** CEL profiles can only select CEL rules and are excluded from XCCDF generation.

**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 @@ -278,19 +381,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,48 @@
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.

failureReason: |-
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.

checkType: Platform

scannerType: CEL

inputs:
- name: hco
kubernetesInputSpec:
apiVersion: hco.kubevirt.io/v1beta1
resource: hyperconvergeds
resourceName: kubevirt-hyperconverged
resourceNamespace: 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,60 @@
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.

failureReason: |-
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>.

checkType: Platform

scannerType: CEL

inputs:
- name: hcoList
kubernetesInputSpec:
apiVersion: hco.kubevirt.io/v1beta1
resource: hyperconvergeds

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,52 @@
documentation_complete: true

title: 'VMs Must Not Overcommit Guest Memory'

description: |-
The <tt>overcommitGuestOverhead</tt> configuration option enables the request for
additional virtual machine management memory inside the virt-launcher pod.
The overcommit feature is used to increase virtual machine density on the
node, as long as the virtual machine doesn't request all the memory that it
would need if fully loaded. However, if the VM were to use all of the
memory it could, this would lead to the OpenShift Scheduler killing the
workload.

rationale: |-
When the <tt>.spec.template.spec.domain.resources.overcommitGuestOverhead</tt> field is
set to <tt>true</tt> in the <tt>VirtualMachine</tt> resource, VMs are allowed to
overcommit KubeVirt's memory which may lead to guests crashing and
interrupting workloads causing malfunctions. To prevent memory-related failures
and ensure workload stability, this setting should not be enabled.

failureReason: |-
The '.spec.template.spec.domain.resources.overcommitGuestOverhead' field exists and is
set to "true" in the 'VirtualMachine' resource, allowing VMs to
overcommit KubeVirt's memory which may lead to guests crashing and
interrupting workloads causing malfunctions.

severity: medium

ocil_clause: 'VMs have overcommitGuestOverhead set to true'

ocil: |-
Run the following command to check VirtualMachine configurations:
<pre>$ oc get virtualmachines -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{": "}{.spec.template.spec.domain.resources.overcommitGuestOverhead}{"\n"}{end}'</pre>
Make sure no VirtualMachine has <tt>overcommitGuestOverhead</tt> set to <tt>true</tt>.

checkType: Platform

scannerType: CEL

inputs:
- name: vms
kubernetesInputSpec:
apiVersion: kubevirt.io/v1
resource: VirtualMachine

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