LFCS 34 ๐ง Password Policies โ chage
A password that never expires is a password that is eventually compromised. An attacker who obtains a password hash has unlimited time to crack it, and the longer the password remains valid, the wider the window during which a leak is exploitable. Password aging is the policy that forces passwords to change on a schedule, warns users before the deadline, and disables accounts that ignore the warning. On Linux, the chage command is the tool that sets and inspects these policies. It writes to /etc/shadow, and its effects are per-user, per-field, and visible to the passwd command as it prompts users for changes.
Key point: chage modifies the password aging fields in /etc/shadow, not the password itself. The passwd command reads those fields and enforces the policy at login. The two commands are complementary: passwd sets the password, and chage sets the rules for how often it must change.
Why password aging exists
The long-lived credential problem. A password that has been in use for years has had years of exposure to phishing, shoulder surfing, database leaks, and reuse on other sites. Aging forces a rotation, which limits the value of any single credential that leaks.
The dormant-account problem. An account that has not been used in months is a liability. The user may have left the organization, the password may have been shared, and no one notices because nothing is being accessed. An inactivity period locks the account automatically after a defined period of non-use following password expiration.
The weak-password problem. Users choose weak passwords when they are not forced to change them. Aging does not make the password strong, but it ensures that a weak password does not remain in use indefinitely. Combined with complexity requirements, aging is one of the few policy levers that operates on existing credentials.
The compliance problem. Security frameworks such as PCI DSS, HIPAA, and NIST SP 800-53 require password aging or its modern equivalent. The LFCS exam tests the commands because the practice is standard in regulated environments.
The visibility problem. Users do not know when their password expires unless the system tells them. The warning field in /etc/shadow controls how many days before expiration the passwd command starts warning the user, and how many days the login prompt displays the warning.
The lockout problem. A password that expires and is not changed should not remain valid indefinitely. The inactivity field controls how many days after expiration the account is locked. Without it, an expired password can be changed by the user at any time in the future, which means the password may never actually change.
a. The chage command
The chage command displays or modifies the password aging fields in /etc/shadow. It has two modes: interactive and flag-based. The flag-based mode is what appears in scripts and in exam tasks.
The list mode
The -l flag lists the current aging information for a user.
sudo chage -l alice
The output shows the last password change, the expiration date, the inactivity period, the account expiration, the minimum and maximum days, and the warning days.
Last password change : Jan 15, 2026
Password expires : Apr 15, 2026
Password inactive : Apr 25, 2026
Account expires : Dec 31, 2026
Minimum number of days between password change : 7
Maximum number of days between password change : 90
Number of days of warning before password expires : 14
The -l flag is read-only. It does not change any field.
The maximum days
The -M flag sets the maximum number of days a password is valid. After this many days, the user must change the password.
sudo chage -M 90 alice
The value 90 means the password expires 90 days after the last change. The special value -1 removes the expiration entirely. The special value 99999 is also used to mean no expiration.
The minimum days
The -m flag sets the minimum number of days between password changes. The user cannot change the password again until this many days have passed.
sudo chage -m 7 alice
The value 7 means the user cannot change the password within seven days of the last change. This prevents a user from cycling through passwords immediately to return to an old one. The special value 0 removes the minimum.
The warning days
The -W flag sets the number of days before expiration that the user is warned.
sudo chage -W 14 alice
The value 14 means the user is warned starting 14 days before expiration. The warning appears at login and as part of the passwd prompt.
The inactivity period
The -I flag sets the number of days after the password expires before the account is locked.
sudo chage -I 30 alice
The value 30 means the account is locked 30 days after the password expires, if the password has not been changed. The special value -1 disables the inactivity lock.
The account expiration date
The -E flag sets the date on which the account expires. The format is YYYY-MM-DD or the number of days since January 1, 1970.
sudo chage -E 2026-12-31 alice
sudo chage -E 0 alice # Immediately expired
sudo chage -E -1 alice # No expiration
The value 0 means the account is expired as of January 1, 1970, which is immediately. The value -1 means the account never expires. The date is stored as a number of days since the epoch.
The last change date
The -d flag sets the date of the last password change.
sudo chage -d 2026-01-15 alice
sudo chage -d 0 alice # Force change on next login
Setting the date to 0 is a way to force a password change on the next login, because the value is interpreted as January 1, 1970, and the password is considered expired.
The interactive mode
Running chage with a username and no flags enters interactive mode, which prompts for each field.
sudo chage alice
The prompts walk through each field, and pressing Enter keeps the current value. The interactive mode is convenient for one-off changes but is not scriptable.
The force-change pattern
The -d 0 flag and the passwd -e command both force a password change on the next login. The two are equivalent in effect.
sudo chage -d 0 alice
sudo passwd -e alice
The passwd -e form is shorter and is the more common idiom.
b. The shadow fields
The chage command writes to specific fields in /etc/shadow. The file has nine colon-separated fields, and chage controls seven of them.
The shadow file format
alice:$6$...:19737:7:90:14:30:20454:
The fields are: username, password hash, days since epoch of last change, minimum days, maximum days, warning days, inactivity days, account expiration days, and a reserved field.
| Field | Position | chage Flag | Meaning |
|---|---|---|---|
| Username | 1 | โ | The account name |
| Hash | 2 | passwd | The password hash |
| Last change | 3 | -d | Days since epoch |
| Minimum | 4 | -m | Days before next change allowed |
| Maximum | 5 | -M | Days before expiration |
| Warning | 6 | -W | Days of warning |
| Inactivity | 7 | -I | Days after expiration before lock |
| Expiration | 8 | -E | Days since epoch |
| Reserved | 9 | โ | Unused |
The days-since-epoch format
The dates in /etc/shadow are stored as the number of days since January 1, 1970. The chage -l output converts them to human-readable dates, but the file itself contains integers.
sudo grep alice /etc/shadow
# alice:$6$...:19737:7:90:14:30:20454:
The number 19737 corresponds to the date the password was last changed. The number 20454 corresponds to the account expiration date.
The conversion
The date command can convert between the two formats.
date -d "1970-01-01 + 19737 days"
# The last change date.
date -d "2026-12-31" +%s
# Seconds since epoch; divide by 86400 for days.
The conversion is rarely needed because chage -l and chage -E accept and display human-readable dates.
The empty field
An empty field in /etc/shadow means the value is not set. The chage -l output shows “never” or “password must be changed” depending on the field.
alice:$6$...:19737:0:99999:7:::
The empty inactivity and expiration fields mean the account never locks from inactivity and never expires. This is the default on many distributions.
The password hash field
The chage command does not modify the password hash field. That field is controlled by passwd. A ! at the beginning of the hash means the password is locked. The chage command does not add or remove the !.
sudo passwd -l alice
sudo grep alice /etc/shadow
# alice:!$6$...:...
The ! remains until passwd -u removes it, regardless of the aging policy.
c. Default policies
New accounts inherit their aging settings from /etc/login.defs. The chage command modifies individual accounts; the default policy is modified in the configuration file.
The login.defs settings
The relevant settings are PASS_MAX_DAYS, PASS_MIN_DAYS, and PASS_WARN_AGE.
PASS_MAX_DAYS 90
PASS_MIN_DAYS 7
PASS_WARN_AGE 14
These values apply to accounts created after the file is modified. Existing accounts are not affected.
The modification
The file is edited directly with a text editor.
sudo vim /etc/login.defs
After the change, new accounts created with useradd inherit the new values. Existing accounts must be updated individually with chage.
The bulk update
To update all existing accounts, a loop over the users is needed.
for user in $(awk -F: '$3 >= 1000 { print $1 }' /etc/passwd); do
sudo chage -M 90 -m 7 -W 14 "$user"
done
The awk command extracts usernames with UID 1000 or greater, which are the regular users. The loop applies the new policy to each.
The system account exclusion
System accounts typically have no aging policy. The PASS_MAX_DAYS value in /etc/login.defs applies to them as well, which can cause system accounts to be locked when their passwords expire. The convention is to set the aging fields for system accounts to -1 or to a high value, or to lock the password with ! so that aging is irrelevant.
sudo chage -M -1 -m 0 -I -1 -E -1 daemon
The values disable aging, inactivity, and expiration. The password field remains locked, and the account is used only for running services.
The exception list
Some distributions provide a way to exclude specific accounts from aging. The mechanism varies. On some systems, the /etc/shadow file is edited directly to set the maximum field to 99999. On others, the system account is created with useradd -r and the aging policy is not applied because the account’s password is never set.
The compliance consideration
NIST SP 800-63B, the modern password guideline, recommends against forced periodic rotation when other controls are in place. The reasoning is that frequent rotation leads to weaker passwords and predictable patterns. The LFCS exam tests the commands because they are part of the standard toolkit, but the reader should be aware that the policy landscape has shifted.
Complete Example Session
# ============================================
# PART 1: LIST CURRENT POLICY
# ============================================
sudo chage -l alice
# Last password change : Jan 15, 2026
# Password expires : never
# Password inactive : never
# Account expires : never
# Minimum number of days : 0
# Maximum number of days : 99999
# Number of days of warning : 7
# ============================================
# PART 2: SET MAXIMUM DAYS
# ============================================
sudo chage -M 90 alice
# ============================================
# PART 3: SET MINIMUM DAYS
# ============================================
sudo chage -m 7 alice
# ============================================
# PART 4: SET WARNING DAYS
# ============================================
sudo chage -W 14 alice
# ============================================
# PART 5: SET INACTIVITY PERIOD
# ============================================
sudo chage -I 30 alice
# The account is locked 30 days after the password expires.
# ============================================
# PART 6: SET ACCOUNT EXPIRATION
# ============================================
sudo chage -E 2026-12-31 alice
# ============================================
# PART 7: FORCE CHANGE ON NEXT LOGIN
# ============================================
sudo chage -d 0 alice
# The last-change date is set to 0, which forces a change.
# ============================================
# PART 8: VERIFY
# ============================================
sudo chage -l alice
# Last password change : password must be changed
# Password expires : password must be changed
# Password inactive : password must be changed
# Account expires : Dec 31, 2026
# Minimum number of days : 7
# Maximum number of days : 90
# Number of days of warning : 14
sudo grep alice /etc/shadow
# alice:$6$...:0:7:90:14:30:20454:
# ============================================
# PART 9: MODIFY DEFAULTS
# ============================================
sudo vim /etc/login.defs
# PASS_MAX_DAYS 90
# PASS_MIN_DAYS 7
# PASS_WARN_AGE 14
# New accounts inherit these values.
# ============================================
# PART 10: BULK UPDATE EXISTING USERS
# ============================================
for user in $(awk -F: '$3 >= 1000 && $3 < 65534 { print $1 }' /etc/passwd); do
sudo chage -M 90 -m 7 -W 14 "$user"
done
# All regular users now have the policy applied.
The ten parts showed listing the policy, setting the maximum, minimum, warning, inactivity, and expiration fields, forcing a change on next login, verifying, modifying defaults, and bulk-updating existing users.
Quick Reference
chage Flags
| Flag | Purpose | Example |
|---|---|---|
-l | List aging info | chage -l alice |
-M DAYS | Maximum password age | chage -M 90 alice |
-m DAYS | Minimum days between changes | chage -m 7 alice |
-W DAYS | Warning days | chage -W 14 alice |
-I DAYS | Inactivity period | chage -I 30 alice |
-E DATE | Account expiration | chage -E 2026-12-31 alice |
-d DATE | Last change date | chage -d 0 alice |
Special Values
| Value | Meaning |
|---|---|
-1 | Disable the field |
0 | Immediate expiration or last-change-to-epoch |
99999 | No expiration |
The Shadow Fields
| Position | Field | chage Flag |
|---|---|---|
| 3 | Last change | -d |
| 4 | Minimum | -m |
| 5 | Maximum | -M |
| 6 | Warning | -W |
| 7 | Inactivity | -I |
| 8 | Expiration | -E |
The login.defs Defaults
| Setting | Purpose |
|---|---|
PASS_MAX_DAYS | Default maximum age |
PASS_MIN_DAYS | Default minimum days |
PASS_WARN_AGE | Default warning days |
Comparison with passwd
| Command | Purpose |
|---|---|
passwd | Set the password |
passwd -e | Force change on next login |
chage -d 0 | Same as passwd -e |
chage -l | List aging info |
Best Practices
โ Do This:
# Set a maximum age for every regular account.
sudo chage -M 90 alice # โ
# Set the minimum days to prevent cycling.
sudo chage -m 7 alice # โ
# Warn the user before expiration.
sudo chage -W 14 alice # โ
# Lock the account after inactivity.
sudo chage -I 30 alice # โ
# Set account expiration for temporary users.
sudo chage -E 2026-12-31 temp # โ
# Force a change on next login for onboarding.
sudo chage -d 0 newuser # โ
# Verify with chage -l.
sudo chage -l alice # โ
# Update the defaults in /etc/login.defs.
PASS_MAX_DAYS 90 # โ
โ Don’t Do This:
# Don't set the maximum to a very small value.
sudo chage -M 1 alice # โ Forces daily changes, weak patterns
# Don't forget the minimum.
sudo chage -M 90 alice # โ Without -m, the user can cycle
# Don't apply aging to system accounts.
sudo chage -M 90 daemon # โ System accounts should not expire
# Don't confuse password expiration with account expiration.
# They are different fields. # โ
# Don't rely on chage to set the password.
sudo chage -M 90 alice # โ Password is set with passwd
# Don't forget to update existing users after changing login.defs.
# New accounts only. # โ
Common Pitfalls
| Pitfall | Why It Happens | Fix |
|---|---|---|
| User cannot log in | Account expired | Reset with chage -E -1 |
| Password never changes | Maximum set to 99999 or -1 | Set a finite value |
| Account locked unexpectedly | Inactivity period elapsed | Extend or disable with -I -1 |
| System account expired | Aging applied to system accounts | Set -M -1 -E -1 |
| New accounts use old policy | /etc/login.defs not updated | Edit the file and recreate accounts |
| Bulk update missed users | Loop range incorrect | Check the UID range in awk |
| Force-change not applied | -d 0 not set | Use chage -d 0 or passwd -e |
Real-World Examples
1. List Policy
sudo chage -l alice
2. Set Maximum Age
sudo chage -M 90 alice
3. Set Minimum Days
sudo chage -m 7 alice
4. Set Warning Days
sudo chage -W 14 alice
5. Set Inactivity
sudo chage -I 30 alice
6. Set Account Expiration
sudo chage -E 2026-12-31 alice
7. Force Change
sudo chage -d 0 alice
8. Disable Expiration
sudo chage -E -1 -M -1 alice
9. System Account
sudo chage -M -1 -m 0 -I -1 -E -1 daemon
10. Bulk Update
for u in $(awk -F: '$3>=1000 && $3<65534 {print $1}' /etc/passwd); do sudo chage -M 90 -m 7 -W 14 "$u"; done
Visual
The Password Lifecycle
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ PASSWORD AGING TIMELINE โ
โ โ
โ Last change โ
โ โ โ
โ โ minimum days (chage -m 7) โ
โ โ Cannot change yet. โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโถ โ
โ โ โ
โ โ warning days (chage -W 14) โ
โ โ User is warned at login. โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโถ โ
โ โ โ
โ โ maximum days (chage -M 90) โ
โ โ Password expires. โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโถ โ
โ โ โ
โ โ inactivity days (chage -I 30) โ
โ โ Account is locked. โ
โ โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโถ โ
โ โ
โ Account expiration (chage -E) is independent of the โ
โ password timeline. It disables the account regardless. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Shadow Fields
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ /etc/shadow FIELD LAYOUT โ
โ โ
โ alice:$6$...:19737:7:90:14:30:20454: โ
โ โโโฌโโ โโโฌโโ โโโฌโ โ โ โ โ โโโฌโโ โ
โ โ โ โ โ โ โ โ โ โ
โ โ โ โ โ โ โ โ โโ account expiration โ
โ โ โ โ โ โ โ โโ inactivity โ
โ โ โ โ โ โ โโ warning โ
โ โ โ โ โ โโ maximum โ
โ โ โ โ โโ minimum โ
โ โ โ โโ last change โ
โ โ โโ password hash โ
โ โโ username โ
โ โ
โ chage controls positions 3 through 8. โ
โ passwd controls position 2. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Password Expiration vs Account Expiration
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ TWO DIFFERENT EXPIRATIONS โ
โ โ
โ PASSWORD EXPIRATION (chage -M 90) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ After 90 days, the password is expired. โ โ
โ โ The user can still log in and change it. โ โ
โ โ The account remains active. โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ ACCOUNT EXPIRATION (chage -E 2026-12-31) โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ After the date, the account is disabled. โ โ
โ โ No login is possible. โ โ
โ โ Used for temporary accounts and contractors. โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ A user can have both. The account expiration is the โ
โ hard limit; the password expiration is the soft limit. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
The Bulk Update Loop
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ BULK UPDATE OF EXISTING USERS โ
โ โ
โ for user in $(awk -F: '$3>=1000 && $3<65534 {print $1}' โ
โ /etc/passwd); do โ
โ sudo chage -M 90 -m 7 -W 14 "$user" โ
โ done โ
โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ awk extracts usernames with UID >= 1000 โ โ
โ โ and < 65534 (regular users, not system or nobody)โ โ
โ โ โ โ
โ โ chage applies the new policy to each. โ โ
โ โ โ โ
โ โ The loop runs once per user. โ โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ /etc/login.defs affects new accounts only. โ
โ Existing accounts must be updated with chage. โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Item | Value |
|---|---|
chage | Manage password aging in /etc/shadow |
-l | List aging information |
-M DAYS | Maximum password age |
-m DAYS | Minimum days between changes |
-W DAYS | Warning days before expiration |
-I DAYS | Inactivity period after expiration |
-E DATE | Account expiration date |
-d DATE | Last password change date |
-d 0 | Force change on next login |
/etc/login.defs | Default policy for new accounts |
PASS_MAX_DAYS | Default maximum age |
PASS_MIN_DAYS | Default minimum days |
PASS_WARN_AGE | Default warning days |
Key takeaways:
chagemodifies the aging fields in/etc/shadow;passwdsets the password. The two commands are complementary. Thepasswdcommand reads the aging fields and enforces the policy at login.- The maximum age is the core policy. It sets how long a password remains valid. A value of
90means the password expires every 90 days. A value of-1or99999disables expiration. - The minimum age prevents cycling. Without a minimum, a user can change the password multiple times in a row to return to an old one. A value of
7means the password cannot be changed for seven days. - The warning days give the user notice. The user is warned at login starting this many days before expiration. A value of
14is common. - The inactivity period locks the account after expiration. Without it, an expired password can be changed at any time, and the account remains accessible. With
-I 30, the account locks 30 days after expiration if the password has not been changed. - Account expiration is separate from password expiration. The
-Eflag disables the account entirely. The-Mflag forces a password change. The two serve different purposes. - Defaults live in
/etc/login.defs. ThePASS_MAX_DAYS,PASS_MIN_DAYS, andPASS_WARN_AGEsettings apply to new accounts. Existing accounts must be updated individually withchage. - The
-d 0flag forces a change on next login. It sets the last-change date to the epoch, which makes the password immediately expired. Thepasswd -ecommand is equivalent.
Remember: Password aging is the policy that forces passwords to rotate, warns users before the deadline, and locks accounts that ignore the warning. The chage command is the tool: -M sets the maximum age, -m sets the minimum, -W sets the warning, -I sets the inactivity lock, and -E sets the account expiration. The -l flag lists the current policy, and the -d 0 flag forces a change on next login. The defaults for new accounts live in /etc/login.defs, and existing accounts must be updated individually. The result is a system where passwords do not remain valid indefinitely, dormant accounts are locked automatically, and the policy is visible and enforceable.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!