<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: nishikawaakira</title>
    <description>The latest articles on DEV Community by nishikawaakira (@nishikawaakira).</description>
    <link>https://dev.to/nishikawaakira</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1330403%2F7ec182ca-5ffc-440a-bd5d-512ee1d134b7.jpg</url>
      <title>DEV Community: nishikawaakira</title>
      <link>https://dev.to/nishikawaakira</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nishikawaakira"/>
    <language>en</language>
    <item>
      <title>Automating Phishing Site Reporting and Takedowns with AWS Lambda + Amazon Bedrock</title>
      <dc:creator>nishikawaakira</dc:creator>
      <pubDate>Fri, 03 Apr 2026 00:51:06 +0000</pubDate>
      <link>https://dev.to/aws-builders/automating-phishing-site-reporting-and-takedowns-with-aws-lambda-bedrock-4ie8</link>
      <guid>https://dev.to/aws-builders/automating-phishing-site-reporting-and-takedowns-with-aws-lambda-bedrock-4ie8</guid>
      <description>&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;"Your account has been suspended." "Please update your payment information." — Phishing SMS and emails like these have become a daily occurrence in Japan.&lt;/p&gt;

&lt;p&gt;As a security engineer, I used to manually report every phishing site I came across, but the workload per report was simply too much to keep up with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Look up the domain's IP address&lt;/li&gt;
&lt;li&gt;Identify the hosting provider via WHOIS&lt;/li&gt;
&lt;li&gt;Find the abuse contact email address&lt;/li&gt;
&lt;li&gt;Write a report email in English&lt;/li&gt;
&lt;li&gt;Take a screenshot and attach it&lt;/li&gt;
&lt;li&gt;Send it, then check whether the site was taken down&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;15 to 30 minutes per report.&lt;/strong&gt; Doing this for multiple sites every day just isn't realistic.&lt;/p&gt;

&lt;p&gt;So I built a system on AWS that automates all of the above with a single URL submission. In this article, I'll share the architecture and the lessons learned during development.&lt;/p&gt;

&lt;h2&gt;
  
  
  System Overview
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://xxx.execute-api.us-east-1.amazonaws.com/prod/report-auto &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url": "https://phishing-site.example.com"}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This single curl command triggers the following automated workflow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Capturing a screenshot of the site&lt;/li&gt;
&lt;li&gt;Using Bedrock (Claude) to analyze the page and infer the impersonated brand&lt;/li&gt;
&lt;li&gt;Looking up technical information such as hosting provider and abuse contact&lt;/li&gt;
&lt;li&gt;Drafting and sending a report email with supporting evidence&lt;/li&gt;
&lt;li&gt;Submitting reports to services such as Google Safe Browsing&lt;/li&gt;
&lt;li&gt;Monitoring DNS changes afterward to help confirm whether the site has been taken down&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To reduce false reports, the system stores reporting history, keeps a registry of known legitimate sites, and relies on multiple pieces of evidence before proceeding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Architecture Diagram
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8oqsdmwcvevnmu0lmqi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk8oqsdmwcvevnmu0lmqi.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  AWS Services Used
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Service&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;API Gateway&lt;/strong&gt; (HTTP API)&lt;/td&gt;
&lt;td&gt;REST endpoints (4 routes)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; (Zip x3)&lt;/td&gt;
&lt;td&gt;Report processing, reply handling, takedown monitoring&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Lambda&lt;/strong&gt; (Container x1)&lt;/td&gt;
&lt;td&gt;Playwright screenshots + form auto-submission&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;DynamoDB&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Abuse contact cache, report history, legitimate site registry&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;S3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Screenshot storage&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ECR&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Container image for Screenshot Lambda&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Bedrock&lt;/strong&gt; (Claude)&lt;/td&gt;
&lt;td&gt;Email generation, screenshot analysis, brand detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;EventBridge&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Daily takedown monitoring schedule&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Route 53&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Email domain DNS (SPF/DKIM/DMARC)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;All infrastructure is managed with Terraform&lt;/strong&gt; — a single &lt;code&gt;terraform apply&lt;/code&gt; deploys everything.&lt;/p&gt;

&lt;h2&gt;
  
  
  Interesting Technical Challenges
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Collecting Usable Screenshots from Phishing Sites
&lt;/h3&gt;

&lt;p&gt;Screenshots are important evidence in abuse reports. In practice, however, phishing sites often make automated access difficult, and a straightforward screenshot attempt may fail.&lt;/p&gt;

&lt;p&gt;To improve reliability, the system uses multiple approaches to collect screenshots, including third-party services such as urlscan.io when appropriate.&lt;/p&gt;

&lt;p&gt;In addition to helping with screenshot collection, third-party scan results can also serve as supporting evidence when reporting a site.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Three Uses of Bedrock (Claude)
&lt;/h3&gt;

&lt;p&gt;This system leverages Claude in three different contexts:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;a) Screenshot Quality Assessment&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Determines whether the captured screenshot shows actual phishing content, rather than a bot detection page, 404 error, or blank page.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;b) Automatic Brand Detection (&lt;code&gt;/report-auto&lt;/code&gt;)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The URL-only endpoint uses screenshots and HTML to determine which brand the phishing site is impersonating.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;c) Report Email Generation&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Based on technical information such as redirect chains, page HTML, and screenshots, Claude automatically generates professional emails for hosting provider abuse teams. It captures technical details that templates can't convey.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. A Multi-Layered Reporting Strategy
&lt;/h3&gt;

&lt;p&gt;Reporting a phishing site to only one destination is often not enough.&lt;/p&gt;

&lt;p&gt;That is why the system can notify multiple relevant parties for a single phishing site:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting provider abuse contacts&lt;/li&gt;
&lt;li&gt;Domain registrar abuse contacts&lt;/li&gt;
&lt;li&gt;Google Safe Browsing&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These channels serve different purposes.&lt;/p&gt;

&lt;p&gt;For example, hosting providers may take action against the server itself, while registrars may suspend the domain. Browser warning services can also help protect users by displaying alerts even if the site remains online for some time.&lt;/p&gt;

&lt;p&gt;This layered approach improves the chance of reducing harm, even when one reporting path does not immediately lead to action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost
&lt;/h2&gt;

&lt;p&gt;Because the system is built on serverless services, the monthly cost has been relatively low in my current usage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lambda&lt;/strong&gt;: Pay-per-use&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DynamoDB&lt;/strong&gt;: On-demand capacity&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;S3&lt;/strong&gt;: Screenshot storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bedrock (Claude)&lt;/strong&gt;: the main variable cost&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resend&lt;/strong&gt;: Email sending&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;urlscan.io&lt;/strong&gt;: public scans within the free tier &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my current setup and usage volume, the total cost has been roughly $5–10 per month, though this can vary depending on report volume and model usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Future Plans
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A reporting analytics dashboard to visualize response times by hosting provider and impersonated brand trends&lt;/li&gt;
&lt;li&gt;Expanding support for additional browser warning services such as Microsoft SmartScreen&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Reporting phishing sites is repetitive and time-consuming work, especially when handled one case at a time.&lt;/p&gt;

&lt;p&gt;By combining AWS serverless services with Bedrock (Claude), I built a system that streamlines much of that process — from screenshot collection to reporting and follow-up monitoring — from a single URL submission.&lt;/p&gt;

