書籍「Linux モニタリング用 BPF」

書籍「Linux モニタリング用 BPF」こんにちは、Habrozhiteliさん!BPF仮想マシンは、Linuxカーネルの最も重要なコンポーネントの1つです。これを適切に使用することで、システムエンジニアは障害を発見し、最も複雑な問題でも解決できるようになります。カーネルの動作を監視および変更するプログラムの作成方法、カーネルイベントを監視するためのコードを安全に挿入する方法などを学びます。David CalaveraとLorenzo FontanaがBPFの機能を理解するお手伝いをします。パフォーマンス最適化、ネットワーク、セキュリティに関する知識を広げましょう。- BPFを使用してLinuxカーネルの動作を監視および変更します。- カーネルを再コンパイルしたり、システムを再起動したりすることなく、カーネルイベントを安全に監視するためのコードを挿入します。- C、Go、またはPythonの便利なコードサンプルを使用します。- BPFプログラムのライフサイクルを管理することで状況を管理します。

Linuxカーネルのセキュリティ、その機能、そしてSeccomp

BPFは、安定性、セキュリティ、速度を犠牲にすることなくカーネルを拡張する強力な手段を提供します。そのため、カーネル開発者は、BPFプログラムでサポートされるSeccompフィルター(Seccomp BPFとも呼ばれます)を実装することで、その汎用性を利用してSeccompのプロセス分離を改善することが良いアイデアだと考えました。この章では、Seccompとは何か、どのように使用するのかを説明します。次に、BPFプログラムを使用してSeccompフィルターを作成する方法を学びます。さらに、Linuxセキュリティモジュール用にカーネルが提供する組み込みBPFフックについて説明します。

Linux Security Modules(LSM)は、さまざまなセキュリティモデルを標準化された方法で実装するために使用できる一連の機能を提供するフレームワークです。LSMは、Apparmor、SELinux、Tomoyoなどのカーネルソースツリーで直接使用できます。

まず、Linux の機能について説明します。

機能

Linux ケイパビリティの本質は、非特権プロセスに特定のタスクを実行する権限を付与する必要があるものの、そのために suid を使用せず、あるいはプロセスを特権化することで攻撃対象領域を縮小し、プロセスが特定のタスクを実行できるようにすることです。例えば、アプリケーションが特権ポート(例えば 80 番)を開く必要がある場合、プロセスを root として実行するのではなく、CAP_NET_BIND_SERVICE ケイパビリティを付与するだけで済みます。

main.go という名前の Go プログラムを考えてみましょう。

package main
import (
            "net/http"
            "log"
)
func main() {
     log.Fatalf("%v", http.ListenAndServe(":80", nil))
}

このプログラムはポート80(特権ポート)でHTTPサーバーとして動作します。通常はコンパイル直後に実行します。

$ go build -o capabilities main.go
$ ./capabilities

ただし、ルート権限を付与していないため、このコードはポートをバインドするときにエラーをスローします。

2019/04/25 23:17:06 listen tcp :80: bind: permission denied
exit status 1

capsh (シェル制御ツール) は、特定の機能セットを持つシェルを起動するツールです。

この場合、既に述べたように、完全なルート権限を付与する代わりに、プログラムに既に存在する他のすべての権限に加えて cap_net_bind_service 権限を付与することで、特権ポートのバインドを許可できます。これを行うには、プログラムを 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 - capsh をシェルとして使用します。
  • —caps='cap_net_bind_service+eip cap_setpcap,cap_setuid,cap_setgid+ep' — ユーザーを変更する必要があるため (root として実行したくない)、cap_net_bind_service と、実際にユーザー ID を root から nothing に変更する機能 (つまり、cap_setuid と cap_setgid) を指定します。
  • --keep=1 - ルート アカウントから切り替えるときに、インストールされている機能を保持します。
  • --user="nobody" — プログラムを実行するエンドユーザーは anyone になります。
  • --addamb=cap_net_bind_service — ルート モードから切り替えた後にバインドされた機能のクリーンアップを設定します。
  • — -c "./capabilities" — プログラムを実行するだけです。

