OpenShift ingress 后 Kerberos 认证成功但登录流程未完成,如何排查?
先确认应用登录状态,再检查 ingress
HTTP 200 本身不能证明登录已完成。服务端验证 Kerberos 票据后,应用还需接收认证身份、关联本地用户、执行权限检查,并按自身设计建立会话或返回登录结果。这些步骤中任何一步中断,都可能出现“认证成功,但页面没有继续”的现象。
SPNEGO 最后一轮协商数据可以放在 HTTP 200 的 WWW-Authenticate: Negotiate <token> 中,客户端仍需处理这个 token。只看状态码,可能会漏掉最终协商失败的问题。RFC 4559 第 4、5 节
建议按以下顺序排查:
- 确认这个 200 由哪个组件返回,以及认证成功后是否进入应用安全框架。
- 检查登录状态是否建立,后续请求是否携带并恢复该状态。
- 查看失败是否集中在特定 Pod,比较各副本的 keytab、配置和会话状态。
- 根据请求证据,检查 ingress 的头部处理、路由和限制。
目前的信息还不足以确定根因,也无法判断是否属于 OpenShift 的特定缺陷。
1. 抓取完整请求链,找到流程停止的位置
在浏览器开发者工具的 Network 面板中开启 Preserve log,从访问受保护页面开始,记录完整的登录过程。成功和失败的记录各保留一份,比较同一阶段的请求。
| 检查点 | 要确认的事实 |
|---|---|
| 初始请求 | 未携带凭据时,是否收到 401 和 WWW-Authenticate: Negotiate |
| 协商请求 | 浏览器是否发送 Authorization: Negotiate <token> |
| 协商结果 | 是否仍在进行 token 交换,客户端是否接受最终结果 |
| HTTP 200 | 请求 URL、响应正文是什么,哪个组件产生了响应 |
| 登录后请求 | 是否访问了业务页面或用户接口,是否仍被识别为已登录 |
客户端可能预先发送认证 token,因此没有看到初始 401 不一定是异常。
检查“Kerberos success”响应来自哪里。如果来自独立测试端点,它只能证明该端点能够完成认证,应用登录链路是否接通还需另行确认。
通过 fetch 或 XHR 发起登录时,还要查看前端逻辑。请求成功不会自动触发顶层页面跳转,前端需按接口约定更新用户状态或导航。涉及跨源请求时,还需检查凭据发送和 CORS 配置。
2. 检查认证身份是否进入应用安全框架
先确定由谁验证 SPNEGO:
- 应用自身的认证过滤器或中间件;
- Pod 内的 Web Server 或 sidecar;
- 独立认证代理。
再沿身份传递路径检查日志,确认应用后续代码能否读取认证模块得到的 principal。
由应用直接认证时,检查以下环节:
- 安全上下文是否已建立完成,不能仅凭某次 token 处理成功来判断;
- principal 是否写入应用框架使用的认证上下文;
- 用户映射是否成功,例如
user@REALM与本地账号是否正确对应; - 认证成功处理器是否提前写出“success”并结束请求;
- 后续过滤器、授权逻辑或业务处理器是否执行;
- 需要会话时,认证状态是否持久化,下一次请求能否恢复。
认证成功后是继续执行过滤器链,还是重定向到原始地址,取决于框架和端点设计。应按应用的成功处理流程修正中断位置,无法统一替换成某个调用。
由代理认证时,检查代理如何向应用传递身份。REMOTE_USER 可能只是代理进程内的变量,不会自动成为后端应用可读取的 HTTP 头。身份头名称必须与应用读取的名称一致,应用也只能信任受控代理写入的值。入口应清除客户端伪造的同名头。
如果应用已识别用户,但组查询或角色映射失败,就转查授权逻辑。Kerberos 票据验证成功不会替应用完成角色分配。
3. 根据 TLS 模式检查 ingress 和响应头
先确认实际使用的是 OpenShift Route、由 Ingress 生成的 Route,还是其他 Ingress Controller。不同控制器的配置项不能直接混用。
OpenShift Route 的 TLS 模式决定了 Router 能否处理 HTTP 头:
| TLS 模式 | Router 对 HTTP 的可见性 |
|---|---|
edge | 在 Router 终止 TLS,可以处理 HTTP 头 |
reencrypt | 在 Router 解密,再通过 TLS 连接后端,可以处理 HTTP 头 |
passthrough | 将加密流量转给后端,无法读取其中的 HTTP 头 |
passthrough 模式下,应在实际终止 TLS 的组件检查头部。Router 无法在加密流量中插入会话 Cookie。OpenShift 路由与会话持久性文档
对于能够处理 HTTP 的代理层,比较浏览器侧和认证服务侧的请求,检查:
Authorization是否到达负责认证的组件;WWW-Authenticate是否完整返回,包括 HTTP 200 中可能携带的最终 token;Set-Cookie是否到达浏览器,后续请求是否带回Cookie;Location是否指向正确的外部域名、协议和路径;- 认证路径与业务路径是否路由到预期服务。
如果代理已消费 Authorization,并改用身份头传递用户,就按这一架构检查。此时不应要求所有层继续转发原始 token。
外部使用 HTTPS、后端却将请求识别为 HTTP 时,检查外部 URL 配置,以及应用如何处理 Forwarded、X-Forwarded-Proto 等代理信息。只有受信代理提供的信息才能用于判断原始请求地址。
如果只有部分用户失败,再检查各层的请求头大小限制。AD 组成员较多可能使认证 token 变大,需要结合实际头部长度、代理错误日志和状态码定位。在拿到证据前,不要直接调整全局缓冲区;OpenShift 的相关参数也需按实际版本核对。IngressController 头部缓冲配置说明
4. 检查 Cookie 和多 Pod 状态
使用会话的应用,先在浏览器中检查:
- 浏览器是否接收了会话 Cookie,是否因
Domain、Path、Secure或SameSite条件而阻止它; - 访问认证端点和业务页面时,Cookie 的作用域是否都匹配;
- 后续请求是否使用了新会话,或会话是否不断被重新创建。
再将请求与 Pod 名称关联,查看是否出现以下情况:
Pod A:认证成功,创建本地会话
Pod B:收到下一次请求,找不到该会话
如果会话只保存在 Pod 内存中,就需要采用适合当前部署方式的会话方案,例如共享会话存储。使用客户端会话或签名 token 时,要核对各 Pod 的签名、加密密钥和相关配置。
可以在隔离测试环境中固定到单个 Pod 做对照。如果单 Pod 正常、多 Pod 异常,可将排查范围收窄到副本差异、状态保存或请求分配,再用日志确认。
会话亲和性可以辅助验证问题,但不会同步各副本的状态,也无法解决 Pod 重启后的会话丢失。Kerberos 本身没有统一要求使用 sticky session。只有认证实现保存了本地协商状态,或应用依赖本地会话时,跨副本请求才可能受到影响。
5. 对间歇性异常,比较 SPN、keytab 和运行环境
先核对浏览器实际访问的域名。目标服务 principal 通常形如:
HTTP/app.example.com@EXAMPLE.COM
以客户端实际申请的服务 principal 为准,同时检查别名、DNS 规范化和重定向后的主机名。直接改用 Pod IP 或内部 Service 名称测试,会改变认证条件。
在具备相应工具和权限的 Windows 环境中,可查询 SPN:
setspn -Q HTTP/app.example.com
setspn -X
确认 SPN 归属正确,且没有意外重复。跨域或跨林环境还需明确查询范围。Microsoft setspn 命令说明
在使用 MIT Kerberos 工具的环境中,可检查票据与 keytab:
# 客户端:查看当前票据,并尝试获取目标服务票据
klist
kvno HTTP/app.example.com@EXAMPLE.COM
# 服务端:查看 keytab 中的 principal、KVNO 和加密类型
klist -k -e /path/to/http.keytab
kvno 成功说明客户端取得了服务票据。服务端能否解密、应用是否登录成功,仍需分别验证。MIT kvno 文档
逐个比较实际接收认证请求的 Pod,检查:
- keytab 是否可读,principal、KVNO 和加密类型是否符合预期;
- 服务账号密码轮换后,是否仍有副本使用旧 keytab;
- 镜像、安全配置和 Secret 挂载是否一致;
- 节点时间、DNS 和 KDC 连通性是否正常。
还需从认证库日志确认实际使用的协商机制。仅凭 Negotiate 头部名称无法证明使用的是 Kerberos。如果发生 NTLM 回退,需结合该实现对连接和代理的要求继续排查。RFC 4559
6. 用命令行和关联日志复现问题
在支持 GSS-API 或 SSPI 的 curl 环境中,可以绕开页面脚本,单独验证 HTTP 认证端点。先确认 curl 支持所需能力,再执行测试:
curl --version
# 将域名和路径替换为实际登录入口
curl --negotiate -u : \
--verbose \
--dump-header response-headers.txt \
--cookie-jar cookies.txt \
--output response-body.txt \
'https://app.example.com/protected'
首次测试先不加 -L,以便检查第一处重定向。确认 Location 正确后,再按实际流程携带 Cookie 请求后续地址。curl 的 Negotiate 支持和 -u : 用法见官方文档。如果应用还依赖 JavaScript,curl 只能验证其中的 HTTP 环节。
可用以下命令检查集群资源和指定 Pod 的日志:
oc get route my-route -n my-namespace -o yaml
oc get service my-service -n my-namespace -o yaml
oc get pods -n my-namespace -l app=my-app -o wide
oc logs pod/my-app-pod -n my-namespace \
-c my-container --since=15m --timestamps
Route 配置可能包含证书私钥,分享前必须脱敏。curl 的详细输出、响应头和 Cookie 文件也可能包含认证材料,不应原样贴入公开工单。
建议在应用日志中记录请求关联 ID、Pod、实际认证机制、协商是否完成、用户映射结果、会话创建或恢复结果,以及授权失败原因。会话标识只记录脱敏值,不记录完整 token。
使用 MIT Kerberos 的进程还可临时开启库级跟踪:
KRB5_TRACE=/dev/stderr kvno HTTP/app.example.com@EXAMPLE.COM
并非所有 Kerberos 实现都支持该变量,Java 等运行时需使用对应实现的调试方式。MIT Kerberos 调试说明
根据请求记录和日志,确定失败发生在哪一步:请求未到认证服务、协商未完成、身份未进入应用、会话未恢复,或授权失败。修复后,验证新登录和后续业务请求,并在多副本及 Pod 重建后复测原来的失败路径。
备注:内容仅供参考。
