Passwords and
Password Aging

Change passwords and adjust password aging for local user accounts

CIS126RH | RHEL System Administration 1
Mesa Community College

Password management goes beyond simply setting a password. Administrators must enforce password aging policies that require users to change passwords regularly, prevent them from reusing a new password too soon, warn them before expiry, and disable accounts after extended inactivity. On RHEL 9, passwd handles immediate password operations and chage (change age) manages the full lifecycle of password aging stored in /etc/shadow. Both are tested on the RHCSA exam.

Learning Objectives

  1. Manage passwords with passwd — Set, expire, lock, and unlock passwords for local user accounts using passwd and understand how each operation changes /etc/shadow
  2. Read and interpret /etc/shadow — Decode all eight fields of the shadow file and explain what each password aging field controls
  3. Configure password aging with chage — Use chage flags and interactive mode to set maximum age, minimum age, warning period, inactivity timeout, and account expiry date
  4. Set system-wide defaults with /etc/login.defs — Explain how /etc/login.defs controls the default password aging values applied to all newly created accounts

/etc/shadow: The Password Database

All password data — hashes and aging policy — is stored in /etc/shadow, readable only by root.

# View an entry (requires root)
$ sudo grep ^alice: /etc/shadow
alice:$6$salt$hashedpw:19500:0:90:7:14:20000:
#  1    2              3     4  5  6  7   8   9
Field Name Example Meaning
1UsernamealiceLogin name
2Password hash$6$salt$hashEncrypted password; ! or !! = locked; * = no login
3Last change19500Days since epoch (1970-01-01) of last password change
4Minimum age0Minimum days before user can change password again
5Maximum age90Days until password expires and must be changed
6Warning7Days before expiry to warn the user
7Inactive14Days after expiry before account is disabled
8Expire20000Account expiry in days since epoch (not password expiry)
9Reserved(empty)Unused; always empty

passwd: Basic Password Operations

# Set or change a user's password (prompts interactively)
$ sudo passwd alice
Changing password for user alice.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.

# A user can change their own password (no sudo needed)
$ passwd
Changing password for alice.
Current password:
New password:
Retype new password:
passwd: all authentication tokens updated successfully.

# Set password non-interactively (for scripts)
$ echo "alice:newpassword123" | sudo chpasswd

# Check password status
$ sudo passwd -S alice
alice PS 2026-05-25 0 90 7 14 (Password set, SHA512 crypt.)
# Format: USER STATUS DATE MIN MAX WARN INACTIVE
# Status: PS=set, LK=locked, NP=no password
passwd -S decodes the shadow entry

passwd -S USERNAME shows a human-readable summary of the password status and aging settings from /etc/shadow — without the days-since-epoch arithmetic. Use it for quick status checks.

passwd: Locking, Unlocking, and Expiring

# Lock an account — prepends ! to password hash in /etc/shadow
$ sudo passwd -l alice
Locking password for user alice.
passwd: Success

# Verify the lock in /etc/shadow
$ sudo grep ^alice: /etc/shadow
alice:!$6$salt$hashedpw:19500:0:90:7:14::
#     ^ ! indicates locked

# Unlock the account — removes the leading !
$ sudo passwd -u alice
Unlocking password for user alice.
passwd: Success

# Expire a password immediately (force change at next login)
# Sets the last-change field (field 3) to 0 in /etc/shadow
$ sudo passwd -e alice
Expiring password for user alice.
passwd: Success

# Verify — last change is now 0
$ sudo grep ^alice: /etc/shadow
alice:$6$salt$hashedpw:0:0:90:7:14::

chage: Viewing Password Aging

chage -l (list) displays all password aging settings for a user in human-readable form — converting the days-since-epoch numbers to dates.

$ sudo chage -l alice
Last password change                    : May 25, 2026
Password expires                        : Aug 23, 2026
Password inactive                       : Sep  6, 2026
Account expires                         : never
Minimum number of days between changes  : 0
Maximum number of days between changes  : 90
Number of days of warning before expiry : 7

# Reading the output:
# "Password expires" = last change + maximum age
# "Password inactive" = password expiry + inactive period
# "Account expires" = absolute account expiry date (field 8)
# "Password inactive" ≠ "Account expires" — different mechanisms

# A user can view their own aging info (no sudo needed)
$ chage -l alice    # works as alice or as root
chage -l is the definitive aging verification command

Always use sudo chage -l USERNAME after configuring password aging to confirm all values are set correctly. The human-readable dates are easier to verify than the raw days-since-epoch numbers in /etc/shadow.

chage: Setting Password Aging Flags

