AWS

【Laravel】S3とプライベート接続時にAccessControlListNotSupportedエラーになる場合の解決法【AWS】

どうも、katです。

今回は、LaravelS3をプライベートに接続する際に、「AccessControlListNotSupported」エラーとなった際に解決した方法について書いていこうと思います。

検索すると色々な情報が出てきますが、プライベート接続を維持しつつ、エラーを解決する場合は今回の方法が有効かと思いますので、試してみていただけたらと思います。

エラーが起きた状況は?

まず、今回のエラーが起きた状況についてですが、AWSのEC2インスタンスにLaravelを構築し、そこからS3に対して接続する際におきました。

S3は、下記の通り、パブリックアクセスは全てブロックしています。

S3のブロックパブリックアクセス編集画面

EC2からS3へは、VPCのインターフェースタイプのエンドポイント(Private Link)を使用し、下記のように.envを設定して接続しようとしていました。

AWS_ENDPOINT={S3用のPriveteLink}
AWS_DEFAULT_REGION={AWSのリージョン}
AWS_BUCKET={S3のバケット名}
AWS_PATH_STYLE_ENDPOINT=true

しかし、いざS3にファイルを送信しようとすると、下記のエラーが発生しました。

[previous exception] [object] (Aws\\S3\\Exception\\S3Exception(code: 0): Error executing \"PutObject\" on \"https://bucket.vpce-xxxxxxxxxxxxxx.s3.xxxxxxx.vpce.amazonaws.com/xxxx/file/path/\"; AWS HTTP error: Client error: `PUT https://bucket.vpce-xxxxxxxxxxxxxx.s3.xxxxxxx.vpce.amazonaws.com/xxxx/file/path/` resulted in a `400 Bad Request` response:
<?xml version=\"1.0\" encoding=\"UTF-8\"?>
<Error><Code>AccessControlListNotSupported</Code><Message>The bucket does not all (truncated...)
AccessControlListNotSupported (client): The bucket does not allow ACLs - <?xml version=\"1.0\" encoding=\"UTF-8\"?>
<Error><Code>AccessControlListNotSupported</Code><Message>The bucket does not allow ACLs</Message><RequestId>xxxxxxxxxxxxx</RequestId><HostId>xxxxxxxxxxxxx</HostId></Error>

原因は?

上記のエラー内容で検索したところ、AWSのACL(AccessControllList)周りが原因で出るエラーのようで、解決策としては、下記のように、S3のオブジェクト所有者の編集画面で、ACL有効にしてやるとうまくいくという記事が散見されました。

S3のオブジェクト所有者編集画面

試しに上記の通りやってみましたが、AccessDeniedエラーとなってしまい、なぜだろうと思っていましたが、恐らく上記は、パブリック接続(S3のブロックパブリックアクセス編集画面のチェックを外す)の場合にのみ有効な解決方法なのではと思います。

今回はプライベートな接続環境なので、上記の方法では解決できず、悩みましたが、試しにベタのPHPでcurlを使い、PrivateLinkに対してファイル送信してみたところ、うまくいきました。

なので、AWSではなく、Laravel側の設定に問題があるのでは?と疑い始めました。

色々と調べていくうちに、Laravelが読み込んでいるvendor/league/flysystem-aws-s3-v3というモジュールが怪しいと思い始めました。

下記は上記モジュール内のAwsS3V3Adapter.phpというファイルの194行目あたりのコードですが、

    private function upload(string $path, $body, Config $config): void
    {
        $key = $this->prefixer->prefixPath($path);
        $options = $this->createOptionsFromConfig($config);
        $acl = $options['params']['ACL'] ?? $this->determineAcl($config);
       ・
       ・
       ・

私の環境では、この$aclという変数に‘public-read’という値が入って来ていました。

これはつまり、アップロードしようとしているファイルを、パブリックアクセス可能な状態でアップロードするという意味のようですが、今回はそもそもパブリックアクセスをブロックしており、ACLも無効にしているため、エラーになっていたようです。

解決策

最終的に、下記のようにconfig/filesystems.phpvisibilityの設定を追加することで解決できました。

        's3' => [
            'driver' => 's3',
            'key' => env('AWS_ACCESS_KEY_ID'),
            'secret' => env('AWS_SECRET_ACCESS_KEY'),
            'region' => env('AWS_DEFAULT_REGION'),
            'bucket' => env('AWS_BUCKET'),
            'url' => env('AWS_URL'),
            'endpoint' => env('AWS_ENDPOINT'),
            'use_path_style_endpoint' => env('AWS_PATH_STYLE_ENDPOINT', false),
            'visibility' => 'private',
        ],

ここでvisibilityにprivateを設定することで、前述の$aclに’private’が渡され、オブジェクトはプライベートな接続しかできない状態で無事にアップロードできました!

最後に

いかがだったでしょうか?

おそらくパブリックなアクセスであれば、今回のエラーの解決策は検索すればすぐに出てくるのですが、プライベートな接続の場合となるとあまり情報がなく、結構原因調査に時間がかかりました。

結果としては、無事に接続ができて良かったです。

今回の記事が、同じようにお困りの方の参考になれば幸いです。

以上、「S3とプライベート接続時にAccessControlListNotSupportedエラーになる場合の解決法」でした〜

ABOUT ME
kat
プログラマー歴7年、2歳の子供を持つパパです。 興味のあることはプログラミングや今後のIT技術などです。 趣味でオンラインカードゲームのサイトを運営しております。 プログラミングを通して社会に貢献していきたいです。