kube-apiserverのAudit

Tetsuya Isogai
17 min readDec 12, 2020

kube-apiserverの監査ログを取ってみる

TL;DR

KubernetesのAPIサーバで監査ログ(Audit)を有効化することでユーザのアクティビティや不正な行為を検知することができるようになる。ただしチューニングや保管先の管理は大変。

どんな情報が取れるか

監査ログはJson形式で指定したファイル、Webhook等で出力が可能。以下はaudit-test-podというPodにkubectl execでログインした際の監査ログ。
誰が・いつ・どのリソースに対して・何をしたかどのレベルで保存するか等が詳細に記録される。

{"kind":"Event","apiVersion":"audit.k8s.io/v1","level":"Metadata","auditID":"15473afa-482e-419e-97b6-fd89e2364e1f","stage":"ResponseStarted","requestURI":"/api/v1/namespaces/1211/pods/audit-test-pod/exec?command=bash\u0026container=audit-test-pod\u0026stdin=true\u0026stdout=true\u0026tty=true","verb":"create","user":{"username":"kubernetes-admin","groups":["system:masters","system:authenticated"]},"sourceIPs":["10.0.1.132"],"userAgent":"kubectl/v1.19.2 (linux/amd64) kubernetes/f574309","objectRef":{"resource":"pods","namespace":"1211","name":"audit-test-pod","apiVersion":"v1","subresource":"exec"},"responseStatus":{"metadata":{},"code":101},"requestReceivedTimestamp":"2020-12-12T04:55:17.134391Z","stageTimestamp":"2020-12-12T04:55:17.148723Z","annotations":{"authorization.k8s.io/decision":"allow","authorization.k8s.io/reason":""}}

試した環境

Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.2", GitCommit:"f5743093fd1c663cb0cbc89748f730662345d44d", GitTreeState:"clean", BuildDate:"2020-09-16T13:41:02Z", GoVersion:"go1.15", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.2", GitCommit:"f5743093fd1c663cb0cbc89748f730662345d44d", GitTreeState:"clean", BuildDate:"2020-09-16T13:32:58Z", GoVersion:"go1.15", Compiler:"gc", Platform:"linux/amd64"}

クラスタはkubeadmで構築しています。(kube-apiserverがPodとして起動している状態)

監査ログを取り始める前に決めること

  • 監査ログポリシー。何を監査対象にするか(対象のリソース、ステージ)、どれくらいの細かさで出力するか(レベル)。
  • ログの保管先。

今回は動作確認のため保管先はローカルディスクに構成。

