返回文章列表

Falco EFK 稽核佈署實戰

本文探討如何利用 Falco 和 EFK 堆積疊實作 Kubernetes 叢集的執行時安全稽核。涵蓋 Falco 如何與主機 /dev 檔案系統互動、佈署 Falco DaemonSet、建立必要的 Service Account 和 Service,以及設定

容器技術 資安

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 許可權:

  1. 使用 kubectl 建立 Service Account:
kubectl apply -f falco/install/falco-account.yaml -n falco
  1. 接下來,我們需要為 Falco 建立一個 Service。所包含的 falco-service.yaml 檔案將在 TCP 連線埠 8765 上建立一個新的 Service。使用 kubectl 套用 manifest:
kubectl apply -f falco/install/falco-service.yaml -n falco
  1. 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 載入更新的檔案。

  1. 最後一步是佈署 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.sizedata.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。

佈署步驟

  1. 組態 values.yaml 檔案:我們提供了一個預先組態好的檔案,其中包含將 Falco 事件傳送到 Elasticsearch 佈署所需的條目。組態檔案中的相關條目如下:
elasticsearch:
  host: "http://elasticsearch-elasticsearch-coordinating-only.logging.svc:9200"
  index: "falco"
  type: "event"
  minimumpriority: ""
  1. 佈署 Falcosidekick:使用 Helm 佈署 chart。首先,切換到 falcosidekick 目錄,然後執行以下命令:
helm install falcosidekick -f values.yaml . --namespace falco
  1. 驗證佈署:透過取得 Falcosidekick Pod 的日誌來驗證 chart 是否正確佈署:
kubectl logs falcosidekick-7656785f89-q2z6q -n logging

日誌中應該顯示 Falcosidekick 正在監聽埠 2801,並且已啟用 Elasticsearch 輸出。

  1. 檢查日誌:一旦 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 將用於解析資料並建立自定義儀錶板,以及搜尋收集的資料。

佈署步驟

  1. 使用 Bitnami chart 佈署 Kibana:
helm install kibana --set elasticsearch.hosts[0]=elasticsearch-elasticsearch-coordinating-only --elasticsearch.port=9200,persistence.size=1Gi --namespace logging bitnami/kibana
  1. 建立 Ingress 規則:為了使 Kibana 可以在叢集外部存取,我們需要建立一個 Ingress 規則。使用提供的 create-ingress.sh 指令碼可以自動完成此過程。

使用 Kibana 儀錶板

  1. 在瀏覽器中開啟 Kibana 儀錶板,使用 Ingress 名稱(例如 http://kibana.10.2.1.107.nip.io)。
  2. 請求將被轉發到 Docker 主機上的 NGINX Ingress 控制器,最終到達 Kibana Pod。

圖表翻譯:

此圖示呈現了使用 Falco 和 EFK 進行稽核的流程,包括 Fluentd 日誌收集、Falcosidekick 事件轉發、Elasticsearch 儲存和 Kibana 儀錶板的建立。