升级到 Pulsar3.0 后深入了解 JWT 鉴权
背景
最近在測(cè)試將 Pulsar 2.11.2 升級(jí)到 3.0.1的過(guò)程中碰到一個(gè)鑒權(quán)問(wèn)題,正好借著這個(gè)問(wèn)題充分了解下 Pulsar 的鑒權(quán)機(jī)制是如何運(yùn)轉(zhuǎn)的。
Pulsar 支持 Namespace/Topic 級(jí)別的鑒權(quán),在生產(chǎn)環(huán)境中往往會(huì)使用 topic 級(jí)別的鑒權(quán),從而防止消息泄露或者其他因?yàn)闄?quán)限管控不嚴(yán)格而導(dǎo)致的問(wèn)題。
我們會(huì)在創(chuàng)建 topic 的時(shí)候?yàn)?topic 綁定一個(gè)應(yīng)用,這樣就只能由這個(gè)應(yīng)用發(fā)送消息,其他的應(yīng)用嘗試發(fā)送消息的時(shí)候會(huì)遇到 401 鑒權(quán)的異常。
同理,對(duì)于訂閱者也可以關(guān)聯(lián)指定的應(yīng)用,從而使得只有規(guī)定的應(yīng)用可以消費(fèi)消息。
鑒權(quán)流程
以上的兩個(gè)功能本質(zhì)上都是通過(guò) Pulsar 的 admin-API 實(shí)現(xiàn)的。
這里關(guān)鍵的就是 role,在我們的場(chǎng)景下通常是一個(gè)應(yīng)用的 AppId,只要是一個(gè)和項(xiàng)目唯一綁定的 ID 即可。
這只是授權(quán)的一步,整個(gè)鑒權(quán)流程圖如下:
詳細(xì)步驟
生成公私鑰
bin/pulsar tokens create-key-pair --output-private-key my-private.key --output-public-key my-public.key
將公鑰分發(fā)到 broker 的節(jié)點(diǎn)上,鑒權(quán)的時(shí)候 broker 會(huì)使用公鑰進(jìn)行驗(yàn)證。
而私鑰通常是管理員單獨(dú)保存起來(lái)用于在后續(xù)的步驟為客戶(hù)端生成 token
使用私鑰生成 token
之后我們便可以使用這個(gè)私鑰生成 token 了:
bin/pulsar tokens create --private-key file:///path/to/my-private.key \
--subject 123456
eyJhbGciOiJSUzI1NiJ9.eyJzdWIiOiJhZG1pbiJ9
其中的
subject和本文長(zhǎng)提到的role相等
使用 subject 授權(quán)
只是單純生成了 token 其實(shí)并沒(méi)有什么作用,還得將 subject(role) 與 topic 進(jìn)行授權(quán)綁定。
也就是上圖的這個(gè)步驟。
這里創(chuàng)建的
admin客戶(hù)端也得使用一個(gè)superRole角色的token才有權(quán)限進(jìn)行授權(quán)。superRole使用在broker.conf中進(jìn)行配置。
客戶(hù)端使用 token 接入 broker
PulsarClient client = PulsarClient.builder()
.serviceUrl("pulsar://broker.example.com:6650/")
.authentication(AuthenticationFactory.token("eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.ipevRNuRP6HflG8cFKnmUPtypruRC4fb1DWtoLL62SY"))
.build();
使用剛才私鑰生成的 token 接入 broker 才能生產(chǎn)或者消費(fèi)數(shù)據(jù)。
originalPrincipal cannot be a proxy role
這些流程正常都沒(méi)啥問(wèn)題,但直到我升級(jí)了 Pulsar3.0 后客戶(hù)端直接就連不上了。
在 broker 中看到了 WARN 的警告日志:
cannot specify originalPrincipal when connecting without valid proxy role
之后在 3.0 的升級(jí)日志中看到相關(guān)的 Issue。
從這個(gè) PR 相關(guān)的代碼和變更的文檔可以得知:
升級(jí)到 3.0 之后風(fēng)險(xiǎn)校驗(yàn)等級(jí)提高了,proxyRole 這個(gè)字段需要在 broker 中進(jìn)行指定(之前的版本不需要強(qiáng)制填寫(xiě))。
因?yàn)槲覀兪褂昧?Proxy 組件,所有的請(qǐng)求都需要從 proxy 中轉(zhuǎn)一次,這個(gè) proxyRole 是為了告訴 broker:只有使用了 proxyRole 作為 token 的 Proxy 才能訪問(wèn) broker,這樣保證了 broker 的安全。
superUserRoles: broker-admin,admin,proxy-admin
proxyRoles: proxy-admin
以上是我的配置,我的 Proxy 配置的也是 proxy-admin 這個(gè) token,所以理論上是沒(méi)有問(wèn)題的,但依然鑒權(quán)失敗了,查看 broker 的日志后拿到以下日志:
Illegal combination of role [proxy-admin] and originalPrincipal [proxy-admin]: originalPrincipal cannot be a proxy role.
排查了許久依然沒(méi)有太多頭緒,所以我提了相關(guān)的 issue:
https://github.com/apache/pulsar/issues/21583
之后我咨詢(xún)了 Pulsar 的 PMC @Technoboy 在他的提示下發(fā)現(xiàn)我在測(cè)試的時(shí)候使用的是 proxy-admin,正好和 proxyRoles 相等。
閱讀源碼和這個(gè) PR 的 comment 之后得知:
也就是說(shuō)客戶(hù)端不能使用和 proxyRole 相同的角色進(jìn)行連接,這個(gè)角色應(yīng)當(dāng)也只能給 Proxy 使用,這樣的安全性才會(huì)高。
所以這個(gè) Comment 還在討論這是一個(gè) breaking change? 還是一個(gè)增強(qiáng)補(bǔ)丁。
因?yàn)楹喜⑦@個(gè) PR 后對(duì)沒(méi)有使用 proxyRole 的客戶(hù)端將無(wú)法連接,同時(shí)也可能出現(xiàn)我這種 proxyRole 就是客戶(hù)端使用的角色,這種情況也會(huì)鑒權(quán)失敗。
所以我換了一個(gè) superRole 角色就可以了,比如換成了 admin。
但其實(shí)即便是放到我們的生產(chǎn)系統(tǒng),只要配置了
proxyRole也不會(huì)有問(wèn)題,因?yàn)槲覀儜?yīng)用所使用的 role 都是不這里的superUserRole,全部都是使用AppId生成的。
token 不一致
但也有一個(gè)疑惑,我在換為存放在 configmap 中的 admin token 之前(測(cè)試環(huán)境使用的是 helm 安裝集群,所以這些 token 都是存放在 configmap 中的),
為了驗(yàn)證是否只要非 proxyRole 的 superRole 都可以使用,我就自己使用了私鑰重新生成了一個(gè) admin 的 token。
bin/pulsar tokens create --private-key file:///pulsar/private/private.key --subject admin
這樣生成的 token 也是可以使用的,但是我將 token 復(fù)制出來(lái)之后卻發(fā)現(xiàn) helm 生成的 token 與我用 pulsar 命令行生成的 token 并不相同。
為了搞清楚為什么 token 不同但鑒權(quán)依然可以通過(guò)的原因,之后我將 token decode之后知道了原因:
原來(lái)是 Header 不同從而導(dǎo)致最終的 token 不同,helm 生成的 token 中多了一個(gè) typ 字段。
之后我檢查了 helm 安裝的流程,發(fā)現(xiàn)原來(lái) helm 的腳本中使用的并不是 Java 的命令行工具:
${PULSARCTL_BIN} token create -a RS256 --private-key-file ${privatekeytmpfile} --subject ${role} 2&> ${tokentmpfile}
這個(gè) PULSARCTL_BIN 是一個(gè)由 Go 寫(xiě)的命令行工具,我查看了其中的源碼,才知道 Go 的 JWT 工具會(huì)自帶一個(gè) header。
https://github.com/streamnative/pulsarctl
而 Java 是沒(méi)有這個(gè)邏輯的,但也只是加了 header,payload 的值都是相同的。
這樣也就解釋了為什么 token 不同但確依然能使用的原因。
總結(jié)
以上是生活随笔為你收集整理的升级到 Pulsar3.0 后深入了解 JWT 鉴权的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 这些新项目一定不要错过「GitHub 热
- 下一篇: Log4j入门使用