# GuardDuty EKS Protection：Kubernetes 監査ログでコントロールプレーンの脅威（匿名アクセス・RBAC 改変・特権昇格）を検知する

> GuardDuty EKS Protection の本番設計ガイド。EKS 監査ログでコントロールプレーンの脅威——system:anonymous への RBAC 付与、cluster-admin の改変、kube-system での exec、特権コンテナ起動——を検知する仕組みを解説。GuardDuty は独立ストリームで監査ログを取り込むため EKS コントロールプレーンログ(CloudWatch)の有効化は不要。finding 型をThreatPurpose別・MITRE ATT&CK対応で整理し、ThreatPurpose別の対応、Terraform(EKS_AUDIT_LOGS)有効化、RBAC ハードニング、SOAR 連携までをコードで示します。Runtime Monitoring(データプレーン)との違いも明確化。

- 公開日: 2026-06-27
- 著者: 友田 陽大
- タグ: セキュリティ, AWS, GuardDuty, EKS, Kubernetes
- URL: https://tomodahinata.com/blog/aws-guardduty-eks-protection-kubernetes-audit-logs-rbac-threats-guide
- カテゴリ: Amazon GuardDuty 本番運用
- 総合ガイド: https://tomodahinata.com/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide

## 要点

- EKS Protection は EKS 監査ログを解析し『どの API 呼び出しが起きたか』＝コントロールプレーンの脅威を検知する。コンテナ内で『実際に何が動いたか』を見る Runtime Monitoring(eBPF/データプレーン)とは別物——両方そろって初めて EKS の攻撃シーケンスがフルに効く
- 決定的な事実：GuardDuty は EKS 監査ログを『独立したストリーム』で取り込むため、追加設定は不要。EKS コントロールプレーンログ(CloudWatch Logs)を自分で有効化する必要はない(それは別物で、自分の可視性のための任意機能)
- finding は ThreatPurpose(匿名アクセス・RBAC/特権昇格・偵察・exec・secrets・impact)で型に意味がエンコードされる。resource_type は EKSCluster。代表例: Policy:Kubernetes/AnonymousAccessGranted(High)、Execution:Kubernetes/ExecInKubeSystemPod(Medium)、PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleBindingCreated(Medium、cluster-admin 絡みなら High)
- 検知は設定ミスの裏返し。匿名認証の無効化・最小権限 RBAC・Pod Security Standards/admission control・default サービスアカウントへの admin 付与回避が、そのまま finding を消すハードニングになる
- Terraform は aws_guardduty_detector_feature(EKS_AUDIT_LOGS)＋組織は aws_guardduty_organization_configuration_feature(auto_enable=ALL)。30日無料トライアルあり。料金は監査ログ100万件あたりの従量(目安・要確認)。リージョンによっては非対応——公式で要確認

---

「あなたの EKS クラスタ、いま誰が `cluster-admin` を、誰に渡したか追えていますか？」——コンテナ基盤のセキュリティを相談される場で、私がよく投げる質問です。

たいてい、答えに詰まります。`kubectl apply` で RoleBinding を1つ足すのは一瞬で、しかも**監査ログを CloudWatch に流していなければ、そもそも記録すら残っていない**。攻撃者から見れば、EKS のコントロールプレーンは「侵入の足がかり（漏洩した kubeconfig や IAM 認証情報）さえ得れば、`system:anonymous` に権限を付け、特権コンテナを立て、secrets を抜く」——という、**API 一発で進む静かな経路**です。ネットワークにもホストにも痕跡を残さず、`kube-apiserver` への正規の API 呼び出しとして進みます。

この記事は、**Amazon GuardDuty の EKS Protection** で、その**コントロールプレーンの脅威**——匿名アクセスの付与、RBAC の改変、特権昇格、`kube-system` での `exec`、secrets アクセス——を**本番品質で検知・対応**する実装ガイドです。題材として、私がマルチアカウント AWS 上の[サーバーレス決済プラットフォーム](/case-studies/payment-platform-reliability)で IAM・可観測性・DR を横断実装した経験——**実際の金銭・カーボンクレジット・地域通貨を扱うため、「誰が何の権限を握っているか」を運用の注意深さではなく仕組みで監視する必要があった**——という視点も交えます。

> **この記事のルール**：仕様・データソース・finding の種類と重大度・料金体系は **AWS 公式ドキュメント（2026年6月時点）** に基づきます。finding の種類・重大度・対応リージョン・料金は改定されるため、本番投入前に必ず公式の最新情報を確認してください。そして2つの鉄則——(1) **GuardDuty は検知（detection）であって防御（prevention）ではありません**。RBAC を直すのは GuardDuty ではなくあなたです。(2) **EKS Protection は『監査ログ＝コントロールプレーン』を見る機能であり、『コンテナの中で実際に何が動いたか＝データプレーン』を見る [Runtime Monitoring](/blog/aws-guardduty-runtime-monitoring-eks-ecs-fargate-ec2-guide) とは別物**です。この2つの違いを最初に固定します。

---

## 0. メンタルモデル：監査ログ（コントロールプレーン）か、ランタイム（データプレーン）か

設計を始める前に、EKS まわりの GuardDuty を**2つの目**として固定します。これを混同すると、片目だけ開けて「守れたつもり」になります。

> **EKS Protection ＝ Kubernetes 監査ログを解析し、「どの API 呼び出しが `kube-apiserver` に飛んだか」＝コントロールプレーンの操作を検知する。** 「誰が `create rolebinding` したか」「`system:anonymous` で API が叩かれたか」を見る目。
>
> **[Runtime Monitoring](/blog/aws-guardduty-runtime-monitoring-eks-ecs-fargate-ec2-guide) ＝ eBPF セキュリティエージェントで、「コンテナ／ホストの中で実際に何のプロセスが動いたか」＝データプレーンを検知する。** 「リバースシェルが起動したか」「`runc` でコンテナエスケープしたか」を見る目。

