Envoy proxy は API を使って動的に構成すると無停止で設定変更等を行うことができます。このような操作は 通常 Istio や AWS App Mesh のようなコントロールプレーンで行うことになります。
この一連の記事では Envoy proxy 単体の機能を説明するために静的な設定を用いて説明しています。
以前の記事で OpenID Connect の ID Token の検証については説明しました。
この記事では、下の図のように、さらに認可ができるようにしたいと思います。
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.ext_authz typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz grpc_service: envoy_grpc: cluster_name: authz-opa timeout: 0.250s transport_api_version: V3 - 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 - name: authz-opa type: STRICT_DNS typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http2_protocol_options: {} load_assignment: cluster_name: authz-opa endpoints: - lb_endpoints: - endpoint: address: socket_address: address: opa-service port_value: 9191 admin: address: socket_address: address: 0.0.0.0 port_value: 9901
<>
で囲んだプレースホルダーにはそれぞれ利用環境に合わせて設定してください。
Open Policy Agent の構成
認可のために追加したフィルターは、Open Policy Agent を利用する External Authorization フィルターです。
Open Policy Agent (OPA) は、Cloud Native Computing Fundation (CNCF) の graduated プロジェクトです。さまざまなポリシーの適用で利用されています。
Stop using a different policy language, policy model, and policy API for every product and service you use. Use OPA for a unified toolset and framework for policy across the cloud native stack.
使用する製品やサービスごとに異なるポリシー言語、ポリシーモデル、およびポリシーAPIの使用をやめましょう。クラウドネイティブスタック全体のポリシーの統合ツールセットとフレームワークにOPAを使用してください。
ここで追加したフィルターの定義は次の部分です。
- name: envoy.filters.http.ext_authz typed_config: "@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz grpc_service: envoy_grpc: cluster_name: authz-opa timeout: 0.250s transport_api_version: V3
OPA は Envoy proxy とは別のコンテナで実行されます。そして、Envoy proxy と OPA とは gRPC による通信を行います。クラスター定義は次の部分になります。
- name: authz-opa type: STRICT_DNS typed_extension_protocol_options: envoy.extensions.upstreams.http.v3.HttpProtocolOptions: "@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions explicit_http_config: http2_protocol_options: {} load_assignment: cluster_name: authz-opa endpoints: - lb_endpoints: - endpoint: address: socket_address: address: opa-service port_value: 9191
Envoy proxy のコンテナと OPA のコンテナは、コンテナ実行環境内のネットワークを利用するため、DNS 名をサービス名で指定することができます。
このための docker-compose.yaml
は次のとおりです。
version: "3" services: envoy: image: envoyproxy/envoy:v1.21-latest volumes: - ./front-envoy.yaml:/etc/front-envoy.yaml ports: - 8080:8080 - 9901:9901 command: ["-c", "/etc/front-envoy.yaml", "--service-cluster", "front-proxy"] opa-service: image: openpolicyagent/opa:latest-envoy volumes: - ./config.yaml:/work/config.yaml - ./opa.rego:/work/opa.rego command: ["run", "--server", "--log-level", "debug", "-c", "/work/config.yaml", "/work/opa.rego"]
OPA の構成ファイル (config.yaml
) も必要です。これは次のとおりです。
plugins: envoy_ext_authz_grpc: addr: :9191 decision_logs: console: true
ポリシー
OPA はデータとポリシーを分離しています。ポリシーは Rego と呼ばれるドメイン固有言語 (DSL) で定義します。この記事では、/index.html
へのリクエストのみを許可し、それ以外の場合は 403 Forbidden
のレスポンスを返すように定義します。また、/index.html
へのアクセスの場合には、JWT 認証トークンの cognito:username
をヘッダに追加するようにしています。ファイル opa.rego
は次のとおりです。
package envoy.authz import input.attributes.request.http as http_request default allow = false name = name { jwt_payload := http_request.headers["jwt-payload"] payload := json.unmarshal(base64url.decode(jwt_payload)) name := payload["cognito:username"] } allow = response { http_request.method == "GET" http_request.path == "/index.html" response := { "allowed": true, "headers": { "x-jwt-name": name } } }
実行
この設定による動作シーケンスの概要は図のようになります。正確に書く場合は、図中の jwks.json
の取得は実際には非同期に実行されキャッシュされることに注意してください。
以上のように定義した構成は、nerdctl または docker-compose で起動することができます。
nerdctl compose up
ここで説明した設定ファイル等は GitHub リポジトリにあります。