OpenSCAP is an open-source scanner that checks a Linux system against a security policy written in SCAP, the standard format used by the CIS benchmarks, DISA STIGs and similar baselines. The policies themselves come from the ComplianceAsCode project as the SCAP Security Guide (SSG). In this tutorial you will install the oscap scanner on Ubuntu 24.04, scan the server against the CIS Level 1 Server profile, read the HTML report, generate a remediation script from the results, and schedule a weekly scan with a systemd timer.

Prerequisites

To follow this guide you need:

  • A server running Ubuntu 24.04 LTS, for example a CubePath VPS, and a non-root user with sudo privileges.
  • Internet access to download the SSG content from GitHub.
  • Ideally, a test server that mirrors production. Scanning is read-only, but remediation changes the system and can lock you out.

Step 1 - Installing the OpenSCAP scanner

The scanner is in the Ubuntu universe repository. Install it together with unzip, which you will need for the policy content:

sudo apt update
sudo apt install openscap-scanner unzip

Check the installed version:

oscap --version | head -n 1
OpenSCAP command line tool (oscap) 1.3.9

Step 2 - Downloading the SCAP Security Guide content

Ubuntu's own SSG packages lag behind upstream and may not include a data stream for Ubuntu 24.04, so download the current release from the ComplianceAsCode project instead. Look up the latest version number:

