XDP 程式開發的核心在於定義高效的封包處理函式,並仔細處理封包偏移和填充,確保程式能正確解析不同網路協定層的資訊。開發過程中,需要包含必要的標頭檔,例如 linux/bpf.hlinux/if_ether.hlinux/tcp.h 等。完成程式撰寫後,應使用 Python 的 unittest 框架編寫測試案例,涵蓋各種情況和邊界條件,以驗證程式的正確性與效率。除了 XDP 程式開發,理解 Linux 核心安全機制也至關重要。Capabilities 可控制程式的許可權,Seccomp 則能限制程式的系統呼叫,兩者搭配使用能有效提升系統安全性。eBPF 作為一種強大的核心技術,也需要安全 HOOK 的保護,例如 security_bpfsecurity_bpf_map,以確保 eBPF 程式的安全執行和資源存取。

XDP 程式開發

XDP(Express Data Path)是一種高效的網路封包處理技術,允許開發人員在 Linux 核心中執行自定義的網路封包處理邏輯。以下是開發一個簡單的 XDP 程式的步驟:

  1. 定義 XDP 函式:首先,需要定義一個 XDP 函式,該函式將被 Linux 核心呼叫以處理網路封包。這個函式應該包含對封包的檢查和處理邏輯。
  2. 包含必要的標頭檔:XDP 程式需要包含必要的標頭檔,以便使用 Linux 核心提供的函式和結構。例如,linux/bpf.hlinux/if_ether.hlinux/tcp.h 等。
  3. 檢查封包偏移量和填充變數:在 XDP 函式中,需要檢查封包的偏移量和填充變數,以便存取封包的不同層(例如 Ethernet、IPv4、TCP)。
  4. 實作邏輯:根據需求,實作封包的檢查和處理邏輯。例如,檢查 TCP 封包的目的埠是否為 9090,如果是,則修改封包的目的 MAC 地址並傳回 XDP_TX。

XDP 程式測試

測試 XDP 程式是一個非常重要的步驟,以確保程式的正確性和效率。以下是測試 XDP 程式的步驟:

  1. 編寫測試案例:編寫測試案例以覆寫不同的情況和邊界條件。例如,測試 TCP 封包的目的埠是否為 9090。
  2. 使用 unittest 框架:使用 Python 的 unittest 框架來編寫和執行測試案例。
  3. 執行測試:執行測試案例以驗證 XDP 程式的正確性和效率。

範例程式碼

以下是範例程式碼,示範如何開發和測試一個簡單的 XDP 程式:

#define KBUILD_MODNAME "kmyprogram"
#include <linux/bpf.h>
#include <linux/if_ether.h>
#include <linux/tcp.h>
#include <linux/in.h>
#include <linux/ip.h>

int myprogram(struct xdp_md *ctx) {
    int ipsize = 0;
    void *data = (void *)(long)ctx->data;
    void *data_end = (void *)(long)ctx->data_end;
    struct ethhdr *eth = data;
    struct iphdr *ip;
    struct tcphdr *th;

    ipsize = sizeof(*eth);
    ip = data + ipsize;
    ipsize += sizeof(struct iphdr);

    if (data + ipsize > data_end) {
        return XDP_DROP;
    }

    if (ip->protocol == IPPROTO_TCP) {
        th = (struct tcphdr *)(ip + 1);

        if ((void *)(th + 1) > data_end) {
            return XDP_DROP;
        }

        if (th->dest == htons(9090)) {
            eth->h_dest[0] = 0x08;
            eth->h_dest[1] = 0x00;
            eth->h_dest[2] = 0x27;
            eth->h_dest[3] = 0xdd;
            eth->h_dest[4] = 0x38;
            eth->h_dest[5] = 0x2a;
            return XDP_TX;
        }
        return XDP_DROP;
    }
    return XDP_PASS;
}
import unittest

class TestXDPProgram(unittest.TestCase):
    def test_transform_dst(self):
        given_packet = Ether() / IP() / TCP(dport=9090)
        expected_packet = Ether(dst='08:00:27:dd:38:2a') / IP() / TCP(dport=9090)
        self._xdp_test_run(given_packet, expected_packet, BPF.XDP_TX)

if __name__ == '__main__':
    unittest.main()

修改XDP程式以觀察變化

我們可以透過修改program.c中的最後一個XDP_PASSXDP_DROP來觀察變化:

//...
if (udp_packet->dest == 53) {
    //...
    return XDP_DROP; // 修改為XDP_DROP
}
//...

