Kubernetesダッシュボードのインターネット公開を検出するGuardDutyイベントを発生させてみた

2022.04.29

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

コンサル部のとばち(@toda_kk)です。

先日、GuardDutyがAmazon EKSに対応し、EKSクラスターにおける脅威を検出できるようになりました。

検出できる脅威のタイプ(Finding Types)のうちPolicy:Kubernetes/ExposedDashboardがあります。これは、EKSクラスターにおいてKubernetesダッシュボードがインターネットに公開されたことを検出するイベントです。

実際にどんなイベントが検出されるのか確認したかったので、本記事ではKubernetesダッシュボードをインターネットに公開する手順について記載します。

Kubernetesダッシュボードとは?

Kubernetesダッシュボードは、Kubernetesリソースを管理するためのWebベースのUIです。

Amazon EKSやKubernetesクラスターには通常、デフォルトでは含まれていませんが、デプロイのためのマニフェストファイルが公式で用意されています。

Kubernetesダッシュボードのデプロイ

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.0/aio/deploy/recommended.yaml

Kubernetesクラスターにダッシュボードをデプロイすることで、ブラウザ上からコンテナをデプロイしたりKubernetesリソースを修正したりといった操作が可能になります。より詳細な情報については、下記のKubernetes公式ドキュメントをご参照ください。

Policy:Kubernetes/ExposedDashboardの発生条件

AWS公式ドキュメントには、下記のように記載されています。

This finding informs you that Kubernetes dashboard for your cluster was exposed to the internet by a Load Balancer service. An exposed dashboard makes the management interface of your cluster accessible from the internet and allows adversaries to exploit any authentication and access control gaps that may be present.

KubernetesダッシュボードがLoadBalancerタイプのServiceを通じてインターネットに公開されたときに発生するようです。

KubernetesではServiceというリソースを通じてPodへのルーティングや負荷分散を実現します。Serviceにはさまざまなタイプがありますが、このうちLoadBalancerタイプでServiceリソースを作成してKubernetesダッシュボードをインターネットに公開することが、GuardDutyイベントPolicy:Kubernetes/ExposedDashboardの発生条件となっているようです。

Kubernetesダッシュボードをインターネットに公開する手順

前提として、EKSクラスターはすでに作成済みとします。eksctlによるクラスター作成については、下記の記事をご参照ください。

今回はtest-eksという名前でEKSクラスターを作成しておきます。

その上で、EKSクラスターでKubernetesダッシュボードをデプロイします。単にデプロイしてローカルからアクセスするだけなら上述のコマンドを実行すれば良いのですが、今回はServiceを通じてインターネットに公開したいので、マニフェストファイルをダウンロードした上でServiceリソースの設定を書き換えます。

マニフェストファイルのダウンロード

$ curl -O https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.0/aio/deploy/recommended.yaml

Servicekubernetes-dashboardspec.typeの行を追加し、LoadBalancerを指定します。

recommended.yaml

# Serviceリソースを抜粋
kind: Service
apiVersion: v1
metadata:
  labels:
    k8s-app: kubernetes-dashboard
  name: kubernetes-dashboard
  namespace: kubernetes-dashboard
spec:
    type: LoadBalancer # ←ここを追加する
  ports:
    - port: 443
      targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard

その上で、kubectl applyを実行してKubernetesダッシュボードをデプロイします。

Kubernetesダッシュボードのデプロイ

$ kubectl apply -f recommended.yaml

なお、上記手順の作成のために、下記ページを参考にしました。

GuardDutyイベントの確認

上記のマニフェストファイルをapplyすると、EKSクラスターの監査ログ(Audit)からGuardDutyがKubernetesダッシュボードのインターネット公開を検知し、下記のようなJSON形式のイベントが作成されます。

下記は2022年4月現在のものになります。フォーマットなど今後変更になる可能性もあるのでご注意ください。

Policy:Kubernetes/ExposedDashboard

[
    {
        "AccountId": "123456789012",
        "Arn": "arn:aws:guardduty:ap-northeast-1:123456789012:detector/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/finding/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "CreatedAt": "2022-04-05T04:00:50.722Z",
        "Description": "The Kubernetes Dashboard in EKS Cluster test-eks was made accessible to the Internet. If this behavior is not expected, it may indicate a configuration mistake or that your credentials are compromised.",
        "Id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "Partition": "aws",
        "Region": "ap-northeast-1",
        "Resource": {
            "AccessKeyDetails": {
                "AccessKeyId": "ASIAXXXXXXXXXXXXXXXX",
                "PrincipalId": "AROAXXXXXXXXXXXXXXXXX",
                "UserName": "user-name",
                "UserType": "Role"
            },
            "EksClusterDetails": {
                "Name": "test-eks",
                "Arn": "arn:aws:eks:ap-northeast-1:123456789012:cluster/test-eks",
                "VpcId": "vpc-xxxxxxxxxxxxxxxxx",
                "Status": "ACTIVE",
                "Tags": [
                    {
                        "Key": "aws:cloudformation:stack-name",
                        "Value": "eksctl-test-eks-cluster"
                    },
                    {
                        "Key": "alpha.eksctl.io/cluster-name",
                        "Value": "test-eks"
                    },
                    {
                        "Key": "aws:cloudformation:stack-id",
                        "Value": "arn:aws:cloudformation:ap-northeast-1:123456789012:stack/eksctl-test-eks-cluster/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
                    },
                    {
                        "Key": "eksctl.cluster.k8s.io/v1alpha1/cluster-name",
                        "Value": "test-eks"
                    },
                    {
                        "Key": "aws:cloudformation:logical-id",
                        "Value": "ControlPlane"
                    },
                    {
                        "Key": "alpha.eksctl.io/eksctl-version",
                        "Value": "0.90.0"
                    }
                ],
                "CreatedAt": "2022-04-05T01:02:44.349Z"
            },
            "KubernetesDetails": {
                "KubernetesUserDetails": {
                    "Username": "kubernetes-admin",
                    "Uid": "heptio-authenticator-aws:123456789012:AROAXXXXXXXXXXXXXXXXX",
                    "Groups": [
                        "system:masters",
                        "system:authenticated"
                    ]
                }
            },
            "ResourceType": "EKSCluster"
        },
        "SchemaVersion": "2.0",
        "Service": {
            "Action": {
                "ActionType": "KUBERNETES_API_CALL",
                "KubernetesApiCallAction": {
                    "RequestUri": "/api/v1/namespaces/kubernetes-dashboard/services/kubernetes-dashboard",
                    "Verb": "patch",
                    "UserAgent": "kubectl/v1.22.4 (darwin/amd64) kubernetes/b695d79",
                    "RemoteIpDetails": {
                        "City": {
                            "CityName": "Chiyoda-ku"
                        },
                        "Country": {
                            "CountryName": "Japan"
                        },
                        "GeoLocation": {
                            "Lat": xx.xxxx,
                            "Lon": xxx.xxxx
                        },
                        "IpAddressV4": "xxx.xxx.xxx.xxx",
                        "Organization": {
                            "Asn": "1234",
                            "AsnOrg": "XXXXXXXXXX",
                            "Isp": "XXXXXXXXXX",
                            "Org": "XXXXXXXXXX"
                        }
                    },
                    "StatusCode": 200,
                    "Parameters": "{\"kind\":\"Service\",\"metadata\":{\"name\":\"kubernetes-dashboard\",\"namespace\":\"kubernetes-dashboard\",\"uid\":\"xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx\",\"resourceVersion\":\"37041\",\"creationTimestamp\":\"2022-04-05T02:11:24Z\",\"labels\":{\"k8s-app\":\"kubernetes-dashboard\"},\"managedFields\":[{\"manager\":\"kubectl-client-side-apply\",\"operation\":\"Update\",\"apiVersion\":\"v1\",\"time\":\"2022-04-05T03:59:48Z\",\"fieldsType\":\"FieldsV1\",\"fieldsV1\":{\"f:metadata\":{\"f:annotations\":{\".\":{},\"f:kubectl.kubernetes.io/last-applied-configuration\":{}},\"f:labels\":{\".\":{},\"f:k8s-app\":{}}},\"f:spec\":{\"f:externalTrafficPolicy\":{},\"f:ports\":{\".\":{},\"k:{\\\"port\\\":443,\\\"protocol\\\":\\\"TCP\\\"}\":{\".\":{},\"f:port\":{},\"f:protocol\":{},\"f:targetPort\":{}}},\"f:selector\":{\".\":{},\"f:k8s-app\":{}},\"f:sessionAffinity\":{},\"f:type\":{}}}}]}}"
                }
            },
            "Archived": false,
            "Count": 1,
            "DetectorId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
            "EventFirstSeen": "2022-04-05T03:59:48.638Z",
            "EventLastSeen": "2022-04-05T03:59:48.665Z",
            "ResourceRole": "TARGET",
            "ServiceName": "guardduty",
            "AdditionalInfo": {
                "Value": "{}",
                "Type": "default"
            }
        },
        "Severity": 5,
        "Title": "The Kubernetes Dashboard was exposed to the Internet.",
        "Type": "Policy:Kubernetes/ExposedDashboard",
        "UpdatedAt": "2022-04-05T04:00:50.722Z"
    }
]

LoadBalancerタイプのServiceで公開したときのみ通知されるので注意

上述の通り、脅威のタイプがPolicy:Kubernetes/ExposedDashboardのイベントの発生条件は、「KubernetesダッシュボードがLoadBalancerタイプのServiceを通じてインターネットに公開されたとき」です。

そのため、LoadBalancer以外のServiceを通じてKubernetesダッシュボードをインターネットに公開したとしても、GuardDutyは通知してくれません。

また、AWS Load Balancer Controllerなどを利用してIngressリソースを通じてKubernetesダッシュボードをインターネットに公開したとしても、同様に通知されません。

つまり、あくまでもLoadBalancerタイプのServiceを通じてインターネットに公開されたときに通知される、という点にご注意ください。

以上、コンサル部のとばち(@toda_kk)でした。