AWS CDK: Automating GuardDuty Event Notifications in All Regions

Terry Hillis
The Startup
Published in
6 min readJan 18, 2021

And why you should, too!

TL; DR — use the CDK to enable and listen for GuardDuty findings across your entire AWS account. All you need is an AWS account, a computer, and a SNS topic. https://github.com/tlh2857/GuardDuty-Global-Notifier

Diagram of CDK Project

I recently began working through a challenge:

How can one easily enable GuardDuty in all AWS Regions (an AWS Security Best Practice) and set up alerts when findings are generated in any of those regions?

Keyword being easily.

If you’re not familiar with GuardDuty, and if you’re using AWS, then I encourage you to check it out. It uses Machine Learning (ML) to identify unusual and malicious activity by analyzing your CloudTrail Logs, VPC Flow Logs, S3 data operations, and DNS logs. It’s a really powerful way to detect an intrusion into your AWS user base or infrastructure. Think detecting a new EC2 instance in a region that you’ve never touched before or detecting port scanning in your private VPC. You can enable GuardDuty to monitor for that nasty activity and then you can set up alerts so that you are notified when that activity occurs. Sounds pretty good, right?

Here was my dilemma. GuardDuty, as with most AWS services, is a regional service. Any findings that GuardDuty generates are only accessible to other services that exist within that same region. If you want to GuardDuty alerts in any AWS region, then you have to set that up in every AWS region.

And thus the problem I set out to solve was to automate the process of enabling and responding to events in GuardDuty in every AWS region. Let the games begin!

Possible Solutions

In trying to solve this task, I looked at several different options. Some of which could have worked if I had wanted to spend more money (I know, I’m a cheapo).

My first thought was to monitor CloudTrail logs directly for GuardDuty findings. What’s nice about this option was that it lessened the number of regional services that you had provision. With CloudTrail already enabled in all regions and for all services, it would have been as easy as sending my CloudTrail logs to CloudWatch Logs and then using a Subscription Filter to forward GuardDuty Finding Logs to Lambda function that serves as a proxy to an SNS topic.

Only problem? GuardDuty does not log findings to CloudTrail. It only sends them to CloudWatch Events. And while CloudTrail logs calls that AWS Services makes, it does not log the generation of a GuardDuty finding itself. Sigh.

I then looked at sending GuardDuty findings to a central S3 bucket. I could then trigger a Lambda function to send an alert whenever findings are pushed to that bucket. This seemed nice, especially because the findings could be sent to a central bucket, but then I realized that you’d have to use a KMS key to encrypt those findings. GuardDuty literally does not give you the option to send those findings without associating a KMS key. While this is a good security practice, I don’t want to spend the $16 required to provision a KMS key in each region. I know, I’m cheap!

After that I looked at sending logs to an EventBridge Event Bus. These are cool because you can use these to aggregate events across multiple AWS accounts. The kicker is that it can only aggregate events within the same region. Sigh again!

Now at this point, you may wonder:

“Why not CloudWatch Event Rules with an SNS topic as a target?”

Only problem there is that services are also regional. I just didn’t want to provision 16 individual SNS topics because the management of those subscriptions seemed tedious. I wanted a way to listen for all GuardDuty findings across all AWS regions via a single SNS subscription.

The Final Solution

Thus I looked to a slight variation of the last proposal:

Provision a CloudWatch Event Rule in each region, each pointing to a regional Lambda function that uses the AWS SDK to call a single SNS topic to which I’m subscribed.

Yay!

But how?

My initial thought was to use CloudFormation StackSets to deploy the resources to each AWS region. This absolutely would have worked, except for my lack of willingness to write CloudFormation from scratch. With that plan thrown out, I moved forward with a second idea: the AWS CDK.

What is the AWS Cloud Development Kit (CDK), you might ask?

“The AWS Cloud Development Kit (AWS CDK) is an open source software development framework to define your cloud application resources using familiar programming languages.”

In short, if you like to code, then you’ll like the CDK. Here’s a diagram of the infrastructure that we’ll provision with this solution:

As far as the AWS infrastructure, it’s a central SNS topic that you create and to which you subscribe. Then, via the CDK, you’ll enable GuardDuty events to send a message that that central SNS topic via a Lambda function in each AWS region. Pretty neat, huh?

Deploying the Solution

OK, so here’s what you’ll need in order to get going.

Once you have the above items configured, the solution is as easy as 1, 2, 3!

  1. Open up a command prompt / terminal, and run:

git clone https://github.com/tlh2857/GuardDuty-Global-Notifier.git`

2. Then head to the ‘resources’ folder and edit the ‘variables.js’ file. Replace the value that says ‘SNSTopicARN’ with the ARN of the SNS topic that you created and are subscribed to. See:

3. If you already have GuardDuty enabled in all regions, then go ahead and remove this line of code in the event_stack.js file under the lib folder:

new guardduty.CfnDetector(this, “GuardDutyDetector”, { enable: true })

By doing this you’ll avoid an error that can stop the deployment of the solution if GuardDuty is already enabled in that region.

4. Perfect. Now hop back into that terminal and run:

npm install

cdk bootstrap

cdk deploy

And if all goes well, you should see the solution deploy right before your very eyes! Note that you’ll need to enter ‘y’ into the console as it will prompt you about making changes to IAM resources in each region that you deploy the solution. One ‘y’ per region unfortunately.

To test, grab the GuardDuty detector ID of any region in AWS and run:

aws guardduty create-sample-findings --detector-id REGIONAL-DETECTOR-ID --finding-types Backdoor:EC2/DenialOfService.Tcp --region AWS-REGION

Then, give it about 5 minutes, and you should get an email from that SNS topic. It may be another protocol depending on your subscription type.

Example Email

And there we have it! Unified event alerting with GuardDuty, CloudWatch Events, Lambda, and a single SNS topic. Neat!

Now, what if you have GuardDuty Deployed in some regions, but not all of them? I had a feeling that you might ask that. And in lieu of elegant scripts that check for that as a condition, you’ll need to deploy this solution in two parts by modifying the regions and the GuardDuty constructor mentioned above in step 3.

Code the iterates through each AWS region

Head to the cwe-gd-cdk.js file in the bin folder, and modify the regions to just the ones that you want.

In fact, you could create two variables, one for regions with GuardDuty already enabled, and one for regions in which GuardDuty is not enabled. Then just deploy both solutions with or without the GuardDuty constructor omitted respectively.

That’s all, folks! Let me know if anyone runs into any difficulty with this and I’ll try to help!

-Terry

P.S. — You can circumvent the notification part with third party solutions, like Trend Micro’s Cloud One — Conformity. It’ll notify if any GuardDuty findings are generated across your entire AWS account.

--

--

Terry Hillis
The Startup

Technical Rotation Associate @TrendMicro; views are my own