&lt;p&gt;There is still plenty of room to improve how we respond to phishing at scale, but even small automation can make this work more sustainable. I hope this article provides a useful reference for others working on similar problems.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0pl469x7ujsh31rpim4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0pl469x7ujsh31rpim4.png" alt=" " width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>security</category>
      <category>aws</category>
    </item>
    <item>
      <title>AWS Security Hub notifications made actionable using Amazon Bedrock</title>
      <dc:creator>nishikawaakira</dc:creator>
      <pubDate>Sat, 28 Dec 2024 05:23:43 +0000</pubDate>
      <link>https://dev.to/aws-builders/aws-security-hub-notifications-made-actionable-using-amazon-bedrock-1f3i</link>
      <guid>https://dev.to/aws-builders/aws-security-hub-notifications-made-actionable-using-amazon-bedrock-1f3i</guid>
      <description>&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Who Should Read This?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Engineers who don’t have dedicated security engineers and feel unsure about what to do with Security Hub notifications.&lt;/li&gt;
&lt;li&gt;Security engineers who send out Security Hub notifications but struggle to get others to take action.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Architecture
&lt;/h2&gt;

&lt;p&gt;The architecture is very simple: &lt;br&gt;
Security Hub detection -&amp;gt; EventBridge -&amp;gt; Lambda (invoking Amazon Bedrock). &lt;br&gt;
While the setup is straightforward, there are some potential pitfalls, and I’d like to share those considerations with you.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step-by-Step Guide (Amazon Bedrock)
&lt;/h2&gt;

&lt;p&gt;First and foremost, you need to prepare to use Bedrock. Let’s start by submitting a request.&lt;/p&gt;

&lt;p&gt;You can apply for access by navigating to the Bedrock page and going to the Model access section.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6rdcx2odi4omo5lfhw9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6rdcx2odi4omo5lfhw9q.png" alt=" " width="590" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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." &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fopr6vs3joei8bhdwm97a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fopr6vs3joei8bhdwm97a.png" alt=" " width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fna4d8aihr2uvtj72isyf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fna4d8aihr2uvtj72isyf.png" alt=" " width="800" height="218"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Even while waiting for approval, you can proceed with the following setup steps, so let’s keep going.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step-by-Step Guide (AWS Lambda)
&lt;/h2&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Settings and adding the layer
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Add an inline policy to allow Lambda to invoke Bedrock. Click on the role name displayed below to configure the role.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ip7ww99s5ce5k2k9bhh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4ip7ww99s5ce5k2k9bhh.png" alt=" " width="800" height="350"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;From the Add permissions section, click Create inline policy.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpy98j91fqzp8crlsz9jz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpy98j91fqzp8crlsz9jz.png" alt=" " width="800" height="252"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select JSON and enter the content below. For Bedrock actions, only InvokeModel is needed.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faz2hjaz9l3ye5w7wf8bd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faz2hjaz9l3ye5w7wf8bd.png" alt=" " width="800" height="234"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For reference, I’ll also provide the text version.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "bedrock:InvokeModel",
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, add a Layer to the Lambda function.&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mkdir libraries
pip3 install requests -t libraries/
pip3 install boto3 -t libraries/
pip3 install botocore -t libraries/
zip -r layer.zip libraries/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the above steps are complete, go to Layers and click Create layer.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8n1lvz8xququ58v2kb24.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8n1lvz8xququ58v2kb24.png" alt=" " width="800" height="289"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter an appropriate name and simply upload the ZIP file you created earlier.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffunuvu9zq6jkqpgf20x5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffunuvu9zq6jkqpgf20x5.png" alt=" " width="800" height="402"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the layer is created, go to its details page and copy the ARN.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywoid3t4zl4m3yz0nnmk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fywoid3t4zl4m3yz0nnmk.png" alt=" " width="800" height="236"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Return to the Lambda function you created earlier and click Add a layer.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdontqpynk6jog50z5oi5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdontqpynk6jog50z5oi5.png" alt=" " width="800" height="48"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Select Specify an ARN, paste the ARN you copied earlier, and click the Add button to complete the process.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvgo065szl89g5okvugfj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvgo065szl89g5okvugfj.png" alt=" " width="800" height="372"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setup Guide (Amazon EventBridge)
&lt;/h2&gt;

&lt;p&gt;Finally, navigate to the EventBridge page, go to Buses, click on Rules, and create a rule by selecting Create rule.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8yr0zifo4kmspdf7s8s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx8yr0zifo4kmspdf7s8s.png" alt=" " width="546" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter an appropriate name for the rule and select any desired Event bus. Then, choose Rule with an event pattern and click Next.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ouuodp17jl7x5vt7pzv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2ouuodp17jl7x5vt7pzv.png" alt=" " width="800" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwquj6tdiunf7nosnukk3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwquj6tdiunf7nosnukk3.png" alt=" " width="800" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Proceed to the next page and specify the Lambda function you created earlier.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjt74zu50b5wujx32e1l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpjt74zu50b5wujx32e1l.png" alt=" " width="800" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Let Security Hub detect it
&lt;/h2&gt;

&lt;p&gt;The easiest way to test is by creating an open Security Group. It's extremely useful for validation!&lt;/p&gt;

&lt;p&gt;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.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmx77b5dr2r2m2ca5zlie.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmx77b5dr2r2m2ca5zlie.png" alt=" " width="800" height="200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual content of the notification
&lt;/h2&gt;

&lt;p&gt;Security Hub Findings Summary:&lt;/p&gt;

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

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;Explanation:&lt;br&gt;
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.&lt;/p&gt;

&lt;p&gt;Why it's important:&lt;br&gt;
Limiting inbound traffic to only necessary ports reduces the attack surface of your EC2 instances, enhancing overall security.&lt;/p&gt;

&lt;p&gt;How to respond:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Review your security group rules.&lt;/li&gt;
&lt;li&gt;Identify which ports are necessary for your application.&lt;/li&gt;
&lt;li&gt;Update your security group rules to allow traffic only on required ports.&lt;/li&gt;
&lt;li&gt;Use CIDR blocks to restrict incoming traffic to known IP ranges when possible.
Here's an example Terraform code to address this issue:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This example:&lt;/p&gt;

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

&lt;p&gt;Remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always use the principle of least privilege.&lt;/li&gt;
&lt;li&gt;Regularly review and update your security group rules.&lt;/li&gt;
&lt;li&gt;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.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;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.&lt;/p&gt;

&lt;p&gt;The possibilities for improvements seem endless, don’t they? Thank you for reading to the end!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
    </item>
    <item>
      <title>Unlocking the Potential of Amazon CloudFront with VPC Origins: Overcoming WAF Bypass Challenges</title>
      <dc:creator>nishikawaakira</dc:creator>
      <pubDate>Sat, 28 Dec 2024 03:44:32 +0000</pubDate>
      <link>https://dev.to/aws-builders/unlocking-the-potential-of-amazon-cloudfront-with-vpc-origins-overcoming-waf-bypass-challenges-492f</link>
      <guid>https://dev.to/aws-builders/unlocking-the-potential-of-amazon-cloudfront-with-vpc-origins-overcoming-waf-bypass-challenges-492f</guid>
      <description>&lt;p&gt;Amazon CloudFront VPC Origins was announced on November 20, 2024. Have you had a chance to try it yet? Today, I’d like to explore why this feature is exciting from a security perspective and discuss the challenges it addresses when this feature is not in use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why is Amazon CloudFront with VPC Origins exciting?
&lt;/h2&gt;

