LIFULL Creators Blog

「株式会社LIFULL(ライフル)」の社員によるブログです。

今更聞けない、AWS SDK for Ruby を利用したクロスアカウントアクセスのやり方

はじめまして、長沢(@simeji)です。

弊社では最近一部サービスでAWS および Ruby を利用しているのですが、 今回はAWSの AWS SDK for Ruby を利用して クロスアカウントアクセス をやってみようと思います。

簡単に クロスアカウントアクセスについての説明

クロスアカウントアクセスとは、一言で言うと、異なるAWSアカウントをまたいだリソースの操作を行う事ができる機能です。

クロスアカウントアクセスの方法は IAM Role の AssumeRole API を利用します。 IAM Roleを利用したクロスアカウント API アクセスの方法の詳しい説明は こちら

この記事たちを読めば大体は分かります。

個人的に思った注意しなくてはいけないところ (個人的にはまったところ) は

  1. AMIユーザーでなければクロスアカウントアクセスは出来ない
  2. 認証先のIAM Role の Trust Relationships は適切に設定して権限を管理する

と言ったところでしょうか。

  1. の部分に関しては最初は気づかずに Role で AssumeRole をしようとしていて、下記のエラーメッセージが出ていました。
"Roles may not be assumed by federated users"

事前準備(Webコンソール上でクロスアカウントアクセスの設定)

最初にWebコンソールを利用してアカウントの下記のAWSの公式ブログにあるような設定をしましょう。(ここでの説明は割愛致します) Amazon Web Services Blog: Delegating API Access to AWS Services Using IAM Roles

また、こちらの サーバーワークス様のブログ も大変分かりやすいです。

早速やってみる

上記の事前準備の設定が済んだら、

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バゲット名を取得する
といったようなことを実現するためのコードを書いてみます。

コードの流れを簡単に説明すると、

  1. IAMユーザーのアクセスキー、シークレットキーを利用してSTSにアクセス
  2. sts.assume_roleのAPIを利用して Credentialを取得
  3. 取得した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/