Configuring AWS GuardDuty with Lambda for Slack Notifications
Dennis Kelly
At Kong, we leverage many tools to protect our services and customers. Terraform from HashiCorp allows us to automate the process with Infrastructure as Code (IaC). Another important tool is Amazon Web Services (AWS) GuardDuty, a continuous monitoring service for security threat detection in your AWS accounts. It analyzes events from CloudTrail, VPC Flow Logs and DNS logs using machine learning, anomaly detection and known threats to provide security intelligence in the form of GuardDuty alerts or findings. Multiple member AWS accounts can be aggregated into a master account to centrally manage alerts across an entire organization. It provides an enterprise with comprehensive threat detection, stronger security through automation and centralized management at scale.
GuardDuty allows us to automatically send notifications to CloudWatch Events. We use this to notify the security team on Slack by configuring a CloudWatch Event Rule on GuardDuty findings that triggers a Lambda serverless function written in Go called GuardDuty2Slack. This post will walk you through the process and code used to join member accounts to an organization and send GuardDuty findings as Slack notifications. The complete example set of code is available here.
What is GuardDuty?
GuardDuty is a regional service, so member accounts need to be invited for every region they use. Some accounts may not use the same regions as others. While there are more sophisticated ways to manage this, for the simplicity of this post, the following directory structure will be used:
accounts/Terraform snippets for GuardDuty member accountsglobal/Terraform applied at a global level (IAM roles and policies)lambda/Terraform Lambda module and Go function for notificationsus-west-1/Terraform applied at a regional level (in this example us-west-1)
In the global directory, the file iam.tf is used to create an IAM role, GuardDuty2Slack, that can be assumed by the Lambda service with basic Lambda execution permissions:
data "aws_iam_policy_document""lambda"{ statement { actions = ["sts:AssumeRole"] principals { type = "Service" identifiers = ["lambda.amazonaws.com"]}}}resource "aws_iam_role""lambda"{ name = "GuardDuty2Slack" assume_role_policy = "${data.aws_iam_policy_document.lambda.json}" tags { Name = "GuardDuty2Slack" Environment = "Prod" Department = "Engineering" Team = "Cloud" Product = "Cloud" Service = "Guard Duty" Owner = "cloud@my.domain"}}resource "aws_iam_policy_attachment""lambda"{ name = "GuardDuty2Slack" roles = ["${aws_iam_role.lambda.id}"] policy_arn = "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"}
The Terraform in the global directory will need to be executed first - a bootstrapping process - so the IAM role will be available when setting up each region. In the Lambda module, main.tf, the IAM role is imported as a data source and associated with the function:
data "aws_iam_role""lambda"{ name = "GuardDuty2Slack"}resource "aws_lambda_function""lambda"{ function_name = "GuardDuty2Slack" filename = "${data.archive_file.lambda.output_path}" source_code_hash = "${data.archive_file.lambda.output_base64sha256}" handler = "main" runtime = "go1.x" role = "${data.aws_iam_role.lambda.arn}" tags { Name = "GuardDuty2Slack" Environment = "Prod" Department = "Engineering" Team = "Cloud" Product = "Cloud" Service = "Guard Duty" Owner = "cloud@my.domain"}}
The event pattern for GuardDuty findings are associated with an event rule, which is used to trigger the Lambda function:
resource "aws_cloudwatch_event_rule""lambda"{ name = "GuardDuty2Slack" description = "AWS GuardDuty Finding Events" event_pattern = "${file("${path.module}/event-pattern.json")}" tags { Name = "GuardDuty2Slack" Environment = "Prod" Department = "Engineering" Team = "Cloud" Product = "Cloud" Service = "Guard Duty" Owner = "cloud@my.domain"}}resource "aws_cloudwatch_event_target""lambda"{ target_id = "GuardDuty2Slack" rule = "${aws_cloudwatch_event_rule.lambda.name}" arn = "${aws_lambda_function.lambda.arn}"}
The event rule will also need permissions to invoke the function:
The configuration of the function is stored in main.yml. It defines the colors associated with GuardDuty severity levels, the default/fall-back webhook and per account settings:
# https://www.color-hex.com/color-palette/33993colors: low:"#fdc500" # Yellow
medium:"#fd8c00" # Orange
high:"#dc0000" # Red
# Format of GuardDuty finding URL
url:"https://console.aws.amazon.com/guardduty/home?region=%s#/findings?macros=current&fId=%s"
# Default webhook for unconfigured accounts
webhook: https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX
# Account specific settings
accounts:111111111111: name: member1
severity: medium
webhook: https://hooks.slack.com/services/XXXXXXXXX/XXXXXXXXX/XXXXXXXXXXXXXXXXXXXXXXXX
To setup a region directory, first add the account to the accounts directory. Using the example of an AWS account named member1 with an ID of 111111111111, create accounts/member1.tf:
When applied via Terraform, an email invitation from AWS will be sent allowing an administrator to login, enable GuardDuty and accept the invitation for the master account to be the GuardDuty administrator of the member account.
We can use the same main.tf from the top-level directory for each region directory:
In the region directory, you will need the files backend.conf to configure the terraform state and variables.tf with the region (i.e. us-west-1). In the region directory, you can link main.tf and accounts desired:
$ cd us-west-1$ ln -s ../main.tf
$ ln -s ../accounts/member1.tf
To deploy the member account invitations in accounts.tf and the Lambda function:
GraphQL is a query language to enable applications to fetch data from servers. In fact, as it isn't tied to any specific database or storage engine, GraphQL can aggregate data from multiple sources to create a natural representation of your data.
Claudio Acquaviva
Get Gravitas and Go Amazonian: Kong Validated for AWS Graviton3, Amazon Linux 2023 OS
Today, we're thrilled to announce that Kong Enterprise and Kong Konnect Data Planes are now validated to run on AWS Graviton3 processors and Amazon Linux 2023 OS. As an APN Advanced Tier Partner of AWS, we were delighted to have the opportunity to
Claudio Acquaviva
Reach for the Clouds: A Crawl/Walk/Run Strategy with Kong and AWS
I once heard someone say, "What the cloud migration strategies lack at the moment is a methodology to Lift-and-Shift connections to the cloud." Let's digest that. In today's landscape, maintaining a competitive edge and delivering a high-quality cus
Danny Freese
APISecOps Tutorial: Delivering APIs Securely Together with Kong Konnect and Red Hat OpenShift Service on AWS (ROSA)
Red Hat OpenShift is the industry's leading enterprise Kubernetes platform that runs ubiquitously across on-prem, and the cloud. With Red Hat OpenShift Service on AWS (ROSA) , a managed Red Hat OpenShift platform that runs natively on AWS, it is
Danny Freese
Maintain Your Kong Gateway Audit Log Trail in AWS CloudTrail Lake
A critical and challenging requirement for many organizations is meeting audit and compliance obligations. The goal of compliance is to secure business processes, sensitive data, and monitor for unauthorized activities or breaches.
AWS CloudTrail
Danny Freese
Kong Data Plane Life Cycle With AWS Cloud Development Kit
From the modern application platform perspective, products should allow architects and DevOps teams to support dynamic topologies. That means a multi-platform capability is required but not sufficient. In fact, for several reasons, companies are loo
Claudio Acquaviva
5 Steps to Serverless Security With the AWS Lambda Plugin
For the DevOps-averse developer, lambdas are heaven. They can focus on writing self-contained and modularized pieces of code, deploying these functions for on-demand execution without being concerned about resource management or infrastructure. Lamb
Alvin Lee
Ready to see Kong in action?
Get a personalized walkthrough of Kong's platform tailored to your architecture, use cases, and scale requirements.