ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Ambari Kerberos下Trino Web UI接入Knox双认证实践

Ambari Kerberos下Trino Web UI接入Knox双认证实践 团队把 Ambari 集群的 Kerberos 开关打开之后老 HDP 栈常见的那套问题一个接一个冒出来。其中最让我头疼的不是 HDFS也不是 Hive反而是看起来最不起眼的 Trino Web UI。Trino 的 CLI 可以通过 Kerberos 正常连接但浏览器访问 Web UI 要么一直 401要么干脆空白。后来把 Knox 加进来做统一入口才算把这条链路理顺。这篇文章就是把当时踩过的坑、试过的方案、最后落地的配置完整记录下来给同样在 Ambari 开启 Kerberos 之后需要把 Trino Web UI 交给 Knox 的团队一个可复现的参照。1. 先说清楚Trino Web UI 在 Kerberos 集群里到底卡在哪1.1 Trino 的 HTTP 服务认证逻辑Trino 的 Web UI 本质上不是独立应用而是 Trino HTTP Server 上的一组静态资源加 REST API。默认监听 8080 端口/ui/路径返回前端页面/v1/statement、/v1/info这些路径则提供服务状态和查询接口。集群没有开 Kerberos 的时候这个 HTTP Server 默认不认证任何人只要能访问 8080 端口就能打开查询页面。Ambari 开启 Kerberos 之后Trino 如果配置了http-server.authentication.typeKERBEROS那么 HTTP Server 会启用 SPNEGO 认证。服务端以自己的 HTTP principal 作为身份通常是HTTP/trino-host.example.comEXAMPLE.COM所有浏览器请求必须携带协商好的 Kerberos 票据否则返回401 Unauthorized。这个机制本身没有问题但对使用者极不友好。浏览器需要额外配置auth_negotiate白名单主机名必须和 principal 完全匹配静态资源请求也会触发 SPNEGO经常一个页面加载出一堆 401 记录。Trino CLI 和 JDBC 客户端走的是标准 Kerberos 认证流程配置好 keytab 和 krb5.conf 之后通常一次成功但 Web UI 的用户往往不是愿意折腾浏览器底层配置的人。1.2 为什么You have to have a ticket这条路走不通我在现场排障时遇到过最典型的场景DBA 把 Kerberos 票据申请好后用浏览器打开http://trino-host:8080/ui/Chrome 直接弹一个登录框输入域账号密码之后还是 401。原因往往不是密码错误而是浏览器根本不认识 SPNEGO 协商流程。Trino 的 Kerberos 认证走的是 HTTP Negotiate 协议。服务端返回WWW-Authenticate: Negotiate浏览器需要用本地的 Kerberos ticket 构造 SPNEGO token 回传。这个过程中如果浏览器所在机器没有加入域或者没有配置intranet站点的协商白名单或者访问的地址和 Trino 的 service principal 中注册的 hostname 不一致就会失败。对于只在内网用网页查询数据的人这个门槛太高。1.3 Knox 在中间扮演的角色Knox 解决的核心问题是统一入口。用户不需要直接面对 Trino 的 8080也不需要在浏览器上配 Kerberos只需要访问https://knox-host:8443/gateway/default/trino-ui/ui/Knox 替用户完成认证再向后端 Trino 发起请求。这里的难点在于 Knox 不是透明代理它有自己的认证、rewrite、header 处理逻辑需要明确告诉它后端是什么路径怎么映射认证用什么方式。这也是很多人配完之后一脸懵的原因。2. 方案取舍三种接入方式的对比与选择2.1 直连 Trino 浏览器 SPNEGO最朴素的方式用户直接访问 Trino 8080 端口浏览器走 Kerberos 协商。优点是少一层网关不需要 Knox。缺点是整个浏览器访问链路的体验非常差而且如果 Trino 被部署在边缘节点8080 端口直接暴露在办公网有安全风险。在 Kerberos 开启的集群里直连还要求每个访问用户的浏览器环境都正确配置运维谈判成本极高。2.2 Knox Trino 纯 Kerberos 认证理论上最正统的方案用户访问 KnoxKnox 认证通过后以 Kerberos 客户端身份向 Trino 发起 SPNEGO 协商。但这个方案在实操层面最麻烦。Knox 虽然支持 Kerberos 相关的认证 provider但要让它自动为每个后端连接维护 SPNEGO token、处理协商循环配置复杂度很高且不同版本的 Knox 支持情况差异很大。我曾经试着在 Knox 的 topology 里加 SPNEGO 客户端配置最后因为 Knox 自带的服务定义里没有一个能干净地处理 Negotiate 过程而放弃了。2.3 Knox Trino 双认证KERBEROS PASSWORD最终我采用的是这个方案。Trino 端开启KERBEROS,PASSWORD双认证CLI、JDBC 这些服务间调用继续走 KerberosWeb UI 的 HTTP 请求则通过 Knox 转发时携带 Basic Auth 凭据使用 Trino 的 Password Authenticator 完成认证。这个方案的优点在于不破坏已有 Kerberos 安全链路。用户只感知 Knox 的统一认证入口。Knox 到 Trino 之间的认证机制简单可控排障难度低。安全上我做了限制Trino 的 8080 端口只对 Knox 节点和运维网段开放办公网用户只能访问 Knox 的 8443。如果你们安全要求很高还可以在这个基础上做 IP 白名单或者给 Knox 单独分配一个服务账号不让它代替用户传递密码。三种方式对比如下方案用户操作复杂度配置复杂度后端暴露风险推荐程度直连 Trino SPNEGO高依赖浏览器配置低高不推荐Knox 纯 Kerberos低很高依赖版本支持较低看情况Knox 双认证低中低可加白名单推荐3. 环境准备Principal、Keytab、密码文件一个都不能少3.1 KDC 上为 Trino 准备 principal 和 keytab在 Ambari 开启 Kerberos 的环境里KDC 通常已经存在我这边是 MIT KDC。要做的第一步是给 Trino 的 HTTP Server 创建 principal。使用HTTP作为 service name因为 Trino 默认http-server.authentication.krb5.service-nameHTTP。假设 Trino 安装在trino-host.example.comrealm 是EXAMPLE.COM命令如下sudo kadmin.local -q addprinc -randkey HTTP/trino-host.example.comEXAMPLE.COM sudo kadmin.local -q ktadd -k /tmp/trino.keytab HTTP/trino-host.example.comEXAMPLE.COM然后把 keytab 拷到 Trino 节点上并设置正确的属主和权限sudo scp /tmp/trino.keytab trino-host:/etc/trino/conf/ sudo chown trino:hadoop /etc/trino/conf/trino.keytab sudo chmod 400 /etc/trino/conf/trino.keytab这里有个细节Trino 的 HTTP principal 的 hostname 必须和浏览器访问的 hostname 一致。如果你希望通过 Knox 转发其实 Trino 不需要对外开放但 principal 里的 hostname 仍然要是本机实际解析名否则 Trino 启动时拉取 keytab 会报 principal 不匹配。3.2 Trino 端开启双认证编辑 Trino 的config.properties一般在/etc/trino/conf/config.properties。关键配置如下http-server.authentication.typeKERBEROS,PASSWORD http-server.authentication.krb5.service-nameHTTP http-server.authentication.krb5.keytab/etc/trino/conf/trino.keytab http-server.authentication.krb5.principalHTTP/trino-host.example.comEXAMPLE.COM password-authenticator.namefile file.password-file/etc/trino/password.dbhttp-server.authentication.type同时保留KERBEROS和PASSWORD这样 Trino 对同一端口既接受 Kerberos 协商也接受 HTTP Basic 认证。Trino 在检测到请求头里的 Authorization 类型后会自动进入不同认证流程不需要额外配置两个端口。生成密码文件可以使用 htpasswdTrino 的 file password authenticator 要求 bcrypt 格式sudo htpasswd -B -C 10 -c /etc/trino/password.db trino_service这里我建了一个专用服务账号trino_service后面 Knox 转发时统一用它。注意-B参数指定 bcrypt-C 10指定 cost 值Trino 对这个格式支持得很稳定。如果你用的是 Ambari 管理的 Trino 服务也可以在 Ambari UI 的 Trino Configs 里找自定义trino-config项把上面的属性加进去。Ambari 保存后会重新生成配置并触发滚动重启。如果 Trino 是纯手工部署直接改文件后重启 Trino 服务即可。3.3 Knox 侧准备事项在动手改拓扑之前先确认三件事一是 Knox 服务本身的运行状态。HDP 环境下可以执行/usr/hdp/current/knox-server/bin/knoxcli.sh status二是 Knox 节点到 Trino 节点的网络连通性。用 Knox 主机直接 curl 一下 Trinocurl -u trino_service:password http://trino-host.example.com:8080/v1/info如果这一步 401 或者连接超时先解决网络和认证问题再配 Knox。三是准备好 Knox 对外使用的认证用户。我这边用的是 LDAP 域账号Knox 通过 ShiroProvider 接 LDAP 完成认证。如果你不想接 LDAP也可以配置本地文件 realm但生产环境建议 LDAP方便和现有账号体系打通。4. Knox 拓扑与服务定义把 Trino UI 挂到网关路径上4.1 查看 Knox 有没有现成的 Trino/Presto 服务定义Knox 的 service definition 决定了一个服务角色支持哪些路径、如何做 URL rewrite、使用什么 dispatch 策略。先看本地有没有避免重复造轮子ls /usr/hdp/current/knox-server/conf/services | grep -iE trino|presto我这边 HDP 版本的 Knox 自带了一个presto.xml但打开后发现它主要覆盖的是/v1/**这一类 REST API 路径对/ui/**静态资源路径支持不全。直接用它的话/ui/页面能转发但页面里的 JS 和 CSS 会加载不出来或者路径被 rewrite 得乱七八糟。最简单的办法是自己写一个专门给 Trino UI 用的服务定义。这个文件不影响 Knox 其他服务安全可靠。4.2 自定义一个 trino-ui 服务定义在 Knox 的服务定义目录下新建trino-ui.xmlsudo vi /etc/knox/conf/services/trino-ui.xml内容如下核心是两条 rewrite 规则和两个 routeservice roleTRINO-UI nametrino-ui version1.0.0 rewrite nameinbound path-paramstrue rule patternhttp://${gateway.host}:${gateway.port}/${gateway.path}/trino-ui/{path**}?{query} rewrite template/{path**}?{query} / /rule /rewrite rewrite nameoutbound path-paramstrue rule patternhttp://trino-host.example.com:8080/{path**}?{query} rewrite templatehttp://${gateway.host}:${gateway.port}/${gateway.path}/trino-ui/{path**}?{query} / /rule /rewrite routes route path/** rewrite nameinbound / rewrite nameoutbound / /route /routes /service这个文件的作用是inbound 规则把用户访问https://knox-host:8443/gateway/default/trino-ui/xxx的路径剥掉 Knox 网关前缀转成/xxx。outbound 规则把 Trino 返回的响应中所有指向http://trino-host.example.com:8080的地址改写成 Knox 的地址这样页面里的链接、重定向、静态资源请求不会跳出网关。不同 Knox 版本的 rewrite 标签可能有一点点差异如果加载报错看 Knox 的 gateway.log 会明确告诉你哪条规则解析失败。大部分网上流传的报错都是路径参数没开path-paramstrue导致路径里的参数被吞。4.3 拓扑文件里注册服务并配置认证Knox 的拓扑文件放在/etc/knox/conf/topologies/下。生产环境我建议新建一个独立拓扑比如trino.xml这样访问路径会更清晰/gateway/trino/trino-ui/ui/。但如果你们的 Knox 已经有default.xml并且不想引入太多拓扑也可以在已有 topology 里加 service。拓扑文件示例topology gateway provider roleauthentication/role nameShiroProvider/name enabledtrue/enabled param namesessionTimeout/name value30/value /param param namemain.ldapRealm/name valueorg.apache.knox.gateway.shirorealm.KnoxLdapRealm/value /param param namemain.ldapRealm.contextFactory.url/name valueldap://ldap.example.com:389/value /param param namemain.ldapRealm.userDnTemplate/name valueuid{0},oupeople,dcexample,dccom/value /param param nameurls./**/name valueauthcBasic/value /param /provider /gateway service roleTRINO-UI/role urlhttp://trino-host.example.com:8080/url /service /topology注意 provider 里的urls./**设置为authcBasic表示所有请求都需要 Basic 认证。如果你的 LDAP 用户和 Trino 密码文件里的用户不是同一套那后面还需要让 Knox 把 Basic header 继续传到后端我在踩坑部分会详细说。4.4 重启并观察日志改完拓扑文件后重启 Knox 让拓扑生效sudo -u knox /usr/hdp/current/knox-server/bin/gateway.sh restart查看启动日志tail -f /var/log/knox/gateway.log看到类似Loaded topology trino.xml的日志说明拓扑加载成功。如果出现服务未定义之类的报错先检查服务定义文件的文件名和 role 名称是否与拓扑里的TRINO-UI匹配。Knox 会根据文件名默认推断 role文件名不匹配会导致拓扑加载失败。5. 验证链路curl 联调、浏览器登录、UI 加载5.1 先直接验证 Trino HTTP 服务在配 Knox 时最容易犯的错误是后端 Trino 本身还没就绪就急着调网关。先在 Trino 节点上验证curl -u trino_service:password http://trino-host.example.com:8080/v1/info如果返回 JSON 数据说明双认证配置生效密码认证可以正常访问。如果返回 401查看 Trino 日志里有没有Authentication failed或者Basic authentication failed确认password.db里的用户名密码是否正确。顺便提一句http-server.authentication.type如果同时配置KERBEROS和PASSWORD一定不能写成KERBEROS,PASSWORD带空格Trino 的属性解析对空格很敏感写错会导致整个认证配置不生效。5.2 验证 Knox 代理后的 API在 Knox 节点或者办公网测试机执行curl -k -u alice:password https://knox-host:8443/gateway/trino/trino-ui/v1/info这里的alice是 LDAP 域账号。如果返回和上一步一样的 JSON说明 Knox 到 Trino 的转发链路已经打通认证头也被正确传递。如果这一步报 401先用-v参数看完整请求响应curl -k -v -u alice:password https://knox-host:8443/gateway/trino/trino-ui/v1/info重点看响应头里是否有WWW-Authenticate: Negotiate。如果后端 Trino 仍在要求 Kerberos 而不是 Basic说明 Knox 转发时把Authorization头丢掉了或者没有带上。5.3 验证 UI 页面API 通了之后验证静态页面curl -k -u alice:password -i https://knox-host:8443/gateway/trino/trino-ui/ui/正常情况下返回200 OKContent-Type: text/html。用浏览器的开发者工具打开 Network 面板随便点击几个 JS 和 CSS 资源确认它们的请求地址都落在https://knox-host:8443/gateway/trino/trino-ui/前缀下而不是 Trino 的 8080 地址。如果前端资源加载不出来大概率是 outbound rewrite 没有覆盖到静态资源的响应路径。这时不要急着怀疑 Knox先用 curl 把 HTML 内容拉下来用文本编辑器搜索trino-host和8080看看是否还有裸地址残留。5.4 浏览器实操上面的步骤都过了之后浏览器访问https://knox-host:8443/gateway/trino/trino-ui/ui/浏览器弹出认证框输入 LDAP 账号密码进入 Trino 查询页面。此时做两件事先执行一个最简单的SELECT 1确认 UI 能正常提交查询再执行一次SHOW SCHEMAS FROM hive确认 Trino 和 Hive 之间的 Kerberos 票据获取正常。如果查询报GSSException那是 Trino 引擎自身访问 Hive 或 HDFS 的 Kerberos 认证问题和 Knox 无关需要回过去查 Trino 的 keytab 和 catalog 配置。6. 踩坑记录那些明明配对了还是不行的瞬间6.1 Knox 自带的 Presto 服务只代理了/v1UI 404我一开始图省事直接用 Knox 自带的PRESTO角色在拓扑里加了service rolePRESTO/role urlhttp://trino-host.example.com:8080/url /service结果访问https://knox-host:8443/gateway/trino/presto/ui/返回 404。查 Knox 服务定义目录里的presto.xml发现 routes 里只有/v1/**的路径映射UI 路径完全没覆盖。这就是前面说为什么要自定义trino-ui.xml的原因。Knox 自带的服务定义主要是为了 CLI 和 JDBC 走 REST API 设计的Web UI 这种带静态资源的场景需要单独定义。6.2 Trino 把 UI 重定向到自己的 8080这个坑藏得很深。Trino 的/ui不带尾部斜杠时会 302 到/ui/。在特殊版本下这个 Location 有时会被拼成http://trino-host.example.com:8080/ui/。浏览器收到后直接跳出了 Knox 网关。排查方法很简单用 curl 看 302 响应curl -k -u alice:password https://knox-host:8443/gateway/trino/trino-ui/ui看返回的Location字段。如果指向后端 Trino 的 8080 端口说明 outbound rewrite 没有处理这一个响应头。在我的自定义服务定义里outbound 规则已经覆盖了 Trino 主机地址所以这个坑其实是上一个坑的衍生问题服务定义改好之后自然消失。6.3 认证头被吞导致 Trino 报 401Knox 的 ShiroProvider 完成认证后默认不一定把用户的Authorization头透传给后端。如果 Trino 的 Password Authenticator 收不到 Basic 头Knox 就会一直向后端发匿名请求后端返回 401Knox 再把 401 透给浏览器浏览器再次弹认证框形成循环。我的拓扑里 LDAP 认证的账号和 Trino 密码文件里的账号并不一样。我是这样解决的让 Knox 认证通过后在 rewrite 阶段手动注入一个固定的 Basic Authorization 头无论用户是谁Knox 后端都统一用trino_service这个服务账号访问 Trino。这样用户侧认证和引擎侧认证解耦我不用要求每个用户都在 Trino 密码文件里也有账号。在 Knox 里实现 header 注入要根据版本选择方式。新版本支持在 provider 里配置header预处理老版本则需要写一个简单的过滤器。如果你遇到这个问题可以先查 Knox 的gateway.log看认证后的请求头长什么样再决定用哪种方式。这是整个方案里唯一需要动代码或者高级配置的地方建议留足时间。6.4 Kerberos 的 SPNEGO 循环浏览器反复弹框如果你在 Trino 端只配置了KERBEROS认证浏览器通过 Knox 访问时会出现反复弹框输入账号密码也没用。原因很简单后端返回的是WWW-Authenticate: Negotiate浏览器以为你要走 Kerberos 协商但 Knox 又没参与协商最终双方僵住。这就是我不推荐 Knox Trino 纯 Kerberos 方案的原因。只要你能接受 Trino 的 Web UI 走 Password Authenticator这个问题就不存在。CLI 和 JDBC 依旧走 Kerberos不会因为开了 PASSWORD 而降低查询链路的安全等级。6.5 页面能开但查询报 GSSExceptionKnox 这层完全连通后Web UI 能打开执行查询却报GSSException: No valid credentials provided。这个报错来自 Trino 引擎访问 Hive connector 时的 Kerberos 认证和 Knox 无关。检查方向Trino 进程是否以trino用户运行keytab 是否可以读取。klist -ekt /etc/trino/conf/trino.keytab确认 principal 和 keytab 条目。jvm.config里有没有设置-Djava.security.krb5.conf/etc/krb5.conf和-Djavax.security.auth.useSubjectCredsOnlyfalse。Trino 的hive.metastore.authentication.typeKERBEROS和hive.metastore.service.principal是否配置正确。页面能打开只是前菜引擎能真正查询才是终点。6.6 Cookie 和静态资源的 Path 问题Trino UI 的某些版本会写 cookiePath默认是/。如果 Knox 后面还挂了其他服务这个 cookie 可能会污染其他路径。处理方式是在 Knox 的 outbound rewrite 里对Set-Cookie响应头做路径替换。我用的是自定义服务定义outbound 规则会把Path/替换成Path/gateway/trino/trino-ui/避免影响同域名下的其他服务。这个坑不一定每个版本都会遇到但如果页面能打开、查询也能跑就是刷新后偶尔跳回登录页先查 cookie 的 Path 对不对。在我处理这个问题的整个过程中最核心的体会是一定要把认证链路拆成两段来看用户到 Knox 是一段Knox 到 Trino 是另一段。Ambari 开启 Kerberos 只是让第一段的难度变高了第二段反而可以用更可控的方式来做。如果你们跟我一样不想让 Trino 的 8080 端口暴露在办公网同时又希望给数据分析师一个不太折腾的网页查询入口Knox Trino 双认证这个组合是目前比较稳妥的选择。最后再补一句生产环境上线前一定要把 Trino 的 8080 端口用防火墙限到只有 Knox 节点能访问双认证只是功能层面的兜底网络层才是第一道防线。
返回列表