單頁 React 應用程式(含 OAuth)

此沙箱提供使用 Envoy 建置和開發單頁應用程式的範例。

此沙箱涵蓋多項 Envoy 的功能,包括

  • direct_response

  • OAuth

  • 動態 xDS 檔案系統更新

  • Websocket 代理

  • Gzip 壓縮

  • TLS/SNI 上/下游連線/終止

  • 路徑/主機重寫

此應用程式使用 React,並使用 Vite 建置,並示範使用 Envoy 的 OAuth 篩選器 進行 OAuth 驗證。

這涵蓋了我們希望 OAuth 同時驗證使用者並提供憑證以進行進一步 API 互動的情況。

藉由將 OAuth 設定 forward_bearer_token 設定為 true 來啟用此功能。

36                redirect_uri: "%REQ(x-forwarded-proto)%://%REQ(:authority)%/authorize"
37                forward_bearer_token: true
38                pass_through_matcher:
39                  name: ":path"
40                  string_match:

警告

設定 forward_bearer_token 表示提供的存取權杖將會轉送到此 HTTP 篩選器鏈的 Envoy 代理的任何叢集/上游。

如果存在不受信任的上游,則需要小心移除任何敏感的 Cookie,例如 BearerToken

可以藉由針對受影響的路由設定 request_headers_to_remove 來達成此目的。

隨附提供了一個虛擬「Myhub」後端,其中包含一個最基本的 OAuth 提供者和 API,以供範例中使用。

隨附提供了建置和更新應用程式以供生產使用的設定,以及具有自動程式碼重新載入功能的開發環境

生產和開發環境分別在連接埠 1000010001 上公開。

Myhub 後端可以輕鬆地替換為 Github 或其他基於 OAuth 的上游服務,並且隨附提供了一些關於如何執行此操作的指引

步驟 1:建立一個 .local 目錄以進行沙箱自訂

變更為 examples/single-page-app 目錄,並建立一個目錄來儲存沙箱自訂。

您可以使用 Git 會忽略的 .local

$ mkdir .local

ui/ 目錄複製到 .local 並設定 UI_PATH。這樣將可以在不變更已提交檔案的情況下進行自訂。

$ cp -a ui .local
$ export UI_PATH=./.local/ui

步驟 2:產生 HMAC 金鑰

Envoy 的 OAuth 篩選器 需要 HMAC 金鑰來編碼憑證。

將預設的沙箱金鑰複製到自訂目錄,並建立所需的 HMAC 金鑰。

MY_HMAC_SECRET_SEED 替換為您選擇的詞組

$ cp -a secrets .local
$ HMAC_SECRET=$(echo "MY_HMAC_SECRET_SEED" | mkpasswd -s)
$ export HMAC_SECRET
$ envsubst < hmac-secret.tmpl.yml > .local/secrets/hmac-secret.yml

為 Docker 匯出金鑰資料夾的路徑

$ export SECRETS_PATH=./.local/secrets

步驟 3:啟動容器

首先匯出 UID,以確保使用您的使用者 ID 建立容器建立的檔案。

然後啟動 Docker 編排

$ pwd
envoy/examples/single-page-app
$ export UID
$ docker compose pull
$ docker compose up --build -d
$ docker compose ps
NAME                          IMAGE                       COMMAND                                                    SERVICE   CREATED         STATUS         PORTS
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
single-page-app-envoy-1       single-page-app-envoy       "/docker-entrypoint.sh envoy -c /etc/envoy/envoy.yaml ..." envoy     2 minutes ago   Up 2 minutes           0.0.0.0:10000-10001->10000-10001/tcp, :::10000-10001->10000-10001/tcp
single-page-app-myhub-1       single-page-app-myhub       "/opt/myhub/app.py"                                        myhub     2 minutes ago   Up 2 minutes (healthy) 0.0.0.0:7000->7000/tcp, :::7000->7000/tcp
single-page-app-myhub-api-1   single-page-app-myhub-api   "/opt/myhub/app.py"                                        myhub-api 2 minutes ago   Up 2 minutes (healthy)
single-page-app-ui-1          single-page-app-ui          "/entrypoint.sh dev.sh"                                    ui        2 minutes ago   Up 2 minutes (healthy)

步驟 4:瀏覽至開發應用程式並登入

開發應用程式現在應該可在 https://127.0.0.1:10001 上使用,並提供登入按鈕