# Set maximum password age (days before password expires)
$ sudo chage -M 90 alice   # must change every 90 days

# Set minimum password age (days before user can change again)
$ sudo chage -m 7 alice    # cannot change more than once a week

# Set warning period (days before expiry to warn user)
$ sudo chage -W 14 alice   # warn 14 days before expiry

# Set inactive period (days after expiry before account disabled)
$ sudo chage -I 30 alice   # disable account 30 days after expiry

# Set account expiry date (absolute date, regardless of password)
$ sudo chage -E 2026-12-31 alice

# Remove account expiry (set to -1 = never expires)
$ sudo chage -E -1 alice

# Force password change on next login (set last-change to 0)
$ sudo chage -d 0 alice

# Set multiple values in one command
$ sudo chage -M 90 -m 7 -W 14 -I 30 alice

chage Flags Reference

Flag Long form /etc/shadow field Effect
-l--listall fieldsDisplay aging info in human-readable format
-d DATE--lastdayfield 3Set last password change date (0 = force change now)
-m DAYS--mindaysfield 4Minimum days before password can be changed
-M DAYS--maxdaysfield 5Maximum password age; password expires after this many days
-W DAYS--warndaysfield 6Days before expiry to warn the user at login
-I DAYS--inactivefield 7Days after expiry before account is permanently disabled
-E DATE--expiredatefield 8Absolute account expiry date (YYYY-MM-DD or -1 for never)
-m vs -M: case matters

Lowercase -m = minimum days (smallest number). Uppercase -M = maximum days (largest number). A common exam trap — mix them up and the policy is inverted.

chage Interactive Mode

Running chage without flags (except the username) opens an interactive wizard that prompts for each aging setting in sequence.

$ sudo chage alice
Changing the aging information for alice
Enter the new value, or press ENTER for the default

        Minimum Password Age [0]: 7
        Maximum Password Age [99999]: 90
        Last Password Change (YYYY-MM-DD) [2026-05-25]: ENTER
        Password Expiration Warning [7]: 14
        Password Inactive [-1]: 30
        Account Expiration Date (YYYY-MM-DD) [-1]: 2026-12-31

# Verify all changes
$ sudo chage -l alice
Last password change                    : May 25, 2026
Password expires                        : Aug 23, 2026
Password inactive                       : Sep 22, 2026
Account expires                         : Dec 31, 2026
Minimum number of days between changes  : 7
Maximum number of days between changes  : 90
Number of days of warning before expiry : 14

The Password Aging Timeline

Understanding the sequence of events in the password lifecycle prevents confusion between password expiry and account expiry.

# With these settings on alice's account:
# Last change: May 25, 2026   (-d)
# Minimum age: 7 days          (-m)  → can change after Jun 1
# Maximum age: 90 days         (-M)  → must change by Aug 23
# Warning: 14 days             (-W)  → warned from Aug 9 onward
# Inactive: 30 days            (-I)  → disabled Sep 22 if not changed
# Account expiry: Dec 31, 2026 (-E)  → hard cutoff regardless
Date Event What happens
May 25Password changedClock resets; aging begins
Jun 1 (+7)Minimum age passedUser may now change their password
Aug 9 (+76)Warning beginsLogin shows "password expires in N days" message
Aug 23 (+90)Password expiresUser must change password at next login
Sep 22 (+30 inactive)Account disabledAccount locked — cannot log in even to change password
Dec 31Account expiryAccount locked regardless of password status

/etc/login.defs: System-Wide Defaults

/etc/login.defs sets the default password aging values applied to all newly created accounts. Existing accounts are not affected by changes here.

# View password aging defaults
$ grep -E "PASS_MAX_DAYS|PASS_MIN_DAYS|PASS_MIN_LEN|PASS_WARN_AGE" \
    /etc/login.defs
PASS_MAX_DAYS   99999
PASS_MIN_DAYS   0
PASS_MIN_LEN    5
PASS_WARN_AGE   7

# Edit to enforce organisation-wide defaults for NEW accounts
$ sudo vim /etc/login.defs
# Change PASS_MAX_DAYS 99999 to PASS_MAX_DAYS 90
# Change PASS_MIN_DAYS 0    to PASS_MIN_DAYS 7
# Change PASS_WARN_AGE 7    to PASS_WARN_AGE 14

# Verify the change
$ grep PASS_MAX_DAYS /etc/login.defs
PASS_MAX_DAYS   90

# These defaults only apply to accounts created AFTER the change
# Existing accounts must be updated individually with chage
/etc/login.defs only affects new accounts

