CloudFrontのS3 Originにはhostヘッダーを転送してはいけない

SREチームの長田です。 CloudFrontでstaticなファイルを配信しようとした時に少々ハマったのでそのメモです。

TL;DR

CloudFront distributionのS3 Originは、Managed Policyの Managed-AllViewer を使うと機能しない。

何をしたのか

statcなファイルを配信する要件があり、その対応としてCloudFront distributionのOriginにS3を設定しました。 CloudFrontのOrigin Access Identity(OAI)を作成し、S3 bucket policyにはこのOAIからのAPIリクエストのみを受け付けるよう設定しました。

docs.aws.amazon.com

いざ配信用URLにHTTPリクエストすると以下のようなエラーになってしまいました。

<Error>
  <Code>SignatureDoesNotMatch</Code>
  <Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>
  <AWSAccessKeyId>AKIA***</AWSAccessKeyId>
  ...
</Error>

何が起こっていたのか

Origin request policyとして、Managed Policyの Managed-AllViewer を使用していました。

docs.aws.amazon.com

このPolicyはすべてのリクエストヘッダーをOriginに転送します。 Originに何らかのアプリケーションがあり、リクエストヘッダーを参照する場合はこのPolicyを使うことになるでしょう。

OriginにS3を設定した場合、S3バケットの特定にはhostヘッダーが使われます。 Managed-AllViewer を使用すると CloudFrontからS3へAPIリクエストする際のhostヘッダーが、クライアントリクエストのhostヘッダーで上書きされてしまいます 結果、S3バケットが見つからず、上記のようなエラーが発生するというわけです。

ドキュメントにもちゃんと記載があります。

docs.aws.amazon.com

それ以外の場合、リクエストのバケットは Host ヘッダーの小文字の値、リクエストのキーはリクエスト URI になります。

host ヘッダーの他にも、 authorization ヘッダーなどS3が使用するヘッダーを上書きしてしまうと同様のエラーになってしまいます。

ハマりどころ

エラーメッセージとして表示される SignatureDoesNotMatch で検索すると、

docs.aws.amazon.com

AWSCLIの不具合issueや、

github.com

これを見て、「CloudFrontが持っているアクセスキーに不備があるのか・・・?」とOrigin Access Identityを作り直したり、 別のCloudFront distributionを用意したりしたものの解決せず、というのを繰り返してしまったわけです。

ちなみに、調査のためOAIを使用せずS3 bucket policyに Principal:"*" を設定した場合は以下のようなエラーになります。

404 Not Found
- Code: NoSuchBcket
- Message: The specified bucket dose not exist
- BucketName: foo.exmaple.com
...

BucketName の部分がクライアントからCloudFrontにリクエストした場合のドメイン名になっています。 これが解決のヒントになりました。

おわり

仕組みがわかっていれば簡単に解決できるところですが、 エラーメッセージに踊らされてだいぶ時間をかけてしまいました。 仕組みを知っているというのは大事ですね。

同じようなハマり方をしている人の助けになれば幸いです。

カヤックでは仕組みから理解したいエンジニアを募集しています!