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:
- 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. - Ship, bake it (Packer, an image pipeline, a template).
- 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 htmlCredentials, 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 / automationThe 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 --sudoEvery 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 rule2. 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
applytakes 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>.jsondiff 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 aharden 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, filteredpavois rules takes --os, --standard, --domain, --pretty; pavois norms takes --pretty only. The full reference is generated from the binary.