Guide · AWS GuardDuty · EKS Runtime Monitoring

GuardDuty EKS Runtime Monitoring for MCP Servers — Kubernetes Threat Detection

GuardDuty EKS protection has two independent layers: EKS Audit Log Monitoring (analyzes Kubernetes control-plane API calls — who created which pods, RBAC changes, exec commands) and EKS Runtime Monitoring (installs a DaemonSet security agent on each node to monitor process execution, network connections, and file system access inside containers at runtime). Audit log monitoring covers RBAC attacks, dashboard exposure, and Kubernetes API abuse — threats that happen at the control plane level. Runtime monitoring covers post-exploitation: what happens inside a container after an attacker gains entry. For MCP servers running on EKS, both layers are needed: audit logs catch the initial compromise vector (malicious kubectl exec, privileged container deployment), while runtime monitoring catches what the attacker does next (downloading malware, establishing reverse shell, lateral movement).

TL;DR

Enable both EKS Audit Log Monitoring and EKS Runtime Monitoring as separate GuardDuty features. Runtime monitoring deploys a DaemonSet (aws-guardduty-agent) in the amazon-guardduty namespace — use managed add-on mode so GuardDuty handles agent lifecycle. Audit log findings cover RBAC and API abuse; runtime findings cover in-container process/network behavior. Create suppression filters for kubectl exec performed by your CI/CD principal and container image scanning tools to avoid alert fatigue. See the main GuardDuty guide for detector setup and the remediation guide for automated pod isolation patterns.

Enable EKS audit log monitoring

EKS Audit Log Monitoring enables GuardDuty to analyze the EKS control-plane audit log stream. EKS must have control-plane logging enabled — specifically the audit log type — for GuardDuty to receive the audit events. GuardDuty uses its own internal log delivery path (not your CloudWatch Logs delivery), so you do not need to route EKS audit logs to CloudWatch for GuardDuty to work, but if you want both, enable both independently.

# Enable EKS Audit Log Monitoring in GuardDuty
aws guardduty update-detector \
  --detector-id abc1234567890abcdef1234567890 \
  --features '[
    {
      "Name": "EKS_AUDIT_LOGS",
      "Status": "ENABLED"
    }
  ]'

# Verify EKS audit logs are enabled on the EKS cluster itself
# (GuardDuty uses its own delivery, but good practice to also enable CloudWatch delivery)
aws eks update-cluster-config \
  --name mcp-production-cluster \
  --logging '{
    "clusterLogging": [{
      "types": ["api", "audit", "authenticator", "controllerManager", "scheduler"],
      "enabled": true
    }]
  }'

# Check GuardDuty feature status
aws guardduty get-detector \
  --detector-id abc1234567890abcdef1234567890 \
  --query 'Features[?Name==`EKS_AUDIT_LOGS`]'

EKS Audit Log Monitoring is priced per EKS vCPU-hour across all clusters in the account — not per finding or per event volume. Cost scales with the number of worker nodes running, not with cluster activity level.

Enable EKS runtime monitoring

EKS Runtime Monitoring adds the in-container agent layer. It deploys the aws-guardduty-agent DaemonSet in the amazon-guardduty namespace on every node in selected (or all) clusters. You can deploy via managed add-on (GuardDuty manages the agent lifecycle, updates, and version compatibility) or self-managed DaemonSet (you manage the manifest). Managed add-on is strongly preferred for production MCP clusters.

# Enable EKS Runtime Monitoring in GuardDuty
aws guardduty update-detector \
  --detector-id abc1234567890abcdef1234567890 \
  --features '[
    {
      "Name": "RUNTIME_MONITORING",
      "Status": "ENABLED",
      "AdditionalConfiguration": [
        {
          "Name": "EKS_ADDON_MANAGEMENT",
          "Status": "ENABLED"
        }
      ]
    }
  ]'

# EKS_ADDON_MANAGEMENT = ENABLED means GuardDuty auto-deploys and manages
# the aws-guardduty-agent add-on on all opted-in clusters

# Opt specific clusters into runtime monitoring (SELECTIVE mode)
# By default all clusters are monitored; use selective if you have dev clusters
# to exclude from runtime monitoring cost
aws guardduty update-detector \
  --detector-id abc1234567890abcdef1234567890 \
  --features '[
    {
      "Name": "RUNTIME_MONITORING",
      "Status": "ENABLED",
      "AdditionalConfiguration": [
        {
          "Name": "EKS_ADDON_MANAGEMENT",
          "Status": "ENABLED"
        }
      ]
    }
  ]'

# After enabling, GuardDuty creates the add-on on opted-in clusters
# Verify the add-on is running:
aws eks list-addons --cluster-name mcp-production-cluster \
  --query 'addons[?contains(@, `aws-guardduty`)]'

