Skip to main content
PathMon

Chapter 14 of 26

Compliance Overview

Updated Read the full guide

What Compliance Scanning Is

Compliance scanning evaluates your hosts against published security benchmarks: CIS Benchmarks for the operating system and Docker Bench for Security for container hosts. Results are reported per rule (pass, fail, warning) back to the web UI, giving you a fleet-wide compliance score, per-host rule detail, and optional auto-remediation for failed rules.

Scanning is performed by the PatchMon agent on each host, not by the server. The agent runs the scanner locally, parses the output, and submits structured results back to the server via POST /api/v1/compliance/scans. The server aggregates the data into dashboards and rule views.

This page covers the overall model: what the scanners are, how SSG content is delivered in 2.0, the module gate, and the permission matrix. For walkthroughs of actually running scans and reading results, see Running Compliance Scans and Results and Remediation.


Module Gate

All compliance UI and API routes are gated by the compliance capability module. Some plans (smaller tiers) do not include compliance at all; on those plans the Security Compliance sidebar item is hidden and the corresponding API endpoints return 403.

UI area Required module
Security Compliance page (all tabs) compliance
Host Detail → Compliance tab compliance
Compliance-related per-host settings (mode, scanner toggles, default profile) compliance

If the module is disabled, the Host Detail page shows an "Upgrade required" placeholder in the Compliance tab and the dashboard hides compliance cards.


Permission Matrix

Compliance uses three RBAC permissions on top of the module gate. Each API route applies a specific combination:

Action Required permission Example route
View the dashboard, scan history, host compliance detail, rule detail, trends, and active scan list can_view_reports GET /compliance/dashboard, GET /compliance/scans/{hostId}
Trigger scans (single or bulk), cancel a running scan, install the scanner, upgrade SSG content, trigger per-rule remediation can_manage_compliance POST /compliance/trigger/{hostId}, POST /compliance/cancel/{hostId}, POST /compliance/remediate/{hostId}
Change per-host compliance mode, per-host scanner toggles (OpenSCAP / Docker Bench), default profile for a host can_manage_hosts POST /hosts/{hostId}/integrations/compliance/mode, POST /hosts/{hostId}/integrations/compliance/scanners

In practice a "compliance operator" role typically has can_view_reports + can_manage_compliance; a "host owner" role typically has can_manage_hosts so they can enable or disable compliance on their own hosts. A pure auditor role with can_view_reports alone can see everything but cannot change anything.

Note: The release-notes shorthand of "can_view_reports and can_manage_hosts" doesn't quite line up with the handler: triggering a scan requires can_manage_compliance, not can_manage_hosts. Use the table above as the source of truth.


The Two Scanners

The compliance integration on the agent (patchmon-agent/internal/integrations/compliance/compliance.go) runs two independent scanners. A "scan" as submitted to the server is actually an array of sub-scans, one per scanner that ran successfully.

1. OpenSCAP: CIS Benchmarks

What it is. OpenSCAP is the OS-level security compliance scanner. On supported Linux distributions it evaluates the host against the CIS Benchmark datastreams published by SCAP Security Guide (SSG). The agent picks the relevant ssg-*-ds.xml datastream for the host's OS and runs oscap xccdf eval against it.

