CloudFront経由のS3のアクセスで404 NotFoundが返却される
初めに
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)があるから...
と諦めてしまうケースもあるかと思いますので本記事がそういった方の助けになればと思います。