DEV Community

AWS Security Hub notifications made actionable using Amazon Bedrock

There were many Generative AI sessions at AWS re:Invent. Although I didn’t attend most of them, participating in a Generative AI GameDay made me realize that Generative AI isn’t as difficult as I thought. This inspired me to work on making Security Hub notifications more actionable, so that any engineer can handle them. This is the background behind writing this blog.

Who Should Read This?

  • Engineers who don’t have dedicated security engineers and feel unsure about what to do with Security Hub notifications.
  • Security engineers who send out Security Hub notifications but struggle to get others to take action.

Architecture

The architecture is very simple:
Security Hub detection -> EventBridge -> Lambda (invoking Amazon Bedrock).
While the setup is straightforward, there are some potential pitfalls, and I’d like to share those considerations with you.

Step-by-Step Guide (Amazon Bedrock)

First and foremost, you need to prepare to use Bedrock. Let’s start by submitting a request.

You can apply for access by navigating to the Bedrock page and going to the Model access section.
Image description

I used Claude 3.5 Sonnet. In my environment, access was already granted, but if you need to request access, click on "Available to request."
Image description

After clicking, a popup will appear. Select "Request model access" and proceed. You’ll then see a screen where you can select the models you want to use. Choose the desired model and click Next.
Image description

If this is your first request, you’ll need to provide information such as your company name and the intended use case during the application process. Fill in the required details and submit your request. In my case, access was granted in about 10 minutes. With that, the Bedrock setup is complete.

Even while waiting for approval, you can proceed with the following setup steps, so let’s keep going.

Note: In this blog, I will also introduce the Lambda code, assuming the use of Claude 3.5 Sonnet. Please be aware that the interface might vary slightly depending on the model. If you’re using a different model, you may need to make adjustments accordingly.

Step-by-Step Guide (AWS Lambda)

Let’s start by writing the code. The example is in Python. Make sure to adjust settings such as the region and the Slack Webhook URL as needed.

import json
import boto3
import requests

bedrock_client = boto3.client(service_name='bedrock-runtime', region_name="ap-northeast-1")

SLACK_WEBHOOK_URL = ""

def lambda_handler(event, context):
    try:

        # Retrieve Security Hub findings
        findings = event.get('detail', {}).get('findings', [])
        if not findings:
            print("No findings received.")
            return

        # Summarize Findings (Bedrock API call)
        summary = summarize_findings_with_bedrock(findings)

        send_to_slack(summary)

        return {
            'statusCode': 200,
            'body': json.dumps('Notification sent successfully!')
        }

    except Exception as e:
        print(f"Error processing findings: {str(e)}")
        return {
            'statusCode': 500,
            'body': json.dumps(f"Error: {str(e)}")
        }

def summarize_findings_with_bedrock(findings):
    # Findings are summarized and converted to text
    findings_text = "\n".join([
        f"Id: {finding.get('Id')}\nDescription: {finding.get('Description')}"
        for finding in findings
    ])

    prompt = f"""
    {findings_text}

    Please provide clear instructions and example terraform code on why and how to respond so that everyone can respond when they receive the above Security Hub findings notification.
    """ 

    body = json.dumps({
        "anthropic_version": "bedrock-2023-05-31",
        "max_tokens": 2048,
        "messages": [{"role": "user", "content": prompt}],
        "temperature": 0.7
    })

    response = bedrock_client.invoke_model(
        body=body,
        modelId='anthropic.claude-3-5-sonnet-20240620-v1:0',
        contentType="application/json",
        accept="application/json"
    )

    response_body = json.loads(response['body'].read())
    return findings_text + response_body['content'][0]['text']    

def send_to_slack(message):
    slack_message = {
        "text": f"Security Hub Findings Summary:\n{message}"
    }

    response = requests.post(
        SLACK_WEBHOOK_URL,
        headers={"Content-Type": "application/json"},
        data=json.dumps(slack_message)
    )

    if response.status_code != 200:
        raise ValueError(f"Failed to send message to Slack: {response.status_code}, {response.text}")
Enter fullscreen mode Exit fullscreen mode

Here, we provide Terraform code as a sample. However, feel free to modify it to suit your organization's needs. If it hasn't been integrated into your IaC process, remove any unnecessary prompts accordingly.

The anthropic_version is included in the body of the request, but this is only required when using Anthropic models. It might not be necessary for some Anthropic models, though this hasn’t been thoroughly investigated. Additionally, the structure of messages and the content passed as a prompt can vary depending on the model, which can be a potential stumbling block. If you encounter issues when using a different model, it’s a good idea to check these aspects.

Settings and adding the layer

I set the timeout to around 1 minute. If it's set to something like 10 seconds, there’s a chance the response won’t return. At the very least, the default 3 seconds for Lambda is definitely not sufficient, so it needs to be increased.

Add an inline policy to allow Lambda to invoke Bedrock. Click on the role name displayed below to configure the role.
Image description

From the Add permissions section, click Create inline policy.
Image description

Select JSON and enter the content below. For Bedrock actions, only InvokeModel is needed.
Image description

For reference, I’ll also provide the text version.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "bedrock:InvokeModel",
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}
Enter fullscreen mode Exit fullscreen mode

Next, add a Layer to the Lambda function.

Since we’re using requests and boto3, we’ll create it quickly. Follow the steps below to create a ZIP file containing the required Python libraries. Note that the latest version of boto3 is required, so make sure to follow this procedure.

