🛡️

GuardDuty EKS Runtime Monitoringの導入

2023/07/28に公開

EKS Runtime Monitoring

https://aws.amazon.com/jp/blogs/news/amazon-guardduty-now-supports-amazon-eks-runtime-monitoring/

3月30日、Amazon GuardDuty EKS ランタイムモニタリングの一般提供についてお知らせします。30 件以上のセキュリティの検出結果からランタイムの脅威を検出して EKS クラスターを保護できます。新しい EKS ランタイムモニタリングは、ファイルアクセス、プロセス実行、ネットワーク接続など、個々のコンテナランタイムアクティビティの可視化を追加するフルマネージド EKS アドオンを使用しています。

https://docs.aws.amazon.com/guardduty/latest/ug/runtime-monitoring.html

ランタイムモニタリングは、オペレーティングシステムレベルのイベントを監視および分析して、環境内の特定の AWS ワークロードにおける潜在的な脅威を検出するのに役立ちます。ランタイムモニタリングは、以前は Amazon Elastic Kubernetes Service (Amazon EKS) リソースでのみ利用可能でしたが、GuardDuty は現在、ランタイムモニタリング機能を拡張して、Amazon Elastic Container Service (Amazon ECS) および Amazon Elastic Compute Cloud (Amazon EC2) リソースの脅威検出を提供しています。

大ざっぱにいえばEKS Audit Log Monitoringがコントロールプレーン担当で、(EKS) Runtime Monitoringがデータプレーン担当になる。

料金

vCPUの数に対して課金されるほか、VPCエンドポイントを用意することになるため、その料金が別途かかる。またDaemonSetをデプロイするためEC2の料金が増える可能性がある。

https://aws.amazon.com/jp/guardduty/pricing/

エージェントコンテナのresourcesはこんなもん

resources

使用開始にあたって決めること

GuardDutyにDaemonSetの管理を任せるか、自分で管理するかを選択する。任せると、EKSアドオンの作成(と更新)、VPCエンドポイントの作成が自動的に行われる。

※任せる設定でEKS Runtime Monitoringを利用開始後、利用停止(無効化した)場合、EKSアドオンもVPCエンドポイントも自動的に削除される(無意味にお金がかかり続けることはない)

任せない場合EKSアドオンの作成(と更新)、VPCエンドポイントの作成を自分で行う。

EKSクラスターとアドオンはTerraformでIaC化されている環境のため、今回は自分で管理することにした。

有効化

AWS Organizationsを利用しているため、GuardDuty管理アカウントからメンバーアカウントに対して有効化する例を載せる。

GuardDuty > Accounts

enable

自分で管理することにしたためmanage agent automatically有効化しない

※現時点ではTerraformプロバイダーがメンバーアカウントのdetector管理に対応していないため手動でコンソールから有効化している

https://github.com/hashicorp/terraform-provider-aws/issues/26168

EKSアドオンとVPCエンドポイントの作成

Terraformのサンプル

data "aws_caller_identity" "caller" {}
data "aws_region" "current" {}

resource "aws_vpc_endpoint" "eks_runtime_monitoring" {
  vpc_id              = var.vpc_id
  service_name        = "com.amazonaws.${data.aws_region.current.name}.guardduty-data"
  vpc_endpoint_type   = "Interface"
  subnet_ids          = var.subnet_ids
  security_group_ids  = var.security_group_ids
  private_dns_enabled = true
  policy              = data.aws_iam_policy_document.eks_runtime_monitoring.json
}

data "aws_iam_policy_document" "eks_runtime_monitoring" {
  statement {
    actions   = ["*"]
    resources = ["*"]
    effect    = "Allow"
    principals {
      type        = "*"
      identifiers = ["*"]
    }
  }
  statement {
    actions   = ["*"]
    resources = ["*"]
    effect    = "Deny"
    principals {
      type        = "*"
      identifiers = ["*"]
    }
    condition {
      test     = "StringNotEquals"
      variable = "aws:PrincipalAccount"
      values   = [data.aws_caller_identity.caller.account_id]
    }
  }
}

VPCエンドポイントに付与するポリシーやセキュリティグループの要件は以下を参照

https://docs.aws.amazon.com/guardduty/latest/ug/eks-runtime-monitoring-security-agent-manual.html

resource "aws_eks_addon" "guardduty_agent" {
  cluster_name      = var.cluster_name
  addon_name        = "aws-guardduty-agent"
  addon_version     = "v1.2.0-eksbuild.1"
  resolve_conflicts = "OVERWRITE"
}

EKS Runtime Monitoringを有効化のうえアドオンを作成するとこうなる。

image

amazon-guardduty Namespaceにaws-guardduty-agent DaemonSetができている。

image

PriorityClassも作成され、それなりの優先度を持っているが、descriptionpreemptionPolicyにある通り自分より低優先度のPodを押しのけることはしない

image