&lt;p&gt;One of the significant advantages of this feature is the elimination of direct access to ALB or EC2 instances. These resources can now reside in private subnets, making them inaccessible directly from external sources. Access is strictly limited to routes through CloudFront within the same AWS account. Moreover, when WAF is configured on CloudFront, it ensures that origin access cannot bypass the WAF, adding an extra layer of security.&lt;/p&gt;

&lt;p&gt;The structure is as shown in the following diagram.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fovzwgq3j0yxeeq6u5yff.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fovzwgq3j0yxeeq6u5yff.png" alt=" " width="800" height="309"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How does it differ from previous setups?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5nufgci4ipyp93mn4nn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb5nufgci4ipyp93mn4nn.png" alt=" " width="800" height="322"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the architecture shown above, direct access to the ALB was possible if no additional configurations were applied. Similarly, if EC2 instances were deployed in public subnets, they could also be accessed directly. This meant that even with WAF configured on CloudFront, it couldn’t function effectively in this scenario.&lt;/p&gt;

&lt;p&gt;To mitigate these risks and block direct access to origins, it was necessary to implement additional measures. These included configuring CloudFront's managed prefix list on the ALB or adding custom headers in CloudFront and validating them at the ALB, as demonstrated below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60tdj2djusuv2b7rfih2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60tdj2djusuv2b7rfih2.png" alt=" " width="800" height="408"&gt;&lt;/a&gt;&lt;br&gt;
*Example: Configuring the managed prefix list.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6ijz2fgvwr90purzjt0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc6ijz2fgvwr90purzjt0.png" alt=" " width="800" height="176"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5sax0yaj7c7gtkljflv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5sax0yaj7c7gtkljflv.png" alt=" " width="800" height="529"&gt;&lt;/a&gt;&lt;br&gt;
*Setting and verifying custom headers&lt;/p&gt;

&lt;p&gt;While effective, these configurations could be surprisingly tedious. Ideally, CloudFront should have been the sole entry point from the start. The new VPC Origins feature eliminates the need to worry about inadvertent external access caused by misconfigurations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations of ALB-Based Validation
&lt;/h2&gt;

&lt;p&gt;As mentioned earlier, preventing origin access previously relied on measures like CloudFront's managed prefix list or custom header validation on the ALB. However, these methods could still be bypassed.&lt;/p&gt;

&lt;p&gt;Relying solely on CloudFront's managed prefix list is particularly insufficient. If an attacker knows the domain of the ALB, they can reference it from a different AWS account using their own CloudFront distribution. In other words, cross-account access is still possible.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtg8rw4ny79yu80shmk6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbtg8rw4ny79yu80shmk6.png" alt=" " width="800" height="541"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In such cases, requests can still reach the ALB from CloudFront's IP addresses. The problem here is that while the legitimate CloudFront may have WAF configured, the attacker’s CloudFront would not. As a result, even the most robust WAF rules can be bypassed entirely.&lt;/p&gt;

&lt;p&gt;For this reason, it’s generally recommended to validate custom headers alongside using the managed prefix list. However, this issue is completely resolved with CloudFront VPC Origins—a game-changing improvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  How can the ALB domain be exposed?
&lt;/h2&gt;

&lt;p&gt;There are scenarios where the ALB domain becomes discoverable. For instance, when enabling HTTPS access from CloudFront to the ALB, it’s common to assign a subdomain to the ALB. If the domain assigned to CloudFront is example.com, the ALB might use a subdomain like alb.example.com.&lt;/p&gt;

&lt;p&gt;In such cases, bypassing the legitimate CloudFront becomes relatively straightforward. Additionally, this information could potentially leak through former employees. Rotating credentials or domains to mitigate such risks can be both time-consuming and cumbersome. This is why VPC Origins, which can guard against such threats, is an incredibly valuable feature.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;What do you think? Have you been relying solely on a managed prefix list? As this post highlights, there are potential pitfalls to that approach. Let’s take advantage of CloudFront VPC Origins to build a more secure and robust environment!&lt;/p&gt;

</description>
      <category>aws</category>
      <category>security</category>
      <category>waf</category>
    </item>
    <item>
      <title>How to Transition AWS WAF from COUNT Mode to BLOCK Mode</title>
      <dc:creator>nishikawaakira</dc:creator>
      <pubDate>Wed, 02 Oct 2024 19:37:26 +0000</pubDate>
      <link>https://dev.to/aws-builders/how-to-transition-aws-waf-from-count-mode-to-block-mode-g0l</link>
      <guid>https://dev.to/aws-builders/how-to-transition-aws-waf-from-count-mode-to-block-mode-g0l</guid>
      <description>&lt;p&gt;In this article, I will share my experience after a colleague asked me, “I've been running WAF in COUNT mode for a while, but how do I determine which rules should be switched to BLOCK mode?”&lt;/p&gt;

&lt;p&gt;Although I initially thought it would be straightforward, I couldn't easily explain it, so I created a procedure. This article outlines how to search logs in Amazon CloudWatch Logs, analyze them, and determine when to switch to BLOCK mode. Even if you're using a different WAF, the same logic should apply when transitioning to BLOCK mode.&lt;/p&gt;

&lt;p&gt;Since this article assumes prior knowledge, I won’t explain basic concepts like “What is WAF?” or the specifications of AWS WAF’s managed rules.&lt;/p&gt;

&lt;h2&gt;
  
  
  COUNT Mode vs BLOCK Mode
&lt;/h2&gt;

&lt;p&gt;WAF typically has two modes: COUNT and BLOCK. Although there is also an ALLOW mode, it’s less commonly used as a default setting. &lt;/p&gt;

&lt;p&gt;When first providing a service, you use COUNT mode because starting with BLOCK mode might accidentally block legitimate requests. After running in COUNT mode, you assess the logs to decide whether to switch to BLOCK mode.&lt;/p&gt;

&lt;p&gt;This article aims to share the know-how of analyzing logs. The general idea is that the rule can be switched to BLOCK if legitimate requests aren't recorded under COUNT mode.&lt;/p&gt;

&lt;p&gt;The truth is, the decision of whether or not to block also depends on the service we are providing. &lt;br&gt;
For example, it fluctuates depending on the following factors.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does the service require authentication?&lt;/li&gt;
&lt;li&gt;Is it offered internationally?&lt;/li&gt;
&lt;li&gt;Does it provide APIs that third parties can call?&lt;/li&gt;
&lt;li&gt;Is it B2C or B2B?&lt;/li&gt;
&lt;li&gt;This article focuses on general SaaS services and how to proceed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article summarizes the content of what you should look for in general SaaS.&lt;/p&gt;

&lt;p&gt;Also, you need to pay attention to the logs output by AWS WAF, so let's examine those first.&lt;/p&gt;
&lt;h2&gt;
  
  
  Viewing AWS WAF Logs
&lt;/h2&gt;

&lt;p&gt;Here, we will look at the logs using CloudWatch Logs Insights.&lt;br&gt;
However, you will only use CloudWatch Logs Insights the first time to download the COUNT logs.&lt;br&gt;
Because the AWS WAF logs are complex and the detection details are written in arrays, it is extremely difficult for me to analyze them using a query as they are, and depending on the number of logs, trial and error can end up costing a lot of money. Furthermore, when you want to search for User-Agent and user-agent at the same time, you can't even write a regular expression, so I chose jq. However, of course, there are cases where it is not possible to download the logs for governance reasons. In that case, I personally think it is better to output the logs to Amazon S3 and use Amazon Athena.&lt;/p&gt;

