Password aging and scoped sudo.
chage, sudoers.d, and visudo.
A compliance audit wants password expiry policies on four accounts and passwordless sudo for the ops user, scoped to three commands. Done with chage and a validated sudoers drop-in.
The ticket: secadmin has two compliance items. Password aging on dev1, dev2, ops1, and dbadmin1: expire every 90 days, at least 7 days between changes, 14 days warning, and force dev1 to change at next login. And scoped sudo so ops1 can run systemctl, dnf, and firewall-cmd with no password, but nothing else. Use /etc/sudoers.d/ so it is reviewable, and validate the syntax before saving, because a sudoers lockout last quarter was a nightmare.
The real problem: a bad sudoers file can lock everyone out of privilege escalation. The whole point of this ticket is doing it the safe way, with validation.
What we are doing: password aging with chage, and a safe, scoped sudoers drop-in that is syntax-checked before it goes live.
The ticket
for u in dev1 dev2 ops1 dbadmin1; do id $u; done ls /etc/sudoers.d/
Concept review. chage and sudoers syntax.
man chage man sudoers
-l list current aging -M max days before a change is required -m min days between changes -W warning days before expiry -I inactive days after expiry -E account expiry date (YYYY-MM-DD or -1) -d 0 force a change on next login
WHO WHERE=(AS_WHOM) [TAG:] WHAT # tags: NOPASSWD: PASSWD: NOEXEC: EXEC: ops1 ALL=(ALL) NOPASSWD: /usr/bin/systemctl, /usr/bin/dnf
Apply the password policies in bulk
Four accounts get the same policy, so loop over them. That is the real sysadmin move, and it is faster than four separate commands.
# see the current state
for u in dev1 dev2 ops1 dbadmin1; do echo "=== $u ==="; sudo chage -l $u; done
# apply 90 / 7 / 14 to all four
for u in dev1 dev2 ops1 dbadmin1; do
sudo chage -M 90 -m 7 -W 14 $u
done
# verify
for u in dev1 dev2 ops1 dbadmin1; do
echo "=== $u ==="; sudo chage -l $u | grep -E "Maximum|Minimum|Warning"
doneForce a password change on next login
sudo chage -d 0 dev1 sudo chage -l dev1 # 'password must be changed'
-d 0 sets the last-change date to the epoch, so the system treats the password as immediately expired and prompts at next login.
System-wide defaults in login.defs
chage changes existing users. To set the policy for every future user, edit /etc/login.defs. This does not touch existing accounts.
grep -E "PASS_MAX_DAYS|PASS_MIN_DAYS|PASS_WARN_AGE" /etc/login.defs sudo cp /etc/login.defs /etc/login.defs.bak sudo sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS\t90/' /etc/login.defs sudo sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS\t7/' /etc/login.defs sudo sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE\t14/' /etc/login.defs grep -E "PASS_MAX_DAYS|PASS_MIN_DAYS|PASS_WARN_AGE" /etc/login.defs
Build the sudoers.d drop-in
Never edit /etc/sudoers directly with a plain editor. Write a drop-in to /etc/sudoers.d/, set its mode to 440, and validate with visudo -c before trusting it.
sudo bash -c 'cat > /etc/sudoers.d/ops1-scoped << "EOF" # scoped sudo for ops1: services, packages, firewall ops1 ALL=(ALL) NOPASSWD: /usr/bin/systemctl, /usr/bin/dnf, /usr/bin/firewall-cmd EOF' sudo chmod 440 /etc/sudoers.d/ops1-scoped # sudoers ignores it otherwise sudo visudo -c # must report: parsed OK
visudo -c reports an error, fix it before you log out. A broken file in /etc/sudoers.d/ can disable sudo for everyone. The 440 permission and the syntax check are the two things standing between you and last quarter’s lockout.Test ops1’s access
sudo -l -U ops1 # what can ops1 run sudo -u ops1 sudo systemctl status sshd # allowed, no password sudo -u ops1 sudo firewall-cmd --list-all # allowed sudo -u ops1 sudo cat /etc/shadow # not in scope, refused
Exam questions
Write the command first.
Q1. Configure alice so her password expires every 60 days, with a 7-day minimum between changes and a 10-day warning.
Q2. Force bob to change his password at next login, without changing his other aging settings.
Q3. Create a sudoers drop-in giving auditor1 passwordless cat, grep, and tail. Validate the syntax.
Answers.
# A1 sudo chage -M 60 -m 7 -W 10 alice # A2 sudo chage -d 0 bob # A3 sudo bash -c 'cat > /etc/sudoers.d/auditor1 << EOF auditor1 ALL=(ALL) NOPASSWD: /usr/bin/cat, /usr/bin/grep, /usr/bin/tail EOF' sudo chmod 440 /etc/sudoers.d/auditor1 sudo visudo -c
Final checklist: confirm everything works
If every check passes, the ticket is done.
# 1. all four users on the 90/7/14 policy for u in dev1 dev2 ops1 dbadmin1; do sudo chage -l $u | grep Maximum; done # 2. dev1 forced to change at next login sudo chage -l dev1 | head -1 # 3. the drop-in exists at mode 440 and parses ls -l /etc/sudoers.d/ops1-scoped; sudo visudo -c # 4. ops1 can run the scoped commands but not others # 5. all three exam commands written from scratch # 6. tracker entry checked off
Reply to secadmin: all four accounts are on the 90/7/14 policy, dev1 changes at next login, and ops1 has passwordless sudo scoped to systemctl, dnf, and firewall-cmd via /etc/sudoers.d/ops1-scoped. visudo -c is clean.