Skip to content

CMP-4341: Deliver runtime kubeletconfig via shared emptyDir instead of a host symlink#1255

Open
Vincent056 wants to merge 2 commits into
ComplianceAsCode:masterfrom
Vincent056:cmp-4341-kubeletconfig-emptydir
Open

CMP-4341: Deliver runtime kubeletconfig via shared emptyDir instead of a host symlink#1255
Vincent056 wants to merge 2 commits into
ComplianceAsCode:masterfrom
Vincent056:cmp-4341-kubeletconfig-emptydir

Conversation

@Vincent056

Copy link
Copy Markdown

Summary

The node scanner reads the runtime kubeletconfig from a fixed path. Until now the operator made it available by symlinking it onto the host under /var/run/compliance-operator/kubeletconfig (→ the kubeletconfig ConfigMap mount). That approach is fragile:

  • the ln -s is not idempotent: the symlink persists on the host /run tmpfs across scans, so the 2nd+ scan on a node hits the existing symlink and tries to write inside the read-only ConfigMap mount, failing with ln: ... Read-only file system;
  • that failure is masked by | /bin/true, so the scan silently proceeds with no kubeletconfig — every kubelet yamlfile_value check with check_existence: all_exist then fails;
  • it depends on /host/var/run being writable, which is not guaranteed on all platforms.

The runtime SSH config check already solves the same problem cleanly: it writes the effective config into a shared runtime-config emptyDir mounted in both the init container and the scanner, and the rule reads it from /tmp/runtime/....

This change makes runtime kubeletconfig use the same pattern: the init container now copies the kubeletconfig ConfigMap into /host/tmp/runtime/openscap-kubeletconfig (the shared emptyDir), and the scanner reads it there. No host symlink, no /var/run, no read-only/idempotency issues.

Companion content change (required)

This is a coordinated change. The kubelet rules' yamlfile_value filepath must move to /tmp/runtime/openscap-kubeletconfig:
ComplianceAsCode/content PR: (linked below once opened)

Tests

  • Adds TestKubeletConfigIsScannedAcrossReruns (serial e2e): scans two kubelet rules that PASS on a default node and asserts they actually PASS (only possible if the runtime kubeletconfig was delivered + read), then re-runs the scan on the same nodes and re-asserts — covering the non-idempotent-delivery regression.
  • make verify (vet + gosec) clean
  • go build ./..., go vet, unit tests pass
  • new emptyDir delivery uses the exact mechanism the runtime SSH check already uses in production

CMP-4341

Vincent056 and others added 2 commits June 23, 2026 15:05
…ymlink

The node scanner reads the runtime kubeletconfig from a fixed path. Until now
the operator made it available by symlinking it onto the host under
/var/run/compliance-operator/kubeletconfig (-> the kubeletconfig ConfigMap
mount). That approach is fragile:

  - the `ln -s` is not idempotent: the symlink persists on the host /run tmpfs
    across scans, so the 2nd+ scan on a node hits the existing symlink and tries
    to write inside the read-only ConfigMap mount, failing with
    "ln: ... Read-only file system";
  - that failure is masked by `| /bin/true`, so the scan silently proceeds;
  - it depends on /host/var/run being writable, which is not guaranteed on all
    platforms.

The runtime SSH config check already solves the same problem cleanly: it writes
the effective config into a shared "runtime-config" emptyDir mounted in both the
init container and the scanner, and the rule reads it from /tmp/runtime/...

Make runtime kubeletconfig use the same pattern: the init container now copies
the kubeletconfig ConfigMap into /host/tmp/runtime/openscap-kubeletconfig (the
shared emptyDir), and the scanner reads it there. No host symlink, no /var/run,
no read-only/idempotency issues.

NOTE: requires a matching ComplianceAsCode/content change -- the kubelet rules'
yamlfile_value `filepath` must move from
/var/run/compliance-operator/kubeletconfig/openscap-kubeletconfig to
/tmp/runtime/openscap-kubeletconfig.

CMP-4341

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
TestKubeletConfigIsScannedAcrossReruns scans two kubelet rules that PASS on a
default OpenShift node (anonymous auth disabled, authz mode Webhook) and asserts
they actually PASS - which only happens if the runtime kubeletconfig was
delivered to the scanner and read. It then re-runs the scan on the same nodes
and re-asserts, guarding against a non-idempotent delivery that only breaks on
the 2nd+ scan on a node. This covers the emptyDir-based delivery introduced in
the previous commit.

