プログラミング
AWS Secrets Manager Terraform: 最小権限アクセス
AWS Secrets Manager Terraform: Least-Privilege Access (blog.victorsilva.com.uy)
要約
AWS Secrets Managerで最小権限アクセスを実現する方法を、Terraformを用いて解説します。修正不可能な広範なIDベースポリシーしかない場合でも、シークレットにリソースベースポリシーを直接アタッチすることで、アクセスを効果的に制限できることを説明しています。このパターンは、AWS Academy Learner Labsのような環境で特に有効です。
全文翻訳
あなたはAWSアカウントへのアクセス権限を付与されたばかりで、与えられたID(ロールであり、ユーザーではない)にはすでに広範な権限が付与されています。あなたはそれを生成したわけではなく、編集することもできません。IAM:CreatePolicyを実行してより狭い範囲のポリシーを作成することも、もちろんできません。それにもかかわらず、あなたの目の前のタスクは、このデータベースパスワードをAWS Secrets Managerに保存し、Terraformでプロビジョニングし、アプリケーションのみがそれを読み取れるようにすることです。もしあなたの直感が「IAMを制御しない限り不可能だ」ということであれば、あなたは最小権限を一面的な見方でしか捉えていません。
この状況は、見た目よりも一般的です。AWS Academy Learner Labsは、すべての学生に、広範なIDポリシーが付与され、IAM:CreateRole権限を持たない、事前に作成されたLabRoleを渡します。クロスアカウントの設定では、あなたが所有していない別のアカウントから引き受けられたロールを渡されることがよくあります。ベンダー管理の実行環境(SaaSプラットフォームの「AWSアカウントを持ち込む」統合、中央プラットフォームチームがすべてのIAMポリシーを所有するSCPロックされたランディングゾーン)は、あなたを全く同じ状況に置きます。つまり、あなたは作業するためのIDを持っていますが、そのIDができることをIAMレイヤーで狭める能力はゼロです。
この記事では、AWS Secrets Managerに特化したその問題を解決するパターンをステップバイステップで解説します。触ることができないIDベースのポリシーと戦う代わりに、シークレットに直接リソースベースのポリシーをアタッチします。AWSは両方のレイヤーを評価し、十分に絞り込まれたリソースポリシーは、広範すぎるIDポリシーを補償します。Terraformを使用して、シークレットの作成、安全な値の書き込み、必要なものだけにアクセスを制限するリソースポリシーのレイヤリングをエンドツーエンドで構築し、肯定的なテストと否定的なテストの両方で実際に機能することを確認します。この記事では意図的にカバーしないことの一つは、シークレットのローテーションです。シークレットを作成し、誰がそれを読み取れるかをロックダウンすることは、それ自体で大きなトピックであり、Lambda関数によるローテーションはそれ自身の記事に値します。これは次に続く予定です。
デュアルレイヤーアクセスモデル:IDポリシーが機能しない場合にリソースポリシーが機能する理由
AWS Secrets Manager(および一般的にIAM)のすべての認可決定は、同じリクエストに対して2つの独立したポリシータイプを評価した結果です。
IDベースポリシー — 呼び出しを行うプリンシパル(ユーザー、ロール、またはフェデレーテッドID)にアタッチされます。これは「このIDは何ができるか」というポリシーです。私たちのシナリオでは、これはLabRoleであり、広範で、事前に作成されており、手が届きません。
リソースベースポリシー — アクセスされるリソースにアタッチされます。Secrets Managerの場合、これはaws_secretsmanager_secret_policyリソースです。これは「誰がこの特定のシークレットにアクセスできるか」というポリシーであり、実際に制御できる唯一のレバーです。
AWSはすべてのリクエストに対して両方のレイヤーを評価し、その組み合わせロジックは、一度理解すればシンプルです。
どちらかのレイヤーに、リクエストに一致する明示的な拒否(Deny)が含まれている場合、リクエストは拒否されます。それ以上何も問題になりません。
どちらのレイヤーにも拒否が含まれていない場合、少なくとも一方のレイヤーに、リクエストに一致する明示的な許可(Allow)が含まれている場合にのみ、リクエストは許可されます。明示的に許可されていないものはすべて暗黙的に拒否されます。
このパターンを可能にする部分は次のとおりです。シークレットにリソースベースポリシーが存在する場合、AWSはそのリソースに対して権威があるとみなします。IDポリシーで広範なsecretsmanager:*権限を持つプリンシパルは、シークレットのリソースポリシーがアクセスを許可しない場合でも拒否されます。逆に、シークレットにアタッチされた狭いリソースポリシーは、IDポリシーだけでは問題にならないプリンシパルにアクセスを許可できます。しかし、私たちにとってより重要なのは、それはまた、リソースポリシーの許可を特定のプリンシパル、特定の操作、および特定の条件にスコープすることによって、過度に広範なIDポリシーが本来許可するであろうものを制限できるということです。
平易な言葉で言えば、LabRoleがIDレイヤーで*に対してsecretsmanager:GetSecretValueを呼び出すことが許可されていても、シークレットのリソースポリシーが「はい、そしてこのプリンシパルに限り、これらの条件下で」と言わない限り、それは問題になりません。あなたはLabRoleを狭めているのではありません。あなたは、その自身のポリシーがどれほど寛容であっても、LabRoleが通過しなければならないシークレットの周りにゲートを構築しているのです。
リクエスト:LabRoleがシークレットXに対してsecretsmanager:GetSecretValueを呼び出す
│ ┌───────────┴────────────┐
│ │ IDポリシー リソースポリシー
│ │ (LabRole — 広範、 (シークレットXにアタッチ —
│ │ 編集不可) 制御可能なレバー)
│ │
│ │ 許可 (暗黙的、 │ 許可、LabRole ARNにスコープ
│ │ 広範なワイルドカード) │ + VersionStage = AWSCURRENT
│ └───────────┬────────────┘
│ 両方のレイヤーが許可する必要があり、どちらかのレイヤーでの明示的な拒否が絶対的に優先されます
│ ┌──────┴──────┐
│ 決定
│ └─────────────┘
これはクラウドセキュリティにおける補償的統制のメカニズムです。根本原因を修正できない場合、爆発半径を制約する2番目の独立したレイヤーを追加します。これはAcademyに固有の回避策ではなく、形状を変えられないIDを引き継ぐ場合はいつでも正しいパターンです。
開始前の前提条件と自己チェック
必要となるもの:
Terraform >= 1.5 (例ではメインラインのsecret_stringパターンを使用しています。Terraform >= 1.11を使用している場合は、書き込み専用の代替手段も示します)
AWSプロバイダー hashicorp/aws >= 5.0
AWS CLI v2、ラボセッションの認証情報で設定済み (Academyセッションは通常約4時間で期限切れになり、一時的な認証情報の再コピーが必要です)
アクティブなAWS Academy Learner Labセッション、または変更できない事前作成されたロールで作業している任意のAWS環境
Academyの許可されたサービスの正確な許可リストは公式には公開されておらず、コースやラボテンプレートによって異なります。想定しないで、確認してください。
Terraformの最初の1行を書く前に、2つのことを確認してください。あなたが誰であるか、そしてこのセッションでSecrets Managerがそもそも到達可能かどうかです。
# セッションが実際に使用しているIDを確認する
aws sts get-caller-identity
# Secrets Managerが到達可能で、このラボで許可されているか確認する
aws secretsmanager list-secrets --region us-east-1
最初のコマンドは、assumed-role/LabRole (またはコーステンプレートによってはvoclabs) を含むARNを返すはずです。2番目のコマンドは、新しいアカウントではAccessDeniedExceptionではなく、空のSecretList: [] を返すはずです。2番目のコマンドが失敗した場合は、ここで停止してください。この特定のラボテンプレートはSecrets Managerを公開しておらず、Terraformのどれを使ってもそれを修正することはできません。
以下のすべての例は、Academyのデフォルトリージョンであるus-east-1を対象としています。これはaws_region変数で完全に設定可能ですが、Academyラボは使用可能なリージョンを制限する場合があるため、切り替える前に確認してください。
ステップバイステップの実装
プロバイダーと変数
プロバイダーブロックに特別なものはありませんが、バージョンをピン留めしてください。リソースポリシーの動作とblock_public_policyのサポートは特定のプロバイダーリリースで導入され、Terraform on a different machineでinitしたときに、依存しているフィールドをサポートしていないバージョンをサイレントに選択したくないからです。
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 5.0"
}
}
required_version = ">= 1.5"
}
provider "aws" {
region = var.aws_region
}
variable "aws_region" {
description = "AWS region for all resources"
type = string
default = "us-east-1"
}
variable "db_password" {
description = "Secret value for the database password. Set via TF_VAR_db_password, never committed."
type = string
sensitive = true
}
db_password変数はデフォルト値が設定されていません。Terraformを実行する前に環境変数としてエクスポートします。これにより、.tfファイル、シェル履歴(先頭にスペースを使用すると、ほとんどのシェルはログに記録しません)、およびバージョン管理から完全に除外されます。
export TF_VAR_db_password="$(openssl rand -base64 24)"
terraform apply
LabRole ARNの動的解決
リソースポリシーで参照するためにLabRoleの完全なARNが必要ですが、そのARNにはアカウントIDが含まれています。アカウントIDはハードコードすべきではありません。Academyがラボをリセットするたびに変わるためだけでなく、コンテキストに関係なくアカウントIDをTerraformにハードコードすることは移植性の問題があるためです。aws_caller_identityデータ