関連付けられたケーパビリティは、現在のプログラムが execve() を使用して実行した際に子プログラムに継承される特別な種類のケーパビリティです。子プログラムに継承されるのは、関連付けられたケーパビリティ、つまり環境ケーパビリティとして解決されたケーパビリティのみです。

--caps オプションで機能を指定した後、+eip が何を意味するのか疑問に思うかもしれません。これらのフラグは、機能が以下のいずれかの機能を持つかどうかを判断するために使用されます。

- アクティブ化される必要があります (p);

-使用可能(e)

- 子プロセスに継承できます (i)。

cap_net_bind_service を使用するため、e フラグを付ける必要があります。次に、コマンドに対してシェルを実行します。これにより、capabilities バイナリが実行され、i フラグを付ける必要があります。最後に、p でこの機能を有効化します(UID は変更せずに実行しました)。これは cap_net_bind_service+eip のようになります。

結果はssで確認できます。出力はページに収まるように少し短くしていますが、関連付けられたポートとユーザーIDは0以外(この場合は65)が表示されます。

# ss -tulpn -e -H | cut -d' ' -f17-
128 *:80 *:*
users:(("capabilities",pid=30040,fd=3)) uid:65534 ino:11311579 sk:2c v6only:0

この例ではcapshを使用しましたが、libcapを使用してシェルを作成することもできます。詳細については、man 3 libcapを参照してください。

プログラムを作成するとき、開発者は実行時にプログラムに必要なすべての機能を事前に把握していないことが多く、さらに、これらの機能は新しいバージョンで変更される可能性があります。

プログラムの機能をよりよく理解するために、kprobe をカーネル関数 cap_capable に設定する BCC 対応ツールを使用できます。

/usr/share/bcc/tools/capable
TIME      UID  PID   TID   COMM               CAP    NAME           AUDIT
10:12:53 0 424     424     systemd-udevd 12 CAP_NET_ADMIN         1
10:12:57 0 1103   1101   timesync        25 CAP_SYS_TIME         1
10:12:57 0 19545 19545 capabilities       10 CAP_NET_BIND_SERVICE 1

カーネル関数 cap_capable で bpftrace と 1 行の kprobe を使用して同じことを実現できます。

bpftrace -e 
   'kprobe:cap_capable {
      time("%H:%M:%S ");
      printf("%-6d %-6d %-16s %-4d %dn", uid, pid, comm, arg2, arg3);
    }' 
    | grep -i capabilities

kprobe の後にプログラムの機能が有効になっている場合、次のような出力が表示されます。

12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 21 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 12 0
12:01:56 1000 13524 capabilities 10 1

1 列目はプロセスに必要な機能です。この出力には監査以外のイベントも含まれているため、すべての監査以外のチェックが表示され、最後に監査フラグ (出力の最後のフラグ) が 10 に設定された必要な機能が表示されます。ここで注目する機能は CAP_NET_BIND_SERVICE で、これは ID XNUMX で include/uapi/linux/ability.h のカーネル ソースに定数として定義されています。

/* Allows binding to TCP/UDP sockets below 1024 */
/* Allows binding to ATM VCIs below 32 */
#define CAP_NET_BIND_SERVICE 10<source lang="go">

ケーパビリティは、runCやDockerなどのコンテナの実行中に有効化されることが多く、コンテナは非特権モードで実行されますが、ほとんどのアプリケーションの実行に必要なケーパビリティのみが許可されます。アプリケーションが特定のケーパビリティを必要とする場合、Dockerは --cap-add を使ってそれらを提供します。

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

このコマンドは、コンテナに CAP_NET_ADMIN 機能を付与し、ネットワーク リンクを構成して dummy0 インターフェイスを追加できるようにします。

次のセクションでは、フィルタリングなどの機能の使用方法を示しますが、独自のフィルターをプログラムで実装できる別の方法を使用します。

セコンプ

SeccompはSecure Computingの略で、Linuxカーネルに実装されたセキュリティレイヤーです。開発者はSeccompを使用することで、特定のシステムコールをフィルタリングできます。SeccompはLinuxの機能に匹敵しますが、特定のシステムコールを制御できるため、Linuxよりもはるかに柔軟です。

