この記事は Classi developers Advent Calendar 2021 の13日目の記事です。
こんにちは。開発本部プロダクト開発部学習チームでエンジニアをしています、藤田です。 本記事では AWS の IAM の Policy の定義から、アクセス可能なリソース範囲・許可されるアクション等を事前に検証できる IAM Policy Simulator を紹介します。
記事を書こうと思った背景
Classi の Infrastructure as Code(IaC) への取り組み
Classi では複数のシステムを AWS 上で運用しており、それらの AWS リソースの変更内容のレビュー・変更履歴の管理を適切に行うため、 組織的に Terraform を用いた Infrastructure as Code(IaC) を実践しています。
大部分のシステムは、システムと対応する GitHub のリポジトリに Terraform コードが記述されており、システムを実現するための AWS リソースがコードとして表現されています。
「AWS リソース」と一口に言っても
- 特定のシステムに閉じない複数のシステムから参照され得る Global なリソース
- 特定のシステムに閉じた Non Global なリソース
のように、性質や影響範囲の違いに応じて、リソースの宣言場所や運用の方法をカスタマイズしています。
更に、Classi では Terraform の plan
や apply
を実行者のローカル環境ではなく、 GitHub の Pull Request 上で実行できるように、 Atlantis を採用しています。
Classi における AWS リソースの追加・変更の運用
例えば、「ある新規のシステム A」で必要とする AWS リソースを追加したい場合は
- システム A 用のリポジトリで必要とする AWS リソースを記述した Terraform のコード
- システム A 用のリポジトリから Terraform を実行できる設定・許可
- Atlantis とシステム A 用のリポジトリの連携設定
- SRE チームから実行許可(Pull Request への approve 等)
- システム A 用のリポジトリから Terraform を実行して、実際に AWS リソースを作成できる権限
- 適切な Policy が定義されたシステム A 用の IAM Role
が必要になります。(細かい前提は省いています)
そして、3つ目の IAM Role は Global な AWS リソースとして、これまた、各システム用のリポジトリとは別のリポジトリで Terraform でコード管理されています。
つまり、実際にシステム A 用の AWS リソースを反映するために必要な作業・流れはおおよそ次のようになります。
①システム A 用の IAM Role(例えば role-for-a)を Terraform で定義 ↓ ②①の内容を SRE が Pull Request 上でレビュー・承認 ↓ ③②が承認されたら、 Atlantis 経由で apply を実行 ↓ ④システム A 用の AWS リソースを Terraform で定義 ↓ ⑤④の内容を SRE が Pull Request 上でレビュー・承認 ↓ ⑥⑤が承認されたら、Atlantis 経由で apply を実行 (このとき、①で定義した role-for-a を使う)
何が起きたか
私が所属しているチームで新たなシステムを構築する際に、 先程の流れの⑥で Atlantis 経由で apply を実行した際に、必要な権限が不足していることに気がつきました。
不慮の事故を防ぐためにも、「AWS リソースの変更」といった繊細な操作に関しては、許可するアクションと対象のリソースは必要最低限にするのが鉄則なので、必要に応じて権限を徐々に追加していくのが安全であることは間違いありません。 そのため、「必要な権限が不足しているのであれば、また追加で依頼すればいいだけじゃん」というのも納得できます。
ただ、一方で「権限不足が発覚 -> Terraform コードを修正 -> 追加で SRE にレビュー依頼」の頻度が多くなってしまうのは、変更を依頼する側も変更内容をレビューする側もアンハッピーな状態であることは想像に難くありません。
つまり、Terraform コードを書いてレビューを依頼する側が、現在の IAM Policy の記述内容が「"やりたいこと" に対して、権限的に "できること" が充足しているか?」を Terraform を実際に 反映する前に確認できると、 問題は緩和されそうです。
とても、前置きが長くなってしまいましたが、そこでタイトルの IAM Policy Simulator のお話につながるわけです。
IAM Policy Simulator とは
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_testing-policies.html
公式ドキュメントの説明をお借りします。
IAM Policy Simulator を使用すると、アイデンティティベースのポリシー、IAM アクセス許可の境界、組織のサービスコントロールポリシー、リソースベースのポリシーをテストおよびトラブルシューティングできます
この IAM Policy Simulator を使用すると、今回のケースの Atlantis 用の IAM Role に付与されている権限で「できること」「できないこと」を事前に確かめることができそうです。
また、Policy Simulator を安全に使いたい開発者や、AWS アカウントの権限管理者の双方にとって、安心できる仕様も明記されています。
- IAM Policy Simulator は 実際の AWS サービスリクエストを行わない
- 勝手にリソースを変更してしまうことはない
- IAM Policy Simulator は選択したリクエストされたアクションが許可されるか拒否されるかのみを返す
- Get や List 系のリクエストによって、誤ってリソースの情報を IAM Policy Simulator の実行者に見られることはない
- IAM Policy Simulator 内でポリシーを編集した場合も AWS アカウントの対応するポリシーが変更されることはない
- 勝手に権限を変更してしまうことはない
IAM Access Analyzer との違い
ここで IAM Pollicy Simulator のように IAM の権限やアクセス範囲を検証する IAM Access Analyzer についても言及したいと思います。
https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/what-is-access-analyzer.html
こちらも公式ドキュメントの説明をお借りします。
AWS IAM Access Analyzer の機能は、外部エンティティと共有されている Amazon S3 バケットや IAM ロールなど、組織とアカウントのリソースを識別するのに役立ちます。これにより、セキュリティ上のリスクであるリソースやデータへの意図しないアクセスを特定できます。Access Analyzer は、ロジックベースの推論を使用して AWS 環境のリソースベースのポリシーを分析することにより、外部プリンシパルと共有されているリソースを識別します。
今回の私達のシチュエーションでは、「アイデンティティベースのポリシーを作成前/更新前に検証すること」がメインの目的であり、同一 AWS アカウント内で権限が完結していたので「外部エンティティによる意図しないアクセス許可」の検証は基本的には不要だったのもあり、 IAM Policy Simulator を使いました。
IAM Policy Simulator を使う前に
「IAM Policy Simulator 良さそう!よし使おう!」となる前に
- 誰に対して許可したいか?
- どのリソースに対して許可したいか?
- どの操作を許可したいか?
が事前に適切に整理できていると、更に精度の高い検証が可能になります。
誰に対して許可したいか?
AWS のリソースの文脈では、基本的に「誰に」の部分には特定の IAM User か IAM Group か IAM Role が当てはまるはずです。 少し抽象度を上げると「どの社員のアカウントに」「どの開発チームのメンバーに」「どのシステム/サーバーに」といった表現になるでしょうか。
今回の私達のケースでは「Atlantis に対して許可したい」となります。 もう少し具体的に書くと「Atlantis 経由で Terraform を実行する際に AssumeRole する IAM Role に必要な権限を付与したい」となります。
この「誰に対して許可したいか?」によって、 IAM Policy Simulartor で行う検証の操作が多少変わってくるため、事前に整理しておくと良いでしょう。
どのリソースに対して許可したいか?
AWS のリソースに対して操作を加える際には、 悪意の無いヒューマンエラーによる「間違ってお隣のシステムのリソースを変更してしまった!/削除してしまった!」 という事故が考えられます。 また、万が一 AWS の認証情報が流出してしまった場合にも、 攻撃者によって「AWS リソースにアクセスされる/変更される/削除される」 といった最悪の事態も考えられます。
そのため、操作を許可するリソースの範囲を必要最低限に絞ることで、リスクを最小限にする必要があります。
AWS のリソースを特定する ARN の表現方法は AWS のサービスのアクション、リソース、および条件キー の左メニューから関心のある AWS リソースを選択し、確認できます。
例えば、「S3 の特定のバケット配下のオブジェクト全て」という表現方法を知りたい場合は こちら で確認できます。
ARN の一例
- tasmaniadecoco-test という S3 バケット直下の index.html を指定する場合は
arn:aws:s3:::tasmaniadecoco-test/index.html
- tasmaniadecoco-test という S3 バケット配下のオブジェクト全てを指定する場合は
arn:aws:s3:::tasmaniadecoco-test/*
と表現できます。
どの操作を許可したいか?
「どのリソースに対して許可したいか?」とは考え方は基本的には同じです。 許可する操作の種類を必要最低限に絞ることで、リスクを最小限にする必要があります。
AWS のリソースに対する操作は基本的に AWS が提供する API 経由で行うことになります。 そして、その API と対応する各 AWS リソースに対するアクションの一覧が定義されています。
AWS のリソースに対するアクションの表現方法は AWS のサービスのアクション、リソース、および条件キー の左メニューから関心のある AWS リソースを選択し、確認できます。
例えば、「S3 のバケットのオブジェクト一覧を取得する」というアクションの表現方法を知りたい場合は こちら で確認できます。
主なアクションの指定方法の一例
- S3 バケットを一覧で取得するアクションの場合は
s3:ListAllMyBuckets
- S3 バケット内のオブジェクト一覧を取得するアクションの場合は
s3:ListBucket
- S3 バケット内にオブジェクトを追加する場合は
s3:PutObject
- S3 バケット内のオブジェクトを削除する場合は
s3:DeleteObject
と表現できます。
どんな条件下で許可したいか?
ここまでで「どのリソースに対して許可したいか?」と「どの操作を許可したいか?」を指定する表現を確認しました。 実際に Policy として記述する際は、これらの要素を組み合わせて表現します。
{ "Version":"2012-10-17", "Statement":[ { "Effect":"Allow", "Action":[ "s3:GetObject" ], "Resource":[ "arn:aws:s3:::tasmaniadecoco-test/index.html" ] } ] }
上記は 「tasmaniadecoco-test という S3 バケット直下の index.html を取得できる」という権限の表現例です。
しかし、ユースケースによっては上記の権限が有効になる条件を更に絞りたい場合があります。 例えば、「アクションの呼び出し元の IP アドレスの範囲を絞りたい」といったケースです。
このような追加の条件は Policy を表現する JSON 内に "Condition" 要素を追加することで指定できます。
指定できる条件は下記のドキュメントから確認できます。
- AWS グローバル条件コンテキストキー
- AWS リソースに依存せずに指定できる条件
- Amazon S3 の条件キー
- S3 に対するアクションに定義されている条件
今回の私達のケースでは "Condition" 要素を使う必要がなかったため、詳細は割愛します。
IAM Policy Simulator の使い方
New Policy Mode と Existing Policies Mode
IAM Policy Simulator には検証用の Policy を即席で作成して検証する New Policy Mode
と既存の Policy を検証する Existing Policies Mode
があります。
New Policy Mode
では IAM Policy Simulator のエディタ上に JSON 形式で IAM Policy を記述し、その Policy を使って特定のリソースに対する特定の操作の可否を検証できます。
Existing Policies Mode
では、「既に存在する特定の IAM User/Group/Role にどんな権限が付与されているか?」を検証できます。
更に、Existing Policies Mode
では既存の IAM User/Group/Role にアタッチされている Policy をベースに、権限の記述を追加/変更/削除して検証することもできます。
つまり「もし、この Role に OO という操作の許可が加わったら・・・」という検証を 実際の権限の定義を変更することなく IAM Policy Simulartor 上だけで行うことができます。
ここでは New Policy Mode
で即席で作った Policy によって、実際に特定のリソースに対して特定のアクションが許可されていること・許可されていないことを確かめている実例を見ていきます。
検証の際は下記の Policy を使います。
{ "Version":"2012-10-17", "Statement":[ { "Effect":"Allow", "Action":[ "s3:ListBucket", "s3:GetObject" ], "Resource":[ "arn:aws:s3:::tasmaniadecoco-test", "arn:aws:s3:::tasmaniadecoco-test/*" ] }, { "Effect":"Allow", "Action":[ "s3:PutObject" ], "Resource":[ "arn:aws:s3:::tasmaniadecoco-test/index.html" ] } ] }
許可されていることを確かめる
例1) tasmaniadecoco-test という S3 バケットに ListBucket を実行する
Policy の記述では、 tasmaniadecoco-test という S3 バケットに対する ListBucket を許可していたので、 allowed
という表示とともに、権限が付与されていることを確認できました。
例2) tasmaniadecoco-test/test.html という S3 オブジェクトに GetObject を実行する
Policy の記述では、 tasmaniadecoco-test という S3 バケット配下の全てのオブジェクトに対する GetObject を許可していたので、 allowed という表示とともに、権限が付与されていることを確認できました。 また、例1 の検証結果も画面に表示されていますが、IAM Policy Simulartor では複数のアクションを同時に検証できます。
例3) tasmaniadecoco-test/index.html に対して PutObject を実行する
徐々に検証結果の見方に慣れてきたかと思うので、赤枠による補足を省略しています。
Policy の記述では、 tasmaniadecoco-test という S3 バケット配下の index.html に対してのみ PutObject を許可していたので、 allowed という表示とともに、権限が付与されていることを確認できました。
許可されていないことを確かめる
例4) tasmaniadecoco-not-allowed という S3 バケットに ListBucket を実行する
Policy の記述では、 tasmaniadecoco-test という S3 バケットに対する ListBucket しか許可していないので、 tasmaniadecoco-not-allowed という名前の S3 バケットに対する ListBucket アクションは denied
という表示とともに、権限が付与されていないことを確認できました。
図中の Implicitly denied (no matching statements)
に関しては、「明示的に許可していない = デフォルトでは許可しない」という挙動による denied であることを示しています。
一方で、今回の例では言及しませんが、「"Effect" に "Deny" を指定することによる明示的なアクセス拒否」も存在します。
例5) tasmaniadecoco-test/not-allowed.html に対して PutObject を実行する
Policy の記述では、 tasmaniadecoco-test という S3 バケット配下の index.html に対してのみ PutObject を許可していたので、 not-allowed.html というオブジェクトに対する PutObject アクションは denied
という表示とともに、権限が付与されていないことを確認できました。
IAM Policy SImulator を使った検証で感じたこと
今回 IAM Policy Simulator を使おうと思ったきっかけに立ち返ると 「 Atlantis 経由で Terraform を実行する前に、 Atlantis に付与する IAM Role に必要な権限が付与されているかを事前に検証したい」 というのがモチベーションでした。
実際にこの方法で検証を進めていくなかで、いくつか感じたことがありました。
Terraform の操作と AWS のアクションの対応を把握する必要がある
IAM Policy Simulator で検証できたのは AWS の API による操作の結果です。 しかし、実際は Terraform 経由でリソース変更を行うため、記述した Terraform コードが暗黙的に実行する AWS の API をある程度予想・把握する必要があります。 (実際 Terraform 実行時に暗黙的に List 系のアクションを実行していることが判明したこともありました。)
今回の記事で「リソース」と「アクション」の表現方法について言及したのも、Terraform コードが暗黙的に実行する可能性のある AWS の API にある程度当たりをつけるためでもありました。 副次的に AWS のリソースとアクションの一覧を見ることで、「そのリソースで他に何ができるか?」が見えてきて、学びもありました。
IAM Policy Simulator に少し使いづらさがある
検証するアクションを選択する際に "Select actions" というプルダウンから所望のアクションを選択するのですが、アクションを絞り込むインターフェイスが提供されておらず、いつもブラウザの画面内検索で探していました笑
さいごに
今回、 IAM Policy Simulator を使った IAM の事前検証について紹介しました。 IAM Policy Simulator 自体の使い方だけでなく、AWS の「リソース」と「アクション」の確認方法についても触れています。
「分かりにくい」と表現されがちな AWS の IAM ですが、この記事を見て、少しでも IAM が捉えやすいものになれば幸いです。