メインコンテンツへスキップ
友田 陽大
Amazon GuardDuty 本番運用
セキュリティ
AWS
GuardDuty
EKS
Kubernetes

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(データプレーン)との違いも明確化。

公開日
読了時間
30分
著者
友田 陽大
シェア

「あなたの 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 上のサーバーレス決済プラットフォームで IAM・可観測性・DR を横断実装した経験——実際の金銭・カーボンクレジット・地域通貨を扱うため、「誰が何の権限を握っているか」を運用の注意深さではなく仕組みで監視する必要があった——という視点も交えます。

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


0. メンタルモデル:監査ログ(コントロールプレーン)か、ランタイム(データプレーン)か

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

EKS Protection = Kubernetes 監査ログを解析し、「どの API 呼び出しが kube-apiserver に飛んだか」=コントロールプレーンの操作を検知する。 「誰が create rolebinding したか」「system:anonymous で API が叩かれたか」を見る目。

Runtime Monitoring = 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 課金という別の設計が乗ります(詳細はデータプレーン記事)。

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

観点EKS Protection(本記事)Runtime Monitoring
見る面コントロールプレーン(監査ログ)データプレーン(OS レベルの挙動)
データ源EKS 監査ログ(API 呼び出し)eBPF エージェントのランタイムイベント
問い「どの API 呼び出しが起きたか」「コンテナの中で何が動いたか」
エージェント不要(エージェントレス)必要(DaemonSet / SSM / サイドカー)
feature 名EKS_AUDIT_LOGSRUNTIME_MONITORING
resource typeEKSClusterInstance / コンテナ系
代表 findingPolicy:Kubernetes/AnonymousAccessGrantedExecution:Runtime/ReverseShell
攻撃シーケンスEKS のシーケンスに寄与EKS/ECS/EC2 のシーケンスに寄与

