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 を呼ぶようなものまであります。

参考

#
豆蔵デベロッパーサイト - 先週のアクセスランキング
  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)