公式の定義はこうです——*"EKS Protection uses EKS audit logs to analyze activities of users and applications."* 監査ログは、ユーザー・アプリケーション（Kubernetes API 経由）・コントロールプレーンの**逐次的なアクション**を記録します。一方 Runtime Monitoring は OS レベルのプロセス実行・ファイルアクセス・ネットワーク接続を**ワークロードの内側**から観測します。

この対比から、3つの帰結が出ます。

1. **見る場所が違う。** 監査ログは「API サーバに**何が要求されたか**」（コントロールプレーン）。Runtime は「コンテナの中で**何が実行されたか**」（データプレーン）。たとえば「特権コンテナを起動する Deployment が `apply` された」は EKS Protection（監査ログに `create` が残る）が、「そのコンテナの中で `nsenter` でホストに脱出した」は Runtime Monitoring が捉えます。**同じ攻撃の別の断面**です。

2. **エージェントの有無が違う。** EKS Protection は**エージェントレス**。GuardDuty が監査ログを独立ストリームで取り込むだけなので、クラスタに何も入れません（次章の「killer fact」）。Runtime Monitoring は**各ノードに eBPF エージェント（DaemonSet `aws-guardduty-agent`）が要る**——配布・カバレッジ・vCPU 課金という別の設計が乗ります（詳細は[データプレーン記事](/blog/aws-guardduty-runtime-monitoring-eks-ecs-fargate-ec2-guide)）。

3. **両方そろって初めて攻撃シーケンスがフルに効く。** GuardDuty の Extended Threat Detection は、弱いシグナルを24時間窓で相関させ、多段攻撃を `AttackSequence:EKS/CompromisedCluster`（必ず Critical 9.0〜10.0）に束ねます。公式は、EKS の攻撃シーケンスには **EKS Protection または Runtime Monitoring** が要るとし、**両方そろえると最も広くカバーできる**としています。「監査ログで RBAC 改変を捉え、ランタイムでコンテナ内の実行を捉える」——両目で見て初めて、コントロールプレーンからデータプレーンに伸びる連鎖を1本の線で受け取れます（攻撃シーケンスの読み解きは[専用記事](/blog/aws-guardduty-extended-threat-detection-attack-sequence-findings-guide)へ）。

| 観点 | **EKS Protection（本記事）** | **[Runtime Monitoring](/blog/aws-guardduty-runtime-monitoring-eks-ecs-fargate-ec2-guide)** |
| --- | --- | --- |
| 見る面 | コントロールプレーン（監査ログ） | データプレーン（OS レベルの挙動） |
| データ源 | **EKS 監査ログ**（API 呼び出し） | eBPF エージェントのランタイムイベント |
| 問い | 「どの API 呼び出しが起きたか」 | 「コンテナの中で何が動いたか」 |
| エージェント | **不要（エージェントレス）** | 必要（DaemonSet / SSM / サイドカー） |
| feature 名 | `EKS_AUDIT_LOGS` | `RUNTIME_MONITORING` |
| resource type | `EKSCluster` | `Instance` / コンテナ系 |
| 代表 finding | `Policy:Kubernetes/AnonymousAccessGranted` | `Execution:Runtime/ReverseShell` |
| 攻撃シーケンス | EKS のシーケンスに寄与 | EKS/ECS/EC2 のシーケンスに寄与 |

本記事はこの表の**左列**を深掘りします。右列は[データプレーン記事](/blog/aws-guardduty-runtime-monitoring-eks-ecs-fargate-ec2-guide)に譲ります。GuardDuty 全体の地図（基盤検知・保護プラン選定・組織統制・EventBridge 自動対応）は[ピラー記事](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)が土台です。

---

## 1. killer fact：監査ログは「独立ストリーム」で取り込まれる——追加設定は不要

EKS Protection を設計するうえで、**最初に腑に落とすべき事実**がこれです。多くの人がここで「EKS のコントロールプレーンログ（CloudWatch Logs）を有効にしないと GuardDuty が見えないのでは？」と詰まりますが、**答えは No**。

公式の表現を引きます。

> *"When you enable EKS Protection, GuardDuty automatically starts monitoring your Amazon EKS clusters for potential security threats. GuardDuty uses its own independent stream to collect and analyze EKS audit logs – no additional configuration is required."*

つまり——**GuardDuty は EKS 監査ログを「自前の独立したストリーム」で直接収集・解析する**。あなたが EKS の**コントロールプレーンログ（audit ログを CloudWatch Logs に送る機能）を有効化する必要はありません**。これは[ピラー記事](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)で見た「基盤データソース（CloudTrail・VPC Flow Logs・DNS）の独立した複製ストリーム」と**完全に同じ思想**です。GuardDuty は「あなたのログ配管」に依存せず、自分のパイプで見ます。

ここを取り違えると、**二重コスト**を払うか、**穴を開ける**かのどちらかに転びます。公式は、混同を避けるために明示しています。

> *"To view EKS audit logs in your account (optional), you can configure Amazon EKS control plane logging to send audit logs to CloudWatch Logs. This configuration is separate from EKS Protection and is not required for security monitoring capability in GuardDuty."*

> *"GuardDuty doesn't manage your Amazon EKS control plane logging ... To manage access to and retention of your EKS audit logs, you must configure the Amazon EKS control plane logging feature."*

要点を表に落とします。

| 機能 | 何のため | GuardDuty に必要か | 課金 |
| --- | --- | --- | --- |
| **GuardDuty EKS Protection** | GuardDuty が脅威検知のために監査ログを解析 | — | GuardDuty 側で監査ログ量に応じて課金 |
| **EKS コントロールプレーンログ（audit → CloudWatch Logs）** | **あなた自身**が監査ログを閲覧・保管・分析するため | **不要**（別物） | CloudWatch Logs の取り込み・保管費が**別途**かかる |

設計上の結論はシンプルです。

- **GuardDuty で監査ログ由来の脅威を検知したいだけ**なら、`EKS_AUDIT_LOGS` を有効化するだけで完結します。EKS 側の audit ログ出力（CloudWatch Logs）はオフのままで構いません。
- **自分でも監査ログを SIEM や CloudWatch で見たい／インシデント時に生ログを追いたい**なら、EKS コントロールプレーンログを**別途・任意で**有効化します。これは GuardDuty とは独立した、自分の可観測性のための投資です。

