Authentication with Keycloak and OAuth2-Proxy on Kubernetes [Part 1]
因為某些需求,需對部署在Kubernetes上的應用服務做額外的保護,避免讓任何人都能存取對外暴露的應用服務,因此研究了一些驗證與授權的方式,來對Kubernetes內對外暴露的服務進行保護。
本系列文章主要說明如何在Kubernetes上,藉由OAuth2-Proxy與Keycloak的整合,建立一個OIDC驗證機制,讓每個要使用kubernetes內的應用服務時,都需先登入驗證後才能存取,此系列主要分兩個部分:
- 本篇文章會說明如何簡單使用NGINX Ingress Controller來整合。
- Part2:則是說明如何整合Istio來建立外部驗證機制。
本系列文章主要環境建置在virtualbox上:
- OS: Ubuntu 18.0.4
- Kubernetes: v1.20.8,單一節點
- 使用NodePort暴露ingress-nginx-controller service
- 服務所用的Domain: [service].[node-ip].nip.io
0x00 Prerequisite
- 需具備Kubernetes叢集與相關知識(ex:Ingress、StorageClass)。
- keycloak會使用到postgresql database,因此需要有可用的StorageClass來建立所需的PVC。
0x01 Nginx Ingress Controller
有關Nginx Ingress Controller可參考這裡,Ingress的說明,請參考kubernetes官方文件。
# Install Nginx Ingress Controller
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.47.0/deploy/static/provider/baremetal/deploy.yaml
因為是在Bare-metal上安裝,為求簡單,因此直接使用NodePort,安裝成功預期可看到類似以下結果:
0x02 Keycloak
Keycloak是一個open source的軟體,可爲應用程式或服務,提供像是身份認證和存取控制的功能,完整功能介紹請詳見Keycloak官網,這裡僅針對應用上需要的設定進行說明。
Install Keycloak-Operator
官方keycloak-operator文件中,是透過olm方式來管理keycloak-operator,詳細的安裝資訊可以參考官方網站,這裡會簡化一點,直接apply所需的資源來部署keycloak-operator。
Install Keycloak Server
建立以下keycloak.yaml,並修改以下欄位的值
- storageClassName: 自行修改為環境內所使用的StorageClassName
Note:
1.Keycloak會使用PostgreSQL作為資料庫,因此需指定可用的StorageClass,供建立 所需的PVC給PostgreSQL使用。
2.如註解說明,如果採用nginx ingress controller,則可將externalAccess註解部分取消,並刪除Ingress部分,改由operator幫忙建立,不過因為Part2會使用到istio-ingressgateway,因此這裡採用自行建立ingress的方式。
執行以下指令:
kubectl apply -f keycloak.yaml -n keycloak
正常無誤的話,應該能看到以下畫面
接著,逐一建立Keycloak Realm、Client與User。
kubectl apply -f basic_realm.yaml -n keycloak
kubectl apply -f lab_client.yaml -n keycloak
#請先修改credentials內password的值,自行定義demouser的密碼。
kubectl apply -f demo_user.yaml -n keycloak
上述的操作會在keycloak內建立相對應名稱的Realm、Client以及User,後續可於Keycloak Admin Console確認。
這邊建立Realm、Client與User的步驟,同樣也可以進入Keycloak Admin Console,透過web介面手動操作來達到同樣的效果。
Keycloak Ingress設定
Keycloak Ingress設定內容如下面的keycloak-ingress.yaml檔案,這裡需根據環境使用的domain,取代掉host原設定的值。
如果沒有domain也可以參考以下格式進行設定:
# service:自行定義的服務名稱: ex: keycloak
# NodeIP :k8s節點使用的IP
[service].[NodeIP].nip.io
修改好後:
kubectl apply -f keycloak-ingress.yaml
連線到Keycloak Admin Console
Keycloak-Ingress設定好後,我們就可以從Keycloak admin Console來確認上述的資源是否成功被建立。
首先,先取得Keycloak admin Console完整網址。
#取得ingress nodePort
ingress_nodePort=$(kubectl get svc -n ingress-nginx ingress-nginx-controller -o jsonpath='{.spec.ports[?(@.name=="https")].nodePort}')#取得ingress host
ingress_host = $(kubectl get ingress -n keycloak keycloak -o jsonpath='{.spec.rules[0].host}')#設定Keycloak URL
Keycloak_URL=https://$ingress_host:$ingress_nodePort/auth#print Keycloak URL
echo “Keycloak Admin Console: $Keycloak_URL/admin”
接著,透過以下指令取得Keycloak Admin Console登入帳號與密碼
kubectl get secret -n keycloak credential-keycloak -o go-template='{{range $k,$v := .data}}{{printf "%s: " $k}}{{if not $v}}{{$v}}{{else}}{{$v | base64decode}}{{end}}{{"\n"}}{{end}}'
之後,即可透過網址與帳號密碼來登入Admin Console。
登入後,應該會看到以下畫面,Basic即為剛剛前面建立的realm。
接著點選左邊列表中的Clients ,可以看到清單中的lab,點擊後可以看到相關的設定,後面會使用到這些設定。
最後,選擇左側選單下面的Users,點擊View all users,可以看到目前所建立的user資訊。
建立Protocol Mapper
確認Realm、Client與User都建立好後,需在Client新增幾個Protocol Mapper。
選擇[Clients] →[lab] →[Mappers] →[Add Builtin],進入Add Builtin Protocol Mapper頁面,勾選email、profile、username,新增後應該看到如下圖畫面。
OAuth2-Proxy在做驗證的時候,會去確認id_token內是否有email資訊,因此需將email加入Mapper中。
到目前為止,Keycloak安裝與相關所需資源建立大致上皆已完成,後續將說明如何跟OAuth2-Proxy做OIDC驗證流程的整合。
0x03 OAuth2-Proxy安裝
OAuth2-proxy提供一個OIDC的驗證流程,對於未經驗證的使用者,會將其重導向到登入畫面,要求使用者必需先登入才得以存取後面服務,本身支援多種不同的auth provider,例如:Google, GitHub、Keycloak等,在這一系列文章中則是選擇Keycloak作為provider。
建立oauth2-proxy
首先,先建立oauth2-proxy所需的deployment與service。
眼尖的可能會發現provider這裡用的是oidc而不是keycloak,主要是因為oauth2-proxy目前支援cooke-refresh功能的只有 GitLab, Google and OIDC這三個provider,因此在這裡選擇OIDC為provider。
根據上述的內容,底下幾個參數需根據環境設定修改:
- oidc-issuer-url: 需改成前面取得的[Keycloak_URL]/realms/basic
- redirect-url:此值為之後要call back連到oauth2-porxy的網址,格式為https://[domain]:[ingress_nodePort]/oauth2/callback,同樣如果沒有domain,可參考keycloak設定domain的方式設定,Ingress_nodePort也請參考Keycloak部分。
- OAUTH2_PROXY_CLIENT_SECRET: 請於admin console的[Clients] →[lab] →[credentials]頁面,找到Secret,複製其內的值到此欄位。
- OAUTH2_PROXY_COOKIE_SECRET: 用於建立安全的cookie,可透過以下指令簡單建立一個cookie secret:
openssl rand -base64 32 | tr -- '+/' '-_'
,其它方式可以參考官方說明 - whitelist-domain: 需與實際使用的domain一致,如果使用nodePort需於domain後面再加上
:*
(該值僅適用whiltelist-domain) - cookie-domain: 需與實際使用的domain一致
完成後,部署到Kubernetes上。
kubectl create ns oauth2-proxy
kubectl apply -f oauth2-proxy.yaml on oauth2-proxy
OAuth2-Proxy Ingress設定
建立oauth2-proxy外部入口。
- host: 需與oauth2-proxy中設定的domain一致。
部署oauth2-proxy ingress到kubernetes上。
kubectl apply -f oauth2-proxy-ingress.yaml -n oauth2-proxy
基本上這樣oauth2-proxy就算裝好了,接著來簡單建個echo service做測試。
0x04 部署echo-service
先建立一個echo service
kubectl apply -f https://gist.githubusercontent.com/MorpheusPH/dac706a2affd9c0349f274f4a66cbaa7/raw/3795b3533a96419cee3dcef0b5f8b76fe9759345/echo-service.yaml
接著,必須建立一個echo-service用的ingress
上面有幾個地方需根據環境做修改
- nginx.ingress.kubernetes.io/auth-url & auth-signin: 需將oauth2.192.168.56.26.nip.io:31966修改為之前在oauth2-proxy-ingress時設定的host值,以及先前設定的ingress_nodePort
- host: 同樣給定一個echo-service用的host
部署echo-service ingress:
kubectl apply -f echo-ingress.yaml
0x05 測試
打開瀏覽器,輸echo-service設定的host與ingress_nodePort,ex: https://echo.192.168.56.26.nip.io:31966
- 一開始會因為沒有登入所以被導到keycloak登入頁面
- 輸入帳號密碼(demouser/自行設定的密碼)
- 登入成功後就能看到類似如下圖的,echo service的資訊
0x06 結論
本文主要透過keycloak、oauth2-proxy與nginx ingress controller搭配,來對在kubernetes內的應用作保護,避免任何人都能存取對外暴露的應用服務。當然,透過nginx ingress controller只是其中一個方式,Part2部分會說明如何用istio來做到同樣的功能。
0x07 Troubleshooting
這裡列出幾個,明明能用user帳號登入keycloak,但oauth2-proxy卻出現錯誤的情況。
- id_token did not contain an email and profileURL is not defined
這是因為oauth2-proxy會去確認是否有設定e-mail,因此user沒有設定e-mail資訊的話,就會出現此錯誤。
解決方式:在user的帳號設定頁面輸入e-mail。
- email in id_token (xxx) isn’t verified
這是因為user雖然設定了e-mail,但是還沒通過e-mail verify。
解決方式:在user設定頁面將Email Verified設為true