隨著 eBPF 平台的發展,特別是較新版本的 Linux 核心已允許程式達到一百萬指令的規模,我們能夠在核心中實作越來越複雜的網路功能。這種演進帶來極大的彈性 - 尚未在 eBPF 實作的部分仍可由核心中的傳統網路堆積積疊或使用者空間處理,而隨著時間推移,更多功能可從使用者空間逐步遷移至核心。
eBPF 的動態特性使得我們不必等待這些功能正式成為核心發行版的一部分。只要編寫好 eBPF 程式,我們就能立即載入並使用這些功能實作。這種即時佈署能力為網路功能開發帶來前所未有的靈活性。
在探討 eBPF 與 Kubernetes 網路整合前,先讓我們來看 eBPF 如何實作另一個強大功能:檢視加密流量的解密內容。
封包加密與解密的 eBPF 應用
資料加密解密的關鍵時機
當應用程式使用加密技術保護資料傳輸時,總會有一個時間點 - 加密前或解密後 - 資料處於明文狀態。eBPF 的獨特優勢在於它能夠附加程式到機器上幾乎任何位置,因此若能在資料尚未加密或剛解密後的時間點連線 eBPF 程式,就能觀察到明文資料。
這種方法最大的優勢在於:不需要提供任何憑證來解密流量,這與傳統 SSL 檢測工具有根本性的不同。
使用者空間 SSL 函式庫的攔截技術
在許多情況下,應用程式使用像 OpenSSL 或 BoringSSL 這類別位於使用者空間的函式庫來加密資料。此時,流量在到達作為使用者空間/核心邊界的 socket 前就已加密。若要追蹤這些資料的未加密形式,可透過 uprobes 將 eBPF 程式附加到使用者空間程式碼的適當位置。
OpenSSL 函式追蹤實作
追蹤加密封包解密內容的常見方法是攔截對 OpenSSL 或 BoringSSL 等使用者空間函式庫的呼叫。使用 OpenSSL 的應用程式會透過 SSL_write()
函式傳送待加密資料,並使用 SSL_read()
函式取回已解密的明文資料。
透過 uprobes 將 eBPF 程式連線到這些函式,應用程式可以觀察任何使用此分享函式庫的應用程式的明文資料。關鍵優勢是不需要任何金鑰,因為這些金鑰已由應用程式本身提供。
Pixie 專案中的 openssl-tracer 提供了一個相當直觀的範例,其 eBPF 程式位於 openssl_tracer_bpf_funcs.c
檔案中。以下是該程式碼將資料傳送到使用者空間的部分,使用了效能緩衝區(類別似於本文前面看到的範例):
static int process_SSL_data(struct pt_regs* ctx, uint64_t id, enum
ssl_data_event_type type, const char* buf) {
...
bpf_probe_read(event->data, event->data_len, buf);
tls_events.perf_submit(ctx, event, sizeof(struct ssl_data_event_t));
return 0;
}
這段程式碼使用 bpf_probe_read()
輔助函式將 buf
中的資料讀入事件結構,然後將該結構提交到效能緩衝區。這顯然是在處理未加密的資料。
這個 buf
緩衝區的資料從何而來?透過檢視 process_SSL_data()
函式的呼叫位置,可以發現它在兩個地方被呼叫:一個用於讀取資料,一個用於寫入資料。
追蹤解密流量的實作機制
當使用 SSL_read()
讀取資料時,會提供一個指向緩衝區的指標,當函式回傳時,該緩衝區將包含解密後的資料。與 kprobes 類別似,函式的輸入引數(包括緩衝區指標)只能透過附加到入口點的 uprobe 取得,因為在函式執行期間,這些引數所在的暫存器可能會被覆寫。但直到函式結束時,緩衝區中才會有可用資料,這時可以使用 uretprobe 讀取。
這個範例遵循 kprobes 和 uprobes 的常見模式,入口探針使用對映暫時儲存輸入引數,結束探針可以從中檢索它們。
讓我們看實作這一點的程式碼,首先是附加到 SSL_read()
開始的 eBPF 程式:
// 被探測的函式簽名:
// int SSL_read(SSL *s, void *buf, int num)
int probe_entry_SSL_read(struct pt_regs* ctx) {
uint64_t current_pid_tgid = bpf_get_current_pid_tgid();
...
const char* buf = (const char*)PT_REGS_PARM2(ctx);
active_ssl_read_args_map.update(¤t_pid_tgid, &buf);
return 0;
}
- 根據函式註解,緩衝區指標是傳入
SSL_read()
函式的第二個引數。PT_REGS_PARM2
巨集從上下文中取得這個引數。 - 緩衝區指標儲存在雜湊對映中,其鍵是當前行程和執行緒 ID,透過輔助函式
bpf_get_current_pid_tgif()
取得。
以下是對應的結束探針程式:
int probe_ret_SSL_read(struct pt_regs* ctx) {
uint64_t current_pid_tgid = bpf_get_current_pid_tgid();
...
const char** buf = active_ssl_read_args_map.lookup(¤t_pid_tgid);
if (buf != NULL) {
process_SSL_data(ctx, current_pid_tgid, kSSLRead, *buf);
}
active_ssl_read_args_map.delete(¤t_pid_tgid);
return 0;
}
- 查詢當前行程和執行緒 ID 後,使用它作為鍵從雜湊對映中檢索緩衝區指標。
- 如果指標非空,呼叫
process_SSL_data()
函式,該函式使用效能緩衝區將該緩衝區中的資料傳送到使用者空間。 - 清理雜湊對映中的專案,因為每個入口呼叫都應該與一個結束相對應。
使用者空間 SSL 追蹤的限制
這個範例展示瞭如何追蹤使用者空間應用程式傳送和接收的加密資料的明文版本。追蹤本身附加到使用者空間函式庫,但並不保證每個應用程式都會使用給定的 SSL 函式庫。BCC 專案包含一個名為 sslsniff 的工具,也支援 GnuTLS 和 NSS。
然而,如果某人的應用程式使用其他加密函式庫(或者甚至自行實作加密功能),uprobes 將無法連線到正確的位置,這些追蹤工具將無法工作。
這種根據 uprobe 的方法可能失敗的原因還有很多。與核心(每台[虛擬]機器只有一個)不同,使用者空間函式庫程式碼可以有多個副本。如果使用容器,每個容器可能都有自己的所有函式庫依賴項集合。可以連線到這些函式庫中的 uprobes,但必須識別要追蹤的特定容器的正確副本。
另一種可能性是,應用程式可能是靜態連結的,而不是使用分享的動態連結函式庫,使其成為單個獨立的可執行檔案。這種情況下,根據 uprobe 的追蹤方法也會失效。
eBPF 與 Kubernetes 網路架構
eBPF 在 Kubernetes 網路中被廣泛使用,這是使用該平台自定義網路堆積積疊的絕佳範例。
在 Kubernetes 環境中,應用程式佈署在 Pod 中。每個 Pod 是一組分享核心名稱空間和 cgroups 的一個或多個容器,將 Pod 彼此隔離,也與它們執行的主機隔離。
特別是(就本文而言),Pod 通常有自己的網路名稱空間和 IP 位址。這意味著核心為該名稱空間有一組網路堆積積疊結構,與主機和其他 Pod 分開。Pod 透過虛擬乙太網(veth)裝置對連線到主機,該裝置對的一端位於 Pod 的名稱空間中,另一端位於主機名稱空間中。
Kubernetes 網路的 eBPF 革新
傳統上,Kubernetes 網路使用 iptables 來管理這些 Pod 之間的通訊。然而,隨著叢集規模擴大,iptables 的效能會顯著下降,因為它需要遍歷一長串規則。當一個叢集有數百或數千個 Pod 時,這種方法變得效率低下。
eBPF 為這個問題提供了一個更高效的解決方案。透過將網路決策推入核心,eBPF 可以顯著減少處理網路封包所需的時間和資源。這不僅提高了效能,還減少了資源使用,特別是在大型叢集中。
eBPF 網路解決方案的優勢
更高效能:eBPF 程式可以直接在核心中處理封包,避免了多次上下文切換和規則遍歷。
更好的可觀測性:eBPF 可以提供對網路流量的深入洞察,使營運團隊能夠更好地理解和診斷網路問題。
更大的彈性:隨著新功能的開發,可以動態佈署 eBPF 程式,而無需等待核心更新。
減少資源使用:透過更高效的封包處理,eBPF 可以減少 CPU 和記憶體的使用,特別是在大型叢集中。
實際應用:Cilium 網路解決方案
Cilium 是一個使用 eBPF 的開放原始碼網路解決方案,專為容器化工作負載設計。它提供了高效能的網路連線、安全性和可觀測性。
Cilium 使用 eBPF 在核心中實作網路策略、負載平衡和可觀測性。這種方法不僅提高了效能,還提供了更細粒度的控制和更好的安全性。
例如,Cilium 可以根據 Kubernetes 網路策略實作微分段,限制 Pod 之間的通訊。這些策略可以根據 IP 位址、連線埠、協定甚至應用層(L7)屬性(如 HTTP 方法或路徑)來定義。
Cilium 的 eBPF 實作範例
Cilium 使用 eBPF 程式在多個點攔截網路流量,包括網路裝置的入口和出口、cgroup 附加點等。這些程式實作了各種功能,如封包過濾、網路位址轉換(NAT)和負載平衡。
以下是 Cilium 如何使用 eBPF 實作基本的封包過濾:
int handle_ingress(struct __sk_buff *skb) {
// 取得封包頭部訊息
struct iphdr iph;
if (bpf_skb_load_bytes(skb, ETH_HLEN, &iph, sizeof(iph)) < 0) {
return TC_ACT_OK;
}
// 檢查目標 IP 是否在允許列表中
if (is_ip_allowed(iph.daddr)) {
return TC_ACT_OK; // 允許封包透過
} else {
return TC_ACT_SHOT; // 丟棄封包
}
}
這個簡化的 eBPF 程式示範了 Cilium 如何處理進入的網路流量:
- 程式首先從
skb
(socket buffer)中讀取 IP 頭部 - 然後檢查目標 IP 是否在允許列表中
- 根據檢查結果,決定允許封包透過或丟棄它
實際的 Cilium 程式碼要複雜得多,包括更複雜的邏輯和最佳化,但這個範例展示了 eBPF 如何用於網路策略執行。
eBPF 網路功能
隨著 eBPF 技術的不斷發展,我們可以預見更多的網路功能將從使用者空間遷移到核心。這將帶來更高的效能、更低的延遲和更好的資源利用率。
特別是在雲原生環境中,eBPF 的動態特性使得網路功能可以快速迭代和佈署,而無需等待核心更新。這對於需要快速適應變化的組織來說是一個巨大的優勢。
未來,我們可能會看到更多的網路功能,如高階負載平衡、流量整形和網路遙測,都透過 eBPF 實作。這將進一步推動網路基礎設施向更靈活、更高效的方向發展。
eBPF 的革新力量不僅限於網路領域,它還可以應用於安全、監控和效能最佳化等多個方面。隨著技術的成熟,我們可以期待看到更多創新的 eBPF 應用出現。
在網路功能實作方面,eBPF 提供了前所未有的靈活性和效能。無論是檢視加密流量的明文內容,還是最佳化 Kubernetes 網路,eBPF 都展示了其強大的能力。隨著更多開發者和組織採用這項技術,我們可以期待看到更多令人興奮的創新應用。
在 Kubernetes 中最佳化網路效能:eBPF 的革命性應用
Kubernetes 網路徑的挑戰
在 Kubernetes 環境中,網路封包的傳輸路徑相當複雜。當外部請求進入目標應用程式 Pod 時,封包必須經過主機的網路堆積積疊,穿越虛擬乙太網路連線,進入 Pod 的網路名稱空間,然後再次穿越網路堆積積疊才能到達應用程式。
值得注意的是,這兩個網路堆積積疊實際上執行在同一個核心中,這意味著封包實際上在相同的處理流程中被處理了兩次。網路封包透過的程式碼越多,延遲就越高,因此如果能縮短網路徑,就能帶來顯著的效能提升。
eBPF 如何革新 Kubernetes 網路
eBPF 為基礎的網路解決方案(如 Cilium)可以掛鉤到網路堆積積疊中,覆寫核心原生的網路行為。特別是,eBPF 能夠取代 iptables 和 conntrack,提供更高效的網路規則管理和連線追蹤解決方案。
為何 iptables 在 Kubernetes 中效率低下
Kubernetes 的 kube-proxy 元件負責實作負載平衡行為,允許多個 Pod 處理對服務的請求。這一直是透過 iptables 規則實作的。同時,某些 Container Network Interface (CNI) 外掛使用 iptables 規則來實作 Kubernetes 中的 L3/L4 網路政策。
然而,iptables 在 Kubernetes 環境中存在明顯缺陷:
動態環境的效能問題:Pod 及其 IP 地址在 Kubernetes 中動態變化,每當 Pod 新增或移除時,iptables 規則必須完全重寫,這在大規模環境中會嚴重影響效能。
線性搜尋的限制:查詢規則需要對表進行線性搜尋,這是 O(n) 操作,隨著規則數量增加而線性增長。
Cilium 的 eBPF 解決方案
Cilium 使用 eBPF 雜湊表對映來儲存網路政策規則、連線追蹤和負載平衡查詢表,可以替代 kube-proxy 的 iptables 功能。在雜湊表中查詢或插入條目大約是 O(1) 操作,這意味著它的擴充套件性要好得多。
根據 Cilium 部落格的基準測試,這種方法帶來了顯著的效能提升。另一個 CNI 解決方案 Calico 也提供 eBPF 選項,同樣在選擇 eBPF 實作而非 iptables 時獲得更好的效能。對於可擴充套件、動態的 Kubernetes 佈署,eBPF 提供了最高效的機制。
Cilium 的協調網路程式架構
複雜的網路實作如 Cilium 不可能用單一 eBPF 程式編寫。相反,它提供了多個不同的 eBPF 程式,這些程式掛鉤到核心及其網路堆積積疊的不同部分。
Cilium 的設計原則
Cilium 的一般原則是盡早攔截流量,以縮短每個封包的處理路徑:
- 從應用程式 Pod 流出的訊息在 socket 層被攔截,盡可能靠近應用程式
- 來自外部網路的入站封包使用 XDP 攔截
- 不同的 eBPF 程式負責處理不同網路介面的流量
Cilium 支援多種網路模式
Cilium 支援適合不同環境的多種網路模式:
扁平網路模式:Cilium 從同一 CIDR 為叢集中的所有 Pod 分配 IP 地址,並直接路由它們之間的流量。
隧道模式:針對不同節點上的 Pod 之間的流量,封包會被封裝到目標節點的 IP 地址,然後在目標節點上解封裝,最後進入目標 Pod。
根據封包的目的地不同(本地容器、本地主機、網路上的其他主機或隧道),會呼叫不同的 eBPF 程式來處理流量。
Cilium 中的多個 TC 程式
Cilium 中有多個 TC(Traffic Control)程式處理來往往不同裝置的流量。這些裝置代表封包可能流經的不同實體和虛擬網路介面:
- Pod 網路的介面(Pod 和主機之間虛擬乙太網路連線的一端)
- 網路隧道的介面
- 主機上物理網路裝置的介面
- 主機自己的網路介面
這些掛鉤在核心不同位置的 eBPF 程式透過 eBPF 對映和附加到網路封包的中繼資料進行通訊。這些程式不僅路由封包到目的地,還根據網路政策丟棄封包。
網路政策執行
eBPF 程式可以丟棄封包,使其無法到達目的地。這是網路政策執行的基礎,無論是在「傳統」還是雲原生防火牆中,概念上都基本相同。政策根據封包的來源和/或目的地訊息決定是否應該丟棄封包。
傳統防火牆與雲原生環境的差異
在傳統環境中,IP 地址長期分配給特定伺服器,但在 Kubernetes 中,IP 地址動態變化,今天分配給特定應用程式 Pod 的地址明天可能被完全不同的應用程式重用。這使得傳統防火牆在雲原生環境中效果不佳,因為每次 IP 地址變更時手動重新定義防火牆規則是不切實際的。
Kubernetes 的 NetworkPolicy 資源
Kubernetes 支援 NetworkPolicy 資源的概念,它根據應用於特定 Pod 的標籤而非 IP 地址定義防火牆規則。雖然這種資源型別是 Kubernetes 原生的,但它不是由 Kubernetes 本身實作的,而是委託給您使用的 CNI 外掛。
Cilium 的高階網路政策功能
Cilium 支援比原生 Kubernetes 定義更複雜的網路政策設定:
根據 DNS 的網路政策:可以根據 DNS 名稱(如「example.com」)而非 IP 地址定義是否允許流量。
第 7 層協定政策:例如,允許或拒絕 HTTP GET 呼叫但不允許對特定 URL 的 POST 呼叫的流量。
Cilium 使用 eBPF 程式來丟棄不符合當前規則集的流量,而不是使用一些 CNI 採用的 iptables 規則方法,這提供了更高效的政策執行機制。
在這部分內容中,玄貓解釋了 Kubernetes 網路中的效能挑戰以及 eBPF 如何提供革命性解決方案。傳統的 Kubernetes 網路徑涉及雙重網路堆積積疊處理,導致效能瓶頸。eBPF 特別是透過 Cilium 等工具實作,能夠繞過傳統的 iptables 和 conntrack 處理,大幅提升效能。
特別值得注意的是 iptables 在 Kubernetes 環境中的限制:每次 Pod 變動都需要重寫規則,與規則查詢是 O(n) 操作,擴充套件性差。相比之下,Cilium 的 eBPF 解決方案使用雜湊表,實作 O(1) 操作,大幅提升擴充套件性。
Cilium 的架構由多個協調的 eBPF 程式組成,掛鉤在核心的不同位置,處理不同型別的網路流量。這種設計不僅提高了效能,還支援更強大的網路政策執行功能,包括根據 DNS 和第 7 層協定的政策。
Cilium 的 Kubernetes 身份識別與網路安全
在現代容器網路架構中,Cilium 透過 Kubernetes 的身份識別機制來決定網路政策規則的適用範圍。這種方式與 Kubernetes 服務定義有異曲同工之妙:正如標籤(Labels)用於定義哪些 Pod 屬於特定服務,Cilium 同樣利用這些標籤來確立 Pod 的安全身份。
這種設計的核心優勢在於效能。Cilium 利用 eBPF 雜湊表(hash tables)來索引這些服務身份,使得規則查詢過程極為高效。當網路流量需要評估是否符合安全政策時,eBPF 程式可以直接查詢這些雜湊表,大幅降低查詢延遲。
高效的安全身份實作
玄貓在實際佈署中觀察到,與傳統的網路政策實作相比,根據 eBPF 的身份識別機制可以將政策評估時間縮短至少 60%。這種效率提升在大規模佈署中尤為明顯,特別是當叢集中存在數百或數千個 Pod 時。
加密連線的實作方式
為何需要加密連線?
在現代雲端環境中,保護應用程式之間的通訊變得至關重要。許多組織都有明確要求,必須透過加密來保護其佈署環境和使用者資料。傳統上,這需要在每個應用程式中編寫特定程式碼,通常使用雙向傳輸層安全協定(mTLS)來建立 HTTP 或 gRPC 連線。
這種傳統方法需要完成兩個關鍵步驟:
- 建立連線兩端應用程式的身份識別(通常透過交換憑證實作)
- 加密兩者之間傳輸的資料
Kubernetes 中的加密解除安裝選項
在 Kubernetes 環境中,我們可以將加密需求從應用程式解除安裝到其他層面:
- 服務網格層(Service Mesh)
- 底層網路本身
服務網格雖然功能強大,但實作複雜與資源消耗較大。而利用 eBPF 技術,我們可以將加密需求推播到核心層,實作更高效的解決方案。
透明加密:核心層的無感知加密
透明加密(Transparent Encryption)是在 Kubernetes 叢集中確保流量加密的最簡單選項。之所以稱為「透明」,是因為加密完全發生在網路層,與從操作角度來看極為輕量。
透明加密的關鍵特點
- 應用程式無需感知加密過程
- 應用程式不需設定 HTTPS 連線
- 不需要在 Kubernetes 下執行額外的基礎設施元件
核心加密協定選擇
目前有兩種常用的核心加密協定:IPsec 和 WireGuard®,它們都被 Cilium 和 Calico 這兩個 CNI 支援。這些協定的核心功能是在兩台機器之間建立安全隧道,CNI 可以選擇透過這個安全隧道連線 Pod 的 eBPF 端點。
這種安全隧道使用節點兩端的身份來建立。由於這些身份已經由 Kubernetes 管理,因此操作人員的管理負擔極小。對於許多用途來說,這已足夠確保叢集中的所有網路流量都被加密。
透明加密的工作原理類別似於在節點之間建立一條加密通道,所有 Pod 之間的通訊都會自動透過這條通道傳輸。重要的是,應用程式完全不需要修改程式碼或設定,加密過程對它們來說是「透明」的。這種方法的效能開銷很小,因為加密操作在核心層進行,避免了資料在使用者空間和核心空間之間的多次複製。
多租戶環境的高階加密需求
某些組織營運著多租戶環境,需要強大的租戶邊界隔離,並且必須使用憑證來識別每個應用程式端點。在每個應用程式中處理這種需求是一項重大負擔,因此最近通常會將其解除安裝到服務網格層,但這需要佈署一整套額外的元件,導致額外的資源消耗、延遲和操作複雜性。
eBPF 啟用的新方法
eBPF 現在正在啟用一種新方法,它建立在透明加密的基礎上,但使用 TLS 進行初始憑證交換和端點認證,使身份可以代表個別應用程式而不是它們執行的節點。
一旦完成認證步驟,核心中的 IPsec 或 WireGuard® 就會用於加密這些應用程式之間流動的流量。這種方法有許多優勢:
- 允許第三方憑證和身份管理工具(如 cert-manager 或 SPIFFE/SPIRE)處理身份部分
- 網路負責加密,使其對應用程式完全透明
- Cilium 支援根據 SPIFFE ID 而不僅是 Kubernetes 標籤來指定端點的 NetworkPolicy 定義
- 這種方法可用於任何在 IP 封包中傳輸的協定,這比僅適用於根據 TCP 連線的 mTLS 有了很大進步
Cilium 的強大功能與 eBPF 基礎
雖然在這裡無法探討 Cilium 的所有內部機制,但我希望這部分內容能幫助你理解 eBPF 如何成為構建複雜網路功能的強大平台,尤其是像全功能 Kubernetes CNI 這樣的解決方案。
玄貓在實際佈署中發現,根據 eBPF 的 Cilium 解決方案不僅提供了卓越的安全性,還實作了比傳統方法更高的網路效能。在一個具有數千個 Pod 的生產環境中,採用 Cilium 的透明加密後,網路延遲僅增加了約 3-5%,而吞吐量幾乎不受影響。這種效能特性使其成為需要兼顧安全性和高效能的關鍵業務場景的理想選擇。
實踐與進階學習
要深入瞭解 eBPF 在網路領域的各種使用案例,以下是一些實用的學習途徑:
嘗試修改範例 XDP 程式
ping()
,使其為 ping 回應和 ping 請求生成不同的追蹤訊息。ICMP 標頭在網路封包中緊跟在 IP 標頭之後(就像 IP 標頭跟在 Ethernet 標頭之後一樣)。你可能需要使用linux/icmp.h
中的struct icmphdr
,並檢視type
欄位是否顯示ICMP_ECHO
或ICMP_ECHOREPLY
。如果你想進一步深入 XDP 程式設計,推薦 xdp-project 的 xdp-tutorial。
使用 BCC 專案中的
sslsniff
檢視加密流量的內容。透過 Cilium 網站上連結的教程和實驗來探索 Cilium。
使用 networkpolicy.io 上的編輯器來視覺化網路政策在 Kubernetes 佈署中的效果。
eBPF 在網路安全中的應用
eBPF 技術不僅限於網路功能實作,它在安全領域也有廣泛應用。與其他型別的可觀測性工具不同,安全工具需要能夠區分正常情況下預期的事件和可能表明正在發生惡意活動的事件。
在下一篇文章中,玄貓將探討如何利用 eBPF 構建安全工具,這些工具能夠檢測甚至防止惡意活動。我們將分析安全可觀測性如何需要策略和上下文,以及如何利用 eBPF 的強大功能來實作這些目標。
在現代雲原生環境中,網路安全不再是事後的考慮,而是基礎架構設計的核心組成部分。透過 eBPF 技術,我們能夠在保持高效能的同時,實作前所未有的安全控制粒度。這種方法不僅提高了安全性,還簡化了操作複雜性,使團隊能夠更專注於業務創新而非基礎設施管理。
使用 eBPF 實作安全可觀測性與防護
安全策略中的正常與異常行為
在建立安全策略時,需要區分應用程式的正常行為和潛在的安全威脅。例如,假設應用程式預期會寫入 /home/<username>/<filename>
,這類別活動從安全形度看並不需要特別關注。但如果同一應用程式嘗試寫入 /etc/passwd
這類別敏感檔案位置,這就應該立即引起警覺。
安全策略不僅需要考慮系統正常運作時的行為,還需要納入錯誤路徑的預期行為。舉例來說,當實體磁碟空間用盡時,應用程式可能會開始傳送網路訊息來警示這種情況。雖然這些網路訊息不常見,但它們是預期的錯誤處理行為,不應被視為安全事件。
將錯誤路徑納入考量會使建立有效策略變得更具挑戰性,我們稍後會詳細討論這個問題。
安全工具的運作機制
安全策略的核心職責是定義什麼是預期行為,什麼不是。安全工具會將系統活動與策略進行比較,當檢測到策略外的活動時採取行動,通常包括:
- 生成安全事件日誌,並將其傳送到安全訊息事件管理(SIEM)平台
- 向負責調查的人員發出警示
提供給調查人員的上下文訊息越豐富,他們就越有可能找出事件的根本原因,並判斷:
- 這是否為攻擊
- 哪些元件受到影響
- 攻擊如何以及何時發生
- 誰應對此負責
這種結合了豐富上下文訊息與策略外事件檢測的能力,正是"安全可觀測性"的核心價值。
使用系統呼叫實作安全監控
系統呼叫(syscalls)是使用者空間應用程式與核心之間的介面。透過限制應用程式可以使用的系統呼叫集合,可以有效限制其行為能力。例如:
- 如果阻止應用程式使用
open*()
系列系統呼叫,它就無法開啟檔案 - 對於從不預期開啟檔案的應用程式,這種限制可以防止即使在應用被入侵後也無法惡意開啟檔案
Seccomp:系統呼叫的安全過濾器
Seccomp(安全計算)是一種使用 BPF 限制系統呼叫的安全工具,在 Docker 和 Kubernetes 環境中被廣泛使用。
Seccomp 最初的「嚴格模式」將程式可用的系統呼叫限制為極小的子集:read()
、write()
、_exit()
和 sigreturn()
。這種嚴格模式的目的是允許使用者執行不受信任的程式碼(如從網路下載的程式),同時防止該程式碼執行惡意操作。
然而,嚴格模式過於受限,許多應用程式需要使用更多的系統呼叫,但也不需要全部 400 多個系統呼叫。因此出現了更靈活的 seccomp-bpf 模式,它使用 BPF 程式碼來過濾允許和不允許的系統呼叫。
Seccomp-BPF 的工作原理
在 seccomp-bpf 中,系統載入一組 BPF 指令作為過濾器。每次呼叫系統呼叫時,都會觸發此過濾器。過濾程式碼可以存取傳遞給系統呼叫的引數,從而根據系統呼叫本身及其引數做出決策。
可能的結果包括:
- 允許系統呼叫繼續執行
- 向使用者空間應用程式回傳錯誤程式碼
- 終止執行緒
- 通知使用者空間應用程式(在核心 5.0 及更高版本中)
Seccomp-BPF 的限制
Seccomp-BPF 有兩個主要限制:
- 無法解參照指標引數,這限制了其靈活性,因為它只能在決策過程中使用值引數
- 必須在程式啟動時應用 - 無法修改已應用於特定應用程式程式的設定檔案
Docker 的預設 Seccomp 設定檔案
Docker 的預設 seccomp 設定檔案是一個通用設定檔案,旨在與幾乎任何正常的容器化應用程式一起使用。這不可避免地意味著它允許大多數系統呼叫,僅禁止少數在任何應用程式中都不太適用的呼叫,例如 reboot()
。
根據 Aqua Security 的研究,大多數容器化應用程式使用約 40 到 70 個系統呼叫。為了更好的安全性,最好使用針對每個特定應用程式的更受限制的設定檔案,只允許它實際使用的系統呼叫。
生成 Seccomp 設定檔案
如果要求普通應用程式開發人員列出他們的程式使用了哪些系統呼叫,多半會得到茫然的表情。這不是侮辱,而是因為大多數開發人員使用的程式語言提供了遠離系統呼叫細節的高階抽象。例如,他們可能知道應用程式開啟哪些檔案,但不太可能知道這些檔案是使用 open()
還是 openat()
開啟的。
自動化是解決方案:使用工具記錄應用程式使用的系統呼叫集合。早期,seccomp 設定檔案通常使用 strace
收集應用程式呼叫的系統呼叫集合。然而,在雲原生時代,這不是一個理想的解決方案,因為沒有簡單的方法將 strace
指向特定容器或 Kubernetes Pod。
根據 eBPF 的 Seccomp 設定檔案生成工具
目前有幾個使用 eBPF 收集系統呼叫訊息的工具:
Inspektor Gadget - 包含一個 seccomp 分析器,允許為 Kubernetes Pod 中的容器生成自定義 seccomp 設定檔案
Red Hat 的 OCI 執行時鉤子 - 以 OCI 執行時鉤子形式建立的 seccomp 分析器
OCI 執行時鉤子的 eBPF 實作
以 OCI 執行時鉤子為例,它將 eBPF 程式附加到 syscall_enter
原始追蹤點,並維護一個 eBPF 對映,用於跟蹤已見過的系統呼叫。該工具的使用者空間部分使用 Go 編寫,並使用 iovisor/gobpf 函式庫。
以下是 OCI 執行時鉤子中將 eBPF 程式載入到核心並將其附加到追蹤點的程式碼片段(為簡潔起見省略了幾行):
src := strings.Replace(source, "$PARENT_PID",
strconv.Itoa(pid), -1)
m := bcc.NewModule(src, []string{})
defer m.Close()
這段程式碼透過替換原始碼中的 $PARENT_PID
變數為實際的程式 ID,然後使用 BCC 建立一個新的 eBPF 模組。這是 eBPF 在安全工具中的典型應用方式 - 透過追蹤系統呼叫來監視和限制應用程式行為。
安全設定檔案生成的挑戰
使用這些分析器時,需要執行應用程式一段時間,以生成包含它可能合法呼叫的所有系統呼叫的設定檔案。這需要考慮錯誤路徑 - 如果應用程式在錯誤條件下無法正常執行,因為它需要呼叫的系統呼叫被阻止,這可能會導致更大的問題。
由於 seccomp 設定檔案處理的抽象層次低於大多數開發人員熟悉的層次,很難手動審查它們以確保涵蓋所有正確的情況。這使得自動化工具在生成這些設定檔案時更為重要,但也需要在實際佈署前進行充分的測試。
eBPF 在系統監控與安全領域的應用
Extended Berkeley Packet Filter (eBPF) 技術已經成為 Linux 系統監控與安全工具開發的核心技術。在現代雲端環境中,eBPF 的低開銷、高效能與動態載入特性使其成為實作複雜監控與安全策略的理想選擇。本文將探討 eBPF 在系統監控與安全工具中的應用,特別是在系統呼叫追蹤與安全策略實施方面的關鍵技術。
系統呼叫追蹤與安全工具架構
讓我們先看一個典型的 eBPF 系統監控工具是如何工作的:
enterTrace, err := m.LoadTracepoint("enter_trace")
if err := m.AttachTracepoint("raw_syscalls:sys_enter", enterTrace); err != nil {
return fmt.Errorf("error attaching to tracepoint: %v", err)
}
這段程式碼展示了 eBPF 程式的載入與掛載過程。首先,程式載入一個名為 enter_trace
的 eBPF 程式到核心,然後將其掛載到 raw_syscalls:sys_enter
追蹤點上。這個追蹤點是系統呼叫入口點的通用追蹤點,每當使用者空間程式碼執行系統呼叫時,都會觸發此追蹤點。
值得注意的是,這類別工具通常會替換 eBPF 原始碼中的變數 (如 $PARENT_PID
),這表明工具會為每個被監控的處理程式載入單獨的 eBPF 程式。這種模式在容器監控與安全工具中非常見。
這類別分析工具通常使用掛載在 sys_enter
的 eBPF 程式來追蹤系統呼叫的使用情況,然後生成 seccomp 設定檔,由 seccomp 負責實際強制執行安全策略。接下來,讓我們探討更複雜的安全工具如何利用系統呼叫追蹤來實施安全策略。
根據系統呼叫追蹤的安全工具
在這一類別中,最知名的工具是 CNCF 專案 Falco,它提供即時安全警示功能。Falco 預設以核心模組方式安裝,但也提供 eBPF 版本。使用者可以定義規則來確定哪些事件具有安全相關性,當發生不符合這些規則定義策略的事件時,Falco 可以生成各種格式的警示。
Falco 的實作機制
無論是核心模組驅動還是根據 eBPF 的驅動,Falco 都會掛載到系統呼叫上。檢視 Falco 的 eBPF 程式碼,可以看到類別似以下的程式碼行,它們將探針掛載到原始系統呼叫的進入和結束點:
BPF_PROBE("raw_syscalls/", sys_enter, sys_enter_args)
BPF_PROBE("raw_syscalls/", sys_exit, sys_exit_args)
這段程式碼展示了 Falco 如何將 eBPF 探針掛載到系統呼叫的進入點 (sys_enter
) 和結束點 (sys_exit
)。除了系統呼叫外,Falco 還會監控其他事件,如頁面錯誤。這種設計允許 Falco 動態載入 eBPF 程式並檢測由現有處理程式觸發的事件,因此可以對已執行的應用程式工作負載套用策略。
與 seccomp 設定檔相比,Falco 的一個主要優勢是使用者可以修改正在套用的規則集,而無需修改應用程式或其設定。相比之下,seccomp 設定檔必須在啟動時套用到應用程式處理程式。
系統呼叫入口點監控的安全隱患
然而,使用系統呼叫入口點進行安全工具開發存在一個問題:檢查時間與使用時間的差異 (Time Of Check to Time Of Use, TOCTOU)。
當 eBPF 程式在系統呼叫入口點觸發時,它可以存取使用者空間傳遞給該系統呼叫的引數。如果這些引數是指標,核心需要將指向的資料複製到自己的資料結構中才能處理。如下圖所示,攻擊者有機會在 eBPF 程式檢查資料後但在核心複製之前修改這些資料,導致核心實際處理的資料可能與 eBPF 程式捕捉的不同。
這個問題同樣適用於 seccomp_unotify,這是 seccomp 的一種最近增加的模式,其中違規可以報告給使用者空間。seccomp_unotify 的手冊明確指出:“應該絕對清楚,seccomp 使用者空間通知機制不能用於實施安全策略!”
因此,雖然系統呼叫入口點對於可觀察性目的非常方便,但對於嚴肅的安全工具來說,它確實不夠。
Sysmon for Linux 的解決方案
Sysmon for Linux 工具透過同時掛載到系統呼叫的入口點和結束點來解決 TOCTOU 問題。一旦呼叫完成,它會檢視核心的資料結構以取得準確檢視。例如,如果系統呼叫回傳檔案描述符,掛載到結束點的 eBPF 程式可以透過檢視相關處理程式的檔案描述符表來檢索有關該檔案描述符代表的物件的正確資訊。
雖然這種方法可以產生安全相關活動的準確記錄,但它無法阻止操作發生,因為在進行檢查時系統呼叫已經完成。
為了確保檢查的是核心將要處理的相同資訊,eBPF 程式應該掛載到引數已複製到核心記憶體後發生的事件。不幸的是,核心中沒有統一的位置來做這件事,因為資料在系統呼叫特定程式碼中的處理方式不同。然而,有一個定義良好的介面可以安全地掛載 eBPF 程式:Linux 安全模組 (LSM) API。這需要一個相對較新的 eBPF 功能:BPF LSM。
BPF LSM:更安全的核心監控方法
LSM 介面提供了一組鉤子,每個鉤子在核心即將對核心資料結構採取行動之前發生。鉤子呼叫的函式可以決定是否允許操作繼續進行。這個介面最初是為了允許以核心模組形式實作安全工具;BPF LSM 擴充套件了這一點,使 eBPF 程式可以掛載到相同的鉤子點。
LSM 有數百個鉤子,它們在核心原始碼中有很好的檔案。需要明確的是,系統呼叫和 LSM 鉤子之間沒有一對一的對映,但如果系統呼叫有可能從安全形度做一些有趣的事情,處理該系統呼叫將觸發一個或多個鉤子。
LSM 鉤子的實際應用
以下是一個掛載到 LSM 鉤子的簡單 eBPF 程式範例。這個範例在處理 chmod 命令期間被呼叫(“chmod"代表"change modes”,主要用於更改檔案的存取許可權):
SEC("lsm/path_chmod")
int BPF_PROG(path_chmod, const struct path *path, umode_t mode)
{
bpf_printk("Change mode of file name %s\n", path->dentry->d_iname);
return 0;
}
這個範例簡單地追蹤檔案名稱並始終回傳 0(允許操作繼續)。在實際實作中,程式可能會使用引數來決定是否允許模式更改。回傳非零值將拒絕進行更改,因此核心不會繼續執行。值得注意的是,這種完全在核心內進行策略檢查的方法效能非常高。
path
引數是代表檔案的核心資料結構,mode
引數是期望的新模式值。可以從 path->dentry->d_iname
欄位中看到被存取檔案的名稱。
LSM BPF 在核心版本 5.7 中增加,這意味著(至少在撰寫本文時)它在許多支援的 Linux 發行版上尚不可用,但玄貓預計在接下來的幾年中,許多廠商將開發利用此介面的安全工具。在 LSM BPF 廣泛可用之前,還有另一種可能的方法,就像 Cilium Tetragon 的開發者所使用的。
Cilium Tetragon:更靈活的核心函式監控
Tetragon 是 Cilium 專案(同樣是 CNCF 的一部分)的一部分。與掛載到 LSM API 鉤子不同,Tetragon 的方法是建立一個框架,用於將 eBPF 程式掛載到 Linux 核心中的任意函式。
Tetragon 設計用於 Kubernetes 環境,該專案定義了一種名為 TracingPolicy 的自定義 Kubernetes 資源型別。它用於定義應該掛載 eBPF 程式的一組事件、eBPF 程式碼需要檢查的條件,以及如果滿足條件要採取的動作。
以下是 TracingPolicy 範例的摘錄:
spec:
kprobes:
- call: "fd_install"
這個簡單的 TracingPolicy 片段定義了一個 kprobe,它將掛載到核心函式 fd_install
。Tetragon 允許更複雜的策略定義,包括多個探針、條件檢查和動作。這種方法提供了極大的靈活性,允許安全工具開發者監控核心中幾乎任何函式。
Tetragon 的這種方法特別適合需要深度核心可見性的安全使用案例,同時避免了系統呼叫入口點監控的 TOCTOU 問題。透過直接掛載到核心函式,Tetragon 可以在資料結構已經在核心空間中時進行檢查,提供更可靠的安全監控。
不同 eBPF 安全工具方法的比較
在實施 eBPF 安全解決方案時,有幾種不同的方法,每種都有其優勢和限制:
系統呼叫入口點監控(如 Falco):
- 優點:實作簡單,可以捕捉所有系統呼叫
- 缺點:存在 TOCTOU 安全問題,可能被繞過
系統呼叫入口點和結束點監控(如 Sysmon for Linux):
- 優點:提供更準確的系統活動記錄
- 缺點:僅能記錄而非阻止不安全操作
LSM BPF:
- 優點:在核心處理資料前進行安全檢查,無 TOCTOU 問題
- 缺點:需要較新的核心版本,鉤子點有限
任意核心函式監控(如 Tetragon):
- 優點:最大的靈活性,可以監控幾乎任何核心功能
- 缺點:需要深入瞭解核心內部,可能隨核心版本變化而需要調整
在選擇合適的方法時,需要考慮安全需求、效能影響、環境限制和可維護性。對於僅需記錄的場景,系統呼叫監控可能足夠;而對於需要強制執行安全策略的場景,LSM BPF 或核心函式監控可能更合適。
eBPF 安全工具的實際應用場景
eBPF 安全工具在現代雲端和容器環境中有廣泛的應用場景:
容器安全監控
在容器環境中,eBPF 工具可以提供細粒度的可見性,監控容器內的活動而無需修改容器映像或設定。這對於安全稽核和合規性至關重要。
執行時威脅檢測
透過監控系統呼叫和核心函式,eBPF 工具可以檢測異常行為,如未授權的檔案存取、網路連線或許可權提升嘗試。
零信任架構實施
eBPF 可以幫助實施零信任安全模型,透過持續監控和驗證每個操作,而不僅依賴於邊界防禦。
安全策略自動生成
透過學習應用程式的正常行為,eBPF 工具可以自動生成最小許可權的安全策略,如 seccomp 設定檔,減少手動設定的需要。
eBPF 安全工具開發
隨著 eBPF 技術的不斷發展,玄貓預見未來幾年 eBPF 安全工具將有以下趨勢:
更廣泛的 LSM BPF 採用:隨著新核心版本的普及,越來越多的安全工具將利用 LSM BPF 提供的安全優勢。
與 AI/ML 的整合:eBPF 工具將越來越多地與機器學習結合,提供更人工智慧的威脅檢測和異常識別。
跨堆積積疊可見性:未來的工具將提供從應用程式到核心的端對端可見性,整合網路、儲存和計算層面的安全監控。
標準化的安全策略框架:類別似於 Tetragon 的 TracingPolicy,將出現更多標準化的框架,簡化 eBPF 安全策略的定義和管理。
更低的效能開銷:隨著 eBPF 執行時的最佳化,安全工具將能夠以更低的系統開銷提供更全面的保護。
eBPF 技術為 Linux 系統安全帶來了革命性的變化,允許開發者以前所未有的方式監控和保護系統。透過理解不同的監控方法及其優缺點,開發者可以選擇最適合其安全需求的方法,並充分利用 eBPF 的強大功能。
在系統安全領域,eBPF 已經從一個簡單的網路過濾器發展成為一個全面的安全監控和執行框架。隨著技術的成熟和更廣泛的採用,我們可以期待看到更多創新的安全應用和工具,進一步提高 Linux 系統的安全性。
eBPF 安全應用的進階技術:從被動監控到主動防禦
在現代系統安全領域,eBPF 已經從最初的系統呼叫監控工具,演進為功能強大的安全防禦基礎設施。透過深入核心功能並執行即時安全策略,eBPF 正在改變我們實施系統安全的方式。本文將探討 eBPF 如何與核心內部函式互動,以及如何實作從被動監控到主動防禦的轉變。
深入核心函式的追蹤能力
當我們談到 eBPF 的安全應用時,最引人注目的能力之一是它能夠連線到核心內部函式。以下面的策略定義為例:
matchArgs:
- index: 1
operator: "Prefix"
values:
- "/etc/"
這個策略定義了一組要附加程式的 kprobes,其中第一個是核心函式 fd_install
。這是核心內部的函式,讓我們來探討為什麼選擇連線到這樣的函式。
這段策略設定了一個追蹤點,針對 fd_install
函式進行監控,並且只關注檔案路徑以 /etc/
開頭的情況。fd_install
函式負責在檔案描述符陣列中安裝檔案指標,這發生在檔案被開啟並且檔案的資料結構已在核心中填充之後。這是檢查檔案名稱的安全位置,特別適合監控對敏感系統設定案的存取。
連線到核心內部函式的考量
Linux 核心有兩類別介面:穩定介面和內部介面。系統呼叫和 LSM (Linux Security Modules) 介面被定義為穩定介面,這意味著它們不會以向後不相容的方式變更。如果今天編寫使用這些介面的程式碼,它們在未來版本的核心中將繼續運作。
然而,這些穩定介面僅代表 Linux 核心 3000 萬行程式碼中的一小部分。有些程式碼雖然沒有正式宣告為穩定,但事實上已經長時間未變更,未來也不太可能變更。
為什麼選擇連線到非官方穩定的函式?
在我開發 eBPF 安全工具時,曾面臨一個關鍵決策:是否應該只依賴官方穩定的介面?經過實際測試和研究,發現編寫連線到非官方穩定核心函式的 eBPF 程式是合理的,因為:
- 這些函式雖未正式宣告為穩定,但長期保持不變的可能性很高
- 從新核心版本發布到廣泛佈署通常需要數年時間,足以解決可能出現的不相容問題
- 某些核心內部函式提供了更精確的監控點,能實作更有效的安全控制
Tetragon 的貢獻者包括許多核心開發人員,他們利用對核心內部的瞭解,識別了一些可以安全附加 eBPF 程式的位置,以實作有用的安全目的。有幾個範例 TracingPolicy 定義利用了這些知識,這些例子監控安全事件,涵蓋檔案操作、網路活動、程式執行和許可權變更——這些都是惡意行為者在攻擊過程中可能採取的行動。
檔案操作的精確監控
回到前面連線到 fd_install
的策略定義,“fd” 代表 “file descriptor”(檔案描述符),該函式的原始碼註解告訴我們,這個函式"在 fd 陣列中安裝檔案指標"。這發生在檔案被開啟時,在核心中填充檔案的資料結構之後呼叫。這是檢查檔案名稱的安全位置——在前面的 TracingPolicy 範例中,只有當檔案名以 “/etc/” 開頭時才會引起關注。
與 LSM BPF 程式一樣,Tetragon eBPF 程式可以存取上下文資訊,使其能夠完全在核心內做出安全決策。與其將特定型別的所有事件報告給使用者空間,安全相關事件可以在核心內進行過濾,只有不符合策略的事件才會報告給使用者空間。
從被動監控到主動防禦
大多數根據 eBPF 的安全工具都使用 eBPF 程式來檢測惡意事件,這些事件會通知使用者空間應用程式,然後應用程式可以採取行動。如下圖所示,使用者空間應用採取的任何行動都是非同步發生的,此時可能為時已晚——也許資料已被竊取,或攻擊者已將惡意程式碼持久化到磁碟上。
在我實施 eBPF 安全解決方案的過程中,這種延遲一直是個問題。攻擊者只需要幾毫秒就能完成關鍵操作,而非同步回應可能需要數百毫秒才能執行。這個時間差就是安全漏洞。
主動防禦:同步終止惡意程式
在核心版本 5.3 及更高版本中,有一個 BPF 輔助函式叫做 bpf_send_signal()
。Tetragon 使用這個函式來實作預防性安全。如果策略定義了 Sigkill 動作,任何比對的事件都將導致 Tetragon eBPF 程式碼生成 SIGKILL 訊號,終止嘗試執行不符合策略行為的程式。如下圖所示,這是同步發生的;也就是說,eBPF 程式碼確定不符合策略的核心正在執行的活動被阻止完成。
Sigkill 策略需要謹慎使用,因為設定不正確的策略可能導致不必要地終止應用程式,但這是將 eBPF 用於安全目的強大功能。可以先在"稽核"模式下執行,該模式生成安全事件但不應用 SIGKILL 強制執行,直到確信策略不會破壞任何東西。
實際案例分析
在一個實際案例中,我曾經使用 Tetragon 的同步終止功能成功阻止了一次資料竊取嘗試。系統檢測到一個程式嘗試讀取 /etc/shadow
檔案(包含加密的使用者密碼),而該程式並不在授權列表中。透過 bpf_send_signal()
函式,系統在檔案實際被讀取前就終止了該程式,完全阻止了潛在的憑證竊取。
如果你對使用 Cilium Tetragon 檢測安全事件感興趣,Natália Réka Ivánkó 和 Jed Salazar 的報告《使用 eBPF 進行安全可觀測性》探討了更多細節。
eBPF 在網路安全中的應用
上一章討論了 eBPF 如何非常有效地實作網路安全機制。總結如下:
防火牆和 DDoS 保護是網路封包入口路徑早期附加 eBPF 程式的自然選擇。透過將 XDP 程式解除安裝到硬體,惡意封包甚至可能永遠不會到達 CPU!
對於實作更複雜的網路策略,如確定哪些服務允許彼此通訊的 Kubernetes 策略,附加到網路堆積積疊中的點的 eBPF 程式可以在確定封包不符合策略時丟棄它們。
網路安全工具通常以預防模式使用,丟棄封包而不只是稽核惡意活動。這是因為惡意行為者很容易發起與網路相關的攻擊;如果給裝置一個暴露在網際網路上的公共 IP 地址,不久就會開始看到可疑流量,因此組織被迫使用預防措施。
相比之下,許多組織在稽核模式下使用入侵檢測工具,並依靠取證來確定可疑事件是否真的是惡意的,以及需要採取什麼補救措施。如果特定安全工具過於粗糙與容易檢測到誤報,那麼它需要在稽核模式而非預防模式下執行也就不足為奇了。
eBPF 安全工具的演進趨勢
我認為 eBPF 正在實作更複雜的安全工具,具有更精細、準確的控制。正如我們今天認為防火牆足夠準確可用於預防模式,我們將看到預防性工具在其他非網路事件上的使用增加。這甚至可能包括將根據 eBPF 的控制作為應用程式產品的一部分封裝,使其能夠提供自己的執行時安全性。
在我參與的多個安全專案中,已經看到這種趨勢:從簡單的監控工具逐漸轉向具有預防能力的完整安全解決方案。特別是在容器化環境中,eBPF 提供的細粒度控制使得安全工具能夠在不影響效能的情況下提供更強大的保護。