../../_images/spa-login.png

注意

虛擬 OAuth 提供者自動信任所有人為硬式編碼的 envoydemo 使用者,並重新導向回應用程式。

在實際情況下,提供者會在繼續之前驗證和授權使用者。

沙箱設定為對 pass_through_matcher 進行反轉比對。

這會忽略 OAuth 的所有路徑,但以下路徑除外

  • /authorize.*

  • /hub.*

  • /login

  • /logout.

37                forward_bearer_token: true
38                pass_through_matcher:
39                  name: ":path"
40                  string_match:
41                    safe_regex:
42                      regex: >-
43                        ^\/(authorize.*|login|logout)$
44                  invert_match: true
45                redirect_path_matcher:
46                  path:

當使用者按一下 login 時,應用程式會藉由呼叫 Envoy 中的 /login 路徑來啟動 OAuth 流程。

這會將使用者重新導向至 OAuth 提供者以進行授權/驗證,其中包含進一步的重新導向連結

34                default_expires_in: 600s
35                authorization_endpoint: https://127.0.0.1:7000/authorize
36                redirect_uri: "%REQ(x-forwarded-proto)%://%REQ(:authority)%/authorize"
37                forward_bearer_token: true
38                pass_through_matcher:
39                  name: ":path"

成功授權/驗證後,使用者會透過此連結重新導向回應用程式,並附帶必要的 OAuth 授權碼 以繼續

44                  invert_match: true
45                redirect_path_matcher:
46                  path:
47                    prefix: /authorize
48                signout_path:
49                  path:
50                    exact: /logout

然後,Envoy 會使用此授權碼及其用戶端金鑰來確認授權,並取得使用者的存取權杖

50                    exact: /logout
51                credentials:
52                  client_id: "0123456789"
53                  token_secret:
54                    name: token
55                    sds_config:
56                      path_config_source:
57                        path: /etc/envoy/secrets/myhub-token-secret.yml
58                  hmac_secret:
59                    name: hmac
60                    sds_config:
61                      path_config_source:
1resources:
2- "@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.Secret
3  name: token
4  generic_secret:
5    secret:
6      inline_string: VERY_SECRET_KEY

登入後,您應該能夠使用 OAuth 憑證來查詢 API

../../_images/spa-resources.png

警告

Envoy 的 OAuth 實作預設會針對端點上的所有路徑觸發 OAuth 流程。

這可能會在請求資產時輕易觸發 OAuth 洪水,並在 OAuth 流程失敗時導致迴圈。

可以藉由限制 OAuth 流程使用的路徑來避免這種情況。

沙箱範例會藉由反轉 pass_through_matcher,使其僅比對必要 OAuth 路徑來達成此目的。

提示

Myhub OAuth 提供者不會提供發出憑證的到期日。同樣地,Github 也可能會提供,也可能不會,視設定而定。就 OAuth2 規格而言,這是有效的。

如果授權提供者未包含到期日,Envoy 預設會使驗證失敗。

可以藉由設定 default_expires_in 來解決此問題

33                  timeout: 3s
34                default_expires_in: 600s
35                authorization_endpoint: https://127.0.0.1:7000/authorize
36                redirect_uri: "%REQ(x-forwarded-proto)%://%REQ(:authority)%/authorize"
37                forward_bearer_token: true

步驟 5:發出 API 查詢

對於沙箱應用程式,已設定 forward_bearer_token,因此 Envoy 也會將取得的存取權杖做為 Cookie 傳回給使用者

../../_images/spa-cookies.png

然後,此 Cookie 會在後續對代理 Myhub API 的任何請求中透過 Envoy 傳遞

76                  prefix: "/hub/"
77                route:
78                  host_rewrite_literal: api.myhub
79                  regex_rewrite:
80                    pattern:
81                      regex: '^/hub/(.*)'
82                    substitution: '/\1'
83                  cluster: hub-api
84              - match:
85                  prefix: "/"
86                route:
87                  cluster: ui

步驟 6:即時重新載入程式碼變更

在瀏覽器開啟 https://127.0.0.1:10001 的情況下,對 UI 進行一些變更。

例如,您可以變更頁面標題

$ sed -i s/Envoy\ single\ page\ app\ example/DEV\ APP/g .local/ui/index.html

頁面應該會自動重新整理。

同樣地,對 .local/ui/src/... 中的 Typescript 應用程式元件所做的任何變更,都應該在瀏覽器中自動重新載入。

