Skip to main content
友田 陽大
Amazon GuardDuty in production
セキュリティ
AWS
GuardDuty
RDS
Lambda

GuardDuty RDS Protection and Lambda Protection: Detecting DB Login Anomalies and Serverless Network Threats Agentlessly, with Zero Infrastructure Change

An implementation guide for designing Amazon GuardDuty's RDS Protection and Lambda Protection for production. Directly monitor Aurora/RDS login anomalies (success, failure, brute force, malicious IPs, Tor), and detect crypto mining / C&C communication from the network activity of all Lambda functions (including non-VPC) — both agentless, zero infrastructure change, no code change. Explained per the official docs: supported engines, finding types, the up-to-two-week learning period, pricing and the 30-day free trial, Terraform enablement, and EventBridge automated response.

Published
Reading time
28 min read
Author
友田 陽大
Share

"Can we even notice suspicious logins to the DB, or a Lambda communicating with the outside on its own? — but we don't want to install agents or rebuild our logging platform." This is a consultation I often get from teams running serverless- or managed-DB-centric configurations.

The answer is "you can detect it without touching infrastructure at all, without changing code, and without installing an agent." That's GuardDuty's two protection plans, RDS Protection and Lambda Protection. What they have in common is being completely agentless and requiring no additional infrastructure. RDS Protection reads Aurora/RDS login activity directly from the AWS side, and Lambda Protection analyzes the network activity of all Lambda functions. You don't need to plant audit extensions in the DB, nor stretch VPC Flow Logs over the Lambda ENIs.

This article is an implementation guide for designing and operating these two "zero infrastructure change, agentless" protection plans at production quality. I leave GuardDuty's overall design (foundational detection, org governance, EventBridge automated response) to the pillar article, and concentrate here on digging one level deeper into the pillar's two lines, "RDS Protection" and "Lambda Protection" — into supported engines, finding types, the learning period, cost, and automated response. I implemented IAM, observability, and DR across a serverless payment platform on multi-account AWS and have operated Lambda in production — from that perspective, I'll talk about "how to design and implement these two layers on your DBs and functions."

The rule of this article: The specs, supported engines, finding types / severities, and pricing model are based on the AWS official documentation (as of June 2026). Because the official compatibility table for supported database engine versions is updated from time to time, the body doesn't assert specific versions but shows the verification procedure — always check the latest official table before going to production. And another premise — GuardDuty is "detection," not "defense." Neither RDS Protection nor Lambda Protection replaces least-privilege IAM, IAM database authentication, encryption, network isolation, or input validation. These play the "layer that quickly signals indications of compromise" within defense-in-depth. And "agentless" does not mean "free" — both are usage-based billing (pricing in Section 7).


0. Mental model: two "zero infrastructure change" detection layers

Before starting the design, let me pin down, one line each, what these two plans are and are not.

RDS Protection = a protection plan that ingests Aurora/RDS "login activity (success, failure)" directly and agentlessly from the AWS side and detects anomalous logins with ML.

Lambda Protection = a protection plan that analyzes "the network activity logs of all Lambda functions in the account (including functions that don't use VPC)" and detects communication indicating a function's compromise, such as communication with malicious hosts or crypto mining.

From here, three common consequences that bundle these two emerge. These are the foundation of the design decisions.

  1. Both are agentless and require no infrastructure change. RDS Protection is one where "GuardDuty monitors login activity directly from the Aurora/RDS service," and you don't need to add audit-log extensions to the DB or worry about performance tuning (official: "RDS Protection doesn't require additional infrastructure; it is designed so as not to affect the performance of your database instances."). Lambda Protection is the same — you don't need to enable VPC Flow Logs yourself for the ENIs of VPC-configured functions. You also don't need to distribute agents to workloads like Runtime Monitoring.
  2. For both, "the log itself doesn't come to you." GuardDuty analyzes login activity / network activity internally and does not make it available in your account (RDS: "GuardDuty doesn't ... make RDS login activity available to you." / Lambda: "GuardDuty doesn't manage your Lambda network activity logs ..., or make them accessible in your account."). "Storing and billing for logs for detection" is GuardDuty's own responsibility, independent of your log configuration.
  3. A finding is a "starting point," not an "endpoint." Both generate structured findings, and they only get into operation once you receive them with EventBridge and connect them to automated response. The climax of this article is Section 6 (EventBridge → idempotent automated response).

Grasp these three points, and you'll see that introducing the two plans is a design of "① is there a supported asset (the enablement decision) → ② how do you read the finding types → ③ how do you turn them into action (automated response)." Let's build them in order.


1. RDS Protection: what it monitors, and how

1.1 What it monitors is only "login activity" — not query content

First, let me clear up a misconception. What RDS Protection looks at is login activity to the DB (success/failure authentication events), not the content of SQL queries or table reads/writes. The official definition is — "RDS Protection in Amazon GuardDuty analyzes and profiles RDS login activity for potential access threats."

