AWS Config Rulesをアカウント間で共有しつつ、たくさんのアカウントに自動で展開する

Config Rulesを複数アカウントにどっかーんと自動で展開してみます。どれだけたくさんアカウントがあっても平静を保てます。
2018.12.12

コンニチハ、千葉です。

はじめに

複数チームで利用していくと、たくさんAWSアカウントが増えて、セキュリティやガバナンス、コンプライアンスを守るために中央管理で頑張り、ボトルネックになっていくことがあります。しかし、AWSでは自動化ができるので、チームに権限を委譲したうえでセキュリティ、ガバナンス、コンプライアンスを構築する土台があります。ブロッカーではなく、ガードレールが作れると幸せな世界が築けそうです。

ってことで、AWSにはConfig Rulesというサービスがあり、こちらを導入すると意図しない設定が入っているリソースを可視化したり、通知したりすることができます。 今回は、マルチアカウント環境でConfig Rulesを共有し、かつ自動で複数アカウントに展開してみました。これで、どれだけアカウントが増えようともへっちゃらです。

やってみた

全体イメージサマリ

Config Rulesを管理するアカウントを管理アカウント、ルールを利用するアカウントを管理対象アカウントとします。 動作イメージはこちら。

設定イメージはこちら。

管理アカウントでやること

  • Lambda実行用のIAMロール作成(STS、CWLogs,config:PutEvaluations)
  • Lambda関数の作成
  • STSで管理対象の一時クレデンシャルを取得
  • 任意の処理を実行
  • 評価結果をputする
  • Lambda関数にクロスアカウント Configからの実行許可を付与(add permission)

管理対象アカウントでやること

  • AWS Config Rules用のIAMロールを作成
  • 付与する権限:SecurityAudit
  • 信頼関係:AWS Config, 管理アカウントのIAMロール
  • AWS Config Rulesの作成
  • 管理アカウントのLambda ARNを指定

やってみた

いやー、やること整理するだけでかなり大変でした。すごく複雑です。そのため簡単に実装できるようにCFn化しました。

前提

前提として管理対象アカウントでConfigを有効にしておきます。

管理アカウントでやること

CloudFormationを貼っておきます。事前にLambdaのコードはzipでS3へアップロードしてください。 Config Rules用のコードはawslabs/aws-config-rulesに色々いあるので参考にしたり、コピーしてそのまま使いましょう。コード利用時の注意事項としては、ASSUME_ROLE_MODE という項目があるのでTrueにします。そうすると、STSを利用して一時クレデンシャルを取得するようになるので、クロスアカウントで利用可能になります。

管理アカウント用のCFn

AWSTemplateFormatVersion: '2010-09-09'
Description: This CloudFormation template to create Crross Account Config Rules for Admin Account.

Parameters:
Environment:
Description: Type of this environment.
Type: String
Default: stg
AllowedValues:
- prd
- stg
- dev
SystemName:
Description: Name of this system.
Type: String
Default: web
LambdaFunctionS3Bucket:
Description: S3 bucket name
Type: String
Default: s3-bucket-name-your-lambda-function
LambdaFunctionS3Key:
Description: S3 key
Type: String
Default: xx/xx.zip
LambdaInvokeCrossAccount:
Description: Cross AWS Account ID
Type: String
Default: 111111111111
Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Environment Configuration
Parameters:
- SystemName
- Environment
- Label:
default: Lambda Function
Parameters:
- LambdaFunctionS3Bucket
- LambdaFunctionS3Key

Resources:
# Lambda Function
LambdaFunctionIAMCheck:
Type: "AWS::Lambda::Function"
Properties:
Handler: "lambda_function.lambda_handler"
Role:
Fn::GetAtt:
- "LambdaExecRole"
- "Arn"
Code:
S3Bucket: !Sub ${LambdaFunctionS3Bucket}
S3Key: !Sub ${LambdaFunctionS3Key}
Runtime: "python3.6"
Timeout: "60"

# Lambda Function add execute permission from config rules of managed account
LambdaPermission:
Type: AWS::Lambda::Permission
Properties:
Action: "lambda:InvokeFunction"
FunctionName: !GetAtt
- LambdaFunctionIAMCheck
- Arn
Principal: "config.amazonaws.com"
SourceAccount: !Sub ${LambdaInvokeCrossAccount}

# IAM Role
LambdaExecRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub role-${Environment}-${SystemName}-lambda
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSConfigRole
- arn:aws:iam::aws:policy/SecurityAudit
Path: /
AssumeRolePolicyDocument:
!Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}

