OpenBMC PAM认证详解:配置、移植与排障实战

发布时间:2026/9/14 17:51:05
OpenBMC PAM认证详解:配置、移植与排障实战 OpenBMC 这块板子第一次上电我最先看的往往不是 IPMI 能不能通也不是 Redfish 返回是否正常而是先敲一条登录命令。登录这关过不了后面全是白搭。而 OpenBMC 的登录认证绕不开一个叫 PAM 的东西——Pluggable Authentication Modules可插拔认证模块。今天这篇就专门把 OB 里的 PAM 从头到尾捋一遍内容包括它在 OpenBMC 里的角色、配置结构、硬件移植时的实操方法以及我这些年踩过的坑和排查习惯希望能帮正在做 OpenBMC 开发、板卡移植或者日常维护 BMC 的同行省点时间。PAM 不是 OpenBMC 独有的东西它本来就是 Linux 的标准认证框架OpenBMC 虽然是一个面向服务器管理的嵌入式操作系统但内核和用户态都继承了 Linux 的这套机制所以 PAM 在里面的地位跟普通发行版一样重要。只不过 OpenBMC 的 rootfs 更加精简很多配置被裁剪、合并或者定制过导致不少人在移植过程中遇到登录失败、Web 认证 401、IPMI 通道验证不过等问题时第一反应是怀疑应用层代码绕了一大圈才发现是 PAM 配置在 rootfs 构建时被动了手脚。这篇文章的重点就是把这些容易踩的地方提前指出来并给出一套可以直接复用的排查和配置方案。1. 为什么在 OpenBMC 里要专门聊 PAM1.1 从 BMC 登录链路说起我们平时登录 BMC 的方式有四种Web 界面、Redfish API、SSH、IPMI。表面上看路径完全不同但底层账号认证最终都要落到操作系统层。OpenBMC 跑的是 Linux 内核和精简的用户态 rootfs登录控制仍然遵守 Linux 的规则。也就是说谁来验证你输入的密码不是应用自己拍脑袋决定的而是交给一个统一框架去处理这个框架就是 PAM。以 Web 登录为例浏览器请求 Redfish 的 Session 创建接口bmcweb 这个 C 守护进程收到请求后会把用户名和密码交给 libpam 库通过pam_authenticate()这个函数走一遍配置好的认证栈然后返回成功或失败。SSH 登录也一样dropbear 或者 openssh 在验证密码时会调用 PAM。IPMI 的 RMCP 通道认证在部分版本里也会通过 PAM 来做二次校验。所以PAM 相当于所有认证请求的汇聚点把它搞明白了BMC 的整个账号体系基本就通了。这里要纠正一个常见误解很多人觉得 BMC 是嵌入式设备账号认证就是简单比对一下/etc/shadow里的哈希没必要用 PAM 这种“重机制”。其实恰恰相反OpenBMC 虽然体积小但它在服务器管理场景下面对的是多协议、多入口、多用户体系这种复杂性决定了认证逻辑必须集中管理。比如一个数据中心里几千台服务器BMC 管理员不可能每台都维护本地密码而是要统一接 LDAP 或者 RADIUS。如果没有 PAM 这种抽象层每个功能模块都要自己对接一次 LDAP那才是灾难。1.2 PAM 接手之后解决了什么如果没有 PAM每个应用都要自己实现一套密码验证逻辑。bmcweb 写一套SSH 写一套IPMI 再写一套大家各自为政想加一个 LDAP 认证就得同时改多个应用测试成本和维护成本都会成倍增加。PAM 的解决办法是把认证逻辑从应用里抽出来放到统一的配置化模块里应用只需要调用 libpam至于具体怎么验证由/etc/pam.d下的配置文件决定。这样一来有几个直接好处。第一认证方式可以热切换想从本地密码换成 LDAP、RADIUS 或者证书只需要改 PAM 配置或者新增模块应用代码完全不用动这在实际运维里非常重要因为你不需要为了换认证方式去升级整个 BMC 固件。第二多个入口共用一套逻辑行为一致Web、SSH、IPMI 对同一个用户返回的认证结果完全相同不会出现 Web 能登录但 IPMI 拒绝这种诡异现象。第三可以在认证栈上串联多个动作比如先验证密码、再检查账号是否锁定、记录登录时间这些都可以由不同模块按顺序完成灵活度非常高。从 OpenBMC 的社区发展也能看到这个趋势。早期版本很多认证逻辑散落在各个守护进程里后来逐渐收敛到 PAM 这一层bmcweb、IPMI 模块、LDAP 支持都往这个方向靠。对硬件移植来说这意味着只要 PAM 这层配置正确所有上层应用的认证行为就都是可控的排查问题也只需要聚焦一个点而不用逐个服务去查代码。1.3 OpenBMC 里的 PAM 和桌面 Linux 的差异OpenBMC 里的 PAM 跟 Ubuntu 这类桌面系统相比差别不小。桌面系统有完整的pam.d配置树包含common-auth、common-account、common-password、common-session每个服务的配置文件也很多。OpenBMC 因为面向专用管理场景配置往往精简得多文件数量少甚至某些服务会直接复用同一个配置。另一个差异是运行环境。OpenBMC 的 rootfs 很多以只读方式挂载PAM 配置文件在运行期不能随意修改。这意味着你想在生产环境临时调一个参数不能像在服务器上那样直接vim /etc/pam.d/common-auth而是要先确认文件系统是否可写或者通过构建系统重新生成镜像。很多刚从服务器运维转到 BMC 开发的同事第一次改配置后重启发现改动丢失还以为是自己没保存其实就是没搞清楚 overlayfs 和只读分区的关系。此外OpenBMC 的很多命令和工具来自 BusyBoxPAM 依赖的系统库也不会像完整发行版那么全。比如pamtester这种辅助工具调试镜像里可能没有需要自己加进去。所以在 OpenBMC 里玩 PAM除了懂机制还得懂构建系统和镜像裁剪这两块是绑在一起的。2. PAM 核心机制拆解从配置文件到认证栈2.1 四个管理组与“栈”的执行顺序PAM 的配置按功能分成四组auth、account、password、session。auth 负责验证身份也就是核对密码、证书、指纹这些凭据account 负责检查账户状态比如是否过期、是否被锁定、是否允许该用户在这个时间登录password 负责更新密码时的策略校验session 负责登录前和登录后的环境准备比如记录日志、挂载家目录、设置环境变量。每个管理组下面可以写多行规则这些规则按顺序执行这就是 PAM 的“栈”。每一行规则的结构是固定的由四段组成模块类型、control flag、模块路径、模块参数。模块类型跟你写在哪个管理组里是对应的比如 auth 组里只能写认证类模块。control flag 是理解 PAM 的关键它决定这行规则的结果如何影响整组认证的最终结果。常用的 control flag 有四种。required 表示这行规则必须通过失败不会立即中断而是等整组规则跑完才返回失败这样设计是为了避免攻击者通过响应时间的差异来判断哪一步失败了。requisite 比 required 更严格失败会当场中断不再执行后面的规则。sufficient 表示一旦通过就直接返回成功后面的规则不再执行。optional 则是可选项它的结果只有在没有其他 required 或 sufficient 规则时才起作用。实际配置里还会见到[success1 defaultignore]这类复杂写法但那属于进阶用法BMC 场景下前四种 flag 已经覆盖绝大多数需求。举个例子检查账号是否锁定并验证密码最简配置如下auth required pam_unix.so nullok_secure account required pam_unix.so这里的 auth 组验证密码account 组检查账户状态。如果我想在密码验证前先做一个来源 IP 的白名单校验就在上面加一行模块。规则按顺序执行前面失败后面就失去意义。理解这四个管理组和 control flag 的组合逻辑是看懂任何 PAM 配置的前提也是排查认证问题的基本功。2.2 OpenBMC 里常见的 PAM 模块盘点OpenBMC 基于 Yocto 构建rootfs 里的 PAM 模块主要来自 linux-pam 包同时也会引入一些定制模块。日常接触最多的有下面这些模块管理组作用注意事项pam_unix.soauth / account / password本地 shadow 密码验证最常用依赖 /etc/shadowpam_securetty.soauth限制 root 只能从安全终端登录BMC 上经常要放行串口和 SSHpam_tally2.soauth / account登录失败计数与锁定锁死会导致多个入口同时失联pam_faillock.soauth / account新版失败锁定与 pam_tally2 类似二选一pam_deny.soauth / account强制拒绝常用于兜底配置pam_permit.soauth / account强制放行慎用等同于关闭认证pam_env.soauth / session设置登录环境变量对 BMC 意义不大pam_lastlog.sosession记录上次登录时间能帮排查谁登录过pam_radius_auth.soauth对接 RADIUS 服务器企业环境常用在这些模块里OpenBMC 定制最典型的是 IPMI 场景。IPMI 的 RMCP 通道需要单独校验用户权限和密码OpenBMC 有专门的模块把 IPMI 请求映射到 PAM 认证上平台侧会做定制。这么做的好处是BMC 的本地账号体系和 IPMI 账号强制保持了一致不会再出现 IPMI 里维护一份密码、Web 里维护另一份密码的荒唐情况。如果启用了 LDAPOpenBMC 会通过 nss-pam-ldapd 或 sssd 把远端用户映射到本地视角所有入口同样走 PAM框架本身保持不变变的只是底层模块。这也是 PAM 设计的核心价值上层不感知变化。2.3 OpenBMC 里 PAM 配置文件的真实布局OpenBMC 的 PAM 配置集中在/etc/pam.d目录。Yocto 的 linux-pam recipe 会装一批默认文件OpenBMC 层的 meta-phosphor 又会在自己的 recipe 里覆盖或追加配置。常见的有 login、passwd、sshd、sudo 等有的版本还有 bmcweb、logind 等自定义服务名。配置文件的组织方式延续了 Debian 的传统一个服务对应一个文件文件内部通过 include 引入公共栈。比如/etc/pam.d/login的内容通常是auth include common-auth account include common-account password include common-password session include common-session真正的认证逻辑写在 common 系列文件里比如 common-auth 中可能有这样一行auth required pam_unix.so try_first_pass nullok_secure用 include 组织的好处是显而易见的想统一加一个 TOTP 验证码或者风险检测模块只要在 common-auth 的栈里插入一行所有依赖 common-auth 的服务全部生效不用逐个文件去改。在 OpenBMC 构建系统里这些文件通常放在recipes-extended/pam/pam.d/目录下由 bitbake 打包进镜像。如果你在做 machine 层移植想针对自己的板卡调整认证策略正确做法是在自己的 layer 里追加pam.d文件而不是修改 meta-phosphor 原始内容否则后续同步上游代码时会冲突。做硬件移植时最容易出的问题就是这个目录被裁剪。有些裁剪脚本看见/etc/pam.d觉得是纯系统文件就顺手删了或者因为 distro feature 里没开 pam整个 linux-pam 包根本没进 rootfs。结果 bmcweb 调用pam_authenticate()时找不到配置直接返回PAM_SERVICE_ERR。很多人应用层查了半天也找不到原因最后才发现配置目录是空的。3. 实操在 OpenBMC 硬件移植中配置与验证 PAM3.1 新板子移植前PAM 依赖怎么确认OpenBMC 硬件移植的核心是 BSP 适配但很多人在 Bringup 阶段就卡在登录上。拿到一块新板子我先不看业务逻辑先确认三件事rootfs 里有没有 libpam 库/etc/pam.d目录下有没有实际的配置文件内核的 tty 和 pty 相关配置是否正常。缺任何一个登录都起不来。第一件是 libpam 库文件它通常位于/lib/aarch64-linux-gnu/或/lib/riscv64-linux-gnu/这类目录取决于你的 CPU 架构。检查方法不复杂ls -l /lib/*/libpam.so*如果没有输出说明 linux-pam 没有进 rootfs。回到 Yocto 构建环境查看 distro feature 里是否包含 pam。OpenBMC 的默认配置通常已经带上但不同的 machine layer 可能使用精简 distro 覆盖了 feature这是移植中常见的隐性坑。如果确实没开可以在 local.conf 里临时追加IMAGE_INSTALL:append libpam做验证等确认无误后再正式改 distro 配置。第二件是/etc/pam.d目录里的内容。把板子启动后列一下ls -l /etc/pam.d/如果目录为空或者只残留几个无关文件建议先把 linux-pam 自带的样本配置恢复出来。同时检查 meta-phosphor 里对 linux-pam 的 bbappend 是否被你的 machine layer 跳过。我记得有一次移植问题出在 layer 优先级上我的 machine layer 把linux-pam_%.bbappend的配置覆盖掉了导致 pam.d 文件没装进镜像花了一个多小时才排查出来。第三件是内核配置。PAM 本身不依赖特别的内核选项但登录链路依赖 tty、pty 和伪终端。串口登录依赖CONFIG_SERIAL_*SSH 登录依赖CONFIG_UNIX98_PTYS和CONFIG_DEVPTS_MULTIPLE_INSTANCES。如果 SSH 一连接就断开先别急着看 PAM查内核这些选项是否正确。很多板卡在默认 defconfig 里没有开全 pty 相关选项导致 SSH 通道建不起来登录自然失败。3.2 一个可落地的认证栈定制方案这里给一套我常用于 OpenBMC 的认证栈配置覆盖本地账号、失败锁定和会话日志三个需求。前提是 rootfs 已经装了 linux-pam并且你确认要修改/etc/pam.d下的 common 系列文件。先看 common-auth加入失败计数模块auth required pam_tally2.so onfail deny5 unlock_time300 auth required pam_unix.so nullok_secure auth optional pam_permit.sodeny5 表示连续 5 次失败就锁定unlock_time300 表示 300 秒后自动解锁。这个配置在 BMC 上比较实用既防暴力尝试又不会因为一次误操作导致永久锁死。后面的auth optional pam_permit.so是为了避免整组规则全失败时出现一些奇怪的边缘情况作为兜底。然后看 common-account追加账户状态检查account required pam_tally2.so account required pam_unix.so再把 session 日志打开session required pam_unix.so session required pam_lastlog.so改完之后在板子上重新发起一次认证请求即可。这里有个好习惯先通过 SSH 发起一次登录或者从 Web 端登一次确认新配置生效。PAM 的配置是每次调用时实时读取的不需要重启 libpam 本身所以只要应用新发起认证请求就会走到新栈。如果修改的是 bmcweb 相关配置重启一下 bmcweb 服务更稳妥systemctl restart bmcweb这里有个细节pam_tally2 的计数文件默认在/var/log/tallylogOpenBMC 的 rootfs 经常把/var挂成 tmpfs重启后计数就清零。如果你希望锁定状态在设备重启后仍然保留需要把 tallylog 放到不会丢的 flash 分区或者在启动脚本里做恢复。BMC 虽然一般不是频繁重启的设备但固件升级往往会触发重启升级后锁定状态丢失这在安全审计里是会被扣分的点值得提前规划。3.3 验证 PAM 生效的三种方法配置改了不等于生效必须验证。我常用的验证方法有三个由浅入深。方法一直接用 pamtester 模拟认证前提是镜像里装了 pamtester。这个方法不依赖任何网络服务能最快定位是不是 PAM 本身的问题pamtester -v login testuser authenticatepamtester 会按 login 服务的配置走一遍 auth 栈分别用正确和错误的密码各试一次观察返回是否符合预期。如果 pamtester 都不过说明问题在 PAM 配置层跟 bmcweb、dropbear 这些应用无关可以直接省掉一大段排查时间。方法二从 Web 端观察。浏览器访问 Redfish 的 Session 服务用 curl 模拟登录请求curl -k -u testuser:password https://bmc-ip/redfish/v1/SessionService/Sessions如果返回 201 Created说明 bmcweb 的 PAM 认证链路是通的。如果返回 401再用方法一的 pamtester 做区分pamtester 成功但 curl 失败问题大概率在 bmcweb 到 libpam 的调用环节比如服务名不匹配、PAM 配置里没有 bmcweb 对应的条目两个都失败问题就在 PAM 配置本身。方法三开启 PAM 调试日志。最简单的是看 syslog 或 journaljournalctl -u bmcweb -f认证失败时bmcweb 通常会有对应的 auth 日志。如果想看 PAM 模块内部的参数解析可以在/etc/pam.d里的模块行加 debug 参数比如pam_unix.so debug然后再次触发认证日志会输出模块判定的明细信息。注意 debug 参数在生产环境别开太久调试完记得去掉否则日志会刷得很快而且会把敏感信息打出来。4. 常见问题与排查技巧实录4.1 密码正确却永远登录失败这个现象在 OpenBMC 移植后特别常见。Web 和 SSH 都报密码错误但用串口 root 登录却能进去。遇到这种情况第一步别怀疑密码先看 PAM 配置里有没有 pam_securetty。pam_securetty 会检查/etc/securetty中允许 root 登录的终端列表BMC 的伪终端大多不在列表里表现为所有非串口入口全部拒绝 root 登录。处理方法有两个要么删除这段配置要么把pts/0、pts/1这类伪终端加进 securetty。对 BMC 这种管理设备来说我更推荐直接去掉 pam_securetty因为它的保护意义在带外管理场景下很有限带来的麻烦却不小。另一个隐蔽原因是 NSS 配置。PAM 的 pam_unix 模块在验证密码前需要通过 getpwnam 获取用户信息这一步会读取/etc/nsswitch.conf。如果 passwd 那行把 files 排在了 ldap 后面而 LDAP 服务器恰好超时整个查询会被卡住表现为认证过程特别慢然后失败。排查方法很简单在板子上执行getent passwd testuser如果这条命令本身就卡住说明问题在 NSS 层不要继续在 PAM 配置里折腾。把 nsswitch.conf 里的顺序调整为 files 优先或者优化 LDAP 超时参数问题自然就解了。这个坑在很多调试场景里会被误判为 PAM 配置错误实际上 PAM 只是“受害者”。4.2 账号锁定导致的“连坐”事故pam_tally2 的锁是针对用户名的不是针对来源 IP 的。一个用户在同一时间用 Web、SSH、IPMI 连续输错几次密码会把失败计数飞快刷满接下来所有入口全部拒绝这个用户登录。BMC 上的超级用户往往只有一个 root一旦 root 被锁板卡管理直接瘫痪只能去串口用物理访问清 tallylog这在机房场景下是很痛苦的。所以我建议在 BMC 上用 deny5 甚至更大的值并且一定要设置 unlock_time不要用 unlock_time0 这种永久锁定。同时关于 root 是否参与锁定要特别注意模块参数。pam_tally2 的 even_deny_root 参数加上后 root 也参与锁定不加则默认 root 豁免。在 BMC 上我强烈建议让 root 豁免毕竟这是最后的管理通道锁死代价太高。如果你实在想保护 root可以考虑配合 fail2ban 这类网络层防护而不是简单地在 PAM 层把 root 锁死。还有个运维细节如果你确实需要手动清除某个用户的锁定计数可以登录串口执行pam_tally2 --userroot --reset这个命令在带外管理事故恢复时非常有用建议写进自己的排障手册。如果 root 已经被锁串口通常还能登录因为很多平台对物理串口有单独的认证路径未必走同一个 tally 计数。4.3 PAM 调试里的独门经验最后分享几条硬经验。第一PAM 模块加载失败时应用不会崩溃只会在 syslog 里留一行 cannot open shared object file。看到这类日志第一反应是模块路径不对或者模块包没装而不是配置逻辑错误。在 OpenBMC 交叉编译环境下模块路径经常因为 sysroot 不同而缺库此时用 ldd 检查 PAM 模块的依赖最靠谱ldd /lib/aarch64-linux-gnu/security/pam_unix.so第二多个入口同时出问题优先查 common 系列配置只有单个入口出问题优先查该服务自己的配置文件。这个二分法能帮你快速缩小范围。有一次 SSH 正常但 Web 登录失败最后发现是 bmcweb 用了独立的服务名而 pam.d 里没有创建对应的条目libpam 回落到 other 策略other 里配置的又是 deny所以所有 Web 认证都被拒绝。这种问题看配置一眼就能发现但不知道这个排查逻辑的人往往会去翻 bmcweb 源码。第三做一个 PAM 改动前备份原文件永远是王道。BMC 的 rootfs 很多是只读挂载改配置前先确认能否 remountmount -o remount,rw /有时候你辛苦改完发现重启后被 rootfs 覆盖那是 overlayfs 或只读分区没正确配置不是 PAM 的问题。早点确认文件系统的读写状态能避免很多无用功。再补充一个容易忽略的点OpenBMC 的调试镜像和发布镜像在认证相关组件上可能差异很大。调试镜像里带了调试符号、额外工具PAM 配置也可能被放宽发布镜像则会裁剪掉这些内容。如果你在调试镜像上验证通过发布镜像却认证失败先检查两个镜像里/etc/pam.d的差异。我遇到过几次生产环境无法登录最后都是因为发布镜像裁剪掉了 pam_tally2 模块而配置里还引用着它导致认证栈加载失败。5. 从 PAM 视角看 OpenBMC 认证体系的设计取舍5.1 为什么 OpenBMC 坚持用 PAM 而不是自研认证有些做嵌入式开发的人会问OpenBMC 作为专用系统为什么不干脆自己写一套轻量认证逻辑反而要引入 PAM 这种“重框架”我的理解是OpenBMC 的定位决定它不能为了省资源而牺牲兼容性和安全性。服务器管理领域有大量现成的安全基础设施比如 LDAP、RADIUS、TOTP、证书认证这些都是企业运维的标配。PAM 作为一个被 Linux 生态验证了几十年的框架天然对接这些设施与其重复造轮子不如把精力放在管理功能上。另一个现实原因是OpenBMC 的开发者来自不同公司大家维护着各自的平台层。如果认证逻辑散落在每个平台的 bmcweb 分支里上游合并时会产生大量冲突。而把认证统一收敛到 PAM 配置层后平台差异被大大压缩合代码时的摩擦也小很多。这也是为什么 OpenBMC 社区在推进功能时倾向于在 PAM 模块层面扩展而不是在各个应用里加认证逻辑。5.2 安全加固时PAM 能做什么和不能做什么PAM 能做的事集中在认证栈内密码策略、失败锁定、账号状态检查、会话日志、多因素认证扩展。在 OpenBMC 安全加固时我通常把 PAM 配置当作第一道防线确保本地账号和远程账号都经过相同的校验。比如通过 common-auth 统一加入 pam_pwquality 做密码强度检查或者通过 pam_faillock 做统一失败计数这些都可以在不动应用代码的前提下完成。但 PAM 也有边界。它管不了网络层攻击不能防 IP 扫描也不能替代 TLS 加密。有些安全需求需要在更上层实现比如在 bmcweb 里限制会话并发数、在 IPMI 层限制来源网段等。所以正确的心态是把 PAM 当作认证体系里负责“身份验证和账户状态”的那一层而不是包治百病的万能药。在规划 OpenBMC 安全方案时我会先画一张链路图标清哪些点在 PAM 层解决哪些点在网络层或应用层解决避免把所有希望压在 PAM 上。我个人在实际操作中最深的体会是PAM 在 OpenBMC 里是个“平时看不见、一坏全瘫痪”的组件。移植一块新板卡时与其在业务代码里找认证问题不如先把 PAM 这一层打通。用 pamtester 做冒烟测试、用 journalctl 看实时日志养成这两个习惯能少熬好几个通宵。最后再分享一个小技巧在 OpenBMC 的调试镜像里把/lib/*/security目录下的模块列出来跟配置文件里引用的模块做一次 diff几乎所有认证相关的问题都能在这份 diff 里找到线索。先确保模块存在再讨论配置逻辑这个顺序能帮你省掉大量无效排查时间。