現在Kubernetesの証跡ログ(auditlog)周りを業務で扱っており、思ったより情報がなかった(もしかしたら基本なことなので書くまでも無いってこともありますが。。)ので記事にしてみようと思います。 以下寄り証跡ログのことをauditlogとして表現します。
Kubernetesのauditlogはデフォルトでは出力されず、kube-apiserverの起動時にパラメータを追加して起動してあげる必要があります。ここまでは割と事例があるので割愛します。 auditlogの出力ポリシーはGoogle Container-Optimized OSの監査プロファイルを参考に構築すれば大丈夫です。具体的にはconfigure-helper.shのスクリプトに書かれているのでこちらを参考にします。
今回はkubernetesをLimaVMで構築し、auditlogを有効化したクラスタの上で作業してみます。クラスタ自体はLimaVMのプリセットで存在する k8s
を利用します。VM自体の起動コマンドは以下になります。
VM名は k8s
としていますが、それ以外はデフォルト値で上がってきます。CPU: 4コア, メモリ: 4GB, ルートボリューム100GBのVMが出来上がるかと思います
$ limactl start --name=k8s template://k8s
このKubernetesクラスタはkubeadmで構築されており、kube-apiserverのマニフェストなどは /etc/kubernetes/manifests/kube-apiserver.yaml
に存在しており、起動時のオプションもここに記述する必要があります。
またauditlogを格納するボリュームのマウント、audit-policy.yamlもconfigとしてマウントしてあげる必要があるため、volumeMountなどの記載も必要になります。
brバニラで起動したkuberenetesクラスタのkube-apiserver.yamlをのdiffは以下のようになります。
diff <(limactl shell k8s-vanilla -- sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml) <(limactl shell k8s -- sudo cat /etc/kubernetes/manifests/kube-apiserver.yaml) 43c43,48 --- > - --audit-log-path=/var/log/kubernetes-audit/audit.log > - --audit-policy-file=/etc/kubernetes/manifests/audit-policy.yaml > - --audit-log-maxsize=100 > - --audit-log-maxbackup=10 > - --audit-log-maxage=7 93a99,104 > - mountPath: /etc/kubernetes/manifests/audit-policy.yaml > name: audit > readOnly: true > - mountPath: /var/log/kubernetes-audit > name: audit-log > readOnly: false 119a131,138 > - hostPath: > path: /etc/kubernetes/manifests/audit-policy.yaml > type: File > name: audit > - hostPath: > path: /var/log/kubernetes-audit > type: DirectoryOrCreate > name: audit-log
設定が問題なければ、kube-apiserver.yamlの保存ととともに、ファイルの変更をkubeletが検知して自動でkube-apiserverが再起動しauditlogを出力する状態になったクラスタが立ち上がります。 設定のどこかに問題があればkube-apiserverが再起動を繰り返すので、kube-apiserverやkubeletのログを確認しましょう。
上記の設定にしていれば、auditlogは /var/log/kubernetes-audit/audit.log
に出力されます。中はJSON形式になっており、出力されている量が多ければ100MB毎にローテートされているはずです。
出力されている内容の一部を切り出すと以下の内容になっていました。
{ "kind": "Event", "apiVersion": "audit.k8s.io/v1", "level": "Metadata", "auditID": "b6bf51ff-9ea1-40d8-98bb-a95b2a2ccd21", "stage": "ResponseStarted", "requestURI": "/api/v1/namespaces/kube-system/configmaps?allowWatchBookmarks=true&fieldSelector=metadata.name%3Dextension-apiserver-authentication&resourceVersion=655053&timeout=8m13s&timeoutSeconds=493&watch=true", "verb": "watch", "user": { "username": "system:kube-scheduler", "groups": [ "system:authenticated" ] }, "sourceIPs": [ "192.168.5.15" ], "userAgent": "kube-scheduler/v1.26.2 (linux/amd64) kubernetes/fc04e73", "objectRef": { "resource": "configmaps", "namespace": "kube-system", "name": "extension-apiserver-authentication", "apiVersion": "v1" }, "responseStatus": { "metadata": {}, "code": 200 }, "requestReceivedTimestamp": "2023-04-02T03:32:36.682654Z", "stageTimestamp": "2023-04-02T03:32:36.685631Z", "annotations": { "authorization.k8s.io/decision": "allow", "authorization.k8s.io/reason": "RBAC: allowed by RoleBinding \"system::extension-apiserver-authentication-reader/kube-system\" of Role \"extension-apiserver-authentication-reader\" to User \"system:kube-scheduler\"" } }
これらをPromtailで読み取るためにはscrape_configを作成する必要があります。JSONなのでpipeline_stagesでJSONを利用します。 k=vとなっている要素に対しては以下のように記述し、
- json: expressions: kind: kind apiVersion: apiVersion
要素が更にネストしている場合は再帰的にparseしてあげる必要があるので、sourceで指定して再帰的に読み取るような設定の追加が必要になります。例えば objectRefは要素が {k: {k: v}}
のようになっているため、
- json: expressions: objectRef: - json: expressions: resource: resource namespace: namespace name: name apiVersion: apiVersion source: objectRef
のような記述が必要となります。 記載が完了し、うまくauditlogをparse出来るようになっていれば、lokiにpromtail経由でlokiにデータが蓄積されるようになっているはずです。 今回はGrafana、Loki、Promtailに関して以下のチャートを利用しました。GrafanaとLokiに関してはLoki-Stackを用いて、Promtailだけは別立てしてLoki-Stackで構築したLokiにデータをpushする形を取っています。
問題なくデータがpushされていれば、GrafanaからデータをExprole出来るようになっているかと思います。Loki-Stackに同梱されているGrafanaを用いると無事表示されているのを確認出来るかと思います。
Promtailのscrape_configも無事Fieldsとして認識しているようです