Envoy 中啟用此功能的方式是允許代理連線到 Vite 開發後端「升級」以使用 Websockets

22          "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
23          upgrade_configs:
24          - upgrade_type: websocket
25          http_filters:
26          - name: envoy.filters.http.oauth2
27            typed_config:

您可以使用以下指令檢視開發伺服器的日誌:

$ docker compose logs ui
single-page-app-ui-1  | Starting (dev.sh) with user: 1000 worker /home/worker
single-page-app-ui-1  | yarn run v1.22.19
single-page-app-ui-1  | $ vite --host 0.0.0.0 --port 3000
single-page-app-ui-1  |
single-page-app-ui-1  |   VITE v5.0.10  ready in 119 ms
single-page-app-ui-1  |
single-page-app-ui-1  |   ➜  Local:   https://127.0.0.1:3000/
single-page-app-ui-1  |   ➜  Network: http://172.30.0.5:3000/

如果您想與程序互動,也可以使用 docker attach

提示

您可以使用 Yarn 管理 Typescript 套件。

$ docker compose run --rm ui yarn

步驟 7:登出應用程式

登出時,應用程式會向 Envoy 設定的 signout_path 發出請求。

47                    prefix: /authorize
48                signout_path:
49                  path:
50                    exact: /logout
51                credentials:
52                  client_id: "0123456789"
53                  token_secret:

這會清除 Envoy 儲存的 Cookie 和憑證,然後將使用者返回應用程式首頁。

應用程式也會清除任何與使用者會話相關的已儲存資料。

步驟 8:建置生產環境資源

首先,建立並設定自訂的 xds/ 目錄。

您需要重新建置 Envoy,以確保它能看到正確的目錄。

$ mkdir .local/production
$ cp -a xds .local/production/
$ export XDS_PATH=./.local/production/xds
$ docker compose up --build -d envoy

您可以使用以下指令建置應用程式的生產環境資源:

$ docker compose run --rm ui build.sh

在建置 React 應用程式後,沙箱腳本會自動使用提供應用程式所需的靜態路由來更新 Envoy 的設定。

您可以檢視產生的路由

$ jq '.resources[0].filter_chains[0].filters[0].typed_config.route_config.virtual_hosts[0].routes' < .local/production/xds/lds.yml
[
  {
    "match": {
      "path": "/assets/index-dKz4clFg.js"
    },
    "direct_response": {
      "status": 200,
      "body": {
        "filename": "/var/www/html/assets/index-dKz4clFg.js"
      }
    },
    "response_headers_to_add": [
      {
        "header": {
          "key": "Content-Type",
          "value": "text/javascript"
        }
      }
    ]
  },
  {
    "match": {
      "path": "/myhub.svg"
    },
    "direct_response": {
      "status": 200,
      "body": {
        "filename": "/var/www/html/myhub.svg"
      }
    },
    "response_headers_to_add": [
      {
        "header": {
          "key": "Content-Type",
          "value": "image/svg+xml"
        }
      }
    ]
  },
  {
    "match": {
      "prefix": "/"
    },
    "direct_response": {
      "status": 200,
      "body": {
        "filename": "/var/www/html/index.html"
      }
    },
    "response_headers_to_add": [
      {
        "header": {
          "key": "Content-Type",
          "value": "text/html"
        }
      }
    ]
  }
]

注意

此設定會設定 Envoy 將必要檔案儲存在記憶體中。

這可能很適合單頁應用程式的使用案例,但對於許多或大型檔案來說,則無法很好地擴展。

提示

當您變更 javascript/typescript 檔案時,重新建置應用程式會建立指向已編譯資源的新路由。

在此情況下,Envoy 將透過 xDS 更新並使用新路由的資源。

如果您只變更未獲得新路由的資源 (例如 index.html),您應該在重新建置應用程式後重新啟動 Envoy。

$ docker compose run --rm ui build.sh
$ docker compose restart envoy

步驟 9:瀏覽至生產環境伺服器

您可以透過 https://127.0.0.1:10000 瀏覽至此伺服器。

與開發端點不同,生產端點已設定:

  • TLS (自我簽署)

  • Gzip 壓縮

  • 靜態提供的資源

步驟 10:設定 Github OAuth/API 存取權

提示

此沙箱說明了 Github 的設定,但您應該很容易就能將這些指示調整為其他提供者。