SeccompとLinuxのケイパビリティは相互に排他的ではなく、両方のアプローチの利点を活かすために併用されることがよくあります。例えば、プロセスにCAP_NET_ADMINケイパビリティを付与しながら、acceptおよびaccept4システムコールをブロックすることで、ソケット接続の受け入れを禁止したい場合などです。

Seccomp フィルタリング方式は、SECCOMP_MODE_FILTER モードで動作する BPF フィルタに基づいており、システム コールのフィルタリングはパケットの場合と同じ方法で実行されます。

Seccompフィルタは、prctlのPR_SET_SECCOMPオペレーションを介してロードされます。これらのフィルタは、Seccompパケットごとに実行されるBPFプログラムであり、seccomp_data構造体で表されます。この構造体には、リファレンスアーキテクチャ、システムコール実行時のプロセッサ命令へのポインタ、およびuint64で表現される最大XNUMXつのシステムコール引数が含まれます。

linux/seccomp.h のカーネル ソース コードの seccomp_data 構造体は次のようになります。

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

この構造からわかるように、システム コール、その引数、またはその両方の組み合わせでフィルターできます。

Seccompパケットを受信したフィルタは、最終決定を下し、カーネルに次に何をすべきかを指示するための処理を実行する必要があります。最終決定は、戻り値(ステータスコード)の1つによって表されます。

— SECCOMP_RET_KILL_PROCESS — このため実行されないシステムコールをフィルタリングした後、直ちにプロセス全体を終了します。

— SECCOMP_RET_KILL_THREAD — これによって実行されないシステム コールをフィルタリングした後、直ちに現在のスレッドを終了します。

— SECCOMP_RET_KILL — SECCOMP_RET_KILL_THREAD のエイリアス。下位互換性のために残されています。

— SECCOMP_RET_TRAP — システム コールは禁止され、それを呼び出したタスクに SIGSYS (Bad System Call) シグナルが送信されます。

— SECCOMP_RET_ERRNO — システムコールが失敗し、SECCOMP_RET_DATAフィルタの戻り値の一部がerrno値としてユーザー空間に渡されます。エラーの原因に応じて異なるerrno値が返されます。エラー番号のリストは次のセクションに記載されています。

— SECCOMP_RET_TRACE — システムコールの実行時に、ptrace トレーサーに PTRACE_O_TRACESECCOMP を通知してインターセプトし、プロセスを監視および制御します。トレーサーが接続されていない場合はエラーが返され、errno は -ENOSYS に設定され、システムコールは実行されません。

— SECCOMP_RET_LOG — システムコールは許可され、ログに記録されます。

— SECCOMP_RET_ALLOW — システムコールは単純に許可されます。

ptraceは、トレース対象プロセスにトレース機構を実装するためのシステムコールであり、プロセスの実行を監視および制御する機能を備えています。トレーサーは、トレース対象プロセスの実行に効果的に影響を与え、メモリレジスタを変更できます。Seccompのコンテキストでは、ptraceはSECCOMP_RET_TRACEステータスコードによってトリガーされたときに使用されるため、トレーサーはシステムコールの実行を阻止し、独自のロジックを実装できます。

Seccompエラー

Seccomp を使用すると、SECCOMP_RET_ERRNO 型の戻り値で識別されるさまざまなエラーが発生することがあります。seccomp システムコールはエラーを報告するために、1 ではなく -0 を返します。

次のエラーが発生する可能性があります。

— EACCESS — 呼び出し元はシステムコールを実行できません。これは通常、CAP_SYS_ADMIN 権限を持っていないか、prctl で no_new_privs が設定されていないために発生します(これについては後述します)。

— EFAULT — 渡された引数 (seccomp_data 構造体の args) に有効なアドレスがありません。

— EINVAL — これには 4 つの理由が考えられます:

- 要求された操作は不明であるか、現在の構成のカーネルではサポートされていません。

- 指定されたフラグは要求された操作に対して無効です。