mkdir libraries
pip3 install requests -t libraries/
pip3 install boto3 -t libraries/
pip3 install botocore -t libraries/
zip -r layer.zip libraries/
Enter fullscreen mode Exit fullscreen mode

Once the above steps are complete, go to Layers and click Create layer.
Image description

Enter an appropriate name and simply upload the ZIP file you created earlier.
Image description

After the layer is created, go to its details page and copy the ARN.
Image description

Return to the Lambda function you created earlier and click Add a layer.
Image description

Select Specify an ARN, paste the ARN you copied earlier, and click the Add button to complete the process.
Image description

Setup Guide (Amazon EventBridge)

Finally, navigate to the EventBridge page, go to Buses, click on Rules, and create a rule by selecting Create rule.

Image description

Enter an appropriate name for the rule and select any desired Event bus. Then, choose Rule with an event pattern and click Next.
Image description

Keep most of the default settings, focusing only on selecting the Event pattern. For AWS service, select Security Hub, and for Event type, choose Security Hub Findings - Imported.

Next, configure the notification targets. To avoid excessive notifications, limit the findings to HIGH and CRITICAL severity levels. Additionally, to target only newly created findings, select NEW for the workflow status.
Image description

Proceed to the next page and specify the Lambda function you created earlier.
Image description

If no additional tags or settings are needed, go ahead and click Create rule. That’s it — the setup is complete!

Let Security Hub detect it

The easiest way to test is by creating an open Security Group. It's extremely useful for validation!

Enter a suitable name and description, select a VPC, and add the following settings to the Inbound rules. Click the Create security group button, and then just wait for it to be detected.
Image description

One final note: if the detection frequency in your AWS Config settings is set to Daily, detection may not happen immediately. In that case, you’ll just need to wait. There likely isn’t any workaround other than temporarily changing the frequency. If the setting is configured for continuous detection, it was detected within around 5 minutes in my experience.

The actual content of the notification

Security Hub Findings Summary:

Id: arn:aws:securityhub:ap-northeast-1:637423304033:security-control/EC2.18/finding/9e0e7000-0e27-435f-9f59-e5cc716e561b

Description: This control checks whether an Amazon EC2 security group permits unrestricted incoming traffic from unauthorized ports. The control status is determined as follows: If you use the default value for 'authorizedTcpPorts', the control fails if the security group permits unrestricted incoming traffic from any port other than ports 80 and 443; If you provide custom values for 'authorizedTcpPorts' or 'authorizedUdpPorts', the control fails if the security group permits unrestricted incoming traffic from any unlisted port; If no parameter is used, the control fails for any security group that has an unrestricted inbound traffic rule.Certainly! I'll provide an explanation of the finding and how to address it using Terraform code.

Explanation:
This Security Hub finding (EC2.18) is alerting you about potentially insecure configurations in your EC2 security groups. The control checks for unrestricted incoming traffic on unauthorized ports, which could expose your instances to unnecessary risks.

Why it's important:
Limiting inbound traffic to only necessary ports reduces the attack surface of your EC2 instances, enhancing overall security.

How to respond:

  1. Review your security group rules.
  2. Identify which ports are necessary for your application.
  3. Update your security group rules to allow traffic only on required ports.
  4. Use CIDR blocks to restrict incoming traffic to known IP ranges when possible. Here's an example Terraform code to address this issue:
hcl
resource "aws_security_group" "example" {
  name        = "example-sg"
  description = "Example security group with restricted inbound rules"
  vpc_id      = aws_vpc.main.id

  # Allow incoming HTTPS traffic
  ingress {
    description = "HTTPS from anywhere"
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # Allow incoming HTTP traffic
  ingress {
    description = "HTTP from anywhere"
    from_port   = 80
    to_port     = 80
    protocol    = "tcp"
    cidr_blocks = ["0.0.0.0/0"]
  }

  # Allow SSH access from a specific IP range
  ingress {
    description = "SSH from corporate network"
    from_port   = 22
    to_port     = 22
    protocol    = "tcp"
    cidr_blocks = ["10.0.0.0/8"]  # Replace with your corporate network CIDR
  }

  # Allow outbound traffic to anywhere
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "example-sg"
  }
}
Enter fullscreen mode Exit fullscreen mode

This example:

  1. Allows inbound HTTPS (443) and HTTP (80) traffic from anywhere.
  2. Restricts SSH (22) access to a specific IP range (replace with your actual range).
  3. Allows all outbound traffic. To use this:
  4. Replace aws_vpc.main.id with your actual VPC ID or reference.
  5. Adjust the ingress rules based on your specific needs.
  6. Update the CIDR blocks to match your required IP ranges.

Remember:

  • Always use the principle of least privilege.
  • Regularly review and update your security group rules.
  • Consider using Security Group references instead of CIDR blocks for internal resources. By implementing these changes, you should resolve the Security Hub finding and improve your EC2 instance security.

Conclusion

What did you think? By reviewing the notifications this way, it’s now much clearer why detections occur and what actions need to be taken. From the next time on, it seems likely that more attention will be paid to the configurations. While the notification content of Security Hub findings can be quite hard to understand as is, passing them through Generative AI makes them significantly easier to interpret.

Additionally, if your infrastructure is managed as code, you could even pass the source code itself to Generative AI and ask directly what needs to be changed. That could be a great approach as well.

The possibilities for improvements seem endless, don’t they? Thank you for reading to the end!

Top comments (0)