Agentless security-hardening / compliance scanner for a fleet of Unix hosts.
hardaudit.py connects over SSH and evaluates each host against a baseline of
CIS-style hardening checks, then assigns a 0–100 severity-weighted score plus a
per-check pass/fail breakdown.
It is the configuration-posture sibling of cvescan: cvescan asks
"do I have vulnerable packages?", hardaudit asks "is the box configured securely?".
Same conventions as the rest of the suite: same host-list format as rcmd
and cpwd, paramiko + ThreadPoolExecutor, TXT + JSON reports, and
Nagios-compatible exit codes.
Strictly read-only. It only inspects configuration and state — it never changes
anything. It runs as a non-root user; checks that genuinely require root (e.g. reading
/etc/shadow contents) are reported as unknown rather than guessed.
pip install paramikopython hardaudit.py host_list.txt --user admin --key ~/.ssh/id_ed25519
python hardaudit.py host_list.txt --ask-pass --fail-under 80 --min-severity importanthost_list.txt — one host/IP per line; blank lines and # comments are ignored.
| Flag | Default | Description |
|---|---|---|
--user USER |
current user | SSH username |
--ask-pass |
off | Prompt for an SSH password (default: key/agent auth) |
--key KEYFILE |
— | Private key file |
--port PORT |
22 |
SSH port |
--workers N |
8 |
Parallel SSH connections |
--min-severity LEVEL |
low |
Only report failed checks >= low|moderate|important|critical |
--fail-under SCORE |
off | Flag hosts scoring below SCORE (raises WARNING) |
--log-file FILE |
— | Tee logs to a file |
--output FILE |
hardening_report.txt |
TXT report path (JSON written alongside) |
| ID | Check | Severity | Ref |
|---|---|---|---|
ssh_root_login |
SSH root login disabled | important | CIS 5.2 |
ssh_password_auth |
SSH password auth disabled | moderate | CIS 5.2 |
ssh_empty_passwords |
SSH empty passwords disabled | critical | CIS 5.2 |
ssh_max_auth_tries |
SSH MaxAuthTries ≤ 4 | low | CIS 5.2 |
ssh_x11 |
SSH X11Forwarding disabled | low | CIS 5.2 |
shadow_perms |
/etc/shadow permissions |
important | CIS 6.1 |
passwd_perms |
/etc/passwd permissions |
moderate | CIS 6.1 |
uid0_unique |
Only root has UID 0 | important | CIS 6.2 |
empty_passwords |
No accounts with empty password (root-only) | critical | CIS 6.2 |
firewall |
Host firewall active (ufw/firewalld/nft/iptables) | moderate | CIS 3.5 |
insecure_services |
No telnet/ftp/r-services listening | important | CIS 2.x |
aslr |
ASLR enabled (kernel.randomize_va_space=2) |
moderate | CIS 1.5 |
pass_max_days |
Password max age ≤ 365 days | low | CIS 5.4 |
umask |
Default UMASK 027/077 | low | CIS 5.4 |
crontab_perms |
/etc/crontab permissions |
low | CIS 5.1 |
CIS references are indicative section numbers, not a certified benchmark. Tune the
CHECKStable inhardaudit.pyto your own baseline.
hardening_report.txt— fleet summary (hosts, average score), then hosts sorted worst score first, each with its failed and unknown checks; plus a "not scanned" list.hardening_report.json— full structured data for pipelines/dashboards.
| Code | Meaning |
|---|---|
0 OK |
every check passed on every host |
1 WARNING |
only low/moderate checks failed, or hosts below --fail-under |
2 ERROR |
at least one important/critical check failed |
3 UNKNOWN |
nothing evaluated, but one or more hosts could not be scanned |