您需要設定 Github OAuth 或完整應用程式。後者提供更多控制權,通常是較佳的選擇。

這可以在 使用者 或組織層級完成。

../../_images/spa-github-oauth.png

注意

在設定 Github OAuth 時,您需要提供重新導向 URI。

這必須與 Envoy 中設定的 URI 相符。

為此範例的目的,將其設定為 https://127.0.0.1:10000

您需要一個單獨的 OAuth 應用程式以進行開發。

根據您的使用案例,您可能還需要設定應用程式所需的任何權限。

設定完成後,您將需要提供的用戶端 ID 和密碼

步驟 11:更新 Envoy 的設定以使用 Github

新增 Github 提供的用戶端密碼

$ TOKEN_SECRET="GITHUB PROVIDED CLIENT SECRET"
$ export TOKEN_SECRET
$ envsubst < secrets/token-secret.tmpl.yml > .local/secrets/github-token-secret.yml

建立的檔案將在容器中的 /etc/envoy/secrets 下提供。

提示

以下指示使用 sed,但您可能希望使用您的編輯器進行必要的取代。

對於每個設定,有 2 個地方需要更新,一個用於開發接聽器,另一個用於生產環境。

建立 Envoy 設定的副本,並告知 Docker 使用它

$ cp -a envoy.yml .local/envoy.yml
$ export ENVOY_CONFIG=.local/envoy.yml

對於 .local/envoy.yml 中的 OAuth 設定,設定 Github 提供的用戶端密碼

$ sed -i s@client_id:\ \"0123456789\"@client_id:\ \"$GITHUB_PROVIDED_CLIENT_ID\"@g .local/envoy.yml

使用 https://github.com/login/oauth/authorize 取代 authorization_endpoint

$ sed -i s@authorization_endpoint:\ https://127.0.0.1:7000/authorize@authorization_endpoint:\ https://github.com/login/oauth/authorize@g .local/envoy.yml

使用 https://github.com/login/oauth/access_token 取代 token_endpoint > uri

$ sed -i s@uri:\ http://myhub:7000/authenticate@uri:\ https://github.com/login/oauth/access_token@g .local/envoy.yml

token_secret > path 指向上面建立的 github-token-secret.yml

$ sed -i s@path:\ /etc/envoy/secrets/myhub-token-secret.yml@path:\ /etc/envoy/secrets/github-token-secret.yml@g .local/envoy.yml

取代 主機重寫

$ sed -i s@host_rewrite_literal:\ api.myhub@host_rewrite_literal:\ api.github.com@g .local/envoy.yml

最後新增 (或使用 githubgithub-api 叢集取代 myhub* 叢集) Github 設定的 叢集

$ cat _github-clusters.yml >> .local/envoy.yml

步驟 12:更新應用程式設定以使用 Github

我們需要告知應用程式提供者的名稱。

目前已實作 Myhub 和 Github 的提供者

 7export const AuthProviders: IAuthProviders = {
 8  "myhub": {
 9    "name": "Myhub",
10    "icon": MyhubIcon},
11  "github": {
12    "name": "Github",
13    "icon": GithubIcon}}

如果您依照上述步驟操作,Vite 應用程式環境設定會從 .local/ui/.env* 讀取。

$ echo "VITE_APP_AUTH_PROVIDER=github" > .local/ui/.env.local

步驟 13:重新建置應用程式並重新啟動 Envoy

$ docker compose run --rm ui build.sh
$ docker compose up --build -d envoy

提示

請注意使用 up --build -d,而不是 restart

這是必要的,因為我們已變更 envoy.yml,該檔案會在建置時載入容器中。

瀏覽至生產環境伺服器 https://127.0.0.1:10000

您現在可以登入並使用 Github API

../../_images/spa-login-github.png

另請參閱

Envoy OAuth 篩選器

Envoy OAuth 篩選器的設定參考。

Envoy OAuth 篩選器 API

Envoy OAuth 篩選器的 API 參考。

OAuth2 規範

OAuth 2.0 是業界標準的授權協定。

React

用於網頁和原生使用者介面的程式庫。

Vite

下一代前端工具。

Envoy Gzip 壓縮 API

Envoy gzip 壓縮的 API 和設定參考。

保護 Envoy 快速入門指南

保護 Envoy 的關鍵概念概述。

Github OAuth 應用程式

有關設定 Github OAuth 應用程式的資訊。

Github API

Github API 的參考。