AWS CLI で IAM Role を作りつつ、「Assume」や「Principal」について考える


CLI でやってみようシリーズ。

# とりあえずロールを作ってみる

インラインの Assume Role に何を指定して良いのか分からなかったので、とりあえずブラウザコンソールから一つ適当なロールを作り、それを取得してみる。 ∥ [get-role — AWS CLI 1.10.32 Command Reference](http://docs.aws.amazon.com/cli/latest/reference/iam/get-role.html)

~~~~console:
$ aws iam get-role --role-name myLambdaRole | jq .
{
  "Role": {
    "AssumeRolePolicyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": "sts:AssumeRole",
          "Effect": "Allow",
          "Principal": {
            "Service": "lambda.amazonaws.com"
          }
        }
      ]
    },
    "RoleId": "AROA～NYHE",
    "CreateDate": "2016-05-16T08:59:21Z",
    "RoleName": "myLambdaRole",
    "Path": "/",
    "Arn": "arn:aws:iam::～:role/myLambdaRole"
  }
}
~~~~

それをコピペしてみる。jq(1) を通しておけば、とりあえずフォーマットとして不正な JSON は飛ばない。 ∥ [create-role — AWS CLI 1.10.32 Command Reference](http://docs.aws.amazon.com/cli/latest/reference/iam/create-role.html)

~~~~bash:aws-iam-create-role
#!/bin/bash
# -*- coding: utf-8 -*-
set -o nounset -o errexit -o pipefail

name=myNewRole
path="/division_x/project_y/"
assume_role_principal_service=lambda.amazonaws.com

assume_role_policy_document=$(jq -c . <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": "sts:AssumeRole",
      "Effect": "Allow",
      "Principal": {
        "Service": "$assume_role_principal_service"
      }
    }
  ]
}
EOF
)
role=$(aws iam create-role \
  --role-name "$name" \
  --path "$path" \
  --assume-role-policy-document "$assume_role_policy_document" | jq -c . )
jq . <<<"$role"
~~~~

実行結果:

~~~~console:
$ ./aws-iam-create-role
{
  "Role": {
    "AssumeRolePolicyDocument": {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Action": "sts:AssumeRole",
          "Effect": "Allow",
          "Principal": {
            "Service": "lambda.amazonaws.com"
          }
        }
      ]
    },
    "RoleId": "AROA～UOW2",
    "CreateDate": "2016-05-22T10:08:43.394Z",
    "RoleName": "myNewRole",
    "Path": "/division_x/project_y/",
    "Arn": "arn:aws:iam::～:role/division_x/project_y/myNewRole"
  }
}
~~~~

# 説明

だんだん語源学みたいになって行きますが。

## ロール名

まず、ロール名のつけ方について。ここでは “myNewRole” としている。ブラウザコンソールから新規ロールを作ろうとすると、サンプルとして出てくるロール名が “myRole” と lowerCamelCase 形式なので、それに従ってみた。

## Assume Role Policy について

次に `AssumeRolePolicyDocument` がそもそも何なのかである。

コンソールで言うと、“Role Type” の “AWS Service Roles” から選んだものに相当し、このサービスからしかこのロールが使えなくなる。

それを AWS 用語で言うと、この Assume Role Policy は、今回作ったこのロールが、AWS STS (AWS Security Token Service) によって使われる（STS が AssumePolicy アクションを起こすことを許す）ことを意味するポリシーである。 ∥ [一時的セキュリティ認証情報 - AWS Identity and Access Management](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/id_credentials_temp.html)

IAM における普通のポリシー（普通の Inline Policy や Managed Policy）が、「当該ロールが持つ、何かをする権限」について規定しているのに対して、このポリシーは「このロールが STS によって使われる際の権限」について規定しているので、別枠になっているのである。

“assume” は普通は「仮定する」「みなす」などの意味で用いられるが、やや形式張った用法として「〈実権など〉を握る; 〈役割責任任務など〉を引き受ける(undertake)」“take or begin to have (power or responsibility)” の意味がある。認証・認可系のコンテキストで出てくる “assume” はこちらだろうな。