SSG_VERSION=$(curl -fsSL https://api.github.com/repos/ComplianceAsCode/content/releases/latest | grep -oP '"tag_name": "v\K[^"]+')
echo "$SSG_VERSION"

Download and extract the release into /opt/ssg:

curl -fLO "https://github.com/ComplianceAsCode/content/releases/download/v${SSG_VERSION}/scap-security-guide-${SSG_VERSION}.zip"
sudo mkdir -p /opt/ssg
sudo unzip -q "scap-security-guide-${SSG_VERSION}.zip" -d /opt/ssg

Create a stable symbolic link, so scripts do not depend on the version number, and check that the Ubuntu 24.04 data stream is there:

sudo ln -sfn "/opt/ssg/scap-security-guide-${SSG_VERSION}" /opt/ssg/current
ls /opt/ssg/current/ssg-ubuntu2404-ds.xml
/opt/ssg/current/ssg-ubuntu2404-ds.xml

A data stream (-ds.xml) bundles the rules (XCCDF), the automated checks (OVAL) and the remediations for one operating system.

Step 3 - Choosing a profile

Each data stream contains several profiles, and each profile is a selection of rules that implements one baseline. List them:

oscap info --profiles /opt/ssg/current/ssg-ubuntu2404-ds.xml

The output lists each profile ID followed by its title, similar to this (the exact set depends on the SSG release):

xccdf_org.ssgproject.content_profile_cis_level1_server:CIS Ubuntu 24.04 Level 1 Server Benchmark
xccdf_org.ssgproject.content_profile_cis_level1_workstation:CIS Ubuntu 24.04 Level 1 Workstation Benchmark
xccdf_org.ssgproject.content_profile_cis_level2_server:CIS Ubuntu 24.04 Level 2 Server Benchmark
xccdf_org.ssgproject.content_profile_cis_level2_workstation:CIS Ubuntu 24.04 Level 2 Workstation Benchmark
xccdf_org.ssgproject.content_profile_stig:Canonical Ubuntu 24.04 LTS Security Technical Implementation Guide (STIG)

For a general-purpose server, start with CIS Level 1 Server: it covers the basics with little impact on functionality. Level 2 and STIG are stricter and are usually required only by specific contracts or regulations. To read a profile's description and rule count before you commit to it, run:

oscap info --profile xccdf_org.ssgproject.content_profile_cis_level1_server /opt/ssg/current/ssg-ubuntu2404-ds.xml

Step 4 - Running the first scan

Create a directory for the results, readable only by root because reports describe the weaknesses of the system:

sudo install -d -m 0700 /var/lib/openscap

Run the evaluation. --results stores the machine-readable results and --report writes a human-readable HTML report:

sudo oscap xccdf eval \
  --profile xccdf_org.ssgproject.content_profile_cis_level1_server \
  --results /var/lib/openscap/results.xml \
  --report /var/lib/openscap/report.html \
  /opt/ssg/current/ssg-ubuntu2404-ds.xml

The scan takes one to several minutes and prints one line per rule:

Title   Ensure SSH root login is disabled
Rule    xccdf_org.ssgproject.content_rule_sshd_disable_root_login
Result  fail

Title   Ensure permissions on /etc/passwd are configured
Rule    xccdf_org.ssgproject.content_rule_file_permissions_etc_passwd
Result  pass

Check the exit code right after the scan:

echo $?
Exit codeMeaning
0Every evaluated rule passed
1The scan itself failed (wrong path, profile ID, or content)
2The scan worked and at least one rule failed

On a fresh system 2 is normal: few default installations pass a CIS profile.

Step 5 - Reading the report

Count the results by type directly from the results file:

sudo grep -o '<result>[a-z]*</result>' /var/lib/openscap/results.xml | sort | uniq -c
    142 <result>fail</result>
     35 <result>notapplicable</result>
    118 <result>pass</result>

The HTML report is easier to work with. Copy it to your workstation (run this on your local machine, after making the file readable by your user on the server with sudo cp /var/lib/openscap/report.html ~ && sudo chown your_user: ~/report.html):

scp your_user@your_server_ip:report.html .

Open report.html in a browser. It shows the overall score, and for each failed rule its severity, why it matters, and the Bash or Ansible fix that the remediation would apply. Filter by severity and work through the high findings first. Delete the copy from your home directory on the server when you are done.

Step 6 - Generating and applying a remediation script

OpenSCAP can turn the failed rules from a scan into a Bash script. Generating it from the results (rather than from the whole profile) means the script only contains fixes for what actually failed. The --result-id value is xccdf_org.open-scap_testresult_ followed by the profile ID:

sudo oscap xccdf generate fix \
  --fix-type bash \
  --result-id xccdf_org.open-scap_testresult_xccdf_org.ssgproject.content_profile_cis_level1_server \
  --output /var/lib/openscap/remediation.sh \
  /var/lib/openscap/results.xml

Read the script before running it:

sudo less /var/lib/openscap/remediation.sh

Once you are satisfied with it, apply it on the test server and reboot so kernel and mount changes take effect:

sudo bash /var/lib/openscap/remediation.sh
sudo reboot

If you manage servers with Ansible, generate a playbook instead with --fix-type ansible and review it the same way. That fits better into an existing configuration management workflow than a one-off script.

Run the scan from Step 4 again and compare the counts with the previous run. The number of fail results should drop sharply; the remaining ones are rules you excluded on purpose or that need manual work.

Step 7 - Scheduling weekly scans

A scan is only useful if you repeat it: packages, users and configuration drift over time. Create a small script that keeps a dated report for each run:

sudo nano /usr/local/sbin/openscap-scan
#!/usr/bin/env bash
set -euo pipefail

content="/opt/ssg/current/ssg-ubuntu2404-ds.xml"
profile="xccdf_org.ssgproject.content_profile_cis_level1_server"
outdir="/var/lib/openscap"
stamp="$(date +%F)"

rc=0
oscap xccdf eval \
  --profile "$profile" \
  --results "$outdir/results-$stamp.xml" \
  --report "$outdir/report-$stamp.html" \
  "$content" > /dev/null || rc=$?

# 2 means rules failed, which is expected; 1 is a real error.
if [ "$rc" -eq 1 ]; then
  echo "oscap scan failed" >&2
  exit 1
fi

fails=$(grep -c '<result>fail</result>' "$outdir/results-$stamp.xml" || true)
echo "OpenSCAP $profile: $fails failed rules, report $outdir/report-$stamp.html"

# Keep 26 weeks of history.
find "$outdir" -name 'results-*.xml' -mtime +182 -delete
find "$outdir" -name 'report-*.html' -mtime +182 -delete

Make it executable and run it once by hand:

sudo chmod 750 /usr/local/sbin/openscap-scan
sudo /usr/local/sbin/openscap-scan
OpenSCAP xccdf_org.ssgproject.content_profile_cis_level1_server: 38 failed rules, report /var/lib/openscap/report-2026-09-25.html

Create a systemd service for the script:

sudo nano /etc/systemd/system/openscap-scan.service
[Unit]
Description=Weekly OpenSCAP compliance scan

[Service]
Type=oneshot
ExecStart=/usr/local/sbin/openscap-scan
Nice=10
IOSchedulingClass=idle

Then a timer that runs it every Sunday at 03:00:

sudo nano /etc/systemd/system/openscap-scan.timer
[Unit]
Description=Run the OpenSCAP compliance scan weekly

[Timer]
OnCalendar=Sun *-*-* 03:00:00
Persistent=true
RandomizedDelaySec=15m

[Install]
WantedBy=timers.target

Enable the timer and confirm the next run:

sudo systemctl daemon-reload
sudo systemctl enable --now openscap-scan.timer
systemctl list-timers openscap-scan.timer

The summary line of every run is stored in the journal, so you can follow the trend with:

sudo journalctl -u openscap-scan.service --no-pager | grep 'failed rules'

When a new SSG version is released, repeat Step 2 and update the /opt/ssg/current link. New releases add rules, so expect the failure count to change after an update.

Troubleshooting

oscap prints "No such file or directory" for the content. The path is wrong or the link was not created. Check with ls -l /opt/ssg/current/ and use the file name exactly as listed.

"No profile matching suffix" or "Profile not found". Profile IDs differ between SSG releases. Copy the exact ID from oscap info --profiles.

Some rules show notchecked or error. notchecked rules have no automated check and must be verified by hand. error usually means the check needs a tool or file that is not present; the HTML report shows details for each rule.

The scan tries to download remote resources. Some content references external OVAL files. The Ubuntu data stream works without them; only add --fetch-remote-resources if the report says a check was skipped because a resource was not available.

Conclusion

You installed OpenSCAP on Ubuntu 24.04, evaluated the server against the CIS Level 1 Server profile with current SSG content, turned the failures into a reviewed remediation script, and scheduled a weekly scan that keeps six months of reports. Next, record which rules you excluded and why so auditors can see the reasoning, feed the weekly summary into your monitoring or ticketing system, and consider evaluating the Level 2 profile once Level 1 is clean.