Envoy を使用して ID Token (OIDC) を検証する

| 6 min read
Author: shigeki-shoji shigeki-shojiの画像

庄司です。

Envoy proxy は API を使って動的に構成すると無停止で設定変更等を行うことができます。このような操作は 通常 Istio や AWS App Mesh のようなコントロールプレーンで行うことになります。

この一連の記事では Envoy proxy 単体の機能を説明するために静的な設定を用いて説明しています。

以前構築した S3 にアクセスする Envoy proxy を拡張して、Amazon Cognito user pools で認証されたユーザのみがこの静的コンテンツにアクセスできるようにしたいと思います。

図1

Information

認証されたユーザとは、あらかじめ Amazon Cognito user pools にログインして OIDC トークン (JWT トークン) を取得済みで、トークンを Authorization ヘッダ等に含むリクエストでアクセスされることをここでは指しています。

Envoy proxy の構成

#

Envoy proxy の定義ファイル (front-envoy.yaml) は次のようになります。

static_resources:
listeners:
- address:
socket_address:
address: 0.0.0.0
port_value: 8080
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
codec_type: AUTO
stat_prefix: ingress_http
route_config:
name: s3_route
virtual_hosts:
- name: static_web
domains:
- "*"
routes:
- match:
prefix: /
route:
cluster: s3
auto_host_rewrite: true
regex_rewrite:
pattern:
google_re2: {}
regex: '^(.*)\/$'
substitution: '\1/index.html'
http_filters:
- name: envoy.filters.http.jwt_authn
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
providers:
amazoncognito:
issuer: "https://cognito-idp.<REGION>.amazonaws.com/<POOL ID>"
audiences:
- "<CLIENT ID>"
forward_payload_header: jwt-payload
remote_jwks:
http_uri:
uri: "https://cognito-idp.<REGION>.amazonaws.com/<POOL ID>/.well-known/jwks.json"
cluster: jwks
timeout: 5s
cache_duration: 600s
rules:
- match:
prefix: /
requires:
provider_name: amazoncognito
- name: envoy.filters.http.router

clusters:
- name: s3
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: s3
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: <BUCKET_NAME>.s3.amazonaws.com
port_value: 443
hostname: <BUCKET_NAME>.s3.amazonaws.com
transport_socket:
name: envoy.transport_sockets.tls
- name: jwks
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: jwks
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: cognito-idp.<REGION>.amazonaws.com
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls

admin:
address:
socket_address:
address: 0.0.0.0
port_value: 9901

<> で囲んだプレースホルダーにはそれぞれ利用環境に合わせて設定してください。

OpenID Connect の ID Token を検証するために JWT Authentication フィルターを追加しています。該当部分は次のとおりです。

- name: envoy.filters.http.jwt_authn
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
providers:
amazoncognito:
issuer: "https://cognito-idp.<REGION>.amazonaws.com/<POOL ID>"
audiences:
- "<CLIENT ID>"
forward_payload_header: jwt-payload
remote_jwks:
http_uri:
uri: "https://cognito-idp.<REGION>.amazonaws.com/<POOL ID>/.well-known/jwks.json"
cluster: jwks
timeout: 5s
cache_duration: 600s
rules:
- match:
prefix: /
requires:
provider_name: amazoncognito

Envoy proxy の実装では、OpenID Connect Discovery を経由した JSON Web Key (JWK) の取得はできません。そのため、remote_jwks にキー取得の URI を設定する必要があります。

これに対応して、JSON Web Key を取得するクラスタ設定も必要になります。

- name: jwks
type: LOGICAL_DNS
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: jwks
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: cognito-idp.<REGION>.amazonaws.com
port_value: 443
transport_socket:
name: envoy.transport_sockets.tls

実行

#

以上の構成を、次のコマンド (nerdctl または docker) で起動することができます。

nerdctl run -it --rm --name envoy -v `pwd`/front-envoy.yaml:/etc/front-envoy.yaml -p 8080:8080 -p 9901:9901 envoyproxy/envoy:v1.21-latest -c /etc/front-envoy.yaml --service-cluster front-proxy

この設定による動作シーケンスの概要は図のようになります。正確に書く場合は、図中の jwks.json の取得は実際には非同期に実行されキャッシュされることに注意してください。

図2

まとめ

#

ここまで説明したとおり、ノーコードで静的コンテンツに対する認証機能の追加ができました。静的コンテンツに限らず、既存の認証機能を持たない API サーバに認証機能を追加する場合も同様の方法で追加することができます。

ここで説明した設定ファイル等は GitHub リポジトリにあります。

参考

#
豆蔵デベロッパーサイト - 先週のアクセスランキング
  1. 基本から理解するJWTとJWT認証の仕組み (2022-12-08)
  2. AWS認定資格を12個すべて取得したので勉強したことなどをまとめます (2022-12-12)
  3. Nuxt3入門(第4回) - Nuxtのルーティングを理解する (2022-10-09)
  4. Nuxt3入門(第1回) - Nuxtがサポートするレンダリングモードを理解する (2022-09-25)
  5. Nuxt3入門(第8回) - Nuxt3のuseStateでコンポーネント間で状態を共有する (2022-10-28)
  6. Jest再入門 - 関数・モジュールモック編 (2022-07-03)
  7. 自然言語処理初心者が「GPT2-japanese」で遊んでみた (2022-07-08)
  8. IoT を使ってみる(その6:MQTTブローカー Mosquitto編) (2022-10-08)
  9. Nuxt3入門(第3回) - ユニバーサルフェッチでデータを取得する (2022-10-06)
  10. 統計学で避けて通れない自由度の話 (2022-06-20)