Linux不支持密码到期前自动切换限制级Shell,仅能在过期后通过宽限期(-I)配合PAM强制调用passwd实现降权;警告期(-W)只能提醒,不可限制功能。

Linux系统本身不提供“密码到期前自动切换限制级Shell”的原生机制。密码到期前(如提前7天警告期),用户仍拥有完整登录权限,系统仅提示、不降权。所谓“自动进入限制级Shell并关闭对外通道”,需通过组合策略主动干预登录流程,而非依赖到期倒计时自动触发。关键在于:不是“到期前自动切换”,而是“密码过期后强制拦截+降权”;到期前只能提醒,不能锁功能。
以下配置逻辑清晰、可落地,兼顾安全与可用性:
明确区分两个阶段:警告期 vs 过期后
- 警告期(如 -W 7):用户可正常登录、执行任意命令,系统只显示提醒(如PAM echo或.bashrc横幅)。此阶段无法也不应关闭SSH、禁用网络或换Shell——否则业务中断、运维失联。
- 过期后(密码真正失效):此时用户仍能认证登录(若未设宽限期),但必须立即改密。若设了宽限期(-I),则登录后强制跳转到密码修改界面;若未设宽限期,登录即失败。
所以,“自动进入限制级Shell”实际发生在密码已过期且账户仍允许登录的宽限期(inactive period)内,目标是:只允许运行 passwd,禁止其他操作。
配置宽限期 + 强制密码修改Shell(核心方案)
-
为用户设置宽限期(-I)和到期警告(-W)
chage -M 90 -W 7 -I 3 username
含义:密码90天一换,提前7天提醒,过期后给予3天宽限期。宽限期内用户可登录,但必须改密。
-
启用PAM强制密码修改(关键一步)
编辑/etc/pam.d/system-auth(RHEL/CentOS)或/etc/pam.d/common-auth(Debian/Ubuntu),在auth [default=1 success=ignore] pam_succeed_if.so user != root quiet 之后添加:auth [default=bad success=ok user_unknown=ignore] pam_faildelay.so delay=3000000 auth [default=1 success=ok] pam_permit.so auth [default=bad success=ok user_unknown=ignore] pam_succeed_if.so user = username quiet auth [success=done default=bad] pam_unix.so try_first_pass auth [default=bad success=ok] pam_pwhistory.so enforce_for_root auth [default=bad success=ok] pam_pwquality.so retry=3 minlen=12 # 新增这一行,仅对密码过期用户生效 auth [default=ignore success=ok] pam_time.so auth [default=bad success=ok] pam_exec.so /usr/local/bin/check-passwd-expired.sh
更稳妥的做法是使用标准模块:在
auth [success=ok default=ignore] pam_unix.so后插入:auth [default=bad success=ok] pam_succeed_if.so service = login quiet use_uid \ uid >= 1000 \ && (user = username) \ && (password_expired) auth [default=bad success=ok] pam_exec.so /usr/local/bin/force-passwd-shell.sh但实际中,推荐直接启用
pam_pwcheck或依赖pam_unix自带行为:只要设置了-I 3,login和sshd服务默认会在宽限期内自动调用/bin/passwd作为唯一可执行程序,用户登录后不输入新密码就无法获得Shell。 -
验证宽限期是否生效
chage -l username
确认输出含:
Password expires : Jun 15, 2026 Password inactive : Jun 18, 2026 ← 宽限期截止日(过期日+3天) Account expires : never Minimum number of days between password change : 0 Maximum number of days between password change : 90 Number of days of warning before password expires : 7
“关闭对外通道”的等效实现(非粗暴断网)
你无法、也不该在用户登录前“关闭SSH端口”或“禁用网络”。但可通过以下方式实质性阻断外连能力,仅保留改密必需通路:
-
限制宽限期用户的网络访问
使用iptables或nftables配合用户ID匹配:# 允许本机回环和DNS解析(改密可能需LDAP/AD) iptables -A OUTPUT -m owner --uid-owner username -o lo -j ACCEPT iptables -A OUTPUT -m owner --uid-owner username -d 127.0.0.1 -j ACCEPT iptables -A OUTPUT -m owner --uid-owner username -p udp --dport 53 -j ACCEPT # 拒绝其余所有外发连接 iptables -A OUTPUT -m owner --uid-owner username -j REJECT
此规则仅影响该用户进程,不影响系统服务或其他用户。
-
替换其Shell为专用改密脚本(更彻底)
创建/usr/local/bin/passwd-only-shell:#!/bin/bash echo -e "\033[1;31m⚠️ 密码已过期!请立即修改:\033[0m" exec /usr/bin/passwd "$USER"
赋权并设为用户Shell:
chmod +x /usr/local/bin/passwd-only-shell usermod -s /usr/local/bin/passwd-only-shell username
注意:此法需配合
chage -d $(date -d "1 day ago" +%Y-%m-%d) username强制密码过期,并确保该Shell不在/etc/shells中(避免被su绕过)。
不推荐但需知晓的误区
- ❌ 直接改
/etc/passwdShell 为/bin/false或/sbin/nologin:这会完全禁止登录,不属于“到期前限制”,而是“永久禁用”。 - ❌ 在
.bashrc中检测过期并exit 1:用户可绕过(如bash --norc),且不阻止SSH会话建立。 - ❌ 修改
sshd_config的ForceCommand:它对所有用户生效,无法按密码状态动态判断。
不复杂但容易忽略:真正的限制始于密码过期那一刻,而非之前;所有“到期前自动降权”都是伪需求,提醒到位 + 过期后强控,才是合规又可用的设计。


















