| |

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.

FieldPositionchage FlagMeaning
Username1โ€”The account name
Hash2passwdThe password hash
Last change3-dDays since epoch
Minimum4-mDays before next change allowed
Maximum5-MDays before expiration
Warning6-WDays of warning
Inactivity7-IDays after expiration before lock
Expiration8-EDays since epoch
Reserved9โ€”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

FlagPurposeExample
-lList aging infochage -l alice
-M DAYSMaximum password agechage -M 90 alice
-m DAYSMinimum days between changeschage -m 7 alice
-W DAYSWarning dayschage -W 14 alice
-I DAYSInactivity periodchage -I 30 alice
-E DATEAccount expirationchage -E 2026-12-31 alice
-d DATELast change datechage -d 0 alice

Special Values

ValueMeaning
-1Disable the field
0Immediate expiration or last-change-to-epoch
99999No expiration

The Shadow Fields

PositionFieldchage Flag
3Last change-d
4Minimum-m
5Maximum-M
6Warning-W
7Inactivity-I
8Expiration-E

The login.defs Defaults

SettingPurpose
PASS_MAX_DAYSDefault maximum age
PASS_MIN_DAYSDefault minimum days
PASS_WARN_AGEDefault warning days

Comparison with passwd

CommandPurpose
passwdSet the password
passwd -eForce change on next login
chage -d 0Same as passwd -e
chage -lList 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

PitfallWhy It HappensFix
User cannot log inAccount expiredReset with chage -E -1
Password never changesMaximum set to 99999 or -1Set a finite value
Account locked unexpectedlyInactivity period elapsedExtend or disable with -I -1
System account expiredAging applied to system accountsSet -M -1 -E -1
New accounts use old policy/etc/login.defs not updatedEdit the file and recreate accounts
Bulk update missed usersLoop range incorrectCheck the UID range in awk
Force-change not applied-d 0 not setUse 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

ItemValue
chageManage password aging in /etc/shadow
-lList aging information
-M DAYSMaximum password age
-m DAYSMinimum days between changes
-W DAYSWarning days before expiration
-I DAYSInactivity period after expiration
-E DATEAccount expiration date
-d DATELast password change date
-d 0Force change on next login
/etc/login.defsDefault policy for new accounts
PASS_MAX_DAYSDefault maximum age
PASS_MIN_DAYSDefault minimum days
PASS_WARN_AGEDefault warning days

Key takeaways:

  • chage modifies the aging fields in /etc/shadow; passwd sets the password. The two commands are complementary. The passwd command 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 90 means the password expires every 90 days. A value of -1 or 99999 disables 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 7 means 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 14 is 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 -E flag disables the account entirely. The -M flag forces a password change. The two serve different purposes.
  • Defaults live in /etc/login.defs. The PASS_MAX_DAYS, PASS_MIN_DAYS, and PASS_WARN_AGE settings apply to new accounts. Existing accounts must be updated individually with chage.
  • The -d 0 flag forces a change on next login. It sets the last-change date to the epoch, which makes the password immediately expired. The passwd -e command 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!