Changing /etc/login.defs does not retroactively update existing accounts. To apply a policy to all existing users, use chage on each account individually or loop over them with a shell script.

Forcing Password Change at Next Login

A common administrative task: create a user, set an initial password, and force them to choose their own password when they first log in.

# Method 1: passwd -e (expire immediately)
$ sudo useradd newstaff
$ sudo passwd newstaff        # set initial password
$ sudo passwd -e newstaff    # force change on next login

# Method 2: chage -d 0 (set last change to epoch)
$ sudo chage -d 0 newstaff

# Verify
$ sudo chage -l newstaff
Last password change                    : password must be changed
Password expires                        : password must be changed
Password inactive                       : password must be changed
...

# When newstaff next logs in:
WARNING: Your password has expired.
You must change your password now and login again!
New password:

Practical Scenario: Applying a Password Policy

Apply a complete password aging policy to user alice: 90-day maximum, 7-day minimum, 14-day warning, 30-day inactive grace period.

# Step 1: Check current settings
$ sudo chage -l alice
Maximum number of days between changes  : 99999  ← needs to change

# Step 2: Apply the complete policy in one chage command
$ sudo chage -M 90 -m 7 -W 14 -I 30 alice

# Step 3: Verify all settings applied correctly
$ sudo chage -l alice
Last password change                    : May 25, 2026
Password expires                        : Aug 23, 2026
Password inactive                       : Sep 22, 2026
Account expires                         : never
Minimum number of days between changes  : 7
Maximum number of days between changes  : 90
Number of days of warning before expiry : 14

# Step 4: Also update /etc/login.defs for future accounts
$ sudo sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS   90/' /etc/login.defs
$ sudo sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE   14/' /etc/login.defs

Password and Aging Quick Reference

Task Command
Set a user's passwordsudo passwd USERNAME
Set password (non-interactive)echo "USER:PASS" | sudo chpasswd
Check password statussudo passwd -S USERNAME
Lock an accountsudo passwd -l USERNAME
Unlock an accountsudo passwd -u USERNAME
Expire password (force change)sudo passwd -e USERNAME or sudo chage -d 0 USERNAME
View all aging settingssudo chage -l USERNAME
Set maximum password agesudo chage -M DAYS USERNAME
Set minimum password agesudo chage -m DAYS USERNAME
Set warning periodsudo chage -W DAYS USERNAME
Set inactive grace periodsudo chage -I DAYS USERNAME
Set account expiry datesudo chage -E YYYY-MM-DD USERNAME
Remove account expirysudo chage -E -1 USERNAME
Interactive aging setupsudo chage USERNAME
Set default aging for new accountsEdit /etc/login.defs

Common Mistakes

Mistake What goes wrong Correct approach
Confusing chage -m and chage -M Minimum and maximum ages are swapped — policy is inverted (e.g. max=7 days, min=90 days) Lowercase -m = minimum; uppercase -M = maximum. Verify with chage -l
Confusing -I (inactive) with -E (expiry date) Wrong field is set — account locked by wrong mechanism -I sets days-after-expiry grace; -E sets absolute calendar date
Changing /etc/login.defs and expecting existing users to be affected login.defs only applies to new accounts — existing users are not updated Run chage on each existing user individually to apply the new policy
Locking with passwd -l but forgetting SSH keys still work User cannot log in with password but can still access the system via SSH key To fully block access also expire or remove their SSH authorized_keys, or use usermod -L combined with account expiry
Not verifying with chage -l after setting aging values Typos or flag confusion go undetected until the policy fails to enforce correctly Always run sudo chage -l USERNAME after any aging change to confirm
Using chage -E 0 thinking it means "never expires" Day 0 = January 1, 1970 — the account is immediately locked Use chage -E -1 to set no expiry date

Reading /etc/shadow After chage

After running chage, examine /etc/shadow directly to confirm which fields were modified and understand the raw values.

# Before: default settings
$ sudo grep ^alice: /etc/shadow
alice:$6$hash:19500:0:99999:7:-1::
#                   min  max  warn inactive

# After: chage -M 90 -m 7 -W 14 -I 30 alice
$ sudo grep ^alice: /etc/shadow
alice:$6$hash:19500:7:90:14:30::
#                   min  max  warn inactive

# After: passwd -l alice (locked)
$ sudo grep ^alice: /etc/shadow
alice:!$6$hash:19500:7:90:14:30::
#     ^ ! = locked

# After: chage -d 0 alice (force change)
$ sudo grep ^alice: /etc/shadow
alice:$6$hash:0:7:90:14:30::
#             ^ 0 = password must be changed

