Ranger Admin 对接 FreeIPA 登录失败:Bad credentials 排查实践
你是不是也遇到过这个场景Ranger Admin 页面填上管理员账号点登录界面冷冷地弹出一句Bad credentials然后日志里翻到一行javax.naming.AuthenticationException: [LDAP: error code 49 - Invalid Credentials]。我在多个项目里接过 Ranger 对接 FreeIPA 这类认证源的任务第一次看到这个报错时第一反应也是“密码是不是打错了”。但后来实测下来真正密码错的只占很少一部分大部分是绑定账号的 DN 构造不对、加密连接配置没跟上、用户被 FreeIPA 锁了或者是 Spring Security 的认证链里某些参数没有对上 FreeIPA 的目录结构。这篇文章就把我实际排查Bad credentials的完整路径写出来尤其围绕 FreeIPA 这个特殊目录服务给后面接手的同学省点时间。1. 先搞清楚 Bad credentials 是哪一层抛出来的1.1 登录按钮背后到底跑了几步Ranger Admin 的 Web UI 本身是一个 Java Web 应用用户认证这块默认走的是 Spring Security 那一套。你在配置文件里把ranger.authentication.method设成LDAP之后登录请求并不会直接拿着用户名和密码去问 FreeIPA而是先经过 Spring Security 的认证管理器再交给 LDAP 认证 Provider。这个 Provider 内部用 JNDI 去连 LDAP尝试绑定。整个链路大致是用户在登录页输入用户名和密码。Ranger Admin 根据配置构造一个用户名映射用绑定账号bind DN去 LDAP 服务器搜索用户的完整 DN。拿到完整 DN 之后再用这个 DN 加上用户自己输入的密码发起一次新的 LDAP Bind 请求。FreeIPA 侧的 389 Directory Server 根据 Bind 结果返回成功或失败。失败时 Spring Security 把底层的AuthenticationException包装成BadCredentialsExceptionUI 上统一显示Bad credentials。也就是说你在页面上看到的Bad credentials只是一个笼统的业务提示真正的技术原因在 Ranger Admin 的日志里。如果不先把日志打开上来就改密码基本是白忙活。1.2 第一步永远是翻日志不是改配置Ranger Admin 的日志默认在/var/log/ranger/目录下文件名一般是ranger-admin-YYYY-MM-DD.log。我建议排查时先这样过滤grep -E AuthenticationException|Invalid Credentials|error code 49|bad credentials /var/log/ranger/ranger-admin-*.log | tail -n 50实际输出的关键字大概是这样javax.naming.AuthenticationException: [LDAP: error code 49 - Invalid Credentials] at com.sun.jndi.ldap.LdapCtx.mapErrorCode(LdapCtx.java:1919) ... Root DN being authenticated: uidadmin,cnusers,cnaccounts,dcexample,dctest注意看最后那一行Root DN being authenticated会直接告诉你当时到底用哪个 DN 在做 Bind。如果这个 DN 都不对那下面的结果一定就是error code 49。这里还要强调一个习惯日志里出现error code 49时专门去区分下面三种常见 LDAP 错误码。LDAP 错误码含义常见原因49Invalid CredentialsBind DN 不存在、密码错误、用户被锁定、密码策略拦截32No Such Object搜索的 base DN 写错或者用户 DN 拼写错误53Unwilling To Perform服务端策略不允许这种操作比如禁止匿名绑定或明文绑定很多时候界面只显示Bad credentials日志里其实是error code 32那问题本质是 DN 写错了不是密码问题。所以先看日志比什么都强。1.3 用 ldapwhoami 手工复现一次 Bind在动 Ranger 配置之前我建议你先在 Ranger Admin 所在的服务器上用ldapwhoami主动做一次 LDAP Bind把这个错误还原出来。这样能把 Ranger 这个中间层剥掉直接看 FreeIPA 的反应。ldapwhoami -x -H ldap://ipa.example.test:389 \ -D uidadmin,cnusers,cnaccounts,dcexample,dctest \ -w 你的密码如果 Bind 成功输出会是dn: uidadmin,cnusers,cnaccounts,dcexample,dctest如果失败你会直接看到ldap_bind: Invalid credentials (49)这一步能非常清晰地把问题定位到“FreeIPA 拒绝这个 Bind”。接下来再一步一步排查为什么拒绝。2. FreeIPA 这个认证源有哪些“反直觉”的地方2.1 DN 命名空间别用 cnadminDirectory Manager 不存在很多同学对 OpenLDAP、AD 比较熟来到 FreeIPA 之后第一反应是拿管理员 DN 去配。于是配置文件里出现这样的绑定账号ranger.ldap.bind.dncnadmin,dcexample,dctest这在 FreeIPA 里是查无此人的。FreeIPA 的所有用户都在cnusers,cnaccounts子树下面管理员账号是普通用户DN 是uidadmin,cnusers,cnaccounts,dcexample,dctest还有一个更隐蔽的坑传统 389 Directory Server 有一个cnDirectory Manager的最高管理账号可以不受 ACL 限制地操作整个目录。但 FreeIPA 出于安全考虑把这个超级管理员账号给移除掉了你配cnDirectory Manager也是会报 49 或者 32。这个点我看过不少人踩包括我自己第一次接 FreeIPA 的时候也下意识试了一下。这里顺带提一句服务账号的事。如果你不想让 Ranger 用admin这种超管账号做绑定正确做法是先到 FreeIPA 里创建一个专门的服务用户然后让它有权限去cnusers,cnaccounts搜索用户和组。比如uidranger-svc,cnsysaccounts,cnetc,dcexample,dctest注意cnsysaccounts,cnetc这个子树是 FreeIPA 用来放服务账号的也是一些项目里常见的配置写法。如果你的环境里没有这个用户那绑定会失败排查方向就从密码问题转到了账号创建问题。2.2 用户状态和密码策略会伪装成 Bad credentialsFreeIPA 底层是 389 DS它的密码策略和账号状态管理做得比普通 OpenLDAP 严。最容易让 Ranger 报Bad credentials的有两类一是用户被锁定。管理员执行了ipa user-disable user1之后FreeIPA 会在用户条目上标记nsAccountLock: true。这种情况下用户密码并没有变但 LDAP Bind 就是会失败而且错误码同样是 49故意不告诉你真实原因避免账号枚举。遇到这种情况需要用管理员身份去查一下用户状态ipa user-show user1如果输出里看到Account disabled: True就说明是这个用户被锁了不是密码问题。二是密码刚被重置且强制要求修改。FreeIPA 管理员用ipa passwd重置用户密码后通常会设置一个必须改密的状态。此时用户拿着这个临时密码去 LDAP Bind很可能会被策略拦下来。真实环境里我遇到过 Ranger Admin 登录一直 49但kinit能成功后来发现就是用户在下一次登录时被要求强制改密改完就好了。这种问题在日志里往往看不出太多信息因为它和“密码错误”返回的是同一种错误码。所以只要出现 49我倾向于先做一个动作拿这个账号尝试登录 FreeIPA Web UI 或者执行一次ipa user-auth类操作看看 FreeIPA 自己给不给过。如果 FreeIPA 自己也过不去那问题大概率在 FreeIPA 这一侧而不是 Ranger。2.3 加密连接与证书问题容易被扣上“认证失败”的帽子FreeIPA 的 LDAP 服务在 389 端口默认允许简单绑定但从生产环境的安全角度一般要求走 LDAPS 或者至少开启 StartTLS。如果你在 Ranger 里配的是明文ldap://ipa.example.test:389而 FreeIPA 侧已经做了加密策略限制那服务端会直接拒绝这个 Bind返回的错误可能表现为 49也可能是连接被重置。另外常见的是 LDAPS 证书问题你配的是ldaps://ipa.example.test:636但 Ranger Admin 的 JVM 不信任 FreeIPA 自签发的 CA 证书于是在 JNDI 握手阶段就会抛SSLHandshakeException。虽然这个异常和Bad credentials不完全一样但有些现场会同时出现多条异常日志让人误以为还是认证问题。判断方法很简单先用openssl s_client检查 636 端口证书链再用 ldapsearch 走 LDAPS 试一次绑定如果命令行下能通Ranger 不通那问题基本就在 JVM 证书库。3. Ranger 侧配置怎么配合 FreeIPA 的目录结构3.1 一组可以落地的配置项Ranger 对接 FreeIPA跟对接 AD 或者 OpenLDAP 不太一样。FreeIPA 的用户搜索库、组对象类型都有自己的一套。我这里给一个生产环境里用过的配置片段字段位置一般在ranger-admin-site.xml或者通过 Ambari / 自建脚本维护的ranger-admin-site.xml里property nameranger.authentication.method/name valueLDAP/value /property property nameranger.ldap.url/name valueldaps://ipa.example.test:636/value /property property nameranger.ldap.bind.dn/name valueuidranger-svc,cnsysaccounts,cnetc,dcexample,dctest/value /property property nameranger.ldap.bind.password/name valueXXXXXXXXXXXXXXXX/value /property property nameranger.ldap.base.dn/name valuedcexample,dctest/value /property property nameranger.ldap.user.searchbase/name valuecnusers,cnaccounts,dcexample,dctest/value /property property nameranger.ldap.user.searchfilter/name value(uid{0})/value /property property nameranger.ldap.group.searchbase/name valuecngroups,cnaccounts,dcexample,dctest/value /property property nameranger.ldap.group.searchfilter/name value(objectclassipausergroup)/value /property property nameranger.ldap.user.roleattribute/name valuecn/value /property这里有个容易被忽略的点ranger.ldap.url里配了ldaps://那 JVM 就必须信任 FreeIPA 的 CA 证书。验证方法keytool -list -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit | grep ipa如果没有条目需要把 FreeIPA 的 CA 证书导入keytool -importcert -alias ipaca \ -file /etc/ipa/ca.crt \ -keystore $JAVA_HOME/lib/security/cacerts \ -storepass changeit -noprompt3.2 搜索是“先绑定账号查再用用户 DN 绑定”搞清楚 Ranger 的搜索行为才能看懂为什么bind.dn配置错了会导致登录失败。典型的 LDAP 认证流程不是直接用用户名去搜索而是Ranger 先使用ranger.ldap.bind.dn和ranger.ldap.bind.password在 FreeIPA 上发起一次 Bind。如果这个服务账号 Bind 成功Ranger 以这个身份去user.searchbase里执行searchfilter找到目标用户对应的完整 DN。找到 DN 之后Ranger 用这个 DN 加上用户在页面输入的密码做第二次 Bind。第二次 Bind 成功了登录才算通过。所以你可以想象如果第 1 步服务账号都绑不上去后续步骤全是白搭最终表现就是页面报Bad credentials。我见过一个项目ranger.ldap.bind.dn配成了普通用户的 DN 而密码却填错了结果怎么登都失败后来换成服务账号立刻就好了。另一个常见问题是searchfilter的占位符。FreeIPA 的用户登录名在 LDAP 里叫uid不叫sAMAccountName更不叫cn。如果你的过滤器是(cn{0})用户填写的用户名是adminFreeIPA 里 admin 的cn可能并不是 admin这一步就搜不到用户后续绑定也就无从谈起。对 FreeIPA最稳的过滤器就是(uid{0})。如果里还需要支持用户用自己的邮箱登录FreeIPA 里也不一定每条用户都写了 mail所以在不确定的情况下不要贪多先用 uid 跑通再扩展。3.3 登录成功但组权限不对问题在 group 搜索配置还有一种情况不算Bad credentials但属于同一类认证链路问题用户能登录成功但登录之后在 Ranger Admin 里看不到任何组导致后续鉴权都空空的。这个问题的根源通常不是绑定而是 group 搜索没对上 FreeIPA 的组对象类型。FreeIPA 的组对象用的是ipausergroup这个对象类而 OpenLDAP 常见的groupOfNames在 FreeIPA 里不一定是默认类型。你在配置里如果沿用老的过滤器比如ranger.ldap.group.searchfilter(objectclassgroupOfNames)那么在 FreeIPA 上很可能搜不到任何组。Ranger 拿到的用户组列表为空看起来像是“登录成功了但无权访问任何资源”。解决办法就是把 group 搜索过滤器和搜索基准都改成 FreeIPA 的写法ranger.ldap.group.searchbasecngroups,cnaccounts,dcexample,dctest ranger.ldap.group.searchfilter(objectclassipausergroup)另外要留意用户条目上的组属性。FreeIPA 会在用户条目上维护memberOf属性但它不是像 AD 那样一个扁平的多值属性而是 FreeIPA 自动维护的内部派生属性。Ranger 这边如果想要拿到用户的组归属需要正确配置ranger.ldap.user.memberofattributememberof。如果这个属性名给错了用户就算属于某个组Ranger 也感知不到。4. Bad credentials 问题速查表与典型场景复盘这部分我把这些年碰到过的典型情况汇总成一张表按“症状 - 原因 - 验证方法 - 解决动作”来组织。你可以直接把它抄进自己的排障手册里。症状可能原因验证方法解决动作所有账号登录都报 Bad credentials绑定账号bind DN不存在或密码错误ldapwhoami -x -D ... -w ...手动测一下把 bind DN 改成 FreeIPA 存在的服务账号admin 账号登录报 49使用了cnadmin这种 DNipa user-show admin看正确 DN改成uidadmin,cnusers,cnaccounts,dc...日志里有 error code 32搜索基准或 DN 拼写错误用ldapsearch -b检查 base DN 是否存在修改 searchbase 或 bind DN 的子树路径用户被禁用后登录失败FreeIPA 账号锁定ipa user-show user看 Account disabled联系管理员ipa user-enable user密码刚被重置仍报 49用户强制改密状态尝试 kinit看是否提示密码修改用户先改一次密码LDAPS 连接握手失败JVM 不信任 FreeIPA 的 CA 证书openssl s_client -connect ...:636检查证书链导入 FreeIPA CA 到 Ranger 所在 JVM 的 cacerts登录成功但没有组group searchfilter 用了错误的对象类ldapsearch -b cngroups,... (objectclassipausergroup)改成(objectclassipausergroup)用户组列表为空但组搜索没问题用户条目的 memberOf 属性没被 Ranger 识别查看用户条目里memberOf字段配置ranger.ldap.user.memberofattributememberof仅特定用户失败其他人正常该用户密码错、被锁或用户不存在于搜索基准ldapwhoami 单测该用户按 FreeIPA 用户状态排查页面一直转圈最后超时网络不通或 LDAP 地址不可达telnet ipa.example.test 389检查防火墙和主机名解析这里有一个值得单独拿出来说的场景用管理员账号登录 Ranger Admin。在 FreeIPA 里admin用户本身就在cnusers,cnaccounts搜索树里所以只要 Ranger 的搜索过滤器是(uid{0})你是可以用admin登录的。但它能不能在 Ranger 里看到所有资源取决于它的组成员关系。FreeIPA 里默认的管理员组叫adminsadmin用户在这个组里。如果你在 Ranger 侧没有把这个组映射成超级管理员组或者授予对应角色那么即使登录成功也可能只有普通用户权限。这个问题经常和Bad credentials一起被误判成认证失败实际上认证已经通过了是授权映射不到位。5. 跑通之后的验证脚本与后续维护建议当你按上面的思路排查到所有配置项看起来都对了我建议写一个小脚本把要验证的账号和密码都测一遍再让 Ranger 去重试登录。这样能避免每次都在 Web UI 上反复输入账号效率太低。下面这个脚本基于 ldapsearch / ldapwhoami可以在 Ranger 节点上直接执行#!/bin/bash # usage: ./check_ipa_auth.sh username password IPA_HOSTipa.example.test BASE_DNdcexample,dctest USER_SEARCHcnusers,cnaccounts,${BASE_DN} USERNAME$1 PASSWORD$2 echo [1/3] 使用绑定账号测试服务账号 Bind... BIND_DNuidranger-svc,cnsysaccounts,cnetc,${BASE_DN} BIND_PASSyour_service_password ldapsearch -x -H ldap://${IPA_HOST}:389 \ -D ${BIND_DN} -w ${BIND_PASS} \ -b ${USER_SEARCH} \ (uid${USERNAME}) uid memberOf /tmp/user_info.txt 21 if [ $? -ne 0 ]; then echo 绑定账号搜索失败请检查 bind DN 和密码 cat /tmp/user_info.txt exit 1 fi echo [2/3] 获取用户完整 DN... USER_DN$(grep ^dn: /tmp/user_info.txt | awk {print $2}) echo 用户 DN: ${USER_DN} if [ -z ${USER_DN} ]; then echo 未找到用户 ${USERNAME}检查 searchbase 或用户是否存在 exit 1 fi echo [3/3] 用用户 DN 直接测试密码绑定... ldapwhoami -x -H ldap://${IPA_HOST}:389 \ -D ${USER_DN} -w ${PASSWORD} if [ $? -eq 0 ]; then echo 认证成功Ranger 侧相同的配置应该可以登录 else echo 认证失败请检查密码、账号状态和密码策略 fi这个脚本的逻辑就是前面人工操作的自动化版本。跑一遍如果功Ranger 还在报错那问题几乎可以锁定在 Ranger 应用本身的连接配置或 JVM 证书上而不是 FreeIPA 本身。最后再分享两个日常维护的小建议。第一不要用admin账号当 Ranger 的服务绑定账号。给 Ranger 单独创建一个最小权限的服务账号并且定期轮换密码。这样即使 Ranger 的配置文件泄漏了也不至于把整个 FreeIPA 域的管理账号暴露出去。创建服务账号后还要确保它能读取用户和组子树这一步可以通过 FreeIPA 的权限委派实现创建完账号之后用ipa permission-add/ipa privilege-add给最小读权限。第二时间同步一定要关注。FreeIPA 的 LDAP 简单绑定本身不依赖 Kerberos但对依赖 Kerberos 的功能、以及后续 Ranger 如果要接 FreeIPA 的 Kerberos 票据认证时间偏移超过 5 分钟就会遇到各种无法归因的认证失败。我见过一个现场Ranger 一直报密码错误后来发现是 Ranger 节点和 FreeIPA 时间差了十几分钟改完 NTP 同步后立马恢复。回到最初的问题Bad credentials在 FreeIPA 场景下从来不是单纯的“密码不对”。它可能是服务账号的问题可能是加密链路的问题可能是用户状态的问题也可能是 Ranger 里搜索路径和属性名没有跟 FreeIPA 的目录结构对齐。按照日志定位 - 手工复现 - 逐层替换变量这条路走下来基本都能在半小时内找到根因。