Set Account Expiration Following Inactivity
Set the INACTIVE default in /etc/default/useradd to a value between 0 and 30 days, so an account whose password has expired is automatically disabled after at most 30 days of inactivity instead of staying open indefinitely.
Checked against the content of a persistent configuration file, the source of truth that survives reboots.
A mapping is a cross-reference to where each standard places this requirement, anchored and cross-validated, not a claim of equivalence. A passing check is evidence toward these references, how to read it.
Why this rule matters
Inactive identifiers pose a risk to systems and applications because attackers may exploit an inactive identifier and potentially obtain undetected access to the system. Disabling inactive accounts ensures that accounts which may not have been responsibly removed are not available to attackers who may have compromised their credentials. Owners of inactive accounts will not notice if unauthorized access to their user account has been obtained.
What Pavois checks
Pavois runs awk -F= '/^INACTIVE=/{v=$2} ...' /etc/default/useradd and keeps the last INACTIVE= assignment in the file, exactly as useradd itself would: the value must be a positive integer in the 0..30 range. A commented, duplicated or -1 (never disable) value therefore fails. This default is what useradd stamps into the inactive field of /etc/shadow for every account it creates, so it governs future accounts, not the ones already on the system.
describe command('awk -F= \'/^INACTIVE=/{v=$2} END{print (v ~ /^[0-9]+$/ && v>=0 && v<=30)?"ok":"ko"}\' /etc/default/useradd') do
its('stdout.strip') { should eq 'ok' }
endHow to verify it is applied
Run useradd -D | grep INACTIVE (which prints the resolved default, not the raw file) and expect:
INACTIVE=30
For an existing account, check the value actually stored in the shadow database with chage -l <user> and look at Password inactive / Account expires.
Inspect & investigate
Setting the default produces no log entry: it is a static value in /etc/default/useradd. Its effect is visible only when a stale account is refused at login, in /var/log/auth.log (Debian/Ubuntu) or journalctl -u sshd (RHEL family):
sshd[1234]: pam_unix(sshd:account): account alice has expired (inactive password)
If the file watch on /etc/default/useradd is in place, a modification also raises an auditd event: grep 'key="usergroup_modification"' /var/log/audit/audit.log.
Remediation
The Pavois harden plan runs the command useradd -D -f 30, which rewrites the INACTIVE= line of /etc/default/useradd through the shadow tooling itself (no hand-edited file). It is guarded by a not_if that re-runs the same awk test, so the command only fires when the value is out of range and the apply stays idempotent. It sets the default for new accounts; existing users keep their current setting until you run chage --inactive 30 <user> on them.
Pavois applies this with its own harden engine, the plan below, not a shell script:
| command | useradd -D -f 30 |
|---|---|
| name | set-default-inactive |
| not_if | awk -F= '/^INACTIVE=/{v=$2} END{exit !(v ~ /^[0-9]+$/ && v>=0 && v<=30)}' /etc/default/useradd |
| resource | exec |
pavois harden plan localwhere the target is local, a user@host SSH alias, or a container , Docs
Impact & precautions
Leaving INACTIVE=-1 means an account whose password expired stays usable forever, which is exactly the dormant-account foothold attackers look for. Conversely, propagating a short inactivity window to existing accounts is where the danger lies: a service or admin account that has not logged in for more than 30 days past its password expiry will be locked out on the spot. Before applying, list the accounts you are about to touch (chage -l on each) and make sure emergency access does not depend on a rarely used account; the harden plan itself only changes the default and cannot lock you out.
Standards mapping
| Standard | Reference | Type | Version | Confidence |
|---|---|---|---|---|
| CIS | 5.4.1.5 | direct | per OS, see the benchmark table | high |
| NIST | IA-4(e), AC-2(3), CM-6(a) | supporting | 800-53 Rev 5 · 800-171 Rev 2 (pinned) | medium |
| PCI DSS | 8.2.6 | supporting | 4.0.1 | medium |
Each reference is a cross-reference anchored in the upstream benchmark and cross-validated against the SCAP Security Guide and ansible-lockdown, not a claim of equivalence. Direct = a prescriptive, line-level requirement; supporting = an abstract control family (NIST) the check provides evidence toward. How to read a mapping.