タイトル
k8s の audit 設定をちゃんと理解する
目的
- api-server に設定する audit 機能を理解する
手段
- マニュアルベースで手を動かす
ざっくり理解する
- 必要な設定内容
- kube-apiserver に audit 関連のオプション設定追加
- kube-apiserver に audti 設定ファイルとロギング用をマウント
- audit 用の設定ファイル作成(yaml)
環境
- killercode 環境を活用
audit 機能とは?
audit 機能は k8s クラスター内の一連アクション監査ログを提供します。(いわゆる監査ログ)
kube-apiserver 内で処理されるリクエストを各ステージごとにロギングします。各ステージは以下の通りです。
- RequestReceived : audit handler はリクエストを受けたら可能な限り早くイベントを生成するステージ
- ResponseStarted : response header が送信され、response body が送信される前のステージ
- ResponseComplete : response body 送信が完了したステージ
- Panic : panic 発生時のステージ(エラーの様なイメージ)
また、どのような監査ログ(ログレベル)を記録するか設定ファイルで定義します。
- None : イベントを記録しません
- Metadata : リクエストのメタデータ(リクエストしたユーザ、タイムスタンプ、リソース、動作など)を記録します。リクエストやレスポンスのボディは記録しません
- Request : イベントのメタデータとリクエストボディを記録します。レスポンスボディは記録しません
- RequestResponse : イベントのメタデータ・リクエストとレスポンスのボディを記録します
audit 機能の設定方法
autdit 機能は kube-apiserver.yaml を用いて有効化します。必要設定は以下の通りです。
- --audit-policy-file : audit 設定ファイル
- --audit-log-path : audit ログ保存パス
- --audit-log-maxage : audit ログの最大保持日数を指定
- --audit-log-maxbackup : audit ログの最大保持ファイル数を指定
- --audit-log-maxsize : audit ログがローテーションされるまでの最大ファイルサイズを指定(1ファイルの最大サイズ)
controlplane $ diff kube-apiserver.yaml /root/kube-apiserver.yaml
43,46d42
< - --audit-log-maxsize=7
< - --audit-log-maxbackup=2
< - --audit-log-path=/etc/kubernetes/audit-logs/audit.log
< - --audit-policy-file=/etc/kubernetes/audit-policy/policy.yaml
# kube-apiserver.yaml にフラグを追加します
98,103d93
< - mountPath: /etc/kubernetes/audit-policy/policy.yaml
< name: audit
< readOnly: true
# kube-apiserver に監査ログ用の設定ファイルを readonly でマウントします
< - mountPath: /etc/kubernetes/audit-logs
< name: audit-log
< readOnly: false
# kube-apiserver に監査ログ保存ディレクトリをマウントします
131,138d120
< - name: audit
< hostPath:
< path: /etc/kubernetes/audit-policy/policy.yaml
< type: File
< - name: audit-log
< hostPath:
< path: /etc/kubernetes/audit-logs/
< type: DirectoryOrCreate
# 監査ログ設定ファイルとログディレクトリのボリュームを作成します
動作確認方法
動作確認方法は以下の通りです。
controlplane $ stat /etc/kubernetes/audit-logs/audit.log
File: /etc/kubernetes/audit-logs/audit.log
Size: 984661 Blocks: 1928 IO Block: 4096 regular file
Device: fc01h/64513d Inode: 1327002 Links: 1
Access: (0600/-rw-------) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2025-02-10 10:21:18.846192987 +0000
Modify: 2025-02-10 10:23:35.235664013 +0000
Change: 2025-02-10 10:23:35.235664013 +0000
Birth: -
# ログファイルの書き込み有無を確認
controlplane $ cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep audit-policy-file=/etc/kubernetes/audit-policy/policy.yaml
- --audit-policy-file=/etc/kubernetes/audit-policy/policy.yaml
controlplane $
controlplane $ cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep audit-log-path=/etc/kubernetes/audit-logs/audit.log
- --audit-log-path=/etc/kubernetes/audit-logs/audit.log
controlplane $
controlplane $ cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep audit-log-maxsize=7
- --audit-log-maxsize=7
controlplane $
controlplane $ cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep audit-log-maxbackup=2
- --audit-log-maxbackup=2
# kube-apiserver.yaml 内の設定値を確認
audit 用の yaml ファイルを作成する
まずは、Pods 関連のみロギングする。
controlplane $ cat policy.yaml
# /etc/kubernetes/audit/policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# log Secret resources audits, level Metadata
- level: Metadata
resources:
- group: ""
resources: ["pods"]
controlplane $ tail -f audit.log |grep pods
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"Metadata","auditID":"7f926292-edc9-48d0-8975-595ffb966675","stage":"RequestReceived","requestURI":"/api/v1/pods?fieldSelector=spec.nodeName%3Dcontrolplane\u0026resourceVersion=2545","verb":"list","user":{"username":"system:node:controlplane","groups":["system:nodes","system:authenticated"]},"sourceIPs":["172.30.1.2"],"userAgent":"kubelet/v1.31.0 (linux/amd64) kubernetes/9edcffc","objectRef":{"resource":"pods","apiVersion":"v1"},"requestReceivedTimestamp":"2025-02-13T11:20:35.906449Z","stageTimestamp":"2025-02-13T11:20:35.906449Z"}
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"Metadata","auditID":"7f926292-edc9-48d0-8975-595ffb966675","stage":"ResponseComplete","requestURI":"/api/v1/pods?fieldSelector=spec.nodeName%3Dcontrolplane\u0026resourceVersion=2545","verb":"list","user":{"username":"system:node:controlplane","groups":["system:nodes","system:authenticated"]},"sourceIPs":["172.30.1.2"],"userAgent":"kubelet/v1.31.0 (linux/amd64) kubernetes/9edcffc","objectRef":{"resource":"pods","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":200},"requestReceivedTimestamp":"2025-02-13T11:20:35.906449Z","stageTimestamp":"2025-02-13T11:20:35.908974Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""}}
# Pod関連ログのみロギングされている。
次に、ログレベルを Metadata
→ RequestResponse
に変更する。kube-apiserverをリスタート(Pod再作成)させるために kube-apiserver.yaml を編集して、kube-apiserverをリスタートさせるのが手間ですね。コマンドで実行したいです。
controlplane $ cat policy.yaml
# /etc/kubernetes/audit/policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# log Secret resources audits, level Metadata
- level: RequestResponse
resources:
- group: ""
resources: ["pods"]
# level を変更済み
controlplane $ tail -f audit.log |grep RequestResponse
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","auditID":"af23f5d2-c145-413d-a48a-09bb54425274","stage":"ResponseStarted","requestURI":"/api/v1/pods?allowWatchBookmarks=true\u0026resourceVersion=2962\u0026timeout=7m12s\u0026timeoutSeconds=432\u0026watch=true","verb":"watch","user":{"username":"system:kube-controller-manager","groups":["system:authenticated"]},"sourceIPs":["172.30.1.2"],"userAgent":"kube-controller-manager/v1.31.0 (linux/amd64) kubernetes/9edcffc/shared-informers","objectRef":{"resource":"pods","apiVersion":"v1"},"responseStatus":{"metadata":{},"code":200},"requestReceivedTimestamp":"2025-02-13T11:25:28.542419Z","stageTimestamp":"2025-02-13T11:25:28.542797Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":"RBAC: allowed by ClusterRoleBinding \"system:kube-controller-manager\" of ClusterRole \"system:kube-controller-manager\" to User \"system:kube-controller-manager\""}}
# "RequestResponse" レベルのログがロギングされている
続いて、namespace でロギング対象を制限してみます。
controlplane $ cat policy.yaml
# /etc/kubernetes/audit/policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# log Secret resources audits, level Metadata
- level: RequestResponse
resources:
- group: ""
resources: ["pods"]
namespaces: ["test"]
controlplane $ tail -f audit.log |grep test
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","auditID":"b13d607d-60f5-4640-9ada-8b222029b59f","stage":"RequestReceived","requestURI":"/api/v1/namespaces/test/pods?fieldManager=kubectl-run","verb":"create","user":{"username":"kubernetes-admin","groups":["kubeadm:cluster-admins","system:authenticated"]},"sourceIPs":["172.30.1.2"],"userAgent":"kubectl/v1.31.0 (linux/amd64) kubernetes/9edcffc","objectRef":{"resource":"pods","namespace":"test","apiVersion":"v1"},"requestReceivedTimestamp":"2025-02-13T11:42:39.035337Z","stageTimestamp":"2025-02-13T11:42:39.035337Z"}
# test namespace のみロギングされる。
続いて、omitSatges 設定とロギング設定をバッティングさせてみます。想定では、ロギングされない動作になると思います。
controlplane $ cat ../audit-policy/policy.yaml
# /etc/kubernetes/audit/policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
omitStages:
- "RequestResponse"
# log Secret resources audits, level Metadata
- level: RequestResponse
resources:
- group: ""
resources: ["pods"]
namespaces: ["test"]
# 結果的に、kube-apiserver は起動しませんでした。policy.yaml ファイルに不備があるためでしょう。
namespaces を "" 指定した場合の動作を確認します。"" はnamespace に所属しないオブジェクトを対象にするようです。
controlplane $ cat ../audit-policy/policy.yaml
# /etc/kubernetes/audit/policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# log Secret resources audits, level Metadata
- level: RequestResponse
resources:
- group: ""
resources: ["pods"]
namespaces: [""]
controlplane $ k run -n test nginx2 --image=nginx
pod/nginx2 created
# test に nginx2 を作成
controlplane $ tail -f ../audit-logs/audit.log |grep nginx2
# nginx2 pod 関連葉ロギングされません
最後のサンプル設定を読み解きます。
apiVersion: audit.k8s.io/v1 # This is required.
kind: Policy
# Don't generate audit events for all requests in RequestReceived stage.
omitStages:
- "RequestReceived"
# audit ポリシーは設定を上から適用するため、最初にomitStages で設定すれば、当該ステージのログはロギングされない
rules:
# Log pod changes at RequestResponse level
- level: RequestResponse
resources:
- group: ""
# Resource "pods" doesn't match requests to any subresource of pods,
# which is consistent with the RBAC policy.
resources: ["pods"]
# Log "pods/log", "pods/status" at Metadata level
- level: Metadata
resources:
- group: ""
resources: ["pods/log", "pods/status"]
# ロギング対象を pod 内のログやステータスに限定している
# Don't log requests to a configmap called "controller-leader"
- level: None
resources:
- group: ""
resources: ["configmaps"]
resourceNames: ["controller-leader"]
# ロギング対象をリソース名で限定しています
# Don't log watch requests by the "system:kube-proxy" on endpoints or services
- level: None
users: ["system:kube-proxy"]
verbs: ["watch"]
# ロギング対象を操作内容で限定しています
resources:
- group: "" # core API group
resources: ["endpoints", "services"]
# 複数オブジェクト存在する場合はカンマで区切って指定します
# Don't log authenticated requests to certain non-resource URL paths.
- level: None
userGroups: ["system:authenticated"]
nonResourceURLs:
- "/api*" # Wildcard matching.
- "/version"
# Log the request body of configmap changes in kube-system.
- level: Request
resources:
- group: "" # core API group
resources: ["configmaps"]
# This rule only applies to resources in the "kube-system" namespace.
# The empty string "" can be used to select non-namespaced resources.
namespaces: ["kube-system"]
# namespace でロギング対象を限定しています
# Log configmap and secret changes in all other namespaces at the Metadata level.
- level: Metadata
resources:
- group: "" # core API group
resources: ["secrets", "configmaps"]
# Log all other resources in core and extensions at the Request level.
- level: Request
resources:
- group: "" # core API group
- group: "extensions" # Version of group should NOT be included.
# A catch-all rule to log all other requests at the Metadata level.
- level: Metadata
# Long-running requests like watches that fall under this rule will not
# generate an audit event in RequestReceived.
omitStages:
- "RequestReceived"
# 最後のルールはその他ルールに該当しない全てのリクエストを対象とします。
# 基本、None にしたり、上記のように Metadata のみロギングするなど設定します。
audit log 出力時の deployment は group に apps を指定するべきか確認する。
controlplane $ cat ../audit-policy/policy.yaml
# /etc/kubernetes/audit/policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# log Secret resources audits, level Metadata
- level: RequestResponse
resources:
- group: "apps"
resources: ["deployments"]
namespaces: ["test"]
controlplane $ tail -f ../audit-logs/audit.log |grep dep
{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"RequestResponse","auditID":"8bdfc262-1d6d-42b6-97c9-8d77defb9045","stage":"RequestReceived","requestURI":"/apis/apps/v1/namespaces/test/deployments/nginx-deploy","verb":"get","user":{"username":"system:serviceaccount:kube-system:generic-garbage-collector","uid":"6fd135c6-2b99-483d-a01e-c5272aa913f2","groups":["system:serviceaccounts","system:serviceaccounts:kube-system","system:authenticated"],"extra":{"authentication.kubernetes.io/credential-id":["JTI=5da5661c-6262-4c8b-9554-22bcdad8008d"]}},"sourceIPs":["172.30.1.2"],"userAgent":"kube-controller-manager/v1.31.0 (linux/amd64) kubernetes/9edcffc/system:serviceaccount:kube-system:generic-garbage-collector","objectRef":{"resource":"deployments","namespace":"test","name":"nginx-deploy","apiGroup":"apps","apiVersion":"v1"},"requestReceivedTimestamp":"2025-02-13T12:09:19.131306Z","stageTimestamp":"2025-02-13T12:09:19.131306Z"}
group に "apps" を指定すべきかどうかは kubectl resources
コマンドの APIVERSION 列を確認すれば良いです。
controlplane $ kubectl api-resources |grep -e dep -e NAME
NAME SHORTNAMES APIVERSION NAMESPACED KIND
deployments deploy apps/v1 true Deployment
# deplyments リソースは `apps/v1` ですので、group に apps を指定する必要があります
controlplane $ kubectl api-resources |grep -e pod -e NAME
NAME SHORTNAMES APIVERSION NAMESPACED KIND
pods po v1 true Pod
# pods リソースh `v1` ですので、group は空で問題ありません
あとがき
ログレベルの書き方に注意が必要ですね。例えば、namespace 単位での設定方法など、設定単位を整理する必要がありそうです。