Automate Linux Password Expiration for GB/T 22239 Compliance (90‑Day Policy)
This guide presents a complete, compliance‑ready solution for enforcing the GB/T 22239 (等保2.0) requirement that Linux user passwords be changed every 90 days, including a safe Bash script, audit logging, crontab scheduling, permission hardening, and evidence collection for security assessments.
Overview
“The compliance requirement states: operating system user passwords must be forced to change within 90 days.”
Someone might write a simple script: chage -M 90 -d $(date) username Although it seems to meet the requirement, it introduces three major compliance pitfalls:
Including service accounts such as nobody or systemd in the password policy can cause system instability.
No operation logs or audit trail – evidence cannot be provided during assessment.
Script permissions are too open, allowing attackers to reverse‑engineer the operational logic.
We provide a “GB/T 22239 compliance version password‑policy automation solution” that not only satisfies the “90‑day validity” rule but also covers account classification, audit logging, permission control, and strong‑password linkage.
Optimization Principles – Align with GB/T 22239 “Secure Computing Environment” controls
Account Management : Process only real login users (UID ≥ 1000, login shell /bin/bash, exclude system accounts like nobody).
Operation Auditing : Record user, timestamp, old and new policies; support syslog reporting.
Permission Control : Script readable/writable only by root; crontab entries strictly limited.
Policy Linkage : Verify that /etc/login.defs enables strong passwords via pam_pwquality.
Compliant Hardened Script (with detailed comments)
#!/bin/bash
# Filename: password_policy_compliance.sh
# Revision: 2.0 (GB/T 22239 compliance version)
# Author: Security Team
# Description: Linux user password expiration management script meeting GB/T 22239 requirements
set -euo pipefail
# === Configuration ===
LOG_FILE="/var/log/password_policy.log"
SYSLOG_TAG="passwd-compliance"
SCRIPT_NAME=$(basename "$0")
SCRIPT_PATH=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# === Helper: Get real users ===
get_real_users() {
awk -F: '
$7 ~ /\/bin\/(ba)?sh$/ && # shell is bash or sh
$3 >= 1000 && # UID ≥ 1000 (covers common distributions)
$1 != "nobody" && # exclude nobody
!/^(halt|sync|shutdown|mail|news|uucp|operator|games|gopher)/ {print $1}
' /etc/passwd
}
# === Permission check ===
if [[ $EUID -ne 0 ]]; then
echo "Error: This script must be run as root." >&2
exit 1
fi
# === Logging function ===
log_audit() {
local msg="$1"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "[$timestamp] $msg" >> "$LOG_FILE"
logger -t "$SYSLOG_TAG" "$msg"
}
# === Ensure crontab entry ===
CRON_ENTRY="0 2 10 * * $SCRIPT_PATH/$SCRIPT_NAME >> $LOG_FILE 2>&1"
(crontab -l 2>/dev/null | grep -Fq "$CRON_ENTRY") || {
(crontab -l 2>/dev/null; echo "$CRON_ENTRY") | crontab -
log_audit "Automatically configured monthly password‑policy task"
}
# === Main logic: update password expiration for real users ===
log_audit "Starting password‑policy compliance check..."
while IFS= read -r user; do
[[ -z "$user" ]] && continue
current_max=$(chage -l "$user" 2>/dev/null | grep "Maximum number" | awk -F: '{print $2}' | tr -d ' ')
if [[ "$current_max" != "90" ]]; then
chage -M 90 -d "$(date +%Y-%m-%d)" "$user"
log_audit "Updated user [$user] password expiration to 90 days (previous: ${current_max:-N/A})"
fi
done < <(get_real_users)
log_audit "Password‑policy compliance check completed."Deployment and Permission Hardening (must execute!)
# 1. Set script permissions (root only)
chmod 700 password_policy_compliance.sh
chown root:root password_policy_compliance.sh
# 2. Protect log file
touch /var/log/password_policy.log
chmod 600 /var/log/password_policy.log
chown root:root /var/log/password_policy.log
# 3. Verify syslog receives logs (GB/T 22239 requires 180‑day retention)
grep "passwd-compliance" /var/log/messagesEvidence to Provide During Assessment
Policy file: PASS_MAX_DAYS 90 in /etc/login.defs
Execution log: /var/log/password_policy.log (contains operation details)
System log: syslog entries in /var/log/messages
Cron job: output of crontab -l (proves automation)
Account list: script processes only real users, no system accountsThese five items can be directly used as evidence for the “Secure Computing Environment – Identity Authentication” control.
Advanced Recommendations (for Level‑3 compliance)
Enable strong password policy: ensure pam_pwquality.so is active in /etc/pam.d/system-auth
Integrate with bastion host: automatically sync expired‑password users to an audit system
Periodic verification: run monthly awk -F: '$5 ~ /90/ {print $1}' /etc/shadow to validate policyFinal Thoughts
Compliance is not a checkbox; it is a trusted baseline.
Even a tiny password‑policy script can become an anchor point of your security framework.Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Xiao Liu Lab
An operations lab passionate about server tinkering 🔬 Sharing automation scripts, high-availability architecture, alert optimization, and incident reviews. Using technology to reduce overtime and experience to avoid major pitfalls. Follow me for easier, more reliable operations!
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
