こんにちは、みやもーと申します。
以前から様々なお客様でのAWS環境構築のお手伝いをさせていただいています。
その中でも、最近増えてきているのが複数のAWSアカウントを管理するための方法について、どのようにした良いのか?という相談です。
AWS リソースの増加やスケーリングに合わせて、環境を一元的に管理し、統制するためのサービスとして「AWS Organizations」があります。
今回は、この「AWS Organizations」が提供する機能のうち、サービスコントロールポリシー (SCP) の活用例をご紹介します。
- AWS Organizations
- サービスコントロールポリシー (SCP)
- SCPの実装例
- まとめ
AWS Organizationsは、複数のAWSアカウントを効率的に管理・統制するためのサービスです。
AWS Organizations を使うと、以下のようなことが実現できます。
- 新しい AWS アカウントを作成しリソースを割り当てる
- AWSアカウントをグループ化して整理
- AWSアカウントまたはグループに統一されたポリシーを適用
- すべてのAWSアカウントに単一の支払い方法を利用することで請求を簡素化
AWS Organizations の用語と概念について、以下にまとめます。
AWS契約の単位でメールアドレスや12桁のIDで識別されます。AWS Organizationsで管理する最小単位です。
AWS Organizationsで一元管理する対象の全体。複数AWSアカウントのセットです。
AWS Organizations組織の全体を管理する権限を持つAWSアカウント。以前はマスターアカウントと言われていました。
組織内にあるマスターアカウント以外のAWSアカウント。
組織単位 (OU) 階層の開始点。AWS Organizationsの初期セットアップ時に自動作成されます。
組織内にある複数AWSアカウントのグループ。ルートOUを頂点に階層構造で複数のOUを作成、管理することが出来ます。
組織内で適用してAWSアカウントを管理する仕組み。
「サービスコントロールポリシー (SCP) 」は、AWSアカウントまたはグループに統一されたポリシーを適用するための機能です。
現在サポートされている唯一の組織ポリシーのタイプで、利用できるAWSサービスとアクションを制御できます。
アクセス許可が付与されない点を除き、SCP は IAM 許可ポリシーと類似しており、基本的には同じ構文で記述できます。
構文の詳細はドキュメントに記載がありますが、以下が注意点となります。
- Action要素のワイルドカード (*) 文字は、ポリシー自体、または文字列の末尾にのみ使用可能(先頭もしくは中間には利用できない)
- 拒否ステートメント(Effect:Deny)のみNotAction要素が利用可能。許可ステートメント(Effect:Allow)では利用できない
- 許可ステートメント(Effect:Allow)ではResource要素は”*”しか指定できない
- 許可ステートメント(Effect:Allow)ではCondition要素は利用できない
- Principal要素、NotPrincipal要素、NotResource要素は利用できない
それでは、AWSドキュメントに記載のあるSCPの例から、いくつか実際に実装したものをご紹介します。
監査系のサービスであるAWS Configについて、無効化やルール変更を勝手にされてしまうと監査ログとしての信用性に欠けてしまいます。一般ユーザによる操作をSCPで禁止することで、アカウント内のIAMユーザにどんな権限を付与しても操作が出来なくなります。
{ |
"Version": "2012-10-17", |
"Statement": [ |
{ |
"Effect": "Deny", |
"Action": [ |
"config:DeleteConfigRule", |
"config:DeleteConfigurationRecorder", |
"config:DeleteDeliveryChannel", |
"config:StopConfigurationRecorder" |
], |
"Resource": "*" |
} |
] |
} |
「”Effect”: “Deny”」とあるので拒否ステートメントです。対象リソースは全て、AWS Configの無効化、ルール変更のアクションを拒否しています。
ただ、このままですと、このSCPが適用されたAWSアカウントではConfigルールの変更を行う場合はSCPの設定変更が必要となってしまいます。そのため、特定のIAMロールに対しては除外する設定を追加したのが以下です。
{ |
"Version": "2012-10-17", |
"Statement": [ |
{ |
"Effect": "Deny", |
"Action": [ |
"config:DeleteConfigRule", |
"config:DeleteConfigurationRecorder", |
"config:DeleteDeliveryChannel", |
"config:StopConfigurationRecorder" |
], |
"Resource": "*", |
"Condition": { |
"ArnNotLike": { |
"aws:PrincipalARN": [ |
"arn:aws:iam::*:role/role_sysadmin*" |
] |
} |
} |
} |
] |
} |
Condition要素のArnNotLike句で特定のロール(ここではロール名の先頭が「role_sysadmin*」)を指定、このロール以外は拒否するポリシーとしました。
実際にはこのロールに対する編集不可のポリシーも記載していますが、ここでは省略します。
リソースを作成する際、特定のタグを要求するポリシーです。ここでは、EC2インスタンス作成時に特定のタグが無いとインスタンスを開始できないよう制限しています。
{ |
"Version": "2012-10-17", |
"Statement": [ |
{ |
"Sid": "DenyCreateSecretWithNoProjectTag", |
"Effect": "Deny", |
"Action": "secretsmanager:CreateSecret", |
"Resource": "*", |
"Condition": { |
"Null": { |
"aws:RequestTag/Project": "true" |
} |
} |
}, |
{ |
"Sid": "DenyRunInstanceWithNoProjectTag", |
"Effect": "Deny", |
"Action": "ec2:RunInstances", |
"Resource": [ |
"arn:aws:ec2:*:*:instance/*", |
"arn:aws:ec2:*:*:volume/*" |
], |
"Condition": { |
"Null": { |
"aws:RequestTag/Project": "true" |
} |
} |
}, |
{ |
"Sid": "DenyCreateSecretWithNoCostCenterTag", |
"Effect": "Deny", |
"Action": "secretsmanager:CreateSecret", |
"Resource": "*", |
"Condition": { |
"Null": { |
"aws:RequestTag/CostCenter": "true" |
} |
} |
}, |
{ |
"Sid": "DenyRunInstanceWithNoCostCenterTag", |
"Effect": "Deny", |
"Action": "ec2:RunInstances", |
"Resource": [ |
"arn:aws:ec2:*:*:instance/*", |
"arn:aws:ec2:*:*:volume/*" |
], |
"Condition": { |
"Null": { |
"aws:RequestTag/CostCenter": "true" |
} |
} |
} |
] |
} |
「”Effect”: “Deny”」とあるので拒否ステートメントです。Condition要素でリソースタグに「Project」タグ、「CostCenter」タグがあるかを確認、無い場合はEC2インスタンスの開始アクションを拒否しています。
こちらを実際に利用した際は、環境に合わせたタグ名を指定して制御しました。
特定のリージョン以外でリソースを作成されたくない場合に、指定外のリージョンでの操作を拒否するポリシーです。リージョン指定のないグローバルサービス(Amazon CloudFrontなど)については明記されたサービスのみ許可されます。この例では、指定した 2 つの管理者ロールのいずれかによるリクエストは除外するようになっています。
{ |
"Version": "2012-10-17", |
"Statement": [ |
{ |
"Sid": "DenyAllOutsideEU", |
"Effect": "Deny", |
"NotAction": [ |
"a4b:*", |
"acm:*", |
"aws-marketplace-management:*", |
"aws-marketplace:*", |
"aws-portal:*", |
"budgets:*", |
"ce:*", |
"chime:*", |
"cloudfront:*", |
"config:*", |
"cur:*", |
"directconnect:*", |
"ec2:DescribeRegions", |
"ec2:DescribeTransitGateways", |
"ec2:DescribeVpnGateways", |
"fms:*", |
"globalaccelerator:*", |
"health:*", |
"iam:*", |
"importexport:*", |
"kms:*", |
"mobileanalytics:*", |
"networkmanager:*", |
"organizations:*", |
"pricing:*", |
"route53:*", |
"route53domains:*", |
"s3:GetAccountPublic*", |
"s3:ListAllMyBuckets", |
"s3:PutAccountPublic*", |
"shield:*", |
"sts:*", |
"support:*", |
"trustedadvisor:*", |
"waf-regional:*", |
"waf:*", |
"wafv2:*", |
"wellarchitected:*" |
], |
"Resource": "*", |
"Condition": { |
"StringNotEquals": { |
"aws:RequestedRegion": [ |
"eu-central-1", |
"eu-west-1" |
] |
}, |
"ArnNotLike": { |
"aws:PrincipalARN": [ |
"arn:aws:iam::*:role/Role1AllowedToBypassThisSCP", |
"arn:aws:iam::*:role/Role2AllowedToBypassThisSCP" |
] |
} |
} |
} |
] |
} |
「”Effect”: “Deny”」とあるので拒否ステートメントです。Condition要素でAWSリージョンが「eu-central-1」「 eu-west-1」以外、かつロール名が「Role1AllowedToBypassThisSCP」「Role2AllowedToBypassThisSCP」以外の場合に、NotAction要素で指定されたグローバルサービスに対するアクション以外を拒否するポリシーです。
ここで記載されたグローバルサービスは最新のサービス、オペレーションがすべて含まれているわけではないことに注意が必要です。
また、このポリシーをカスタマイズするときの注意事項として、「ec2:DescribeRegions」アクションは入れておいた方が無難です。カスタマイズ当初はこのアクションを除外していたのですが、AWS WAFの操作が権限不足で行えない事象が発生しました。調査したところ、リージョン名などを取得する際にこのアクションを使うようでした。ご参考まで。
サービスコントロールポリシー(SCP)の設定について、ドキュメントに記載された例から実際に適用した場合の事例をいくつかご紹介しました。
上手く設定することで、AWSアカウント全体の権限制御を集中管理できるようになります。ただしその分影響範囲も大きく、適用する際は事前の検証は必須ですので、その点はご留意ください。