Falco 作為雲原生執行時安全專案,能有效偵測容器中的異常行為。本文將詳細說明如何在 Kubernetes 叢集中佈署 Falco 並整合 EFK 堆積疊,實作全面的安全稽核。首先,Falco 需要存取主機的 /dev 檔案系統以載入核心模組,這透過在 KinD 容器啟動時掛載 /dev:/dev 磁碟區,以及在 Falco DaemonSet 中設定 hostPath 磁碟區來實作。佈署 Falco DaemonSet 時,需建立對應的 Service Account、Service 和 ConfigMap,確保 Falco 能夠正常運作並讀取設定檔。設定 EFK 流程時,先佈署 Elasticsearch 作為日誌儲存中心,再利用 Fluentd 收集容器日誌並轉發至 Elasticsearch。為了更有效地處理 Falco 事件,我們引入 Falcosidekick,它能將 Falco 事件格式化後直接傳送到 Elasticsearch,簡化整合流程。最後,佈署 Kibana 並設定 Ingress 規則,即可透過視覺化儀錶板監控和分析 Falco 產生的安全事件,提升 Kubernetes 叢集的安全性。
使用 Falco 和 EFK 進行稽核
在之前的章節中,我們提到了 KinD 有一個功能,可以在啟動 KinD 容器時掛載額外的磁碟區。在我們的安裝過程中,我們增加了一個掛載點 /dev:/dev,它會在我們的容器內建立一個掛載點,指向主機的 /dev 檔案系統。如果我們檢視主機的 /dev 檔案系統,我們會看到 Falco 的條目,如下所示:
cr
---
-
---
- 1 root root 244, 0 May 4 00:58 falco0
這就是 Falco Pod 在啟動時將使用的模組。
Falco 如何存取 /dev 檔案系統
但是,我們剛剛提到 /dev 已經被掛載在我們的 KinD 容器中,指向主機的 /dev 檔案系統。那麼,Kubernetes 叢集中的容器如何存取 /dev 檔案系統呢?
如果我們檢視下一節中將使用的 Falco DaemonSet 檔案,我們會看到該 manifest 建立了幾個掛載點給 Pod。其中一個 volumeMount 的條目如下:
- mountPath: /host/dev
name: dev-fs
readOnly: true
這個 volumeMount 條目使用了一個在 volumes 部分宣告的磁碟區:
- name: dev-fs
hostPath:
path: /dev
當 Falco Pod 啟動時,它將把 Pod 的 /dev 掛載到 KinD 容器的 /dev 掛載點。最後,KinD 容器的 /dev 掛載點被掛載到 Docker 主機的 /dev,而 Falco 模組就位於其中。
佈署 Falco DaemonSet
如果您要從 GitHub 倉函式庫執行 install-falco.sh 指令碼,Falco 將使用本文中提供的相同步驟進行安裝。在本文的 GitHub 倉函式庫中,所有 Falco 檔案都位於 chapter12 目錄中。
手動佈署 Falco DaemonSet
要手動佈署 Falco DaemonSet,您需要佈署 install 目錄中的三個 manifest,並使用 falco-config 目錄中的內容建立一個 Secret。
建立 Falco Service Account 和 Service
由於我們希望在專用的 namespace 中執行 Falco,因此我們需要在叢集中建立一個名為 falco 的 namespace。執行以下命令:
kubectl create ns falco
像所有 Kubernetes 應用程式一樣,我們需要建立一個具有正確 RBAC 許可權的帳戶,以便應用程式執行必要的任務。我們的第一步是建立一個 Service Account,它將用於在 DaemonSet 佈署中分配 RBAC 許可權:
- 使用
kubectl建立 Service Account:
kubectl apply -f falco/install/falco-account.yaml -n falco
- 接下來,我們需要為 Falco 建立一個 Service。所包含的
falco-service.yaml檔案將在 TCP 連線埠8765上建立一個新的 Service。使用kubectl套用 manifest:
kubectl apply -f falco/install/falco-service.yaml -n falco
- Falco 使用檔案進行基本組態和規則。由於我們正在 Kubernetes 中執行 Falco,因此我們需要將檔案儲存在 Kubernetes 物件中,以便 Falco Pod 可以使用它們。要將檔案儲存在 ConfigMap 中,請使用
falco-config目錄中的所有檔案建立一個名為falco-config的新 ConfigMap:
kubectl create configmap falco-config --from-file=falco/falco-config -n falco
重點注意事項
如果您需要在佈署 Falco 後修改任何組態檔案,您應該刪除 ConfigMap,並使用新更新的檔案重新建立它。更新 ConfigMap 後,您還需要重新啟動每個 Falco Pod,以從 ConfigMap 載入更新的檔案。
- 最後一步是佈署 DaemonSet:
kubectl apply -f falco/install/falco-daemonset-configmap.yaml -n falco
一旦 Falco Pod(s) 正在執行,您可以透過檢視 Pod 的日誌來驗證其健康狀態。輸出將類別似於以下輸出(錯誤是預期的,Falco 正在嘗試在所有位置找到核心模組,其中一些不存在,從而導致 “錯誤”):
成功的 Falco Pod 啟動日誌
現在,您已經設定了一個 Falco DaemonSet,它將對您的 Pod 中的事件進行稽核。
重點注意事項
您可能會在 Falco Pod 日誌的最後一行收到錯誤,類別似於以下範例:
Tue May 5 20:38:14 2020: Runtime error: error opening device /host/dev/falco0. Make sure you have root credentials and that the falco-probe module is loaded. Exiting.
在這種情況下,您的 Falco 模組可能未被載入,因此請傳回到 modprobe 步驟並再次執行它們。您不應該需要重新啟動 Falco Pod,因為更改將被捕捉,並且 Falco 將在可以看到 /dev 目錄中的模組後開始記錄日誌。
當然,為了使其有用,我們需要將事件轉發到中央日誌系統。在預設佈署中,Falco 日誌僅在每個主機上執行的 Pod 上可用。如果您有 30 個主機,您將有 30 個唯一的 Falco 日誌,每個主機上都有一個。在分散式系統中查詢事件就像大海撈針一樣困難。
Falco 日誌使用標準輸出,因此我們可以輕鬆地將日誌轉發到任何第三方日誌系統。雖然我們有很多選項可供選擇作為日誌伺服器,但我們選擇使用 Elasticsearch、Fluentd 和 Kibana(EFK)以及 Falcosidekick 將日誌轉發。
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
skinparam rectangleBackgroundColor #F5F5F5
skinparam rectangleBorderColor #333333
skinparam arrowColor #333333
title 重點注意事項
rectangle "logs" as node1
rectangle "forward" as node2
rectangle "visualize" as node3
node1 --> node2
node2 --> node3
@enduml
圖表翻譯: 此圖表示Falco產生的日誌透過Fluentd轉發到Elasticsearch,最後由Kibana進行視覺化展示。
詳細解說
上述的流程圖展示了Falco、Fluentd、Elasticsearch以及Kibana之間的互動關係。Falco負責產生日誌並輸出到標準輸出,Fluentd負責收集這些日誌並將其轉發到Elasticsearch進行儲存,最後由Kibana提供視覺化介面來查詢和分析這些日誌。整個流程實作了對容器環境下安全事件的監控和回應。
使用Falco和EFK進行稽核
佈署EFK
首先,我們需要佈署Elasticsearch來接收事件資料。要安裝Elasticsearch,我們需要持久化儲存資料。幸運的是,我們正在使用KinD叢集,因此我們有Rancher的本地供應器提供的持久化儲存。
為了簡化佈署過程,我們將使用Bitnami的Helm圖表來佈署Elasticsearch和Kibana。你需要在叢集中安裝Helm二進位制檔案來佈署圖表。如果你正在按照書中的練習進行操作,那麼你應該已經在第5章的KinD佈署中安裝了Helm3。
驗證Helm安裝
透過執行helm version命令來驗證你是否已經安裝並執行了Helm。如果Helm已經安裝在你的路徑中,你應該會收到一個包含你正在執行的Helm版本的回應:
version.BuildInfo{Version:"v3.2.0",
GitCommit:"e11b7ce3b12db2941e90399e874513fbd24bcb71",
GitTreeState:"clean", GoVersion:"go1.13.10"}
如果收到錯誤,你需要在繼續之前重新安裝Helm。
執行佈署指令碼
在GitHub儲存函式庫中,我們包含了一個用於佈署EFK的指令碼。該指令碼名為install-logging.sh,位於chapter12/logging目錄中。與之前的指令碼一樣,我們將詳細介紹指令碼的內容和執行的命令。
建立新的名稱空間
由於我們可能希望將存取許可權委託給集中的日誌記錄團隊,因此我們將建立一個名為logging的新名稱空間:
kubectl create ns logging
將圖表倉函式庫新增到Helm
由於我們將使用Helm從Bitnami佈署圖表,因此我們需要將Bitnami圖表倉函式庫新增到Helm中。你可以使用helm repo add <repo name> <repo url>命令新增圖表倉函式庫:
helm repo add bitnami https://charts.bitnami.com/bitnami
你應該會收到Bitnami已新增到你的倉函式庫中的確認資訊:
"bitnami" has been added to your repositories
新增Bitnami倉函式庫後,你就可以開始從Bitnami倉函式庫佈署圖表了。
佈署Elasticsearch圖表
Elasticsearch佈署將資料儲存在持久化磁碟上。我們希望控制所建立磁碟的大小,因此在helm install命令中傳遞值以將大小限制為1 GB。
要使用選項佈署Bitnami的Elasticsearch,請使用以下helm install命令。我們只為安裝設定了幾個值,但與任何Helm圖表一樣,有一個長長的選項列表允許我們自定義安裝。對於我們的示例佈署,我們只設定了持久化卷大小為1 GB,資料副本數量為2。我們還希望將圖表佈署在logging名稱空間中,因此我們還增加了--namespace logging選項:
helm install elasticsearch bitnami/elasticsearch --set master.persistence.size=1Gi,data.persistence.size=1Gi,data.replicas=2 --namespace logging
內容解密:
此命令用於安裝Elasticsearch圖表,並設定持久化儲存的大小和資料副本數量。其中,master.persistence.size和data.persistence.size設定了主節點和資料節點的持久化儲存大小,而data.replicas則設定了資料副本的數量。最後,--namespace logging指定了佈署的名稱空間。
一旦你開始佈署圖表,你將收到關於vm.max_map_count核心設定的警告。對於我們的KinD叢集,包含的initContainer將在工作節點上設定此值。在生產環境中,你可能不允許特權pod執行,這將導致initContainer失敗。如果你不允許在叢集中執行特權pod(這是一個非常好的想法),你需要在佈署Elasticsearch之前手動在每個主機上設定此值。
你可以使用kubectl檢查佈署的狀態,驗證所有pod是否處於執行狀態,然後再繼續下一步:
kubectl get pods -n logging
你應該會收到以下輸出:
Figure 12.7 – Elasticsearch pod list
如我們所見,Helm圖表建立了幾個Kubernetes物件。主要物件包括以下內容:
- Elasticsearch伺服器pod(
elasticsearch-elasticsearch-coordinating-only) - Elasticsearch資料StatefulSet(
elasticsearch-elasticsearch-data-x) - Elasticsearch主StatefulSet(
elasticsearch-elasticsearch-master-x)
每個StatefulSet都為建立的每個pod建立了一個1 GB的PersistentVolumeClaim。我們可以使用kubectl get pvc -n logging檢視PVC,生成以下輸出:
Figure 12.8 – PVC list used by Elasticsearch
由於Elasticsearch僅由其他Kubernetes物件使用,因此建立了三個ClusterIP服務。我們可以使用kubectl get services -n logging檢視服務,生成以下輸出:
Figure 12.9 – Elasticsearch services
透過檢視pod、服務和PVC,我們可以確認圖表佈署成功,並且可以繼續進行下一個元件Fluentd。
佈署Fluentd
我們在GitHub儲存函式庫中的chapter12/logging目錄中包含了一個Fluentd佈署。
Fluentd是一種常見的日誌轉發器,用於將Kubernetes的日誌轉發到中央位置。我們正在安裝它以將Kubernetes日誌轉發到Elasticsearch,以提供一個完整的EFK佈署示例。我們的Falco事件將使用Falcosidekick轉發。
組態Fluentd
要將Fluentd佈署到叢集中,第一步是應用Fluentd組態。 fluentd-config.yaml檔案將建立一個ConfigMap,其中包含Fluentd佈署的組態選項。
組態Fluentd超出了本文的範圍。要使用Fluentd轉發日誌,我們需要解釋ConfigMap中的output.conf部分,該部分組態了Fluentd將日誌傳送到的主機。
在 fluentd-config.yaml 檔案中,在檔案的底部,你會看到一個名為 output.conf 的部分:
Figure 12.10 – Fluentd output configuration
你可以看到,我們為id和type設定了elasticsearch的選項,並且host設定已設定為 elasticsearch-elasticsearch-coordinating-only.logging.svc。如果你回頭看幾頁並檢視 kubectl get services -n logging 命令的輸出,你會在輸出中看到具有該名稱的服務。這是在與Elasticsearch佈署互動時必須定位的服務:
elasticsearch-elasticsearch-coordinating-only ClusterIP 10.107.207.18
請注意,我們還在主機名中增加了名稱空間和svc。由於Fluentd DaemonSet將安裝到 kube-system 名稱空間,因此要與其他名稱空間中的服務進行通訊,我們需要提供服務的完整名稱。在我們的KinD叢集中,我們不需要在 hostname 值中新增叢集名稱。
我們可以使用 kubectl apply 佈署ConfigMap:
kubectl apply -f fluentd-config.yaml
內容解密:
此命令用於應用Fluentd的組態,建立一個ConfigMap物件,用於定義Fluentd的日誌轉發規則。其中, output.conf 部分定義了日誌輸出的目標主機和型別。
在ConfigMap之後,我們可以使用以下命令佈署DaemonSet:
kubectl apply -f fluentd-ds.yaml
內容解密:
此命令用於佈署Fluentd的DaemonSet,用於在每個節點上執行Fluentd代理,將日誌轉發到Elasticsearch。其中, fluentd-ds.yaml 檔案定義了DaemonSet的組態。
使用 Falco 和 EFK 進行稽核
驗證 Fluentd Pod 是否正常運作
首先,我們需要確認 Fluentd Pod 是否正在執行。透過檢查 kube-system 名稱空間中的 Pods,可以使用以下命令:
kubectl get pods -n kube-system
由於我們的環境中只有一個節點,因此只會看到一個 Fluentd Pod。
Fluentd 的作用
Fluentd 將用於將所有容器日誌轉發到 Elasticsearch。為了便於使用 Kibana,我們希望在不包含其他容器日誌的情況下轉發 Falco 日誌。最簡單的方法是使用 Falco 團隊的另一個專案,稱為 Falcosidekick。
佈署 Falcosidekick
Falco 提供了一個實用工具,可以格式化並轉發 Falco 事件到不同的日誌伺服器。該專案在 GitHub 上,支援多種日誌系統,包括 Slack、Teams、Datadog、Elasticsearch、AWS Lambda、SMTP 和 Webhooks。
為什麼使用 Falcosidekick?
由於 Falcosidekick 提供了一種簡單的轉發方法,用於各種不同的後端,因此我們將佈署它來將 Falco 事件轉發到 Elasticsearch。
佈署步驟
- 組態 values.yaml 檔案:我們提供了一個預先組態好的檔案,其中包含將 Falco 事件傳送到 Elasticsearch 佈署所需的條目。組態檔案中的相關條目如下:
elasticsearch:
host: "http://elasticsearch-elasticsearch-coordinating-only.logging.svc:9200"
index: "falco"
type: "event"
minimumpriority: ""
- 佈署 Falcosidekick:使用 Helm 佈署 chart。首先,切換到
falcosidekick目錄,然後執行以下命令:
helm install falcosidekick -f values.yaml . --namespace falco
- 驗證佈署:透過取得 Falcosidekick Pod 的日誌來驗證 chart 是否正確佈署:
kubectl logs falcosidekick-7656785f89-q2z6q -n logging
日誌中應該顯示 Falcosidekick 正在監聽埠 2801,並且已啟用 Elasticsearch 輸出。
- 檢查日誌:一旦 Falcosidekick Pod 開始接收來自 Falco Pods 的資料,日誌檔案中將顯示成功傳送到 Elasticsearch 的記錄:
2020/05/05 23:42:40 [INFO] : Elasticsearch - Post OK (201)
目前的進展
到目前為止,我們已經佈署了 Elasticsearch 來儲存 Fluentd 代理轉發的資訊。我們的 worker 節點正在使用 Fluentd 代理將所有日誌傳送到 Elasticsearch 例項,而 Falcosidekick 正在將 Falco 事件轉發到 Elasticsearch。
佈署 Kibana
接下來的步驟是安裝 Kibana。Kibana 將用於解析資料並建立自定義儀錶板,以及搜尋收集的資料。
佈署步驟
- 使用 Bitnami chart 佈署 Kibana:
helm install kibana --set elasticsearch.hosts[0]=elasticsearch-elasticsearch-coordinating-only --elasticsearch.port=9200,persistence.size=1Gi --namespace logging bitnami/kibana
- 建立 Ingress 規則:為了使 Kibana 可以在叢集外部存取,我們需要建立一個 Ingress 規則。使用提供的
create-ingress.sh指令碼可以自動完成此過程。
使用 Kibana 儀錶板
- 在瀏覽器中開啟 Kibana 儀錶板,使用 Ingress 名稱(例如
http://kibana.10.2.1.107.nip.io)。 - 請求將被轉發到 Docker 主機上的 NGINX Ingress 控制器,最終到達 Kibana Pod。
圖表翻譯:
此圖示呈現了使用 Falco 和 EFK 進行稽核的流程,包括 Fluentd 日誌收集、Falcosidekick 事件轉發、Elasticsearch 儲存和 Kibana 儀錶板的建立。