※ 事前に AWS CLI version 2 以降をインストールしておくこと。
Amazon S3 へのアクセスを許可する AWS Identity and Access Management (IAM) プロファイルロールを作成します。
IAM インスタンスプロファイルをインスタンスにアタッチします。
S3 バケットのアクセス許可を検証します。
EC2 インスタンスから Amazon S3 へのネットワーク接続を検証します。
S3 バケットへのアクセスを検証します。
以下はEC2にアタッチしたロールのポリシー例:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Stmt1664856193519",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:PutObject"
],
"Effect": "Allow",
"Resource": [
"arn:aws:s3:::your-bucket-name",
"arn:aws:s3:::your-bucket-name/*"
]
}
]
}
以下はS3バケットポリシーの例:
{
"Version": "2012-10-17",
"Id": "Policy1665296051262",
"Statement": [
{
"Sid": "PublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:GetObject",
"s3:GetObjectVersion"
],
"Resource": "arn:aws:s3:::your-bucket-name/*"
},
{
"Sid": "EC2ServiceRoleWrite",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::your-ec2-attached-iam-role"
},
"Action": [
"s3:GetObject",
"s3:GetObjectVersion",
"s3:ListBucket",
"s3:ListBucketVersions",
"s3:PutObject"
],
"Resource": [
"arn:aws:s3:::your-bucket-name",
"arn:aws:s3:::your-bucket-name/*"
]
}
]
}
バケットポリシーにはPrincipal
にEC2インスタンスプロファイルのロールのみを指定している。
IAM ロールにおけるプリンシパルとは、「誰がこのロールを引き受けることができるか?」を指定するものだが、S3バケットポリシーにおけるプリンシパルとは、AWS公式ドキュメントのS3ユーザーガイドによれば以下のように説明されている:
リソースへのアクセスを許可または拒否するユーザー、アカウント、サービス、または他のエンティティを指定します。
また、同じくユーザーガイドは、インスタンスプロファイルは「IAM ロールを納めるコンテナであり、インスタンスの起動時に EC2 インスタンスにロール情報を渡す役割をしている」と説明している。
以下導入の手順は AssetSync/asset_sync github リポジトリの README.md を参照する。
In your Gemfile:
gem "asset_sync"
gem "fog-aws"
$ bundle config set --local without 'test development' # 既に設定してあればスキップ
$ bundle install
#config/environments/production.rb
config.action_controller.asset_host = "//#{Rails.application.credentials.aws[:fog_directory]}.s3.amazonaws.com"
ちなみにRubyでは二重引用符""
の中で式展開が効き、一重引用符''
の中では式展開が効かないので要注意。
$ RAILS_ENV=production bundle exec rails g asset_sync:install --provider=AWS
#=> create config/initializers/asset_sync.rb
$ EDITOR=vim rails credentials:edit
#config/credentials.yml.enc
aws:
access_key_id: 123
secret_access_key: 345
fog_directory: your-s3-bucket-name
In your config/initializers/asset_sync.rb:
if defined?(AssetSync)
AssetSync.configure do |config|
config.fog_provider = 'AWS'
config.aws_access_key_id = Rails.application.credentials.aws[:access_key_id]
config.aws_secret_access_key = Rails.application.credentials.aws[:secret_access_key]
config.aws_session_token = ENV['AWS_SESSION_TOKEN'] if ENV.key?('AWS_SESSION_TOKEN')
# Disable automatic run on precompile in order to attach to webpacker rake task
config.run_on_precompile = false
# Use IAM roles
config.aws_iam_roles = true
# Change canned ACL of uploaded object. Default is unset. Will override fog_public if set.
# Choose from: private | public-read | public-read-write | aws-exec-read |
# authenticated-read | bucket-owner-read | bucket-owner-full-control
config.aws_acl = "bucket-owner-full-control"
# Use http instead of https. Default should be "https" (at least for fog-aws)
# config.fog_scheme = "http"
config.fog_directory = Rails.application.credentials.aws[:fog_directory]
# Increase upload performance by configuring your region
config.fog_region = 'ap-northeast-1'
# Set `public` option when uploading file depending on value,
# Setting to "default" makes asset sync skip setting the option
# Possible values: true, false, "default" (default: true)
config.fog_public = true
end
end
Create lib/tasks/asset_sync.rake:
if defined?(AssetSync)
Rake::Task['webpacker:compile'].enhance do
Rake::Task["assets:sync"].invoke
end
end
ちなみに webpacker で静的アセット・CSS・JSファイルをバンドルさせているので、AssetSync/asset_sync の README.md 該当部分に倣って設定を追記している。
また、config/webpacker.yml で以下のように webpack でバンドルさせているファイルのコンパイル後の出力先を public/assets/packs 以下に変更しているので、README.md からは一部記述を省略している(『asset_syncの設定を見直してデプロイ時間を7分半削減した話』を参照)
#config/webpacker.yml
public_output_path: assets/packs
# デフォルトの出力先は public/packs 配下になる
プリコンパイル後にS3バケットに期待するファイルパスでアセットがアップロードされていればOK。
$ bundle exec rails assets:precompile assets:clean RAILS_ENV=production
S3の権限に地獄を見せられたので以下実際に遭遇したエラーとその対処法を記述する(初歩的なミスを許すな)
オブジェクト所有権のバケット所有者強制設定を適用すると、ACL は無効になります。ACL の設定または ACL の更新の要求は 400 エラーで失敗し、AccessControlListNotSupported エラーコードを返します。ACL の読み取り要求は引き続きサポートされています。ACL の読み取りリクエストは、バケット所有者の完全制御を示すレスポンスを常に返します。PUT オペレーションでは、バケット所有者の完全制御 ACL を指定するか、ACL を指定しない必要があります。そうしないと、失敗します。
トラブルシューティング - Amazon Simple Storage Service | docs.aws.amazon.com重要:バケットおよびオブジェクト ACL によるクロスアカウントアクセスを付与することは、S3 オブジェクトの所有権が [Bucket Owner Enforced] に設定されているバケットでは機能しません。ほとんどの場合、ACL はオブジェクトやバケットにアクセス権限を付与するために必要がありません。代わりに、AWS Identity Access and Management (IAM) 施策と S3 バケット施策を使用して、オブジェクトとバケットにアクセス許可を付与します。
bucket-owner-full-control ACL を Amazon S3 のオブジェクトに追加する | aws.amazon.com
要するに、以下というわけである:
* asset_sync がS3にプリコンパイル済みアセットファイルをアップロードする = S3 PUT リクエストを行う
* 「オブジェクト所有権のバケット所有者強制設定」を適用しているので、バケット所有者の完全制御 ACL を指定するか、ACL を指定しない必要がある
* ∴「 IAM ポリシーと S3 バケットポリシーの双方を使用して、オブジェクトとバケットにアクセス許可を付与する」必要がある
今回は、バケット所有者の完全制御 ACL を指定した。
#config/initializers/asset_sync.rb
# Change canned ACL of uploaded object. Default is unset. Will override fog_public if set.
# Choose from: private | public-read | public-read-write | aws-exec-read |
# authenticated-read | bucket-owner-read | bucket-owner-full-control
config.aws_acl = "bucket-owner-full-control"
ちなみに、ACLを指定しないを選んだ場合、推測に留まるが(別の機会に検証する積もり)、以下のような設定でいけるのではないだろうか?
* デフォルトの config.aws_acl = nil
のままにする
* IAM Policy でs3:PutObject
を許可 & S3 Bucket Policy で 当該ロールのs3:PutObject
を許可する
デフォルトでは、すべての Amazon S3 バケットとオブジェクトはプライベートです。リソース所有者およびバケットを作成した AWS アカウントのみが、バケットにアクセスできます。ただし、リソース所有者は、他のリソースとユーザーにアクセス許可を付与することを選択できます。これを行う 1 つの方法として、アクセスポリシーを記述します。
Amazon S3 バケットのアクセス許可 - AWS Config | docs.aws.amazon.com
「忘れるな、S3バケットとオブジェクトへのアクセスはデフォルトで全て拒否」
ブラウザからページにアクセスすると GET リクエストでS3バケットに置いてあるリソースファイルのURLを要求するので、S3バケットポリシーに「匿名ユーザーへの読み取り専用アクセス」許可を定義しなければいけなかったのは当たり前体操だった……
Crieitは誰でも投稿できるサービスです。 是非記事の投稿をお願いします。どんな軽い内容でも投稿できます。
また、「こんな記事が読みたいけど見つからない!」という方は是非記事投稿リクエストボードへ!
こじんまりと作業ログやメモ、進捗を書き残しておきたい方はボード機能をご利用ください。
ボードとは?
コメント