# Check DaemonSet status on the cluster (kubectl)
# kubectl get daemonset aws-guardduty-agent \
#   --namespace amazon-guardduty \
#   -o wide

# Verify all nodes have the agent pod running:
# kubectl get pods --namespace amazon-guardduty \
#   --field-selector spec.nodeName=

If the GuardDuty agent DaemonSet is not running on a node, runtime findings are not generated for containers on that node — there is no error or warning, just silent missing coverage. After enabling runtime monitoring, always verify the DaemonSet pod count matches the number of nodes in your cluster. On Fargate nodes, runtime monitoring is not supported (Fargate does not support DaemonSets); Fargate-based MCP servers are protected only by EKS Audit Log Monitoring.

EKS Audit Log finding types

Audit log findings analyze the Kubernetes control-plane event stream. These findings represent RBAC changes, unauthorized API access, and suspicious pod configurations detected at schedule/deploy time — before any container code runs.

Finding typeWhat triggered itMCP server implication
Policy:Kubernetes/AdminAccessToDefaultServiceAccountClusterRoleBinding grants cluster-admin to default service account in any namespaceAny pod in that namespace can make arbitrary Kubernetes API calls — escape path for compromised MCP container
Policy:Kubernetes/AnonymousAccessGrantedRBAC binding grants permissions to system:anonymous or system:unauthenticatedUnauthenticated access to Kubernetes API — check if your MCP server's service account needs RBAC tightening
Policy:Kubernetes/KubeflowDashboardExposedKubeflow or other dashboard service exposed via LoadBalancer or NodePort without authNot directly MCP-related but indicates a misconfigured cluster that may expose other services
Persistence:Kubernetes/ContainerWithSensitiveMountContainer deployed with hostPath mount to sensitive paths (/etc/passwd, /etc/shadow, /proc)Container can read/write host credentials — check if MCP server deployment uses any hostPath mounts
PrivilegeEscalation:Kubernetes/PrivilegedContainerContainer deployed with securityContext.privileged: trueContainer has full root access to host — MCP server deployment should never need privileged mode
Execution:Kubernetes/ExecIntoContainerkubectl exec or API equivalent executed into a running containerLegitimate for debugging; suppress for your CI/CD principal; investigate if principal is unexpected
Discovery:Kubernetes/MaliciousIPCallerKubernetes API calls from a known-malicious IPSomeone with stolen credentials calling K8s API from compromised host — rotate kubeconfig and IRSA tokens
# Check finding details for an Execution:Kubernetes/ExecIntoContainer finding
# Key fields: who ran exec, into which pod, from where
aws guardduty get-findings \
  --detector-id abc1234567890abcdef1234567890 \
  --finding-ids "exec-finding-id" \
  --query 'Findings[0].{
    Type:Type,
    Severity:Severity,
    Principal:Resource.KubernetesDetails.KubernetesUserDetails,
    Target:Resource.KubernetesDetails.KubernetesWorkloadDetails,
    Action:Service.Action.KubernetesApiCallAction
  }'

# KubernetesUserDetails:
# {
#   "Username": "arn:aws:sts::123456789012:assumed-role/developer-role/session",
#   "Uid": "...",
#   "Groups": ["system:masters", "system:authenticated"]
# }
# If Username is your CI/CD role → suppress
# If Username is system:anonymous or unexpected → investigate immediately

EKS runtime finding types

Runtime findings require the DaemonSet agent and capture in-container behavior: process execution, library loading, network connections, and file system events inside running MCP server containers.

Finding typeWhat triggered itMCP server context
Execution:Kubernetes/NewBinaryExecutedInContainerA binary was executed inside a container that was not present in the original imageIndicates post-compromise binary download and execution; also fires for container startup scripts that download tools at runtime
Execution:Kubernetes/NewLibraryLoadedInContainerA shared library was loaded that was not present in the original imageIndicates dynamic library injection; common in memory-based attacks
PrivilegeEscalation:Kubernetes/ContainerMountsHostDirectoryAt runtime, a process inside the container accessed a host directory mount that includes sensitive pathsDifferent from the audit-log finding — this detects actual access of the mount, not just configuration
Backdoor:Kubernetes/C&CActivity.BContainer process making network connections to known C2 server IPs/domainsPost-compromise communication from within MCP server container — immediate isolation required
DefenseEvasion:Kubernetes/ProcessInjectedProcess injection detected inside a container (e.g., ptrace, /proc/mem write)Sophisticated in-container attack; attacker has code execution in MCP server process
Impact:Kubernetes/CryptoCurrencyMiningContainer process executing known cryptocurrency miner binaryContainer compromised and used for crypto mining; likely via code injection in MCP tool handler
# Get runtime finding details — note containerDetails in the resource
aws guardduty get-findings \
  --detector-id abc1234567890abcdef1234567890 \
  --finding-ids "runtime-finding-id" \
  --query 'Findings[0].Resource.KubernetesDetails.KubernetesWorkloadDetails'