> **コスト目線（要確認）**：「とりあえず両方 ON」にすると、GuardDuty の監査ログ解析費に加えて、CloudWatch Logs の取り込み・保管費まで二重に乗ります。**まず GuardDuty 側だけ**で検知を回し、生ログの常時保管が本当に必要かを見極めてから CloudWatch 側を判断するのが、費用対効果で勝ちやすい順序です。

---

## 2. finding 型のカタログ：ThreatPurpose で読む

EKS Protection が出す finding は、すべて **resource type が `EKSCluster`** で、型名が `ThreatPurpose:Kubernetes/...` の形をとります。[ピラー記事の 5 章](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)で見たとおり、**型名に意味がエンコードされている**ので、先頭の ThreatPurpose を見れば「攻撃のどの段階か」が即わかります。ここを ThreatPurpose 別に整理すると、自動対応のルーティング設計がそのまま見えてきます。

### 2.1 検知メカニズムの2系統

EKS の finding は、検知の出どころで2つに分かれます。

- **`.AnomalousBehavior`** ＝ GuardDuty の **ML モデルがベースラインからの逸脱**を検知したもの。「このユーザーが、この場所から、この namespace で、この API を叩くのは普段ない」を捉えます。
- **`.Custom`** ＝ あなたが**アップロードした脅威リスト（custom threat list）の IP に一致**したもの（[ピラー記事 7 章](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)の脅威リスト）。
- それ以外（`MaliciousIPCaller` / `TorIPCaller` / `SuccessfulAnonymousAccess` / `Policy:*`）は、**既知の悪性 IP・Tor 出口・匿名アクセス・設定ポリシー違反**といった、ML を経ない明示的なシグナルです。

> **`SuccessfulAnonymousAccess` の意味（重要）**：型名の末尾が `SuccessfulAnonymousAccess` の finding は、**`system:anonymous` ユーザーが API を「成功裏に」叩けた**ことを示します。公式いわく *"API calls made by `system:anonymous` are unauthenticated."*——つまり**認証なしで API が通った**。これは「攻撃」というより**設定ミスの証拠**で、匿名アクセスが許可されていることを意味します。`MaliciousIPCaller` 系は逆に**未認証アクセスでは生成されない**点に注意（公式：*"MaliciousIPCaller findings are not generated for unauthenticated access."*）。

### 2.2 ThreatPurpose 別カタログ（重大度は公式 2026-06 準拠）

重大度は **Critical 9.0–10.0 / High 7.0–8.9 / Medium 4.0–6.9 / Low 1.0–3.9** の4帯（[ピラー記事 5.2](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)）。以下は公式の **Default severity** をそのまま転記しています。

| ThreatPurpose（MITRE ATT&CK 戦術） | finding 型 | 重大度 | 何を意味するか |
| --- | --- | --- | --- |
| **Policy**（設定ポリシー違反） | `Policy:Kubernetes/AnonymousAccessGranted` | **High** | `system:anonymous` を role に束ねる RoleBinding/ClusterRoleBinding が作られた＝匿名 API アクセスを許可した |
| | `Policy:Kubernetes/AdminAccessToDefaultServiceAccount` | **High** | namespace の **default サービスアカウントに admin 権限**が付いた（pod が意図せず admin で起動し得る） |
| | `Policy:Kubernetes/ExposedDashboard` | **Medium** | Kubernetes Dashboard が LB 経由でインターネット公開された |
| | `Policy:Kubernetes/KubeflowDashboardExposed` | **Medium** | Kubeflow Dashboard がインターネット公開された |
| **CredentialAccess**（認証情報・secrets 窃取） | `CredentialAccess:Kubernetes/AnomalousBehavior.SecretsAccessed` | **Medium** | secrets 取得 API が**異常な形**（ML 逸脱）で呼ばれた |
| | `CredentialAccess:Kubernetes/SuccessfulAnonymousAccess` | **High** | 認証情報系 API が**匿名ユーザー**で叩けた |
| | `CredentialAccess:Kubernetes/{MaliciousIPCaller, MaliciousIPCaller.Custom, TorIPCaller}` | **High** | 認証情報系 API が**悪性 IP / Tor 出口**から叩かれた |
| **PrivilegeEscalation**（特権昇格） | `PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleBindingCreated` | **Medium**（`admin`/`cluster-admin` 絡みは **High**） | 過剰権限 role への RoleBinding/ClusterRoleBinding が**異常な形**で作成・改変された |
| | `PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleCreated` | **Low** | 過剰権限の Role/ClusterRole が**異常な形**で作成された（admin 系を避けつつ検知回避） |
| | `PrivilegeEscalation:Kubernetes/AnomalousBehavior.WorkloadDeployed!PrivilegedContainer` | **High** | **特権コンテナ**を含むワークロードが異常な形で起動された（root でホストにアクセス可） |
| **Persistence**（永続化） | `Persistence:Kubernetes/AnomalousBehavior.WorkloadDeployed!ContainerWithSensitiveMount` | **High** | **機微なホストパスを volumeMount** したワークロードが異常な形で起動された |
| | `Persistence:Kubernetes/SuccessfulAnonymousAccess` | **High** | 永続化系の高権限 API が**匿名ユーザー**で叩けた |
| | `Persistence:Kubernetes/{MaliciousIPCaller, MaliciousIPCaller.Custom, TorIPCaller}` | **Medium** | 永続化系 API が悪性 IP / Tor から叩かれた |
| **Execution**（実行） | `Execution:Kubernetes/ExecInKubeSystemPod` | **Medium** | **`kube-system` namespace の pod 内でコマンドが exec された**（システムコンポーネントへの干渉） |
| | `Execution:Kubernetes/AnomalousBehavior.ExecInPod` | **Medium** | pod 内 exec が**異常な形**（ユーザー/namespace/pod が普段と違う）で行われた |
| | `Execution:Kubernetes/AnomalousBehavior.WorkloadDeployed` | **Low**（疑わしいイメージ名/コマンドなら **Medium**） | ワークロードが**異常な形**で作成・改変された |
| **Discovery**（偵察） | `Discovery:Kubernetes/AnomalousBehavior.PermissionChecked` | **Low** | `kubectl auth can-i` 等で**自分の権限を異常な形で確認**した（昇格前の下見） |
| | `Discovery:Kubernetes/SuccessfulAnonymousAccess` | **Medium** | 偵察系 API が**匿名ユーザー**で叩けた（`/healthz` 等のヘルスチェックは除外） |
| | `Discovery:Kubernetes/{MaliciousIPCaller, MaliciousIPCaller.Custom, TorIPCaller}` | **Medium** | 偵察系 API が悪性 IP / Tor から叩かれた |
| **DefenseEvasion**（防御回避） | `DefenseEvasion:Kubernetes/{MaliciousIPCaller, MaliciousIPCaller.Custom, SuccessfulAnonymousAccess, TorIPCaller}` | **High** | 防御回避系 API が悪性 IP / Tor / 匿名で叩かれた |
| **Impact**（破壊・改ざん） | `Impact:Kubernetes/{MaliciousIPCaller, MaliciousIPCaller.Custom, SuccessfulAnonymousAccess, TorIPCaller}` | **High** | リソース改ざん・破壊系 API が悪性 IP / Tor / 匿名で叩かれた |