測試結果分析

執行測試後,我們可以看到測試結果如下:

======================================================================
FAIL: test_pass_udp (__main__.XDPExampleTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
  File "test_xdp.py", line 48, in test_pass_udp
    self._xdp_test_run(given_packet, expected_packet, BPF.XDP_PASS)
  File "test_xdp.py", line 31, in _xdp_test_run
    self.assertEqual(test_retval.value, expected_return)
AssertionError: 1!= 2
----------------------------------------------------------------------
Ran 3 tests in 4.667s
FAILED (failures=1)

測試結果顯示,修改後的XDP程式導致測試失敗,錯誤資訊指出預期傳回值與實際傳回值不匹配。

MAC地址簡介

MAC地址(Media Access Control address)是一個唯一的識別碼,由兩組十六進位制數字組成,每個網路介面都有一個MAC地址,用於在資料連結層(OSI模型中的第2層)中透過技術如乙太網、藍牙和Wi-Fi來連線裝置。MAC地址在網路通訊中扮演著重要的角色,能夠確保資料包被正確地送達目的地裝置。

XDP 的應用案例

XDP(Express Data Path)是一種高效能的網路封包處理技術,能夠在網路卡級別對封包進行處理和過濾。以下是 XDP 的一些常見應用案例:

監控

監控是 XDP 的一個常見應用案例。傳統的監控系統通常需要在核心中實作,或者使用 socket filtering 或 Traffic Control 等技術。然而,這些方法可能會導致效能下降或複雜性增加。XDP 提供了一種更高效、更靈活的監控解決方案,能夠在網路卡級別對封包進行處理和過濾。

DDoS 緩解

DDoS(分散式拒絕服務)攻擊是一種常見的網路攻擊,能夠對網站或服務造成巨大的壓力。XDP 能夠在網路卡級別對封包進行過濾和丟棄,從而有效地緩解 DDoS 攻擊。透過使用 XDP,能夠在封包到達核心之前就將其丟棄,從而節省系統資源和提高效能。

負載平衡

負載平衡是一種常見的網路技術,能夠將流量分配到多個伺服器上,以提高系統的可用性和效能。XDP 能夠在網路卡級別對封包進行處理和轉發,從而實作負載平衡。透過使用 XDP,能夠在每個伺服器上實作負載平衡,從而提高系統的整體效能。

防火牆

防火牆是一種常見的網路安全技術,能夠控制進出網路的流量。XDP 能夠在網路卡級別對封包進行過濾和控制,從而實作防火牆功能。透過使用 XDP,能夠在每個伺服器上實作防火牆功能,從而提高系統的安全性。

Linux Kernel Security、Capabilities 和 Seccomp

Linux Kernel Security 是一種安全框架,能夠提供一系列的安全功能,以保護 Linux 系統的安全。Capabilities 是一種安全機制,能夠控制程式的許可權和存取權。Seccomp 是一種安全技術,能夠限制程式的系統呼叫,以提高系統的安全性。

Capabilities

Capabilities 是一種安全機制,能夠控制程式的許可權和存取權。透過使用 Capabilities,能夠將程式的許可權限制在最小化,從而提高系統的安全性。例如,能夠使用 Capabilities 來控制程式是否能夠繫結特定的埠或存取特定的檔案。

Seccomp

Seccomp 是一種安全技術,能夠限制程式的系統呼叫,以提高系統的安全性。透過使用 Seccomp,能夠將程式的系統呼叫限制在最小化,從而提高系統的安全性。例如,能夠使用 Seccomp 來限制程式是否能夠呼叫特定的系統函式或存取特定的系統資源。

Linux 核心安全、能力與 Seccomp

在 Linux 中,能力(Capabilities)是一種用於限制程式許可權的機制,允許系統管理員授予程式特定的許可權,而不需要授予它全部的 root 許可權。這對於提高系統安全性和防止程式濫用許可權非常重要。

使用 Capsh 限制能力

Capsh 是一個用於限制程式能力的工具。以下是一個使用 Capsh 限制能力的例子:

capsh --caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' \
  --keep=1 --user="nobody" \
  --addamb=cap_net_bind_service -- -c "./capabilities"

這個命令使用 Capsh 限制程式的能力,僅允許它繫結到特定的網路埠,並切換到 nobody 使用者。

Ambient 能力

Ambient 能力是一種特殊的能力,當程式啟動時,它會繼承父程式的能力。Ambient 能力可以透過 --addamb 選項新增。

驗證能力

可以使用 ss 命令驗證程式的能力:

ss -tulpn -e -H | cut -d' ' -f17-

這個命令會顯示程式繫結的網路埠和使用者 ID。

Capable 工具

Capable 工具可以用於顯示程式所需的能力。以下是一個使用 Capable 工具的例子:

/usr/share/bcc/tools/capable

這個工具會顯示程式所需的能力和相關的資訊。

Bpftrace 工具

Bpftrace 工具可以用於追蹤程式的能力要求。以下是一個使用 Bpftrace 工具的例子:

bpftrace -e 'kprobe:cap_capable {... }' | grep -i capabilities

這個工具會顯示程式所需的能力和相關的資訊。

容器化應用

在容器化應用中,能力可以用於限制容器的許可權。例如,Docker 可以使用 --cap-add 選項新增能力:

docker run -it --rm --cap-add=NET_ADMIN ubuntu ip link add dummy0 type dummy

這個命令會給予容器 NET_ADMIN 能力,允許它設定網路連結。

圖表翻譯:

以下是使用 Mermaid 語法繪製的程式能力限制流程圖:

  flowchart TD
    A[開始] --> B[限制能力]
    B --> C[驗證能力]
    C --> D[顯示結果]
    D --> E[結束]

這個流程圖顯示了限制程式能力、驗證能力和顯示結果的流程。

內容解密:

上述內容介紹了 Linux 核心安全、能力與 Seccomp 的相關知識。能力是一種用於限制程式許可權的機制,允許系統管理員授予程式特定的許可權,而不需要授予它全部的 root 許可權。Capsh 是一個用於限制程式能力的工具,Ambient 能力是一種特殊的能力,當程式啟動時,它會繼承父程式的能力。可以使用 ss 命令驗證程式的能力,Capable 工具和 Bpftrace 工具可以用於顯示程式所需的能力和相關的資訊。在容器化應用中,能力可以用於限制容器的許可權。

Seccomp:Linux 核心安全、能力和系統呼叫過濾

Seccomp 是 Secure Computing 的縮寫,是 Linux 核心中實作的一層安全機制,允許開發人員過濾特定的系統呼叫(syscalls)。雖然 Seccomp 與能力(capabilities)相似,但其控制特定系統呼叫的能力使其更加靈活。

Seccomp 和能力並不相互排斥,通常一起使用以結合兩者的優點。例如,您可能想要給予一個程式 CAP_NET_ADMIN 能力,但不允許它接受通訊端連線。

Seccomp 的過濾機制根據 BPF(Berkeley Packet Filter)過濾器,使用 SECCOMP_MODE_FILTER 模式,系統呼叫過濾與封包過濾類別似。Seccomp 過濾器使用 prctl 載入,透過 PR_SET_SECCOMP 運作,過濾器以 BPF 程式的形式表達,並在每個 Seccomp 封包上執行。

以下是 Seccomp 封包的結構,包含參考架構、CPU 指令指標和最多六個系統呼叫引數:

struct seccomp_data {
    int nr;
    __u32 arch;
    __u64 instruction_pointer;
    __u64 args[6];
};

這允許根據系統呼叫、引數或兩者的組合進行過濾。

Seccomp 傳回值

Seccomp 過濾器在處理每個 Seccomp 封包後,必須做出最終決定,以告知核心下一步該怎麼做。這些決定透過傳回值(狀態碼)表達,包括:

  • SECCOMP_RET_KILL_PROCESS:立即終止整個程式。
  • SECCOMP_RET_KILL_THREAD:立即終止當前執行緒。
  • SECCOMP_RET_TRAP:禁止系統呼叫,並向呼叫任務傳送 SIGSYS 訊號。
  • SECCOMP_RET_ERRNO:不執行系統呼叫,並將錯誤號傳回給使用者空間。
  • SECCOMP_RET_TRACE:通知 ptrace 追蹤器攔截系統呼叫,以觀察和控制其執行。
  • SECCOMP_RET_LOG:允許系統呼叫並記錄。
  • SECCOMP_RET_ALLOW:直接允許系統呼叫。

Ptrace 和 Seccomp

Ptrace 是一個系統呼叫,用於實作程式追蹤機制,允許觀察和控制程式的執行。在 Seccomp 中,Ptrace 用於當 Seccomp 觸發時,允許追蹤器預防系統呼叫的執行,並實作自己的邏輯。

Seccomp 錯誤

在使用 Seccomp 時,您可能會遇到不同的錯誤。Seccomp 系統呼叫傳回 -1 而不是 0 以指示錯誤發生。可能的錯誤包括多種情況,詳細的錯誤號可以在相關檔案中查詢。

內容解密:

以上內容介紹了 Seccomp 的基本概念、過濾機制、傳回值以及與 Ptrace 的關係。Seccomp 提供了一種靈活的方式來控制程式的系統呼叫,增強了 Linux 系統的安全性。透過使用 Seccomp,開發人員可以更好地控制和限制程式的行為,防止潛在的安全風險。

圖表翻譯:

  flowchart TD
    A[Seccomp 啟動] --> B[載入 BPF 過濾器]
    B --> C[過濾系統呼叫]
    C --> D{是否允許}
    D -->|是| E[允許系統呼叫]
    D -->|否| F[禁止系統呼叫並傳回錯誤]
    F --> G[記錄錯誤]

這個流程圖展示了 Seccomp 的基本工作流程,從啟動到載入過濾器,然後根據過濾規則決定是否允許或禁止系統呼叫,並對應地傳回結果或記錄錯誤。

Linux 核心安全、能力與 Seccomp

在 Linux 中,Seccomp 是一種機制,允許使用者空間程式控制特定的程式方面,例如端序、執行緒名稱、安全計算(Seccomp)模式、許可權、Perf 事件等。Seccomp 不是一種沙盒機制,而是一種工具,讓使用者可以開發自己的沙盒機制。

Seccomp 的錯誤碼

Seccomp 可能會傳回以下錯誤碼:

  • EACCESS:呼叫者沒有足夠的許可權執行 syscall,通常是因為沒有 CAP_SYS_ADMIN 許可權或沒有設定 no_new_privs
  • EFAULT:傳遞的引數(seccomp_data 結構中的 args)沒有有效的地址。
  • EINVAL:可以有四種含義:
    • 要求的操作不被 Seccomp 支援。
    • 指定的旗標對於要求的操作無效。
    • 操作包含 BPF_ABS,但指定的偏移量可能超過 seccomp_data 結構的大小。
    • 傳遞給過濾器的指令數量超過最大允許數量。
  • ENOMEM:沒有足夠的記憶體來執行程式。
  • EOPNOTSUPP:操作指定了 SECCOMP_GET_ACTION_AVAIL,但核心實際上不支援傳回動作。
  • ESRCH:在同步其他執行緒時出現問題。
  • ENOSYS:沒有附加到 SECCOMP_RET_TRACE 動作的追蹤器。

Seccomp BPF 篩選器範例

以下範例展示如何組合兩個動作:

  1. 寫入 Seccomp BPF 程式作為篩選器,根據其決策傳回不同的程式碼。
  2. 使用 prctl 載入篩選器。

首先,需要包含標準函式庫和 Linux 核心的標頭檔:

#include <errno.h>
#include <linux/audit.h>
#include <linux/bpf.h>
#include <linux/filter.h>
#include <linux/seccomp.h>
#include <linux/unistd.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <unistd.h>

在嘗試執行此範例之前,需要確保核心已經編譯了 CONFIG_SECCOMPCONFIG_SECCOMP_FILTER,並設定為 y。在 live 機器上,可以使用以下命令檢查:

cat /proc/config.gz | zcat | grep -i CONFIG_SECCOMP

接下來是 install_filter 函式,它由兩部分組成。第一部分包含 BPF 篩選指令列表:

static int install_filter(int nr, int arch, int error) {
    struct sock_filter filter[] = {
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, arch))),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3),
        BPF_STMT(BPF_LD + BPF_W + BPF_ABS, (offsetof(struct seccomp_data, nr))),
        BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1),
        BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)),
        BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW),
    };

