
简介PAMPluggable Authentication Modules是Linux系统实现灵活认证的核心组件本资源为Linux-PAM 1.3.0源码包面向系统管理员、安全运维及Linux开发人员用于掌握PAM插件化认证机制的编译、安装与配置。压缩包内含1064个文件约2.05MB文件类型以C源代码、XML配置、PO翻译、automake脚本为主涵盖libpam核心库、各认证模块如pam_unix、pam_securetty及测试程序并配有README、INSTALL、CHANGELOG等文档便于查阅版本更新与构建细节。包中同时提供了示例pamd配置和测试脚本可辅助理解服务与模块的交互方式快速搭建本地认证策略。目前已有1109人学习下载适合需要深入理解PAM工作原理或定制认证流程的中高级Linux用户通过阅读源码与配置示例能够掌握认证模块的扩展方法为系统安全加固提供基础。1. 先搞清楚Linux PAM 是怎么卡住你登录的遇到“密码明明没错root 就是登不进去”“su 突然要认证两次”“sshd 连接直接被秒断”这类诡异问题十有八九是 Linux PAM 配置翻车了。PAMPluggable Authentication Modules可插拔认证模块是几乎所有涉及认证的软件——sshd、login、su、sudo、vsftpd——都会走一遍的中转层。它把“认证”这件事拆成独立模块由你编排顺序和策略。这里拆的是 Linux-PAM 1.3.0 源码包对应维护分支 1.3.1适合运维工程师、安全岗和做嵌入式 Linux 集成的开发者。读完后你能搞懂模块机制、看懂 pam.d 配置、知道编译替换系统 PAM 时哪些坑不能踩。2. 扒源码Linux-PAM 1.3.0 的模块地图与认证事务2.1 源码目录长什么样先别急着跑 configure解压linux-PAM-1.3.0.tar.gz后你会发现它是个标准的 autotools 工程。真正决定 PAM 行为的是几个核心目录libpam/PAM 核心库提供pam_start()、pam_authenticate()等 C API应用层通过这个库与模块对话。modules/全部官方模块源码包括pam_unix、pam_env、pam_limits、pam_exec、pam_time等编译后生成.so动态库。conf/与doc/配置模板与文档里面附带的pam.d示例文件值得直接抄。tests/自带回归测试run-tests替换系统 PAM 前先跑一遍能降低翻车概率。整个架构可以理解为应用 — libpam — 配置文件/etc/pam.d/xxx— 具体模块.so。应用不知道也不关心你用的是密码还是指纹它只把认证请求交给 libpamlibpam 按配置文件的顺序逐个调用模块最后把每个模块的结果按 control 规则汇总成“通过/拒绝”。这种解耦设计让你不需要改 sshd、login 源码就能整体换掉认证逻辑。2.2 四个管理组与认证事务PAM 把认证过程分成了四个独立的管理组facility每个组对应一种场景管理组职责典型调用时机auth验证身份输密码、验证指纹、检查锁定account检查账户可用性判断是否过期、是否允许此刻登录password更新密码passwd修改密码时的策略检查session登录前后做环境准备挂载家目录、写入审计日志、设置资源限制一次完整登录的事务链路应用视角是pam_start()→pam_authenticate()auth→pam_acct_mgmt()account→pam_open_session()session→pam_close_session()→pam_end()。在源码libpam/pam_start.c可以看到事务上下文如何初始化但普通用户不需要碰这些 C 代码理解调用链是为了排查时定位日志里的报错阶段。比如/var/log/secure里写pam_unix(sshd:auth): authentication failure就说明卡在了 auth 阶段如果写pam_unix(sshd:account): expired password则是 account 阶段判定密码过期。2.3 模块命名与链接位置模块编译好之后是pam_unix.so、pam_env.so这类动态库默认安装到/lib/security/或/lib/x86_64-linux-gnu/security/具体的目录由--secure-dir参数指定。这个路径非常重要ssh 如果找不到模块会直接报pam_unix(sshd:auth): unable to dlopen。常见做法是先用find /lib /usr/lib -name pam_unix.so 2/dev/null找到系统里已有的模块位置再决定 configure 时要不要覆盖它。嵌入式环境里如果你想把 PAM 装到/usr/lib之外可以在编译时加--with-libdir...但建议尽量维持发行版的既有布局否则 ld.so 缓存与 PAM 模块搜索路径都对不上。3. 编译安装configure 参数、链接顺序与替换系统 PAM 的后果3.1 编译全套模块的完整流程先从源码包根目录开始我会用一个独立目录build-pam当安装前缀避免直接污染系统目录。具体步骤# 解压并进入源码目录 tar -xzf linux-PAM-1.3.0.tar.gz cd linux-PAM-1.3.0 # 生成 configure源码包自带所以跳过 autoreconf 也可以 # 如果包内 configure 是空壳才需要 autoreconf -vif # 配置只装模块不覆盖系统原有 pam_unix.so ./configure --prefix/opt/pam-1.3.1 \ --enable-debug \ --disable-nls \ --with-securedir/opt/pam-1.3.1/security \ --enable-mods-dir/opt/pam-1.3.1/modules make -j$(nproc) make install参数说明--prefix决定 libpam 与头文件装到哪--with-securedir是 PAM 模块.so的放置目录务必和运行时路径一致--enable-debug会在模块里保留符号方便用 gdb 追--disable-nls关掉多语言消息减少依赖。如果目标机上已存在系统自带的 PAM我不会直接把make install指向/usr而是先装到自定义前缀用LD_LIBRARY_PATH/opt/pam-1.3.1/lib做小范围验证。随后再用ldd /opt/pam-1.3.1/security/pam_unix.so检查它依赖的libcrypt、libpam是否能在本机解析。3.2 链接顺序的坑libcrypt 与 libpam 互相依赖编译官方模块时最常见的链接报错是undefined reference to pam_get_item或undefined reference to crypt。原因有两类一类是编译pam_unix.so时没有链接-lpam -lcrypt另一类是静态打包时库顺序写反了。写链接命令时被依赖的库必须放在依赖者之后例如# 编译一个自定义 PAM 模块示例注意 -lpam 放在源码文件之后 gcc -shared -fPIC -o pam_example.so example.c -lpam -lcrypt顺序错了linker 扫到example.o时还不知道pam_get_item在哪就会报未定义引用。1.3.1 源码包里modules/pam_unix/Makefile.am里已经写好了正确的LIBADD一般不会碰这个错真正容易踩的是你自己写第三方模块时把-lpam放到了前面。还有一点如果目标系统启用了 PIE地址空间随机化编译.so时记得加-fPIC否则运行时dlopen会拒绝加载。3.3 用 pamtester 验证编译结果模块编译完不代表能跑通在没有图形界面或远程会话的情况下用pamtester模拟一个认证请求最直接。安装 pamtester 后先写一个最小的配置# /etc/pam.d/test-login 内容 auth required /opt/pam-1.3.1/security/pam_unix.so account required /opt/pam-1.3.1/security/pam_unix.so # 命令行执行 pamtester test-login 你的用户名 authenticatepamtester会弹出密码输入提示如果认证通过返回 0。这里注意auth required pam_unix.so在真实配置里不会直接成功因为pam_unix.so需要拿到应用传入的密码而 pamtester 会走pam_authenticate内部的读取逻辑如果返回PAM_NEW_AUTHTOK_REQD说明 account 阶段认为密码已过期需要重置符合预期。把那台机器的/opt/pam-1.3.1打包备份好后续哪条配置出问题可以靠它和系统默认 PAM 做二选一切换。4. 配置落盘pam.d 语法、模块顺序与双因子思路4.1 从 pam.conf 到 pam.d优先级与语法现代发行版基本都走/etc/pam.d/目录每个服务sshd、login、su一个文件。旧式的/etc/pam.conf只在目录缺失时兜底。配置文件里每一行是type control module-path module-argstype去对应第二章讲的 auth、account、password、session 四组module-path可以是绝对路径如/opt/pam-1.3.1/security/pam_unix.so也直接写模块名默认搜索 secure dir。control 字段决定多个模块结果如何汇总这是整个 PAM 配置最需要想清楚的地方control 值行为required必须通过失败后不立即终止但最终整体失败requisite必须通过失败后立即终止sufficient通过则整体成功且不再执行后续 auth 模块optional结果可忽略只在没有其他模块提供结果时起作用include / substack引用其他服务的配置substack 会把结果作为独立栈处理很多新手把sufficient当成“可有可无”完全理解反了。实际上pam_permit.so放在sufficient位置时一旦执行就直接放行整个认证流程后面规则全部失效。include与substack的差别在于失败后的控制流include 被引入的规则失败时会影响整体结果substack 被引入的规则像一个独立子栈失败只影响子栈不反向污染外层。4.2 写出一个可复现的 sshd 认证链一个常见的、可落地的 ssh 登录配置如下我加上了注释说明每条规则的作用auth sufficient pam_unix.so nullok try_first_pass auth optional pam_exec.so /usr/local/bin/auth-notify.sh auth required pam_deny.so account required pam_nologin.so account required pam_unix.so session required pam_limits.so session required pam_unix.so逻辑说明先走pam_unix.so做常规密码校验sufficient表示只要本地密码匹配就立刻成功不再往下走nullok允许空密码存在生产环境建议去掉try_first_pass表示如果上层已经拿到了密码这里不再重复提问。pam_exec.so用optional挂一个审计脚本脚本输出只会记日志不影响认证结果。最后一个pam_deny.so是保险如果前面所有 sufficient 都没通过、required 都没成功执行到这里必然拒绝并停止。account 阶段先检查/etc/nologin是否存在存在就拒绝非 root 登录再走pam_unix.so的账户有效性检查。session 阶段加载资源限制模块并完成最终的会话记录。这里给两条实操建议一是pam_nologin.so别放 auth 阶段否则 root 也会被拦住应该放在 account二是pam_limits.so不要用required以外的 control 值resource limit 没设上会静默放行风险极大。4.3 用 pam_listfile 与 pam_time 做登录时间窗和 IP 白名单有时需要限制“只有内网 IP 能登录”“非工作时间禁止访问”不需要改 sshd 配置用 PAM 模块就能实现auth required pam_listfile.so onerrsucceed \ itemuser senseallow file/etc/ssh-allowed-users auth required pam_time.so account required pam_time.sopam_listfile.so的onerrsucceed含义是如果文件不存在或权限异常采取“放行”策略保护性差但不易把自己锁在门外一般调试期用生产建议改成onerrfail。pam_time.so读取/etc/security/time.conf格式是sshd;*;*;!Wd0800-1800它的含义是所有用户*在非周一至周六!Wd的 08:00–18:00 之外禁止登录。注意!是取反标记语法上带感叹号的行表示“不满足该时间窗则拒绝”。这两条规则组合能实现简单的双因子——文件白名单决定谁能登录时间窗决定何时能登录。4.4 日志输出与 debug 配置排查 PAM 问题时先把 syslog 和 journald 的认证日志打开。在/etc/pam.d/sshd里加一行auth optional pam_debug.so源码包里未默认编译需要--enable-debug能看到每个模块返回的 PAM 错误码。同时检查/etc/rsyslog.conf或/etc/systemd/journald.conf是否记录了authpriv.*类别。如果/var/log/secure里只有少量条目而看不到模块级日志大概率是日志级别被过滤了。常见做法是把authpriv.info级别打开并在测试终端上再跑一次journalctl -f -t sshd实时观察。5. 避坑指南PAM 翻车现场与排查工具5.1 root 也登不进去pam_securetty 的锅现象本地 tty 密码能登录但 SSH 方式 root 被拒绝journalctl显示pam_securetty(sshd:auth): access denied。原因/etc/pam.d/login或 sshd 配置中 auth 阶段带上了pam_securetty.so它只允许 root 从/etc/securetty里列出的终端tty1~tty6登录SSH 伪终端不在列表里。解决要么从配置里删掉该行要么在/etc/securetty追加pts/0、pts/1等条目。我建议优先删掉该模块因为现代 Linux 下这个机制已经过时不如改用 sshd 自身的PermitRootLogin yes显示控制。5.2 su 突然要求输入两次密码现象su - root输入密码通过一次后又弹出密码提示输错就认证失败。原因/etc/pam.d/su里存在两个不同的 auth 条目一个来自系统原生配置pam_unix.so另一个来自/etc/pam.d/common-auth的include模块执行链里同时跑了两遍pam_unix.so。第一遍要求密码required第二遍没有try_first_pass参数于是再次询问。解决给多余的pam_unix.so行加上try_first_pass或干脆只保留include common-auth不要在同一 auth 链里重复写本地密码模块。5.3 修改配置后立刻重启测试结果把其他服务一起带崩现象为了验证新配置直接systemctl restart sshd随后发现无法再登录想改回原配置时连 console 都进不去。原因PAM 配置是进程启动时读取并缓存的重启服务后新配置立即生效如果你只留下了pam_deny.so或写错了模块路径认证链路直接断掉。解决在改任何 PAM 配置前先在/etc/pam.d/同级目录做一个备份比如/etc/pam.d/sshd.bak改完用pamtester sshd你的用户名 authenticate模拟测试不要直接重启生产服务。我自己的习惯是在同一个 SSH 会话里先运行ssh localhost验证新配置确认能认证通过后再重启 sshd。5.4 编译第三方模块后报dlopen失败现象自编译pam_example.so放进--with-securedir指定目录运行时报unable to dlopen(/.../pam_example.so): cannot open shared object file。原因模块文件权限不对或依赖的库不在ld.so.cache里常见于sudo chmod 600了.so。解决先chmod 755和ldd检查依赖再ldconfig刷新缓存如果模块链接了不属于标准路径的库用LD_LIBRARY_PATH或把库放进/usr/lib/x86_64-linux-gnu。注意pam.so需要能被 uid 为 root/任意用户执行权限过紧会直接拒载。5.5 两个模块打架导致“永远通过”或“永远拒绝”现象配置写好后pamtester测试任何用户都能通过或者任何用户都被拒绝。原因control 序列里sufficient位置放得太靠前或required与requisite混用后逻辑错乱。比如auth sufficient pam_permit.so放在第一位后面的pam_unix.so永远不执行。解决先在纸上理清楚链条——从首到末哪些模块失败会断链哪些是“双保险”。逻辑复杂度高时拆成两个文件用include引进来一层层测试别把十几行串在单个服务配置里赌启动结果。6. 进阶技巧pam_exec 与 pam_time 做登录策略扩展编译pam_exec.so的意义在于把认证流程延伸到任意外部脚本上。比如我想让每次 SSH 登录失败都记录 IP并触发告警脚本# /etc/pam.d/sshd 中加入 auth optional pam_exec.so /usr/local/bin/failed-login-notify.sh脚本内容核心是传入参数区分阶段——PAM 会把PAM_TYPE环境变量设置为auth、account、session等值脚本读取并判断#!/bin/bash # /usr/local/bin/failed-login-notify.sh if [ $PAM_TYPE auth ] [ -n $PAM_USER ]; then echo $(date) user$PAM_USER rhost${PAM_RHOST:-unknown} /var/log/pam-custom.log fi验证方法仍建议用pamtester在一个未登录的 shell 里执行pamtester sshd 任意用户 authenticate故意输错密码再查看/var/log/pam-custom.log是否追加了新行。注意auth optional的定位是“记录但不阻断”如果你把该模块的 control 改成required脚本返回非零退出码就会中断认证一般不会有人这么干。另一个常用的策略是pam_time按时段控制开发环境。配置/etc/security/time.conf*;*;www-data;!Al0800-1800这条表示用户名匹配www-data可用*代替任意用户的情况下在周一至周六Al代表周一到周六的 08:00–18:00 之外拒绝认证。这个模块的可测性是pamtester sshd www-data authenticate如果当前时刻不在允许窗口内会直接报PAM_TIME_ACCT_EXPIRED。想调试时把系统时间用date -s临时改回允许时段测完再改回来——别在生产机上这么干。整套折腾下来我最大的教训是PAM 不是配完就不动的黑匣子它离内核层只差一步出问题就是登录门锁死。从那以后我每次改/etc/pam.d/下任何文件都强制先跑一遍pamtester或开一个备用 SSH 会话验证可登录再动生产服务journalctl -f -t sshd持续挂着看输出也成了习惯。希望帮到你少走几次我踩过的弯路。本文还有配套的精品资源点击获取