AWS CDKを利用した権限昇格
AWS CDKは、AWS公式のIaCフレームワークです。CloudFormationを通じてAWSリソースの構築、管理が可能になります。CloudFormationを直接使う場合と比べてより抽象度の高い簡潔な記述ができる等の長所があり、広く利用されています。
便利なCDKですが、その利用にあたりセキュリティ上の注意事項があります。CDKによるAWSリソース構築はデフォルトでAdministratorAccess権限にて実行されるので、非特権ユーザーであってもCDKを実行可能であればフル権限を取得できてしまいます。これは権限昇格の深刻なセキュリティホールにつながります。
本稿では、CDKによデプロイを開発者に許可しつつも、想定外の権限昇格を防止するための方法を紹介します。
CDKデプロイ時の権限推移のおさらい
以下は、本稿の前提知識となるCDKデプロイ時の権限推移を示した概要図です。
まずCDKデプロイの前に、CDKが使用するIAMロール等のリソースを事前に作成しておく必要があります。この事前の環境構築がブートストラップです。ブートストラップはIAM操作を伴うので、管理者など強い権限を持つエンティティによって行われます。
ブートストラップ完了後、開発者等のIAMエンティティがCDKデプロイを行うと、以下の流れで権限が推移します。
- 開発者のIAMエンティティが
cdk deployを実行する - CDKが、開発者IAMエンティティの権限でdeployロールをAssumeRoleする
- CDKが、deployロールの権限でcfn-execロールをCloudFormationにPassRoleする (cfn-execロールはCloudFormationのサービスロールとして使用される)
- CloudFormationが、cfn-execロールの権限でスタック操作する
最終的にcfn-execロールの権限でリソース操作が行われることになります。
権限昇格の問題となるのはcfn-execロールです。このロールはブートストラップ時に作成され、デフォルトで無制限のAdministratorAccess権限を持ちます。上記のとおり、deployロールのAssumeRoleさえできれば開発者など非特権エンティティでもcfn-execロールのAdministratorAccess権限でAWSリソースの操作が可能になってしまいます。
権限昇格を防止するには
cfn-execロールの権限は、ブートストラップ時にカスタマイズすることが可能です。IAMロールの権限制御には2種類の方法があります:
- 許可ポリシーによる制限
- アクセス境界 (Permission Boundary) による制限 1
以下で示す通り、許可ポリシーによる制限はある程度有用ですが、これのみに頼ることは推奨されません。アクセス境界を用いた防御を本稿では主に取り上げます。
許可ポリシーによる制限
CDKデプロイ時に必要となる最小権限がもし事前に分かっているなら、許可ポリシーによる制限は有用でしょう。しかし、現実的には必要な最小限の許可ポリシーを事前に定義することは難しいケースが多いでしょう。
- 必要な最小権限の特定に時間が掛かる
- 必要な権限は時間や状況とともに変化するので事前の特定が困難
- 権限が不足していた場合、開発作業がブロックされ生産性が低下する
また、IAMロール/ポリシーの作成をどう管理するかという問題もあります。小さなLambda関数にもロールを割り当てる必要があります。しかしながらIAMロール/ポリシーの作成を許可すると、特権を持ったロールの作成を許す抜け穴につながります。結果的に権限昇格を招きます。IAMをCDK管理外としてロール/ポリシー作成を禁止することも可能ですが、CDKによるIaC管理のメリットが損なわれてしまいます。
アクセス境界による制限
ロールにアクセス境界を設定すれば、たとえAdministratorAccess権を持っていたとしても、アクセス境界ポリシーの定義を超えた操作は拒否できるようになります。アクセス境界には主に以下の2点を設定します。
- 権限昇格の抑止ポリシー
- 当該アクセス境界を付与しないIAMエンティティの作成を禁止する
- 当該アクセス境界の変更/削除を禁止する
- IAMエンティティからアクセス境界の除去を禁止する
- 組織固有のセキュリティガードレールポリシー
- 例: AWS Config、CloudTrail、Billings等の設定変更を禁止する
- etc,
アクセス境界ポリシーにより、ガードレールを逸脱した操作を禁止すると同時に、当該アクセス境界を付与しないIAMエンティティの作成を禁止することができます。IAMロール作成は許可しつつも、作成したロールにも推移的なガードレール適用を強制できるようになります。これにより、ガードレールを超えた権限昇格を抑止できます。
なおCDKでは、デプロイ時に作成されるIAMロールに特定のアクセス境界ポリシーを自動的に適用させる設定が可能であり、適用漏れによるIAMロール作成失敗(=デプロイ失敗)を防止することができます (具体的な方法は後述)。
アクセス境界ポリシーの定義例
cdk bootstrap --show-template を実行すると、ブートストラップで使用されるCloudFormationテンプレート (ブートストラップテンプレート) を出力できます。実はこのテンプレートの中にアクセス境界ポリシーの汎用的な定義例が参考情報として書かれています (テンプレートはGitHubでも公開されています)。以下のYAMLファイルは、このテンプレートのアクセス境界定義に、コメント文と固有のセキュリティガードレールポリシー例を追加したものです2。
Resources:
CdkBoostrapPermissionsBoundaryPolicy:
Condition: ShouldCreatePermissionsBoundary
Type: AWS::IAM::ManagedPolicy
Properties:
PolicyDocument:
Statement:
###############################################
# 基本ポリシー (権限昇格抑止ポリシー)
###############################################
# 許可ポリシー定義の操作を明示的に許可する
# これが無いと全操作が禁止されてしまう
- Sid: ExplicitAllowAll
Action:
- "*"
Effect: Allow
Resource: "*"
# 当該アクセス境界を付与しないIAMエンティティの作成を禁止する
- Sid: DenyAccessIfRequiredPermBoundaryIsNotBeingApplied
Action:
- iam:CreateUser
- iam:CreateRole
- iam:PutRolePermissionsBoundary
- iam:PutUserPermissionsBoundary
Condition:
StringNotEquals:
iam:PermissionsBoundary:
Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region}
Effect: Deny
Resource: "*"
# 当該アクセス境界の変更/削除を禁止する
- Sid: DenyPermBoundaryIAMPolicyAlteration
Action:
- iam:CreatePolicyVersion
- iam:DeletePolicy
- iam:DeletePolicyVersion
- iam:SetDefaultPolicyVersion
Effect: Deny
Resource:
Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region}
# IAMエンティティからアクセス境界の除去を禁止する
- Sid: DenyRemovalOfPermBoundaryFromAnyUserOrRole
Action:
- iam:DeleteUserPermissionsBoundary
- iam:DeleteRolePermissionsBoundary
Effect: Deny
Resource: "*"
###############################################
# 組織固有のセキュリティガードレールポリシー
###############################################
# 例: Configの変更を禁止する
- Sid: DenyModificationOfConfig
Action: config:*
Effect: Deny
Resource: "*"
Version: "2012-10-17"
Description: Bootstrap Permission Boundary
ManagedPolicyName:
Fn::Sub: cdk-${Qualifier}-permissions-boundary-${AWS::AccountId}-${AWS::Region}
Path: /
アクセス境界の適用方法
cfn-execロールはブートストラップで作成されるので、ロールへのアクセス境界適用はブートストラップ時に設定する必要があります。方法は以下の2つがあります。
- アクセス境界定義を含めたブートストラップテンプレートを作成し、
cdk bootstrap --templateオプションで指定する - アクセス境界ポリシーを事前に作成し、
cdk bootstrap --custom-permissions-boundaryオプションでそのポリシーを指定する
本稿では上記1番の--templateオプションを使用する方法を取り上げます。
本稿では扱いませんが、cfn-execロールだけでなく、ブートストラップで作成される全ロールに対してアクセス境界を適用する場合は、--templateオプションを使用する方法1のみが使用可能です。アクセス境界ポリシーを付与した全ロール定義をブートストラップテンプレートに記載し、cdk bootstrap --template で指定すれば実現できます。一方、方法2の --custom-permissions-boundary オプションで指定したアクセス境界ポリシーは、cfn-execロールのみに適用されます。
以下は操作手順です。
-
cdk-cliでデフォルトのブートストラップテンプレートを出力する
cdk bootstrap --show-template > bootstrap-template.yaml -
ブートストラップテンプレートを編集し、アクセス境界を無条件で適用する (以下のdiffを参考にしてください。ポリシー定義は「アクセス境界ポリシーの定義例」で記載した内容と同一です)
yamldiff --git a/bootstrap-template.yaml b/bootstrap-template.yaml index 2d86a3d..e387a1c 100644 --- a/bootstrap-template.yaml +++ b/bootstrap-template.yaml @@ -701,12 +701,8 @@ Resources: RoleName: Fn::Sub: cdk-${Qualifier}-cfn-exec-role-${AWS::AccountId}-${AWS::Region} PermissionsBoundary: - Fn::If: - - PermissionsBoundarySet - - Fn::Sub: arn:${AWS::Partition}:iam::${AWS::AccountId}:policy/${InputPermissionsBoundary} - - Ref: AWS::NoValue + Ref: CdkBoostrapPermissionsBoundaryPolicy CdkBoostrapPermissionsBoundaryPolicy: - Condition: ShouldCreatePermissionsBoundary Type: AWS::IAM::ManagedPolicy Properties: PolicyDocument: @@ -743,6 +739,10 @@ Resources: - iam:DeleteRolePermissionsBoundary Effect: Deny Resource: "*" + - Sid: DenyModificationOfConfig + Action: config:* + Effect: Deny + Resource: "*" Version: "2012-10-17" Description: Bootstrap Permission Boundary ManagedPolicyName: -
上記のテンプレートを使用し、ブートストラップを実行する
cdk bootstrap --template bootstrap-template.yaml ${environment}
ブートストラップが完了すれば、以下のようにアクセス境界が設定されたcfn-execロールが作成されます。
アクセス境界の動作確認
それではアクセス境界の動作確認を行います。
権限昇格を試みる
権限昇格のために、AdministratorAccess権限を持ちアクセス境界を適用しない、権限昇格可能なIAMロールの作成を試みてみます。cfn-execロールのアクセス境界ポリシーが、同じアクセス境界を設定しないロール作成を禁止しているので、操作は失敗するはずです。
以下がスタック定義です。
export class PermissionBoundaryDemoStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);
// AdministratorAccessを持つIAMロールを作成
new iam.Role(this, 'AdminRole', {
assumedBy: new iam.AccountRootPrincipal(),
managedPolicies: [
iam.ManagedPolicy.fromAwsManagedPolicyName('AdministratorAccess'),
],
});
}
}
cdk deployの結果です。期待通りアクセス境界ポリシー違反によりロール作成が失敗しました。
$ cdk deploy
(中略)
0:12:34 PM | CREATE_FAILED | AWS::IAM::Role | AdminRole38563C57
Resource handler returned message: "Encountered a permissions error performing a tagging operation, please add required tag permissions. See https://repost.aws/knowledge-center/cloudformation-tagging-permission-error for how to resolve. Resource hand
ler returned message: "User: arn:aws:sts::xxxxxxxxxxxx:assumed-role/cdk-hnb659fds-cfn-exec-role-xxxxxxxxxxxx-ap-northeast-1/AWSCloudFormation is not authorized to perform: iam:CreateRole on resource: arn:aws:iam::xxxxxxxxxxxx:role/PermissionBoundaryD
emoStack-AdminRole38563C57-d1JsjxWNek8U with an explicit deny in a permissions boundary (Service: Iam, Status Code: 403, Request ID: c11cf83a-9b50-4713-a07a-375f274ae8a7) (SDK Attempt Count: 1)"" (RequestToken: dce833d2-e688-f03a-0f81-8f8e10590619, H
andlerErrorCode: UnauthorizedTaggingOperation)
アクセス境界ポリシー準拠のロールを作成してみる
アクセス境界ポリシーにより、同一アクセス境界を設定しないロールの作成が失敗することを確認できたので、次にポリシーに準拠し、同一アクセス境界によるガードレールを敷いたAdministratorAccess権限を持つロールの作成を行います。
作成するロールにアクセス境界を設定する方法はいくつかありますが、ここでは最も簡単な方法を紹介します。設定ファイル cdk.json の .context.@aws-cdk/core:permissionsBoundary.name にアクセス境界ポリシー名を指定すれば、スタックで定義したロールにアクセス境界が自動設定されます。スタック定義を書き換える必要もありません。各ロール個別にアクセス境界を設定することも可能ですが、設定ファイルに記載しておけば設定漏れが防げ、かつ管理も楽なので便利です。
{
"context": {
"@aws-cdk/core:permissionsBoundary": {
"name": "{ポリシー名}"
}
}
}
上記の通り cdk.jsonを設定し、cdk deploy すると今度はロール作成が成功します。以下の通り、ロールにはアクセス境界も期待通り設定されていることが確認できます。
cfn-execロールに設定したアクセス境界によるガードレールを、CDKで構築される他のIAMエンティティにも継承させることができました。これによりCDKを通じた権限昇格を防止することができます。
さいごに
本稿でお伝えした要点をまとめます。
- AWS CDKのcfn-execロールはデフォルトで無制限のAdministratorAcess権限を持つ点に注意
- 権限昇格の防止およびセキュリティガードレールのための具体的なアクセス境界ポリシーを紹介
- CDKで作成されるロールにアクセス境界を設定する方法を紹介
本稿ではアクセス境界によりIAMエンティティレベルでガードレールを敷く方法を説明しました。
なお、本稿でお伝えしたアクセス境界によるガードレールとは異なるレベルの対策として、AWS OrganizationsのSCP (サービスコントロールポリシー) によるガードレールも有用です。アクセス境界がIAMエンティティレベルで機能するのに対して、SCPはアカウントレベルで機能します。それぞれターゲットレベルが異なるので組織の方針に応じて上手く使い分けるとよいでしょう。
参考資料
- CDK Security and Safety Dev Guide
- Secure CDK deployments with IAM permission boundaries
- Permissions boundaries for IAM entities
- AWS CDK bootstrapping


