人が使う IAM ロール(信頼されたエンティティが AWS サービスでない IAM ロール)だけを棚卸しする

完全に「人が使う IAM ロール(信頼されたエンティティが AWS サービスでない IAM ロール)」に合致しているわけではないですが、多くのケースでは充足するかと思います。考え方の参考にしてもらえればと思います。

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

IAM ユーザーだけでなく IAM ロールも棚卸ししたい

コンバンハ、千葉(幸)です。

以前、IAM ユーザーを棚卸しする方法をまとめたことがありました。ここでは以下情報を取得することで IAM ユーザーがどのような権限を有しているかを確認しました。

  • IAM ユーザーが所属する IAM グループの一覧
  • IAM グループにアタッチされた管理ポリシーの一覧
  • IAM グループにアタッチされたインラインポリシーの一覧
  • IAM ユーザーに直接アタッチされた管理ポリシーの一覧
  • IAM ユーザーに直接アタッチされたインラインポリシーの一覧

ここで、ひとが使う IAM エンティティとして IAM ユーザーでなく IAM ロールがメインの環境もあるでしょう。となると IAM ロールを対象に同じことをしたいです。とは言え、AWS サービスが使用する IAM ロールは別に対象に含みたくないです。

うまいことやる方法を考えました。

人が使う IAM ロールだけを抽出するアプローチ

AWS CLI コマンドのaws iam list-rolesでクエリをかけることで抽出します。

aws iam list-roles \
    --query "Roles[?contains(AssumeRolePolicyDocument.Statement[0].to_string(Principal.AWS),\`arn:\`)].[RoleName]" --output text

aws iam list-rolesコマンドで信頼ポリシーの中身を取得できるので、プリンシパルが特定のものだけを引っ掛けます。