That is, the aim is anomaly detection of "who tried to log in, from where, and how." Concretely:

  • An unknown external actor succeeded in logging in for the first time (a user / IP never seen before).
  • An attempt at password brute-forcing.
  • Login attempts from known malicious IPs or Tor exit nodes.

RDS login activity captures both successful and failed login attempts ("RDS login activity captures both successful and failed login attempts."). GuardDuty's ML model learns factors like "the requesting user, the location of the connection source, the connection details" and detects behavior that deviates from the baseline as anomalous.

1.2 Supported engines (verify versions against the official compatibility table)

The engines RDS Protection supports are, per the official compatibility table, as follows.

Supported engineNote
Aurora MySQL-Compatible EditionAurora's MySQL-compatible
Aurora PostgreSQL-Compatible EditionAurora's PostgreSQL-compatible
RDS for PostgreSQLFor read replicas, the condition is "the primary is a supported version"
RDS for MySQL
RDS for MariaDB
Aurora PostgreSQL Limitless DatabaseAccounts with RDS Protection already enabled are added to monitoring automatically

I don't assert versions (per the official docs): the above is a list of engine types. The supported versions of each engine are managed in the official documentation's "Supported Amazon Aurora, Amazon RDS, and Aurora Limitless databases" table and are updated from time to time. Listing individual versions in this article would make it stale, so always check the official compatibility table before going to production. Note that an RDS for PostgreSQL read replica presupposes that the primary is a supported version and is replicating normally from the primary.

A note on Limitless Database (a billing pitfall): in accounts that already have RDS Protection enabled, Aurora PostgreSQL Limitless Database is added to monitoring automatically. If RDS Protection's 30-day free trial has already ended, usage charges for the Limitless Database monitoring are incurred immediately. To avoid "the billing target grew before I knew it," check the billing impact when you adopt Limitless.

1.3 The "up to two weeks" learning period — this is an operational pitfall

This is the fact you must always factor into the design, specific to RDS Protection. When you enable RDS Protection for the first time, or for a newly created DB instance, there is a period of learning the baseline of normal behavior. The official original text:

When you enable RDS Protection for the first time or you have a newly created database instance, there is a learning period to baseline normal behavior. For this reason, newly enabled or newly created database instances may not have an associated anomalous login finding for up to two weeks.

That is — immediately after enablement or immediately after creating a new instance, no anomalous-login finding may appear for up to two weeks. This is not a defect but a spec. It's because the ML needs time to build the baseline of "it's normal for this user to enter from this IP with these connection details."

Two design implications follow from here.

  • "I enabled it but no finding comes = safe" is not necessarily true. It may be in the learning period. You should record "the date you enabled it" and "the date it stabilizes as a monitoring target (about two weeks later)" in your operational documentation.
  • The MaliciousIPCaller / TorIPCaller families don't wait for learning. These fire not from ML anomaly detection (the AnomalousBehavior family) but from matching against known malicious-IP / Tor-exit-node lists, so they don't depend on baseline learning. That is, even right after enablement, "a login from a malicious IP" can be detected. It's accurate to understand the distinction that the learning period applies to AnomalousBehavior.* (ML-based).

1.4 Information contained in a finding

An RDS Protection finding contains details of the potentially compromised DB. The Resource Type is RDSDBInstance (ordinary Aurora/RDS) or RDSLimitlessDB (Limitless Database). An ML-based finding carries "RDS login activity-based anomalies" that deviate from the baseline — which user logged in, from where, with what connection details — so in triage you can read "whose access was anomalous." This becomes the material for deciding, in automated response, "whose DB user's credentials should be rotated."


2. RDS Protection finding types: deciphering the 9

RDS Protection has 9 finding types. The type-name structure is GuardDuty's common ThreatPurpose:ResourceType/Family.Mechanism, and the excellent point is that just by looking at the type name, you understand "what happened" (for the details of the type format, see Section 5 of the pillar article).

Finding typeDefault severityWhat it detects
CredentialAccess:RDS/AnomalousBehavior.SuccessfulLoginVariable (Low/Medium/High)A successful login the ML judged "anomalous" (a first login from an unknown user / IP, etc.)
CredentialAccess:RDS/AnomalousBehavior.FailedLoginLowA failed login the ML judged "anomalous" (may suggest a brute-force attempt)
CredentialAccess:RDS/AnomalousBehavior.SuccessfulBruteForceHighA success after a series of anomalous failed logins — suspected successful brute force
CredentialAccess:RDS/MaliciousIPCaller.SuccessfulLoginHighA successful login from a known malicious IP
CredentialAccess:RDS/MaliciousIPCaller.FailedLoginMediumA failed login from a known malicious IP
Discovery:RDS/MaliciousIPCallerMediumA known malicious IP probed the DB (no login attempt = reconnaissance)
CredentialAccess:RDS/TorIPCaller.SuccessfulLoginHighA successful login from a Tor exit node (intent to conceal identity)
CredentialAccess:RDS/TorIPCaller.FailedLoginMediumA failed login from a Tor exit node
Discovery:RDS/TorIPCallerMediumA Tor exit node probed the DB (no login attempt)

Let me organize the key points of deciphering.

  • AnomalousBehavior.* is ML-based, MaliciousIPCaller.* / TorIPCaller.* are threat-intelligence-based. The former depends on the up-to-two-week learning mentioned in 1.3, while the latter is matching against known lists, so no learning is needed — this distinction decides which detections work right after enablement.
  • Severity differs between SuccessfulLogin (success), FailedLogin (failure), and Discovery (probe only). A successful login (especially from a malicious IP / Tor / successful brute force) is High, and failures and probes are Medium / Low. The automated-response threshold can be cut by this number (Section 6).
  • Only AnomalousBehavior.SuccessfulLogin is Variable. Even for the same type, if the login source is a private IP it's Low, a public IP Medium, and accompanied by a pattern of consecutive failures High — the severity changes with context. "Don't treat it uniformly by type name alone; judge by the severity number" is the correct design.

The standard response (based on the official remediation): when a successful-login type (SuccessfulLogin / SuccessfulBruteForce) appears — ① rotate the password of the relevant DB user, and ② check that user's operations in the audit log. When a failure/probe type appears, ③ place the DB in a private VPC and narrow the security group to only the necessary sources (rectify a public state / overly lax access policy). As a root countermeasure, the royal road is to prioritize IAM database authentication over password authentication and reduce the long-lived passwords that can be leaked in the first place.


3. Lambda Protection: looking at the network activity of all functions

3.1 Monitoring "including functions that don't use VPC"

The core of Lambda Protection is the breadth of its monitoring scope. The official definition:

When you enable Lambda Protection, GuardDuty starts monitoring Lambda network activity logs. This includes VPC Flow Logs from all Lambda functions for your account (including those logs that don't use VPC networking) and logs that get generated when Lambda function gets invoked.

What's decisive here is "all Lambda functions ... (including those logs that don't use VPC networking)"it monitors the network activity of all Lambda functions in the account, including functions that don't use VPC networking. The intuition "aren't Lambdas not placed in a VPC out of scope?" is wrong — GuardDuty looks at network activity regardless of a function's VPC configuration.

And what's important from the operational-load standpoint — you don't need to enable VPC Flow Logs yourself for the ENIs (the Elastic Network Interfaces Lambda creates) of VPC-configured functions:

For Lambda functions that are configured to use VPC networking, you don't need to enable VPC flow logs for the elastic network interfaces (ENI) created by Lambda for GuardDuty.

That is, the work of "building a flow-log platform yourself" is zero. GuardDuty internally ingests the necessary network activity logs.

3.2 What it detects — the communication of "compromised function code"

What Lambda Protection looks for is "suspicious network communication that indicates malicious code is lurking inside the function." Official: "When GuardDuty identifies suspicious network traffic that is indicative of the presence of a potentially malicious piece of code in your Lambda function ..."

The concrete scenario is this — a dependency package tainted by a supply-chain attack, function code rewritten via leaked credentials, and the like begin communicating with external malicious hosts. Lambda Protection captures that communication:

  • Crypto mining: an attacker hijacks the function and repurposes it for mining Bitcoin, etc. (CryptoCurrency:Lambda/BitcoinTool.B).
  • Communication with a C&C (command-and-control) server: receiving commands from outside as a member of a botnet (Backdoor:Lambda/C&CActivity.B).
  • Communication with malicious hosts / Tor: a destination for stolen credentials (DropPoint), a blackhole, a Tor relay / client, and so on.

3.3 Lambda@Edge is out of scope (an important exclusion)

There's an exclusion you must always grasp at design time — the logs of Lambda@Edge functions are not included in Lambda Protection's scope. Official: "Lambda Network Activity Monitoring doesn't include the logs for Lambda@Edge functions."

If you use Lambda@Edge running at CloudFront's edge, recognize that it is not covered by Lambda Protection. You need to think about edge protection at a different layer like WAF.

3.4 Information contained in a finding

A Lambda Protection finding's Resource Type is Lambda. The finding contains the name of the Lambda function that was communicating and the destination IP (plus threat-list details). This becomes the material for deciding, in automated response, "which function to isolate/disable" and "which dependency to suspect."


4. Lambda Protection finding types: deciphering the 7

Lambda Protection has 7 finding types. All have resourceType Lambda, and being able to identify them at a glance by the resource segment being Lambda.

