简介:Linux-PAM(Pluggable Authentication Modules)是Linux系统下实现可插拔身份验证的核心组件,这套源码包面向系统管理员、嵌入式开发者和安全工程师,用于理解并定制登录、服务访问等多场景的认证机制。压缩包整体仅2.05MB,共含1064个文件,其中C源码与头文件(.c/.h)构成libpam核心库及各认证模块,configure、Makefile.am等构建脚本支撑跨环境编译,pamd与conf文件提供典型服务配置参考,man手册和README/CHANGELOG则帮助快速上手与版本追踪,另有大量po翻译文件及tst_pam_*测试用例。通过解压、编译与安装,可以将PAM 1.3.x集成进目标系统,也可基于示例配置学习pam_unix、pam_cracklib等常见模块的调用与调试方法,为审计认证流程、二次开发或安全加固提供直接素材。已有1109人学习下载,适合需要从源码层面掌握PAM机制、配置策略或进行定制移植的Linux开发者与运维人员。
1. Linux-PAM 1.3.0 是什么:为什么老运维还在手动编译它
手头一台 CentOS 7 服务器,安全扫描报告 PAM 版本偏低,要求升到 1.3.x。系统源里只有 1.1.8,绕不开的路就是去镜像站拉 Linux-PAM-1.3.0.tar.gz,现场 configure 编译。这个标题看起来是个源码包文件名,实际上背后是整个 Linux 登录认证链路的更换:核心库 libpam 的替换、几十个认证模块的编译、/etc/pam.d 配置的兼容。PAM 是 Linux 下所有需要认证的程序共用的通道,ssh、su、sudo、login、passwd 全部走它,动一处就是动全系统登录入口。适合谁?适合被安全基线逼着升级的运维,适合要维护内网镜像源、给成批 Linux 服务器统一认证策略的人。说实话,我不是天生爱折腾源码,但认证栈这种“黑匣子”,不敢随便交给来路不明的二进制,自己编一次至少知道装了什么。
2. 拿到源码先看门道:Linux-PAM 1.3.0 的目录结构与编译前准备
2.1 解压后先确认这五个目录,别急着 make
很多新手拿到 tar.gz 第一反应是直接 ./configure,结果要么缺依赖,要么编出来的模块路径不对。我习惯先解压,把目录结构扫一遍再动手。
mkdir -p ~/src cd ~/src tar xzf Linux-PAM-1.3.0.tar.gz cd Linux-PAM-1.3.0 ls -d libpam libpamc libpam_misc modules conf doc teststar.gz 只是发布容器,真正的工程从展开后的目录才算开始。上面这一串目录里,有五个是重点。libpam 是核心动态库源码,所有应用最终都链到它;libpamc 和 libpam_misc 是辅助库,调用方用得到;modules 是重头戏,pam_unix、pam_faillock、pam_env、pam_limits 这些模块的源码全在它下面;conf 里放着示例 pam.d 配置,编译完可以参考它搭自己的认证规则;doc 是文档,tests 是回归测试。
modules 目录里每个子目录名就是模块名,编译时按目录逐个生成 .so。比如 modules/pam_unix 生成 pam_unix.so,modules/pam_faillock 生成 pam_faillock.so。知道这个对应关系,后面裁剪、排错都能少花时间。我一般还会看一眼 modules 下有哪些符号链接,有些模块是软链到公共实现的,比如 pam_unix 的某些变体,但这在 1.3.0 里不多,不影响主流程。
2.2 编译前依赖:flex、bison、libcrypt、libdb 缺一不可
Linux-PAM 的 configure 会在生成 Makefile 前做一堆依赖探测,最常见的失败原因不是缺编译器,而是缺词法、语法分析器,或者缺 libcrypt。最小化安装的 Linux 系统尤其容易踩。
rpm -qa --qf '%{NAME}\n' | grep -E '^(flex|bison|glibc-devel|libxcrypt|libdb|libselinux)$'这里列的是编译 1.3.0 时绕不开的依赖。flex 是词法分析生成器,libpam 解析配置文件 token 时要用它生成 C 代码;bison 是语法分析器,跟 flex 配合处理 pam.d 的语法。libcrypt 提供 crypt_r 这类函数,pam_unix.so 和 pam_pwhistory 都直接依赖它。libdb 只有编 pam_userdb 才需要,libselinux 也只有编 pam_selinux 时需要,这两个可以按需开着,但前三个是默认模块链路上的硬依赖。
如果 configure 报flex: command not found或者bison: command not found,别慌,这是最普通的缺失。CentOS 系执行yum install flex bison glibc-devel libxcrypt-devel,Debian 系用 apt 装同名包,装完重跑 configure 通常就过了。真正麻烦的是 libcrypt 版本太老,编译能过,运行时反而报符号缺失,这个后面第 5 章会专门讲。检查完依赖再往下走,比 make 到一半回头补包要省事得多。
2.3 用 configure --help 把功能裁剪做在前头
Linux-PAM 默认会把几乎所有模块都编一遍,编译时间长不说,内网环境下很多模块根本用不上。我一般会先看 configure 支持哪些开关,把不需要的模块裁掉,减小升级后的攻击面。
cd Linux-PAM-1.3.0 ./configure --help | grep -E 'modules|selinux|audit|debug|pie'几个参数在我做过的大多数生产环境里都会用到。--with-modules-dir=/usr/lib64/security指定模块安装目录,64 位系统这里是标准路径;--prefix=/usr让库文件装到 /usr/lib64,和系统已有 PAM 位置保持一致;--disable-pie对纯内网环境一般可以关掉,省一点编译时间;--enable-debug只在需要排查时才开,生产环境不建议带,它会打大量日志。模块裁剪用--disable-模块名或--enable-模块名,比如明确不用 pam_ssh 模块就 disable 掉,让 configure 直接跳过。
裁剪思路是:先想清楚这台机器承担什么角色。普通 Web 服务器只需要 pam_unix、pam_env、pam_limits、pam_faillock 这几个核心模块,其他模块编了也是占地方。如果拿不准,就保持全量编译,毕竟 PAM 模块体积不大,安全收益优先。
3. 编译安装 Linux-PAM 1.3.0:configure / make / make install 的最小可复现步骤
3.1 三条命令跑通本地安装
依赖确认后,编译安装本身并不复杂。下面这套命令是我在 64 位 CentOS 系上反复用的最小步骤,新机器拿来就能跑。
cd Linux-PAM-1.3.0 ./configure --prefix=/usr --sysconfdir=/etc \ --with-modules-dir=/usr/lib64/security \ --disable-pie make -j$(nproc) sudo make install sudo ldconfigconfigure 里三条参数决定了大方向。--prefix=/usr把动态库放进 /usr/lib64,因为系统现有应用在查找 libpam.so.0 时默认走 /usr/lib64,如果装到 /usr/local,后面 ldd 会看到一堆应用链到了旧路径。--sysconfdir=/etc让 pam.d 配置目录保持 /etc/pam.d,不迁移位置,这能少改很多应用配置。--with-modules-dir=/usr/lib64/security指明 .so 模块的落点,写错就会出现“32 位和 64 位路径错位”的坑。make 的-j$(nproc)是按 CPU 核数并行编译,老机器如果内存小,可以改成-j2,避免编译中途被 OOM 杀掉。
make install 完成后一定要跑 ldconfig。它更新动态链接缓存,不然系统里好几个 libpam.so.0 并存时,应用不知道该用哪个。装完这一步,先不要急着重启任何服务,继续往下验证。
3.2 安装后立刻要改的 /etc/pam.d 配置
make install 只会替换二进制,不会帮你改 /etc/pam.d 下的认证规则。原有的配置还在,但引用的模块文件已经被新版本覆盖,所以接下来必须按新版本的行为把配置顺一遍,重点是密码失败锁定策略。
我之前在内网加固时,最常做的事情就是把 system-auth 和 password-auth 里的 pam_tally2 替换成 pam_faillock。PAM 1.3.0 对 faillock 的支持已经相当完整,计数文件独立,不容易出现 tally2 那种多会话互相覆盖的问题。参考配置如下:
# /etc/pam.d/system-auth auth required pam_env.so auth [success=1 default=ignore] pam_unix.so nullok try_first_pass auth requisite pam_faillock.so preauth auth required pam_faillock.so authfail auth optional pam_permit.so account required pam_faillock.so这里每个关键字的顺序都不能乱。preauth 阶段在输入密码前先检查计数,authfail 阶段在密码错误后累加计数,account 阶段最终判断是否锁定。如果 preauth 放到了 pam_unix.so 后面,用户输错一次密码都不会被记数,锁定策略就失效了。pam_faillock 的默认锁定阈值是 3 次,可以通过/etc/security/faillock.conf调整,这个文件如果不存在就用系统默认值。改完配置后,我一般会另外打开一个 root 会话,再重启 sshd,防止当前会话把登录入口堵死。
3.3 验证 PAM 是否生效:pamtester 与 ldconfig 两条路
验证环节最容易被跳过,但恰恰是最该花时间的。编译安装后至少要用两条路确认:动态库加载路径是否正确,认证流程是否真的走到了新模块。
ldconfig -p | grep libpam.so sudo pamtester system-auth yourname authenticateldconfig -p 列出当前所有动态库缓存,重点看 libpam.so.0 是不是指向 /usr/lib64 下的新文件。如果列出两个路径,说明机器上有多个版本共存,需要检查 /etc/ld.so.conf.d 里有没有旧路径的残留配置。pamtester 是测试 PAM 配置的常用工具,它会模拟一次完整认证流程,第三个参数是你要测试的用户名。执行后输入正确密码,返回 success 就说明 pam_unix 链路正常;如果阶段上卡住,pamtester 会把卡在哪个模块直接暴露出来。机器上没有 pamtester 的话,装一个也很简单,CentOS 系直接yum install pamtester。这种验证在 linux 系统管理里属于基本功,但很多人图省事跳过,结果上线后 sudo 全挂。
4. 从 1.3.0 到 1.3.1:升级到底改了哪些和认证相关的实货
4.1 1.3.1 的 bugfix 集中在哪几个模块
标题里同时出现 1.3.0 和 1.3.1,说明很多人第一步拿到的可能不是最终版。我的建议是:源码包如果是 1.3.0,编完能跑以后,只要有条件还是升到 1.3.1。1.3.1 是维护性发布,不会大刀阔斧地改 API,但对认证链路的修正很有价值。
从我对 PAM 版本演进的观察看,这类小版本通常集中修四个方向。第一是 pam_unix 对密码散列轮数的边界处理,某些场景下超过默认轮数的密码会让验证异常;第二是 pam_faillock 在认证成功后的计数重置,旧版本里偶发锁定计数不清的问题;第三是 pam_limits 和 pam_env 的解析器对特殊字符的容错;第四是 libpam 核心层对 name=value 参数的空值处理。这些听起来都不大,但认证链路恰恰是“小问题引发大故障”的高发区。
升级前不要只看版本号,我习惯把两个版本的 modules 目录做一次 diff,确认改动是否影响我当前用到的模块。这一步能帮你判断,这次升级是普通维护还是需要重写配置。
4.2 升级前用 diff 核对 modules 目录
具体操作是解压两份源码,然后用 diff 对比模块目录。这里我用的是 release 目录对比,不需要 git 仓库,离线服务器也能做。
tar xzf Linux-PAM-1.3.1.tar.gz diff -ruN Linux-PAM-1.3.0/modules Linux-PAM-1.3.1/modules \ | grep '^diff' | head -50diff 的-r表示递归比较子目录,-u输出上下文格式,-N让新增文件也能显示出来。grep 筛选出所有发生变化的文件列表,看 1.3.1 究竟动了哪个模块。如果列表里有 pam_unix 和 pam_faillock,那我当前配置大概率受影响,必须重新编译;如果只改了 pam_ssh 这类我不用的模块,升级风险就小很多。
实际升级时,我不太推荐在 1.3.0 的源码目录上直接打补丁,除非你手里有官方 release 的完整补丁文件。常见做法是把 1.3.1 重新解压,重复第 3 章的 configure、make、make install 流程。因为两个 tar.gz 的构建环境可能已经变了,局部替换 .so 容易造成 Makefile 依赖版本错位,最后模块之间符号对不上。整包重编虽然耗时,但结果是干净的。升级前记得备份,备份方法在最后一章会给到。
4.3 升完级必须回归的三类场景:sudo、login、密码策略
版本升完,配置不能想当然地沿用。我给自己定了一个回归清单,每次升 PAM 都照着跑一遍,不跑完不放出。
| 场景 | 验证命令 | 预期结果 |
|---|---|---|
| SSH 登录 | ssh user@localhost | 正确密码直接进,无延迟 |
| sudo 提权 | sudo -i | 不报 PAM 认证错误 |
| 密码修改 | passwd | 旧密码校验通过,新密码按策略生效 |
| su 切换 | su - root | 输入 root 密码后正常切换 |
这四类场景分别覆盖 PAM 的四个管理组。SSH 登录走 auth 和 account,sudo 走 account 和 session,passwd 走 password,su 综合走 auth、account、session。只要有一个组配置写错,对应的场景就会挂。回归时如果发现 SSH 登录失败,第一时间看日志,journalctl -u sshd比翻 /var/log/secure 更直接,能定位到具体是哪个 .so 出了问题。这类 linux 运维故障案例的处理思路,核心就是先确认是哪个模块报的错,再回滚或修正对应配置。
5. PAM 编译与配置避坑:五个让系统登录翻车的真实案例
5.1 案例一:make install 后 sudo 直接不可用
现象:编译安装完还没重启,sudo 就报sudo: PAM account management error: Permission denied,当前用户明明有 sudo 权限,就是提不了权。
原因:常见做法是 make install 把 /usr/lib64/libpam.so.0 和所有模块都覆盖了,但 sudo 二进制仍然持有旧的 libpam 句柄,或者模块目录里缺少 sudo 配置引用的 pam_rootok.so。另一个高频原因是 configure 时 prefix 写成了 /usr/local,模块装到了 /usr/local/lib/security,而 sudo 默认去 /usr/lib64/security 找模块,全部扑空。
解决:先确认 sudo 链接的是哪个 libpam,再确认模块目录里的文件数量。
ldd /usr/bin/sudo | grep pam ls -l /usr/lib64/security/pam_*.so | wc -l如果 ldd 指向 /usr/local/lib/libpam.so.0,说明 prefix 没设对,需要在 /etc/ld.so.conf.d/ 里补 /usr/local/lib 并跑 ldconfig,或者干脆按第 3 章重编一套到 /usr。如果模块文件数很少,说明 make install 只装了部分模块,回到源码目录重新make install。这个案例给我最大的教训是:安装完没验证前,千万别关当前 root 会话。
5.2 案例二:pam_unix.so 找不到符号
现象:登录时 journal 里出现pam_unix.so: undefined symbol: crypt_r,然后整个 sshd 子进程退出,密码验证直接失败。
原因:configure 时系统里有 libcrypt 头文件,模块编译期正常;但运行时加载的 libcrypt.so.1 是老版本,不导出 crypt_r 这个符号。最小化安装的系统尤其常见,装了 libxcrypt 但版本偏老,符号表对不上。
解决:先查系统里 libcrypt 的实际版本,再决定是补包还是重编。
rpm -q libxcrypt nm -D /lib64/libcrypt.so.1 | grep crypt_rnm 命令没有输出的话,说明运行库不提供这个符号。CentOS 系直接yum install libxcrypt-devel,装完重新 configure、make、make install 一遍。补完还不行,就别纠结符号了,把整个依赖链一起升级。这类问题翻车过一次后,我现在每次编 PAM 前都会先确认 libcrypt 的符号表,少走一小时弯路。
5.3 案例三:pam_faillock 和 pam_tally2 混用
现象:密码连续错三次后被锁定,等了十分钟恢复正常,但再次输错三次,锁定时间反而越来越长,有时甚至不自动解锁。
原因:配置里同时出现了 pam_faillock 和 pam_tally2。两个模块各自维护独立的计数存储,faillock 写 /run/faillock 下的文件,tally2 写 /var/log/tallylog。auth 阶段两个模块都被调用时,失败次数会叠加;account 阶段如果顺序不对,一个模块允许通过,另一个模块又判定锁定,最终表现就是“有时锁、有时不锁、锁了难解开”。
解决:统一只保留一个锁定模块,我推荐 pam_faillock,并在 /etc/pam.d/system-auth 里按下面顺序配:
auth required pam_env.so auth [success=1 default=ignore] pam_unix.so nullok try_first_pass auth requisite pam_faillock.so preauth auth required pam_faillock.so authfail auth optional pam_permit.so account required pam_faillock.sopam_faillock 的机制是 preauth 阶段先看是否已锁定,authfail 阶段在认证失败后计数,account 阶段最终检查锁定状态。三处缺一不可,顺序也不能颠倒。切换后记得把 /var/log/tallylog 删掉,避免旧计数干扰判断。这个问题在在线答疑里见过无数次,核心是“用一个模块的思路替代另一个模块的配置”,不是简单叠加。
5.4 案例四:64 位系统模块路径 32 位残留
现象:新装的 64 位 CentOS,编译安装 PAM 后 64 位程序认证正常,但某些服务报找不到 pam_unix.so,或者报pam_authenticate: Module is unknown。
原因:configure 时把--with-modules-dir写成了 /lib/security。在 64 位发行版上,/lib 是 32 位兼容目录,64 位动态链接器默认搜索 /usr/lib64/security,两边的模块目录并不互通。残留的 32 位路径会让部分应用读取不到新模块,而系统自带应用又能找到旧模块,表现就是时好时坏。
解决:重新编译,把模块目录指到 64 位路径,并验证模块实际落地位置。
./configure --prefix=/usr --sysconfdir=/etc \ --with-modules-dir=/usr/lib64/security make -j$(nproc) sudo make install sudo find /usr/lib64/security -maxdepth 1 -name 'pam_*.so' | wc -l最后一行 find 统计模块数量,如果数字远小于预期,说明 configure 参数没生效或编译过程中有模块跳过。另外检查 /etc/ld.so.conf.d 下有没有旧配置残留,有的话删掉并 ldconfig。路径这个坑,在 linux 系统安装阶段就埋下了,选架构时没注意,后面编译才会连环踩。
5.5 案例五:升级后 SSH 登录卡住不动
现象:输入 SSH 密码后不是立即失败,而是卡一两分钟,最后才报Permission denied或Connection closed。
原因:pam_unix.so 在验证密码时会调用外部辅助程序 /usr/bin/unix_chkpwd。这个程序必须具有 setuid root 权限,否则它无法读取 shadow 文件,只能卡在等待状态然后超时失败。make install 在覆盖模块时,有时会把辅助程序的 setuid 位清掉,或者系统本身就没装这个文件。
解决:先检查 unix_chkpwd 是否存在,再检查它的权限位。
ls -l /usr/bin/unix_chkpwd sudo chmod u+s /usr/bin/unix_chkpwd正常情况应看到-rwsr-xr-x,s 位在 owner 权限位置。如果文件不存在,说明辅助程序没装全,回到源码目录重新 make install,并确认make install-exec-hook没有被跳过。这种卡顿问题用 strace 跟一下也很直观,能看到 sshd 进程阻塞在哪个文件打开上。以后遇到 SSH 慢登录,先查 unix_chkpwd,而不是怀疑网络。
6. 把 PAM 用得更稳:配置权限收口与回滚技巧
PAM 这类底层认证组件,最怕的不是功能不会配,而是改挂了没有后悔药。我现在的习惯是:任何改动之前,先把配置目录和模块目录打包成一个带日期的备份包,放到系统盘之外的地方。
sudo mkdir -p /var/backup/pam sudo tar czf /var/backup/pam/pam.d-$(date +%F).tar.gz /etc/pam.d sudo tar czf /var/backup/pam/security-$(date +%F).tar.gz /usr/lib64/security两个 tar 命令分别备份配置和模块。恢复顺序要反过来:先恢复模块目录,再恢复配置目录,因为配置里可能引用了备份时还不存在的模块版本。恢复完跑 ldconfig,再按第 4 章的回归清单验证。这套方法不复杂,但能在最关键的时候把你从“auth 彻底打不开”的噩梦里救出来。
另一个进阶技巧是尽量少动主配置文件,把自定义策略收口到单独的文件里,再用@include链带进去。比如自定义登录通知,单独建一个 /etc/pam.d/login-local,内容只有一行auth optional pam_exec.so /usr/local/bin/login-notify.sh,然后在 login 文件里加@include login-local。这样主配置保持干净,出问题只需要注释掉一行 include,不用 diff 整个文件。
我自己被 5.1 那种问题坑过一次之后,已经形成肌肉记忆:编译安装前先确认 prefix 和模块目录,安装后不急着关当前会话,先跑 pamtester 和 ldd,确认无误再做配置改动。PAM 不是那种“装完就完事”的组件,它需要你像管防火墙策略一样管配置,每次改动前后都留后路。希望这些经验能帮你在升级 Linux-PAM 的路上少翻几次车。
本文还有配套的精品资源,点击获取