どうも、katです。
今回は、LaravelとS3をプライベートに接続する際に、「AccessControlListNotSupported」エラーとなった際に解決した方法について書いていこうと思います。
検索すると色々な情報が出てきますが、プライベート接続を維持しつつ、エラーを解決する場合は今回の方法が有効かと思いますので、試してみていただけたらと思います。
エラーが起きた状況は?
まず、今回のエラーが起きた状況についてですが、AWSのEC2インスタンスにLaravelを構築し、そこから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有効にしてやるとうまくいくという記事が散見されました。
試しに上記の通りやってみましたが、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.phpにvisibilityの設定を追加することで解決できました。
'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エラーになる場合の解決法」でした〜