🗝︎

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

2022/04/01に公開

概要

ECSで起動したEnvoyコンテナをエッジプロキシとして使用しAWSサービスへアクセスする方法を整理します
コンテナに割り当てたIAMロールを使用してAWSサービスへ接続します
設定はV3 APIを使用します
なお、Emvoy初心者のため設定が一部おかしい可能性があります

テスト環境

テスト対象として
S3オブジェクトのGET/PUT
API Gatewayへの接続の確認をします

S3

samplebktenvoytestという名前のバケットを作成しました
読み出しテスト用にtest.txtというファイルをアップロードしました

https://samplebktenvoytest.s3.ap-northeast-1.amazonaws.com/test.txt   

でアクセスできますが、認証が必要です

Lambda関数の作成

プロキシからアクセスするためのLambda関数を作成します

関数名: proxy-test
ランタイム: Node.js 14.x
アーキテクチャ x86_64

index.jsを次のように変更します
入力データをログに記録し、そのまま返す関数です。

console.log('Loading function');

exports.handler = async (event, context) => {
    let body = event.body;
    let headers = event.headers;
    let pathparams = event.pathParameters;
    let queryparams = event.queryStringParameters;
    let statusCode = '200';
    console.log(body);
    console.log(headers);
    console.log(pathparams);
    console.log(queryparams);
    
    return {
        statusCode,
        body,
        headers,
    };
};

API Gateway

今作成したLambda関数へ接続するAPI Gatewayを作成します
HTTP APIでproxy-test-apiというAPIを作成します
ルートはパスパラメータ有りと無しの2種類作成しました
ステージはデフォルト設定です
IAM認証によるアクセス制限をかけておきます。

https://apigwid.execute-api.ap-northeast-1.amazonaws.com/proxy-test  
https://apigwid.execute-api.ap-northeast-1.amazonaws.com/proxy-test/foo/bar  

でアクセスできますが、認証が必要です

コンテナの作成

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

Port10000がS3サービスのリスナー
Port10001がApi Gatewayサービスのリスナーです
パスで切り替えたかったのですが署名のフィルターがうまく動作しませんでしたので個別のリスナーとしています

S3の設定を抜粋します
AWS Request Signingフィルターを使用して署名を生成します

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

service_nameにはサービスURIのリージョンの前にある箇所を記載します
s3は"s3"、Api Gatewayは"execute-api"です
AWSリクエスト署名は環境変数 AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEYが設定されていればこの値から、無ければコンテナに割り当てられた実行ロールから生成されます。

  • envoy設定
static_resources:
  listeners:
  - name: listener_s3
    address:
      socket_address:
        protocol: TCP
        address: 0.0.0.0
        port_value: 10000
    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
          scheme_header_transformation:
            scheme_to_overwrite: https
          stat_prefix: ingress_http
          route_config:
            name: s3_route
            virtual_hosts:
            - name: s3_web
              domains: ['*']
              routes:
              - match:
                  prefix: '/'
                route:
                  cluster: s3
          http_filters:
          - name: envoy.filters.http.aws_request_signing
            typed_config:
              '@type': type.googleapis.com/envoy.extensions.filters.http.aws_request_signing.v3.AwsRequestSigning
              service_name: s3
              region: "${AWS_REGION}"
              use_unsigned_payload: true
              host_rewrite: "${AWS_S3_ENDPOINT}"
          - name: envoy.filters.http.router
  clusters:
  - name: s3
    connect_timeout: 30s
    type: LOGICAL_DNS
    # Comment out the following line to test on v6 networks
    dns_lookup_family: V4_ONLY
    lb_policy: ROUND_ROBIN
    load_assignment:
      cluster_name: s3
      endpoints:
      - lb_endpoints:
        - endpoint:
            address:
              socket_address:
                address: "${AWS_S3_ENDPOINT}"
                port_value: 443
    transport_socket:
      name: envoy.transport_sockets.tls

コンテナの実行

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

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 envoy:v1

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

curl  http://localhost:10000/test.txt
echo "This is test2 file." > test2.txt
curl -X PUT --data-binary @test2.txt http://localhost:10000/test2.txt

curl -H 'Content-type: application/json' -d '{"Name": "Jone"}' http://localhost:10001/proxy-test
curl -H 'Content-type: application/json' -d '{"Name": "Jone"}' http://localhost:10001/proxy-test/param1/param2
curl -H 'Content-type: application/json' -d '{"Name": "Jone"}' 'http://localhost:10001/proxy-test/param1/param2?qparam1=abc&qparam2=def'

認証情報はEnvoyが生成するためURIのみで接続できます
Lambdaのログを確認すると、API Gatewayへ渡したパスパラメータ、クエリパラメータが記録されていました

ECSで起動する場合は、環境変数AWS_S3_ENDPOINT, AWS_APIGW_ENDPOINT, AWS_REGIONだけ設定すればよいです

後続記事ではCognitoを使用したアクセス制限、SSL設定について記載します

関連記事

参考リンク

Discussion