Configure automated compliance scanning with OpenSCAP and audit reporting

Intermediate 25 min May 13, 2026 620 views
Ubuntu 24.04 Debian 12 AlmaLinux 9 Rocky Linux 9

Set up OpenSCAP security compliance scanning with automated systemd timers, generate HTML and XML audit reports, and configure SCAP security profiles for continuous compliance monitoring.

Prerequisites

  • Root or sudo access
  • Basic Linux command line knowledge
  • Understanding of compliance frameworks

What this solves

OpenSCAP provides automated security compliance scanning against industry standards like CIS benchmarks, NIST 800-53, and PCI DSS. This tutorial shows you how to install OpenSCAP, configure security profiles, set up automated scanning with systemd timers, and generate detailed compliance reports in HTML and XML formats for audit trails.

Step-by-step installation

Update system packages

Start by updating your package manager to ensure you get the latest OpenSCAP tools and dependencies.

sudo apt update && sudo apt upgrade -y
sudo dnf update -y

Install OpenSCAP and dependencies

Install the core OpenSCAP scanner, utilities, and security content packages that contain the compliance profiles.

sudo apt install -y libopenscap8 openscap-utils scap-security-guide ssg-debderived ssg-base
sudo dnf install -y openscap openscap-utils scap-security-guide

Verify OpenSCAP installation

Check that OpenSCAP is installed correctly and view the available scanning capabilities.

oscap --version
oscap --help

Locate security content profiles

Find the installed SCAP security content that contains compliance profiles for different standards.

ls -la /usr/share/xml/scap/ssg/content/
oscap info /usr/share/xml/scap/ssg/content/ssg-ubuntu2004-ds.xml
ls -la /usr/share/xml/scap/ssg/content/
oscap info /usr/share/xml/scap/ssg/content/ssg-almalinux9-ds.xml

List available compliance profiles

View all available security profiles to choose the appropriate compliance standard for your environment.

oscap info --profiles /usr/share/xml/scap/ssg/content/ssg-ubuntu2004-ds.xml
oscap info --profiles /usr/share/xml/scap/ssg/content/ssg-almalinux9-ds.xml

Create compliance scan directories

Set up directories to store scan results, reports, and configuration files with proper permissions.

sudo mkdir -p /var/lib/openscap/{reports,results,logs}
sudo mkdir -p /etc/openscap/profiles
sudo chmod 755 /var/lib/openscap
sudo chmod 755 /var/lib/openscap/{reports,results,logs}

Run initial compliance scan

Perform a test scan using the CIS benchmark profile to verify everything works correctly.

sudo oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_cis_level1_server \
  --results /var/lib/openscap/results/initial-scan-results.xml \
  --report /var/lib/openscap/reports/initial-scan-report.html \
  /usr/share/xml/scap/ssg/content/ssg-ubuntu2004-ds.xml
sudo oscap xccdf eval --profile xccdf_org.ssgproject.content_profile_cis \
  --results /var/lib/openscap/results/initial-scan-results.xml \
  --report /var/lib/openscap/reports/initial-scan-report.html \
  /usr/share/xml/scap/ssg/content/ssg-almalinux9-ds.xml

Configure SCAP security profiles

Create custom profile configuration

Set up a configuration file to define which profiles to scan and customize scanning parameters.

# OpenSCAP Compliance Configuration
# Profile selection and scanning parameters

# Default profile for automated scanning
DEFAULT_PROFILE="xccdf_org.ssgproject.content_profile_cis_level1_server"

# Content location
CONTENT_PATH="/usr/share/xml/scap/ssg/content/ssg-ubuntu2004-ds.xml"

# Results and reports directories
RESULTS_DIR="/var/lib/openscap/results"
REPORTS_DIR="/var/lib/openscap/reports"
LOGS_DIR="/var/lib/openscap/logs"

# Report formats to generate
GENERATE_HTML="true"
GENERATE_XML="true"
GENERATE_ARF="false"

# Retention policy (days)
RETENTION_DAYS="30"

