🗝︎

[ECS] EnvoyをエッジプロキシとしてAWSサービスへ接続する方法(2)

2022/04/01に公開

概要

ECSで起動したEnvoyコンテナをエッジプロキシとして使用しAWSサービスへアクセスする方法を整理します
この記事ではCognitoで取得したアクセストークンの検証をします

前回の記事の環境を使用します

コンテナの作成

サンプルコードはこちらにあります
https://github.com/tubame0505/envoy_aws_request_signing_sample/tree/main/docker_jwt

Port10000がS3サービスのリスナー
Port10001がApi Gatewayサービスのリスナーです

S3の設定を抜粋します
JwtAuthenticationフィルターで署名, 有効期限, issを検証し、"payload_in_metadata: jwt-payload"にてDynamicMetadataに情報を保存します
この情報を参照し、Luaフィルターでclient_id, scopeを検証します

https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/jwt_authn_filter
https://www.envoyproxy.io/docs/envoy/latest/configuration/http/http_filters/lua_filter

  • envoy設定
          http_filters:
          - name: envoy.filters.http.jwt_authn
            typed_config:
              '@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication
              providers:
                cognito:
                  issuer: "${JWT_ISSUER}"
                  forward_payload_header: jwt-payload
                  payload_in_metadata: jwt-payload
                  remote_jwks:
                    http_uri:
                      uri: "${JWT_JWK_URI}"
                      cluster: jwks
                      timeout: 5s
                    cache_duration: 600s
              rules:
              - match:
                  prefix: '/'
                requires:
                  provider_name: cognito
          - name: envoy.filters.http.lua
            typed_config:
              '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
              inline_code: |
                -- Called on the request path.
                function envoy_on_request(request_handle)
                  -- Do something.
                  local jwt_tokenuse = request_handle:streamInfo():dynamicMetadata():get('envoy.filters.http.jwt_authn')['jwt-payload']['token_use']
                  local jwt_client = request_handle:streamInfo():dynamicMetadata():get('envoy.filters.http.jwt_authn')['jwt-payload']['client_id']
                  local jwt_scope = request_handle:streamInfo():dynamicMetadata():get('envoy.filters.http.jwt_authn')['jwt-payload']['scope']
                  if jwt_tokenuse == nil or jwt_tokenuse ~= 'access' then
                    request_handle:respond({[':status'] = '401'}, 'unauthorized_tokenuse_error')
                  end
                  if jwt_client == nil or jwt_client ~= '"${JWT_CLIENT}"' then
                    request_handle:respond({[':status'] = '401'}, 'unauthorized_client_error')
                  end
                  if jwt_scope == nil or jwt_scope ~= 's3/access' then
                    request_handle:respond({[':status'] = '401'}, 'unauthorized_scope_error')
                  end
                end

JwtAuthenticationフィルタが検証する内容

フィルタが検証する内容は次のコードで実装されています
https://github.com/google/jwt_verify_lib/blob/e5d6cf7067495b0868787e1fd1e75cef3242a840/src/verify.cc#L210

Cognitoが推奨する検証項目は次のリンク先に記載されています
https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-verifying-a-jwt.html

Filterが検証する項目

  • kidがjwkに存在するかどうか
  • algの一致
  • 署名検証
  • issuer
  • exp (有効期限)

ユーザが追加で検証すべき項目

  • token_use
  • scope
  • client_id

コンテナの実行

ECSを使わずローカルで起動して動作確認ができます

export AWS_ACCESS_KEY_ID=xxxxxx
export AWS_SECRET_ACCESS_KEY=xxxxxx
export AWS_S3_ENDPOINT=samplebktenvoytest.s3.ap-northeast-1.amazonaws.com
export AWS_APIGW_ENDPOINT=apigwid.execute-api.ap-northeast-1.amazonaws.com
export AWS_REGION=ap-northeast-1
export JWT_ISSUER=https://cognito-idp.ap-northeast-1.amazonaws.com/userpoolid
export JWT_CLIENT=client_id
export JWT_JWK_URI=https://cognito-idp.ap-northeast-1.amazonaws.com/userpoolid/.well-known/jwks.json
export JWT_ISSUER_HOST=cognito-idp.ap-northeast-1.amazonaws.com

docker build -t envoy:v1 .
docker run --rm --name envoy -p 9901:9901 -p 10000:10000 -p 10001:10001 -e AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID -e AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY -e AWS_S3_ENDPOINT=$AWS_S3_ENDPOINT -e AWS_REGION=$AWS_REGION -e AWS_APIGW_ENDPOINT=$AWS_APIGW_ENDPOINT -e JWT_ISSUER=$JWT_ISSUER -e JWT_CLIENT=$JWT_CLIENT -e JWT_JWK_URI=$JWT_JWK_URI -e JWT_ISSUER_HOST=$JWT_ISSUER_HOST envoy:v1

別のシェルで接続確認をします

curl -H 'Authorization: Bearer accesstoken_string' http://localhost:10000/test.txt

curl -H 'Authorization: Bearer accesstoken_string' -H 'Content-type: application/json' -d '{"Name": "Jone"}' http://localhost:10001/proxy-test

後続記事ではSSL設定について記載します

関連記事

参考リンク

Discussion