Knowledge Check

Answer these before moving to the next slide.

  1. Write the commands to lock user bob's account and then unlock it. What indicator in /etc/shadow shows the account is locked?
  2. Write the single chage command to configure user bob with: maximum password age of 60 days, minimum of 5 days, warning of 10 days, and inactive grace period of 14 days.
  3. Write the command to force bob to change his password the next time he logs in. Name two ways to achieve this.
  4. What is the difference between chage -I 30 and chage -E 2026-12-31?
  5. A student runs chage -m 90 -M 7 bob. What is wrong with this configuration, and what should the correct command be?
  6. An administrator changes PASS_MAX_DAYS from 99999 to 90 in /etc/login.defs. Does this affect user alice who was created yesterday? Why or why not?

Knowledge Check — Answers

  1. Lock: sudo passwd -l bob. Unlock: sudo passwd -u bob.
    A locked account has a ! prepended to the password hash in field 2 of /etc/shadow: bob:!$6$hash...
  2. sudo chage -M 60 -m 5 -W 10 -I 14 bob
  3. Method 1: sudo passwd -e bob
    Method 2: sudo chage -d 0 bob
    Both set the last-change date in /etc/shadow field 3 to 0, which causes the system to treat the password as immediately expired.
  4. chage -I 30 sets the inactive grace period — a relative number of days after the password expires during which the user can still log in and change their password. After 30 days past expiry, the account is disabled.
    chage -E 2026-12-31 sets the absolute account expiry date — regardless of password status, the account is locked on that specific calendar date.
  5. The flags are reversed: -m (lowercase) is minimum and -M (uppercase) is maximum. The command sets minimum=90 days and maximum=7 days, which is impossible — the minimum cannot exceed the maximum. The correct command: sudo chage -m 7 -M 90 bob
  6. No — it does not affect alice. /etc/login.defs only sets the defaults used when useradd creates new accounts. Alice's aging settings were copied from login.defs at account creation time and are now stored in /etc/shadow. To update alice's policy, run sudo chage -M 90 alice explicitly.

Key Takeaways

  1. passwd manages the password itself; chage manages aging policy. passwd -l/-u locks and unlocks. passwd -e expires immediately. passwd -S shows current status. All changes are written to /etc/shadow.
  2. Know all eight /etc/shadow fields and what controls each one. Fields 4–7 are the aging policy: minimum age, maximum age, warning days, inactive period. Field 8 is the absolute account expiry date. Field 3 set to 0 forces a password change. Field 2 starting with ! means the account is locked.
  3. chage flags: lowercase -m = minimum; uppercase -M = maximum. -W = warning days. -I = inactive grace period (relative to expiry). -E = account expiry date (absolute). -d 0 = force password change. Always verify with chage -l USERNAME.
  4. /etc/login.defs sets defaults for new accounts only. Change PASS_MAX_DAYS, PASS_MIN_DAYS, and PASS_WARN_AGE to enforce organisation-wide policies for future users. Apply chage individually to existing accounts — login.defs changes do not retroactively update them.

Graded Lab

  • Create user labpass and set a password. Run sudo chage -l labpass to record the default aging settings. View the raw /etc/shadow entry with sudo grep labpass /etc/shadow and identify each field.
  • Apply a complete password policy to labpass: maximum age 45 days, minimum age 3 days, warning 10 days, inactive grace 7 days. Use a single chage command. Verify all four settings with chage -l labpass.
  • Force labpass to change their password on next login using passwd -e labpass. Verify with chage -l labpass that "password must be changed" appears. Then run sudo chage -d 0 labpass and confirm the /etc/shadow field 3 is 0.
  • Lock labpass with passwd -l labpass. Confirm the ! appears in /etc/shadow. Attempt a login as labpass to confirm it fails. Unlock with passwd -u labpass and confirm the ! is removed.
  • Set an account expiry date of 2026-12-31 on labpass with chage -E 2026-12-31. Verify with chage -l labpass that "Account expires: Dec 31, 2026" appears. Then remove the expiry with chage -E -1 and confirm.
  • Edit /etc/login.defs to set PASS_MAX_DAYS 60. Create a new user newdefaults and check their aging with chage -l — confirm they inherit the 60-day maximum. Confirm that labpass still has 45 days (the /etc/login.defs change did not affect existing accounts).
RHCSA Objective

"Change passwords and adjust password aging for local user accounts." Know passwd -l/-u/-e/-S, chage -l/-M/-m/-W/-I/-E/-d, and /etc/login.defs for new-account defaults.