カテゴリー
Linux

Laravel 9 。 AWS S3 (オブジェクト所有者: ACL 無効、かつ、このバケットのブロックパブリックアクセス設定: パブリックアクセスをすべてブロック) を操作するときに AccessDenied と AccessControlListNotSupported に遭遇した時の解決方法と解決してから一通り操作方法を試した記録

はじめに

AWS S3 を今まで使ったことがありませんでした。テキトーに使い始めてみたら、何やらよくわからないエラーで手間取ってしまいました。そこで、基礎の部分から調べてみようと思い、本投稿を残します。

特に、なぜか S3 フォルダを作成できない問題を解消したかったのでした。

まとめ

遭遇したエラー達

AWS IAM ユーザー (アクセスキー IDとシークレットアクセスキーが紐づいているユーザーを指す) に権限が足りない場合

app@fe07f31e7473:/var/www/html/laravel$ php artisan tinker
Psy Shell v0.11.9 (PHP 8.2.0 — cli) by Justin Hileman
> $files = Storage::disk('s3')->allFiles('.');

   League\Flysystem\UnableToListContents  Unable to list contents for '.', deep listing

Reason: Error executing "ListObjectsV2" on "https://myawsbucket-test-s3-1.s3.ap-northeast-1.amazonaws.com?list-type=2&prefix="; AWS HTTP error: Client error: `GET https://myawsbucket-test-s3-1.s3.ap-northeast-1.amazonaws.com?list-type=2&prefix=` resulted in a `403 Forbidden` response:
<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>A69JFX (truncated...)
 AccessDenied (client): Access Denied - <?xml version="1.0" encoding="UTF-8"?>
<Error><Code>AccessDenied</Code><Message>Access Denied</Message><RequestId>A69JFX4HC5PKRTZ6</RequestId><HostId>fcQ91/Aw7Ed3FO+3TnijfjMGaLD4gKdNRNdjhXS5sPjGCOMh7fLumx8pUe1eBhY2bjJbRKRnHlE=</HostId></Error>.

> 

Laravel の設定が足りない場合

laravel/config/filesystems.php で 'visibility' => 'private', を追加しない場合に S3 フォルダを作成しようとした場合に発生したエラーです。

app@fe07f31e7473:/var/www/html/laravel$ php artisan tinker
Psy Shell v0.11.9 (PHP 8.2.0 — cli) by Justin Hileman
> Storage::disk('s3')->makeDirectory('dir');

   League\Flysystem\UnableToWriteFile  Unable to write file at location: dir/. Error executing "PutObject" on "https://myawsbucket-test-s3-1.s3.ap-northeast-1.amazonaws.com/dir/"; AWS HTTP error: Client error: `PUT https://myawsbucket-test-s3-1.s3.ap-northeast-1.amazonaws.com/dir/` 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>WMA9WF6B5G9D7740</RequestId><HostId>K+1NzKx5J/VFUQggExIvplHiguvK6S7napQ491sdXpkEHX1fd/O/RPoXX+3MlvsQVLc71PCw/tw=</HostId></Error>.

> 

S3 バケットを作成する

  1. バケットを作成 – S3 bucket にて、次の内容で作成した。
    • バケット名: myawsbucket-test-s3-1 - その他: デフォルトのまま
      • オブジェクト所有者: ACL 無効 (推奨)
      • このバケットのブロックパブリックアクセス設定: パブリックアクセスをすべてブロック

作成した S3 バケットにアクセス可能な IAM ポリシーとそのポリシーを持つ IAM ユーザーを作成する

ポリシーの作成 – IAM Management Console から IAM ポリシーを作成した。

  1. エディタの JSON で次を入力した。
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Effect": "Allow",
                "Action": [
                    "s3:*",
                    "s3-object-lambda:*"
                ],
                "Resource": [
                    "arn:aws:s3:::myawsbucket-test-s3-1",
                    "arn:aws:s3:::myawsbucket-test-s3-1/*"
                ]
            }
        ]
    }
    
    
  2. タグを追加では何もしなかった。
  3. ポリシーの確認で次を入力した。
    • 名前: AmazonS3FullAccessToMyawsbucketTestS31

参考としたのは次の arn:aws:iam::aws:policy/AmazonS3FullAccess です。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:*",
                "s3-object-lambda:*"
            ],
            "Resource": "*"
        }
    ]
}

IAM ユーザーを、ポリシーを作紐付けて成した。 ユーザーを追加 – IAM Management Console のページより。

  1. 詳細

    • ユーザー名: s3-test
    • AWS 認証情報タイプを選択: アクセスキー – プログラムによるアクセス
  2. アクセス権限

    • 既存のポリシーを直接アタッチ
      • AmazonS3FullAccessToMyawsbucketTestS31
  3. タグでは何も行わなかった。

  4. 確認では何も行わず、ユーザーを作成した。

  5. アクセスキー ID とシークレットアクセスキーをコピーし、控えた。

Laravel で設定する

S3ドライバ設定 – ファイルストレージ 9.x Laravel

composer require league/flysystem-aws-s3-v3 "^3.0"

laravel/.env

AWS_ACCESS_KEY_ID=IAM ユーザー作成時に得たアクセスキー ID
AWS_SECRET_ACCESS_KEY=IAM ユーザー作成時に得たシークレットアクセスキー
AWS_DEFAULT_REGION=ap-northeast-1
AWS_BUCKET=myawsbucket-test-s3-1
AWS_USE_PATH_STYLE_ENDPOINT=false

また、ディレクトリ作成でのエラーを解消するために、次の設定が必要となった。

laravel/config/filesystems.php で 'visibility' => 'private', を追加した。

        '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_USE_PATH_STYLE_ENDPOINT', false),
            'throw' => false,
            'visibility' => 'private',
        ],

Laravel tinker から S3 操作を試した時のコマンドまとめ

ファイルストレージ 9.x Laravel

php artisan tinker

// 設定を確認
Storage::disk('s3')->getConfig();



// ファイルの CRUD
// ファイルの保存
$contents = '🗒ファイルの中身の文章です。✏️';
Storage::disk('s3')->put('file1.txt', $contents);
Storage::disk('s3')->put('dir1/file2.txt', $contents);

// ファイルの取得
$contents = Storage::disk('s3')->get('file1.txt');
$contents = Storage::disk('s3')->get('dir1/file2.txt');

// ファイルの前後への追加
Storage::disk('s3')->prepend('file1.txt', 'Prepended Text');
$contents = Storage::disk('s3')->get('file1.txt');
Storage::disk('s3')->append('file1.txt', 'Appended Text');
$contents = Storage::disk('s3')->get('file1.txt');

// ファイルのコピーと移動
Storage::disk('s3')->copy('file1.txt', 'file3.txt');
Storage::disk('s3')->copy('file1.txt', 'dir2/file4.txt');
Storage::disk('s3')->move('dir2/file4.txt', 'file5.txt');
// ↑ファイル移動により S3 フォルダにファイルが無くなり、 S3 フォルダも無くなった。
// 存在しないディレクトリへ、ディレクトリ作成しない状態での移動
Storage::disk('s3')->move('file5.txt', 'dir3/file6.txt');

// ファイルの削除
// 他ファイルのないディレクトリは AWS ウェブコンソールから存在が消えた。
Storage::disk('s3')->delete('dir3/file6.txt');



// ディレクトリの CRUD
// ディレクトリ内のすべてのファイルを取得
$files = Storage::disk('s3')->allFiles('.');

// ディレクトリを作成する
// `400 Bad Request` `AccessControlListNotSupported` のエラーとなった。これは設定が足りないためで、 Laravel 設定ファイル `laravel/config/filesystems.php` にて `'visibility' => 'private',` を設定することで解消できた。
$directory = 'dir4';
Storage::disk('s3')->makeDirectory($directory);

// ディレクトリを削除する
Storage::disk('s3')->deleteDirectory($directory);
// 存在しない S3 フォルダを削除すると true が返ったが、元々存在しないフォルダなため何も変化しない
$directory = 'aaaaaaaaaaaaaaa';
Storage::disk('s3')->deleteDirectory($directory);
// ファイルの存在する S3 フォルダを削除すると含まれるファイルも込みでフォルダごと削除された。
$directory = 'dir1';
Storage::disk('s3')->deleteDirectory($directory);

Laravel tinker から S3 操作を試した時の実践結果