指令使用 BPF_STMTBPF_JUMP 巨集定義在 linux/filter.h 中。

讓我們逐步分析指令:

  • BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))):載入並累積以 BPF_LD 形式的字 BPF_W,並且封包資料包含在固定 BPF_ABS 偏移量中。
  • BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3):檢查以 BPF_JEQ 是否架構值在累積器常數 BPF_K 中等於 arch。如果是,則跳轉到下一指令;否則,跳轉到偏移量為 3 的指令,以產生錯誤,因為架構不匹配。
  • BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))):載入並累積以 BPF_LD 形式的字 BPF_W,其中包含系統呼叫號碼資料,在固定 BPF_ABS 偏移量中。

圖表翻譯:

  graph LR
    A[開始] -->|載入架構|> B[檢查架構]
    B -->|匹配|> C[載入系統呼叫號碼]
    B -->|不匹配|> D[產生錯誤]
    C -->|檢查系統呼叫號碼|> E[允許或錯誤]
    E -->|允許|> F[傳回 SECCOMP_RET_ALLOW]
    E -->|錯誤|> G[傳回 SECCOMP_RET_ERRNO]

這個流程圖描述了 Seccomp BPF 篩選器的邏輯流程,從載入架構到檢查系統呼叫號碼,並根據結果傳回允許或錯誤程式碼。

Seccomp BPF 基礎與實作

Seccomp(Short for Secure Computing)是一種 Linux 核心安全機制,允許使用者空間程式限制自己能夠呼叫的系統呼叫(system call),藉此增強系統的安全性。Seccomp BPF(Berkeley Packet Filter)是 Seccomp 的一部分,利用 BPF 來過濾系統呼叫。

Seccomp 是 cBPF

Seccomp 使用的是 cBPF(classic BPF),而不是 eBPF(extended BPF)。cBPF 與 eBPF 的主要差異在於 cBPF 沒有暫存器,只有一個累加器(accumulator)來儲存計算結果。

Seccomp 指令

Seccomp 的指令以 BPF 指令的形式存在,包括 BPF_JUMPBPF_STMT 等。以下是 Seccomp 中的一個例子:

BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1)

這行指令比較系統呼叫編號 nr 是否等於特定值,如果相等則跳至下一指令,否則允許系統呼叫。

Seccomp 程式結構

一個 Seccomp 程式由多個 BPF 指令組成,每個指令都有一個特定的功能。程式的結尾通常是 BPF_RET 指令,表示程式的終止。

安裝 Seccomp 篩選器

要安裝一個 Seccomp 篩選器,需要先定義篩選器的程式碼,然後使用 prctl 系統呼叫將其載入核心。以下是安裝篩選器的範例:

struct sock_fprog prog = {
   .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
   .filter = filter,
};
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
    perror("prctl(PR_SET_SECCOMP)");
    return 1;
}

測試 Seccomp 篩選器

要測試 Seccomp 篩選器,可以使用 strace 工具觀察系統呼叫的行為。以下是測試篩選器的範例:

strace -f./filter-write "ls -la"

這個範例會執行 ls -la 命令,並觀察系統呼叫的行為。如果篩選器設定正確,應該會看到系統呼叫被阻止的相關訊息。

eBPF 與 BPF LSM Hooks

eBPF 是 BPF 的擴充套件版本,提供了更多的功能和彈性。BPF LSM Hooks 是 Linux 核心安全模組(LSM)的一部分,提供了對 eBPF 物件的保護機制。這些 Hooks 可以用於控制 eBPF 程式的行為,並提供額外的安全性檢查。

eBPF 安全HOOK介紹

eBPF(extended Berkeley Packet Filter)是一種高階的Linux核心技術,允許開發人員在核心空間執行自定義程式碼。為了確保eBPF的安全性,Linux核心提供了一系列的安全HOOK,包括:

1. security_bpf

此HOOK負責對執行的eBPF系統呼叫進行初始檢查,確保只有授權的程式才能執行eBPF相關操作。

2. security_bpf_map

當核心傳回一個對映(map)的檔案描述符時,此HOOK會被觸發,進行安全檢查,以確保只有具有適當許可權的程式才能存取對映。

深入剖析 XDP 和 Seccomp 等 Linux 核心技術後,我們可以發現,它們為網路和系統安全提供了強大的工具。多維比較分析顯示,XDP 在網路封包處理方面展現出優異的效能,尤其在 DDoS 緩解和負載平衡等場景下,其效率遠超傳統方案。然而,XDP 的開發和佈署也存在一定的技術門檻,需要開發者深入理解 Linux 核心和網路協定堆疊。Seccomp 則提供更細粒度的系統呼叫控制,有效限制程式行為,提升系統安全性。但 Seccomp 的組態相對複雜,需要仔細設計 BPF 規則以避免誤攔截正常操作。技術限制深析指出,兩種技術都需要謹慎組態以免影響系統穩定性。從技術演進角度,eBPF 的持續發展將為 XDP 和 Seccomp 帶來更多可能性,例如更豐富的程式功能和更精細的安全控制。玄貓認為,隨著 eBPF 技術和生態的日趨成熟,XDP 和 Seccomp 將在更多場景下發揮關鍵作用,成為構建高效能、高安全系統的根本。對於追求極致效能和安全性的系統,深入研究並應用這些技術將帶來顯著的效益。