S3 の静的 Web サイトをセキュアに Envoy でホスティング

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

庄司です。

モダンな UI のフレームワークは静的なコンテンツとしてパッケージングされることが主流となっています。これは変化の激しい UI とそれと比較すれば比較的変化のスピードが遅くてよいバックエンドとの関係で理にかなっています。UI のこうした静的なコンテンツの配信では多くの SaaS 製品で CloudFront のような CDN が利用されています。しかし、エンタープライズで利用されるフロントエンドの場合には VPN を通じたアクセスのみを許可している場合が多くあります。このような場合にまで世界中の多数のユーザに低レイテンシーな配信を目的としている CDN を利用することは本来の用途外であるというだけでなく、セキュリティ要件のために CDN の利点を失わせる方向で無用な修正やオーバーヘッドを加えることにもつながります。

2022年2月16日の前回の記事「S3 の静的 Web サイトを Envoy でホスティング」では、説明を簡略化するために S3 バケットのパブリックアクセスを有効にして Envoy proxy を使った S3 上の静的コンテンツのホスティングについて説明しました。

この記事でも、本来はパブリックアクセスを許可せず、VPC 内に閉じて運用すると説明したものの、そのための方法について踏み込むことはしませんでした。

今回は VPC 内で Envoy proxy のコンテナイメージを AWS Fargate で実行するようにし、Amazon S3 へのアクセスに AWS PrivateLink の VPC エンドポイント (Gateway) を利用することで通信をインターネットに公開しない構成で説明します。

ブラウザから VPC 内のこれらリソースにアクセスするため Application Load Balancer を利用することとします。

これらから、構成は図のようになります。

このように構成した場合、S3 バケットへのアクセスは他の AWS サービスの API を利用する場合と同様になります。つまり、Fargate の TaskRole など IAM を使ったアクセスポリシーが有効になります。

S3 バケットへのアクセスには、IAM の認証情報を使った署名が必要になりますが、Envoy proxy には AWS Request Signing という、署名をするフィルターが含まれています。

このフィルターを利用する設定例は次のようになります。

  - 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: ${BUCKET_URL}

前の記事では、ホスト名のリライトもルート設定で行っていましたが、署名時にリライト後のホスト名が必要なため、ルート設定で行っていたリライトはやめて、署名フィルターでリライトするようにしています。

また、インデックスアクセス (URL の末尾が '/' となるアクセス) のパスもルート (route) 設定で regex_rewrite を使って index.html にリライトしていました。ただし、そうすると、署名時のパス (path) と実際の S3 バケットのパスが異なることになり、署名の検証に失敗してしまいます。

これを回避するため、ルート設定での regex_rewrite の使用をやめて、署名フィルターの前にパスをリライトするために Lua スクリプトを使うフィルターを追加しました。

  - name: envoy.filters.http.lua
    typed_config:
      '@type': type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
      inline_code: |
        function envoy_on_request(request_handle)
          path = request_handle:headers():get(":path"):gsub("/$", "/index.html")
          request_handle:headers():replace(":path", path)
        end

ここで紹介した設定ファイル、Fargate 上で実行するためのコンテナイメージ用の Dockerfile、図の構成を構築する Cloudformation テンプレート 等の全体は GitHub リポジトリ にあります。

また、ローカル PC にインストールした Rancher Desktop (または k3s) の Traefik から S3 の静的コンテンツにアクセスする構成も GitHub リポジトリ にあります。Ingress を除けば、Amazon ECSAmazon EKS にかえる場合に適用できます。

まとめ

#

この一連の記事では、Envoy proxy を静的に設定していますが、AWS App MeshIstio などのサービスメッシュ製品が持つコントロールプレーンによる構成やファイルを使用する動的構成 を採用することもできます。このような動的な構成 (configuration) を採用することで、Envoy proxy の再起動を必要とせず、設定変更を反映させることができます (Feature Toggle、A/B テスト、Blue/Green デプロイ、カナリアなどに対応できることを意味します)。

Envoy proxy にはさまざまな機能があります。フィルターのページだけをみても、AWS Lambda を呼ぶようなものまであります。

参考

#

豆蔵では共に高め合う仲間を募集しています!

recruit

具体的な採用情報はこちらからご覧いただけます。