# KubernetesWorkloadDetails:
# {
#   "Name": "mcp-server-deployment-abc123",
#   "Type": "Pod",
#   "Uid": "pod-uid",
#   "Namespace": "production",
#   "Containers": [{
#     "Name": "mcp-server",
#     "Image": "123456789012.dkr.ecr.us-east-1.amazonaws.com/mcp-server:v1.2.3",
#     "ContainerId": "containerd://abc...",
#     "SecurityContext": {
#       "Privileged": false,
#       "AllowPrivilegeEscalation": false
#     }
#   }]
# }

# Service.RuntimeDetails provides the process that triggered the finding:
aws guardduty get-findings \
  --detector-id abc1234567890abcdef1234567890 \
  --finding-ids "runtime-finding-id" \
  --query 'Findings[0].Service.RuntimeDetails'

# RuntimeDetails:
# {
#   "Process": {
#     "Name": "curl",
#     "ExecutablePath": "/tmp/curl",  ← /tmp is suspicious for a compiled binary
#     "ExecutableSha256": "abc...",
#     "Pid": 12345,
#     "Uuid": "process-uuid",
#     "ParentUuid": "parent-uuid",
#     "User": "root",
#     "UserUid": "0"
#   },
#   "Context": {
#     "ModifyingProcess": {...}
#   }
# }

Suppression patterns for EKS workloads

EKS environments generate several legitimate activities that GuardDuty flags as suspicious. Create suppression filters before your first deployment cycle to prevent alert fatigue.

# Suppress Execution:Kubernetes/ExecIntoContainer for CI/CD principal
# (e.g., Jenkins, GitHub Actions runner IAM role running kubectl exec for health checks)
aws guardduty create-filter \
  --detector-id abc1234567890abcdef1234567890 \
  --name "cicd-kubectl-exec-suppression" \
  --action ARCHIVE \
  --rank 1 \
  --finding-criteria '{
    "Criterion": {
      "type": {
        "Equals": ["Execution:Kubernetes/ExecIntoContainer"]
      },
      "resource.kubernetesDetails.kubernetesUserDetails.username": {
        "Prefix": ["arn:aws:sts::123456789012:assumed-role/github-actions-role/"]
      }
    }
  }'

# Suppress NewBinaryExecutedInContainer for container image scanner (Trivy, etc.)
# Scanner may execute binaries during scan; filter by the scanner pod name
aws guardduty create-filter \
  --detector-id abc1234567890abcdef1234567890 \
  --name "image-scanner-suppression" \
  --action ARCHIVE \
  --rank 2 \
  --finding-criteria '{
    "Criterion": {
      "type": {
        "Equals": ["Execution:Kubernetes/NewBinaryExecutedInContainer"]
      },
      "resource.kubernetesDetails.kubernetesWorkloadDetails.name": {
        "Prefix": ["trivy-scan-", "snyk-scan-"]
      }
    }
  }'

# Suppress PrivilegedContainer for known infrastructure pods
# (node-local-dns, kube-proxy, vpc-cni require privileged mode in some configurations)
aws guardduty create-filter \
  --detector-id abc1234567890abcdef1234567890 \
  --name "infra-privileged-container-suppression" \
  --action ARCHIVE \
  --rank 3 \
  --finding-criteria '{
    "Criterion": {
      "type": {
        "Equals": ["PrivilegeEscalation:Kubernetes/PrivilegedContainer"]
      },
      "resource.kubernetesDetails.kubernetesWorkloadDetails.namespace": {
        "Equals": ["kube-system"]
      }
    }
  }'

Failure modes reference

FailureSymptomFix
Runtime findings not generated despite runtime monitoring enabledNo Execution:Kubernetes/* findings from known container eventsCheck DaemonSet pod count equals node count — agent not running on a node means no coverage for that node; also check agent version is compatible with kernel version
Fargate pods not covered by runtime monitoringEKS Fargate workloads never generate runtime findingsExpected — GuardDuty runtime agent requires DaemonSet which is not supported on Fargate; only audit log findings cover Fargate pods
EKS_AUDIT_LOGS enabled but no audit findingskubectl exec and RBAC changes not generating findingsVerify EKS cluster has audit control-plane logging enabled (aws eks describe-cluster --query cluster.logging); GuardDuty requires audit log type to be enabled
Excessive NewBinaryExecutedInContainer findings from init containersEvery pod startup generates a findingInit containers that download dependencies at startup trigger this; build dependencies into the image instead; or suppress by namespace/workload name
GuardDuty agent DaemonSet in CrashLoopBackOffPods in amazon-guardduty namespace failingCheck node kernel version compatibility (agent requires kernel 4.14+); check node IAM role has necessary permissions for GuardDuty agent; review pod logs