IAM Roleで権限設定している場合のcredentialの渡し方
アプリケーションからAWSのリソースにアクセスする場合、何かしらのIAMユーザを作って、 AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
を発行して、環境変数などにセットして
Aws::S3::Client.new(
region: 'ap-northeast-1',
access_key_id: ENV['AWS_ACCESS_KEY_ID'],
secret_access_key: ENV['AWS_SECRET_ACCESS_KEY']
)
=> #<Aws::S3::Client>
でアクセスすること、多いですよね。
でも、EC2インスタンスや、ECSのタスクに Roleを設定してそちらで権限管理すると、credentialをアプリケーション側で管理しなくてもよくてより管理しやすくなってよいのですが、その場合のアプリケーションからのクレデンシャルの渡し方があまり分からなかったので調べました。
carrierwaveの場合
carrierwaveの場合は、gem側で用意されていて、通常のAWS_ACCESS_KEY_IDなどを渡す代わりに、
CarrierWave.configure do |config|
config.fog_credentials = {
provider: 'AWS',
region: 'ap-northeast-1',
use_iam_profile: true
}
end
と、 use_iam_profile:true
を渡せば、carrirwaveの中(から呼ばれている aws-fog
というgem)でよしなにやってくれます。
Shrineの場合
carrierwaveと似たような画像アップロードのgemでShrineというのがありますが、こちらの場合は、S3を使う場合、
class Shrine
module Storage
class S3
# ... 略 ...
def initialize(bucket:, client: nil, prefix: nil, upload_options: {}, multipart_threshold: {}, signer: nil, public: nil, **s3_options)
raise ArgumentError, "the :bucket option is nil" unless bucket
@client = client || Aws::S3::Client.new(**s3_options) # <= これ
と、aws-sdkに、直接アプリからの設定を渡していました。
この場合どうするんだろう?というのが分からずに色々調べた結果になります。
結果
最終的にはすごく単純で、 ec2や、ecsコンテナに、iam roleが設定された場合、特定のurlにリクエストを送るとクレデンシャルが取得できるAPIがつかえるようになるので、そこから持ってきた内容を設定するだけということでした。
これを実装しているのが、 carrierwaveでみた、 aws-fog
の
と、
use_iam_profile:true
を渡せば、carrirwaveの中(から呼ばれているaws-fog
というgem)でよしなにやってくれます。
の部分です。
で、Shrineで、この処理実装しないといけないのかーとおもっていたら、aws-sdkにこれらの処理をやってくれる処理があってそれを使うとすごく楽です。
最終的には下記のようにしました。
ECSの場合、 Aws::ECSCredentials
EC2の場合、 Aws::InstanceProfileCredential
が使えます。これらを credentials
というkeyでsdkにわたすと、いろいろ取得してくれてよしなにcredentialを作ってくれます。
require 'shrine'
require 'shrine/storage/s3'
s3_options = {
region: 'ap-northeast-1',
bucket: 'some_bucket_name'
}
# AWS_CONTAINER_CREDENTIALS_RELATIVE_URI が定義されている場合
# Role設定されたECSで動いているので、ECSの方、そうでなければ、EC2用のInstanceProfileの方を使う。
if ENV["AWS_CONTAINER_CREDENTIALS_RELATIVE_URI"]
s3_options = s3_options.merge(credentials: Aws::ECSCredentials.new)
else
s3_options = s3_options.merge(credentials: Aws::InstanceProfileCredentials.new)
end
Shrine.storages = {
cache: Shrine::Storage::S3.new(prefix: 'prefix', **s3_options),
... 略 ...
}
結果的には非常に簡単だったのですが、これらに気づくまで結構時間がかかったので、メモとして残しておきます。