Profile levels. Each datastream ships with two CIS-derived profiles:

  • CIS Level 1 Server (level1_server): the baseline profile, intended for general-purpose systems with minimal operational impact. This is the default profile used for ad-hoc scans.
  • CIS Level 2 Server (level2_server): the extended profile, for environments that require defence-in-depth (classified/regulated workloads). Some L2 rules impose real operational restrictions (e.g. disabling wireless where it's present).

The per-host default profile setting (Host Detail → Integrations → Compliance) controls which profile is used for scheduled scans. A manual scan can override the default by passing profile_id in the trigger request; the Host Detail Compliance tab exposes this as "Pick a profile".

Supported operating systems (as surfaced by the Compliance Settings panel):

OS Profiles shipped in SSG
Ubuntu CIS Level 1 Server, CIS Level 2 Server
Debian CIS Level 1 Server, CIS Level 2 Server
RHEL CIS Level 1 Server, CIS Level 2 Server
CentOS CIS Level 1 Server, CIS Level 2 Server
Rocky Linux CIS Level 1 Server, CIS Level 2 Server
AlmaLinux CIS Level 1 Server, CIS Level 2 Server
Fedora CIS Level 1 Server, CIS Level 2 Server
SLES CIS Level 1 Server, CIS Level 2 Server
OpenSUSE CIS Level 1 Server, CIS Level 2 Server

Any host OS not listed has no SSG datastream available and OpenSCAP scans will be skipped on it. The scanner is still "available" in the integrations metadata if oscap is installed; it just has nothing to evaluate.

SLES and OpenSUSE: the datastreams above ship with the server and the agent already maps SUSE-family hosts to the right profiles, but these rows are not usable yet in practice. SUSE hosts cannot complete enrolment because the agent has no zypper backend (see the package manager table earlier in this guide), so they never reach the point of running a scan. These profiles become available once zypper support lands.

Default per-host state: OpenSCAP is enabled by default on every host that has compliance turned on. This is controlled by the compliance_openscap_enabled host flag, defaulted to true in 1.4.2 and preserved on upgrade.

2. Docker Bench for Security

What it is. Docker Bench is the container-host security scanner from the Center for Internet Security. It evaluates the Docker daemon, its configuration files, running containers, images, and Swarm configuration against the CIS Docker Benchmark. Rules are categorised into sections:

  • Host Configuration
  • Docker Daemon Configuration
  • Docker Daemon Configuration Files
  • Container Images and Build File
  • Container Runtime
  • Docker Security Operations
  • Docker Swarm Configuration

Results use a different status model from OpenSCAP: instead of pass/fail, most Docker Bench rules either pass or emit a warning. There are very few hard fails. The Compliance dashboard surfaces Docker Bench statistics separately in the "Docker Bench Analysis" section, with "Warnings by Section" charts instead of the severity-based ones used for OpenSCAP.

When it runs. Docker Bench runs only when both of the following are true:

  1. The Docker integration is enabled on the host (the scanner reads the same Docker socket).
  2. Docker Bench is enabled on the per-host scanner toggle.

If either is off, Docker Bench is skipped even if the binary is installed.

Default per-host state: Docker Bench is disabled by default on every host (per 1.4.2). You must explicitly toggle it on per host (Host Detail → Integrations → Compliance → Docker Bench). Most hosts do not run Docker, and running Docker Bench on a host without Docker produces a long list of misleading "Docker daemon not running" failures.


Per-Host Scanner Configuration

Every host that has compliance enabled exposes four compliance-related fields, manageable from the Host Detail → Integrations → Compliance panel or from the Compliance page's Hosts tab:

Field Values Meaning
compliance_mode disabled, on-demand, enabled Overall compliance switch for this host. disabled means the agent does not run any scanner. on-demand means scans run only when manually triggered. enabled means scans run on the fleet-wide compliance_scan_interval.
compliance_openscap_enabled true / false Whether OpenSCAP runs on this host. Default true.
compliance_docker_bench_enabled true / false Whether Docker Bench runs on this host. Default false.
compliance_default_profile_id profile ID or null The OpenSCAP profile used for scheduled / "all profiles" scans on this host. Null means the agent defaults to level1_server.

Changes to these fields are queued as pending config and pushed to the agent on the next heartbeat via the Apply Pending Config flow (see Managing the PatchMon Agent). They do not take effect until the agent confirms receipt.

Mode: disabled vs on-demand vs enabled

  • Disabled: nothing runs on this host. The scanner integration is marked off and the Compliance UI shows "Disabled" in the Mode column.
  • On-demand: the scheduled-scan path is disabled but manual Run Scan buttons still work. Use this when you only want to scan on investigation.
  • Enabled: the agent runs scheduled scans at the fleet-wide interval set in Security Compliance → Settings → Scan Interval (default 24 hours, configurable from 6 hours to 7 days).

The fleet-wide default compliance mode (Security Compliance → Settings → Default Compliance Mode) applies only to newly registered hosts. Existing hosts keep their current mode across server upgrades.


SSG Content Is Bundled in the Server Binary

This is one of the most important architectural changes in 2.0 for compliance.

In 1.x and earlier, each agent fetched SCAP Security Guide (SSG) content from GitHub at scan time. That required every agent to have outbound access to github.com, created occasional transient failures when GitHub was unavailable, and allowed agents to drift to different SSG versions depending on when they last pulled.

In 2.0, SSG and CIS benchmarking content is bundled into the server image at build time and served from a single SSG_CONTENT_DIR on the server. Agents now fetch content from the server itself, via two new endpoints:

  • GET /api/v1/compliance/ssg-version, returns the SSG version string and the list of ssg-*-ds.xml files available.
  • GET /api/v1/compliance/ssg-content/{filename}, streams a specific datastream file to the agent.

Both endpoints accept agent API-key authentication.

What this means operationally

  • No external network calls at scan time. The server is the only place an agent gets SSG content from. Agents never contact github.com for it under any circumstance, so air-gapped fleets need nothing beyond the server access they already had for heartbeats.
  • One SSG version across the fleet. Every agent gets the same content bundle. The Compliance Settings page shows the active version and the list of content files under OpenSCAP Content.
  • Version-pinned scanning. Because the image ships with the content, upgrading the server is the way to get new SSG rules. Hosts pick the new version up automatically on the daily content check, and you can push it to a single host sooner with the API call described under "Upgrading SSG content on a host".
  • A server with no content fails loudly. If the content directory is empty, for example because a volume was mounted over it, the server logs a warning at startup, Compliance Settings reports the content as unavailable, and agents are told so explicitly rather than quietly falling back to some other source.

Where to see the active version

Security Compliance → Settings → OpenSCAP Content shows:

  • The SSG version string (e.g. 0.1.77).
  • The number of content files bundled.
  • A collapsible list of every ssg-*-ds.xml filename.
  • The table of supported OSes and their profiles.

Where Compliance Lives in the UI

There are three ways in:

  1. Security Compliance (top-level sidebar): the fleet-wide view. Five tabs: Overview (dashboard), Hosts (per-host table with scan controls), Scan Results (drill into rules), History (chronological scan list), Settings (default mode, interval, SSG content).
  2. Hosts → select a host → Compliance tab: per-host drill-down with the same scan controls, latest scan summary, rule breakdown by status / severity / section, and a per-rule remediation action.
  3. Dashboard → Compliance cards: the main PatchMon dashboard includes a compliance summary card that deep-links into the Compliance page with the relevant filter applied. Hidden when the compliance module is disabled.

The top of every compliance view has five status cards: Total hosts, Compliant, Warning, Critical, Never scanned. Clicking "Never scanned" filters the Hosts tab to just the never-scanned subset so you can fix coverage gaps.


What a Scan Looks Like End-to-End

A typical ad-hoc scan flows like this:

  1. An operator clicks Run Scan on a host (either from the Compliance Hosts tab or from the Host Detail page).
  2. The browser calls POST /compliance/trigger/{hostId}. The server clears any stale "cancel" flag for this host in Redis, enqueues a run_scan task on the compliance asynq queue, and returns the job ID.
  3. The task dequeues and sends a run_scan WebSocket command to the agent. The server updates the compliance_scans record to running status as soon as the agent confirms receipt.
  4. The agent runs one or both scanners in sequence. OpenSCAP calls oscap xccdf eval against the SSG datastream; Docker Bench calls docker-bench-security (bundled with the agent).
  5. Each sub-scan produces structured rule results. The agent batches them into a CompliancePayload and submits via POST /api/v1/compliance/scans.
  6. The server's ReceiveScans handler validates API credentials, applies a 10-requests-per-minute rate limit, and writes the scan + results into the database in a single transaction. It honours the per-host openscap_enabled and docker_bench_enabled flags on the server side too, so accidental submissions from a scanner the host has disabled are rejected.
  7. On success, the server emits a compliance_scan_completed notification event (with per-profile summaries) and, if any scan errored, a separate compliance_scan_failed event.
  8. The UI's active-scan poll sees the row disappear from the active_scans endpoint, shows a toast "Compliance scan completed", and the dashboard refetches.

Scheduled scans go through the same run_scan → agent → ReceiveScans pipeline; only the initiator differs.


Stuck Scans and Auto-Cleanup

Compliance scans can take a long time: a full OpenSCAP L2 scan on a mid-size host can run 15–45 minutes, so PatchMon has an explicit stall detection threshold rather than a short timeout.

A scan is considered stalled if it has been in running status for more than 3 hours without completing. A recurring asynq job (ComplianceScanCleanup, at POST /api/v1/compliance/scans/cleanup) runs periodically and moves every stalled scan to a terminal state with the error message Scan terminated automatically after running for more than 3 hours. This guarantees the Scans in Progress widget doesn't accumulate ghost scans and frees up the per-host "currently scanning" flag.

The GET /compliance/scans/stalled endpoint lets you see which scans are about to be cleaned up. The Compliance page exposes this via the stalled-scans widget (when any rows exist).


  • Running Compliance Scans: triggering scans, bulk scan modal, cancelling, handling stuck scans.
  • Results and Remediation: reading the dashboard, drilling into host detail and rule detail, auto-remediation.
  • Docker Monitoring: Docker integration prerequisite for Docker Bench.
  • Managing the PatchMon Agent: Apply Pending Config flow used to push compliance toggles to agents.
  • Release Notes - 1.4.0: the release that introduced compliance scanning.
  • Release Notes - 1.4.2: per-host scanner toggles, scan cancel, 3h auto-cleanup.
  • Release Notes - 2.0.0: bundled SSG content and the rewrite.