※EKS Runtime Monitoringの有効化前にアドオンを作成するとこうなる(EKS Runtime Monitoringがまだ使えないというだけでクラスターの運用に支障はない)

image

検出結果(一例)

datadog-agentのprocess-agentコンテナ(およびagentコンテナ)に対してPrivilegeEscalation:Runtime/DockerSocketAccessedが検出された。

image

image

調査

/var/run/docker.sock

と書いてあるが、その時点でクラスターではKubernetes 1.26が実行されていたため、すでにcontainerdに移行済みだった。

https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/dockershim-deprecation.html

docker.sockの謎

Session Managerでノードに入ってみると/var/run/docker.sockは存在したがソケットではなくディレクトリだった。

image

中身はない。

image

このディレクトリが何者なのかの疑問は解消できなかった。このissueが関係あるかもしれない。

https://github.com/moby/moby/issues/30348

なんにしろソケットの存在確認(checkSocketExists)のときに失敗するため、通信には使われないはず。

https://github.com/DataDog/datadog-agent/blob/883ec0a720533b17b3dfecd046ca73d3d7ae590e/pkg/config/environment_containers.go#L80-L85

https://github.com/DataDog/datadog-agent/blob/d05e41f7d2e7cd92c3df2a618410a31bfe19a880/pkg/util/system/socket.go#L21-L24

https://github.com/DataDog/datadog-agent/blob/d05e41f7d2e7cd92c3df2a618410a31bfe19a880/pkg/util/system/socket.go#L45-L56

https://go.dev/play/p/Ie8ne01K-nW

Datadogのコンテナの/host/var/runにはノードの/var/runがマウントされている。

image

image

ノードには/var/run/containerd/containerd.sockが存在し、Datadogコンテナの/host/var/run/containerd/containerd.sockに同じものがある。

image

image

なので実際の通信はcontainerd.sockを通じて発生していると考えた。
この通信はContainerdインテグレーションに必要なものであるため、抑制ルールを作成して自動的にアーカイブすることにした。

https://docs.aws.amazon.com/guardduty/latest/ug/findings_suppression-rule.html

抑制ルールを作成すると以後同じ条件の検出結果は自動的にアーカイブされるようになる。

対応(抑制ルールの作成)

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/guardduty_filter

すべてのクラスターでDatadog Agentはdatadog Namespaceにデプロイしているため、datadog Namespaceで検出されたDockerSocketAccessedは抑制するルールにする。

resource "aws_guardduty_filter" "datadog_dockersocketaccessed" {
  rank        = 4
  detector_id = aws_guardduty_detector.ap_northeast_1.id
  action      = "ARCHIVE"
  name        = "datadog-DockerSocketAccessed"

  finding_criteria {
    criterion {
      field  = "resource.kubernetesDetails.kubernetesWorkloadDetails.namespace"
      equals = ["datadog"]
    }
    criterion {
      field  = "type"
      equals = ["PrivilegeEscalation:Runtime/DockerSocketAccessed"]
    }
  }
}

fieldの書き方はここを見る(Filter attributesの表)。

https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_filter-findings.html#filter_criteria

rankはproviderの必須argument(v5.31.0現在)ですが、AWS APIでは必須ではありません

https://github.com/hashicorp/terraform-provider-aws/issues/34470

おまけ:抑制ルール例

Argo CDによるRole/RoleBinding作成・変更への警告を抑制

resource "aws_guardduty_filter" "argocd_rbac" {
  rank        = 5
  detector_id = aws_guardduty_detector.ap_northeast_1.id
  action      = "ARCHIVE"
  name        = "argocd-rbac"

  finding_criteria {
    criterion {
      field  = "resource.kubernetesDetails.kubernetesUserDetails.username"
      equals = [
        # in-clusterな操作のusername
        "system:serviceaccount:argocd:argocd-application-controller",
        # remote clusterへの操作のusername
        # https://argo-cd.readthedocs.io/en/stable/operator-manual/declarative-setup/#eks
        "iam-role-name"
      ]
    }
    criterion {
      field  = "type"
      equals = [
        "PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleCreated",
        "PrivilegeEscalation:Kubernetes/AnomalousBehavior.RoleBindingCreated"
      ]
    }
  }
}
criteriaの仕様

https://docs.aws.amazon.com/guardduty/latest/ug/guardduty_filter-findings.html#filter_console

Each filter criteria attribute is evaluated as an AND operator. Multiple values for the same attribute are evaluated as AND/OR.

criterion同士はANDで、equals内のそれぞれはORです。

https://github.com/argoproj/argo-cd/blob/6eba5be864b7e031871ed7698f5233336dfe75c7/cmd/argocd-k8s-auth/commands/aws.go#L84
https://github.com/aws/aws-sdk-go/blob/f703d5e91c5099ba5e4606f7283b177192eb9d31/aws/credentials/stscreds/assume_role_provider.go#L311

Discussion