IAM Roleがなぜ安全にアクセス権限を委任できるかを理解する

AWSのAPIにアクセスするには、APIへのリクエストに署名する必要があり、署名するための秘密鍵が必要である。IAM Userのアクセス・キーを割り当てることもできるが、AWSのサービス、例えばEC2インスタンスからAPIにアクセスする場合は、IAM Roleを使用すると、秘密情報をホスト内に保存することなく、安全にAPIへのリクエストを行うことができる。

この記事では、AWSの、あるアカウントのEC2インスタンスから、別のアカウントのAWSサービスのAPIをコールする場合を例として、どのようにアクセス権限を委任しているかを順に説明する。

この記事では、以下のリソースを作成するとする。

* アカウント__A__
 * アカウントA内のEC2インスタンス__C__
 * アカウントA内のIAM Role __Role_D__
 * アカウントA内のIAM Policy __Policy_E__
* アカウント__B__
 * アカウントB内のIAM Role __Role_F__


## EC2インスタンスAにIAM Roleをアタッチする

EC2インスタンスには、一つだけ IAM Roleをアタッチできる。

EC2インスタンスにアタッチするIAM Role Role_Dの「信頼関係」のポリシー・ドキュメントは、以下のように PrincipalにEC2サービスを指定し、ActionにSTSサービスのAssumeRoleを指定する。AWSコンソールでIAM Roleを作成する場合は、ウィザードで簡単にポリシー・ドキュメントを作成できる。

```javascript:Role_D
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}
```

PrincipalがEC2サービスの場合、インスタンス内から以下の仮想的なネットワークのエンドポイントにアクセスすることでActionのサービスを呼び出すことができる。

```:アクセス先
http://169.254.169.254/latest/meta-data/iam/security-credentials/<role-name>
```

```javascript:レスポンス
{
  "Code" : "Success",
  "LastUpdated" : "yyyy-mm-ddThh:mm:ssZ",
  "Type" : "AWS-HMAC",
  "AccessKeyId" : "ASIAW7##############",
  "SecretAccessKey" : "G5sCiFYmltsyiHV#########################",
  "Token" : "IQoJb3###########################################################################################",
  "Expiration" : "yyyy-mm-ddThh:mm:ssZ"
}
```

IAM Role Role_DのActionに指定した STSサービスのAssumeRole APIが、アクセス・キーを作成する。このアクセス・キーは、IAM Userに割り当てたアクセス・キーと同様に、AWSのサービスのAPIへのリクエストの署名の鍵として使用できる。IAM Userのアクセス・キーとは異なり、STSサービスが生成するアクセス・キーには有効期限が設定されていて、保存することなく、都度生成して使用する。

このIAM Roleのアクセス権限に、例えば、CloudWatchReadOnlyポリシーをアタッチすると、EC2インスタンスからCloud WatchのAPIにアクセスできるようになる。

## アカウントBに、アカウントAへ権限を委任するRoleを設置する

アカウントBのIAM Roleのポリシー・ドキュメントに、PrincipalがアカウントAを指すARNを指定し、ActionにSTSサービスのAssumeRoleを指定したRole_Fを作成する。

```javascript:Role_F
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::<アカウントAのID>:root"
      },
      "Action": "sts:AssumeRole",
      "Condition": {}
    }
  ]
}
```

このRoleは、AWSコンソールでは、ウィザードを使用して、アカウントIDを指定するだけで作成できる。

このRoleが存在することで、アカウントAからこのIAM Roleにアタッチしたポリシーの権限の委任を受けることができる。

## アカウントAのRole_Dに、アカウントBのRole_Fを要求する権限を与える

Role_Dのアクセス権限に、以下のIAM Policy Policy_Eをアタッチする。Actionに指定した、STSサービスのAssumeRoleにより、Resouceに指定したARNが示すアカウントBのRole_Fにアクセスする権限をこのIAM Roleに与える。

```javascript:Policy_E
{ 
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "arn:aws:iam::<アカウントBのID>:role/Role_F"
        }
    ]
}
```

## EC2インスタンスAから、アカウントBのIAM Role Role_Fの権限の委任を受ける

EC2インスタンスAから、アカウントBのIAM Role Role_Fを使って、アカウントBのサービスの権限の委任を受けるには、Role_Dの委任により、アカウントAのSTSサービスのAssumeRole APIに、RoleArnパラメータにRole_FのARNを指定してアクセスすることでアクセス・キーを作成し、アカウントBのAWSサービスのAPIへのリクエストの署名の鍵とする。

AWS SDK for Rubyの場合、[「AWS STS アクセストークンの作成」](https://docs.aws.amazon.com/ja_jp/sdk-for-ruby/v3/developer-guide/setup-config.html#aws-ruby-sdk-credentials-access-token)のように、Role_FのARNを指定して、アクセスキーを取得する。

```ruby:rubyによるAPI呼び出し
role_credentials = Aws::AssumeRoleCredentials.new(
  client: Aws::STS::Client.new,
  role_arn: "arn:aws:iam::<アカウントBのID>:role/Role_F",
  role_session_name: "<任意の名前>"
)

s3 = Aws::S3::Client.new(credentials: role_credentials)
```

上記のコードは、STSサービス、S3サービスにアクセスするリージョンをデフォルト設定から取得していることに注意。必要に応じて、regionオプションを指定する。

## まとめ

EC2インスタンスAは、Role_Dにより、Role_DにアタッチされたIAM Policyによる権限の委任を受け、Policy_Eの権限とアカウントBのRole_FによりRole_FにアタッチされたIAM Policyによる権限の委任を受けることで、アカウントBのサービスにアクセスすることができる。

おつかれさまでした。