> **2つの注意（公式準拠）**：
> (1) `PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleBindingCreated` は**通常 Medium だが、`admin` または `cluster-admin` ClusterRole が絡むと High** に上がります（公式の注記そのまま）。`cluster-admin` を誰かに渡す改変は、まさにこの High が立ちます。
> (2) `Execution:Kubernetes/AnomalousBehavior.WorkloadDeployed` は**通常 Low だが、既知の pentest ツール名や reverse shell のような疑わしい起動コマンドを含むと Medium** に上がります。

### 2.3 設計への含意：Policy 系は「攻撃」ではなく「設定ミス」

このカタログから、自動対応の設計に効く読みが3つ出ます。

- **`Policy:Kubernetes/*` は侵害ではなく『設定の事実』**。匿名アクセスを付けた、default SA に admin を付けた、Dashboard を公開した——これらは**今まさに悪用されているとは限らない**が、**悪用の入り口を開けた**ことを示します。だから対応は「封じ込め」より**「設定を直す（ハードニング）」**（4・5 章）。
- **`SuccessfulAnonymousAccess` で終わる型は最優先で潰す**。これは「匿名で API が**通ってしまった**」事実。`Policy:Kubernetes/AnonymousAccessGranted`（付与の瞬間）とセットで、**匿名認証の無効化**に直結させます。
- **`.AnomalousBehavior` 系は『普段と違う』のシグナル**。正規運用でも出得る（CI/CD が新しい RoleBinding を作る等）ので、**抑制（suppression）ではなく通知側のトリアージ**で扱うのが基本。ただし[ピラー記事 7 章](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)で触れたとおり、**抑制で消すと攻撃シーケンスの材料まで消える**点に注意します。

---

## 3. worked example ①：匿名アクセスの付与 → RBAC を直す

最も「設定ミスがそのまま finding になる」典型が、**`system:anonymous` への権限付与**です。攻撃チェーンを実コードで追います。

### 3.1 何が起きると finding が立つか

誰か（侵害された kubeconfig を持つ攻撃者かもしれないし、善意だが危険な運用者かもしれない）が、こういう ClusterRoleBinding を `apply` したとします。

```yaml
# ❌ アンチパターン：system:anonymous に閲覧権限を「とりあえず」付ける。
# 「ヘルスチェックを通したかった」「動作確認を急いだ」——理由は何であれ、
# これは未認証ユーザーに cluster 全体の secrets 列挙を許す穴になり得る。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: anonymous-viewer
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: view            # view でも secrets 以外を広く読める。cluster-admin なら全権
subjects:
  - apiGroup: rbac.authorization.k8s.io
    kind: User
    name: system:anonymous   # ← 未認証ユーザー。誰でもこの権限で API を叩ける
```

この `create clusterrolebinding` は**監査ログに残り**、GuardDuty は独立ストリームでそれを読み、**`Policy:Kubernetes/AnonymousAccessGranted`（High）** を生成します。公式の説明：

> *"a user on your Kubernetes cluster successfully created a `ClusterRoleBinding` or `RoleBinding` to bind the user `system:anonymous` to a role. This enables unauthenticated access to the API operations permitted by the role."*

その後、攻撃者がこの匿名権限で実際に secrets を列挙すれば、**`CredentialAccess:Kubernetes/SuccessfulAnonymousAccess`（High）** も続けて立ちます。**「付与（Policy）」と「行使（SuccessfulAnonymousAccess）」が別々の finding**として出る——この2つが時間窓で並べば、攻撃シーケンスの材料になります。

### 3.2 対応：RBAC を直し、匿名認証を断つ

公式の remediation はこうです——*"examine the permissions that have been granted to the `system:anonymous` user or `system:unauthenticated` group on your cluster and revoke unnecessary anonymous access."*

具体手順に落とします。

```bash
# ① 何が system:anonymous / system:unauthenticated に紐づいているかを棚卸しする。
#    「知らないうちに付いていた」binding をまず可視化するのが第一歩。
kubectl get clusterrolebindings,rolebindings -A -o json \
  | jq -r '.items[]
      | select(.subjects[]?
          | (.name=="system:anonymous") or (.name=="system:unauthenticated"))
      | "\(.kind)/\(.metadata.name) -> \(.roleRef.name)"'

# ② 不要な匿名 binding を削除する（root cause の除去）。
kubectl delete clusterrolebinding anonymous-viewer
```

