表格17条
| 服务器未配置密码复杂度和密码有效期,可能会被设置为弱口令和长时间使用,恶意人员可能通过猜解或暴力破解口令非法登录。 | Web服务器 (172.22.3.2) 数据库服务器1 (172.22.3.129) 数据库服务器2 (172.22.3.130) 应用服务器 (172.22.3.131) Web服务器 (172.22.3.3) 数据库服务器 (172.22.3.133) 应用服务器1 (172.22.3.134) 应用服务器2 (172.22.3.135) | 高 | 建议Linux服务器在/etc/pam.d/system-auth配置文件中配置密码最小长度为8位,引用pwquality.so模块,配置密码必须由大小写字母、数字、特殊字符中三种或三种以上, 密码有效期为90天。 |
|---|
手动配置步骤:
一、安装必要的软件包
# 检查是否已安装 pwquality
rpm -qa | grep libpwquality
# 如果未安装,执行安装
yum install -y libpwquality
二、配置密码复杂度要求
1. 编辑 `/etc/pam.d/system-auth`
# 备份原文件
cp /etc/pam.d/system-auth /etc/pam.d/system-auth.bak.$(date +%F)
# 编辑文件
vim /etc/pam.d/system-auth
2. 修改 `password requisite` 行
找到类似这样的行:
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
**替换为:**
password requisite pam_pwquality.so try_first_pass retry=3 authtok_type= minlen=8 dcredit=-1 ucredit=-1 lcredit=-1 ocredit=-1 minclass=3 enforce_for_root
同步修改 `/etc/pam.d/password-auth`
# 备份
cp /etc/pam.d/password-auth /etc/pam.d/password-auth.bak.$(date +%F)
# 直接复制 system-auth 的配置
cp /etc/pam.d/system-auth /etc/pam.d/password-auth
三、配置 pwquality 详细参数
# 编辑 `/etc/security/pwquality.conf`
# 备份
cp /etc/security/pwquality.conf /etc/security/pwquality.conf.bak.$(date +%F)
# 编辑文件
vim /etc/security/pwquality.conf
### 添加或修改以下内容:
# 密码最小长度 8 位
minlen = 8
# 至少包含 1 个小写字母(-1 表示至少 1 个)
#lcredit = -1
# 至少包含 1 个大写字母
#ucredit = -1
# 至少包含 1 个数字
#dcredit = -1
# 至少包含 1 个特殊字符
#ocredit = -1
# ✅ 不强制要求每种字符类型,只要求至少 3 种即可
# 至少包含 3 种字符类型(小写、大写、数字、特殊字符)
minclass = 3
# 新密码中最多有 3 个字符与旧密码相同
difok = 3
# 禁止使用用户名
usercheck = 1
# 禁止使用字典单词
dictcheck = 1
四、配置密码有效期 90 天
### 方法 1:修改 `/etc/login.defs`(全局默认)
# 备份
cp /etc/login.defs /etc/login.defs.bak.$(date +%F)
# 编辑文件
vim /etc/login.defs
### 修改以下参数:
# 密码最大有效期(90 天)
PASS_MAX_DAYS 90
# 密码最小修改间隔(0 表示随时可改)
PASS_MIN_DAYS 0
# 密码过期前提前警告天数(7 天)
PASS_WARN_AGE 7
# 密码最小长度(这里设置 8,但主要由 pwquality 控制)
PASS_MIN_LEN 8
### 方法 2:对已存在的用户修改密码策略
# 查看当前用户密码策略
chage -l root
# 为现有用户设置密码有效期 90 天
chage -M 90 root
# 批量修改所有普通用户(UID >= 1000)
for user in $(awk -F: '$3>=1000 && $3!=65534 {print $1}' /etc/passwd); do
chage -M 90 -m 1 -W 7 "$user"
echo "已设置用户 $user 的密码策略"
done
五、参数说明
PAM 模块参数:
| 参数 | 说明 | 示例 |
|------|------|------|
| `minlen=8` | 密码最小长度 8 位 | `minlen=8` |
| `dcredit=-1` | 至少包含 1 个数字 | `-1` 表示至少 1 个 |
| `ucredit=-1` | 至少包含 1 个大写字母 | `-1` 表示至少 1 个 |
| `lcredit=-1` | 至少包含 1 个小写字母 | `-1` 表示至少 1 个 |
| `ocredit=-1` | 至少包含 1 个特殊字符 | `-1` 表示至少 1 个 |
| `minclass=3` | 至少包含 3 种字符类型 | 小写、大写、数字、特殊字符中至少 3 种 |
| `retry=3` | 密码设置失败后重试次数 | 最多重试 3 次 |
| `remember=5` | 记住最近 5 次密码,防止重复使用 | 在 `pam_unix.so` 行配置 |
### login.defs 参数:
| 参数 | 说明 | 推荐值 |
|------|------|--------|
| `PASS_MAX_DAYS` | 密码最大有效期(天) | `90` |
| `PASS_MIN_DAYS` | 两次修改密码的最小间隔(天) | `1` |
| `PASS_WARN_AGE` | 密码过期前警告天数 | `7` |
| `PASS_MIN_LEN` | 密码最小长度(被 pwquality 覆盖) | `8` |
## 六、验证配置
### 1. 测试密码复杂度
# 创建测试用户
useradd testuser
# 尝试设置不符合规则的密码
passwd testuser
**测试案例:**
| 密码 | 是否通过 | 原因 |
|------|---------|------|
| `123456` | ❌ | 长度不足 8 位 |
| `12345678` | ❌ | 只有数字,未满足 3 种字符类型 |
| `Abcd1234` | ❌ | 没有特殊字符,只有 3 种字符类型(需要特殊字符) |
| `Abcd@123` | ✅ | 包含大写、小写、数字、特殊字符,满足 minclass=3 |
| `Test@123` | ✅ | 符合所有要求 |
| `testuser` | ❌ | 包含用户名 |
### 2. 检查密码有效期
# 查看用户密码策略
chage -l testuser
# 预期输出:
# Last password change : Dec 15, 2024
# Password expires : Mar 15, 2025 ← 90 天后
# Password inactive : never
# Account expires : never
# Minimum number of days between password change : 1
# Maximum number of days between password change : 90
# Number of days of warning before password expires : 7
### 3. 验证 PAM 模块加载
# 检查 pwquality 模块
grep pam_pwquality.so /etc/pam.d/system-auth
自动化脚本
/tmp/password_policy_centos7.sh
#!/bin/bash
#########################################################################
# Script Name: password_policy_centos7.sh
# Description: CentOS 7.9 密码复杂度和有效期配置脚本(修正版)
# Author: zhangpeng
# Date: 2026-1-12
# Version: 2.1
# Changes:
# - 密码复杂度为"至少 3 种字符类型"
# - 移除强制 4 种字符类型的限制
#########################################################################
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $1"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
log_step() {
echo -e "${BLUE}[STEP]${NC} $1"
}
# 检查是否为 root 用户
check_root() {
if [ "$(id -u)" -ne 0 ]; then
log_error "此脚本必须以 root 用户运行!"
exit 1
fi
}
# 检查系统版本
check_system() {
if [ ! -f /etc/redhat-release ]; then
log_error "此脚本仅支持 CentOS/RHEL 系统!"
exit 1
fi
local version=$(cat /etc/redhat-release | grep -oP '(?<=release )\d+')
if [ "$version" != "7" ]; then
log_warn "检测到系统版本为 $version,此脚本专为 CentOS 7.9 设计"
read -p "是否继续执行?(y/n): " choice
if [ "$choice" != "y" ]; then
exit 0
fi
fi
}
# 获取普通用户列表
get_normal_users() {
awk -F: '$3>=1000 && $3!=65534 {print $1":"$3":"$6}' /etc/passwd
}
# 显示普通用户列表
show_normal_users() {
local users=$(get_normal_users)
if [ -z "$users" ]; then
log_warn "系统中没有普通用户(UID >= 1000)"
return 1
fi
echo ""
echo -e "${CYAN}=========================================="
echo -e " 检测到的普通用户列表"
echo -e "==========================================${NC}"
printf "%-20s %-10s %-30s\n" "用户名" "UID" "家目录"
echo "------------------------------------------------------------------"
while IFS=: read -r username uid homedir; do
printf "%-20s %-10s %-30s\n" "$username" "$uid" "$homedir"
done <<< "$users"
echo "------------------------------------------------------------------"
local user_count=$(echo "$users" | wc -l)
echo -e "总计: ${GREEN}$user_count${NC} 个普通用户"
echo ""
return 0
}
# 安装必要软件包
install_packages() {
log_step "步骤 1/5: 检查并安装必要软件包..."
if rpm -qa | grep -q libpwquality; then
log_info "libpwquality 已安装"
else
log_info "正在安装 libpwquality..."
yum install -y libpwquality
if [ $? -eq 0 ]; then
log_info "libpwquality 安装成功"
else
log_error "libpwquality 安装失败"
exit 1
fi
fi
}
# 备份配置文件
backup_configs() {
log_step "步骤 2/5: 备份原配置文件..."
local backup_dir="/root/password_policy_backup_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$backup_dir"
local files=(
"/etc/pam.d/system-auth"
"/etc/pam.d/password-auth"
"/etc/security/pwquality.conf"
"/etc/login.defs"
)
for file in "${files[@]}"; do
if [ -f "$file" ]; then
cp "$file" "$backup_dir/"
log_info "已备份: $file -> $backup_dir/"
fi
done
log_info "备份目录: $backup_dir"
}
# ✅ 修改后的 PAM 配置
configure_pam() {
log_step "步骤 3/5: 配置 PAM 密码复杂度策略..."
# 配置 system-auth
log_info "配置 /etc/pam.d/system-auth..."
# 删除旧的 pam_pwquality.so 行
sed -i '/^password.*pam_pwquality.so/d' /etc/pam.d/system-auth
# ✅ 只使用 minclass=3,不强制每种字符类型
sed -i '/^password/i\password requisite pam_pwquality.so try_first_pass retry=3 authtok_type= minlen=8 minclass=3 enforce_for_root' /etc/pam.d/system-auth
# 删除重复行
awk '!seen[$0]++' /etc/pam.d/system-auth > /tmp/system-auth.tmp
mv /tmp/system-auth.tmp /etc/pam.d/system-auth
# 移除 remember 参数(如果存在)
sed -i 's/\s*remember=[0-9]*//g' /etc/pam.d/system-auth
# 同步到 password-auth
log_info "同步配置到 /etc/pam.d/password-auth..."
cp /etc/pam.d/system-auth /etc/pam.d/password-auth
log_info "PAM 配置完成"
}
# ✅ 修改后的 pwquality 配置
configure_pwquality() {
log_step "步骤 4/5: 配置 /etc/security/pwquality.conf..."
cat > /etc/security/pwquality.conf << 'EOF'
# Configuration for systemwide password quality limits
# Number of characters in the new password that must not be present in the old password.
difok = 3
# Minimum acceptable size for the new password
minlen = 8
# ✅ 不强制要求每种字符类型,只要求至少 3 种即可
# The maximum credit for having digits in the new password.
# dcredit = 1
# The maximum credit for having uppercase characters in the new password.
# ucredit = 1
# The maximum credit for having lowercase characters in the new password.
# lcredit = 1
# The maximum credit for having other characters in the new password.
# ocredit = 1
# The minimum number of required classes of characters for the new password
# ✅ 至少 3 种字符类型(大写、小写、数字、特殊字符)
minclass = 3
# Whether to check for the words from the passwd entry GECOS string of the user
usercheck = 1
# Whether to check for the words from the cracklib dictionary
dictcheck = 1
# Enforces pwquality checks on the root user password.
enforce_for_root
EOF
log_info "pwquality.conf 配置完成"
}
# 配置密码有效期
configure_password_aging() {
log_step "步骤 5/5: 配置密码有效期策略..."
log_info "配置 /etc/login.defs..."
sed -i 's/^PASS_MAX_DAYS.*/PASS_MAX_DAYS 90/' /etc/login.defs
sed -i 's/^PASS_MIN_DAYS.*/PASS_MIN_DAYS 0/' /etc/login.defs
sed -i 's/^PASS_WARN_AGE.*/PASS_WARN_AGE 7/' /etc/login.defs
if ! grep -q "^PASS_MIN_LEN" /etc/login.defs; then
echo "PASS_MIN_LEN 8" >> /etc/login.defs
else
sed -i 's/^PASS_MIN_LEN.*/PASS_MIN_LEN 8/' /etc/login.defs
fi
log_info "login.defs 配置完成"
log_info "为 root 用户设置密码有效期..."
chage -M 90 -m 0 -W 7 root
log_info "重置 root 密码修改时间为今天..."
chage -d $(( $(date +%s) / 86400 )) root
log_success "已对 root 用户应用密码策略并设置有效期90天"
local users=$(get_normal_users)
if [ -z "$users" ]; then
log_warn "未找到普通用户(UID >= 1000),跳过普通用户配置"
return
fi
echo ""
show_normal_users
echo -e "${YELLOW}是否为以上普通用户配置密码有效期?${NC}"
echo " [1] 是 - 为所有普通用户配置密码有效期 90 天"
echo " [2] 否 - 跳过普通用户配置(仅配置 root)"
echo " [3] 自定义 - 手动选择要配置的用户"
echo ""
read -p "请选择 [1-3]: " user_choice
case $user_choice in
1)
log_info "为所有普通用户设置密码有效期..."
local user_count=0
while IFS=: read -r username uid homedir; do
chage -M 90 -m 0 -W 7 "$username"
log_info " ✓ 已设置用户: $username"
((user_count++))
done <<< "$users"
log_info "共设置 $user_count 个普通用户的密码有效期"
;;
2)
log_warn "已跳过普通用户密码有效期配置"
;;
3)
configure_selected_users "$users"
;;
*)
log_warn "无效选择,跳过普通用户密码有效期配置"
;;
esac
}
# 为选定的用户配置密码有效期
configure_selected_users() {
local users="$1"
echo ""
log_info "请输入要配置密码有效期的用户名,多个用户用空格分隔"
log_info "可用用户: $(echo "$users" | cut -d: -f1 | tr '\n' ' ')"
echo ""
read -p "用户列表: " selected_users
if [ -z "$selected_users" ]; then
log_warn "未选择任何用户,跳过配置"
return
fi
local configured_count=0
local failed_users=""
for username in $selected_users; do
if echo "$users" | grep -q "^${username}:"; then
chage -M 90 -m 0 -W 7 "$username"
log_info " ✓ 已设置用户: $username"
((configured_count++))
else
log_warn " ✗ 用户 $username 不存在或不是普通用户,跳过"
failed_users="$failed_users $username"
fi
done
echo ""
log_info "成功配置 $configured_count 个用户的密码有效期"
if [ -n "$failed_users" ]; then
log_warn "以下用户配置失败:$failed_users"
fi
}
# 验证配置
verify_configuration() {
echo ""
log_step "===== 配置验证 ====="
echo ""
echo -e "${BLUE}[1] PAM 配置:${NC}"
grep pam_pwquality.so /etc/pam.d/system-auth
echo ""
echo -e "${BLUE}[2] pwquality 配置:${NC}"
cat /etc/security/pwquality.conf | grep -v '^#' | grep -v '^$'
echo ""
echo -e "${BLUE}[3] 密码有效期配置:${NC}"
grep PASS_ /etc/login.defs | grep -v '^#'
echo ""
echo -e "${BLUE}[4] root 用户密码策略:${NC}"
chage -l root || chage -l root | grep -E "(Last password change|Password expires|Maximum number|Minimum number|Number of days of warning)"
echo ""
echo -e "${BLUE}[5] 已配置密码有效期的普通用户:${NC}"
local users=$(get_normal_users)
if [ -n "$users" ]; then
echo ""
printf "%-20s %-15s %-15s %-15s\n" "用户名" "最大有效期" "最小间隔" "警告天数"
echo "---------------------------------------------------------------"
while IFS=: read -r username uid homedir; do
local max_days=$(chage -l "$username" | grep "Maximum" | awk -F: '{print $2}' | xargs)
local min_days=$(chage -l "$username" | grep "Minimum" | awk -F: '{print $2}' | xargs)
local warn_days=$(chage -l "$username" | grep "warning" | awk -F: '{print $2}' | xargs)
if [ "$max_days" = "90" ]; then
printf "%-20s %-15s %-15s %-15s\n" "$username" "$max_days" "$min_days" "$warn_days"
fi
done <<< "$users"
echo ""
else
log_info "无普通用户"
echo ""
fi
}
# 测试密码复杂度
test_password_policy() {
echo ""
log_step "===== 密码复杂度测试 ====="
echo ""
read -p "是否进行密码复杂度测试?(y/n): " do_test
if [ "$do_test" != "y" ]; then
log_info "跳过测试"
return
fi
local test_user="testpwd_$(date +%s)"
log_info "创建测试用户: $test_user"
useradd "$test_user"
echo ""
log_info "请为测试用户设置密码,建议测试以下密码:"
echo " ✅ Abcd1234 (3种: 大写+小写+数字)"
echo " ✅ [O_GtbyX (3种: 大写+小写+特殊字符)"
echo " ✅ test@123 (3种: 小写+数字+特殊字符)"
echo " ✅ Test@123 (4种: 大写+小写+数字+特殊字符)"
echo " ❌ 123456 (1种: 只有数字)"
echo " ❌ abcdefgh (1种: 只有小写)"
echo " ❌ Test123 (2种: 大写+小写+数字,但只有2种)"
echo ""
passwd "$test_user"
echo ""
log_info "查看测试用户密码策略:"
chage -l "$test_user"
echo ""
read -p "是否删除测试用户 $test_user?(y/n): " do_delete
if [ "$do_delete" = "y" ]; then
userdel -r "$test_user" 2>/dev/null
log_info "已删除测试用户: $test_user"
else
log_warn "保留测试用户: $test_user"
log_warn "手动删除命令: userdel -r $test_user"
fi
}
# 显示配置摘要
show_summary() {
echo ""
echo "=========================================="
log_info "配置完成摘要"
echo "=========================================="
echo ""
echo "✅ 密码复杂度要求:"
echo " - 最小长度: 8 位"
echo " - 字符类型: 至少 3 种(大写、小写、数字、特殊字符)"
echo " - 示例密码:"
echo " • Abcd1234 (大写+小写+数字)"
echo " • [O_GtbyX (大写+小写+特殊字符)"
echo " • test@123 (小写+数字+特殊字符)"
echo " • Test@123 (大写+小写+数字+特殊字符)"
echo " - 禁止使用: 用户名、字典单词"
echo " - 对 root 生效: 是"
echo " - 历史密码限制: 无"
echo ""
echo "✅ 密码有效期策略:"
echo " - 最大有效期: 90 天"
echo " - 最小修改间隔: 0 天 (随时可改)"
echo " - 过期前警告: 7 天"
echo ""
echo "✅ 已生效用户:"
echo " - root 用户: 已设置"
local users=$(get_normal_users)
local configured_count=0
if [ -n "$users" ]; then
while IFS=: read -r username uid homedir; do
local max_days=$(chage -l "$username" | grep "Maximum" | awk -F: '{print $2}' | xargs)
if [ "$max_days" = "90" ]; then
((configured_count++))
fi
done <<< "$users"
fi
local total_users=$(echo "$users" | wc -l)
[ -z "$users" ] && total_users=0
echo " - 普通用户: $configured_count/$total_users 个已配置"
echo ""
echo "=========================================="
}
# 显示未配置的用户
show_unconfigured_users() {
local users=$(get_normal_users)
if [ -z "$users" ]; then
return
fi
local unconfigured_users=""
while IFS=: read -r username uid homedir; do
local max_days=$(chage -l "$username" | grep "Maximum" | awk -F: '{print $2}' | xargs)
if [ "$max_days" != "90" ]; then
unconfigured_users="$unconfigured_users $username"
fi
done <<< "$users"
if [ -n "$unconfigured_users" ]; then
echo ""
log_warn "以下普通用户未配置密码有效期:"
echo " $unconfigured_users"
echo ""
log_info "如需配置,可手动执行:"
for user in $unconfigured_users; do
echo " chage -M 90 -m 0 -W 7 $user"
done
fi
}
# 主函数
main() {
echo "=========================================="
echo " CentOS 7.9 密码复杂度配置脚本 v2.1"
echo "=========================================="
echo ""
check_root
check_system
echo ""
read -p "即将开始配置,是否继续?(y/n): " confirm
if [ "$confirm" != "y" ]; then
log_info "用户取消操作"
exit 0
fi
echo ""
install_packages
backup_configs
configure_pam
configure_pwquality
configure_password_aging
verify_configuration
test_password_policy
show_summary
show_unconfigured_users
log_info "所有配置已完成!"
log_warn "建议重启系统后再次验证配置"
}
main "$@"
配置后验证清单
# ✅ 检查 PAM 配置
grep pam_pwquality.so /etc/pam.d/system-auth
# ✅ 检查 pwquality 配置
cat /etc/security/pwquality.conf | grep -v '^#' | grep -v '^$'
# ✅ 检查密码有效期
grep PASS_ /etc/login.defs | grep -v '^#'
# ✅ 检查root用户密码策略
chage -l root
# ✅ 测试密码复杂度(创建测试用户)
useradd testpwd
passwd testpwd # 尝试输入 Test@123
# ✅ 检查用户密码策略
chage -l testpwd
# 为单个用户配置密码有效期
chage -M 90 -m 0 -W 7 username
# 批量配置多个用户
for user in user1 user2 user3; do
chage -M 90 -m 0 -W 7 "$user"
echo "已配置用户: $user"
done
# ✅ 清理测试用户
userdel -r testpwd
