OAuth2 Proxy 為你的後台提供認證

OAuth2 Proxy 為你的後台提供認證

TD;DR

今天又來跟大家介紹好東西,有時候我們在 Kubernetes 架了一些東西想要在離開家裡的區域網路環境也要能使用,雖然可以用下面文章提到的 Cloudflare ZeroTrust 服務 來把服務公開到 Internete,不過像是一些有權限存取重要資料的後台就不適合,最好還要加上認證確保只有特定的使用者才可以使用。

網站,誕生在樹梅派
本站作為一個有技術分享的網站,首先當然要介紹一下這個網站是怎麼搭建起來的。這篇文章會分別介紹,這個部落格網站的主體、樹梅派上的環境設定以及如何買域名跟配置公網連接。 開源的部落格 Ghost 其實要架設部落格網站目前還蠻多選擇的,例如像是:WordPress、Hexo、Hugo等...... 那其實我在找的是有文章管理後台可以讓我很方便的新增文章,所以像是Hexo、Hugo這兩套是屬於撰寫Markdown檔案再生成靜態站點的我就暫時不考慮。 接著是WordPress,筆者在工作上深受PHP其害,那剛好WordPress是以PHP做開發,我就直接不考慮。 介面美觀易用 向下面的截圖…

其實通常第一直覺就是想到用 Basic Auth 來驗證,不過這種方法雖然也是可以但卻有以下的缺點:

  1. 需要自己管理密碼,尤其當服務一多就很麻煩
  2. 密碼外洩問題,這種方式只要密碼外洩那防護一樣形同虛設
  3. 沒有 2FA 的保護,通常現在的服務例如 Google、GitHub 都有 2FA 提供額外的保障,萬一密碼外洩仍然部會輕易的被其他人登入自己的帳號

因此今天就來介紹這個東西 OAuth2 Proxy,它可以把你公開在網路上的後台結合你的 Google、GitHub 帳號的認證服務,例如:登入你的 GitHub 帳號並且確定你的使用者 ID 是你本人,才能訪問你的後台。

Installation | OAuth2 Proxy
1. Choose how to deploy:

運作原理

OAuth2 Proxy Architecture

通常你的網站架設在 Kubernetes 中,會透過 Ingress Controller 來去公開在 Internet 上,並掛上 SSL 憑證。

以上圖為例子,流量近來第一步就會進到 nginx ,nginx 這個角色就是 Ingress Controller 的部分,由它來提供 SSL 證書以及第一手的流量轉發。
接著流量會轉到 OAuth2 Proxy,由它檢查請求中是否有登入過的 Cookie 沒有的話,就會向下面的圖一樣,顯示一個登入畫面,如果使用者按下 Sign in 就會跳轉到發起 OAuth2 流程的部分,接著使用者就會跳轉到設定好的登入提供者,例如:Google、GitHub、Keycloak。

對於認證流程有興趣的可以打開開發者工具,勾選保留紀錄後,去找我打勾的三個請求,就會是 OAuth2 認證的過程,可以參考下面的文章看看。
An Introduction to OAuth 2 | DigitalOcean
OAuth 2 is an authorization framework that enables applications to obtain limited access to user accounts on an HTTP service, such as Facebook, GitHub, and D…

最後可以看到完成登入後你的瀏覽器就會被塞一把 Cookie,這個就會是 Oauth token 下次再進入你的後台時,流量到 Oauth2 Proxy 這一層,它發現你有攜帶這把 Cookie 就會直接放行流量到你的後台。

具體怎麼運作的細節,可以參照以下官方文件的說明。
Behaviour | OAuth2 Proxy
1. Any request passing through the proxy (and not matched by --skip-auth-regex) is checked for the proxy’s session cookie (--cookie-name) (or, if allowed, a JWT token - see --skip-jwt-bearer-tokens).

如何整合到你的服務

其實如果不想了解原理單純看,如何整合的話相對是很簡單的,這裡以 Code Server 為例子:

選擇要整合的 Provider

有支援的 Provider 如官網,還蠻多種的,這裡會選定 GitHub當作例子。

