この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
コンバンハ、千葉(幸)です。
GuardDutyでは検出結果をS3にエクスポートすることができます。
エクスポート時には、内容を暗号化するためのKMSキーと、必要なバケットポリシーを定義したS3バケットを準備する必要があります。それらをサクッと用意するためのCloudFormationのテンプレートを作成しました。
目次
- 目次
- 本記事の対象範囲
- GuardDutyの検出結果のS3へのエクスポート
- GuardDutyを有効化するCloudFormationテンプレート
- 追加するCloudFormationテンプレート
- S3へのエクスポート設定
- 終わりに
本記事の対象範囲
以下のイメージです。
青枠で囲っている部分が、今回新たに作成したCloudFormationテンプレートで構成されるリソースです。
補足
- エクスポート用のKMSキーおよびS3バケットはGuardDutyを有効化するリージョンと同一のリージョンで新規作成するものとしています
- GuardDutyディテクター、CloudWatchイベントルール、SNSトピックなどのリソースは、先人のテンプレートがあるのでそれを活用させてもらいます
- S3へのエクスポートも含めてCloudFormationで完結できるのでは、と考えていたのですが、2020年3月時点でリファレンスに記述が見当たらなかったので、そこの設定だけは手動で行うものとしています
GuardDutyの検出結果のS3へのエクスポート
詳細は以下の記事に詳しいです。
公式ドキュメントも一緒に確認しておきましょう。
簡単に言うと、以下の事前設定が必要です。
- KMSキー(カスタマーマスターキー)
- S3バケットポリシー
エクスポートの際にはカスタマーマスタキーによる暗号化が必須になっています。暗号化なしや、デフォルトKMSキーを用いた暗号化には対応していません。
また、ここでのKMSキーとS3バケットは同一のリージョンにある必要があります。
KMSキーポリシー
使用するKMSキーはキーポリシーによって以下が許可されている必要があります。
{
"Sid": "Allow GuardDuty to use the key",
"Effect": "Allow",
"Principal": {
"Service": "guardduty.amazonaws.com"
},
"Action": "kms:GenerateDataKey",
"Resource": "*"
}
S3バケットポリシー
エクスポート先のS3バケットはバケットポリシーによってGuardDutyからのエクスポートを許可していることが必要です。
ドキュメントに記載されているサンプルは以下の通りです。Deny
の部分は必須ではありませんが、特に理由がなければ制限しておくに越したことはないでしょう。
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "Allow GuardDuty to use the getBucketLocation operation",
"Effect": "Allow",
"Principal": {
"Service": "guardduty.amazonaws.com"
},
"Action": "s3:GetBucketLocation",
"Resource": "arn:aws:s3:::myBucketName"
},
{
"Sid": "Allow GuardDuty to upload objects to the bucket",
"Effect": "Allow",
"Principal": {
"Service": "guardduty.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::myBucketName/[optional prefix]/*"
},
{
"Sid": "Deny unencrypted object uploads. This is optional",
"Effect": "Deny",
"Principal": {
"Service": "guardduty.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::myBucketName/[optional prefix]/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption": "aws:kms"
}
}
},
{
"Sid": "Deny incorrect encryption header. This is optional",
"Effect": "Deny",
"Principal": {
"Service": "guardduty.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::myBucketName/[optional prefix]/*",
"Condition": {
"StringNotEquals": {
"s3:x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:region:111122223333:key/KMSKeyId"
}
}
},
{
"Sid": "Deny non-HTTPS access",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": "arn:aws:s3:::myBucketName/*",
"Condition": {
"Bool": {
"aws:SecureTransport": "false"
}
}
}
]
}
GuardDutyを有効化するCloudFormationテンプレート
以下の記事の内容を参考にします。
記事の主題は全リージョンで有効化するところまでですが、1つのリージョンで有効化する際のテンプレートが載っているので、そこにアドオンする形で作成していきます。
折り畳み
AWSTemplateFormatVersion: 2010-09-09
Description:
"enable guardduty and set alert"
Parameters:
MailAddress:
Description: Enter email address to send notification.
Type: String
Resources:
GDD:
Type: "AWS::GuardDuty::Detector"
Properties:
Enable: true
SNST:
Type: "AWS::SNS::Topic"
Properties:
TopicName: GuardDutyTopic
SNSS:
Type: "AWS::SNS::Subscription"
Properties:
Endpoint: !Ref MailAddress
Protocol: email
TopicArn: !Ref SNST
SNSTP:
Type: "AWS::SNS::TopicPolicy"
Properties:
PolicyDocument:
Id: default_policy_ID
Version: "2012-10-17"
Statement:
- Sid: default_statement_ID
Effect: Allow
Principal:
AWS: "*"
Action:
- "SNS:GetTopicAttributes"
- "SNS:SetTopicAttributes"
- "SNS:AddPermission"
- "SNS:RemovePermission"
- "SNS:DeleteTopic"
- "SNS:Subscribe"
- "SNS:ListSubscriptionsByTopic"
- "SNS:Publish"
- "SNS:Receive"
Resource: !Ref SNST
Condition:
StringEquals:
"AWS:SourceOwner": !Ref "AWS::AccountId"
- Sid: AWSEvents_AlertGuardDutyFindings_Id123
Effect: Allow
Principal:
Service:
- "events.amazonaws.com"
Action: "sns:Publish"
Resource: !Ref SNST
Topics:
- !Ref SNST
ER:
Type: "AWS::Events::Rule"
Properties:
Name: AlertGuardDutyFindings
Description: "Alert to SNS topic when find threats by GuardDuty"
EventPattern: {
"source": [
"aws.guardduty"
],
"detail-type": [
"GuardDuty Finding"
]
}
Targets:
- Arn: !Ref SNST
Id: Id123
追加する前の時点で、以下のリソースが定義されています。
論理ID | リソースタイプ |
---|---|
GDD | AWS::GuardDuty::Detector |
SNST | AWS::SNS::Topic |
SNSS | AWS::SNS::Subscription |
SNSTP | AWS::SNS::TopicPolicy |
追加するCloudFormationテンプレート
以下をアドオンすることにしました。
折り畳み
# ------------------------------------------------------------#
# KMS key for GuardDuty
# ------------------------------------------------------------#
KMSK:
Type: AWS::KMS::Key
Properties:
Description: "Key for GuardDuty"
EnableKeyRotation: true
KeyPolicy:
Version: "2012-10-17"
Id: "key-default-1"
Statement:
-
Sid: "Enable IAM User Permissions"
Effect: "Allow"
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action: "kms:*"
Resource: "*"
-
Sid: "Allow GuardDuty to use the key"
Effect: "Allow"
Principal:
Service: "guardduty.amazonaws.com"
Action:
- "kms:GenerateDataKey"
Resource: "*"
PendingWindowInDays: 7
KMSA:
Type: AWS::KMS::Alias
Properties:
AliasName: "alias/KMS-GuardDuty"
TargetKeyId:
Ref: KMSK
# ------------------------------------------------------------#
# S3 Bucket for GuardDuty
# ------------------------------------------------------------#
S3B:
Type: "AWS::S3::Bucket"
Properties:
BucketName: ## バケット名を指定してください ##
VersioningConfiguration:
Status: Enabled
LifecycleConfiguration:
Rules:
- Id: 365days-All-LifeCycleRule
Status: Enabled
ExpirationInDays: 365
NoncurrentVersionExpirationInDays: 10
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
S3BP:
Type: "AWS::S3::BucketPolicy"
Properties:
Bucket:
Ref: "S3B"
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: "Deny non-HTTPS access"
Action: ['s3:*']
Effect: "Deny"
Resource:
- !Join ['', ['arn:aws:s3:::', !Ref 'S3B','/*']]
Principal:
Service: [guardduty.amazonaws.com]
Condition:
Bool:
aws:SecureTransport: "false"
- Sid: "Deny incorrect encryption header"
Action: ['s3:PutObject']
Effect: "Deny"
Resource:
- !Join ['', ['arn:aws:s3:::', !Ref 'S3B','/*']]
Principal:
Service: [guardduty.amazonaws.com]
Condition:
StringNotEquals:
s3:x-amz-server-side-encryption-aws-kms-key-id: !GetAtt KMSK.Arn
- Sid: "Deny unencrypted object uploads"
Action: ['s3:PutObject']
Effect: "Deny"
Resource:
- !Join ['', ['arn:aws:s3:::', !Ref 'S3B','/*']]
Principal:
Service: [guardduty.amazonaws.com]
Condition:
StringNotEquals:
s3:x-amz-server-side-encryption: "aws:kms"
- Sid: "Allow PutObject"
Action: ['s3:PutObject']
Effect: "Allow"
Resource:
- !Join ['', ['arn:aws:s3:::', !Ref 'S3B','/*']]
Principal:
Service: [guardduty.amazonaws.com]
- Sid: "Allow GetBucketLocation"
Action: ['s3:GetBucketLocation']
Effect: "Allow"
Resource:
- !Join ['', ['arn:aws:s3:::', !Ref 'S3B']]
Principal:
Service: [guardduty.amazonaws.com]
追加したリソースは以下の通りです。
論理ID | リソースタイプ |
---|---|
KMSK | AWS::KMS::Key |
KMSA | AWS::KMS::Alias |
S3B | AWS::S3::Bucket |
S3BP | AWS::S3::BucketPolicy |
以下、リソースを順番に確認していきます。
AWS::KMS::Key
KMSK:
Type: AWS::KMS::Key
Properties:
Description: "Key for GuardDuty"
EnableKeyRotation: true
KeyPolicy:
Version: "2012-10-17"
Id: "key-default-1"
Statement:
-
Sid: "Enable IAM User Permissions"
Effect: "Allow"
Principal:
AWS: !Sub "arn:aws:iam::${AWS::AccountId}:root"
Action: "kms:*"
Resource: "*"
-
Sid: "Allow GuardDuty to use the key"
Effect: "Allow"
Principal:
Service: "guardduty.amazonaws.com"
Action:
- "kms:GenerateDataKey"
Resource: "*"
PendingWindowInDays: 7
キーポリシーとしては、デフォルトのポリシー + GuardDutyの要件を満たすように設定しています。
AWS::KMS::Alias
KMSA:
Type: AWS::KMS::Alias
Properties:
AliasName: "alias/KMS-GuardDuty"
TargetKeyId:
Ref: KMSK
エイリアスがリソースとして分かれているのが少し意外でしたが、キーと1対1ではないことが理由のようです。
各エイリアスは 1 つの CMK のみを指し示すことができますが、複数のエイリアスが同じ CMK を指すことができます。
また、先頭に「alias/
」を付与しないとCloudFormation実行時にエラーになるので注意が必要です。
各エイリアス名は、alias/ で始まり、その後に名前が続きます (alias/exampleKey など)。エイリアス名に使用できるのは、英数字、スラッシュ (/)、アンダースコア (_)、およびダッシュ (-) のみです。エイリアス名を alias/aws/ で始めることはできません。
AWS::S3::Bucket
S3B:
Type: "AWS::S3::Bucket"
Properties:
BucketName: ## バケット名を指定してください ##
VersioningConfiguration:
Status: Enabled
LifecycleConfiguration:
Rules:
- Id: 365days-All-LifeCycleRule
Status: Enabled
ExpirationInDays: 365
NoncurrentVersionExpirationInDays: 10
AccessControl: Private
PublicAccessBlockConfiguration:
BlockPublicAcls: True
BlockPublicPolicy: True
IgnorePublicAcls: True
RestrictPublicBuckets: True
このサンプルではバージョニングを有効化してライフサイクルルールを設定していますが、エクスポートの要件として必要なわけではありません。必要に応じてカスタマイズしてください。 また、S3バケット名は埋め込みで指定するか、パラメータで渡す形にしてください。
AWS::S3::BucketPolicy
S3BP:
Type: "AWS::S3::BucketPolicy"
Properties:
Bucket:
Ref: "S3B"
PolicyDocument:
Version: 2012-10-17
Statement:
- Sid: "Deny non-HTTPS access"
Action: ['s3:*']
Effect: "Deny"
Resource:
- !Join ['', ['arn:aws:s3:::', !Ref 'S3B','/*']]
Principal:
Service: [guardduty.amazonaws.com]
Condition:
Bool:
aws:SecureTransport: "false"
- Sid: "Deny incorrect encryption header"
Action: ['s3:PutObject']
Effect: "Deny"
Resource:
- !Join ['', ['arn:aws:s3:::', !Ref 'S3B','/*']]
Principal:
Service: [guardduty.amazonaws.com]
Condition:
StringNotEquals:
s3:x-amz-server-side-encryption-aws-kms-key-id: !GetAtt KMSK.Arn
- Sid: "Deny unencrypted object uploads"
Action: ['s3:PutObject']
Effect: "Deny"
Resource:
- !Join ['', ['arn:aws:s3:::', !Ref 'S3B','/*']]
Principal:
Service: [guardduty.amazonaws.com]
Condition:
StringNotEquals:
s3:x-amz-server-side-encryption: "aws:kms"
- Sid: "Allow PutObject"
Action: ['s3:PutObject']
Effect: "Allow"
Resource:
- !Join ['', ['arn:aws:s3:::', !Ref 'S3B','/*']]
Principal:
Service: [guardduty.amazonaws.com]
- Sid: "Allow GetBucketLocation"
Action: ['s3:GetBucketLocation']
Effect: "Allow"
Resource:
- !Join ['', ['arn:aws:s3:::', !Ref 'S3B']]
Principal:
Service: [guardduty.amazonaws.com]
上で確認したサンプルのバケットポリシーを再現しています。サンプルではリソースとしてプレフィックス([optional prefix]
)まで絞った形で記載されていますが、このコードではそこまで制限はしていません。
S3へのエクスポート設定
上記を踏まえた形でCloudFormationを実行すればKMSキーとS3バケットが作成されるので、エクスポートの設定をマネジメントコンソールから実施します。
GuardDutyのコンソール画面に遷移し、[設定]ペインを選択します。S3バケットについて「今すぐ設定する」を押下します。
作成したS3バケットとKMSキーを指定して「保存」を押下すれば設定完了です。オプションでプレフィックスを指定することもできます。
終わりに
GuardDutyの検出結果をエクスポートするためのS3バケットとKMSキーを作成するCloudFormationテンプレートのご紹介でした。
あとはエクスポートの設定まで含めてCloudFormation化されることを期待するばかりです。
実際の構成としては複数リージョンの結果を1つのS3バケットに集約したりなど様々なパターンが考えられますが、「リージョンごとにGuardDuty + KMSキー + S3バケット」というユースケースに合致していれば参考にしていただければと思います。
以上、最近バナナをもりもり食べる千葉(幸)がお送りしました。