- 操作には BPF_ABS が含まれていますが、指定されたオフセットに問題があり、seccomp_data 構造体のサイズを超える可能性があります。

- フィルタに渡される命令の数が最大値を超えている。

— ENOMEM — プログラムを実行するためのメモリが不足しています。

— EOPNOTSUPP — 操作は、SECCOMP_GET_ACTION_AVAIL でアクションが利用可能であることを示していますが、カーネルは引数での戻りをサポートしていません。

— ESRCH — 別のスレッドを同期中に問題が発生しました。

— ENOSYS — SECCOMP_RET_TRACE アクションにトレーサーが接続されていません。

prctl は、バイト順序、スレッド名、セキュア コンピューティング モード (Seccomp)、特権、Perf イベントなど、プロセスの特定の側面をユーザー空間プログラムが制御 (設定および取得) できるようにするシステム コールです。

Seccompはサンドボックス技術のように見えるかもしれませんが、そうではありません。Seccompは、ユーザーがサンドボックス機構を開発できるようにするユーティリティです。では、Seccompシステムコールによって直接呼び出されるフィルターを使用して、ユーザーインタラクションプログラムがどのように作成されるかを見てみましょう。

BPF Seccompフィルタの例

ここでは、前に説明した 2 つのアクションを組み合わせる方法を説明します。

— 決定に応じて異なる戻りコードを持つフィルターとして使用される Seccomp BPF プログラムを作成します。

— 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_SECCOMPとCONFIG_SECCOMP_FILTERをyに設定してコンパイルされていることを確認する必要があります。実稼働マシンでは、以下の手順で確認できます。

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

残りのコードは install_filter 関数で、2つの部分から構成されています。最初の部分には、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),
  };

命令は、linux/filter.h で定義されている BPF_STMT および BPF_JUMP マクロを使用して設定されます。
手順を確認してみましょう。

— BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, arch))) — システムはワード BPF_W の形式で BPF_LD からロードして蓄積し、パケット データは固定オフセット BPF_ABS に配置されます。

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, arch, 0, 3) — BPF_JEQ を使用して、BPF_K アキュムレータ定数のアーキテクチャ値が arch と等しいかどうかを確認します。等しい場合は、次の命令のオフセット 0 にジャンプします。そうでない場合は、エラーをスローするために、オフセット 3 (この場合は arch が一致しない) にジャンプします。

— BPF_STMT(BPF_LD + BPF_W + BPF_ABS (offsetof(struct seccomp_data, nr))) — は、固定オフセット BPF_ABS に含まれるシステムコール番号であるワード BPF_W の形式で BPF_LD からロードして蓄積します。

— BPF_JUMP(BPF_JMP + BPF_JEQ + BPF_K, nr, 0, 1) — システムコール番号と変数nrの値を比較します。等しい場合は次の命令に進み、システムコールを禁止します。等しくない場合は、SECCOMP_RET_ALLOWを使用してシステムコールを許可します。

— BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ERRNO | (error & SECCOMP_RET_DATA)) — は BPF_RET でプログラムを終了し、結果として err 変数の番号とともにエラー SECCOMP_RET_ERRNO を返します。

— BPF_STMT(BPF_RET + BPF_K, SECCOMP_RET_ALLOW) — BPF_RET でプログラムを終了し、SECCOMP_RET_ALLOW でシステムコールを実行できるようにします。

SECCOMPはCBPFです
コンパイルされた ELF オブジェクトや JIT コンパイルされた C プログラムの代わりに、命令リストが使用される理由が疑問に思われるかもしれません。

これにはXNUMXつの理由があります。

• まず、Seccomp は eBPF の代わりに cBPF (クラシック BPF) を使用します。つまり、例でわかるように、レジスタはなく、計算の最後の結果を格納するアキュムレータのみがあります。

• 第二に、SeccompはBPF命令の配列へのポインタを直接受け取り、それ以外は何も行いません。ここで使用したマクロは、これらの命令をプログラマにとって使いやすい形式で指定するのに役立つだけです。

このアセンブリを理解するためにさらにサポートが必要な場合は、同じことを実行する次の疑似コードを検討してください。

if (arch != AUDIT_ARCH_X86_64) {
    return SECCOMP_RET_ALLOW;
}
if (nr == __NR_write) {
    return SECCOMP_RET_ERRNO;
}
return SECCOMP_RET_ALLOW;

socket_filter構造体でフィルタコードを定義したら、コードと計算されたフィルタ長を含むsock_fprogを定義する必要があります。このデータ構造体は、後でプロセスの動作を宣言するための引数として必要になります。

struct sock_fprog prog = {
   .len = (unsigned short)(sizeof(filter) / sizeof(filter[0])),
   .filter = filter,
};

install_filter 関数で残す作業は1つだけです。プログラム自体をロードするだけです!そのためには、prctl を使用し、オプションとして PR_SET_SECCOMP を指定してセキュアコンピューティングモードに入ります。次に、sock_fprog 型の prog 変数に格納されている SECCOMP_MODE_FILTER を使用して、フィルタをロードするようモードに指示します。

  if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog)) {
    perror("prctl(PR_SET_SECCOMP)");
    return 1;
  }
  return 0;
}

最後に、install_filter関数を使用できますが、その前にprctlを使用して、現在の実行に対してPR_SET_NO_NEW_PRIVSを設定する必要があります。これにより、子プロセスが親プロセスよりも高い権限を取得する状況を回避できます。これにより、install_filter関数内で以降のprctl呼び出しをroot権限なしで実行できるようになります。

これで、install_filter関数を呼び出すことができます。X86-64アーキテクチャに関連するすべての書き込みシステムコールをブロックし、すべての試行をブロックする権限を付与します。フィルターをインストールした後、最初の引数を使用して実行を続行します。

int main(int argc, char const *argv[]) {
  if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
   perror("prctl(NO_NEW_PRIVS)");
   return 1;
  }
   install_filter(__NR_write, AUDIT_ARCH_X86_64, EPERM);
  return system(argv[1]);
 }

さあ、始めましょう。プログラムをコンパイルするにはclangかgccのどちらかを使用できます。どちらの場合も、特別なオプションなしでmain.cファイルをコンパイルするだけです。

clang main.c -o filter-write

前述の通り、すべてのプログラムエントリをロックしました。これをテストするには、何かを出力するプログラムが必要です。ls が候補として適していると思われます。典型的な動作は以下のとおりです。

ls -la
total 36
drwxr-xr-x 2 fntlnz users 4096 Apr 28 21:09 .
drwxr-xr-x 4 fntlnz users 4096 Apr 26 13:01 ..
-rwxr-xr-x 1 fntlnz users 16800 Apr 28 21:09 filter-write
-rw-r--r-- 1 fntlnz users 19 Apr 28 21:09 .gitignore
-rw-r--r-- 1 fntlnz users 1282 Apr 28 21:08 main.c

素晴らしいですね!ラッパープログラムの使い方は次のようになります。テストしたいプログラムを最初の引数として渡すだけです。

./filter-write "ls -la"

このプログラムを実行すると、完全に空白の出力が生成されます。しかし、strace を使えば何が起こっているのか確認できます。

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

出力は非常に短くなっていますが、関連部分を見ると、レコードがEPERMエラー(設定したエラー)でブロックされていることがわかります。これは、writeシステムコールにアクセスできないため、プログラムが何も出力しないことを意味します。

[pid 25099] write(2, "ls: ", 4) = -1 EPERM (Operation not permitted)
[pid 25099] write(2, "write error", 11) = -1 EPERM (Operation not permitted)
[pid 25099] write(2, "n", 1) = -1 EPERM (Operation not permitted)

Seccomp BPFの仕組みと、それを使って何ができるのか、ご理解いただけたかと思います。しかし、cBPFではなくeBPFを使って同じことを実現し、そのパワーを最大限に活用できたら素晴らしいと思いませんか?