# Email notifications
EMAIL_ALERTS="false"
EMAIL_ADDRESS="admin@example.com"

Create compliance scanning script

Build an automated script that reads the configuration and performs compliance scans with proper logging.

#!/bin/bash

# OpenSCAP Compliance Scanner Script
# Automated compliance scanning with reporting

set -euo pipefail

# Source configuration
CONFIG_FILE="/etc/openscap/profiles/compliance-config.conf"
if [[ -f "$CONFIG_FILE" ]]; then
    source "$CONFIG_FILE"
else
    echo "Configuration file not found: $CONFIG_FILE"
    exit 1
fi

# Generate timestamp for unique filenames
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
HOSTNAME=$(hostname -s)

# Define output files
RESULT_FILE="$RESULTS_DIR/compliance-results-$HOSTNAME-$TIMESTAMP.xml"
REPORT_FILE="$REPORTS_DIR/compliance-report-$HOSTNAME-$TIMESTAMP.html"
LOG_FILE="$LOGS_DIR/compliance-scan-$TIMESTAMP.log"

# Logging function
log() {
    echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" | tee -a "$LOG_FILE"
}

log "Starting OpenSCAP compliance scan"
log "Profile: $DEFAULT_PROFILE"
log "Content: $CONTENT_PATH"

# Check if content file exists
if [[ ! -f "$CONTENT_PATH" ]]; then
    log "ERROR: Content file not found: $CONTENT_PATH"
    exit 1
fi

# Run the compliance scan
log "Executing compliance scan..."
if oscap xccdf eval \
    --profile "$DEFAULT_PROFILE" \
    --results "$RESULT_FILE" \
    --report "$REPORT_FILE" \
    "$CONTENT_PATH" >> "$LOG_FILE" 2>&1; then
    SCAN_STATUS="SUCCESS"
else
    SCAN_STATUS="COMPLETED_WITH_FINDINGS"
fi

log "Scan completed with status: $SCAN_STATUS"
log "Results saved to: $RESULT_FILE"
log "Report saved to: $REPORT_FILE"

# Generate summary
if [[ -f "$RESULT_FILE" ]]; then
    TOTAL_RULES=$(oscap xccdf generate report "$RESULT_FILE" | grep -o 'Rule.*:' | wc -l || echo "0")
    log "Total rules evaluated: $TOTAL_RULES"
fi

# Cleanup old files based on retention policy
find "$RESULTS_DIR" -name "*.xml" -mtime +"$RETENTION_DAYS" -delete 2>/dev/null || true
find "$REPORTS_DIR" -name "*.html" -mtime +"$RETENTION_DAYS" -delete 2>/dev/null || true
find "$LOGS_DIR" -name "*.log" -mtime +"$RETENTION_DAYS" -delete 2>/dev/null || true

log "Compliance scan completed successfully"

exit 0

Make the script executable

Set proper permissions on the scanning script to allow execution by root and the system.

sudo chmod 755 /usr/local/bin/openscap-compliance-scan.sh
sudo chown root:root /usr/local/bin/openscap-compliance-scan.sh

Update content path for distribution

Modify the configuration file to use the correct content path for your Linux distribution.

sudo sed -i 's|ssg-ubuntu2004-ds.xml|ssg-ubuntu2004-ds.xml|g' /etc/openscap/profiles/compliance-config.conf
sudo sed -i 's|ssg-ubuntu2004-ds.xml|ssg-almalinux9-ds.xml|g' /etc/openscap/profiles/compliance-config.conf
sudo sed -i 's|xccdf_org.ssgproject.content_profile_cis_level1_server|xccdf_org.ssgproject.content_profile_cis|g' /etc/openscap/profiles/compliance-config.conf

Set up automated scanning with systemd timers

Create systemd service unit

Define a systemd service that runs the compliance scanning script with proper environment and security settings.

[Unit]
Description=OpenSCAP Compliance Scanner
After=network.target
Wants=network.target

