In the previous part, Configuring Authentication Modules Arguments (PAM Arguments) was completed.
1. CIS Benchmark Requirements (Section 5.4.1)
- 5.4.1 Configure shadow password suite parameters
- 5.4.1.1 Ensure password expiration is configured (Automated)
- 5.4.1.2 Ensure minimum password days is configured (Manual)
- 5.4.1.3 Ensure password expiration warning days is configured (Automated)
- 5.4.1.4 Ensure strong password hashing algorithm is configured (Automated)
- 5.4.1.5 Ensure inactive password lock is configured (Automated)
- 5.4.1.6 Ensure all users last password change date is in the past (Automated)
2. Concept & Rationale
- PASS_MAX_DAYS: The maximum number of days a password remains valid (usually 365 days).
- PASS_MIN_DAYS: The minimum number of days that must pass after a password change before the user can change it again (usually 1 day, to prevent circumvention of the password history).
- PASS_WARN_AGE: The number of days before expiration that the user is warned (usually 7 days).
- ENCRYPT_METHOD: Use of strong algorithms like
YESCRYPTorSHA512for password hashing. - INACTIVE: Deactivation of the user account after a specified number of days (e.g., 30 days) from the password expiration date.
3. Oracle Database Compatibility (RAC, ASM, Grid)
Conflict Level: Very High (Critical)
Oracle Requirements and Explanation:
- Service User Password Expiration Crisis: Setting a password expiration policy for service users like
oracle,grid, andopccan cause severe disruptions. If the passwords for these users expire, background processes (Cron jobs, SSH key-based communication in RAC environments, and monitoring agents) will face authentication failures and stop, leading to a service outage. - Solution: The values applied in
/etc/login.defsonly apply to new users. For existing system and Oracle service users (which are created beforehand or later), the expiration policy must be explicitly disabled using commands likechage -M 99999 oracleto ensure cluster and database stability.
4. How to Audit (Audit Script)
GitHub link for this script: modules/audit_20_Shadow_Password_Suite.sh
If you are not familiar with Bash scripting, you can refer to the training published for database administrators on the site: Bash for Oracle DBAs.
#!/bin/bash
# Script: audit20.sh
# Purpose: Audit Script for Password Expiration & Aging (CIS Section 20)
echo "=========================================================================="
echo " CIS Requirement: Password Aging Policies (/etc/login.defs & Shadow)"
echo " - Ensure password expiration is 365 days or less (PASS_MAX_DAYS)."
echo " - Ensure minimum days between changes is 1 or more (PASS_MIN_DAYS)."
echo " - Ensure inactive password lock is 30 days or less (INACTIVE)."
echo " - Ensure all passwords have a valid change date (not in the future)."
echo " Oracle Context:"
echo " - WARNING: Enforcing expiration (PASS_MAX_DAYS) on database service"
echo " accounts ('oracle', 'grid') will cause critical cluster and database"
echo " outages. Service accounts must be explicitly excluded from aging."
echo "=========================================================================="
if [ "$EUID" -ne 0 ]; then
echo -e "\e[31m[-] Please run as root\e[0m"
exit 1
fi
FAIL_COUNT=0
echo -e "\n[*] Checking /etc/login.defs parameters..."
check_login_defs() {
local param=$1
local expected=$2
local current=$(grep -E "^\s*${param}\b" /etc/login.defs | awk '{print $2}')
if [ "$current" == "$expected" ]; then
echo -e " \e[32m[PASS]\e[0m $param is $current"
else
echo -e " \e[31m[FAIL]\e[0m $param is $current (Expected: $expected)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
}
check_login_defs "PASS_MAX_DAYS" "365"
check_login_defs "PASS_MIN_DAYS" "1"
check_login_defs "PASS_WARN_AGE" "7"
check_login_defs "ENCRYPT_METHOD" "YESCRYPT"
echo -e "\n[*] Checking Default Inactive Lock..."
inactive_val=$(useradd -D | grep INACTIVE | cut -d= -f2)
if [ "$inactive_val" == "30" ]; then
echo -e " \e[32m[PASS]\e[0m INACTIVE is set to $inactive_val"
else
echo -e " \e[31m[FAIL]\e[0m INACTIVE is set to $inactive_val (Expected: 30)"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
echo -e "\n[*] Checking for passwords changed in the future (/etc/shadow)..."
# Fix for the broken awk command in the original script
CURRENT_EPOCH=$(date +%s)
CURRENT_DAYS=$((CURRENT_EPOCH / 86400))
FUTURE_USERS=$(awk -v now="$CURRENT_DAYS" -F: '($2 != "!" && $2 != "*" && $2 != "" && $3 > now) {print $1}' /etc/shadow)
if [ -z "$FUTURE_USERS" ]; then
echo -e " \e[32m[PASS]\e[0m No accounts have a password change date in the future."
else
echo -e " \e[31m[FAIL]\e[0m Accounts with future password change dates: $(echo $FUTURE_USERS | tr '\n' ' ')"
FAIL_COUNT=$((FAIL_COUNT + 1))
fi
if [ "$FAIL_COUNT" -eq 0 ]; then
echo -e "\n\e[32m[+] AUDIT RESULT: PASS\e[0m"
else
echo -e "\n\e[31m[-] AUDIT RESULT: FAIL ($FAIL_COUNT issues found)\e[0m"
fi
5. How to Remediate with Bash (Remediation Script)
GitHub link for this script: modules/remediate_20_Shadow_Password_Suite.sh
#!/bin/bash
# 1. Update /etc/login.defs
sed -i -E 's/^\s*PASS_MAX_DAYS.*/PASS_MAX_DAYS 365/' /etc/login.defs
sed -i -E 's/^\s*PASS_MIN_DAYS.*/PASS_MIN_DAYS 1/' /etc/login.defs
sed -i -E 's/^\s*PASS_WARN_AGE.*/PASS_WARN_AGE 7/' /etc/login.defs
sed -i -E 's/^\s*ENCRYPT_METHOD.*/ENCRYPT_METHOD YESCRYPT/' /etc/login.defs
# 2. Update inactive password lock (useradd default)
useradd -D -f 30
# 3. Apply changes to existing users (excluding oracle, grid, root, opc)
for user in $(awk -F: '($3 == "" || $3 > 0) && $1 != "root" && $1 != "oracle" && $1 != "grid" && $1 != "opc" {print $1}' /etc/shadow); do
chage --maxdays 365 --mindays 1 --warndays 7 --inactive 30 "$user"
done
# Fix any users with future password change dates to today
CURRENT_DAY=$(($(date +%s) / 86400))
for user in $(awk -F: -v today="$CURRENT_DAY" '$3 > today {print $1}' /etc/shadow); do
chage --lastday "$CURRENT_DAY" "$user"
done
echo "Configuration applied successfully. Note: 'oracle' and 'grid' users were explicitly excluded from password aging."
In the next part, we will cover System User Accounts, the Root User, and the Default Environment.