どうやら “assume” は、原義としては「証拠無しに認める」ということのようです。よって、以下のような 2 通りの語義が出てきたのでしょう。 ∥ [assume - Wiktionary](https://en.wiktionary.org/wiki/assume)


1.「（証拠は無いけれどとりあえず状況を認めて）仮定する」「みなす」
2.「（これといった credential は提示せずに、役割を）引き受ける」「（権限を）得る」

つまりこれは、とある Role なりサービスなりが、他の Role に対して sts:AssumeRole する（他の Role（の権限）を assume（取得する））ことができるという意味なんだな。 ∥ [AssumeRole - AWS Security Token Service](http://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRole.html)

## Principal って？

次に、“principal” について。

ポリシーでは何かと出てくる「プリンシパル」だが、これもどうも今まで意味がピンと来ていなかった。一般的には「(演劇などの)主役, 主演者; (コンサートの)独奏者, 主席奏者; (犯罪の)主犯」の方の意味なんだろうが、別の意味で「〖しばしば～s〗(代理人に対して)本人; (決闘の介添人に対し)決闘する本人」“a person for whom another acts as an agent or representative” がある。

原義としては「（何らかの集団の中で）一番目の人」の意味なので、下記の意味が出てきたのだろう。

1. 「（演劇などの他の端役に対して）主役」「（オーケストラの他の楽器担当に対して）独奏」
2. 「（一緒に働く代理人に対して）本人」

つまりは、そのポリシーを持ったもの（User や Role）の権限で動く際はいわば agent であり、それを使う側（ユーザやサービス）が principal（代理人ではなく、代理人を使う本人）である、という意味になる。 ∥ [IAM ポリシーエレメントの参照 - AWS Identity and Access Management](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_policies_elements.html#Principal)

## パス

web コンソールではパスが付けられず、API 経由ならば付けられるので嬉しくなってつけてみた。 ∥ [IAM ID - AWS Identity and Access Management](http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/reference_identifiers.html)

要は、ディレクトリサービスにおける部署のようなもので、ユーザやロールの数が多くなってきたら、「パス」に基いて何かを出来るように用意されている属性です。あくまでも我々の便宜のために用意されている属性であり、AWS のシステム側では全く使っていない模様。

# Managed Policy の付与

せっかくなので、role に managed policy が attach されているか否かを確認して、attach されていなかったら attach するようにしてみます。このへんの jq(1) の使い方については、前回の投稿を参考にしてください。 ∥ [jq(1) の力を借りて Bash スクリプト内で JSON を操作する - Qiita](http://qiita.com/knaka/items/9f18fe6630438eb3b576)

~~~~bash:aws-iam-attach-role-policy
#!/bin/bash
# -*- coding: utf-8 -*-
set -o nounset -o errexit -o pipefail

role_name="myNewRole"
policy_arns=(
  "arn:aws:iam::aws:policy/AWSLambdaFullAccess"
  "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
)
policies_attached=$(aws iam list-attached-role-policies \
  --role-name "$role_name" | jq -c .AttachedPolicies )
for policy_arn in "${policy_arns[@]}"
do
  if test "$(jq -c --arg policy_arn "$policy_arn" \
    '. | map(select(.PolicyArn == $policy_arn))' <<<"$policies_attached" )" \
    = "[]"
  then
    aws iam attach-role-policy \
      --role-name "$role_name" \
      --policy-arn "$policy_arn"
    echo Attached "$policy_arn"
  else
    echo Policy "$policy_arn" already attached.
  fi
done
~~~~

実行結果:

~~~~console:
$ aws iam attach-role-policy \
  --role-name myNewRole \
  --policy-arn "arn:aws:iam::aws:policy/AWSLambdaFullAccess"
$ ./aws-iam-attach-role-policy
Policy arn:aws:iam::aws:policy/AWSLambdaFullAccess already attached.
Attached arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
$ aws iam list-attached-role-policies --role-name myNewRole | jq .
{
  "AttachedPolicies": [
    {
      "PolicyName": "AWSLambdaFullAccess",
      "PolicyArn": "arn:aws:iam::aws:policy/AWSLambdaFullAccess"
    },
    {
      "PolicyName": "AWSLambdaBasicExecutionRole",
      "PolicyArn": "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
    }
  ]
}
~~~~