&lt;p&gt;First of all, we need to understand the structure of the log, so let's take a look. It looks like this.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{

    "timestamp":1533689070589,                            
    "formatVersion":1,                                   
    "webaclId":"385cb038-3a6f-4f2f-ac64-09ab912af590",  
    "terminatingRuleId":"Default_Action",                
    "terminatingRuleType":"REGULAR",                     
    "action":"ALLOW",                                    
    "httpSourceName":"CF",                               
    "httpSourceId":"i-123",                             
    "ruleGroupList":[                                    
     {  
        "ruleGroupId":"41f4eb08-4e1b-2985-92b5-e8abf434fad3",
        "terminatingRule":null,    
        "nonTerminatingMatchingRules":[                  
            {"action" : "COUNT", "ruleId" : "4659b169-2083-4a91-bbd4-08851a9aaf74"}       
        ],
        "excludedRules":[
            {"exclusionType" : "EXCLUDED_AS_COUNT", "ruleId" : "5432a230-0113-5b83-bbb2-89375c5bfa98"}
        ]                          
     }
    ],
    "rateBasedRuleList":[                                 
     {  
        "rateBasedRuleId":"7c968ef6-32ec-4fee-96cc-51198e412e7f",   
        "limitKey":"IP",
        "maxRateAllowed":100                                                                                           
     },
     {  
        "rateBasedRuleId":"462b169-2083-4a93-bbd4-08851a9aaf30",
        "limitKey":"IP",
        "maxRateAllowed":100
     }
    ],      
    "nonTerminatingMatchingRules":[                                
        {"action" : "COUNT",  "ruleId" : "4659b181-2011-4a91-bbd4-08851a9aaf52"}    
    ],                                  
    "httpRequest":{                                                             
        "clientIp":"192.10.23.23",                                           
        "country":"US",                                                         
        "headers":[                                                                 
            {  
                "name":"Host",
                "value":"127.0.0.1:1989"
             },
             {  
                "name":"User-Agent",
                "value":"curl/7.51.2"
             },
             {  
                 "name":"Accept",
                 "value":"*/*"
             }
        ],
        "uri":"REDACTED",                                                
        "args":"usernam=abc",                                         
        "httpVersion":"HTTP/1.1",
        "httpMethod":"GET",
        "requestId":"cloud front Request id"                    
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reference: &lt;a href="https://docs.aws.amazon.com/waf/latest/developerguide/classic-logging.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/waf/latest/developerguide/classic-logging.html&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the case of AWS WAF, the field to look at in COUNT mode is called nonTerminatingMatchingRules.&lt;br&gt;
If the rule is set to ALLOW or BLOCK mode, you need to look at the &lt;code&gt;TerminatingMatchingRules&lt;/code&gt; field, which ends processing when a match is found, but COUNT mode is a non-terminating process, and it evaluates whether or not other rules match until the end. Therefore, you need to look at &lt;code&gt;notTerminatingMatchingRules&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Analyzing Logs
&lt;/h2&gt;

&lt;p&gt;You can extract the COUNT log by executing the following query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fields @timestamp, @message 
| filter @message like /"action":"COUNT"/ 
| sort @timestamp desc 
| display @timestamp, @message, httpRequest.clientIp, httpRequest.uri, httpRequest.country
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above results can be downloaded in JSON format. We recommend a minimum period of one month, but you should be aware that CloudWatch Logs Insights can only retrieve up to 10,000 items at a time.&lt;/p&gt;

&lt;p&gt;Once you've downloaded it, you can use jq to analyze it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Determining if the Request is Legitimate
&lt;/h2&gt;

&lt;p&gt;Now that we've come this far, we'll finally start to judge whether or not the request is legitimate, but we'll continue on the assumption that the SaaS has authentication (login).&lt;/p&gt;

&lt;p&gt;First, run the following query. As previously mentioned, check the &lt;code&gt;ruleId&lt;/code&gt; within the &lt;code&gt;nonTerminatingMatchingRules&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jq -r '.[] | .["@message"].nonTerminatingMatchingRules[] | .ruleId' logs-insights-results.json | sort | uniq -c | sort -nr 

10 AWSManagedRulesAnonymousIpList 
5   AWSManagedRulesCommonRuleSet 
5   AWSManagedRulesBotControlRuleSet 
1   AWSManagedRulesLinuxRuleSet 
1   AWSManagedRulesKnownBadInputsRuleSet
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can confirm these results. It is okay to set the rules that are not displayed here to BLOCK. This is because the fact that they are not recorded in normal use means that they are only recorded when an attack is received.&lt;/p&gt;

&lt;p&gt;Next, we will look at the combination of rules and URIs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jq -r '.[] | .["@message"] as $msg | $msg.nonTerminatingMatchingRules[] | "\(.ruleId) \($msg.httpRequest.uri)"' logs-insights-results.json | sort AWSManagedRulesAnonymousIpList / 

AWSManagedRulesAnonymousIpList / 
AWSManagedRulesAnonymousIpList /.env 
AWSManagedRulesAnonymousIpList /auth/favicon.ico AWSManagedRulesAnonymousIpList /auth/login 
AWSManagedRulesAnonymousIpList /auth/login 
AWSManagedRulesAnonymousIpList /auth/login 
AWSManagedRulesAnonymousIpList /auth/login 
AWSManagedRulesAnonymousIpList /wp-login.php 
AWSManagedRulesAnonymousIpList /wp-login.php 
AWSManagedRulesBotControlRuleSet / 
AWSManagedRulesBotControlRuleSet / 
AWSManagedRulesBotControlRuleSet /auth/favicon.ico AWSManagedRulesBotControlRuleSet /auth/login 
AWSManagedRulesBotControlRuleSet /auth/login 
AWSManagedRulesCommonRuleSet / 
AWSManagedRulesCommonRuleSet /news/12345
AWSManagedRulesCommonRuleSet /news/files 
AWSManagedRulesCommonRuleSet /auth/login 
AWSManagedRulesCommonRuleSet /news/sources/03a0 AWSManagedRulesKnownBadInputsRuleSet /.env 
AWSManagedRulesLinuxRuleSet /.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the following cases, it is possible to determine that it is BLOCK.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Detection is only performed for URIs that are not used in the service

&lt;ul&gt;
&lt;li&gt;In the above example, the following are targeted

&lt;ul&gt;
&lt;li&gt;AWSManagedRulesKnownBadInputsRuleSet&lt;/li&gt;
&lt;li&gt;AWSManagedRulesLinuxRuleSet&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;IP addresses/bot-related rules that are not detected in access after authentication

&lt;ul&gt;
&lt;li&gt;If there is no detection even after authentication, it can be seen that it is not legitimate use

&lt;ul&gt;
&lt;li&gt;It means that an unauthorized user logged in and was unable to use it&lt;/li&gt;
&lt;li&gt;If you don't want to allow bot access in the first place, just BLOCK them&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;In the example above, the following are targeted

&lt;ul&gt;
&lt;li&gt;AWSManagedRulesAnonymousIpList&lt;/li&gt;
&lt;li&gt;AWSManagedRulesBotControlRuleSe&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;If you are not confident about your decision here, outputting the User-Agent may help you make a decision. You can output the User-Agent as follows (I will omit the results)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jq -r '.[] | .["@message"] as $msg | $msg.nonTerminatingMatchingRules[] | "\(.ruleId) \($msg.httpRequest.uri) \($msg.httpRequest.headers[] | select(.name | test("(?i)user-agent")) | .value)"' logs-insights-results.json | sort
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you look at this, you can see &lt;code&gt;User-Agent&lt;/code&gt; that are clearly not your own users, so you can use this as an element to determine that it is not a legitimate request Basically, the rules for BLOCK can be determined from this investigation. For the rest, we recommend continuing to operate in COUNT mode in case anything happens. The reason for BLOCKing up to this point is that there are not many cases where it is okay to change to BLOCK after logging in, and if there is a case where BLOCK should be used, it means that the site has already been attacked, so there is a possibility that incident response will begin.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes on detecting bots
&lt;/h2&gt;

&lt;p&gt;AWS WAF has a managed rule called &lt;code&gt;“AWSManagedRulesBotControlRuleSet”&lt;/code&gt;. As the name suggests, this is for detecting bot access.&lt;br&gt;
If you BLOCK access because “our service does not allow bot access”,&lt;br&gt;
there may be unexpected pitfalls. When you set this to BLOCK, please keep in mind the E2E test. It is possible that the E2E test will be detected as a Bot access, the test will fail, and the deployment will fail. Of course, you will be able to notice this before it goes live, so it won't be a big problem, but it is possible that the deployment will suddenly fail, and you won't know what the cause is, so I think it would be good to be aware of this. Also, if you are using a Bot for a service that anyone can access, such as a toC service, you may receive many attack scans if you are outputting logs. This may result in a large amount of output and cost you money. Please consider whether or not to enable BotControl, including these points.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;This article summarized how to determine which rules can be safely switched to BLOCK mode based on AWS WAF logs. While it may seem straightforward, figuring out how to search logs required some trial and error. Understanding the structure of logs is key.&lt;/p&gt;

&lt;p&gt;I hope this article helps, and I’d appreciate any feedback or suggestions. Thank you for reading.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Delete automatic assignment of Public IPv4 addresses to Amazon EC2 instances using the AWS Systems Manager Automation runbook.</title>
      <dc:creator>nishikawaakira</dc:creator>
      <pubDate>Thu, 11 Jul 2024 07:49:47 +0000</pubDate>
      <link>https://dev.to/nishikawaakira/delete-automatic-assignment-of-public-ipv4-addresses-to-amazon-ec2-instances-using-the-aws-systems-manager-automation-runbook-15h8</link>
      <guid>https://dev.to/nishikawaakira/delete-automatic-assignment-of-public-ipv4-addresses-to-amazon-ec2-instances-using-the-aws-systems-manager-automation-runbook-15h8</guid>
      <description>&lt;p&gt;I recently did something similar using AWS Config -&amp;gt; Amazon EventBridge -&amp;gt; AWS Lambda in another article. &lt;a href="https://dev.to/nishikawaakira/automatically-remove-automatic-ipv4-address-assignment-to-amazon-ec2-instances-12fl"&gt;another article&lt;/a&gt;. In this case, I would like to use an Automation runbook in AWS Systems Manager to automatically delete the public IPv4 addresses of Amazon EC2 instances.&lt;/p&gt;

&lt;p&gt;I would like to tell you that you don't need many programming skills to use an Automation runbook to gain control.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an AWS Systems Manager Automation runbook
&lt;/h2&gt;

&lt;p&gt;Now, let's go ahead and create an AWS Systems Manager Automation runbook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv4dao7p0u23klm29ac4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhv4dao7p0u23klm29ac4.png" alt=" " width="800" height="503"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figqn4z2gyazad1jx33yq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Figqn4z2gyazad1jx33yq.png" alt=" " width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Initially, the following screen is opened, with the design tab now selected.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79dkawy15gq1tmtm3hxd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F79dkawy15gq1tmtm3hxd.png" alt=" " width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can create a runbook using the graphical interface in this way, or you can select the {} Code tab and edit YAML or JSON directly.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F023d7wmhs2lquytl96jn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F023d7wmhs2lquytl96jn.png" alt=" " width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Writing an Automation runbook
&lt;/h2&gt;

&lt;p&gt;When you visually add parts to the Automation runbook, you will see YAML being described. You can also write Python within this YAML. Take, for example, &lt;a href="https://ap-northeast-1.console.aws.amazon.com/systems-manager/documents/AWS-BulkDeleteAssociation/content?region=ap-northeast-1" rel="noopener noreferrer"&gt;AWS-BulkDeleteAssociation&lt;/a&gt; in the managed runbook.&lt;/p&gt;

&lt;p&gt;The following is written in Python, but this is quite difficult without some programming skills. This is not smart, and for the purpose of this article, I want to show that it can be implemented by people with no programming knowledge, so I want to prove that we can do it without using services like AWS Lambda!&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fif3trflsmtzzy8tja9st.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fif3trflsmtzzy8tja9st.png" alt=" " width="800" height="860"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;First of all, I will create a runbook with only the minimum required elements. Select the "AWS APIs" tab under the search box and enter "modifynetwork" in the search box to find the ModifyNetworkInterfaceAttribute of AWS API to be used this time. Drag and drop it to the middle of the "ModifyNetworkInterfaceAttribute" between Start and End.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmifojlreaiu5m1tbi0xu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmifojlreaiu5m1tbi0xu.png" alt=" " width="800" height="511"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;{} Open the Code tab. You will see that "ModifyNetworkInterfaceAttribute" is being hit as an API, as shown below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feog08tu6jpqry91n9j7x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feog08tu6jpqry91n9j7x.png" alt=" " width="800" height="494"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We want to put arguments here, as you can see from the AWS Lambda code in the previous article. It seems to be sufficient to enter NetworkInterfaceId.　&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifyNetworkInterfaceAttribute.html" rel="noopener noreferrer"&gt;API Reference is here&lt;/a&gt; You can also see that you need to set AssociatePublicIpAddress to false to prevent the automatic assignment of Public IPv4 addresses.&lt;/p&gt;

&lt;p&gt;Let's describe it as follows. The regular expression is written assuming that NetworkInterfaceId starts with "eni-" and that a combination of alphabets and numbers is entered. Also, entering (Required) in the parameter description will make it a required parameter. It is not mandatory to pass assumeRole as a parameter, but it is always recommended to do so. This is because you may want to use this runbook by assuming it from another AWS account. For example, there are use cases where a runbook is distributed to each account in CloudFormation StackSets, and the runbook can be assumed and executed from an administrative account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;schemaVersion: '0.3'
description: Disable Public IPv4 on EC2.
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
  AutomationAssumeRole:
    type: String
    description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
    default: ''
  NetworkInterfaceId:
    type: String
    description: (Required) Network Interface Id
    allowedPattern: ^eni-[a-z0-9]+$
mainSteps:
  - name: ModifyNetworkInterfaceAttribute
    action: aws:executeAwsApi
    isEnd: true
    inputs:
      Service: ec2
      Api: ModifyNetworkInterfaceAttribute
      NetworkInterfaceId: '{{ NetworkInterfaceId }}'
      AssociatePublicIpAddress: false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, enter a suitable name in NewRunbook and press "Create runbook".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcovitff45kpch1p5qls1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcovitff45kpch1p5qls1.png" alt=" " width="800" height="175"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Test Run Automation runbook
&lt;/h2&gt;

&lt;p&gt;Open the runbook you have created. You can find the runbook you created in the "Owned by me" tab.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2guz0eusmxahyanft4s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft2guz0eusmxahyanft4s.png" alt=" " width="800" height="329"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Open it and click on Execute automation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i3gesva1jivupytioe0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0i3gesva1jivupytioe0.png" alt=" " width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before you do so, create an EC2 instance. Check the network interface ID of the Amazon EC2 instance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6ytvwlqn2twj1h4e5hs.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fl6ytvwlqn2twj1h4e5hs.png" alt=" " width="800" height="762"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Try entering a Network Interface ID starting with "eni-" in the "NetworkInterfaceId" field of the Input parameters and click Execute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6em9to6wy24dlt0da7mb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6em9to6wy24dlt0da7mb.png" alt=" " width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check that the public IP address in the network interface ID on the EC2 instance has been removed. You will see that it has been deleted. We will now see if this is really the end of the process.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Config Settings
&lt;/h2&gt;

&lt;p&gt;As before, I will use the "ec2-instance-no-public-ip" managed rule. Open this Config rule and click on "Manage remediation".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0gsh24r6v6k31jkrtfk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz0gsh24r6v6k31jkrtfk.png" alt=" " width="800" height="229"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Choose your own AWS Systems Manager Automation runbook.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10zxtv3h4oyloon4pefl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F10zxtv3h4oyloon4pefl.png" alt=" " width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Notice that only InstanceId can be passed as a Resource ID parameter.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdfltv551yq40gjn2ei7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjdfltv551yq40gjn2ei7.png" alt=" " width="800" height="362"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oops, it seems that the network interface ID cannot be passed here, even though I really want to pass it here. In other words, there seems to be no way to pass the network interface ID dynamically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Get a List of Network Interface IDs from EC2 Instance ID
&lt;/h2&gt;

&lt;p&gt;We have no choice but to change course. You will find that you need to get the network interface ID from the EC2 instance to pass it on. There are two main methods. The first is to use DescribeInstances to get all EC2 information and extract only the necessary information. The second is to use &lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_DescribeNetworkInterfaces.html" rel="noopener noreferrer"&gt;DescribeNetworkInterfaces&lt;/a&gt; to get just the network interface information.&lt;/p&gt;

&lt;p&gt;This time, the information on the EC2 instance will hardly be used, so we will hit the DescribeNetworkInterfaces API to get the information on network interfaces only.&lt;/p&gt;

&lt;p&gt;So, as shown below, the DescribeNetworkInterfaces API is used to create the part that obtains information on network interfaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9epmrzxb1fx814oa38c9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9epmrzxb1fx814oa38c9.png" alt=" " width="688" height="584"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The code is as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;schemaVersion: '0.3'
description: Disable Public IPv4 on EC2.
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
  InstanceId:
    type: String
    description: The ID of EC2 instance
  AutomationAssumeRole:
    type: String
    description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
    default: ''
mainSteps:
  - name: DescribeNetworkInterfaces
    action: aws:executeAwsApi
    isEnd: true
    inputs:
      Service: ec2
      Api: DescribeNetworkInterfaces
      Filters:
        - Name: attachment.instance-id
          Values:
            - '{{InstanceId}}'
    outputs:
      - Name: NetworkInterfaceIds
        Selector: $.NetworkInterfaces..NetworkInterfaceId
        Type: StringList
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Network interface IDs can now be retrieved as a StringList. There is a caveat here: if you look at the API specification for DescribeNetworkInterfaces, the Request Parameter is written as Filter.N, but at first, it was not clear how to write it in YAML. The answer I arrived at is below, which is closer to the way &lt;a href="https://docs.aws.amazon.com/cli/latest/reference/ec2/describe-network-interfaces.html" rel="noopener noreferrer"&gt;CLI&lt;/a&gt;　is written.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Filters:
- Name: attachment.instance-id
Values:
- '{{InstanceId}}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Another is the Selector in outputs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Selector: $.NetworkInterfaces..NetworkInterfaceId
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This Selector allows you to specify which values are output and used in the next step. As an EC2 instance may have multiple network interfaces, it is necessary to retrieve them all, but when using Selector: $.NetworkInterfaces, only a MapList (like an associative array) can be specified for Type. In this case, the loop in the runbook cannot be used. Therefore, you need to get the array as a StringList, which can be done by writing "..". By writing this, the list of specified items can be retrieved as a StringList.&lt;/p&gt;

&lt;h2&gt;
  
  
  Delete the Public IPv4 Address of the Relevant Network Interface ID
&lt;/h2&gt;

&lt;p&gt;Now that you have reached this point, the rest is easy. Create a runbook like the following.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F692nutvvc6kyzioci1ae.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F692nutvvc6kyzioci1ae.png" alt=" " width="800" height="890"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final code is as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;schemaVersion: '0.3'
description: Disable Public IPv4 on EC2.
assumeRole: '{{ AutomationAssumeRole }}'
parameters:
  InstanceId:
    type: String
    description: The ID of EC2 instance
  AutomationAssumeRole:
    type: String
    description: (Optional) The ARN of the role that allows Automation to perform the actions on your behalf.
    default: ''
mainSteps:
  - name: DescribeNetworkInterfaces
    action: aws:executeAwsApi
    nextStep: Loop
    isEnd: false
    inputs:
      Service: ec2
      Api: DescribeNetworkInterfaces
      Filters:
        - Name: attachment.instance-id
          Values:
            - '{{InstanceId}}'
    outputs:
      - Name: NetworkInterfaceIds
        Selector: $.NetworkInterfaces..NetworkInterfaceId
        Type: StringList
  - name: Loop
    action: aws:loop
    isEnd: true
    inputs:
      Iterators: '{{ DescribeNetworkInterfaces.NetworkInterfaceIds }}'
      Steps:
        - name: ModifyNetworkInterfaceAttribute
          action: aws:executeAwsApi
          isEnd: true
          inputs:
            Service: ec2
            Api: ModifyNetworkInterfaceAttribute
            NetworkInterfaceId: '{{ Loop.CurrentIteratorValue }}'
            AssociatePublicIpAddress: false

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;"Create new version", then select "Create new default version" to create a new document.&lt;/p&gt;

&lt;p&gt;Before you start testing, launch an EC2 instance and attach several Network Interfaces.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyfmh3j2d41zkq86orw1y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fyfmh3j2d41zkq86orw1y.png" alt=" " width="800" height="779"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then open the runbook, click on "Execute automation", enter the InstanceId and press Execute.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faly8h53ps2gg4xo9pwfy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faly8h53ps2gg4xo9pwfy.png" alt=" " width="800" height="351"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check the previous screen to see if it has been disabled properly. If it is disabled as shown in the image below, you have succeeded.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygzk1df8gb1126uccmgu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fygzk1df8gb1126uccmgu.png" alt=" " width="800" height="774"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In this way, control can be exercised without programming knowledge. However, when you write it like this, you might think it's easy to do! And while that is the aim, there are some real quirks in the way YAML is written, and I had a lot of trouble with it. At first, I thought that to get the network interface IDs as a list, I would need to write some Python code. And indeed, the first version of this blog did. While researching, I realized that it could be retrieved using "..".&lt;/p&gt;

&lt;p&gt;I'm going to accumulate this kind of runbook writing style and work towards a situation where anyone can control the AWS environment!&lt;/p&gt;

&lt;p&gt;Thank you for reading to the end.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>automation</category>
    </item>
    <item>
      <title>Automatically remove automatic IPv4 address assignment to Amazon EC2 instances</title>
      <dc:creator>nishikawaakira</dc:creator>
      <pubDate>Wed, 05 Jun 2024 18:49:42 +0000</pubDate>
      <link>https://dev.to/nishikawaakira/automatically-remove-automatic-ipv4-address-assignment-to-amazon-ec2-instances-12fl</link>
      <guid>https://dev.to/nishikawaakira/automatically-remove-automatic-ipv4-address-assignment-to-amazon-ec2-instances-12fl</guid>
      <description>&lt;p&gt;It is now possible to dynamically remove and add public IPv4 addresses that were recently automatically assigned to an EC2 instance.&lt;br&gt;
&lt;a href="https://aws.amazon.com/about-aws/whats-new/2024/04/removing-adding-auto-assigned-public-ipv4-address/" rel="noopener noreferrer"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Accordingly, this article will try to automatically delete the public IPv4 address that was assigned to EC2 by default.&lt;/p&gt;
&lt;h2&gt;
  
  
  Configuration
&lt;/h2&gt;

&lt;p&gt;This time, I used the configuration AWS Config -&amp;gt; Amazon EventBridge -&amp;gt; AWS Lambda. Actually, AWS Config -&amp;gt; AWS Systems Manager can also be configured using Automation runbook, which is smarter, but this time I would like to introduce the method using AWS Lambda.&lt;/p&gt;
&lt;h2&gt;
  
  
  Configure AWS Config
&lt;/h2&gt;

&lt;p&gt;First, configure AWS Config." &lt;a href="https://docs.aws.amazon.com/config/latest/developerguide/ec2-instance-no-public-ip.html" rel="noopener noreferrer"&gt;ec2-instance-no-public-ip&lt;/a&gt;". This managed rule, however, does not enable evaluation of provisioned resources. However, the evaluation mode of this managed rule enables evaluation of provisioned resources, and evaluation is performed only after the instance is started. Therefore, if you want to modify it at startup, you need to prepare your own rules. On the other hand, if you want to target EC2 after startup, this rule may be suitable.&lt;/p&gt;

&lt;p&gt;That's all there is to AWS Config. It is very easy.&lt;/p&gt;
&lt;h2&gt;
  
  
  Create AWS Lambda
&lt;/h2&gt;

&lt;p&gt;In fact, here is where I run into a bit of a snag. In this case, I have chosen Python, but in general, if you want to use Python in Lambda to execute APIs, you will need to use a library called Boto3. Lambda comes standard with this library, but if the API specification has just been changed, as in this case, Boto3 is not supported, or the version of Boto3 in Lambda itself is a bit old, and an error will occur.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/APIReference/API_ModifyNetworkInterfaceAttribute.html" rel="noopener noreferrer"&gt;API Specification&lt;/a&gt; and send a request as follows,&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt0r9o4m6uitcc2uz6oo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpt0r9o4m6uitcc2uz6oo.png" alt="Actual error" width="800" height="71"&gt;&lt;/a&gt;&lt;br&gt;
Unknown parameter in input: "AssociatePublicIpAddress"&lt;br&gt;
You may see that you have been told, "Unknown parameter in input: "AssociatePublicIpAddress".&lt;/p&gt;

&lt;p&gt;So, let's first find out how many versions of Boto3 Lambda has.&lt;br&gt;
Let's run the following in Lambda.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzzag7dtzt3lcx4wahza.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzzag7dtzt3lcx4wahza.png" alt="print Boto3 version" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When I ran this, it showed 1.34.42 (as of 5/14/2024).&lt;br&gt;
However, the latest version of Boto3 at this time is 1.34.103. And I see that the ability to dynamically remove and add auto-assigned public IPv4 addresses for this EC2 instance was added in 1.34.91.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60scwkzo74zmc4sucecq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F60scwkzo74zmc4sucecq.png" alt="Boto3 version" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I found that it is not possible to run the application as it is, so we need to create a Lambda Layer.&lt;/p&gt;

&lt;p&gt;Incidentally, as a side note, the same results were obtained when making API calls with YAML in Systems Manager, so it seems that Systems Manager also takes some time to catch up with the latest API.&lt;br&gt;
So, keep in mind that you will need to wait a few days after a new specification is implemented.&lt;/p&gt;

&lt;p&gt;I will not discuss how to create the Lambda Layer, but once it has been created, implement it as follows.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import json
import boto3

def modify_network_interface_attribute(network_interface_id):
    ec2 = boto3.client('ec2')

    try:
        response = ec2.modify_network_interface_attribute(
            NetworkInterfaceId=network_interface_id, # Specify the target Network Interface ID.
            AssociatePublicIpAddress=False # False deletes the public IPv4 address
        )
        print("Network Interface Modified Successfully:")
        print(response)
    except Exception as e:
        print("An error occurred:", e)



def lambda_handler(event, context):

    detail = event['detail']
    # network_interface_id = detail['configurationItem']['configuration']['networkInterfaceId']

    network_interface_ids = []

    # Get a list of resources that have changed
    result = detail.get('newEvaluationResult')
    identifier = result.get('evaluationResultIdentifier')
    configuration_item = identifier.get('evaluationResultQualifier')

    # Check if the resource type is an EC2 instance
    if configuration_item and configuration_item['resourceType'] == 'AWS::EC2::Instance':
        instance_id = configuration_item['resourceId']

        ec2_client = boto3.client('ec2')

        # Get instance details
        response = ec2_client.describe_instances(InstanceIds=[instance_id])
        reservations = response['Reservations']

        # Stop auto-assign of all network interface IDs
        for reservation in reservations:
            for instance in reservation['Instances']:
                for interface in instance['NetworkInterfaces']:
                    network_interface_ids.append(interface['NetworkInterfaceId'])
                    modify_network_interface_attribute(interface['NetworkInterfaceId'])

    print("Network Interface IDs: ", network_interface_ids)

    return {
        'statusCode': 200,
        'body': json.dumps('Success!!')
    }

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It is written in a rather crude manner and returns, "Success! but if you just want to remove the public IP address automatically, you can do it with this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing AWS Lambda
&lt;/h2&gt;

&lt;p&gt;The following can be used for testing. Please enter the instance ID you have set up appropriately in resourceId, and then run the test.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "version": "0",
  "id": "ID",
  "detail-type": "Config Rules Compliance Change",
  "source": "aws.config",
  "account": "[AWS Account ID]",
  "time": "2024-05-19T12:19:54Z",
  "region": "ap-northeast-1",
  "resources": [],
  "detail": {
    "resourceId": "[Instance ID]",
    "awsRegion": "ap-northeast-1",
    "awsAccountId": "[AWS Account ID]",
    "configRuleName": "ec2-instance-no-public-ip",
    "recordVersion": "1.0",
    "configRuleARN": "arn:aws:config:ap-northeast-1:[AWS Account ID]:config-rule/config-rule-qx4cpe",
    "messageType": "ComplianceChangeNotification",
    "newEvaluationResult": {
      "evaluationResultIdentifier": {
        "evaluationResultQualifier": {
          "configRuleName": "ec2-instance-no-public-ip",
          "resourceType": "AWS::EC2::Instance",
          "resourceId": "[Instance ID]",
          "evaluationMode": "DETECTIVE"
        },
        "orderingTimestamp": "2024-05-19T00:03:43.749Z"
      },
      "complianceType": "NON_COMPLIANT",
      "resultRecordedTime": "2024-05-19T12:19:53.922Z",
      "configRuleInvokedTime": "2024-05-19T12:19:53.696Z",
      "annotation": "This Amazon EC2 Instance uses a public IP."
    },
    "notificationCreationTime": "2024-05-19T12:19:54.417Z",
    "resourceType": "AWS::EC2::Instance"
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If it works correctly, the configuration of the instance in question should change as follows.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4u3w06tl66sobq8pw45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4u3w06tl66sobq8pw45.png" alt="Auto-assign public IP disabled" width="800" height="747"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Configure Amazon Event Bridge
&lt;/h2&gt;

&lt;p&gt;Finally, let's create the rules for EventBridge.&lt;/p&gt;

&lt;p&gt;The event pattern is as follows AWS Config is the source and calls Lambda based on Compliance Change for ec2-instance-no-public-ip.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "source": ["aws.config"],
  "detail-type": ["Config Rules Compliance Change"],
  "detail": {
    "configRuleName": ["ec2-instance-no-public-ip"]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Select AWS Lambda function in Target and select AWS Lambda function you just created.&lt;/p&gt;

&lt;p&gt;All that remains is to create another instance and confirm that the modifications have been made automatically.&lt;br&gt;
You may want to monitor it to make sure it is working properly.&lt;br&gt;
If it is not working properly, try logging in AWS Lambda function and debugging to see if the Lambda itself is being called properly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I was able to remove the public IP from EC2 more easily than I expected, but it is not as simple as using AWS Systems Manager Automation runbook (my own subjective opinion), and requires a fair amount of coding knowledge. I'm writing roughly again this time, but we should also pick up error cases more thoroughly, and we need to pay attention to such things.&lt;br&gt;
Next time, I would like to do the same assignment to see how it can be written using AWS Systems Manager Automation runbook.&lt;/p&gt;

</description>
      <category>aws</category>
    </item>
    <item>
      <title>Use Central configuration for AWS Security Hub operation in AWS multi-accounts</title>
      <dc:creator>nishikawaakira</dc:creator>
      <pubDate>Wed, 10 Apr 2024 01:14:34 +0000</pubDate>
      <link>https://dev.to/aws-builders/use-central-configuration-for-aws-security-hub-operation-in-aws-multi-accounts-23n8</link>
      <guid>https://dev.to/aws-builders/use-central-configuration-for-aws-security-hub-operation-in-aws-multi-accounts-23n8</guid>
      <description>&lt;p&gt;Today I would like to write about AWS Security Hub Central configuration, a feature announced at AWS re:Invent 2023.&lt;/p&gt;

&lt;p&gt;At the company called Kaminashi I work for, we already have a Security Hub in operation. However, we are halfway to the ideal situation where developers themselves understand the risks and proactively respond to them.&lt;br&gt;
That being the case, Security Hub Central configuration is a highly recommended feature that may help.&lt;/p&gt;

&lt;h2&gt;
  
  
  AWS Security Hub Operational Issues and Central configuration
&lt;/h2&gt;

&lt;p&gt;I was unaware that this feature had been added until recently.&lt;br&gt;
But this feature is indispensable for operating Security Hub. The reason for this is that in Automation rules, it is not possible to write rules by OU or by identifying tags, etc. attached to an account. However, when AWS accounts are managed by Organization, there are various cases such as Sandbox OUs, accounts for prototype implementation, accounts for personal verification (which may be included in Sandbox OUs), etc. How do you control these accounts? You need to think about that.&lt;br&gt;
Because when you want to try out a workshop and Security Hub detects something that is not so dangerous, you may think, “Forgive me, it's only temporary”.&lt;br&gt;
And because of this, there are times when you might want to disable Security Hub itself for such accounts, or loosen the rules. Central configuration can be used for this purpose.&lt;/p&gt;

&lt;h2&gt;
  
  
  Notes on activating AWS Security Hub Central configuration
&lt;/h2&gt;

&lt;p&gt;Note that enabling Central configuration while Security Hub is already enabled will cause AWS Config to be redetected.&lt;/p&gt;

&lt;p&gt;the image attached below is what you will see after activating the Central configuration.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahk6lxltj4fkj15qziv3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fahk6lxltj4fkj15qziv3.png" alt=" " width="800" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this state, the policy is self-managed, but the account owner is configuring it individually.&lt;br&gt;
If that is what you intended, fine, but if you want total control and it is happening, you need to be careful.&lt;br&gt;
However, there are times when Self-managed is effective, and those are when an account has already been suspended and cannot be excluded from Organization. In such a situation, the new policy cannot be applied. If that happens, the policy status of that higher OU will be failure. In such a case, please try to change the policy status to Self-Managed to make the policy status successful.&lt;/p&gt;

&lt;p&gt;Yes, so let's start by creating a default policy. Then apply it to the Root OU. By doing so, the rule is applied to all accounts under the OU to which it belongs. The same policy will also be automatically applied to any accounts created thereafter.&lt;/p&gt;

&lt;p&gt;Now let's look at policy creation.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuyudklk9lc5b5uq856uu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuyudklk9lc5b5uq856uu.png" alt=" " width="800" height="718"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This is how it looks like, the recommended setting type is "Use the AWS recommended Security Hub configuration across my entire organization", but if you choose this, only the "AWS Foundational  Security Best Practices v1.0.0" security criteria are in effect and cannot be changed.&lt;/p&gt;

&lt;p&gt;So, although it is recommended, do not use this, but select "Customize my Security Hub configuration" and choose the minimum security criteria to be used by your organization.&lt;/p&gt;

&lt;p&gt;Incidentally, if you choose "Use the AWS recommended Security Hub configuration across my entire organization" without customization, you will not be able to change the policy name, and a policy with the very subtle name “configuration-policy-01” will be created initially.(It can be changed later)&lt;/p&gt;

&lt;h2&gt;
  
  
  Confirmation of configuration
&lt;/h2&gt;

&lt;p&gt;Once successfully configured, you will see the following on Security Hub overview page for each AWS account.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapbpu6rh1h97a7d3c5ag.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fapbpu6rh1h97a7d3c5ag.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if you try to activate the security criteria on this page, you will get the following error message.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0hyoope3mjvsy718hw1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe0hyoope3mjvsy718hw1.png" alt=" " width="800" height="63"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the account to which the administration of Security Hub has been transferred with the Central configuration set up, you will see that the policy has been applied as shown in the image below upon success.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldrfpj4ow9oxu406atxy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fldrfpj4ow9oxu406atxy.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Furthermore, if a strict policy is applied to the Security OU, which sets all security criteria, the following image shows that only the Security OU has the restrict-policy while the other OUs remain in the general-policy.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fop9kcccrf5yj5qjy7ua5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fop9kcccrf5yj5qjy7ua5.png" alt=" " width="800" height="242"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Very good points about Central configuration
&lt;/h2&gt;

&lt;p&gt;As you may already know from what we have seen so far, what is good about this Central configuration is that you can centrally manage whether or not to activate Security Hub in the first place. As you can see when you do an AWS workshop with a personal verification account, resources that are out of compliance are usually created. I don't want to be notified of alerts in those situations.&lt;br&gt;
And it is not hard to imagine that it would be very noisy in a workshop with all the developers participating. So the first good thing is that you can choose to disable Security Hub only for such OUs and accounts. Also, since security standards can be changed flexibly, you can choose PCI DSS only for services that handle credit cards, or choose a security standard that meets your organization's guidelines if you do not handle credit cards. In other words, you can set stricter standards for production and staging environments, and slightly looser standards for development environments, and so on, depending on the environment. &lt;br&gt;
Oh, how wonderful!&lt;/p&gt;

&lt;p&gt;And, as before, you can choose which of these controls to activate, adjust the parameters of the controls, and make other flexible settings!&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Personally, I am really happy to be able to do better what I could not do with Automation rules. This is exactly what I have been waiting for.&lt;br&gt;
This is a good opportunity for everyone to rethink the operation of Security Hub.&lt;/p&gt;

&lt;p&gt;Thank you for reading to the end.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>securityhub</category>
    </item>
  </channel>
</rss>