本記事はこの表の左列を深掘りします。右列はデータプレーン記事に譲ります。GuardDuty 全体の地図(基盤検知・保護プラン選定・組織統制・EventBridge 自動対応)はピラー記事が土台です。


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 に送る機能)を有効化する必要はありません。これはピラー記事で見た「基盤データソース(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 ProtectionGuardDuty が脅威検知のために監査ログを解析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 章で見たとおり、型名に意味がエンコードされているので、先頭の ThreatPurpose を見れば「攻撃のどの段階か」が即わかります。ここを ThreatPurpose 別に整理すると、自動対応のルーティング設計がそのまま見えてきます。

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

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

  • .AnomalousBehavior = GuardDuty の ML モデルがベースラインからの逸脱を検知したもの。「このユーザーが、この場所から、この namespace で、この API を叩くのは普段ない」を捉えます。
  • .Custom = あなたがアップロードした脅威リスト(custom threat list)の IP に一致したもの(ピラー記事 7 章の脅威リスト)。
  • それ以外(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)。以下は公式の Default severity をそのまま転記しています。

ThreatPurpose(MITRE ATT&CK 戦術)finding 型重大度何を意味するか
Policy(設定ポリシー違反)Policy:Kubernetes/AnonymousAccessGrantedHighsystem:anonymous を role に束ねる RoleBinding/ClusterRoleBinding が作られた=匿名 API アクセスを許可した
Policy:Kubernetes/AdminAccessToDefaultServiceAccountHighnamespace の default サービスアカウントに admin 権限が付いた(pod が意図せず admin で起動し得る)
Policy:Kubernetes/ExposedDashboardMediumKubernetes Dashboard が LB 経由でインターネット公開された
Policy:Kubernetes/KubeflowDashboardExposedMediumKubeflow Dashboard がインターネット公開された
CredentialAccess(認証情報・secrets 窃取)CredentialAccess:Kubernetes/AnomalousBehavior.SecretsAccessedMediumsecrets 取得 API が異常な形(ML 逸脱)で呼ばれた
CredentialAccess:Kubernetes/SuccessfulAnonymousAccessHigh認証情報系 API が匿名ユーザーで叩けた
CredentialAccess:Kubernetes/{MaliciousIPCaller, MaliciousIPCaller.Custom, TorIPCaller}High認証情報系 API が悪性 IP / Tor 出口から叩かれた
PrivilegeEscalation(特権昇格)PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleBindingCreatedMediumadmin/cluster-admin 絡みは High過剰権限 role への RoleBinding/ClusterRoleBinding が異常な形で作成・改変された
PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleCreatedLow過剰権限の Role/ClusterRole が異常な形で作成された(admin 系を避けつつ検知回避)
PrivilegeEscalation:Kubernetes/AnomalousBehavior.WorkloadDeployed!PrivilegedContainerHigh特権コンテナを含むワークロードが異常な形で起動された(root でホストにアクセス可)
Persistence(永続化)Persistence:Kubernetes/AnomalousBehavior.WorkloadDeployed!ContainerWithSensitiveMountHigh機微なホストパスを volumeMount したワークロードが異常な形で起動された
Persistence:Kubernetes/SuccessfulAnonymousAccessHigh永続化系の高権限 API が匿名ユーザーで叩けた
Persistence:Kubernetes/{MaliciousIPCaller, MaliciousIPCaller.Custom, TorIPCaller}Medium永続化系 API が悪性 IP / Tor から叩かれた
Execution(実行)Execution:Kubernetes/ExecInKubeSystemPodMediumkube-system namespace の pod 内でコマンドが exec された(システムコンポーネントへの干渉)
Execution:Kubernetes/AnomalousBehavior.ExecInPodMediumpod 内 exec が異常な形(ユーザー/namespace/pod が普段と違う)で行われた
Execution:Kubernetes/AnomalousBehavior.WorkloadDeployedLow(疑わしいイメージ名/コマンドなら Mediumワークロードが異常な形で作成・改変された
Discovery(偵察)Discovery:Kubernetes/AnomalousBehavior.PermissionCheckedLowkubectl auth can-i 等で自分の権限を異常な形で確認した(昇格前の下見)
Discovery:Kubernetes/SuccessfulAnonymousAccessMedium偵察系 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 章で触れたとおり、抑制で消すと攻撃シーケンスの材料まで消える点に注意します。

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

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

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

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

# ❌ アンチパターン: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."

具体手順に落とします。

# ① 何が 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 させない」ガードを敷きます。

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

# 攻撃者(または侵害された認証情報)がこういう 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 経由のユーザーならアクセスキーをローテート、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."

# ✅ 正しい形:ワークロードごとに専用 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 を作るのは定番です。

# 攻撃者の典型ムーブ:侵害したサービスアカウントに 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."

# 対応:不正な 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 プロファイルで予防できます。

# 予防: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 章で作った EventBridge → 冪等な自動対応の配管に流します。

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

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

# 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 ガード+人間のレビューに寄せるのが安全側の設計です(ピラー記事の「破壊的操作は人間を挟む」を EKS でより厳格に適用)。SOAR 連携の全体像は自動対応の専用記事へ。

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 ユーザー/ロールアクセスキーをローテートし、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/ワークロードを削除し、クラスタを既知の正常状態へ戻す。

決済基盤での教訓:私が決済プラットフォームで 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)。

# 前提: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 章の委任管理者構成に、EKS_AUDIT_LOGS の自動有効化を足します。

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

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

私はマルチアカウントのサーバーレス決済プラットフォームで、実際の金銭・カーボンクレジット・地域通貨を扱う基盤の 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)で速く・安全に伴走できます。 要件の整理段階からでも、お気軽にご相談ください。


参考(公式ドキュメント)

友田

友田 陽大

経済産業大臣賞 受賞プロダクト開発者。TypeScript + Python + AWS で、SaaS・業界DX・ 実用レベルの生成AI(RAG)を、要件定義からインフラ・運用まで一人で完遂します。

この記事の実装を、案件として承ります

EKS / コンテナワークロードのセキュリティ強化

EKS 監査ログ(コントロールプレーン)と Runtime Monitoring(データプレーン)の両面で、RBAC 改変・特権昇格・ランタイム侵害を検知。攻撃シーケンス相関まで含めた設計を伴走します。

プロジェクト単位(請負)・技術顧問のどちらにも対応可能です。まずは30分の無料技術相談から。