CMP-4341

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@openshift-ci-robot

Copy link
Copy Markdown
Collaborator

@Vincent056: This pull request references CMP-4341 which is a valid jira issue.

Warning: The referenced jira issue has an invalid target version for the target branch this PR targets: expected the story to target the "5.0.0" version, but no target version was set.

Details

In response to this:

Summary

The node scanner reads the runtime kubeletconfig from a fixed path. Until now the operator made it available by symlinking it onto the host under /var/run/compliance-operator/kubeletconfig (→ the kubeletconfig ConfigMap mount). That approach is fragile:

  • the ln -s is not idempotent: the symlink persists on the host /run tmpfs across scans, so the 2nd+ scan on a node hits the existing symlink and tries to write inside the read-only ConfigMap mount, failing with ln: ... Read-only file system;
  • that failure is masked by | /bin/true, so the scan silently proceeds with no kubeletconfig — every kubelet yamlfile_value check with check_existence: all_exist then fails;
  • it depends on /host/var/run being writable, which is not guaranteed on all platforms.

The runtime SSH config check already solves the same problem cleanly: it writes the effective config into a shared runtime-config emptyDir mounted in both the init container and the scanner, and the rule reads it from /tmp/runtime/....

This change makes runtime kubeletconfig use the same pattern: the init container now copies the kubeletconfig ConfigMap into /host/tmp/runtime/openscap-kubeletconfig (the shared emptyDir), and the scanner reads it there. No host symlink, no /var/run, no read-only/idempotency issues.

Companion content change (required)

This is a coordinated change. The kubelet rules' yamlfile_value filepath must move to /tmp/runtime/openscap-kubeletconfig:
ComplianceAsCode/content PR: (linked below once opened)

Tests

  • Adds TestKubeletConfigIsScannedAcrossReruns (serial e2e): scans two kubelet rules that PASS on a default node and asserts they actually PASS (only possible if the runtime kubeletconfig was delivered + read), then re-runs the scan on the same nodes and re-asserts — covering the non-idempotent-delivery regression.
  • make verify (vet + gosec) clean
  • go build ./..., go vet, unit tests pass
  • new emptyDir delivery uses the exact mechanism the runtime SSH check already uses in production

CMP-4341

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the openshift-eng/jira-lifecycle-plugin repository.

@openshift-ci openshift-ci Bot requested review from jhrozek and rhmdnd June 24, 2026 06:46
@openshift-ci

openshift-ci Bot commented Jun 24, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Vincent056

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@Vincent056

Copy link
Copy Markdown
Author

Companion content PR (must merge together): ComplianceAsCode/content#14822

@github-actions

Copy link
Copy Markdown

🤖 To deploy this PR, run the following command:

make catalog-deploy CATALOG_IMG=ghcr.io/complianceascode/compliance-operator-catalog:1255-77a3c5bccc5170551f73b88f9ebb1be2942c5722

Vincent056

This comment was marked as duplicate.

@ComplianceAsCode ComplianceAsCode deleted a comment from openshift-ci Bot Jun 24, 2026
@Vincent056

Copy link
Copy Markdown
Author

/retest

@red-hat-konflux-kflux-prd-rh02

Copy link
Copy Markdown

All PipelineRuns for this commit have already succeeded. Use /retest <pipeline-name> to re-run a specific pipeline or /test to re-run all pipelines.

@openshift-ci

openshift-ci Bot commented Jun 25, 2026

Copy link
Copy Markdown

@Vincent056: The following tests failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/e2e-aws-config 77a3c5b link true /test e2e-aws-config
ci/prow/e2e-aws-parallel-arm 77a3c5b link true /test e2e-aws-parallel-arm
ci/prow/e2e-aws-parallel 77a3c5b link true /test e2e-aws-parallel
ci/prow/e2e-rosa 77a3c5b link true /test e2e-rosa
ci/prow/e2e-aws-deployment 77a3c5b link true /test e2e-aws-deployment
ci/prow/e2e-aws-serial 77a3c5b link true /test e2e-aws-serial
ci/prow/e2e-aws-scan-config 77a3c5b link true /test e2e-aws-scan-config
ci/prow/e2e-aws-serial-arm 77a3c5b link true /test e2e-aws-serial-arm

Full PR test history. Your PR dashboard.

Details

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants