EKS コントロールプレーンの監査(Audit)ログを眺めてみた

AWS
AWS
この記事は約21分で読めます。

EKS クラスターのコントロールプレーンのログ記録を行なったので、実際にどのようなログが出力されているかを眺めてみました。

Amazon EKS コントロールプレーンのログ記録 - Amazon EKS
Amazon EKS クラスターのログ記録を設定する方法について説明します。

Audit(監査)ログ

Kubernetes 監査ログで、誰が、いつ、どのような操作を行ったか分かります。
出力先は、CloudWatch Logs ロググループ /aws/eks/${ClusterName}/cluster のログストリーム kube-apiserver-audit-xxx です。

以下のようなログがありました。

{
    "kind": "Event",
    "apiVersion": "audit.k8s.io/v1",
    "level": "Request",
    "auditID": "8db7597c-7fd8-4b8f-b2f7-24b059402413",
    "stage": "ResponseComplete",
    "requestURI": "/api/v1/nodes?limit=100",
    "verb": "list",
    "user": {
        "username": "eks:k8s-metrics",
        "groups": [
            "system:authenticated"
        ]
    },
    "sourceIPs": [
        "172.16.42.46"
    ],
    "userAgent": "eks-k8s-metrics/v0.0.0 (linux/amd64) kubernetes/$Format",
    "objectRef": {
        "resource": "nodes",
        "apiVersion": "v1"
    },
    "responseStatus": {
        "metadata": {},
        "code": 200
    },
    "requestReceivedTimestamp": "2023-02-18T16:07:50.628915Z",
    "stageTimestamp": "2023-02-18T16:07:50.655406Z",
    "annotations": {
        "authorization.k8s.io/decision": "allow",
        "authorization.k8s.io/reason": "RBAC: allowed by ClusterRoleBinding \"eks:k8s-metrics\" of ClusterRole \"eks:k8s-metrics\" to User \"eks:k8s-metrics\""
    }
}

ClusterRole eks:k8s-metrics で定義した権限を持つユーザー eks:k8s-metrics の list リクエストが成功しているログです。
なお、eks が頭につくリソースは、EKS が作成したものです。

ログの構成は EKS 固有のものではないため、 Kubernetes のドキュメントに記載されています。

kube-apiserver Audit Configuration (v1)
Resource Types Event EventList Policy PolicyList Event Appears in: EventList Event captures all the information that can be included in an API audit log. FieldD...

なお、annotations の authorization.k8s.io/reason に ClusterRole などの記載がありますが、ClusterRole はどのリソースに対してどのような操作を行えるか定義し、ClusterRoleBinding はどのユーザーに ClusterRole を関連づけるかを定義します。

Using RBAC Authorization
Role-based access control (RBAC) is a method of regulating access to computer or network resources based on the roles of individual users within your organizati...

上記以外のログも見てみます。
以下では、メトリクスフィルタで検索しています。

{$.user.username != "eks:*" && $.user.username != "system:*"}

一番最初に出てきたログは以下です。

{
    "kind": "Event",
    "apiVersion": "audit.k8s.io/v1",
    "level": "Metadata",
    "auditID": "bea85de7-6a2f-4659-ad5e-1000040c5eb9",
    "stage": "ResponseComplete",
    "requestURI": "/api?timeout=32s",
    "verb": "get",
    "user": {
        "username": "kubernetes-admin",
        "uid": "aws-iam-authenticator:${AccountId}:${UserId}",
        "groups": [
            "system:masters",
            "system:authenticated"
        ],
        "extra": {
            "accessKeyId": [
                "xxx"
            ],
            "arn": [
                "arn:aws:iam::${AccountId}:user/${UserName}"
            ],
            "canonicalArn": [
                "arn:aws:iam::${AccountId}:user/${UserName}"
            ],
            "sessionName": [
                ""
            ]
        }
    },
    "sourceIPs": [
        "xxx"
    ],
    "userAgent": "kubectl/v1.25.4 (darwin/amd64) kubernetes/872a965",
    "responseStatus": {
        "metadata": {},
        "code": 200
    },
    "requestReceivedTimestamp": "2023-02-18T12:56:00.139027Z",
    "stageTimestamp": "2023-02-18T12:56:00.169420Z",
    "annotations": {
        "authorization.k8s.io/decision": "allow",
        "authorization.k8s.io/reason": ""
    }
}

user や userAgent などの情報から、IAM ユーザーの認証情報を利用して kubectl により get リクエストを行い成功したことがわかります。
なお、username が kubernetes-admin となっているのは、EKS クラスターを作成した IAM がその名前にマッピングされるからです。

fields @logstream, @timestamp, @message
| sort @timestamp desc
| filter @logStream like /authenticator/
| filter @message like "username=kubernetes-admin"
| limit 50

このクエリは、クラスター作成者としてマッピングされている IAM エンティティを返します。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/eks-api-server-unauthorized-error/

上記の根拠以外にも、EKS クラスターを作成した IAM に対して、 username に kubernetes-admin をマッピングしている以下内容の Authenticator のログ(監査ログとは別)がありましたので、現時点の仕様と思われます。

time="2023-02-18T12:11:42Z" level=info msg="mapping IAM user" groups="[system:masters]" user="arn:aws:iam::xxx:user/xxx" username=kubernetes-admin

その他の監査ログも見ていきます。

{$.user.username != "eks:*" && $.user.username != "system:authenticated" && $.user.username != "system:kube*" && $.user.username != "system:apiserver" && $.user.username != "kubernetes-admin"}

system:anonymous からのアクセスがありました。

{
    "kind": "Event",
    "apiVersion": "audit.k8s.io/v1",
    "level": "Metadata",
    "auditID": "2cf47a89-48e2-4175-b0f5-f9956c5706c5",
    "stage": "ResponseComplete",
    "requestURI": "/",
    "verb": "get",
    "user": {
        "username": "system:anonymous",
        "groups": [
            "system:unauthenticated"
        ]
    },
    "sourceIPs": [
        "193.118.53.210"
    ],
    "userAgent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36",
    "responseStatus": {
        "metadata": {},
        "status": "Failure",
        "message": "forbidden: User \"system:anonymous\" cannot get path \"/\"",
        "reason": "Forbidden",
        "details": {},
        "code": 403
    },
    "requestReceivedTimestamp": "2023-02-18T12:57:51.698570Z",
    "stageTimestamp": "2023-02-18T12:57:51.703155Z",
    "annotations": {
        "authorization.k8s.io/decision": "forbid",
        "authorization.k8s.io/reason": ""
    }
}

system:anonymous は、他の認証方法で拒否されなかったリクエストに与えられるユーザー名です。system:anonymous は何の権限も設定していないので、上記のように拒否されています。

API サーバエンドポイントの設定をパブリックにしており、誰でもアクセスできる状況であったため上記のようなログが出力されていました。

ちなみに userAgent が Go-http-client/1.1 であるログの方が多かったです。

最後に、作成したノードからのリクエストのログも見ていきます。

{ $.user.username = "system:node:*" }
{
    "kind": "Event",
    "apiVersion": "audit.k8s.io/v1",
    "level": "Request",
    "auditID": "ead13ca8-683d-4c79-bdd4-290dbfdc6ead",
    "stage": "ResponseComplete",
    "requestURI": "/apis/storage.k8s.io/v1/csidrivers?limit=500&resourceVersion=0",
    "verb": "list",
    "user": {
        "username": "system:node:ip-10-0-1-188.ap-northeast-1.compute.internal",
        "uid": "aws-iam-authenticator:${AccountId}:{UserId}",
        "groups": [
            "system:bootstrappers",
            "system:nodes",
            "system:authenticated"
        ],
        "extra": {
            "accessKeyId": [
                "xxx"
            ],
            "arn": [
                "arn:aws:sts::${AccountId}:assumed-role/EKSNodeRole/i-0f154c84ebe55ea8e"
            ],
            "canonicalArn": [
                "arn:aws:iam::${AccountId}:role/EKSNodeRole"
            ],
            "sessionName": [
                "i-0f154c84ebe55ea8e"
            ]
        }
    },
    "sourceIPs": [
        "43.206.153.252"
    ],
    "userAgent": "kubelet/v1.24.9 (linux/amd64) kubernetes/53e5004",
    "objectRef": {
        "resource": "csidrivers",
        "apiGroup": "storage.k8s.io",
        "apiVersion": "v1"
    },
    "responseStatus": {
        "metadata": {},
        "code": 200
    },
    "requestReceivedTimestamp": "2023-02-18T14:30:51.417018Z",
    "stageTimestamp": "2023-02-18T14:30:51.547512Z",
    "annotations": {
        "authorization.k8s.io/decision": "allow",
        "authorization.k8s.io/reason": ""
    }
}