app@fe07f31e7473:/var/www/html/laravel$ php artisan tinker
Psy Shell v0.11.9 (PHP 8.2.0 — cli) by Justin Hileman
> // 設定を確認
> Storage::disk('s3')->getConfig();
= [
    "driver" => "s3",
    "key" => "アクセスキー ID",
    "secret" => "シークレットアクセスキー",
    "region" => "ap-northeast-1",
    "bucket" => "myawsbucket-test-s3-1",
    "url" => null,
    "endpoint" => null,
    "use_path_style_endpoint" => false,
    "throw" => false,
    "visibility" => "private",
    "version" => "latest",
    "credentials" => [
      "key" => "アクセスキー ID",
      "secret" => "シークレットアクセスキー",
    ],
  ]

> 
> 
> 
> // ファイルの CRUD
> // ファイルの保存
> $contents = '🗒ファイルの中身の文章です。✏️';
= "🗒ファイルの中身の文章です。✏️"

> Storage::disk('s3')->put('file1.txt', $contents);
= true

> Storage::disk('s3')->put('dir1/file2.txt', $contents);
= true

> 
> 
> 
> // ファイルの取得
> $contents = Storage::disk('s3')->get('file1.txt');
= "🗒ファイルの中身の文章です。✏️"

> $contents = Storage::disk('s3')->get('dir1/file2.txt');
= "🗒ファイルの中身の文章です。✏️"

> 
> 
> 
> // ファイルの前後への追加
> Storage::disk('s3')->prepend('file1.txt', 'Prepended Text');
= true

> $contents = Storage::disk('s3')->get('file1.txt');
= """
  Prepended Text\n
  🗒ファイルの中身の文章です。✏️
  """

> Storage::disk('s3')->append('file1.txt', 'Appended Text');
= true

> $contents = Storage::disk('s3')->get('file1.txt');
= """
  Prepended Text\n
  🗒ファイルの中身の文章です。✏️\n
  Appended Text
  """

> 
> 
> 
> // ファイルのコピーと移動
> Storage::disk('s3')->copy('file1.txt', 'file3.txt');
= true

> Storage::disk('s3')->copy('file1.txt', 'dir2/file4.txt');
= true

> Storage::disk('s3')->move('dir2/file4.txt', 'file5.txt');
= true

> // ↑ファイル移動により S3 フォルダにファイルが無くなり、 S3 フォルダも無くなった。
> Storage::disk('s3')->move('file5.txt', 'dir3/file6.txt');
= true

> 
> 
> 
> // ファイルの削除
> // 他ファイルのないディレクトリは AWS ウェブコンソールから存在が消えた。
> Storage::disk('s3')->delete('dir3/file6.txt');
= true

> 
> 
> 
> 
> 
> 
> // ディレクトリの CRUD
> // ディレクトリ内のすべてのファイルを取得
> $files = Storage::disk('s3')->allFiles('.');
= [
    "dir1/file2.txt",
    "file1.txt",
    "file3.txt",
  ]

> 
> 
> 
> // ディレクトリを作成する
> // `400 Bad Request` `AccessControlListNotSupported` のエラーとなった。これは設定が足りないためで、 Laravel 設定ファイル `laravel/config/filesystems.php` にて `'visibility' => 'private',` を設定することで解消できた。
> $directory = 'dir4';
= "dir4"

> Storage::disk('s3')->makeDirectory($directory);
= true

> 
> 
> 
> // ディレクトリを削除する
> Storage::disk('s3')->deleteDirectory($directory);
= true

> // 存在しない S3 フォルダを削除すると true が返ったが、元々存在しないフォルダなため何も変化しない
> $directory = 'aaaaaaaaaaaaaaa';
= "aaaaaaaaaaaaaaa"

> Storage::disk('s3')->deleteDirectory($directory);
= true

> // ファイルの存在する S3 フォルダを削除すると含まれるファイルも込みでフォルダごと削除された。
> $directory = 'dir1';
= "dir1"

> Storage::disk('s3')->deleteDirectory($directory);
= true

> 

また、実践を通して残った全てのファイルを削除しました。

> $files = Storage::disk('s3')->allFiles('.');
= [
    "file1.txt",
    "file3.txt",
  ]

> Storage::disk('s3')->delete($files);
= true

> 


   INFO  Ctrl+D.

app@fe07f31e7473:/var/www/html/laravel$ 

おわりに

今回知りたかったことは、調べてみても最初は 公式ドキュメント をコピペしたようなページしか見当たらなかったため、自らやってみた、という流れとなりました。

自分でやってみて、その上でさらに調べてみるとヒントや答えが見つかったりしました。面白いものだなと思います。

以上です。

コメントを残す