Skip to content

OpenSCAP Compliance Scanning

Applies to CentOS Stream 9 & 10 / AlmaLinux 9.x & 10.x / Rocky Linux 9.x & 10.x

Compliance checks often mean walking through a checklist of hundreds of items, verifying your system configuration one by one. OpenSCAP automates this: it reads a standardized security baseline, scans your system, tells you which items pass and which fail, and can even remediate them for you.

  • What the SCAP standard and its components (XCCDF, OVAL, datastream) are
  • How to install OpenSCAP and find the baseline content for your distribution
  • How to scan a system with CIS, DISA STIG, and other profiles and generate a readable HTML report
  • How to interpret scan results and run automatic remediation safely
  • How to apply a baseline at install time, scan container images, and set up periodic compliance checks
  • A system running EL 9.x or EL 10.x
  • sudo privileges
  • Access to DNF repositories (baseline content packages install from the default repos)
  • Strongly recommended: work on a test machine or VM snapshot first before applying to production

SCAP (Security Content Automation Protocol) is a set of standards maintained by NIST that describe “security compliance checks” in a machine-readable format, so that different tools can exchange and execute the same baseline. It consists of several components:

ComponentRole
XCCDFThe checklist format, describing individual rules, profiles, and how pass/fail is determined
OVALThe detection logic, defining “how to actually check the system state” (e.g., whether a file’s permissions are 600)
datastreamThe packaging format (*-ds.xml) that bundles XCCDF, OVAL, and other content into a single file

OpenSCAP (oscap) is the open-source implementation of SCAP on EL — the command-line tool that performs scans and remediation. The actual baseline content is provided by the SCAP Security Guide (SSG) project, which packages CIS, DISA STIG, and other baselines for each distribution.

Install the scanner and baseline content
$ sudo dnf install openscap-scanner scap-security-guide
  • openscap-scanner provides the oscap command.
  • scap-security-guide provides the datastream content for each distribution.

If you also need to scan container images, install openscap-utils (which provides oscap-podman).

SSG installs content under a fixed directory:

List available datastream files
$ ls /usr/share/xml/scap/ssg/content/

Each distribution has its own datastream file. Choosing the wrong one leads to mismatched rules, so match yours up first:

DistributionDatastream file
RHEL 9ssg-rhel9-ds.xml
CentOS Stream 9ssg-cs9-ds.xml
AlmaLinux 9ssg-almalinux9-ds.xml
Rocky Linux 9ssg-rl9-ds.xml
RHEL 10ssg-rhel10-ds.xml

A single datastream contains multiple profiles, each corresponding to a compliance standard. First, see which ones are available:

View datastream info and the profile list
$ oscap info /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml

The Profiles: section of the output lists the title and ID of each profile, similar to:

Profiles:
Title: CIS Red Hat Enterprise Linux 9 Benchmark for Level 2 - Server
Id: xccdf_org.ssgproject.content_profile_cis
Title: DISA STIG for Red Hat Enterprise Linux 9
Id: xccdf_org.ssgproject.content_profile_stig
Title: PCI-DSS v4 Control Baseline for Red Hat Enterprise Linux 9
Id: xccdf_org.ssgproject.content_profile_pci-dss

That xccdf_org.ssgproject.content_profile_... is the profile ID you will use in later commands.

Common profiles:

StandardDescription
CISCIS Benchmark, with Level 1/Level 2 and Server/Workstation variants
DISA STIGThe U.S. Department of Defense Security Technical Implementation Guide, the strictest
PCI-DSSPayment Card Industry Data Security Standard
HIPAAU.S. healthcare information protection
ANSSI-BP-028The French national cybersecurity agency baseline, with minimal/intermediary/enhanced/high levels
CUSPA general-purpose baseline new in EL 10 (Custom Profile), positioned as a moderate, universal starting point

Once you have your datastream and profile ID, run an evaluation scan:

Scan the system and generate a report
$ sudo oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis \
--results scan-results.xml \
--report report.html \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml

Parameter notes:

  • --profile: the profile ID to apply.
  • --results: saves the machine-readable XML results (needed later to generate remediation scripts).
  • --report: generates a human-readable HTML report.

When the scan finishes, open report.html in a browser to see each rule’s pass status, severity, and remediation advice.

Each rule in the report has a result status:

StatusMeaning
passPassed, compliant with the baseline
failFailed, needs remediation
notapplicableNot applicable (e.g., the rule targets software that is not installed)
notchecked / errorNot checked or the check errored

Remediate in order of severity: handle high first, then medium, then low. The summary chart at the top of the report lets you quickly see the distribution of failures. Do not chase 100% pass — some rules may conflict with your business needs (for example, disabling a service you actually use); such items need manual evaluation and a documented exception.

OpenSCAP can automatically bring failing items into compliance, in two ways.

Apply remediation in the same pass as the scan:

Scan and remediate immediately (high risk)
$ sudo oscap xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis \
--remediate \
--results scan-results.xml \
--report report.html \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml

This is the fastest but least controllable approach; only recommended for throwaway test environments.

After remediation, scan again and compare whether the fail count dropped to confirm the changes took effect.

If you use Kickstart for automated installs, you can bring the system into compliance during installation and skip remediation afterward. Anaconda provides the org_fedora_oscap addon:

OSCAP addon snippet in Kickstart
%addon org_fedora_oscap
content-type = scap-security-guide
profile = xccdf_org.ssgproject.content_profile_cis
%end

The installer applies the specified profile during provisioning, so a new machine is already hardened on first boot. This is a common practice for keeping baselines consistent across large-scale deployments.

Compliance is not only about the host — container images need checking too. After installing openscap-utils, use oscap-podman to scan an image directly (without starting a container):

Scan a container image
$ sudo oscap-podman registry.access.redhat.com/ubi9/ubi:latest \
xccdf eval \
--profile xccdf_org.ssgproject.content_profile_cis \
--report container-report.html \
/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml

Put this step in your CI pipeline to block non-compliant builds before an image is published.

Compliance is not a one-time thing — configuration drifts over time. Use a systemd timer to scan periodically and archive the reports so you can continuously track system state.

  1. Write a scan script that archives reports by date:

    /usr/local/sbin/compliance-scan.sh
    $ sudo tee /usr/local/sbin/compliance-scan.sh <<'EOF'
    #!/bin/bash
    set -euo pipefail
    DS=/usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml
    PROFILE=xccdf_org.ssgproject.content_profile_cis
    OUT=/var/log/compliance
    DATE=$(date +%Y%m%d)
    mkdir -p "$OUT"
    oscap xccdf eval --profile "$PROFILE" \
    --results "$OUT/results-$DATE.xml" \
    --report "$OUT/report-$DATE.html" \
    "$DS" || true
    EOF
    $ sudo chmod +x /usr/local/sbin/compliance-scan.sh

    The trailing || true prevents the non-zero exit code from rule failures from marking the timer as failed.

  2. Create the service and timer units to run it weekly. See Timers for the exact syntax.

If /usr/share/xml/scap/ssg/content/ is empty or missing, the baseline content package is not installed:

Terminal window
$ sudo dnf install scap-security-guide

Note that it and openscap-scanner are two different packages — installing the scanner does not install the content.

The profile ID is that xccdf_org.ssgproject.content_profile_... string, not the human-readable title. Use oscap info <datastream> to view the full list and copy the Id: value.

A full scan runs through hundreds of OVAL checks and can take several minutes on slow disks or resource-constrained machines, which is normal. If you only want to validate a few rules, use --rule <rule_id> to run just the specified rules, which is much faster.

This is exactly why “test on a snapshot first” is emphasized repeatedly. If a production system is already broken: rolling back from a VM snapshot or backup is the most reliable fix. So before running --remediate, always confirm you have a restore point to roll back to.