OAuth Provider Configuration | OAuth2 Proxy
You will need to register an OAuth application with a Provider (Google, GitHub or another provider), and configure it with Redirect URI(s) for the domain you intend to run oauth2-proxy on.

首先先到這個網頁去新增一個 OAuth App。

Build software better, together
GitHub is where people build software. More than 100 million people use GitHub to discover, fork, and contribute to over 420 million projects.

域名換成自己的。

接著把 Client ID 留下,並按 Generate a new client secret 並把 Client Secret 留下。

將 OAauth2 Proxy 佈署成服務的上游

為了簡單說明這裡只貼出佈署 Code Server 用的 Deployment,OAauth2 Proxy 是以 Side Car 的方式跟 Code Server 跑在同一個 Pod 裡,OAuth2 Proxy 的設定看下方註解。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: code-server
spec:
  selector:
    matchLabels:
      app.kubernetes.io/instance: code-server
      app.kubernetes.io/name: code-server
  template:
    metadata:
      labels:
        app.kubernetes.io/instance: code-server
        app.kubernetes.io/name: code-server
    spec:
      containers:
      # oauth2-proxy 的容器
      - name: oauth-proxy
        image: quay.io/oauth2-proxy/oauth2-proxy:v7.5.1
        imagePullPolicy: IfNotPresent
        args:
        # command line args 設定
        - --http-address=0.0.0.0:4180 # 預設 127.0.0.1:4180 沒辦法訪問要改掉
        - --upstream=http://localhost:8080 # 指定你的服務,這裡是 Side Car 佈署,所以指定 localhost:服務的port
        - --provider=github
        - --client-id=xxxxx # 前一步GitHub取得的 Client ID
        - --client-secret=xxxxxx # 前一步GitHub取得的 Client Secret
        - --cookie-secret=xxxxxx # 照這裡提供的指令生一個 https://oauth2-proxy.github.io/oauth2-proxy/configuration/overview#generating-a-cookie-secret
        - --email-domain=* # 照抄
        - --github-user=xxxxx # 放想要給訪問的 GitHub User ID,有多組就逗點分隔
        ports:
        - name: oauth-proxy
          containerPort: 4180
      - name: code-server
        image: codercom/code-server:4.18.0
        imagePullPolicy: IfNotPresent
        args:
        - --auth=none
        - --disable-telemetry=true
        - --disable-update-check=true
        ports:
        - name: http
          containerPort: 8080
        livenessProbe:
          httpGet:
            port: http
            path: /
        readinessProbe:
          httpGet:
            port: http
            path: /
        volumeMounts:
        - mountPath: /home/coder
          name: data
      volumes:
      - name: data
        persistentVolumeClaim:
          claimName: code-server

最後創建一個 Service 以及對應的 Ingress 指到服務的 OAuth2 Proxy Port 就大功告成了。

apiVersion: v1
kind: Service
metadata:
  name: code-server
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/instance: code-server
    app.kubernetes.io/name: code-server
  ports:
  - name: oauth-proxy
    port: 4180
    protocol: TCP
    targetPort: 4180
---
ingress略...

結語

其實這個 OAuth2 Proxy 是我來現在的公司看到有在用,發現這通西蠻方便的,可以拿來保護一些沒有原生驗證機制又想開放到 Internet 的後台。

美中不足的是,它沒辦法說去做一個權限以及分組的對應或是切換,這裡我有去給他提了一個 Issue 想要給它支援可以讓使用者跑自定義的 lua 腳本,這樣就能在請求的開始或是結束在 Header 裡塞一些資料,讓例如:Kibana、Grafana 這類的後台能從 Header 特定欄位去對應出使用者的權限或是分組。

[Feature]: custom lua script for modify request or response headers, path, query string · Issue #2495 · oauth2-proxy/oauth2-proxy
Motivation hi, I want to do this approch https://discuss.elastic.co/t/bypass-login-page/337047/3, to by pass Kibana login page via add add the Basic Authentication header to upstream (Kibana). I kn…

那後續如果這個功能提案被接受我攻陷完成後有機會再來寫一篇介紹怎麼樣去使用它,請大家期待。