[Service]
Type=oneshot
User=root
Group=root
ExecStart=/usr/local/bin/openscap-compliance-scan.sh
StandardOutput=journal
StandardError=journal
SyslogIdentifier=openscap-compliance

# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ReadWritePaths=/var/lib/openscap

# Resource limits
TimeoutSec=1800
MemoryMax=512M

[Install]
WantedBy=multi-user.target

Create systemd timer unit

Set up a timer to run compliance scans automatically on a regular schedule.

[Unit]
Description=Run OpenSCAP compliance scan
Requires=openscap-compliance.service

[Timer]
# Run daily at 2 AM
OnCalendar=daily
Persistent=true

# Add randomization to avoid system load spikes
RandomizedDelaySec=1800

# Ensure timer doesn't accumulate if system was down
AccuracySec=1h

[Install]
WantedBy=timers.target

Enable and start the systemd timer

Activate the automated scanning timer and verify it's scheduled correctly.

sudo systemctl daemon-reload
sudo systemctl enable openscap-compliance.timer
sudo systemctl start openscap-compliance.timer
sudo systemctl status openscap-compliance.timer

Test the automated scan

Run the service manually to verify the automation works correctly before relying on the timer.

sudo systemctl start openscap-compliance.service
sudo systemctl status openscap-compliance.service
sudo journalctl -u openscap-compliance.service -n 20

Check timer schedule

Verify when the next automated scan is scheduled to run.

sudo systemctl list-timers openscap-compliance.timer

Generate HTML and XML compliance reports

Create report generation script

Build a script to generate comprehensive reports from existing scan results in multiple formats.

#!/bin/bash

# OpenSCAP Report Generator
# Generate reports from existing scan results

set -euo pipefail

# Configuration
RESULTS_DIR="/var/lib/openscap/results"
REPORTS_DIR="/var/lib/openscap/reports"
LOGS_DIR="/var/lib/openscap/logs"

# Check if results directory exists
if [[ ! -d "$RESULTS_DIR" ]]; then
    echo "Results directory not found: $RESULTS_DIR"
    exit 1
fi

# Find the most recent result file
LATEST_RESULT=$(find "$RESULTS_DIR" -name "*.xml" -type f -printf '%T@ %p\n' | sort -n | tail -1 | cut -d' ' -f2-)

if [[ -z "$LATEST_RESULT" ]]; then
    echo "No scan results found in $RESULTS_DIR"
    exit 1
fi

echo "Processing results from: $LATEST_RESULT"

# Generate timestamp for reports
TIMESTAMP=$(date +"%Y%m%d-%H%M%S")
HOSTNAME=$(hostname -s)

# Generate HTML report
HTML_REPORT="$REPORTS_DIR/compliance-report-$HOSTNAME-$TIMESTAMP.html"
echo "Generating HTML report..."
oscap xccdf generate report "$LATEST_RESULT" > "$HTML_REPORT"
echo "HTML report saved: $HTML_REPORT"

# Generate detailed XML report
XML_REPORT="$REPORTS_DIR/compliance-detailed-$HOSTNAME-$TIMESTAMP.xml"
echo "Generating detailed XML report..."
cp "$LATEST_RESULT" "$XML_REPORT"
echo "XML report saved: $XML_REPORT"

# Generate summary statistics
SUMMARY_FILE="$REPORTS_DIR/compliance-summary-$HOSTNAME-$TIMESTAMP.txt"
echo "Generating compliance summary..."

{
    echo "OpenSCAP Compliance Summary"
    echo "Generated: $(date)"
    echo "Hostname: $HOSTNAME"
    echo "Source: $LATEST_RESULT"
    echo ""
    
    # Extract rule results
    echo "Rule Results:"
    oscap xccdf generate report "$LATEST_RESULT" | grep -E "(pass|fail|error|unknown|notapplicable|notchecked)" | \
        sed 's/<[^>]*>//g' | sort | uniq -c | sort -rn || echo "Unable to extract rule statistics"
    
    echo ""
    echo "Report files generated:"
    echo "  HTML: $HTML_REPORT"
    echo "  XML:  $XML_REPORT"
    echo "  Summary: $SUMMARY_FILE"
} > "$SUMMARY_FILE"

