はじめまして、長沢です。
弊社では最近一部サービスでAWS および Ruby を利用しているのですが、 今回はAWSの AWS SDK for Ruby を利用して クロスアカウントアクセス をやってみようと思います。
簡単に クロスアカウントアクセスについての説明
クロスアカウントアクセスとは、一言で言うと、異なるAWSアカウントをまたいだリソースの操作を行う事ができる機能です。
クロスアカウントアクセスの方法は IAM Role の AssumeRole API を利用します。 IAM Roleを利用したクロスアカウント API アクセスの方法の詳しい説明は こちら
この記事たちを読めば大体は分かります。
個人的に思った注意しなくてはいけないところ (個人的にはまったところ) は
- AMIユーザーでなければクロスアカウントアクセスは出来ない
- 認証先のIAM Role の Trust Relationships は適切に設定して権限を管理する
と言ったところでしょうか。
- の部分に関しては最初は気づかずに Role で AssumeRole をしようとしていて、下記のエラーメッセージが出ていました。
"Roles may not be assumed by federated users"
事前準備(Webコンソール上でクロスアカウントアクセスの設定)
最初にWebコンソールを利用してアカウントの下記のAWSの公式ブログにあるような設定をしましょう。(ここでの説明は割愛致します) Delegating API Access to AWS Services Using IAM Roles | AWS Blog
また、こちらの サーバーワークス様のブログ も大変分かりやすいです。
早速やってみる
上記の事前準備の設定が済んだら、
2つのAWSアカウントA、Bがあって それぞれ S3バゲット “account-a-bucket”(アカウントA) 、 “account-b-bucket”(アカウントB) が作成されていたときに、
クロスアカウントアクセスの仕組みを利用して、
アカウントAの IAM User “homes” が、アカウントBのS3に対してRead Only 権限をもつ IAM Role “s3-viewer” としてふるまい、アカウントBのS3バゲット名を取得する
といったようなことを実現するためのコードを書いてみます。
コードの流れを簡単に説明すると、
- IAMユーザーのアクセスキー、シークレットキーを利用してSTSにアクセス
- sts.assume_roleのAPIを利用して Credentialを取得
- 取得したCredentialを利用してアカウントBにアクセス
といった感じです。
※ 利用したRubyは 1.9.3-p327 です。
※ キーがシンボルのHashの記法が 1.8.x と少々異なりますのでご注意ください。
cross_account_access.rb
require 'aws-sdk' # 設定 AWS.config({ region: 'ap-northeast-1', access_key_id: 'アカウントAのhomesユーザーのアクセスキー', secret_access_key: 'homesユーザーのシークレットキー', }) # AssumeRole対象のIAM Role のARN assume_role_arn = 'arn:aws:iam::{アカウントBのID}:role/s3-viewer' # session名は任意の文字列 assume_role_session_name = 'homess3viewersession' # S3オブジェクトの取得(アカウントA) s3_a = AWS::S3.new puts '===== Account A Buckets =====' # バゲット名を出力 s3_a.buckets.each do |bucket| puts bucket.name end puts # STSオブジェクトの取得 sts = AWS::STS.new # AssumeRoleを呼び出し一時的な認証情報を受け取る assume_info = sts.assume_role( role_arn: assume_role_arn, role_session_name: assume_role_session_name, ) # 認証情報の上書き AWS.config({ access_key_id: assume_info[:credentials][:access_key_id], secret_access_key: assume_info[:credentials][:secret_access_key], session_token: assume_info[:credentials][:session_token], }) # S3オブジェクトの取得(アカウントB) s3_b = AWS::S3.new puts '===== Account B Buckets =====' # バゲット名を出力 s3_b.buckets.each do |bucket| puts bucket.name end puts
実行してみます
$ ruby cross_account_access.rb
===== Account A Buckets ===== account-a-bucket ===== Account B Buckets ===== account-b-bucket
うまくできているようです!
念のため、アカウントA,B それぞれの IAM Policy や Trust Relationship の設定を以下に記載いたします。
アカウント A の設定
IAM User “homes” の Policy (STSのAssumeRoleとS3の閲覧に権限をつけている)
{ "Statement": [ { "Effect": "Allow", "Action": "sts:AssumeRole", "Resource": "*" }, { "Effect": "Allow", "Action": [ "s3:Get*", "s3:List*" ], "Resource": "*" } ] }
ちなみに、STSで AssumeRole しようとして、
もしそのRoleに対して権限がないときは下記のようなエラーが出ます。
A client error (AccessDenied) occurred: User: arn:aws:iam::{AのアカウントID}:user/homes is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::{BのアカウントID}:role/s3-viewer
アカウント B の設定
IAM Role “s3-viewer” の Policy(S3の閲覧に権限をつけている)
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:Get*", "s3:List*" ], "Resource": "*" } ] }
Trust Relationship
{ "Version": "2008-10-17", "Statement": [ { "Sid": "", "Effect": "Allow", "Principal": { "AWS": [ "arn:aws:iam::{AのアカウントID(12桁)}:user/homes" ] }, "Action": "sts:AssumeRole" } ] }
もし、上記のPrincipalの箇所が
"Principal": { "AWS": [ "arn:aws:iam::{AのアカウントID(12桁)}:root" ] }
としてしまうと、 そのアカウントの全ユーザーがデフォルトでアクセスできることになってしまうので注意が必要です。
株式会社ネクストでは、一緒に世の中をもっと便利に楽しくしたい仲間を募集中です。 主体的に楽しみながら仕事できる方からのエントリーお待ちしています! http://recruit.next-group.jp/