> **歴史的な罠（公式が明記）**：*"Before Kubernetes version 1.14, the `system:unauthenticated` group was associated to `system:discovery` and `system:basic-user` ClusterRoles by default ... Cluster updates do not revoke these permissions."*——つまり**古い時代に作られたクラスタは、1.14 以降にアップグレードしても匿名権限が残っている**可能性があります。`system:unauthenticated` への binding は明示的に棚卸し・剥がしてください。

そして**根本対処は「そもそも匿名認証を通さない」**ことです。kube-apiserver の `--anonymous-auth=false` 相当の設定や、Pod Security Standards・admission control で「危険な RBAC を `apply` させない」ガードを敷きます。

```yaml
# 予防：admission control で「危険な subject を持つ RoleBinding を拒否」する例(概念)。
# OPA/Gatekeeper や Kyverno で「subjects に system:anonymous/unauthenticated を
# 含む binding を deny」するポリシーを敷けば、finding が立つ前に apply を止められる。
# GuardDuty(検知) の手前に、admission(予防) を置く——多層防御。
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: deny-anonymous-binding
spec:
  validationFailureAction: Enforce   # 監査ではなく「拒否」。予防に振り切る
  rules:
    - name: block-anonymous-subjects
      match:
        any:
          - resources:
              kinds: ["RoleBinding", "ClusterRoleBinding"]
      validate:
        message: "system:anonymous / system:unauthenticated への binding は禁止"
        deny:
          conditions:
            any:
              - key: "{{ request.object.subjects[].name }}"
                operator: AnyIn
                value: ["system:anonymous", "system:unauthenticated"]
```

**検知（GuardDuty）と予防（admission control）は役割が違う**ことに注意。admission で**入口を塞ぎ**、GuardDuty で**塞ぎ漏れ・既存の穴・侵害された正規ユーザーによる付与**を**検知**します。片方では足りません。

---

## 4. worked example ②：`kube-system` での exec と、default SA への admin

### 4.1 `Execution:Kubernetes/ExecInKubeSystemPod`（Medium）

`kube-system` namespace は `kube-dns` や `kube-proxy` といった**システムコンポーネント**が住む場所です。公式いわく *"It is very uncommon to execute commands inside pods or containers under `kube-system` namespace and may indicate suspicious activity."*——ここで `kubectl exec` が走るのは、ほぼ常に異常です。

```bash
# 攻撃者(または侵害された認証情報)がこういう exec をすると finding が立つ。
# kube-system の pod は「触らない」のが正常。ここでのシェル取得は赤信号。
kubectl exec -it -n kube-system coredns-xxxx -- /bin/sh
```

GuardDuty はこの `exec` サブリソースへの API 呼び出しを監査ログから読み、**`Execution:Kubernetes/ExecInKubeSystemPod`（Medium）** を立てます。対応の公式ガイド：*"the credentials of the user identity used to execute the command may be compromised. Revoke access of the user and reverse any changes made by an adversary to your cluster."*

実務では「誰が exec したか」を finding の `resource.kubernetesDetails.kubernetesUserDetails`（user name / uid / groups）で特定し、**その identity の権限を即座に剥がす**——IAM 経由のユーザーなら[アクセスキーをローテート](/blog/github-actions-oidc-keyless-cicd-aws-gcp-guide)、OIDC ユーザーなら IdP 側で資格情報を失効、サービスアカウントなら**トークンをローテート**します（6 章）。

### 4.2 `Policy:Kubernetes/AdminAccessToDefaultServiceAccount`（High）

Kubernetes は全 namespace に **`default` サービスアカウント**を自動で作り、**明示的に別の SA を指定しない pod に自動で割り当てます**。だから `default` SA に admin 権限が付くと、公式の言葉で *"it may result in pods being unintentionally launched with admin privileges."*——**何も指定していない pod が、知らないうちに admin として動く**ことになります。

公式の remediation は明快で、これがそのまま**ハードニングの原則**になります。

> *"You should not use the default service account to grant permissions to pods. Instead you should create a dedicated service account for each workload and grant permission to that account on a needs basis ... Then you should remove the admin permission from the default service account."*

```yaml
# ✅ 正しい形：ワークロードごとに専用 SA を作り、必要最小限だけ付ける。
# default SA は「何も権限を持たない」状態に保つ(automount も切る)。
apiVersion: v1
kind: ServiceAccount
metadata:
  name: payments-worker            # ワークロード専用。default を使い回さない
  namespace: payments
automountServiceAccountToken: false  # 必要な pod だけ明示的に mount する
---
# pod は専用 SA を「明示」して使う。default への暗黙依存をなくす。
apiVersion: apps/v1
kind: Deployment
metadata:
  name: payments-worker
  namespace: payments
spec:
  template:
    spec:
      serviceAccountName: payments-worker   # ← default ではなく専用 SA を明示
      automountServiceAccountToken: true     # この pod だけトークンを mount
```

そして `default` SA から admin を剥がし、二度と admin が付かないよう admission control でガードします。**finding を消す作業が、そのまま最小権限の徹底になる**——これが EKS Protection の良いところです。検知が「設定の健全性チェック」として機能します。

---

## 5. worked example ③：RBAC の異常な改変（特権昇格）

`cluster-admin` の付与を追うのが、冒頭の問いへの答えです。

### 5.1 `PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleBindingCreated`

攻撃者が足がかりを得た後、**永続的な高権限を確保する**ために RoleBinding を作るのは定番です。

```yaml
# 攻撃者の典型ムーブ：侵害したサービスアカウントに cluster-admin を束ねる。
# 一度これが通れば、以後は「正規の権限」として全 API が叩けてしまう。
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: backdoor-admin
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: cluster-admin          # ← 全権。これが絡むと finding は Medium → High
subjects:
  - kind: ServiceAccount
    name: compromised-sa
    namespace: default
```

この `create clusterrolebinding` を、GuardDuty の **ML モデル**が「このユーザーが、この場所から、この namespace で binding を作るのは普段ない」と判定すれば、**`PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleBindingCreated`** が立ちます。重要なのは重大度の動的変化——**通常は Medium ですが、`admin` または `cluster-admin` が絡むと High** に上がります（2.2 の注記）。冒頭の「誰が `cluster-admin` を誰に渡したか」は、まさにこの High が答えてくれます。