sourceIPs がパブリック IP アドレスになっていますが、これは先ほども述べたように API サーバーエンドポイントの設定がパブリックだったからです。

プライベート設定にすれば EKS が自動的に Route53 プライベートホストゾーンを作成するので、sourceIPs が EC2 のプライベート IP アドレスになると思います。

クラスターでエンドポイントへのプライベートアクセスを有効にすると、Amazon EKS によって自動的に Route 53 のプライベートホストゾーンが作成され、クラスターの VPC に関連付けられます。このプライベートホストゾーンは Amazon EKS によって管理され、アカウントの Route 53 リソースには表示されません。

https://docs.aws.amazon.com/ja_jp/eks/latest/userguide/cluster-endpoint.html

次に、Fargate ノードからのログを見てみます。

{ $.user.username = "system:node:fargate*" }
{
    "kind": "Event",
    "apiVersion": "audit.k8s.io/v1",
    "level": "Request",
    "auditID": "27dbd8cd-63bd-4d33-a3a0-ade847cf232a",
    "stage": "ResponseComplete",
    "requestURI": "/api/v1/nodes?fieldSelector=metadata.name%3Dip-10-0-205-154.ap-northeast-1.compute.internal&limit=500&resourceVersion=0",
    "verb": "list",
    "user": {
        "username": "system:node:fargate-ip-10-0-3-37.ap-northeast-1.compute.internal",
        "uid": "aws-iam-authenticator:${AccountId}:${UserId}",
        "groups": [
            "system:bootstrappers",
            "system:nodes",
            "system:node-proxier",
            "system:authenticated"
        ],
        "extra": {
            "accessKeyId": [
                "xxx"
            ],
            "arn": [
                "arn:aws:sts::${AccountId}:assumed-role/EKSFargateRole/fargate-ip-10-0-3-37.ap-northeast-1.compute.internal"
            ],
            "canonicalArn": [
                "arn:aws:iam::${AccountId}:role/EKSFargateRole"
            ],
            "sessionName": [
                "fargate-ip-10-0-3-37.ap-northeast-1.compute.internal"
            ]
        }
    },
    "sourceIPs": [
        "35.76.81.254"
    ],
    "userAgent": "kube-proxy/v1.24.9 (linux/amd64) kubernetes/83fb24d",
    "objectRef": {
        "resource": "nodes",
        "name": "ip-10-0-205-154.ap-northeast-1.compute.internal",
        "apiVersion": "v1"
    },
    "responseStatus": {
        "metadata": {},
        "code": 200
    },
    "requestReceivedTimestamp": "2023-02-18T15:19:46.663650Z",
    "stageTimestamp": "2023-02-18T15:19:46.698231Z",
    "annotations": {
        "authorization.k8s.io/decision": "allow",
        "authorization.k8s.io/reason": ""
    }
}

EC2 のノードと同様ですね。

エラーが発生した際のログを確認するには、responseStatus.code の値を調査するのが良さそうです。
以下はメトリクスフィルタの例ですが、実際の調査では CloudWatch Logs Insights を利用することになりそうです。クエリの例は、AWS のナレッジセンターで紹介されています。

{ $.responseStatus.code = "4*" || $.responseStatus.code = "5*"}
タイトルとURLをコピーしました