How Pavois works

What Pavois is, how it audits the effective configuration, what a target is, and how the harden engine applies remediations.

What Pavois is for

Pavois builds a compliant image. It does not rescue a legacy host that was misconfigured for ten years.

That is not a limitation to apologise for, it is the shape of the problem. A separate /var/log filesystem is decided when the disk is partitioned; a kernel without the KSPP options cannot grow them at runtime. Those controls carry the classes install-time and kernel-build precisely because no apply can close them on a running production box: you do not repartition a machine that is serving traffic.

So the workflow that works is:

  1. Build, a fresh cloud image + the partition recipe + the kernel recipe + a converged harden apply. That is your golden image, and its grade is the grade your fleet starts from.
  2. Ship, bake it (Packer, an image pipeline, a template).
  3. Watch, scan the running fleet to detect drift from the image you shipped, and apply the drift back.

Run it against a legacy host and it will still tell you the truth, in detail, with a grade. It will simply also tell you that about one gap in five needs a rebuild, not a command, and the remediable grade is exactly the score of what it can reach without one. Operating Pavois covers privileges, air-gap, timing and exceptions.

Overview

Pavois is a compliance scanner built on CINC Auditor (the open-source build of Chef InSpec). It checks a Linux system against security standards (CIS, ANSSI-BP-028, NIST, PCI-DSS) and outputs a report graded A:E, structured by chapter, like OpenSCAP, but it audits the effective configuration (sshd -T, sysctl, systemctl show, auditctl -l…), so it catches the Includes and drop-ins that file-based scanners miss.

In one line: pavois scan local --sudo

Setup lives on the Installation page (signed binary or from source) and Get started (first scan in 5 minutes).

Targets

Every command takes a target, what to scan or harden:

Target Transport Example
local local:// pavois scan local
user@host (or an SSH alias) ssh:// pavois scan admin@server1
a container name docker:// pavois scan my-container

Native mode uses your own ~/.ssh/config and routing, no agent to install. Services read as root (sshd, auditd…) need --sudo.

scan, audit & grade

pavois scan <target> [flags], audit the effective config and grade A:E. Pavois auto-detects the target's OS and selects the matching per-OS profile, so you rarely pass --profile (only to point at a custom one).

Flag Effect
--profile optional, override the auto-detected per-OS profile with a path or URL to a custom profile
--standard audit one standard: bp28|cis|pci-dss|nist|stig
--level level (e.g. --standard cis --level 1)
--sudo run as root (effective config of a service)
--sudo-prompt prompt for the sudo password, no echo (implies --sudo)
--on-target run the scan ON the target, far fewer SSH round-trips, much faster
-f, --format table|json|sarif|junit|csv|html
--fail-under N exit code 1 if grade < N% (CI gate)
--engine auto|native|docker
--from grade an existing InSpec JSON (no scan)
--key / --ssh-prompt SSH auth, a key, or prompt for the password (no echo)
pavois scan admin@server1 --key ~/.ssh/id --standard cis --level 1 --fail-under 80
pavois scan local --sudo --format html

Credentials, no password leaks

Passwords reach cinc-auditor only over stdin (--config -), never the command line, so they can't be read from ps, /proc/<pid>/cmdline, or your shell history.

Need Secure way
sudo password --sudo-prompt (asks at the terminal, echo off) or PAVOIS_SUDO_PASSWORD (CI)
SSH password --ssh-prompt (no echo) or PAVOIS_SSH_PASSWORD, prefer a key when you can
pavois scan admin@server1 --sudo-prompt --standard cis      # prompts, no echo
PAVOIS_SUDO_PASSWORD= pavois scan admin@server1 --sudo    # CI / automation

The env vars are unset the moment they're read and stripped from every child process, so nothing inherits them. Avoid the inline --ssh-pass <value> flag, it leaks via ps and history; the prompt and env are the safe paths.

Defaults use_pty (a Debian default) makes native-SSH sudo fail with “Sudo requires a TTY”, regardless of the password. Use --on-target, it runs cinc on the target with a real pty.

harden, plan & apply

Hardening is a four-step, state-aware flow, never a blind script.

1. Plan, scan and write hardening-plan-<os>.yml:

pavois harden plan local --sudo

Every rule appears with its status (compliant or a gap) and an apply: toggle, false by default, nothing changes until you opt in:

rules:
  ssh-disable-root-login:
    status: non-compliant   # a gap
    apply: false            # <- change to true to fix this rule

2. Edit, flip apply: false to true on the rules you want to fix (and true back to false to skip one). baseline_packages work the same way (true removes the package).

3. Apply, feed the edited plan:

pavois harden apply hardening-plan-debian12.yml --dry-run   # preview the Chef recipe
pavois harden apply hardening-plan-debian12.yml --scan       # apply, then re-scan

apply takes a plan file, not a target. It compiles the enabled items into a native Chef run (no bash) and converges.

apply flag Effect
--dry-run compile & print the recipe, do not converge
--scan re-scan after converging (fresh grade)
--standard apply each rule's value for THIS standard; default = most-secure
--reboot reboot at the end when changes need it
--yes skip the confirmation (CI)
--target override the plan's target

4. Validate, prove the apply actually fixed what the plan found:

pavois diff hardening-plan-debian12.yml reports/<post-apply-scan>.json

diff takes a plan or a scan on each side. Plan vs the post-apply scan lists every gap the hardening closed (✔ fixed) and anything that regressed, the proof a hardening round succeeded. (Scan vs scan works too, for before/after.)

verify & diff

  • pavois verify <target>, behavioral validation: it attempts the forbidden action and confirms the protection actually holds (not just that a setting is present).
  • pavois diff <before.json> <after.json>, compares two scan reports: fixed / regressed / (de)activated rules and the grade delta. Ideal before/after a harden apply, or as a CI regression gate.

Output formats & CI

--format controls the output: table (terminal), html (the chaptered report), json, sarif (GitHub code scanning), junit (CI test report), csv.

The exit code is the CI gate: 0 compliant · 1 below --fail-under · 2 technical error (CINC's own 100/101 never reach your shell). Full reference. Combine with --fail-under:

# GitHub Actions
- run: pavois scan local --sudo --standard cis --format sarif --out report
- uses: github/codeql-action/upload-sarif@v3
  with: { sarif_file: report }

pavois serve --port 8098 serves the HTML reports over HTTP.

The A:E grade behind --fail-under is a published, frozen formula, weights, per-severity caps and the critical-failure rule are spelled out in How the A:E grade is computed.

Profiles & standards

A profile is the set of controls run. Pavois auto-detects the OS and uses the matching per-OS profile under profiles/linux/<os> (e.g. profiles/linux/debian12); you can also pass --profile with a path or URL to a custom one.

pavois profiles lists them; Pavois standards (alias normes) explains the standards and their levels. A standard is a view, one control carries N mappings (CIS / ANSSI / NIST / PCI), never a duplicated rule. Each OS targets its own benchmark version. See the standards pages.

JSON API

Pavois exposes its reference as JSON, the consumable contract:

pavois norms --pretty                 # the standard catalogue (versions, authorities, coverage)
pavois rules --os debian12 --standard cis   # the rule base, filtered

pavois rules takes --os, --standard, --domain, --pretty; pavois norms takes --pretty only. The full reference is generated from the binary.