公式の remediation：*"Examine the permissions granted to the Kubernetes user. These permissions are defined in the role and subjects involved in `RoleBinding` and `ClusterRoleBinding`. If the permissions were granted mistakenly or maliciously, revoke user access and reverse any changes."*

```bash
# 対応：不正な binding を即削除し、「誰が」作ったかを監査ログ/finding で特定する。
kubectl delete clusterrolebinding backdoor-admin

# その binding を作った identity(finding の kubernetesUserDetails)の権限を剥がす。
# IAM 経由なら aws-auth ConfigMap から該当エントリを削除(6章)。
```

### 5.2 関連する特権昇格・永続化の型

同じ ThreatPurpose ファミリーで押さえるべき型：

- **`PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleCreated`（Low）**：admin 系の組み込み role を**避けて**、過剰権限の Role/ClusterRole を自作する手口。公式：*"Actors can use role creation with powerful permissions to avoid using built-in admin-like roles and avoid detection."* 低重大度だが、**検知回避の意図**が見えるシグナルなので、相関の文脈で重要。
- **`PrivilegeEscalation:Kubernetes/AnomalousBehavior.WorkloadDeployed!PrivilegedContainer`（High）**：特権コンテナ（`securityContext.privileged: true`）を含むワークロードの異常起動。root でホストにアクセスでき、ホスト奪取の踏み台になります。
- **`Persistence:Kubernetes/AnomalousBehavior.WorkloadDeployed!ContainerWithSensitiveMount`（High）**：`/`, `/etc`, `/var/run/docker.sock` のような**機微ホストパスを volumeMount** したワークロード。ホストのファイルシステムへ抜ける足場になります。

これら「特権コンテナ／機微マウント」は、**Pod Security Standards の `restricted` プロファイル**で予防できます。

```yaml
# 予防：namespace に Pod Security Standards の restricted を適用。
# 特権コンテナ・hostPath マウント・root 実行を admission 段階で拒否する。
# WorkloadDeployed!PrivilegedContainer / !ContainerWithSensitiveMount は
# そもそも apply できなくなる(finding が立つ前に止まる)。
apiVersion: v1
kind: Namespace
metadata:
  name: payments
  labels:
    pod-security.kubernetes.io/enforce: restricted   # 危険な pod を拒否
    pod-security.kubernetes.io/enforce-version: latest
    pod-security.kubernetes.io/audit: restricted      # 監査ログにも残す
    pod-security.kubernetes.io/warn: restricted       # apply 時に警告
```

ここでも構図は同じです——**admission（PSS）で入口を塞ぎ、GuardDuty（監査ログ）で塞ぎ漏れと侵害された正規ユーザーの異常操作を検知する**。検知と予防は代替関係ではなく補完関係です。

---

## 6. 対応をパイプラインに載せる：SOAR 連携と RBAC 侵害の手当て

検知できても、**対応が手作業のままなら MTTR は縮みません**。EKS の finding も、[ピラー記事 6 章](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)で作った **EventBridge → 冪等な自動対応**の配管に流します。

### 6.1 EKS finding を EventBridge でルーティングする

EKS の finding も `source: aws.guardduty` / `detail-type: GuardDuty Finding` で EventBridge に流れます。データプレーンの finding とルーティングを分けるなら、**`detail.type` の prefix（`*:Kubernetes/*`）と `resource.resourceType = EKSCluster`** でフィルタします。

```hcl
# EKS(監査ログ)由来の High 以上の finding を、専用の responder へ。
# type が ":Kubernetes/" を含むもの = コントロールプレーンの脅威に絞る。
resource "aws_cloudwatch_event_rule" "guardduty_eks_high" {
  name        = "guardduty-eks-control-plane-high"
  description = "Route EKS audit-log findings (severity >= 7) to the K8s responder"
  event_pattern = jsonencode({
    source        = ["aws.guardduty"]
    "detail-type" = ["GuardDuty Finding"]
    detail = {
      severity = [{ numeric = [">=", 7] }]
      # EKS Protection の finding は resource type が EKSCluster。
      resource = { resourceType = ["EKSCluster"] }
    }
  })
}

# 通知は広く(SNS→Slack)、封じ込めは「型の許可リスト」で狭く——という
# ピラー記事の原則は EKS でも同じ。EKS は破壊的操作(pod 削除・SA 失効)の
# ブラスト半径が大きいので、なおさら「人間のレビューを挟む」方に倒す。
resource "aws_cloudwatch_event_target" "eks_to_sns" {
  rule      = aws_cloudwatch_event_rule.guardduty_eks_high.name
  target_id = "notify-sns"
  arn       = aws_sns_topic.security_alerts.arn
}
```

> **EKS 特有の対応設計**：EC2 の自動隔離（隔離 SG へ付け替え）と違い、**Kubernetes の封じ込めはブラスト半径が読みにくい**。「pod を消す」「SA トークンを失効する」は、正規ワークロードを巻き込み得ます。だから EKS の自動対応は**「通知・enrich・チケット化」までを自動**にし、**RBAC の剥奪や pod 隔離は admission ガード＋人間のレビュー**に寄せるのが安全側の設計です（[ピラー記事の「破壊的操作は人間を挟む」](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)を EKS でより厳格に適用）。SOAR 連携の全体像は[自動対応の専用記事](/blog/aws-guardduty-eventbridge-automated-remediation-incident-response-guide)へ。

### 6.2 RBAC 侵害・認証情報漏洩の手当て

EKS の finding は、その user 種別で対応が分岐します。公式の remediation を、私の運用に落とした手順で。

