XDP で作って学ぶ
ファイアウォールとロードバランサー
1
SECCON Fukuoka Workshop 2023
※この資料はセキュリティミニキャンプ 2023 in 宮崎で使用した資料をアップデートしたものです
2
タイムテーブル
3
自己紹介
4
講義内容
5
概要編
6
eBPFとは
7
“eBPF is a revolutionary technology with origins in the Linux kernel that can run sandboxed programs in a privileged context such as the operating system kernel. It is used to safely and efficiently extend the capabilities of the kernel without requiring to change kernel source code or load kernel modules.”
eBPFとは
8
参考資料:(1)
eBPFプログラムはカーネル空間で動作する
カーネルにロードするeBPFプログラムはC言語で書く
ユーザー空間のプログラムは色々な言語で書ける
“e”BPF
9
参考資料:(1)
この子は eBee
“BPF originally stood for Berkeley Packet Filter, but now that eBPF (extended BPF) can do so much more than packet filtering, the acronym no longer makes sense.”
eBPF、流行ってる
10
eBPF、流行ってる 基盤になってる
11
eBPF のいいところ
12
eBPFができること
13
Tracing & Observability
Security
Networking
再び、eBPF とは?
14
参考資料:(5)
そもそも 、、Linux kernel?
15
フックポイント?
16
どうしてeBPFは安全?
17
①
③
④
②
⑤
eBPF Verifier
18
参考資料:(6)(7)
具体的な制約は基礎編で
BPF Map
19
ヘルパー関数
20
ヘルパー関数の使用例は基礎編で
XDPとは?
21
参考資料:(8)
XDP のいいところ
22
XDP のセキュリティ分野への活用
23
XDP in production
24
参考資料:(9)(10)(11)(12)
XDP のいいところ(再掲)
25
柔軟で高速なパケット処理
パケット処理
26
参考資料:(13)
プロトコルとヘッダ
27
ヘッダ
28
参考資料:(14)
ビット単位で意味がある
TCP/IP
29
XDP が有効な領域
30
大体このあたりまでが守備範囲
概要編終わり
31
準備編
32
terassyi/seccamp-xdp
33
使用するツールたち
34
環境のセットアップ
35
IPコマンド
36
参考資料:(15)
準備編終わり
37
基礎編
38
※基礎編はseccamp-xdp/tutorialのコードを使用します
はじめての XDP プログラム
39
見慣れない記法がいくつかある...?
はじめての XDP プログラム
40
①
②
④
③
vmlinux.h
41
①
参考資料:(16)
vmlinux.h
42
生成されたファイルの中を探すと右のような iphdr の定義が見つかるはず
SEC()と関数名
43
②
参考資料:(17)
プログラムタイプ/アタッチタイプ
44
参考資料:(17)
XDP Actions
45
③
ライセンス
46
④
参考資料:(18)(19)
ふたつめの XDP プログラム
47
①
②
③
Mapの宣言
48
参考資料:(20)
最初は形で覚えると良い
xdp_md 構造体とパケットへの変換
49
パケットデータの始まりへのポインタ
パケットデータの終わりへのポインタ
受信した/送信するNICのインデックス
XDPプログラムのエントリポイント関数の引数
Ethernetヘッダの構造体にキャスト
キャストしたEthernetヘッダ構造体の大きさがデータの大きさを越して無効なポインタへのアクセスをしていないか検査している
パケットのデータの始まりと終わりのポインタを記録しておく
消費したEthernetヘッダの構造体の大きさのぶんだけポインタをインクリメントしてIPv4ヘッダの先頭にポインタを合わせる
各種プロトコルのヘッダの構造体へのキャストは定型句として覚えてしまうと良い
ヘルパー関数の利用
50
参考資料:(21)
マップから取り出した値はNULL(無効なポインタ)かもしれないので必ず検査しないといけない
格納している値へのポインタが直接返ってくるので直に書き換え可能
値が取れなかったときは新たに値を挿入する
eBPF Verifier のためのC言語の制約
51
参考資料:(22)
※この資料は少し情報が古く、現在はグローバル変数は使えるようになっています
改めて、XDP プログラムを見てみる
書ける気がしてきた!!
52
動かしてみる
53
テスト用ネットワークの構成
テスト用ネットワーク作成
54
ビルド
55
make <file name>.bpf.o でもビルドできる
ロード
56
ビルドして生成されたオブジェクトファイル名
/sys/fs/bpf/<object name> という感じで指定する
アンロードするときは /sys/fs/bpf/<object name> を削除する
アタッチ
57
プログラムのid
デタッチはデバイス名だけ指定で良い
ipコマンドでのアタッチ
58
オブジェクトファイルを直接指定する
デバイス名を指定する
TARGETにオブジェクトの名前を指定
アタッチの確認
59
Generic XDP
60
参考資料:(23)
今回はこの Generic XDP を使います
動作確認
61
動作確認
62
動作確認
63
パケットカウンタも動かしてみる
64
基礎編終わり
65
実践編
66
パケットの気持ちになってコードを書いてみる
パケット処理プログラムのコツ
67
eBPFパケット処理プログラムのコツ
68
eBPFパケット処理プログラムのコツ
69
つくるもの
70
ハンズオン用インスタンスに入ってすぐはmainブランチにいます
ロードバランサー
71
L7
L4
処理するプロトコルによって特性や役割が異なる
ロードバランサーの利用例
72
scmlbの概要
73
動作イメージを掴む
74
mainブランチにいることを確認
動作イメージを掴む
75
動作イメージを掴む
76
動作イメージを掴む
77
動作イメージを掴む
78
10.0.2.0/24からのICMPを拒否する
10.0.3.0/24からの
8000〜9000番ポート宛のTCPを拒否する
動作確認終わり
79
handsonブランチにcheckoutしてコードを書いていきましょう
scmlbのプロジェクト構成
80
bpfディレクトリの構成
81
bpf/xdp.c
82
各機能の関数にtail callして処理をつなげていきます
tail call
83
受信したNICによって分岐
参考資料:(24)
STEP1: パケットカウンタ
84
今回利用するマップの定義
ここに書く!!
count()関数です
実装してみましょう
85
まずは写経でXDPプログラムを書くことに慣れましょう
STEP1: パケットカウンタ
86
テスト用ネットワーク
STEP1: パケットカウンタ
87
scmlb statコマンドで受信したパケット数を確認できる!!
STEP2: ファイアウォール
88
step2のテスト用ネットワーク
8080番にWebサーバーが公開されてる
host3とhost4からhost2にアクセスしてみる
STEP2: ファイアウォール
89
STEP2: ファイアウォール
90
7070ポートも開いてる...
参考資料:(25)(26)
STEP2: ファイアウォール
91
7070ポートも開いてる...
7070番ポートをファイアウォールを実装して塞いでみましょう!!
STEP2: ファイアウォール
92
STEP2: ファイアウォール
93
STEP2: ファイアウォール
94
参考資料:(27)(28)(29)
ID | PREFIX |
1 | 192.168.2.0/24 |
2 | 192.0.2.128/25 |
3 | 198.51.100.0/24 |
4 | 198.51.100.0/28 |
5 | 0.0.0.0/0 |
203.0.113.1はここにマッチ
192.0.2.252はここにマッチ
例えば...
STEP2: ファイアウォール
95
STEP2: ファイアウォール
96
PREFIX | IDs |
10.0.2.0/24 | 1, 3 |
10.0.3.0/24 | 2 |
192.168.0.0/24 | 4 |
0.0.0.0/0 | 5 |
ID | PROTOCOL | SRC PORT | DST PORT |
1 | ICMP | 0 | 0 |
2 | TCP | 0 | 8000-9000 |
3 | TCP | 30000-60000 | 0 |
4 | UDP | 0 | 1024-65535 |
5 | TCP | 0 | 1024-65535 |
adv_rulematcher(LPM_TRIE)
adv_rules(HASH)
受信したパケットの送信元アドレスからマッチするID一覧を取得する
取得したIDをもとにルールの中身を参照する
STEP2: ファイアウォール
97
ここに書く!!
STEP2: ファイアウォール
98
※seccamp-xdp/scmlb/README.mdにも図を記載しているのでそちらも参照してください
実装してみましょう
99
フローチャートを見ながらコードを記述しましょう
fw_match()関数は既に実装されています
難しければmainブランチを見ながらでOK
STEP2: ファイアウォール
100
$ make step2
$ make build
$ sudo ip netns exec host2 bin/scmlbd start —upstream h2-h0 —vip 203.0.113.11 —gc
STEP2: ファイアウォール
101
STEP2: ファイアウォール
102
STEP2: ファイアウォール
103
レスポンスが返ってこなくなっているはず!!
レスポンスが返ってくるはず!!
STEP3: ロードバランサー
104
ロードバランサーはコネクションを追跡できないといけない
今回はこれ
STEP3: ロードバランサー
105
参考資料:(30)(31)
STEP3: ロードバランサー
106
TCPコネクション
もしLBが適切なバックエンドにパケットを転送できなかったら...
STEP3: ロードバランサー
107
TCPコネクション
もしLBが適切なバックエンドにパケットを転送できなかったら...
STEP3: ロードバランサー
108
TCPコネクション
もしLBが適切なバックエンドにパケットを転送できなかったら...
TCPコネクションが切断されてしまう
STEP3: ロードバランサー
LBはコネクションを一意に判別して、かつその情報・状態を保存する必要がある
109
TCPコネクション
同一のコネクションのパケットは必ず同じバックエンドに転送する
STEP3: ロードバランサー
110
TCP/VIP:80
CLIENT1:30432
CLIENT2:59004
STEP3: ロードバランサー
LBはコネクションを一意に判別して、かつその情報・状態を保存する必要がある
111
保持する状態はプロトコルによる
STEP3: ロードバランサー
112
※ Guojunzheng - Create some pictures and made it gif, CC 表示-継承 3.0, https://commons.wikimedia.org/w/index.php?curid=24426758による
STEP3: ロードバランサー
113
複雑すぎるので簡略化
STEP3: ロードバランサー
114
5-tuple
状態
転送先のバックエンド
送信元のMACアドレス
パケット数
key
value
STEP3: ロードバランサー
115
STEP3: ロードバランサー
116
STEP3: ロードバランサー
117
(再掲)bpfディレクトリの構成
118
STEP3: ロードバランサー
119
backend_ifindexマップにコントロールプレーンからバックエンドの情報を登録
受信したデバイスの番号はxdp_md構造体から取得できる
パケットカウンタ→ファイアウォール→ロードバランサーの順に処理
バックエンドサーバーと繋がっているデバイスを登録
(再掲)tail call
120
受信したNICによって分岐
参考資料:(24)
(再掲)bpf/xdp.c
121
各機能の関数にtail callして処理をつなげていきます
STEP3: ロードバランサー
122
STEP3: ロードバランサー
123
backend構造体にリダイレクト先の情報を格納
リダイレクト
パケットをプロトコルに分けて処理
STEP3: ロードバランサー
124
upstreamの情報は起動時にコントロールプレーンから格納される
パケットをプロトコルに分けて処理
STEP3: ロードバランサー
125
STEP3: ロードバランサー
126
STEP3: ロードバランサー
127
STEP3: ロードバランサー
128
※seccamp-xdp/scmlb/README.mdにも図を記載しているのでそちらも参照してください
STEP3: ロードバランサー
129
ここの処理を記述
ここの処理を記述
STEP3: ロードバランサー
130
STEP3: ロードバランサー
131
STEP3: ロードバランサー
132
ここの処理を記述
STEP3: ロードバランサー
133
実装してみましょう
134
フローチャートを見ながらコードを記述しましょう
フローチャート内の各関数は既に実装されています
(猛者は一から実装してもOK)
ロジックがかなり複雑になっているのでmainブランチを見ながらでOK
STEP3: ロードバランサー(補足)
135
STEP3: ロードバランサー(補足)
136
INDEX | BACKEND_ID |
0 | 1 |
※1 | 3 |
2 | 4 |
rr_table
ID | BACKEND_INFO |
1 | 省略 |
3 | 省略 |
4 | 省略 |
backend_info
※ selected_backend_index
STEP3: ロードバランサー(補足)
137
bpf/include/csum.h に定義しています
実際は差分だけ計算してます
STEP3: ロードバランサー
138
ClientからLBのVIP(203.0.113.11)に向けてリクエストを投げる!!
STEP3: ロードバランサー
139
リクエストが登録したバックエンドから均等に返ってきている!!
scmlbdにバックエンドを登録
STEP3: ロードバランサー
140
Establishedなコネクションが見れる
コネクションを繋いだままにしておく
STEP3: ロードバランサー
141
STEP3: ロードバランサー
142
まとめ
143
参考資料
144
参考資料
145
参考資料
146
はまりどころ
147
これは時と場合によりますが,例として
左のコードは動かなくて,右は動きます
変数宣言の場所がよくなかった
はまりどころ
148
scmlb コントロールプレーン
149
Go言語の知識があったほうがよいです
Go言語入門はA Tour of Goがおすすめ
コントロールプレーン
150
コントロールプレーンの役割
151
使用技術(ライブラリ)
152
Daemonプログラム
153
XDPプログラムのアタッチ/デタッチ
154
XDPプログラムのアタッチ/デタッチ
155
bpf2goで自動生成される
ロードする際のオプションを色々指定できる
XDPプログラムのアタッチ/デタッチ
156
ロードしたプログラム・デバイス名を指定してアタッチ
明示的にGenericXDPを指定している
ロードバランサーコントロールプレーン
157
gc()はなにもやってませんでした...
sync()がGCもやってます
ロードバランサーコントロールプレーン
158
conntrakマップを走査
取得した各エントリを同期していく
conntrackの情報は常にデータプレーンの方が正しい前提で同期する
ロードバランサーコントロールプレーン
159
状態がClosedなエントリを削除する
最後のパケットから一定時間経ったエントリを削除する
conntrackマップからも削除
ロードバランサーコントロールプレーン
160
バックエンドと繋がっているデバイスにプログラムをアタッチする
BPFマップに登録するバックエンド情報を作成する
ロードバランサーコントロールプレーン
161
バックエンド1は新規コネクションを受け付けない
バックエンド1がUnavailableになってる
ロードバランサーコントロールプレーン
162
バックエンドの状態をUnavailableにする
BPFマップを更新して状態を同期する
adjustRrTable()でラウンドロビンの対象からそのバックエンドを除外する
ロードバランサーコントロールプレーン
163
バックエンドがUnavailableであることを確認
各種BPFマップから情報を削除する
機能拡張
164
おすすめの本
165
早くも日本語訳本が出版されます!!
おすすめの本
166
おすすめの本
167
おすすめのコンテンツ
168
ロゴの使用について
※この資料は、eBPF財団と提携しておらず、またその他のスポンサーでもありません。
169