Description
Each account has its own aging fields in /etc/shadow (minimum and maximum age, warning
period, inactivity period, date of last change), copied from the defaults when it was created.
This control applies the policy of CB-DEB-AUTH-014 to every existing account that has a
password, and checks that no last change date lies in the future.
Rationale
Without this step, the aging policy only protects accounts created after it was set. A last change date in the future would postpone expiry indefinitely.
Impact
An account whose password is older than {{ max_days }} days is asked to change it at its next password login, and is disabled {{ inactive_days }} days later. SSH key logins of such an account are also refused until the password is changed: check service and automation accounts before applying.
Parameters
| Parameter | Core | Server | Server hardened | Desktop | Desktop hardened | Bounds |
|---|---|---|---|---|---|---|
max_daysinteger, days. Maximum password age in days. | 365 | 365 | 365 | 365 | 365 | 1 to 99999 |
min_daysinteger, days. Minimum days between password changes. | 0 | 0 | 1 | 0 | 1 | 0 to 7 |
inactive_daysinteger, days. Days after password expiry before the account is disabled. | 45 | 45 | 45 | 45 | 45 | 0 to 365 |
The value used is your override (tailoring file), else the profile's, else the one of the profile it extends, else the default. Bounds limit what an override may set.
Audit
For every account with a password hash in /etc/shadow (field 2 starting with $), check
with chage -l <user>: maximum <= {{ max_days }}, minimum >= {{ min_days }}, warning >= 7,
inactive between 0 and {{ inactive_days }}, and a last change date that is not in the future.
Automated: every check must pass. Scanners report each check by its id.
accounts
Aging fields of every account with a password.
script: |
today=$(( $(date +%s) / 86400 ))
bad=$(awk -F: -v max="$CB_MAX_DAYS" -v min="$CB_MIN_DAYS" -v inact="$CB_INACTIVE_DAYS" -v today="$today" '
$2 ~ /^\$/ {
r = ""
if ($5 == "" || $5 > max) r = r " max=" $5
if ($4 == "" || $4 < min) r = r " min=" $4
if ($6 == "" || $6 < 7) r = r " warn=" $6
if ($7 == "" || $7 < 0 || $7 > inact) r = r " inactive=" $7
if ($3 != "" && $3 > today) r = r " last_change_in_future"
if (r != "") print $1 ":" r
}' /etc/shadow)
[ -z "$bad" ] && { echo "all accounts follow the policy"; exit 0; }
printf '%s\n' "$bad"
exit 1
Remediation
For each account with a password: chage --maxdays {{ max_days }} --mindays {{ min_days }} --warndays 7 --inactive {{ inactive_days }} <user>. Fix last change dates in the future with
chage --lastday <YYYY-MM-DD> <user>.
| Restart | none |
| Disruption | low |
| Lockout risk | yes: can cut off administrative access. Off by default in generated scripts and playbooks, opt in through a tailoring file. |
Rollback
Restore /etc/shadow from the backup, or reset each account with chage.
chage
cb_backup /etc/shadow
awk -F: '$2 ~ /^\$/ {print $1}' /etc/shadow | while read -r u; do
chage --maxdays "$CB_MAX_DAYS" --mindays "$CB_MIN_DAYS" --warndays 7 --inactive "$CB_INACTIVE_DAYS" "$u"
done
Handwritten code reads parameters from CB_<NAME> environment variables. Each release turns these fixes into remediate.sh and an Ansible role per profile: dry run by default, a fix runs only when its check fails.
References
- chage(1), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
- shadow(5), Debian manual pages Debian, vendor-doc, accessed 2026-10-18
Mappings
| Framework | ID | Relation |
|---|---|---|
| NIST SP 800-53 Rev. 55.1.1 | AC-2(3) | partial |
How this control relates to the other framework's: equivalent, stricter, looser, partial or related.
Threats mitigated
| Taxonomy | ID |
|---|---|
| MITRE ATT&CK | T1078.003 |
Related controls
- requires
CB-DEB-AUTH-014Set password aging and hashing defaults for new accounts
People and lifecycle
| Authors |
|
| Verified | 2026-10-18 by Sn0wAlice on 12, Proxmox VM, Debian 12 genericcloud image, kernel 6.1.0-54-cloud-amd64. Generated check, fix and check again for profiles core and server-hardened; state kept after reboot. |
| Verified | 2026-10-18 by Sn0wAlice on 13, Proxmox VM, Debian 13 genericcloud image, kernel 6.12.113+deb13-cloud-amd64. Generated check, fix and check again for profiles core and server-hardened; state kept after reboot. |
| Introduced in | v0.1.0 |
| Created, updated | 2026-10-18, 2026-10-18 |
| Translations | fr |