# IAM Policy
LambdaExecPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub policy-${Environment}-${SystemName}-lambda
Description: !Sub policy-${Environment}-${SystemName}-lambda
PolicyDocument:
!Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"config:PutEvaluations"
],
"Resource": [
"*"
]
},
{
"Effect": "Allow",
"Action": [
"logs:CreateLogGroup",
"logs:CreateLogStream",
"logs:PutLogEvents"
],
"Resource": "arn:aws:logs:*:*:*"
},
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole"
],
"Resource": "*"
}
]
}
Roles:
- !Ref LambdaExecRole

Lambdaのコード

Assume Roleで、一時クレデンシャルを取得するコードを入れておきます。GitHubにあるAWSラボのコードをみたのですが、管理対象アカウントのConfigロールに対してAssume Roleしていました。今回は、以下のようにして作成したRoleにAssume Roleするようなコードを入れました。(AWSラボのコードを利用する場合は、クレデンシャル取得する箇所を以下に変更しましょう。自分で一からカスタムルール作成するときは、このコードを埋め込みましょう。

executionRoleArn = 'arn:aws:iam::' + event["accountId"] + ':role/role-exec-config-rules'
credentials = get_assume_role_credentials(executionRoleArn)
return boto3.client(service, aws_access_key_id=credentials['AccessKeyId'],
aws_secret_access_key=credentials['SecretAccessKey'],
aws_session_token=credentials['SessionToken']
)

管理対象アカウントでやること

入力には、管理アカウントのLambdaに指定しているIAMロールARN、LambdaのARNが必要になりますので取得しておきましょう。 こちらのCFnをStacSetsを利用して複数のアカウントに展開しましょう。

[新機能] CloudFormation StackSetsを試してみた

管理対象のCFn

AWSTemplateFormatVersion: '2010-09-09'
Description: This CloudFormation template to create Crross Account Config Rules for Managed Account.

Parameters:
Environment:
Description: Type of this environment.
Type: String
Default: stg
AllowedValues:
- prd
- stg
- dev
SystemName:
Description: Name of this system.
Type: String
Default: web
AllowAccessRole:
Description: Role of Lambda on Cross Account(ARN)
Type: String
Default: Role on Admin Account
ConfigRulesLambda:
Description: Lambda for ConfigRules(Crros Account Lambda ARN)
Type: String
Default: Cross Account Lambda ARN

Metadata:
AWS::CloudFormation::Interface:
ParameterGroups:
- Label:
default: Environment Configuration
Parameters:
- SystemName
- Environment
- Label:
default: Config Configure
Parameters:
- ConfigRulesLambda
- AllowAccessRole

Resources:
# Config Setting
# ConfigRecorder:
# Type: AWS::Config::ConfigurationRecorder
# Properties:
# Name: default
# RecordingGroup:
# ResourceTypes:
# - "AWS::IAM::User"
# RoleARN:
# Fn::GetAtt:
# - ConfigRole
# - Arn

# Config Rules
ConfigRuleIAMNoUser:
Type: AWS::Config::ConfigRule
Properties:
ConfigRuleName: "ConfigRuleIAM_NO_USER"
Description: "IAM NO USER"
InputParameters:
tag1Key: test1
Scope:
ComplianceResourceTypes:
- "AWS::IAM::User"
Source:
Owner: "CUSTOM_LAMBDA"
SourceIdentifier: !Sub ${ConfigRulesLambda}
SourceDetails:
-
EventSource: "aws.config"
MessageType: "ConfigurationItemChangeNotification"

# IAM Role
ConfigRole:
Type: AWS::IAM::Role
Properties:
RoleName: !Sub role-exec-config-rules
ManagedPolicyArns:
- arn:aws:iam::aws:policy/service-role/AWSConfigRole
- arn:aws:iam::aws:policy/SecurityAudit
Path: /
AssumeRolePolicyDocument:
!Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": "${AllowAccessRole}"
},
"Action": "sts:AssumeRole"
},
{
"Effect": "Allow",
"Principal": {
"Service": "config.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}

# IAM Policy
LambdaExecPolicy:
Type: AWS::IAM::ManagedPolicy
Properties:
ManagedPolicyName: !Sub policy-${Environment}-${SystemName}-lambda
Description: !Sub policy-${Environment}-${SystemName}-lambda
PolicyDocument:
!Sub |
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"config:PutEvaluations"
],
"Resource": [
"*"
]
}
]
}
Roles:
- !Ref ConfigRole

管理対象アカウントから評価を実行

管理対象から評価を実施し、結果が表示されるかやってみました。

表示できました!

最後に

かなり複雑で、はまって検証時間がかかりました。クロスアカウントはやっぱりむずかしいですね。CFn化したので、ある程度簡略化できましたが。最近Resource Access Managerというアカウント間でリソースを共有できるサービスがでたので、こちらのアップデートに期待です。

参考