2019年6月19日 星期三

kubernetes 內建的 audit 機制

官方資料裡有關於 audit 尚稱完整的說明,不過在實際上與設定上還是有一些需要摸索的地方,在這邊做個紀錄。

audit 的功能,是透過 kubernetes API Server 來做,所以首先是要修改 apiserver 的設定。如果是用 kubeadm 安裝,設定檔是在 /etc/kubernetes/manifests/kube-apiserver.yaml

不過在修改設定檔之前,要先設定好 audit 相關的 policy file 提供給 apiserver ,因為 apiserver 是屬於 static pod ,一修改設定就會直接重啟 pod。

Policy 設定的方式稍後再說,先用官方提供的最簡範例
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata

為了方便測試 (稍後說明),將上面內容存放在 /etc/kubernetes/pki/audit-policy.yaml ,然後就可以開始修改 apiserver 的設定檔了。

修改的內容最主要是開啟 audit 及相關參數,這邊先只設定必要的參數,分別是 policy file 的路徑、audit log 的路徑、以及 log 的格式
    - --audit-policy-file=/etc/kubernetes/pki/audit-policy.yaml
    - --audit-log-path=/var/log/kube-audit
    - --audit-log-format=json

前面提到 policy file 放置的路徑是為了方便,原因在於 apiserver 是一個 pod , pods 裡是看不到 host 的檔案的,而觀察一下 apiserver 的 yaml 設定,可以發現 /etc/kubernetes/pki 這個路徑是有掛載在 pod 裡,所以為了方便,把 policy file 放在這邊就不需要額外設定,就可以讓 apiserver 取得。如前所述,修改完設定檔後,apiserver pod 會自動重啟。

接下來用 kubectl exec 的方式進到 apiserver pod ,確認一下 pod 裡的 /var/log/kube-audit 是否有產生。以上面設定的 policy 內容來說,會把所有事件的 metadata 都記錄下來,所以如果有設定正確的話,應該會看到 audit log 如雪片般的飛來。

不過把 log file 放在 pod 裡,這樣不容易管理與稽核,這時有四種做法。

第一種是把 audit log 改送到 stdout (修改 log path 的設定變成 --audit-log-path=-),這樣就會和其他的 container 一樣,會把 log 寫在 host 的 /var/lib/docker/containers/ 裡。不過這種做法,會因為 apiserver 的 log format 是 json ,要和其他 container log 一起管理會產生差異,所以我放棄這種方式。

第二種方式是透過 sidecar。有些 app/image 沒有把 log 丟到 stdout/stderr ,就要透過 sidecar 的方式去收集 log ,常見的做法是把 fluentd 作為 sidecar 來搜集 log 。不過因為 apiserver 是 static pod,我也不太傾向用 sidecar 的方式來處理。

第三種是把 audit log 設定為 webhook ,把 log 送到指定的 url 。因為 apiserver static pod ,這種方式需要在 cluster 外部設定好可以收 log 的 webhook ,如 logstash 或 fluentd 等。因為我目標是把 log 整合在 cluster  內部的 app ,所以也沒採取這種方式。

最後一種方式就是直接把 host 上的路徑掛載到 apiserver 上產生 log 的路徑下。
例如我規劃把 audit log 會寫到 host 的 /var/log/kubernetes/ 下,就修改 apiserver 設定檔,增加內容如下
# 指定 host 路徑
  - hostPath:
      path: /var/log/kubernetes
      type: DirectoryOrCreate
    name: k8s-audit
# 指定掛載路徑
    - mountPath: /var/log
      name: k8s-audit
設定正確的話,就可以看到 host 的 /var/log/kuberentes/kube-audit 裡的 audit log 如雪片般的飛來。

在思考如何把 audit log 和其他 log 一起管理之前,先來調整一下 policy file ,否則 log file 成長的會非常快。

官網上對於 policy file 有一些基本的介紹,不過這篇寫得比較完整

設定檔裡面的幾個主要的項目:

* omitStages
  指 audit log 發出來的時機
  選項有 RequestReceived / ResponseStarted / ResponseComplete / Panic

* level
  記錄 log 內容的完整程度,None 就是不紀錄
  選項有 None / Metadata / Request / RequestResponse

然後參考 rules 裡的其他設定項,如 verbs、users、resources 等,仔細看會發現和 kuberntes RBAC 的設定方式一樣,例如 verbs 的選項有
["get", "list", "watch", "create", "update", "patch", "delete"]


所以假設今天要設定的規則是,只記錄 namespace 的 create 和 delete,其他通通不記錄,那 rules 的寫法會如下
apiVersion: audit.k8s.io/v1beta1
kind: Policy
omitStages:
  - "RequestReceived"
rules:
  - level: Request
    verbs: ["create", "delete"]
    resources:
    - group: ""
      resources: ["namespaces"]
  - level: None

到這邊可能遇到兩個問題

* 修改 policy file 並不會讓 apiserver pod 重啟,如果用 kubectl delete pod 的方式對 static pod 也是無效的,所以最簡單的方式,就是把 apiserver 的設定檔移到別的路徑下再移回來,就可以讓 apiserver pod 重啟了。

* 如果 master node 不止一台,而且有用 haproxy 之類的擋在前面做 load balance ,就會遇到和我一樣的問題。因為對 api server 的 request 是會輪詢的,所以要測試時會出現非預期的結果。我一開始沒注意到這個問題,想說一樣的 kubectl create ns 指令,為什麼有時候有產生 audit log ,有時候沒有。後來修改 kubectl context 直接指向有變更設定的 apiserver ,就可以很正常的依測試產生 audit log 了。


audit 的部份到這邊告一段落,接下來把 host 上面的 audit log 和其他 log 整合一起,就是另一個故事了。


沒有留言: