長期キーなしで権限を渡す
目標
ロールが長期のキーより安全なのは確かですが、信頼ポリシーを緩く書くと、その安全がまるごと消えます。このラボは、クラウドアカウントなしで、信頼ポリシーを自分で書き、危険な形を見つける検査器を手で作って、その場所を確認します。
信頼ポリシーと権限ポリシー
2つのドキュメントを混同すると、何も合いません。
- 権限ポリシー: このロールが何をできるか
- 信頼ポリシー: 誰がこのロールを引き受けられるか
このラボで使うのは、信頼ポリシーのほうです。最後のステップ1つだけが、権限ポリシーです。
条件
- アプリケーションはEC2インスタンスの上で動きます。
- 協力会社のアカウント
210987654321に、読み取り権限を開ける必要があります。 - CIは、リポジトリ
acme/platformのrefs/heads/mainからだけデプロイします。 - 開発者は、ECSタスクを作るときに、タスクロールを付けられる必要があります。
文法の確認
ポリシーはたいてい、コンソールではなくコードで入ります。JSONが壊れていると、エラーメッセージがポリシーの内容と無関係に出るので、毎回確認するほうが速いです。
python3 -m json.tool /root/roles/trust-ec2.json
作るもの
| ファイル | 内容 |
|---|---|
/root/roles/trust-ec2.json |
インスタンスが引き受けるロール |
/root/roles/trust-partner.json |
外部アカウントに開けるロール |
/root/roles/trust-ci.json |
フェデレーションで引き受けるロール |
/root/roles/lint_trust.py |
危険な信頼ポリシーの検査器 |
/root/roles/passrole.json |
ロールを渡す権限 |
/root/roles/06-session.md |
セッション時間の計算 |
/root/roles/07-notes.md |
まとめ |
参考
ステップ4の検査器は、隠してある4つのポリシーと、自分が作った3つのポリシーの両方で実行されます。危険なものを見逃しても失敗で、安全なものを検出しても失敗です。
インスタンスが自分で資格情報を受け取るようにする
EC2インスタンスが引き受けられるロールの信頼ポリシーを、/root/roles/trust-ec2.jsonに書いてください。Versionは2012-10-17です。
信頼ポリシーは、「誰がこのロールを引き受けられるか」だけを決めます。何ができるかは、別の権限ポリシーにあります。
プリンシパルが人ではなくサービスであることが核心です。PrincipalにServiceを書き、アクションは、ロールを引き受けるその操作1つです。
このロールをインスタンスに付けると、その中で動くプログラムが、キーファイルなしで資格情報を得ます。期限切れの前に自動で更新されるので、人が手を加えることはありません。
外部アカウントに開けるときは、もう1欄を埋める
協力会社のアカウント210987654321が引き受けられるロールの信頼ポリシーを、/root/roles/trust-partner.jsonに書いてください。アカウント番号だけを書いてはいけません。
アカウント番号だけを書くと、そのアカウントの中の誰でもこのロールを引き受けられます。協力会社の社員1人の資格情報が漏れたら、こちらのロールが開きます。
そのため、双方だけが知る値を条件として一緒にかけます。sts:ExternalIdをStringEqualsで比較し、値は推測しにくいように8文字以上にしてください。
この条件がないと、その協力会社に仕事を任せた第三者が、協力会社を経由して、こちらのロールに届いてしまいます。
CIからキーをなくす
CIランナーがOIDCで引き受けるロールの信頼ポリシーを、/root/roles/trust-ci.jsonに書いてください。リポジトリacme/platformのrefs/heads/mainからだけ引き受けられる必要があります。
フェデレーションで引き受けるときは、アクションがsts:AssumeRoleではなくsts:AssumeRoleWithWebIdentityで、プリンシパルは、Principal.Federatedに書くIDプロバイダーです。
条件を抜かすと、そのCIサービスを使うどのリポジトリでも、こちらのロールを引き受けます。最もよく出るミスで、条件1行で防げます。
:subをStringEqualsで固定し、:audも一緒に確認してください。StringLikeに*を使って、リポジトリだけを書くと、そのリポジトリのどのブランチからでも引き受けられるので、条件がないのと大きく変わりません。
危険な信頼ポリシーを見つける検査器
/root/roles/lint_trust.pyを作成してください。python3 /root/roles/lint_trust.py <정책.json>(プレースホルダーはポリシーJSONです)で呼び出すと、危険なポリシーは何が危険かを出力して終了コード1、安全なポリシーは終了コード0で終わる必要があります。
3つを見ます。
- プリンシパルが開いているか。
Principal自体が*であるか、Principal.AWSが*であるもの - 外部アカウントを
Principal.AWSに置いているのに、sts:ExternalIdの条件がないもの sts:AssumeRoleWithWebIdentityなのに、:subの条件がないか、値に*があるもの
採点ツールは、前の3つのステップで作ったポリシーも、この検査器に入れてみます。安全なものを検出しても失敗です。誤警報が出始めると、人は検査器をオフにします。
ロールを渡す権限を絞る
開発者がECSタスクを作るときに、タスクロールを付けられる必要があります。その権限を/root/roles/passrole.jsonに書き、付けられるロールと渡す先のサービスまで絞ってください。
リソースを作るときに、そのリソースにロールを付ける操作は、実質的に、「自分にはできないことを代わりにやらせられる」権限です。Resourceを*にすると、低い権限のユーザーが、管理者ロールを付けたリソースを作って、権限を引き上げられます。
Resourceには、付けてよいロールのARNを書きます。名前の前半だけを決めて、後半を開ける方式でもかまいません。
iam:PassedToService条件で、どのサービスに渡すときだけ許可するかも固定してください。そうすれば、同じロールを別のサービスに渡す道が塞がれます。
セッション時間がそのまま対応速度になる
資格情報が発行された直後に漏れたとします。気づくまでに3時間、対応を決めるまでに1時間かかります。セッションが12時間のときと1時間のときで、権限を取り消したあとも残る時間をそれぞれ計算して、/root/roles/06-session.mdに3行以上書いてください。
ロールの権限を減らしても、すでに発行されたセッションは取り消されません。残りの有効時間の分は、そのまま使えます。
取り消しまで3 + 1時間かかります。12時間のセッションなら、そのあと何時間残りますか。1時間のセッションならどうなりますか。
数字を書いておけば、「セッションは短く」という言葉が、好みではなく計算になります。
3つをまとめる
/root/roles/07-notes.mdに3行以上。長期キーと一時的な資格情報の違い、信頼ポリシーが決めるもの、外部とフェデレーションで抜かしてはいけないもの。
本文に만료、신뢰 정책、조건が含まれている必要があります(韓国語で、順に「有効期限切れ」「信頼ポリシー」「条件」を意味する語です)。