{
  "Roles": [
    {
      "AssumeRolePolicyDocument": {
        "Version": "2012-10-17",
        "Statement": [
          {
            "Action": "sts:AssumeRole",
            "Principal": {
              "Service": "ec2.amazonaws.com"
            },
            "Effect": "Allow",
            "Sid": ""
          }
        ]
      },
      "RoleId": "AROAJ52OTH4H7LEXAMPLE",
      "CreateDate": "2013-05-11T00:02:27Z",
      "RoleName": "ExampleRole1",
      "Path": "/",
      "Arn": "arn:aws:iam::123456789012:role/ExampleRole1"
    },
...

IAM ロール信頼ポリシーでプリンシパルに指定できるものは以下ページに記載があります。

ここで、「種別がAWS」「arn:を含む」に合致するものを今回は抽出することにしました。

# プリンシパル 抽出対象
1 AWS アカウント "AWS": "arn:aws:iam::123456789012:root" はい *1
2 IAM ロール "AWS": "arn:aws:iam::AWS-account-ID:role/role-name" はい
3 ロールセッション "AWS": "arn:aws:sts::AWS-account-ID:assumed-role/role-name/role-session-name" はい
4 ウェブ ID セッション "Federated": "cognito-identity.amazonaws.com" いいえ
5 SAML セッション "Federated": "arn:aws:iam::AWS-account-ID:saml-provider/provider-name" いいえ
6 IAM ユーザー "AWS": "arn:aws:iam::AWS-account-ID:user/user-name" はい
7 フェデレーテッドユーザー "AWS": "arn:aws:sts::AWS-account-ID:federated-user/user-name" はい
8 AWS サービス "Service": "service-name.amazonaws.com" いいえ
9 すべて "AWS" : "*" いいえ

#4,#5 は普通に考えれば「人が使う IAM ロール」ですが、今回は対象外にしています。含めたい場合はコマンドをカスタマイズしてご利用ください。

また、例えば #2 IAM ロールを信頼する IAMロールがあったとして、それを人が使っているとは限りません。が、今回はそこを意識せず一律で抽出することにします。

ステートメントが複数ある場合は意図どおりにならない

例えば以下のような信頼ポリシーがあったとします。今回のコマンドでは一つ目のステートメントの Principal のみを評価するようにしているので、この信頼ポリシーを持つ IAM ロールは抽出対象になりません。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        },
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::AWS-account-ID:user/user-name"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

ステートメントが複数あったり「サービスからも IAM ユーザーからも引き受けできる」という状態であったりするケースは少ないと思いますが、今回のコマンドでは例外として扱いますのでご注意ください。

人が使う IAM ロール一覧を出力する

実際に先ほどのコマンドを実行してみます。いくつか結果が得られました。

% aws iam list-roles \
    --query "Roles[?contains(AssumeRolePolicyDocument.Statement[0].to_string(Principal.AWS),\`arn:\`)].[RoleName]" --output text
AWS-QuickSetup-StackSet-Local-ExecutionRole
AWSCloudFormationStackSetExecutionRole
cm-chiba.yukihiro
cm-helpdesk
cm-membersportal
cm-policymaintainer
...

冒頭のふたつは人が使うものではありませんが、AWS アカウントであったり別の IAM ロールを信頼しているため結果に含まれてきています。このあたりをどう弾くかは環境にあわせて検討いただければと思います。

人が使う IAM ロールにアタッチされたポリシー一覧の取得

IAM ロールの一覧が取得できたので、これを用いて後続のコマンドを好きに実行できます。今回は、冒頭のエントリと同様にアタッチされたポリシーの一覧を取得してみます。

ポリシーは以下の種類に分かれており、それぞれ対応したコマンドが異なります。

  • 管理ポリシー(AWS管理ポリシーおよびカスタマー管理ポリシー)
  • インラインポリシー

管理ポリシー

echo "RoleName Policies" > /tmp/awscli.tmp
aws iam list-roles --query "Roles[?contains(AssumeRolePolicyDocument.Statement[0].to_string(Principal.AWS),\`arn:\`)].[RoleName]" --output text | sort | while read line;\
do
   echo ${line} > /tmp/awscli-policy.tmp;\
   aws iam list-attached-role-policies --role-name ${line} --query "AttachedPolicies[].[PolicyName]" --output text >> /tmp/awscli-policy.tmp;\
   cat /tmp/awscli-policy.tmp | tr "\n" " " | sed 's/$/\n/g' >> /tmp/awscli.tmp;\
done
column -t /tmp/awscli.tmp;\
rm /tmp/awscli.tmp /tmp/awscli-policy.tmp

行っている処理の大枠は以下です。

  1. /tmp/awscli.tmpにヘッダーを出力
  2. 人が使う IAM ロールの一覧を取得し、ロールごとに以下をループ
    1. /tmp/awscli-policy.tmpにユーザー名を出力(上書き)
    2. ロールにアタッチされたポリシーの一覧を/tmp/awscli-policy.tmpに出力
    3. /tmp/awscli-policy.tmpの内容を「改行を除外」&「末尾に改行を付与」し/tmp/awscli.tmpに出力(追記)
  3. /tmp/awscli.tmpの内容を表形式に整形する
  4. /tmp/awscli.tmp/tmp/awscli-group.tmpを削除

実行した場合のイメージは以下です。

RoleName                                     Policies
AWS-QuickSetup-StackSet-Local-ExecutionRole  AdministratorAccess
AWSCloudFormationStackSetExecutionRole       AdministratorAccess
cm-chiba.yukihiro                            AdministratorAccess  CustomPolicy-A
cm-helpdesk                                  ReadOnlyAccess
cm-membersportal                             ReadOnlyAccess
cm-policymaintainer
...

インラインポリシー

インラインポリシーの場合も途中で使用するコマンドが違うくらいで構成は同じです。

echo "RoleName InlinePolicies" > /tmp/awscli.tmp
aws iam list-roles --query "Roles[?contains(AssumeRolePolicyDocument.Statement[0].to_string(Principal.AWS),\`arn:\`)].[RoleName]" --output text | sort | while read line;\
do
   echo ${line} > /tmp/awscli-policy.tmp;\
   aws iam list-role-policies --role-name ${line} --query "PolicyNames[]" --output text >> /tmp/awscli-policy.tmp;\
   cat /tmp/awscli-policy.tmp | tr "\n" " " | sed 's/$/\n/g' >> /tmp/awscli.tmp;\
done
column -t /tmp/awscli.tmp;\
rm /tmp/awscli.tmp /tmp/awscli-policy.tmp

終わりに

人が使う IAM ロールだけを一覧取得したい、という内容でした。

  • 人が使うとは限らないものも含まれる
  • ウェブ ID セッション、SAML セッション用のロールが対象に含まれない
  • ステートメントが複数ある場合には対応していない

と、いくつか注意点がありますが、環境に合わせて細部をカスタマイズしていただければある程度は対応できるかと思います。

今回はアタッチされたポリシーを確認する、という内容に留めましたが、get-role と組み合わせて使用すれば「作成時刻」や「最終使用時刻」の取得も可能です。

参考になれば幸いです。

以上、 チバユキ (@batchicchi) がお送りしました。

参考

脚注

  1. AWS アカウントプリンシパルは`"AWS": "123456789012"`の形式にも対応しており、その形式で保存されていた場合は抽出対象になりません。基本的には`"AWS": "arn:aws:iam::123456789012:root"`の形式に変換されるようですが、すべてが変換対象と保証はされていなさそうです。