Finding typeDefault severityWhat it detects
Backdoor:Lambda/C&CActivity.BHighThe function is querying the IP of a known C&C server (suspected member of a botnet)
CryptoCurrency:Lambda/BitcoinTool.BHighThe function is querying a crypto-related IP (suspected repurposing for mining)
Trojan:Lambda/BlackholeTrafficMediumThe function is trying to communicate with a blackhole (sinkhole) IP
Trojan:Lambda/DropPointMediumThe function is trying to communicate with a host where malware aggregates stolen credentials and the like
UnauthorizedAccess:Lambda/MaliciousIPCaller.CustomMediumThe function is communicating with an IP on a threat list you uploaded
UnauthorizedAccess:Lambda/TorClientHighThe function connected to a Tor Guard / Authority node (suspected turning into a Tor client)
UnauthorizedAccess:Lambda/TorRelayHighThe function is behaving as a Tor relay

Key points of deciphering:

  • The meaning of .Custom: the .Custom in UnauthorizedAccess:Lambda/MaliciousIPCaller.Custom indicates a detection mechanism of "a match against a threat list you uploaded yourself" (GuardDuty's threat-list feature; for details, see Section 7 of the pillar article). Register IPs you judged "this is malicious" in-house, and you can detect the moment a Lambda communicates with them.
  • Bipolarization of severity: C&C, crypto, and Tor families are High, and blackhole, DropPoint, and custom-list families are Medium. High is a signal you should act on largely on the premise that "the function is compromised."
  • BitcoinTool.B may have a legitimate exception: if the function is legitimately doing blockchain-related processing (mining, crypto-asset management), this becomes a false positive. In that case, the official docs recommend creating a Suppression Rule with the two conditions "finding type = CryptoCurrency:Lambda/BitcoinTool.B + the target function name."

The standard response (based on the official remediation): if a Lambda finding is unexpected, the official docs recommend "responding on the assumption that the function has been compromised" ("assume that Lambda has been potentially compromised"). Concretely — ① temporarily disable the function / narrow concurrency to 0 to stop communication, ② review the function code and dependency packages (suspect supply-chain taint), ③ rotate, on the premise of leakage, the credentials and environment-variable secrets the function held. This is consistent with the "least-privilege execution role × minimization of secrets" I emphasize in Lambda production operation — a design that reduces from the start what can be stolen in a compromise.


5. Decision table: when, and which, to enable

Let me land all of the above into a practical decision. Since both are "zero code change, zero infrastructure change, agentless," the decision axis is simply "is there a supported asset."

Your situationShould you enablePlanReason
Using a supported-engine Aurora/RDS (PostgreSQL/MySQL/MariaDB) in productionEnableRDS ProtectionDetect login anomalies directly from the AWS side. No impact on DB performance
Using Aurora PostgreSQL Limitless DatabaseEnable (mind billing)RDS ProtectionAdded to monitoring automatically. Billed immediately if the trial has ended
You have Lambda functions that communicate outbound to the internet / external APIsEnableLambda ProtectionDetect C&C, mining, malicious-host communication
You have Lambda functions handling sensitive data / credentialsEnableLambda ProtectionHigh impact in a compromise. Captures repurposing / exfiltration communication
You have Lambdas with many third-party dependencies (npm/pip, etc.)Enablement recommendedLambda ProtectionDetect suspicious communication after supply-chain taint
You use only Lambda@EdgeOut of scopeLambda@Edge is outside Lambda Protection's monitoring
You don't use any DB (DynamoDB only, etc.)RDS is unnecessaryNo monitoring target (YAGNI). Protect DynamoDB with IAM, etc.
You don't use Lambda at allLambda is unnecessaryNo monitoring target (YAGNI)

The crux of the design is "add according to your assets (YAGNI)." For a DynamoDB-based serverless configuration (my payment platform was this shape), even if you enable RDS Protection, the detection targets are zero — only cost increases. On the other hand, for a configuration where outbound-communicating Lambdas line up, Lambda Protection is a cost-effective choice that adds a layer of compromise detection with zero code change.

An honest premise (no exaggeration): my actual case study is a DynamoDB-based serverless payment platform, and it doesn't put RDS at the production core. So I won't say I "ran RDS Protection on a real project" here. What I can speak to is — based on the experience of implementing IAM, observability, and DR across multi-account AWS and operating Lambda in production, I can accompany you on how to design and implement these two layers on your Aurora/RDS and Lambda, based on the official specs and general RDS/Aurora knowledge.


6. Enable with Terraform, turn into action with EventBridge

6.1 Enablement (surprisingly short)

Both plans are added with an aws_guardduty_detector_feature resource, separate from the aws_guardduty_detector body. The feature names are RDS Protection = RDS_LOGIN_EVENTS and Lambda Protection = LAMBDA_NETWORK_LOGS.

# detector 本体は別途定義済みとする(ピラー記事の 2 章を参照)。
# 基盤検知 + Extended Threat Detection は detector 有効化の時点で動いている。

# ── RDS Protection ──
# 対応する Aurora/RDS を使っている時だけ有効化する(YAGNI)。
# エージェントレス・インフラ変更ゼロ。有効化直後〜新規インスタンスは
# 最大2週間の学習期間があり、その間 AnomalousBehavior 系 finding は出ない。
resource "aws_guardduty_detector_feature" "rds_login_events" {
  detector_id = aws_guardduty_detector.this.id
  name        = "RDS_LOGIN_EVENTS"
  status      = "ENABLED"
}

# ── Lambda Protection ──
# 外向き通信する Lambda がある時に有効化する。
# 全関数(非VPC含む)のネットワーク活動を解析。Lambda@Edge は対象外。
# ENI 用の VPC Flow Logs を自分で張る必要はない。
resource "aws_guardduty_detector_feature" "lambda_network_logs" {
  detector_id = aws_guardduty_detector.this.id
  name        = "LAMBDA_NETWORK_LOGS"
  status      = "ENABLED"
}

If you enable it org-wide in one go, you can roll it out from the delegated administrator account with the same feature names (RDS_LOGIN_EVENTS / LAMBDA_NETWORK_LOGS), in the same structure as the pillar article's aws_guardduty_organization_configuration_feature.

# ── 委任管理者アカウントで実行:組織全体に自動展開 ──
# 「対応資産を持つアカウントが多い」場合は組織一括が漏れを防ぐ。
# ただし対応資産ゼロのアカウントにも課金は基本発生しない(監視対象がないため)。
resource "aws_guardduty_organization_configuration_feature" "rds_org" {
  detector_id = aws_guardduty_detector.security.id
  name        = "RDS_LOGIN_EVENTS"
  auto_enable = "ALL" # 既存 + 新規すべて。取りこぼしを構造的に消す
}

resource "aws_guardduty_organization_configuration_feature" "lambda_org" {
  detector_id = aws_guardduty_detector.security.id
  name        = "LAMBDA_NETWORK_LOGS"
  auto_enable = "ALL"
}

Why choose auto_enable = "ALL": with NEW (only accounts that join going forward), accounts that already exist at config time can be left out of scope forever — this is a silent hole against the goal of "detecting org-wide with no gaps." Since RDS/Lambda is unlikely to incur detection or billing in accounts without a monitoring target (a DB / outbound Lambda), the design of enabling broadly with ALL, where only places with assets actually run falls on the safe side.

6.2 EventBridge routing: dispatch by type and severity

The foundation for turning detection into action is EventBridge (the detailed automated-response design is in Section 6 of the pillar article). RDS/Lambda findings, like other GuardDuty findings, flow to EventBridge in near real time. The standard is to dispatch by the type prefix and the severity number.

{
  "source": ["aws.guardduty"],
  "detail-type": ["GuardDuty Finding"],
  "detail": {
    "type": [
      { "prefix": "CredentialAccess:RDS/" },
      { "prefix": "Discovery:RDS/" },
      { "prefix": "Backdoor:Lambda/" },
      { "prefix": "CryptoCurrency:Lambda/" },
      { "prefix": "Trojan:Lambda/" },
      { "prefix": "UnauthorizedAccess:Lambda/" }
    ],
    "severity": [{ "numeric": [">=", 7] }]
  }
}

The prefix match on type picks up "RDS-origin / Lambda-origin" together, and severity >= 7 (High and above) narrows to containment candidates. With this, types prone to noise like AnomalousBehavior.FailedLogin (Low) fall out of containment, and only SuccessfulBruteForce / MaliciousIPCaller.SuccessfulLogin / BitcoinTool.B (all High) get onto the response pipeline.

6.3 An idempotent automated-response Lambda (Python) — the response differs for RDS and Lambda

This is the climax of this article. For RDS findings and Lambda findings, the action to take is fundamentally different:

  • RDS compromise → credential rotation, rectifying the DB's network exposure (an area where human review should be interposed).
  • Lambda compromise → stop the function's communication (narrow concurrency to 0), review the code, dependencies, and secrets.

And an iron rule common to both — since EventBridge is at-least-once delivery, a Lambda can be invoked twice for the same finding. With the same thinking as preventing double charges on a payment platform, the automated response too must be idempotent.

"""GuardDuty の RDS/Lambda Protection finding に応答する自動対応 Lambda。

設計原則:
  - 冪等: EventBridge は at-least-once。同じ finding を2回受けても副作用は1回分。
  - スコープを絞る: 自動アクションは「取り消せる・スコープが狭い」ものに限定。
  - RDS は通知中心: DB の認証情報ローテーションは破壊的なので人間のレビューを挟む。
  - Lambda は封じ込め可: 同時実行数を 0 に絞るのは取り消し可能(後で戻せる)。
  - 入力はサニタイズ: finding フィールド(関数名・ユーザー名等)は信頼しない外部入力として扱う。
"""
from __future__ import annotations

import json
import logging
import os
from typing import Any, Final

import boto3

logger = logging.getLogger()
logger.setLevel(logging.INFO)

lambda_client = boto3.client("lambda")
sns = boto3.client("sns")

ALERT_TOPIC_ARN: Final[str] = os.environ["ALERT_TOPIC_ARN"]
# 破壊的操作を避け、まず観察したい段階では DRY_RUN=true で封じ込めを抑止。
DRY_RUN: Final[bool] = os.environ.get("DRY_RUN", "false").lower() == "true"

# Lambda 関数を自動で封じ込める(同時実行=0)対象の finding 型。
# High 確度の「侵害ほぼ確実」な型に限定する。
LAMBDA_CONTAIN_ALLOWLIST: Final[frozenset[str]] = frozenset(
    {
        "Backdoor:Lambda/C&CActivity.B",
        "CryptoCurrency:Lambda/BitcoinTool.B",
        "UnauthorizedAccess:Lambda/TorClient",
        "UnauthorizedAccess:Lambda/TorRelay",
    }
)


def handler(event: dict[str, Any], _context: object) -> dict[str, str]:
    detail = event["detail"]
    finding_type: str = detail["type"]
    severity: float = float(detail["severity"])
    finding_id: str = detail["id"]

    if finding_type.endswith("/") or "/" not in finding_type:
        # 想定外フォーマットは通知だけして安全側に倒す。
        action = _route_unknown(finding_type, severity, finding_id)
    elif "Lambda/" in finding_type:
        action = _handle_lambda(detail, finding_type, severity, finding_id)
    elif "RDS/" in finding_type:
        action = _handle_rds(detail, finding_type, severity, finding_id)
    else:
        action = _route_unknown(finding_type, severity, finding_id)

    logger.info(
        json.dumps({"finding_id": finding_id, "type": finding_type, "action": action})
    )
    return {"action": action}


def _handle_lambda(
    detail: dict[str, Any], finding_type: str, severity: float, finding_id: str
) -> str:
    """Lambda の侵害: 高確度なら同時実行を 0 に絞って通信を止める(取り消し可能)。"""
    # finding から関数名を取り出す(構造はリソース種別に依存。存在チェックを厳格に)。
    resource = detail.get("resource", {})
    func_name = resource.get("lambdaDetails", {}).get("functionName")

    should_contain = (
        finding_type in LAMBDA_CONTAIN_ALLOWLIST
        and severity >= 7.0
        and bool(func_name)
    )

    if should_contain and not DRY_RUN:
        action = _throttle_function(func_name)  # 冪等
    else:
        action = "dry-run" if (should_contain and DRY_RUN) else "notify-only"

    _notify("Lambda", finding_type, severity, finding_id, func_name, action)
    return action


def _throttle_function(func_name: str) -> str:
    """関数の予約済み同時実行数を 0 にして即時に通信を止める。
    冪等: 既に 0 ならそのまま(2回目の起動は no-op)。後で削除すれば原状復帰。"""
    current = lambda_client.get_function_concurrency(FunctionName=func_name)
    if current.get("ReservedConcurrentExecutions") == 0:
        return "already-contained"
    lambda_client.put_function_concurrency(
        FunctionName=func_name, ReservedConcurrentExecutions=0
    )
    return "contained-throttled-to-zero"


def _handle_rds(
    detail: dict[str, Any], finding_type: str, severity: float, finding_id: str
) -> str:
    """RDS の侵害: 認証情報ローテーション/SG 変更は破壊的。
    自動では『チケット化 + 人間のレビュー依頼』にとどめ、手は出さない。"""
    resource = detail.get("resource", {})
    db = resource.get("rdsDbInstanceDetails", {})
    db_id = db.get("dbInstanceIdentifier")
    # 異常ログインの DB ユーザー(誰の認証情報を疑うか)を通知に含める。
    db_user = (
        resource.get("rdsDbUserDetails", {}).get("user")
        if "rdsDbUserDetails" in resource
        else None
    )
    _notify(
        "RDS", finding_type, severity, finding_id, f"{db_id} (user={db_user})", "review-required"
    )
    # 破壊的操作(パスワードローテーション等)は人間の承認フローへ。ここでは実行しない。
    return "review-required"


def _route_unknown(finding_type: str, severity: float, finding_id: str) -> str:
    _notify("Other", finding_type, severity, finding_id, None, "notify-only")
    return "notify-only"


def _notify(
    kind: str,
    finding_type: str,
    severity: float,
    finding_id: str,
    resource_ref: str | None,
    action: str,
) -> None:
    region = os.environ.get("AWS_REGION", "ap-northeast-1")
    console = (
        f"https://{region}.console.aws.amazon.com/guardduty/home"
        f"?region={region}#/findings?fId={finding_id}"
    )
    sns.publish(
        TopicArn=ALERT_TOPIC_ARN,
        Subject=f"[GuardDuty][{kind}][{severity}] {finding_type}",
        Message="\n".join(
            [
                f"type: {finding_type}",
                f"severity: {severity}",
                # resource_ref は finding 由来=信頼しない入力。Web 表示時は
                # 受け手側で HTML エスケープする(ここでは生のまま SNS へ)。
                f"resource: {resource_ref or 'n/a'}",
                f"action: {action}",
                f"console: {console}",
            ]
        ),
    )

Let me make explicit the design decisions of this code.

  • Branching the response for RDS and Lambda: for Lambda, you can automate a reversible containment of "stop communication with concurrency 0," but RDS's "credential rotation / SG change" is destructive (it can lock out legitimate users), so automatically it stays at ticketing and a human review request. This is an implementation of the iron rule "interpose a human for destructive operations."
  • Idempotent: Lambda containment is guarded with "if concurrency is already 0, no-op." Even if invoked twice with at-least-once delivery, the side effect is one time's worth.
  • Reversible: put_function_concurrency(0) returns to the original state if you delete the reserved concurrency. It does not do irreversible operations like deleting the function or rewriting code.
  • Treat input on the premise of sanitization: the function name and DB user name contained in a finding are external input an attacker can influence. The side that forwards it to a web dashboard or Slack must always run sanitization like HTML escaping — as also touched on in the Runtime Monitoring article, finding fields are untrusted input.
  • Least privilege: this Lambda's execution role is limited to lambda:GetFunctionConcurrency / lambda:PutFunctionConcurrency (scoped to the containment-target function ARN) and sns:Publish. It is not granted destructive permissions on RDS (those go on the human-flow side).

The more full-fledged incident-response wiring (SOAR, ticketing integration, an approval flow via Step Functions) is dug into in the dedicated EventBridge automated-response article.


7. Cost: agentless doesn't mean "free"

"Agentless, zero infrastructure change" is appealing, but the fact that it's usage-based billing doesn't change. Let me pin down each plan's billing unit.

PlanHow billing works
RDS ProtectionUsage-based billing by the amount of RDS login activity monitored (by Region; verify on the official pricing page)
Lambda ProtectionUsage-based billing against the GB of Lambda network activity logs processed. GuardDuty optimizes cost by analyzing only the subset relevant to threat detection with smart filters

The important point the official docs state clearly about Lambda Protection: "GuardDuty only charges for the amount of Lambda network activity logs data processed (in GB) to generate a finding. GuardDuty optimizes cost by applying smart filters and analyzing a subset of Lambda network activity logs that are relevant to threat detection." — that is, it processes not the whole log in its entirety, but only the subset relevant to threat detection.

Beware future billing expansion (Lambda): the official docs state clearly that Lambda's network-activity monitoring may expand in the future to other activity like DNS query data, that in that case the volume of data processed = cost increases, and that they will give at least 30 days' notice at the time of expansion. Don't fix your view as "it's cheap now"; run operations that don't miss the expansion notice.

For both plans, enabling for the first time in a new Region comes with a 30-day free trial. During this, you can know the projected bill at production volume. I leave GuardDuty's overall cost optimization (including foundational and other plans) to the dedicated article, but the iron rule of these two plans is a repeat of Section 5 — enable only where there are assets (a supported DB / outbound Lambda). With no supported asset, zero detection = zero value paid for.

The trial end doesn't auto-stop: after 30 days pass, neither plan is disabled automatically. Usage charges keep being incurred as-is. Build "try it in the trial, and explicitly disable if unneeded" into your operations. Also note that RDS's Limitless Database auto-addition (1.2) is billed immediately if the trial has ended.


8. Summary: an RDS / Lambda Protection production cheat sheet

A quick-reference table for when you're unsure.

  • Common properties: both are detection layers you can add with agentless, zero infrastructure change, zero code change. GuardDuty ingests logs directly from the AWS side, and the log itself doesn't come to you (you don't build a log platform yourself). But it's usage-based billing, not "free."
  • RDS Protection (monitoring target = login activity): supports Aurora (MySQL/PostgreSQL-compatible), RDS for PostgreSQL/MySQL/MariaDB, and Aurora PostgreSQL Limitless. The engine types are settled; verify versions against the official compatibility table. Captures both successful and failed logins. New enablement / new instances have an up-to-two-week learning period, during which AnomalousBehavior.* (ML family) findings don't appear (the MaliciousIPCaller / TorIPCaller families need no learning).
  • RDS finding types (9): AnomalousBehavior.{SuccessfulLogin(Variable)/FailedLogin(Low)/SuccessfulBruteForce(High)}, MaliciousIPCaller.{SuccessfulLogin(High)/FailedLogin(Medium)}, Discovery:RDS/MaliciousIPCaller(Medium), TorIPCaller.{SuccessfulLogin(High)/FailedLogin(Medium)}, Discovery:RDS/TorIPCaller(Medium). Resource Type is RDSDBInstance / RDSLimitlessDB.
  • RDS response: for successful-login types, credential rotation + audit-log check. For failure/probe types, private VPC + SG restriction. The root countermeasure is to reduce passwords with IAM database authentication.
  • Lambda Protection (monitoring target = network activity): analyzes the network activity of all Lambda functions (including functions that don't use VPC). You don't need to stretch VPC Flow Logs yourself for the ENIs. Lambda@Edge is out of scope. Resource Type is Lambda.
  • Lambda finding types (7): Backdoor:Lambda/C&CActivity.B(High), CryptoCurrency:Lambda/BitcoinTool.B(High), Trojan:Lambda/BlackholeTraffic(Medium), Trojan:Lambda/DropPoint(Medium), UnauthorizedAccess:Lambda/MaliciousIPCaller.Custom(Medium), UnauthorizedAccess:Lambda/TorClient(High), UnauthorizedAccess:Lambda/TorRelay(High).
  • Lambda response: if unexpected, on the premise that the function is compromisedcontain with concurrency 0 → review code and dependencies → rotate secrets. For legitimate blockchain processing, a suppression rule with the two conditions "type + function name."
  • Enablement (Terraform): aws_guardduty_detector_feature's RDS_LOGIN_EVENTS / LAMBDA_NETWORK_LOGS. Org-wide is aws_guardduty_organization_configuration_feature + auto_enable = "ALL".
  • Automated response: dispatch by EventBridge's type prefix + severity number. Lambda is a reversible containment with concurrency 0, RDS is destructive so interpose human review. At-least-once → idempotency required. Finding fields are untrusted input = sanitize.
  • Cost: RDS by login-activity volume, Lambda by GB processed (a subset via smart filters). Know the bill in advance with the 30-day free trial. It doesn't auto-stop after the end. Lambda may expand to DNS, etc. in the future = possible billing increase (with 30-day notice).

RDS Protection and Lambda Protection are not "boxes that protect you if you enable them," but detection layers whose value comes out by "① discerning supported assets and adding them (YAGNI), ② reading finding types correctly, and ③ connecting EventBridge → idempotent, scope-narrowed automated response." The greatest appeal is — being able to add a layer of DB-login-anomaly and serverless-network-threat detection without changing code or infrastructure at all. The greatest leverage is in the plumbing that turns findings into safe automated response, more than the detection itself.

On a multi-account serverless payment platform, I implemented IAM, observability, and DR across it and ensured "correctness" with the structure of code and idempotency, not operational vigilance. I design the introduction of RDS/Lambda Protection with the same philosophy — ① enable at minimal cost according to supported assets, ② branch the response per finding type (RDS by human review, Lambda by reversible containment), and ③ shrink MTTR with EventBridge → idempotent automated response. I don't end detection at a "red dashboard" — I build all the way to a response mechanism that gets into operation.

"How do I design RDS Protection and Lambda Protection on my Aurora/RDS and Lambda, how far do I entrust automated response, and how do I hold cost down?" — from inventorying supported assets through Terraform implementation, EventBridge automated response, org governance, and false-positive tuning, I can accompany you fast and safely, one person × generative AI (Claude Code). Even from the requirements-organizing stage, feel free to consult me.


Reference (official documentation)

  • GuardDuty RDS Protection — the definition of RDS Protection, the supported-engine table, login activity, the up-to-two-week learning period, the 30-day free trial, Limitless auto-addition
  • RDS Protection finding types — the 9 finding types, severities, remediation (Resource Type RDSDBInstance / RDSLimitlessDB)
  • GuardDuty Lambda Protection — network-activity monitoring of all functions (including non-VPC), no ENI flow logs needed, Lambda@Edge exclusion, smart-filter billing, the 30-day free trial
  • Lambda Protection finding types — the 7 finding types, severities, remediation (Resource Type Lambda)
  • Amazon GuardDuty pricing — RDS Protection / Lambda Protection usage-based billing, by Region, the 30-day free trial
友田

友田 陽大

Developer of a METI Minister's Award–winning product. With TypeScript + Python + AWS, I deliver SaaS, industry DX, and production-grade generative AI (RAG) end to end — from requirements to infrastructure and operations — single-handedly.

I can take on the implementation from this article as an engagement

Data protection & threat detection for S3 / RDS / Lambda

Automatic malware scanning and quarantine of uploaded files, anomalous DB logins, and serverless network threats. I select a protection plan to fit your assets and implement tag-based access control and automated response.

Available for both project-based (contract) and advisory engagements. Start with a free 30-minute consult.

Also worth reading