ポリシーの内容について詳しく知りたい人は@makocchiさんのCNDT2019発表資料に詳しく載っています。(https://speakerdeck.com/makocchi/cndt2019-kubernetes-audit-log-c4d4c5f6-6058-40f9-a5fc-abbb36073a19)

監査ログを動作させるためにやること

  1. 監査ポリシーを記載したYamlファイルを作成しマスターサーバに配置する。 (後述A)
  2. kube-apiserver のマニフェストに監査設定を行う。kube-apiseverの起動オプション— audit-policy-fileを追加する。当該Yamlファイルへのパスを追加し、監査ログを有効化する。(後述B)
  3. kube-apiserver/etc/kubernetes/auditに当該ファイルをマウントさせるようvolumesvolumeMounts を設定する。
  4. 合わせてログの保存先の設定も同じくkube-apiserverのマニフェストに追記する。(後述C)
  5. kube-apiserverが再起動されるのを待ち、監査ログが出力されていることを確認する。

A:監査ポリシー記述ファイル

ポリシーのサンプルは公式に色々なパターンが載っている。

apiVersion はaudit.k8s.io/v1

apiVersion: audit.k8s.io/v1 # This is required.
kind: Policy

omitStages は出力させたくないステージを指定する。

omitStages:
- "RequestReceived"

監査ログ収集のルールを rules セクションに記述していく。 level は出力の細かさ(Metadataのみ、ボディも含む等)の指定、resources は監査対象のリソースを指定する。

rules:
- level: RequestResponse
resources:
- group: ""
resources: ["pods"]

以下のポリシーは「すべての監査イベントをMetadataレベルで収集する」というルールになる。

apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata

B: kube-apiserverでの監査ポリシーを有効化

kube-apiserverの引数には前述の通り最低限audit-policy-fileを追加する。audit-policy.yamlは上記AのYamlファイル。

--audit-policy-file=/etc/kubernetes/audit-policy.yaml

このaudit-policy-file はパラメータを指定するだけではなく、Podにマウントさせる必要がある。したがって同ファイルをvolumeMounts およびhostPathをつかってkube-apiseverにマウントさせる。

volumeMounts:
- mountPath: /etc/kubernetes/audit-policy.yaml
name: audit
readOnly: true
...
name: audit
hostPath:
path: /etc/kubernetes/audit-policy.yaml
type: File

C:ログの保存先

ここではkube-apiserverが動くノードのhostPathに保存するものとする。

ログの保存先を --audit-log-pathで指定する。( -は標準出力となるため既にFluentd等でログ収集をしている場合は集約が可能なものと思われる(未確認))
保存量を制御するパラメータとして --audit-log-maxsize--audit-log-maxage があるため必要に応じて構成する。

kube-apisever起動パラメータの指定例:

spec:
containers:
- command:
- kube-apiserver
- --audit-policy-file=/etc/kubernetes/audit/policy.yaml
- --audit-log-path=/etc/kubernetes/audit/logs/audit.log
- --audit-log-maxsize=500
- --audit-log-maxbackup=5

Volumeの指定も忘れずに行う:
保存先となるファイルはreadOnlyfalseにしておくこと。

volumeMounts:
...
- mountPath: /var/log/audit.log
name: audit-log
readOnly: false
...
- hostPath:
path: /etc/kubernetes/audit
type: DirectoryOrCreate
name: audit

トラブルシューティング

設定反映にはkube-apiserverを再起動させる必要があるためエラーがあったときはkube-apiseverが起動してこない場合がある。その場合は/var/log/pods/ 以下にあるkube-apiserverのログを見て起動しない原因を確認する。自分の場合は大概はaudit-policy.yamlの誤りとかだった。

注意事項

ログの中に表示されるシークレット情報をMetadataより細かいレベルで取得した場合、はbase64のデータそのままの状態で保存されるため、そこから情報を抜き出されるリスクがあります。センシティブな情報のログを取得する場合はログのレベルおよび結果として保存される情報を確かめるようにしましょう。

参考

最後に実際のテストで使った各種ファイルを張り付けしておく。

/etc/kubernetes/audit/policy.yaml

apiVersion: audit.k8s.io/v1
kind: Policy
omitStages:
- "RequestReceived"
rules:
- level: None
verbs: ["get", "watch", "list"]
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
- level: Metadata

/etc/kubernetes/manifest/kube-apiserver.yaml

apiVersion: v1
kind: Pod
metadata:
annotations:
kubeadm.kubernetes.io/kube-apiserver.advertise-address.endpoint: 10.0.1.132:6443
changedate: 20201210-1043
creationTimestamp: null
labels:
component: kube-apiserver
tier: control-plane
name: kube-apiserver
namespace: kube-system
spec:
containers:
- command:
- kube-apiserver
- --audit-policy-file=/etc/kubernetes/audit/policy.yaml
- --audit-log-path=/etc/kubernetes/audit/logs/audit.log
- --audit-log-maxsize=500
- --audit-log-maxbackup=5
- --admission-control-config-file=/etc/kubernetes/admission/admission_config.yaml
- --anonymous-auth=true
- --advertise-address=10.0.1.132
- --allow-privileged=true
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction,PodSecurityPolicy,ImagePolicyWebhook
- --enable-bootstrap-token-auth=true
- --etcd-cafile=/etc/kubernetes/pki/etcd/ca.crt
- --etcd-certfile=/etc/kubernetes/pki/apiserver-etcd-client.crt
- --etcd-keyfile=/etc/kubernetes/pki/apiserver-etcd-client.key
- --etcd-servers=https://127.0.0.1:2379
- --insecure-port=8080
- --kubelet-client-certificate=/etc/kubernetes/pki/apiserver-kubelet-client.crt
- --kubelet-client-key=/etc/kubernetes/pki/apiserver-kubelet-client.key
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
- --proxy-client-cert-file=/etc/kubernetes/pki/front-proxy-client.crt
- --proxy-client-key-file=/etc/kubernetes/pki/front-proxy-client.key
- --requestheader-allowed-names=front-proxy-client
- --requestheader-client-ca-file=/etc/kubernetes/pki/front-proxy-ca.crt
- --requestheader-extra-headers-prefix=X-Remote-Extra-
- --requestheader-group-headers=X-Remote-Group
- --requestheader-username-headers=X-Remote-User
- --secure-port=6443
- --service-account-key-file=/etc/kubernetes/pki/sa.pub
- --service-cluster-ip-range=10.96.0.0/12
- --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
- --tls-private-key-file=/etc/kubernetes/pki/apiserver.key
image: k8s.gcr.io/kube-apiserver:v1.19.2
imagePullPolicy: IfNotPresent
livenessProbe:
failureThreshold: 8
httpGet:
host: 10.0.1.132
path: /livez
port: 6443
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
name: kube-apiserver
readinessProbe:
failureThreshold: 3
httpGet:
host: 10.0.1.132
path: /readyz
port: 6443
scheme: HTTPS
periodSeconds: 1
timeoutSeconds: 15
resources:
requests:
cpu: 250m
startupProbe:
failureThreshold: 24
httpGet:
host: 10.0.1.132
path: /livez
port: 6443
scheme: HTTPS
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 15
volumeMounts:
- mountPath: /etc/ssl/certs
name: ca-certs
readOnly: true
- mountPath: /etc/ca-certificates
name: etc-ca-certificates
readOnly: true
- mountPath: /etc/kubernetes/pki
name: k8s-certs
readOnly: true
- mountPath: /usr/local/share/ca-certificates
name: usr-local-share-ca-certificates
readOnly: true
- mountPath: /usr/share/ca-certificates
name: usr-share-ca-certificates
readOnly: true
- mountPath: /etc/kubernetes/admission
name: k8s-admission
readOnly: true
- mountPath: /etc/kubernetes/audit
name: audit
readOnly: false

hostNetwork: true
priorityClassName: system-node-critical
volumes:
- hostPath:
path: /etc/ssl/certs
type: DirectoryOrCreate
name: ca-certs
- hostPath:
path: /etc/ca-certificates
type: DirectoryOrCreate
name: etc-ca-certificates
- hostPath:
path: /etc/kubernetes/pki
type: DirectoryOrCreate
name: k8s-certs
- hostPath:
path: /usr/local/share/ca-certificates
type: DirectoryOrCreate
name: usr-local-share-ca-certificates
- hostPath:
path: /usr/share/ca-certificates
type: DirectoryOrCreate
name: usr-share-ca-certificates
- hostPath:
path: /etc/kubernetes/admission
type: DirectoryOrCreate
name: k8s-admission
- hostPath:
path: /etc/kubernetes/audit
type: DirectoryOrCreate
name: audit

--

--

Tetsuya Isogai

Working at Microsoft/Cloud Solution Architect/Azure Core Infra