echo "Summary saved: $SUMMARY_FILE"
echo "Report generation completed successfully"

exit 0

Make report generator executable

Set appropriate permissions on the report generation script.

sudo chmod 755 /usr/local/bin/openscap-report-generator.sh
sudo chown root:root /usr/local/bin/openscap-report-generator.sh

Generate reports from existing scans

Run the report generator to create HTML and XML reports from your compliance scan results.

sudo /usr/local/bin/openscap-report-generator.sh

View generated reports

List the generated reports and check their contents to verify successful generation.

ls -la /var/lib/openscap/reports/
head -20 /var/lib/openscap/reports/compliance-summary-$(hostname -s)-*.txt

Set up web server for report viewing

Configure a simple web server to view HTML reports through a browser interface.

sudo apt install -y nginx
sudo systemctl enable --now nginx
sudo dnf install -y nginx
sudo systemctl enable --now nginx
sudo firewall-cmd --permanent --add-service=http
sudo firewall-cmd --reload

Configure Nginx for compliance reports

Create a virtual host to serve compliance reports securely with basic authentication.

server {
    listen 80;
    server_name compliance.example.com;
    
    # Redirect to HTTPS
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name compliance.example.com;
    
    # SSL configuration (replace with your certificates)
    ssl_certificate /etc/ssl/certs/compliance.crt;
    ssl_certificate_key /etc/ssl/private/compliance.key;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512;
    
    # Document root for compliance reports
    root /var/lib/openscap/reports;
    index index.html;
    
    # Basic authentication
    auth_basic "Compliance Reports Access";
    auth_basic_user_file /etc/nginx/.htpasswd-compliance;
    
    # Security headers
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;
    add_header X-XSS-Protection "1; mode=block" always;
    
    # Serve HTML reports
    location ~* \.html$ {
        try_files $uri =404;
        expires 1h;
    }
    
    # Deny access to XML files and logs
    location ~* \.(xml|log)$ {
        deny all;
        return 404;
    }
    
    # Logging
    access_log /var/log/nginx/compliance-reports.access.log;
    error_log /var/log/nginx/compliance-reports.error.log;
}

Verify your setup

Check that all components are working correctly and compliance scanning is automated.

# Check OpenSCAP installation
oscap --version

# Verify systemd timer is active
sudo systemctl status openscap-compliance.timer
sudo systemctl list-timers openscap-compliance.timer

# Check recent scan results
ls -la /var/lib/openscap/results/
ls -la /var/lib/openscap/reports/

# View compliance service logs
sudo journalctl -u openscap-compliance.service -n 20

# Test manual scan execution
sudo /usr/local/bin/openscap-compliance-scan.sh

# Verify report generation
sudo /usr/local/bin/openscap-report-generator.sh

# Check disk space usage
du -sh /var/lib/openscap/

Common issues

SymptomCauseFix
Scanner fails with "content not found" Incorrect path to SCAP content files Check /usr/share/xml/scap/ssg/content/ and update config file paths
Permission denied writing reports Incorrect directory permissions sudo chmod 755 /var/lib/openscap/{reports,results,logs}
Systemd timer not running Timer unit not enabled sudo systemctl enable --now openscap-compliance.timer
HTML reports show formatting errors Malformed XML results file Re-run scan with --oval-results flag and check content file integrity
Scan takes too long and times out Resource constraints or large profile Increase TimeoutSec in service unit and add memory limits
Old reports not being cleaned up Retention policy not working Check script has write access to directories and RETENTION_DAYS setting

Next steps

Automated install script

Run this to automate the entire setup

Don't want to manage this yourself?

We handle infrastructure for businesses that depend on uptime. Fully managed, with one fixed contact who knows your setup.

You get one fixed contact who knows your setup

Rotterdam 04:09 · reachable in a message, no ticket form