Running OpenSCAP Scans Against Container Images
OpenSCAP (Security Content Automation Protocol) provides automated scanning for CIS, STIG, and other compliance frameworks. CleanStart integrates OpenSCAP for continuous compliance verification.
What OpenSCAP Does
OpenSCAP (Security Content Automation Protocol) is the industry-standard automated compliance tool. It analyzes container images by comparing them against CIS Benchmark controls, DISA STIG security controls, and OVAL (Open Vulnerability and Assessment Language) definitions. During the scan, OpenSCAP evaluates each control, generates a comprehensive report, and calculates an overall compliance score. The output is delivered in standardized formats including XCCDF, JSON, and HTML reports.
Installation
Install OpenSCAP tools by running sudo apt-get install -y openscap-scanner scap-security-guide. Verify installation by checking the version with oscap --version and downloading STIG and CIS profiles by running sudo oscap info /usr/share/xml/scap/ssg/content/ssg-docker-ds.xml.
CIS Docker Benchmark Scan
Run CIS Scan
Scan an image for CIS Docker Benchmark compliance using:
# Scan image for CIS Docker Benchmark complianceoscap xccdf eval \ --profile xccdf_org.ssgproject.content_profile_docker_cis \ --report cis-report.html \ --results cis-results.xml \ /usr/share/xml/scap/ssg/content/ssg-docker-ds.xml # Output: HTML report with CIS findingsCIS Scan Output
A CIS Docker Benchmark scan generates a detailed report organized by control. Control 4.1 (User) checks the docker_non_root_user rule and should pass with the rationale that the container runs as non-root. Control 4.2 (Image) evaluates the docker_minimal_image rule, which passes when the base image is under 50MB. Control 5.28 (Capabilities) checks whether all capabilities are dropped via the docker_drop_all_capabilities rule—this commonly fails if capabilities are not explicitly dropped in the Dockerfile. The summary line shows that 87 out of 94 controls passed, representing 92% compliance.
STIG Scanning
Run STIG Scan
Scan for DISA STIG compliance with Docker containers by running:
# Scan for DISA STIG compliance (Docker)oscap xccdf eval \ --profile xccdf_org.ssgproject.content_profile_stig \ --report stig-report.html \ --results stig-results.xml \ /usr/share/xml/scap/ssg/content/ssg-docker-ds.xmlSTIG Report Differences
STIG reports are more stringent than CIS reports in several ways. STIG defines more controls (150+ vs 94 for CIS), provides less flexibility in configurations, includes military-specific requirements, and may flag acceptable CIS configurations as non-compliant.
OVAL Definitions
OVAL (Open Vulnerability and Assessment Language) defines specific checks using XML. Here's an example OVAL definition that checks for non-root user:
<!-- Example OVAL definition: Check for non-root user --><definition class="compliance" id="oval:org.ssgproject.content:def:docker_non_root_user" version="1"> <metadata> <title>Check Docker container runs as non-root user</title> <description>Verify the container's USER directive is not root</description> </metadata> <criteria> <criterion comment="USER is not root" test_ref="oval:org.ssgproject.content:tst:docker_user_test"/> </criteria></definition> <tests> <file_test id="oval:org.ssgproject.content:tst:docker_user_test" check="all"> <object object_ref="oval:org.ssgproject.content:obj:dockerfile_user_object"/> <state state_ref="oval:org.ssgproject.content:ste:user_state"/> </file_test></tests> <objects> <file_object id="oval:org.ssgproject.content:obj:dockerfile_user_object"> <path>/Dockerfile</path> <filename>Dockerfile</filename> <pattern operation="pattern match">^USER\s+(?!root).*$</pattern> </file_object></objects> <states> <entity_state id="oval:org.ssgproject.content:ste:user_state"> <entity_check>all_exist</entity_check> </entity_state></states>Custom OVAL Definitions
Create custom checks by defining your own OVAL definitions in XML files, for example:
<!-- custom-checks.oval.xml --><?xml version="1.0" encoding="UTF-8"?><oval_definitions xmlns="http://oval.mitre.org/XMLSchema/oval-definitions-5"> <definition id="custom:def:app_version_check" class="compliance" version="1"> <metadata> <title>Application version is current</title> <description>Verify app version matches approved baseline</description> </metadata> <criteria> <criterion comment="App version >= 2.0.0" test_ref="custom:tst:version_test"/> </criteria> </definition> <tests> <file_test id="custom:tst:version_test" check="at least one"> <object object_ref="custom:obj:version_file"/> <state state_ref="custom:ste:version_state"/> </file_test> </tests></oval_definitions>XCCDF Profiles
XCCDF (Extensible Configuration Checklist Description Format) groups controls into profiles. Here are examples of standard and custom profiles:
<!-- Standard profiles --><Profile id="xccdf_org.ssgproject.content_profile_docker_cis"> <title>CIS Docker Benchmark</title> <description>Security hardening based on CIS Docker Benchmark</description> <select idref="xccdf_org.ssgproject.content_rule_docker_non_root_user" selected="true"/> <select idref="xccdf_org.ssgproject.content_rule_docker_minimal_image" selected="true"/> <!-- ... more rules ... --></Profile> <!-- Custom profile for your organization --><Profile id="custom:profile:company_docker_security"> <title>Company Docker Security Standard</title> <description>Custom hardening rules for company workloads</description> <refine-value idref="docker_version" selector="1.24+"/> <!-- Select specific rules relevant to your organization --> <select idref="xccdf_org.ssgproject.content_rule_docker_non_root_user" selected="true"/> <select idref="xccdf_org.ssgproject.content_rule_docker_drop_all_capabilities" selected="true"/></Profile>Kubernetes SCAP Scanning
Scan Kubernetes Configuration
Scan Kubernetes configurations using OpenSCAP by running:
# Download Kubernetes SCAP contentsudo oscap info /usr/share/xml/scap/ssg/content/ssg-kubernetes-ds.xml # Scan Kubernetes manifestsoscap xccdf eval \ --profile xccdf_org.ssgproject.content_profile_k8s \ --report k8s-report.html \ --results k8s-results.xml \ /usr/share/xml/scap/ssg/content/ssg-kubernetes-ds.xmlKubernetes Checks
Kubernetes-specific OVAL definitions check for network policies and other security requirements:
<!-- Kubernetes-specific OVAL: Check for network policies --><definition id="k8s:def:network_policy_enforced" class="compliance"> <criteria> <criterion comment="Network policies are defined" test_ref="k8s:tst:network_policy_test"/> </criteria></definition>Integration with CI/CD
GitHub Actions Scanning
Integrate OpenSCAP scanning into your CI/CD pipeline using GitHub Actions:
name: OpenSCAP Compliance Check on: [push, pull_request] jobs: openscap-scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Install OpenSCAP run: | sudo apt-get update sudo apt-get install -y openscap-scanner scap-security-guide - name: Build image run: docker build -t myapp:${{ github.sha }} . - name: Save Dockerfile for scanning run: cp Dockerfile /tmp/Dockerfile - name: Run OpenSCAP CIS scan run: | oscap xccdf eval \ --profile xccdf_org.ssgproject.content_profile_docker_cis \ --report cis-report.html \ /usr/share/xml/scap/ssg/content/ssg-docker-ds.xml - name: Check for failures run: | if grep -q "FAIL" cis-report.html; then echo "OpenSCAP found compliance issues" exit 1 fi - name: Upload report uses: actions/upload-artifact@v3 with: name: openscap-report path: cis-report.htmlKubernetes Admission Controller
Configure a Kubernetes admission controller to validate compliance:
apiVersion: admissionregistration.k8s.io/v1kind: ValidatingWebhookConfigurationmetadata: name: openscap-validationwebhooks:- name: openscap.company.com failurePolicy: Fail sideEffects: None clientConfig: service: name: openscap-validator namespace: default path: "/validate" rules: - operations: ["CREATE", "UPDATE"] apiGroups: [""] apiVersions: ["v1"] resources: ["pods", "deployments"]Scanning Results Parsing
Parse XCCDF Results
Parse OpenSCAP results programmatically using Python:
# Parse OpenSCAP resultsimport xml.etree.ElementTree as ET tree = ET.parse('cis-results.xml')root = tree.getroot() # Extract test resultsfor result in root.findall('.//{http://checklists.nist.gov/xccdf/1.2}test-result'): test_id = result.get('id') end_time = result.get('end-time') for rule_result in result.findall('{http://checklists.nist.gov/xccdf/1.2}rule-result'): rule_id = rule_result.get('idref') result_val = rule_result.find('{http://checklists.nist.gov/xccdf/1.2}result') status = result_val.text print(f"{rule_id}: {status}") # Generate compliance reportpassed = sum(1 for r in results if r.status == "pass")failed = sum(1 for r in results if r.status == "fail")print(f"Compliance: {passed}/{passed+failed} ({100*passed/(passed+failed):.1f}%)")Custom XCCDF Profiles
Create organization-specific profiles by defining your own XCCDF benchmark file:
<!-- company-docker-profile.xml --><?xml version="1.0" encoding="UTF-8"?><Benchmark xmlns="http://checklists.nist.gov/xccdf/1.2"> <Profile id="custom:profile:company_docker"> <title>Company Docker Security Baseline</title> <description>Hardening profile for company production workloads</description> <!-- CIS controls we require --> <select idref="cis_4.1" selected="true"/> <!-- Non-root user --> <select idref="cis_4.2" selected="true"/> <!-- Minimal image --> <select idref="cis_5.1" selected="true"/> <!-- CPU limits --> <select idref="cis_5.4" selected="true"/> <!-- Read-only root --> <select idref="cis_5.28" selected="true"/> <!-- Drop capabilities --> <!-- CIS controls we don't require --> <select idref="cis_4.11" selected="false"/> <!-- SSH (N/A for containers) --> <!-- Custom controls --> <select idref="custom:rule:app_signed" selected="true"/> <!-- Image signed --> <select idref="custom:rule:no_secrets" selected="true"/> <!-- No hardcoded secrets --> </Profile></Benchmark>Use your custom profile by running:
oscap xccdf eval \ --profile custom:profile:company_docker \ --report company-report.html \ company-docker-profile.xmlOpenSCAP Best Practices
Several best practices ensure effective compliance scanning. Create an organization-specific profile rather than using generic profiles. Scan every build, not just releases, to catch compliance issues early. Track compliance trends over time to identify patterns and improvements. Document and approve exceptions through a formal process. Re-baseline when security guidance changes to stay current with evolving standards.
Troubleshooting
OpenSCAP Says "No Applicable Items"
If OpenSCAP reports no applicable items, verify the profile exists, check profile syntax, and list available profiles:
# Verify profile existsoscap info /usr/share/xml/scap/ssg/content/ssg-docker-ds.xml # Check profile syntaxoscap xccdf validate-rules company-docker-profile.xml # List available profilesoscap xccdf list-profiles /usr/share/xml/scap/ssg/content/ssg-docker-ds.xmlCustom OVAL Not Loading
If custom OVAL definitions aren't loading, validate the OVAL syntax and check namespace declarations:
# Validate OVAL syntaxoscap oval validate custom-checks.oval.xml # Check namespace declarations in XMLxmllint --schema /usr/share/xml/xsd/oval-definitions-5.11.xsd custom-checks.oval.xmlCompliance Reporting
Generate detailed compliance reports for documentation and auditing:
# Generate detailed compliance report (machine-readable)oscap xccdf eval \ --profile xccdf_org.ssgproject.content_profile_docker_cis \ --report cis-report.html \ --results cis-results.xml \ --rfsession session-id-123 \ /usr/share/xml/scap/ssg/content/ssg-docker-ds.xml # Convert to JSON for automationoscap xccdf-results export-json cis-results.xml > cis-results.jsonSee Also
CIS Hardening: cis-hardening.md — CIS controls explained. STIG Hardening: stig-hardening.md — DISA STIG controls. Compliance Verification: ../runtime-evidence/ebpf-falco-integration.md — Runtime verification.