eBPFプログラムについて考えるとき、多くの人は単にプログラムを作成してroot権限でロードするだけと考えます。これは概ね正しいのですが、カーネルは様々なレベルでeBPFオブジェクトを保護するための一連のメカニズムを実装しています。これらのメカニズムはBPF LSMフックと呼ばれます。

BPF LSMトラップ

アーキテクチャに依存しないシステムイベント監視を実現するために、LSMはトラップの概念を実装しています。技術的には、トラップコールはシステムコールに似ていますが、システムから独立しており、インフラストラクチャに統合されています。LSMは、異なるアーキテクチャ上でシステムコールを使用する際に発生する問題を、抽象化レイヤーによって回避できるという新しい概念を提供します。

本稿執筆時点では、カーネルには BPF プログラムに関連するフックが 7 つあり、それらを実装する組み込み LSM は SELinux のみです。

フックのソース コードは、include/linux/security.h ファイルのカーネル ツリーにあります。

extern int security_bpf(int cmd, union bpf_attr *attr, unsigned int size);
extern int security_bpf_map(struct bpf_map *map, fmode_t fmode);
extern int security_bpf_prog(struct bpf_prog *prog);
extern int security_bpf_map_alloc(struct bpf_map *map);
extern void security_bpf_map_free(struct bpf_map *map);
extern int security_bpf_prog_alloc(struct bpf_prog_aux *aux);
extern void security_bpf_prog_free(struct bpf_prog_aux *aux);

それぞれは実行の異なる段階で呼び出されます。

— security_bpf — 実行された BPF システムコールの初期チェックを実行します。

— security_bpf_map — カーネルがマップのファイル記述子を返すタイミングをチェックします。

— security_bpf_prog — カーネルが eBPF プログラムのファイル記述子を返すタイミングをチェックします。

— security_bpf_map_alloc — BPF マップ内のセキュリティ フィールドが初期化されているかどうかを確認します。

— security_bpf_map_free — BPF マップ内のセキュリティ フィールドがクリアされているかどうかを確認します。

— security_bpf_prog_alloc — BPF プログラム内でセキュリティ フィールドが初期化されているかどうかを確認します。

— security_bpf_prog_free — BPF プログラム内のセキュリティ フィールドがクリアされているかどうかを確認します。

これらすべてを見ると、LSM BPF インターセプターの背後にある考え方は、各 eBPF オブジェクトを保護し、適切な権限を持つユーザーだけがカードやプログラムで操作を実行できるようにするというものであることがわかります。

まとめ

セキュリティは、保護したいものすべてに画一的な方法で実装できるものではありません。システムを様々なレベルと方法で保護できることが重要です。信じられないかもしれませんが、システムを保護する最善の方法は、異なる視点から異なる保護レイヤーを構築し、あるレベルでのセキュリティ侵害がシステム全体へのアクセスを許さないようにすることです。コア開発者は、様々なレイヤーとインタラクションポイントのセットを提供するという素晴らしい仕事をしてくれました。レイヤーとは何か、そしてBPFプログラムを使用してレイヤーを操作する方法について、皆さんにご理解いただけたことを願っています。

著者について

デビッド・カラベラ NetlifyのCTOです。Dockerサポートに携わり、Runc、Go、BCCといったオープンソースプロジェクトに貢献してきました。DockerプロジェクトやDockerプラグインエコシステムの開発で知られています。DavidはFlameGraphの大ファンで、常にパフォーマンスの最適化に取り組んでいます。

ロレンツォフォンタナ Sysdigのオープンソースチームに所属し、主にCloud Native Computing FoundationプロジェクトであるFalcoに取り組んでいます。Falcoは、カーネルモジュールとeBPFを介してコンテナランタイムセキュリティと異常検知を提供します。分散システム、ソフトウェア定義ネットワーク、Linuxカーネル、パフォーマンス分析に情熱を注いでいます。

» この本の詳細については、こちらをご覧ください。 出版社のウェブサイト
» 目次
» 抜粋

Khabrozhiteley の場合、クーポン使用で 25% 割引 - Linux

紙版の書籍をお支払いいただくと、電子書籍が電子メールで送信されます。

出所: habr.com

コメントを追加します