1. **誰が叩いたかを特定**：finding の `resource.kubernetesDetails.kubernetesUserDetails`（user name / uid / groups）と、IAM 経由なら `Access Key details` から IAM ロール/ユーザーを特定。**Amazon Detective の "Investigate in Detective"** で横断調査できます。
2. **identity 種別ごとに資格情報を断つ**：
   - **IAM ユーザー/ロール**：[アクセスキーをローテート](/blog/github-actions-oidc-keyless-cicd-aws-gcp-guide)し、`aws-auth` ConfigMap（`kubectl edit configmaps aws-auth -n kube-system`）から該当エントリを削除。
   - **OIDC ユーザー**：IdP 側で資格情報をローテート/失効。
   - **サービスアカウント**：**SA のトークンをローテート**し、その SA を使う pod を洗い出して手当て。
3. **secrets をローテートする**：公式が繰り返し言う通り *"Rotate any secrets that user had access to."*——匿名アクセスや侵害された identity が**触れた可能性のある secrets は、原則すべてローテート**します。「触れたかもしれない」を「触れていないと証明」するのは難しいので、**疑わしきはローテート**が安全側。
4. **改変を巻き戻す**：攻撃者が作った RoleBinding/Role/ワークロードを削除し、クラスタを既知の正常状態へ戻す。

> **決済基盤での教訓**：私が[決済プラットフォーム](/case-studies/payment-platform-reliability)で IAM・可観測性を設計したとき、最も効いたのは「**資格情報の失効とローテートを、手順書ではなくコード/自動化で即実行できる状態**にしておく」ことでした。インシデント時に「誰がどの secrets に触れたか」を追えて、**ワンコマンドでローテートできる**——その準備の有無が MTTR を分けます。EKS でも同じで、`Policy:Kubernetes/AnonymousAccessGranted` を受けてから「さて secrets はどこに…」では遅い。**finding 型 → ローテート対象のマッピングを事前に用意**しておきます。

---

## 7. Terraform で有効化する：`EKS_AUDIT_LOGS` と組織一括展開

設計が固まったら、コードに落とします。EKS Protection は `aws_guardduty_detector` 本体とは別の **`aws_guardduty_detector_feature`** リソースで、feature 名 **`EKS_AUDIT_LOGS`** を足すだけです。エージェントもログ配管も要りません（1 章の killer fact）。

```hcl
# 前提：GuardDuty 本体(detector)は有効化済み。
# 基盤検知 + Extended Threat Detection はこの時点で既にオン(ピラー記事 2章)。
resource "aws_guardduty_detector" "this" {
  enable                       = true
  finding_publishing_frequency = "FIFTEEN_MINUTES" # 更新の遅延を縮める(ピラー記事 6章)
}

# EKS Protection = 監査ログ解析を有効化。
# これだけで GuardDuty が独立ストリームで EKS 監査ログを読み始める。
# EKS 側のコントロールプレーンログ(CloudWatch)は不要(1章)。
resource "aws_guardduty_detector_feature" "eks_audit_logs" {
  detector_id = aws_guardduty_detector.this.id
  name        = "EKS_AUDIT_LOGS"
  status      = "ENABLED"
}
```

組織で一括統制するなら、[ピラー記事 3 章](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)の委任管理者構成に、`EKS_AUDIT_LOGS` の自動有効化を足します。

```hcl
# ── 委任管理者(security-tooling)アカウントで実行 ──
# 組織に入っている/今後入る全アカウントで EKS Protection を自動 ON。
# 「EKS クラスタを持つアカウントだけ検知すればいい」が、
# どのアカウントが EKS を持つかは増減するので、ALL で漏れを構造的に消す。
# (EKS を持たないアカウントでは監査ログが流れず、課金もほぼ発生しない)
resource "aws_guardduty_organization_configuration_feature" "eks_audit_logs_org" {
  detector_id = aws_guardduty_detector.security.id
  name        = "EKS_AUDIT_LOGS"
  auto_enable = "ALL"   # ALL = 既存+新規すべて / NEW = 今後のみ / NONE = 無効
}
```

設計のポイント:

- **`auto_enable = "ALL"`** で「新しく作られたアカウントの EKS も、人手を介さず検知対象に入る」状態を作る（[ピラー記事 3 章](/blog/aws-guardduty-threat-detection-multi-account-terraform-eventbridge-guide)の `NEW` のサイレントな穴を避ける）。EKS クラスタが存在しないアカウントでは監査ログが発生しないため、**全社 ON でも無駄な課金はほぼ生じない**——これが Runtime Monitoring（vCPU 課金で「資産に絞る」必要がある）と違い、**EKS Protection を全社デフォルト ON にしやすい**理由です。
- **30日無料トライアル**：新規有効化したアカウント/リージョンには30日の無料トライアルが付き、**監査ログ量に基づく想定請求額を課金開始前に把握**できます（公式準拠）。

> **リージョンの注意（公式準拠・要確認）**：公式は *"EKS Protection may not be available in all the AWS Regions where GuardDuty is available."* と明記しています。**GuardDuty 本体が使えるリージョンでも、EKS Protection は使えない場合がある**——使用リージョンでの対応可否を、必ず公式の Region-specific feature availability で確認してください。

> **料金（目安・要確認）**：EKS Protection の課金は**解析した EKS 監査ログの量（100万件あたり）**をベースにした従量制です。具体的な単価はリージョンと改定で変わるため、**金額は公式の GuardDuty pricing で要確認**とします（本記事では単価を断定しません）。「全社 ON にしやすい」のは、EKS を持たないアカウントで監査ログが発生しないためで、**クラスタの API トラフィックが多いほど課金は増える**点は押さえておきます。

---

## 8. まとめ：GuardDuty EKS Protection チートシート

迷ったときの早見表です。

