PAM (Pluggable Authentication Modules) 是Linux的认证框架,将认证逻辑从应用程序中解耦。应用程序只需调用PAM API,具体的认证方式由PAM配置决定。
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Application │────▶│ PAM API │────▶│ PAM Modules │
│ (sshd/login) │ │ (libpam) │ │ pam_*.so │
└──────────────┘ └──────────────┘ └──────────────┘
│
┌──────┴──────┐
│ PAM Config │
│ /etc/pam.d/ │
└─────────────┘
$ ls /etc/pam.d/ | head -15
chfn chpasswd chsh common-account common-auth common-password common-session common-session-noninteractive cron login newusers other passwd runuser runuser-l
# 格式:类型 控制标志 模块路径 [模块参数]
# 示例:
auth required pam_sepermit.so
auth include common-auth
account required pam_nologin.so
password requisite pam_pwquality.so retry=3
session optional pam_motd.so
| 类型 | 功能 | 调用时机 |
|---|---|---|
| auth | 身份认证 | 验证用户身份(密码/密钥/令牌) |
| account | 账户验证 | 检查账户是否有效(过期/锁定/时间限制) |
| password | 密码管理 | 修改密码时的策略检查 |
| session | 会话管理 | 登录/注销时的操作(日志/资源限制/家目录) |
| 标志 | 认证失败 | 后续模块 | 整体结果 | 用途 |
|---|---|---|---|---|
| required | 记录失败 | 继续执行 | 任一required失败则失败 | 核心认证(必须通过) |
| requisite | 立即返回失败 | 停止执行 | 立即失败 | 前置条件检查 |
| sufficient | 忽略失败 | 继续执行 | 成功则整体成功 | 替代认证方式 |
| optional | 忽略 | 继续执行 | 仅当唯一模块时有效 | 可选功能 |
| include | N/A | 引用其他配置 | 按引用内容判定 | 配置复用 |
| substack | N/A | 类似include | 不影响外层sufficient | 嵌套配置 |
| 模块 | 功能 | 示例 |
|---|---|---|
| pam_unix.so | 传统密码认证 | auth required pam_unix.so |
| pam_tally2.so | 登录失败计数与锁定 | auth required pam_tally2.so deny=5 |
| pam_faillock.so | 失败锁定(新) | auth required pam_faillock.so preauth deny=5 |
| pam_google_authenticator | 双因素认证 | auth required pam_google_authenticator.so |
| pam_sepermit.so | SELinux登录限制 | auth required pam_sepermit.so |
| 模块 | 功能 |
|---|---|
| pam_nologin.so | 检查/etc/nologin文件,存在时仅允许root登录 |
| pam_time.so | 基于时间的访问控制 |
| pam_access.so | 基于IP/用户的访问控制 |
| pam_rootok.so | root免密(su命令使用) |
# 密码复杂度配置
# /etc/pam.d/common-password (Debian/Ubuntu)
password requisite pam_pwquality.so retry=3 minlen=8 difok=3 ucredit=-1 lcredit=-1 dcredit=-1 ocredit=-1
# 参数说明:
# retry=3 - 最多重试3次
# minlen=8 - 最小长度8
# difok=3 - 与旧密码至少3个字符不同
# ucredit=-1 - 至少1个大写字母
# lcredit=-1 - 至少1个小写字母
# dcredit=-1 - 至少1个数字
# ocredit=-1 - 至少1个特殊字符
| 模块 | 功能 |
|---|---|
| pam_limits.so | 设置资源限制(ulimit) |
| pam_motd.so | 显示当日消息(Message of the Day) |
| pam_lastlog.so | 显示上次登录信息 |
| pam_mkhomedir.so | 自动创建家目录(LDAP用户) |
| pam_env.so | 设置环境变量 |
$ cat /etc/pam.d/sshd 2>/dev/null | grep -v '^#' | grep -v '^$' | head -15
@include common-auth account required pam_nologin.so @include common-account session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close session required pam_loginuid.so session optional pam_keyinit.so force revoke @include common-session session optional pam_motd.so motd=/run/motd.dynamic session optional pam_motd.so noupdate session optional pam_mail.so standard noenv # [1] session required pam_limits.so session required pam_env.so # [1] session required pam_env.so user_readenv=1 envfile=/etc/default/locale session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open @include common-password
# 配置pam_faillock(CentOS 8+/Ubuntu 22.04+)
# 在/etc/pam.d/sshd或login中添加:
auth required pam_faillock.so preauth silent deny=5 unlock_time=900
auth include common-auth
auth [default=die] pam_faillock.so authfail deny=5 unlock_time=900
account required pam_faillock.so
# 查看失败记录
sudo faillock --user username
# 解锁用户
sudo faillock --user username --reset
$ cat /etc/security/limits.conf | grep -v '^#' | grep -v '^$' | head -10
N/A
# 格式:域 类型 项目 值
# 域:用户名/@组名/*(所有)/%(maxlogins)
# 类型:soft(软限制)/hard(硬限制)/-(both)
# 项目:core/fsize/nofile/nproc/memlock/等
# 示例配置
* soft nofile 65535 # 所有用户,软限制,最大打开文件数
* hard nofile 65535 # 所有用户,硬限制
@developers hard nproc 500 # developers组,最大进程数
username hard maxlogins 3 # 特定用户,最大登录数
* soft core unlimited # 允许core dump
$ cat /etc/nsswitch.conf | grep -v '^#' | grep -v '^$'
passwd: files systemd group: files systemd shadow: files systemd gshadow: files systemd hosts: files dns networks: files protocols: db files services: db files ethers: db files rpc: db files netgroup: nis
NSS决定系统如何查找用户、组、主机名等信息。配置中files表示本地文件,dns表示DNS查询,ldap表示LDAP目录。
预防措施:修改PAM配置前,保持一个root会话不退出!
# 1. 测试PAM配置
sudo pam_tally2 --user=username # 查看失败次数
# 2. 恢复方法:从另一个root会话修正配置
# 3. 单用户模式修复
# 启动时在GRUB添加 init=/bin/bash
# 4. Live CD修复
# 挂载根分区后修改pam配置
# 1. 查看当前密码策略
sudo pam-config --list 2>/dev/null || grep -v "^#" /etc/pam.d/common-password
# 2. 修改密码最小长度为12
# 编辑 /etc/security/pwquality.conf 或 /etc/pam.d/common-password
# 3. 测试密码策略
sudo passwd testuser # 尝试设置弱密码,应被拒绝
# 设置用户的最大打开文件数
echo "* soft nofile 65535" | sudo tee -a /etc/security/limits.conf
echo "* hard nofile 65535" | sudo tee -a /etc/security/limits.conf
# 验证(需要重新登录生效)
ulimit -n
✅ 理解PAM框架与4种控制标志
✅ 掌握常见PAM模块功能
✅ 学会配置密码策略与登录锁定
✅ 了解limits.conf资源限制
# 安装Google Authenticator PAM模块
sudo apt install libpam-google-authenticator
# 用户初始化(每个用户执行)
google-authenticator
# 配置SSH使用2FA
# /etc/pam.d/sshd 添加:
auth required pam_google_authenticator.so
# /etc/ssh/sshd_config 修改:
ChallengeResponseAuthentication yes
AuthenticationMethods publickey,keyboard-interactive
sudo systemctl restart sshd
# /etc/security/time.conf
# 格式:services;ttys;users;times
login;tty*;zhangsan;Wd0800-1800 # 工作日8-18点
sshd;*;!*;Wk0900-1700 # 工作时间
# 在PAM中启用
# /etc/pam.d/login 添加:
account required pam_time.so
# 安装PAM LDAP模块
sudo apt install libpam-ldapd
# /etc/nslcd.conf 配置LDAP服务器
uri ldap://ldap.example.com
base dc=example,dc=com
ldap_version 3
# PAM自动创建家目录
# /etc/pam.d/common-session
session optional pam_mkhomedir.so skel=/etc/skel umask=077
#!/bin/bash
# pam-audit.sh - PAM配置安全审计
echo "===== PAM安全审计 ====="
# 检查PAM配置文件权限
echo "--- 配置文件权限 ---"
ls -la /etc/pam.d/ | head -5
# 检查密码策略
echo "\n--- 密码策略 ---"
grep -v "^#" /etc/pam.d/common-password 2>/dev/null || grep -v "^#" /etc/pam.d/system-auth 2>/dev/null
# 检查是否有空密码账户
echo "\n--- 空密码检查 ---"
sudo awk -F: '($2 == "" || $2 == "!")' /etc/shadow
# 检查root直接登录
echo "\n--- root登录限制 ---"
grep -v "^#" /etc/securetty 2>/dev/null | head -5
grep "PermitRootLogin" /etc/ssh/sshd_config 2>/dev/null
# 检查limits配置
echo "\n--- 资源限制 ---"
grep -v "^#" /etc/security/limits.conf | grep -v "^$"