Sending Events (Like GuardDuty) from CloudWatch to Lambda
Someone pointed me to a Reddit post asking how to send and access the JSON from a GuardDuty finding into Lambda. This is easy if you know a few key steps – and a long research process if you don’t. Although we have this totally automated here at DisruptOps there may still be situations where you want to send the information around directly.
On the security side, we typically focus on events from GuardDuty, Security Hub, or Cloudtrail, but these steps will work with nearly every EventBridge (CloudWatch) event. Here’s a quick guide:
Step 1: Create Your Lambda Function
For this tutorial, we will create a default Python Lambda function. In our last step we will write the actual code, but for now, let’s create an empty function. Go to Lambda > Create Function > Author from scratch. Name it event_test and select the latest Python 3.x version. Also select Permissions > Create a new role with basic Lambda permissions.
Step 2: Learn How Lambda Functions Handle Events
Scroll down and look at the Code section of the Lambda console. Double-click the name of your function. It will look like this:
There are 3 key pieces:
- lambda_handler is the name of the function in the code that will receive and process the event. You can change this to point to any function in your program, but we will stick with the default.
- event is the contents of the EventBridge event, which will be JSON by default for nearly any AWS service.
- context is data about the execution of the function, and we won’t use it.
Below, I’ll show you how to get the data to the function, and then we will come back to the function itself.
Step 3: Create your EventBridge Rule
Here’s a simple rule that will send all GuardDuty events to the Lambda function: Go to CloudWatch > Events > Rules > Create Rule. The Service Name will be GuardDuty and the Event Type will be All Events:
Step 4: Pick Your Lambda as Your Target
On that same page, select Add target, then your Lambda function like this:
The default for Configure input is Matched event which will send the full JSON from GuardDuty (or whatever). That section is normally collapsed in the UI, but I clicked it here to show you other options such as sending only part of the event or modified data. You don’t need those if you want the raw source event!
Then click to the next page, name it, and Create Rule.
Interlude: What’s Happening
Here’s how all this works:
- GuardDuty detects a problem and creates a finding.
- The finding is an EventBridge event which means it… happens… on the internal event bus for your account but then it disappears unless you do something with said event.
- The EventBridge Rule says, “Hey if you see an event from GuardDuty, send it over to this Lambda function.” Without a Rule, nothing happens. Rules aren’t limited to Lambda, but this tutorial is.
- The Rule runs the Lambda function and sends the event JSON to the designated handler, as the function parameter of event.
- The Lambda executes and now has the JSON in memory as the variable event in the function lambda_handler.
It’s wonderfully elegant, although we are skipping over all the complex “debugging” stuff.
Step 5: Code your Lambda to Do Something
This default Lambda writes out a log message with the start and end time.
The easiest thing to do is add print(event) into the function per the screenshot below. This will automatically log the GuardDuty finding to the CloudWatch log stream created when you run the Lambda:
Then click Deploy. This saves that code and sets it up to run.
Testing is also easy. Click on Test then Create test event and use the default. Once you save this you can click Test, and it will run the function and send the test JSON to the function as the event:
Then pop back over to CloudWatch > Log > Log groups > eventTest, and you can see your JSON results:
Step 6: Do Some Awesome Security Thing!
Now you have the framework to take any security event in AWS and use it to trigger and run whatever you want. All you need to do is write the code.
If you want a good example, here’s an older proof of concept that automatically reversed any security group changes. It needs an update (soon) since Python 2.7 is deprecated, but you get the idea:
Finally if you want to pull an event from CloudTrail (like we do in the security group example) you will need the information in this post to capture the events correctly.