Skip to content

Instantly share code, notes, and snippets.

@ritesh
Last active August 9, 2020 12:32
Show Gist options
  • Save ritesh/5828305923e1a9ab9d8282f7670a7c3a to your computer and use it in GitHub Desktop.
Save ritesh/5828305923e1a9ab9d8282f7670a7c3a to your computer and use it in GitHub Desktop.
AddPublishingDestinationGdduty
AWSTemplateFormatVersion: "2010-09-09"
Description: |
Publishes findings from a detector in one region to an S3 bucket
Parameters:
DestinationArn:
Type: String
Description: ARN of the S3 bucket that you want GuardDuty to push findings to, GuardDuty must have permissions to write to this bucket
KmsKeyArn:
Type: String
Description: The key that GuardDuty should use to encrypt findings
Resources:
GuardDutyPublishingDestination:
Type: AWS::Lambda::Function
Properties:
Code:
ZipFile: |
import logging
import boto3
import cfnresponse
import os
import json
logger = logging.getLogger()
logger.setLevel(logging.INFO)
gdduty = boto3.client('guardduty')
def lambda_handler(event, context):
logger.info('got event {}'.format(event))
responseData = {}
detector = None
try:
resp = gdduty.list_detectors()
detector = resp['DetectorIds'][0]
if detector is None:
logger.error("No detector found, make sure you have a gdduty detector set up in this region")
cfnresponse.send(event, context, cfnresponse.FAILED, responseData, "GuardDutyS3Exporter")
return
except Exception as e:
logger.error("Couldn't list detectors %s", e)
return
if event['RequestType'] in ('Create', 'Update'):
try:
resp = gdduty.create_publishing_destination(DetectorId=detector, DestinationType='S3', DestinationProperties={'DestinationArn': event['ResourceProperties'].get('DestinationArn'),
'KmsKeyArn': event['ResourceProperties'].get('KmsKeyArn')})
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "GuardDutyS3Exporter")
return
except Exception as e:
logger.error("Couldn't create publishing destination %s", e)
cfnresponse.send(event, context, cfnresponse.FAILED, responseData, "GuardDutyS3Exporter")
return
elif event['RequestType'] == 'Delete':
try:
resp = gdduty.list_publishing_destinations(DetectorId=detector)
try:
destination = resp['Destinations'][0]['DestinationId']
except KeyError as e:
logger.info("Couldn't find destination, maybe it was never created? Returning a successful delete")
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "GuardDutyS3Exporter")
if destination is None:
logger.info("no destination found")
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "GuardDutyS3Exporter")
return
resp = gdduty.delete_publishing_destination(DetectorId=detector, DestinationId=destination)
cfnresponse.send(event, context, cfnresponse.SUCCESS, responseData, "GuardDutyS3Exporter")
return
except Exception as e:
logger.error('failed to delete this resource: %s', e)
cfnresponse.send(event, context, cfnresponse.FAILED, responseData, "GuardDutyS3Exporter")
return
logger.info("Neither CREATE nor UPDATE or DELETE, not sure what to do here")
cfnresponse.send(event, context, cfnresponse.FAILED, responseData, "GuardDutyS3Exporter")
Handler: "index.lambda_handler"
Timeout: 30
Role: !GetAtt GuardDutyPublishDestinationRole.Arn
Runtime: python3.6
GuardDutyPublishDestinationRole:
Type: AWS::IAM::Role
Properties:
AssumeRolePolicyDocument:
Version: 2012-10-17
Statement:
-
Effect: Allow
Principal:
Service:
- lambda.amazonaws.com
Action:
- sts:AssumeRole
Path: /
Policies:
- PolicyName: "lambda-logs"
PolicyDocument:
Version: '2012-10-17'
Statement:
-
Effect: Allow
Action:
- logs:CreateLogGroup
- logs:CreateLogStream
- logs:PutLogEvents
Resource:
- "arn:aws:logs:*:*:*"
-
Effect: Allow
Action:
- guardduty:DeletePublishingDestination
- guardduty:List*
- guardduty:CreatePublishingDestination
Resource: "*"
EnableGuardDutyPublishingDestination:
Type: Custom::EnableGuardDutyPublishingDestination
Properties:
ServiceToken: !GetAtt GuardDutyPublishingDestination.Arn
DestinationArn: !Ref DestinationArn
KmsKeyArn: !Ref KmsKeyArn
AWSTemplateFormatVersion: "2010-09-09"
Description: |
Set up resources in the security account to receive guardduty findings
Parameters:
NameOfBucket:
Type: String
Description: ARN of the S3 bucket that you want GuardDuty to push findings to, this must exist and allow the guardduty service principal to write to it
AccessLogsBucket:
Type: String
Description: Where access logs for the guardduty findings bucket are stored
LogPrefix:
Type: String
Description: Access Logs prefix
Default: "/"
SplunkIAMGroup:
Type: String
Description: ARN of IAM group that is allowed to read/write messages from the queue
Resources:
GDFindingsKey:
Type: AWS::KMS::Key
Properties:
EnableKeyRotation: true
KeyPolicy:
Version: 2012-10-17
Id: bucket-key-1
Statement:
- Sid: Allow GuardDuty to use the key
Effect: Allow
Principal:
Service: guardduty.amazonaws.com
Action: 'kms:GenerateDataKey'
Resource: '*'
- Sid: Enable IAM User Permissions
Effect: Allow
Principal:
AWS: !Join
- ''
- - 'arn:aws:iam::'
- !Ref 'AWS::AccountId'
- ':root'
Action: 'kms:*'
Resource: '*'
GDFindingsKeyAlias:
Type: AWS::KMS::Alias
Properties:
AliasName: !Join
- ''
- - 'alias/s3-'
- !Ref NameOfBucket
TargetKeyId:
Ref: GDFindingsKey
GDFindingsBucket:
Type: AWS::S3::Bucket
Properties:
LoggingConfiguration:
DestinationBucketName: !Ref AccessLogsBucket
LogFilePrefix: !Ref LogPrefix
AccessControl: BucketOwnerFullControl
NotificationConfiguration:
QueueConfigurations:
- Event: s3:ObjectCreated:*
Queue: !GetAtt SplunkSQSQueue.Arn
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
KMSMasterKeyID: !Ref GDFindingsKey
SSEAlgorithm: aws:kms
BucketName: !Ref NameOfBucket
BucketPolicy:
Type: AWS::S3::BucketPolicy
Properties:
Bucket:
Ref: "GDFindingsBucket"
PolicyDocument:
Version: '2012-10-17'
Id: PutObjPolicy
Statement:
- Sid: AllowGuardDutyToGetBucketLocation
Effect: Allow
Principal:
Service: guardduty.amazonaws.com
Action: s3:GetBucketLocation
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref NameOfBucket
- Sid: AllowGuardDutyToPutObjects
Effect: Allow
Principal:
Service: guardduty.amazonaws.com
Action: s3:PutObject
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref NameOfBucket
- /*
- Sid: DenyIncorrectEncryptionHeader
Effect: Deny
Principal: "*"
Action: s3:PutObject
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref GDFindingsBucket
- /*
Condition:
StringNotEquals:
s3:x-amz-server-side-encryption: aws:kms
- Sid: DenyUnEncryptedObjectUploads
Effect: Deny
Principal: "*"
Action: s3:PutObject
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref GDFindingsBucket
- /*
Condition:
'Null':
s3:x-amz-server-side-encryption: true
- Sid: DenyPublicReadACL
Effect: Deny
Principal: "*"
Action:
- s3:PutObject
- s3:PutObjectAcl
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref GDFindingsBucket
- /*
Condition:
StringEquals:
s3:x-amz-acl:
- public-read
- public-read-write
- authenticated-read
- Sid: DenyPublicReadGrant
Effect: Deny
Principal: "*"
Action:
- s3:PutObject
- s3:PutObjectAcl
Resource: !Join
- ''
- - 'arn:aws:s3:::'
- !Ref GDFindingsBucket
- /**
Condition:
StringLike:
s3:x-amz-grant-read:
- "*http://acs.amazonaws.com/groups/global/AllUsers*"
- "*http://acs.amazonaws.com/groups/global/AuthenticatedUsers*"
SplunkSQSQueue:
Type: AWS::SQS::Queue
SQSQueuePolicy:
Type: AWS::SQS::QueuePolicy
Properties:
Queues:
- !Ref SplunkSQSQueue
PolicyDocument:
Statement:
-
Action: "SQS:*"
Effect: "Allow"
Resource: !GetAtt SplunkSQSQueue.Arn
Principal: "*"
Condition:
ArnLike:
aws:SourceArn: !Ref SplunkIAMGroup
-
Action: "SQS:SendMessage"
Effect: "Allow"
Resource: !GetAtt SplunkSQSQueue.Arn
Principal: "*"
Condition:
StringEquals:
aws:SourceAccount: !Sub ${AWS::AccountId}
ArnLike:
aws:SourceArn: !Join
- ''
- - 'arn:aws:s3:*:*:'
- !Ref NameOfBucket
# Outputs:
# KmsKey:
# Description: The CMK that GuardDuty should use
# Value: !GetAtt KMSKeyForGuardDutyFindings.Arn
# BucketName:
# Description: The bucket that GuardDuty will use
# Value: !GetAtt
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment