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
  • 需具備Kubernetes叢集與相關知識(ex:Ingress、StorageClass)。
  • keycloak會使用到postgresql database,因此需要有可用的StorageClass來建立所需的PVC。

有關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,安裝成功預期可看到類似以下結果:

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。

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頁面,勾選emailprofileusername,新增後應該看到如下圖畫面

OAuth2-Proxy在做驗證的時候,會去確認id_token內是否有email資訊,因此需將email加入Mapper中。

到目前為止,Keycloak安裝與相關所需資源建立大致上皆已完成,後續將說明如何跟OAuth2-Proxy做OIDC驗證流程的整合。

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做測試。

先建立一個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

打開瀏覽器,輸echo-service設定的host與ingress_nodePort,ex: https://echo.192.168.56.26.nip.io:31966

  • 一開始會因為沒有登入所以被導到keycloak登入頁面
  • 輸入帳號密碼(demouser/自行設定的密碼)
  • 登入成功後就能看到類似如下圖的,echo service的資訊

本文主要透過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