CloudFront経由のS3のアクセスで404 NotFoundが返却される

2023.05.15

初めに

CloudFrontを経由してS3にアクセスのコンテンツにアクセスを行う場合、 バケットポリシー等でCloudFrontからバケットに対してs3:ListBucketの権限がないと存在しないファイルへのアクセスは404 NotFoundではなく403 Forbiddenが発生する仕様となっております。

OACのドキュメントではこの記載はないので、特に気にならなければドキュメント通りに設定しs3:ListBucketの許可がなしの場合は多いのではないかと思います。

今回OAC側のドキュメントに従いs3:GetObjectのみを付与しs3:ListBucketを付与していない状態において、オブジェクトの有無に関わらずCloudFrontから404 NotFoundが返却され、原因の特定の際に混乱したためご紹介します。

<Error>
<Code>NotFound</Code>
<Message>The resource you requested does not exist</Message>
<RequestId>xxxxx</RequestId>
<HostId>xxxxx</HostId>
</Error>

原因まとめ

単一ではなく複合的な要因で発生となりました。

  • オリジンリクエストポリシーでS3にHostヘッダを渡してしまっている
  • OACの設定で署名が無効になってしまっている

S3へのアクセスにおけるHostヘッダ

バケットの仮想ホスティング
Amazon S3 REST API リクエストでサイトを区別する方法の 1 つとして、単なる URI のパス名部分ではなく、リクエスト URI の明確なホスト名を使用します。(中略)代わりに、Amazon S3 仮想ホスティングでは、HTTP Host ヘッダーを使用することで、REST API コールでバケットを指定することができます。

S3にHTTPSでアクセスを行う場合、どのバケットにアクセスが行われるかはCloudFrontへのアクセスのようにホストヘッダを元に決定されます。

今回404 NotFoundが発生した際はオリジンリクエストポリシーとしてマネージドポリシーであるAllViewerAndCloudFrontHeaders-2022-06を設定してしまっていたため、S3に対してCloudFrontアクセスの際のHostヘッダが渡り、存在しないバケットへのアクセス扱いとなってしまいました。

試しにHostヘッダをCloudFrontのもので指定し直接S3にアクセスをかけるとエラーメッセージから存在しないバケットにアクセスしていることが確認できます。

$ curl 'https://xxxxx.s3.ap-northeast-1.amazonaws.com/xxxx.jpeg' -H "Host: xxxxx.cloudfront.net"
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>NoSuchBucket</Code><Message>The specified bucket does not exist</Message><BucketName>xxxxx.cloudfront.net</BucketName><RequestId>xxxxx</RequestId><HostId>xxxxx</HostId></Error>

$ curl 'https://xxxxx.s3.ap-northeast-1.amazonaws.com/xxxx.jpeg' -H "Host: xxxxx.cloudfront.net" -I
HTTP/1.1 404 Not Found
...
Content-Type: application/xml
Date: Mon, 15 May 2023 05:45:46 GMT
Server: AmazonS3

CloudFrontを経由している場合「初めに」に記載しているXMLのようにただのNotFoundとなってしまい、かつアクセス想定のS3側のアクセスログの出力もないためエラーからの特定は手こずるポイントになりそうです。

署名ができている場合は別のエラーになる

なお上記の現象はCloudFrontからS3のアクセスが署名無し発生に現象であり、S3アクセスの際にOAC等で署名されている場合はSignatureDoesNotMatchエラーが返却されます。

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

SignatureDoesNotMatchは署名の誤り(内容に限らず権限が不足等も含む)等の場合に出力されるエラーとなりますが、バケットの存在有無よりこちらの優先優先度が高いようです。

先ほどNoSuchBucketが発生したcurlのURLを同ファイルでs3 presignコマンドで署名を行ったものに差し替えて実行しましたがSignatureDoesNotMatchが得られました。

$ curl 'https://xxxxx.s3.ap-northeast-1.amazonaws.com/xxxx.jpeg?...' -H "Host: xxxxx.cloudfront.net"
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code><Message>The request signature we calculated does not match the signature you provided. Check your key and signing method.</Message>...

$ curl 'https://xxxxx.s3.ap-northeast-1.amazonaws.com/xxxx.jpeg?...' -H "Host: xxxxxx.cloudfront.net" -I
HTTP/1.1 403 Forbidden
...
Content-Type: application/xml
Date: Mon, 15 May 2023 07:49:01 GMT
Server: AmazonS3

終わりに

今回はCloudFront+S3で設定に問題がある場合に403 Forbiddenではなく404 NotFoundが出る1例を紹介させていただきました。

初歩的なミスで出会いそうな現象ですが、調べてみると意外と同じような現象と解決とまだ見当たりませんでした。

もしかしたらOAIの場合は署名有無という選択肢自体がないためSignatureDoesNotMatchは発生するものの、今回のようにNotFoundというのは起きないためなのかもしれません。
(OAC自体リリースが2022年8月頃の機能で、執筆時点で1年未満の機能)

現在はOAC推奨でとなっておりますが、調べてパッと出てない現象に出会い代替方式(OAI)があるから...
と諦めてしまうケースもあるかと思いますので本記事がそういった方の助けになればと思います。