AWS SDKを使ったアプリケーションを作る時credentialsの扱いがいつも面倒なので、ベストプラクティス的なものを考えていきたい。
例として、
$ ruby myec2.rb list
と叩くと
<AWS::EC2::Instance id:i-12345678>
<AWS::EC2::Instance id:i-12345679>
とEC2のインスタンス一覧を標準出力するだけのアプリケーションを作ってみる。
大雑把に分けて4つある。
-
IAM RoleのInstance Profileを使う
-
環境変数で渡す
-
アプリケーションのオプションや設定ファイルで渡す
-
~/.aws/credentials
や~/.aws/config
に書く -
IAM RoleのEC2 Instance Profileを使う
EC2上で実行する場合に最も便利でセキュアな方法。aws-sdkが自動でメタデータから取得してくれるため、アプリケーション内に明示的に何かを書く必要はない。
- 環境変数で渡す
aws-sdkでは環境変数 AWS_ACCESS_KEY_ID
と AWS_SECRET_ACCESS_KEY
がセットされている場合、これらを自動で読む機能がある。
$ export AWS_ACCESS_KEY_ID=********
$ export AWS_SECRET_ACCESS_KEY=********
$ ruby myec2.rb list
こちらもaws-sdkが処理してくれるので、アプリケーション内に明示的に何かを書く必要はない。
- アプリケーションのオプションや設定ファイルで渡す
アプリケーションの起動時に以下のようにオプションで渡したり、どこかに設定ファイルを置いてそこに決め打っておく方法。
$ ruby myec2.rb list --access-key-id ******** --secret-access-key ********
流石にaws-sdk側では処理してくれないので、自分でこれらから取得した値を使うように実装する必要がある。
~/.aws/credentials
や~/.aws/config
に書く
- A New and Standardized Way to Manage Credentials in the AWS SDKs - AWS Security Blog
- AWS SDK for Rubyで新標準となったCredentials管理方法を使ってみる - 雑記帳(2014-07-11)
あたりを参照。アプリケーションでは以下のように渡せるようにしたい。
$ ruby myec2.rb list --profile myprofile
上記記事を見ると分かるように、 ~/.aws/credentials
はaws-sdk側のサポートがある(default profileは暗黙的にロードされる)。 ~/.aws/config
はこれを読み込む専用のgemがある。
上記4つの方法すべてを使えるようにして、できたのがこちら。
require "thor"
require "aws-sdk"
require "aws_config"
module MyEc2
class CLI < Thor
class_option :access_key_id, default: nil, aliases: [:k]
class_option :secret_access_key, default: nil, aliases: [:s]
class_option :region, default: "ap-northeast-1", aliases: [:r]
class_option :profile
desc "list", "List all instances in region."
def list
ec2.instances.each {|instance| p instance}
end
private
def aws_config
hash = {}
if options[:profile]
aws_config = AWSConfig.profiles[options[:profile]]
if aws_config
hash.update aws_config.config_hash
else
provider = AWS::Core::CredentialProviders::SharedCredentialFileProvider.new profile_name: options[:profile]
hash.update credential_provider: provider
end
else
hash.update access_key_id: options[:access_key_id] if options[:access_key_id]
hash.update secret_access_key: options[:secret_access_key] if options[:secret_access_key]
end
hash.update region: options[:region] if options[:region]
hash
end
def ec2
@ec2 ||= AWS::EC2.new aws_config
end
end
end
MyEc2::CLI.start ARGV
これだけ方法が多いとかなり複雑になるので、privateメソッド aws_config
を定義し、その中で空ハッシュに条件に応じて情報を足していく一連の処理を行い、それを元に AWS::EC2
オブジェクトを生成している。
流れとしては、まず --profile
が指定されている場合はそのprofileを取得しようとする。
if options[:profile]
aws_config = AWSConfig.profiles[options[:profile]]
if aws_config
hash.update aws_config.config_hash
else
provider = AWS::Core::CredentialProviders::SharedCredentialFileProvider.new profile_name: options[:profile]
hash.update credential_provider: provider
end
~/.aws/credentials
と ~/.aws/config
両方に対応するように書いてある。
次に、profileが指定されていなかった場合かつ --access-key-id
と --secret-access-key
が指定されている場合はそれらの情報を使うようにしている。
else
hash.update access_key_id: options[:access_key_id] if options[:access_key_id]
hash.update secret_access_key: options[:secret_access_key] if options[:secret_access_key]
最後にリージョンは --profile
と同時に指定することも多いという予想から、この位置で処理している。
hash.update region: options[:region] if options[:region]
このように書くことで --profile
、 --access-key-id
、 --secret-access-key
のいずれも指定されなかった場合はIAM Instance Profileや環境変数から暗黙的にcredentialsが取得されるようになる。
aws−sdk
は暗黙的にcredentialsを受け取る方法が沢山あるので、 aws−sdk
を使ったアプリをcredentialsを明示的に受け取ることが前提の実装にすることはやめよう。
実装例にはRubyでCLIアプリを作るときに超絶便利なThorを使っている。
突然すみません、たまたま検索に引っかかって見つけたのですが、
Aws::CredentialProviderChain
というのが便利なのでよかったら調べてみてください。