- **2つの目を混同しない**：**EKS Protection＝監査ログ＝コントロールプレーン**（「どの API 呼び出しが起きたか」、エージェントレス、`EKS_AUDIT_LOGS`）。**[Runtime Monitoring](/blog/aws-guardduty-runtime-monitoring-eks-ecs-fargate-ec2-guide)＝eBPF＝データプレーン**（「コンテナの中で何が動いたか」、エージェント要、`RUNTIME_MONITORING`）。**両方そろって `AttackSequence:EKS/CompromisedCluster`（Critical）がフルに効く**。
- **killer fact**：GuardDuty は EKS 監査ログを**独立したストリーム**で取り込む——**追加設定不要**。EKS コントロールプレーンログ（CloudWatch Logs）の有効化は**別物で不要**（自分で生ログを見たい時だけの任意機能）。「両方 ON」は二重コストになり得る。
- **finding は ThreatPurpose で読む**：resource type は **`EKSCluster`**。`SuccessfulAnonymousAccess` で終わる型＝**匿名で API が通った設定ミスの証拠**。`Policy:Kubernetes/*`＝**侵害ではなく『穴を開けた事実』**（対応はハードニング）。`.AnomalousBehavior`＝ML 逸脱、`.Custom`＝自前脅威リスト一致。
- **重大度の動的変化（要記憶）**：`PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleBindingCreated` は通常 **Medium**、`admin`/`cluster-admin` 絡みで **High**。`Execution:Kubernetes/AnomalousBehavior.WorkloadDeployed` は通常 **Low**、疑わしいイメージ/コマンドで **Medium**。
- **検知 → ハードニングの直結**：匿名アクセス → **匿名認証を断ち RBAC を剥がす**。`kube-system` exec → **identity の資格情報を失効**。default SA に admin → **専用 SA ＋ 最小権限**。特権コンテナ/機微マウント → **Pod Security Standards `restricted`**。**finding を消す作業がそのまま最小権限の徹底**になる。
- **検知と予防は補完**：admission control（Kyverno/Gatekeeper/PSS）で**入口を塞ぎ**、GuardDuty（監査ログ）で**塞ぎ漏れ・侵害された正規ユーザーの異常操作を検知**。片方では足りない。
- **Terraform**：`aws_guardduty_detector_feature`（`name = "EKS_AUDIT_LOGS"`）＋ 組織は `aws_guardduty_organization_configuration_feature`（`auto_enable = "ALL"`）。**vCPU 課金がない分、全社デフォルト ON にしやすい**。30日無料トライアルあり。**リージョン対応・料金は公式で要確認**。
- **対応**：EKS finding は `resource.resourceType = EKSCluster` で EventBridge ルーティング。**通知は自動・RBAC の剥奪や pod 隔離は人間のレビュー**（ブラスト半径が大きい）。RBAC 侵害は**binding 削除＋識別子の資格情報失効＋触れた secrets を全ローテート＋改変の巻き戻し**。

GuardDuty EKS Protection は「有効化すれば守ってくれる箱」ではなく、**「監査ログでコントロールプレーンの脅威を検知し、その finding を RBAC ハードニングと SOAR 対応に変える」**ところまで設計して初めて価値が出ます。そして**監査ログ（本記事）だけでは半分**——[ランタイム（データプレーン）](/blog/aws-guardduty-runtime-monitoring-eks-ecs-fargate-ec2-guide)とそろえて、初めて EKS の攻撃を両目で見られます。なお EKS か ECS かという土台の選定自体に迷っているなら、[ECS vs EKS の判断フレーム](/blog/aws-ecs-vs-eks-startup-decision-framework)も合わせてどうぞ。

私はマルチアカウントの[サーバーレス決済プラットフォーム](/case-studies/payment-platform-reliability)で、**実際の金銭・カーボンクレジット・地域通貨を扱う基盤の IAM・可観測性・DR を横断実装**し、「誰がどの権限を握り、どの secrets に触れ得るか」を**コードの構造と最小権限**で担保してきました。GuardDuty EKS Protection の導入も同じ思想で設計します——**①監査ログ検知を組織一括で漏れなく有効化し、②finding 型を RBAC ハードニング（匿名認証無効化・専用 SA・PSS・admission）に直結させ、③RBAC 侵害には資格情報失効と secrets ローテートを即実行できる対応を用意する**。検知を「赤いダッシュボード」で終わらせず、運用に載る仕組みまで作り切ります。

**「自社の EKS に GuardDuty EKS Protection をどう設計し、監査ログ検知と RBAC ハードニング・admission control・SOAR 対応をどう繋ぎ、Runtime Monitoring とどう役割分担するか」——コントロールプレーンの脅威検知から Terraform 実装、RBAC 最小権限化、インシデント対応まで、一人 ×生成AI（Claude Code）で速く・安全に伴走できます。** 要件の整理段階からでも、お気軽にご相談ください。

---

### 参考（公式ドキュメント）

- [GuardDuty EKS Protection](https://docs.aws.amazon.com/guardduty/latest/ug/kubernetes-protection.html) — EKS Protection の定義・**独立ストリームで監査ログを取り込み追加設定不要**・コントロールプレーンログとの分離・30日無料トライアル・リージョン対応
- [EKS Protection finding types](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-finding-types-eks-audit-logs.html) — `EKSCluster` の finding 型一覧と Default severity（本記事の重大度はここ準拠）
- [Remediating EKS Protection findings](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-remediate-kubernetes.html) — 匿名アクセス・compromised user/pod/node・`aws-auth` ConfigMap 編集・secrets ローテートの公式手順
- [Security best practices for Amazon EKS](https://docs.aws.amazon.com/eks/latest/userguide/security-best-practices.html) — `system:anonymous`/`system:unauthenticated` の権限剥奪・RBAC ハードニング
- [GuardDuty finding format](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_finding-format.html) — `ThreatPurpose:Resource/Family.Mechanism!Artifact` と ThreatPurpose の MITRE ATT&CK 対応
- [Severity levels of GuardDuty findings](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_findings-severity.html) — Critical/High/Medium/Low の数値帯（1.0〜10.0）
- [GuardDuty Extended Threat Detection](https://docs.aws.amazon.com/guardduty/latest/ug/guardduty-extended-threat-detection.html) — `AttackSequence:EKS/CompromisedCluster`・EKS Protection / Runtime Monitoring との関係
- [Amazon GuardDuty pricing](https://aws.amazon.com/guardduty/pricing/) — EKS Protection の監査ログ量ベース課金・30日無料トライアル・リージョン別（料金は要確認）
