Description
/etc/login.defs and useradd -D define what new local accounts get:
PASS_MAX_DAYS: maximum age of a password (Ubuntu: 99999, never expires);PASS_MIN_DAYS: minimum days between two changes (Ubuntu: 0);PASS_WARN_AGE: days of warning before expiry (Ubuntu: 7);ENCRYPT_METHOD: hash used bychpasswdandnewusers(Ubuntu 20.04 to 24.04: SHA512, 26.04: YESCRYPT; the shadow tools of 20.04 do not support YESCRYPT);INACTIVE(useradd -D): days after password expiry before the account is disabled (Ubuntu: never).
NIST SP 800-63B advises against forced periodic changes when passwords are long and checked
against breach lists. Set max_days to 99999 in your tailoring file if you follow that model
and rely on the quality and lockout controls instead.
Rationale
A password that is never renewed and an account that is never disabled let a leaked password, or a departed user, keep access indefinitely. A minimum age stops users from cycling through the history to get their old password back.
Impact
New accounts must change their password every {{ max_days }} days and are disabled {{ inactive_days }} days after it expires. Existing accounts are handled by CB-UBU-AUTH-015.
Setting
| Location | /etc/login.defs, /etc/default/useradd |
| Default | PASS_MAX_DAYS 99999, PASS_MIN_DAYS 0, PASS_WARN_AGE 7, INACTIVE -1 |
| Recommended | PASS_MAX_DAYS <= {{ max_days }}, PASS_MIN_DAYS >= {{ min_days }}, PASS_WARN_AGE >= 7, YESCRYPT or SHA512, INACTIVE <= {{ inactive_days }} |
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
- In
/etc/login.defs:PASS_MAX_DAYS<= {{ max_days }},PASS_MIN_DAYS>= {{ min_days }},PASS_WARN_AGE>= 7,ENCRYPT_METHODisYESCRYPTorSHA512. useradd -D | grep INACTIVEshows a value between 0 and {{ inactive_days }}.
Automated: every check must pass. Scanners report each check by its id.
login-defs
Aging and hashing settings in /etc/login.defs.
script: |
get() { sed -n "s/^[[:space:]]*$1[[:space:]]\{1,\}\([^[:space:]]*\).*/\1/p" /etc/login.defs | tail -n 1; }
max=$(get PASS_MAX_DAYS) min=$(get PASS_MIN_DAYS) warn=$(get PASS_WARN_AGE) enc=$(get ENCRYPT_METHOD)
echo "PASS_MAX_DAYS=$max PASS_MIN_DAYS=$min PASS_WARN_AGE=$warn ENCRYPT_METHOD=$enc"
[ "${max:-99999}" -le "$CB_MAX_DAYS" ] && [ "${min:-0}" -ge "$CB_MIN_DAYS" ] && [ "${warn:-0}" -ge 7 ] \
&& { [ "$enc" = YESCRYPT ] || [ "$enc" = SHA512 ]; }
inactive
Default INACTIVE of useradd.
script: |
i=$(useradd -D | sed -n 's/^INACTIVE=//p')
echo "INACTIVE=$i"
[ "${i:--1}" -ge 0 ] && [ "${i:--1}" -le "$CB_INACTIVE_DAYS" ]
Remediation
- Set in
/etc/login.defs:PASS_MAX_DAYS {{ max_days }},PASS_MIN_DAYS {{ min_days }},PASS_WARN_AGE 7and keepENCRYPT_METHODatYESCRYPT(orSHA512; onlySHA512on Ubuntu 20.04). - Run
useradd -D -f {{ inactive_days }}.
| Restart | none |
| Disruption | none |
Rollback
Restore /etc/login.defs and /etc/default/useradd from the backup.
defaults
f=/etc/login.defs
cb_backup "$f"
cb_backup /etc/default/useradd
set_def() {
if grep -Eq "^[[:space:]]*$1[[:space:]]" "$f"; then sed -i "s/^[[:space:]]*$1[[:space:]].*/$1\t$2/" "$f"; else printf '%s\t%s\n' "$1" "$2" >>"$f"; fi
}
set_def PASS_MAX_DAYS "$CB_MAX_DAYS"
set_def PASS_MIN_DAYS "$CB_MIN_DAYS"
w=$(sed -n 's/^[[:space:]]*PASS_WARN_AGE[[:space:]]\{1,\}\([0-9]*\).*/\1/p' "$f" | tail -n 1)
[ "${w:-0}" -ge 7 ] || set_def PASS_WARN_AGE 7
e=$(sed -n 's/^[[:space:]]*ENCRYPT_METHOD[[:space:]]\{1,\}\([A-Z0-9]*\).*/\1/p' "$f" | tail -n 1)
h=YESCRYPT
[ "$(. /etc/os-release && printf '%s' "$VERSION_ID")" != 20.04 ] || h=SHA512
case $e in YESCRYPT | SHA512) ;; *) set_def ENCRYPT_METHOD "$h" ;; esac
useradd -D -f "$CB_INACTIVE_DAYS"
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
- login.defs(5), Ubuntu manual pages Canonical, vendor-doc, accessed 2026-10-19
- useradd(8), Ubuntu manual pages Canonical, vendor-doc, accessed 2026-10-19
- NIST SP 800-63B - Digital Identity Guidelines, Authentication NIST, standard, accessed 2026-10-19
Mappings
| Framework | ID | Relation |
|---|---|---|
| NIST SP 800-53 Rev. 55.1.1 | IA-5(1) | partial |
| 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
- related
CB-UBU-AUTH-015Apply the password aging policy to existing accounts
People and lifecycle
| Authors |
|
| Verified | 2026-10-19 by Sn0wAlice on 20.04, Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic. Fails on a fresh install as expected; generated fix, check again and second fix idempotent; kept after reboot; profiles core and server-hardened. |
| Verified | 2026-10-19 by Sn0wAlice on 22.04, Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic. Fails on a fresh install as expected; generated fix, check again and second fix idempotent; kept after reboot; profiles core and server-hardened. |
| Verified | 2026-10-19 by Sn0wAlice on 24.04, Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic. Fails on a fresh install as expected; generated fix, check again and second fix idempotent; kept after reboot; profiles core and server-hardened. |
| Verified | 2026-10-19 by Sn0wAlice on 26.04, Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic. Fails on a fresh install as expected; generated fix, check again and second fix idempotent; kept after reboot; profiles core and server-hardened. |
| Introduced in | v0.1.0 |
| Created, updated | 2026-10-19, 2026-10-19 |
| Translations | fr |