GMOペパボの冬期インターンに参加してきました
概要
7日間のインターンシップで,内容としてはeBPFを使ったOSSの強化を行うものでした.
ペパボではバイトをしていた経験もあり,楽しい会社だとわかっていたのと,eBPF及びGoのキャッチアップをしたかったので応募しました.
agenda
目的
eBPFとGoの理解を深めたかったのが大きな目標です.
実は私は最近の流行(?)である,ライブラリに対するサプライチェーンアタックの対策について考える事があります.
eBPFでビルド時の通信をフィルタリングするなどして対策が出来ないものかと前々から構想していて,その為にもeBPFを理解しようと応募してみたところ,まさにその用途で使えるbouhekiの改修が業務内容でした.うれしい.
bouhekiについて
現在,bouhekiではeBPFを使ってconnect(2)
の接続先をFilteringしています.
設定は以下のようなフォーマットのyamlで行います.
network:
# Block or monitor the network.
# If block is specified, communication that matches the policy will be blocked.
mode: block # monitor or block. Default: monitor
# Restriction to the whole host or to a container
target: host # host or container. Default: host
cidr:
allow:
- 0.0.0.0/0
# - 10.0.1.1/24
# - 127.0.0.1/24
# Override "allow" list with exceptions. Default: []
deny: # []
- 10.0.1.71/32
# Restrictions by command name (optional).
command:
# Default: empty. All command will be allowed.
allow: []
# - curl
# Default: empty. All command will be allowed.
deny: []
# - wget
# - nc
# Restrictions by UID (optional).
uid:
allow:
- 0 # Default []
deny:
- 1000 # Default []
# Restrictions by GID (optional).
gid:
allow: []
# - 0
deny: []
# 1000
log:
# Log format(json or text). Default: json
format: json
# Specified log file location. Default: stdout
# output: /var/log/bouheki.log.json
# Maximum size to rotate (MB)
# max_size: 100
# Period for which logs are kept
# max_age: 365
- host及びcontainer内の通信に対し,
monitor/block
の2種の挙動を設定できます. - 監視対象はCIDR表記/domainの2種のフォーマットを用いて,
allow/deny
で指定できます. - 対象をコマンド/uid/gidで指定する事も可能です.
bouhekiは,ユーザ空間で動くプログラム(Go)と,カーネル空間で動くプログラム(eBPF)に分類され,それぞれ
ユーザ空間
- configをパースし,eBPF mapに流し込む
- Ring Bufferから情報を取り出し,ログに出力する
カーネル空間
connect
をフックし,mapの内容から接続の可否を判断する- Ring Bufferに各種情報を流し込む
やったこと
IPv6対応,DNS Lookupなどの機能追加を行いました.
- eBPFを活用したプログラム
- Cによるネットワーク周りのプログラミング
を触るのはほぼ初めてだった上,Goのコードもかなり久しぶりだった為,結構手こずった点はありますが,サポーターのmrtc0さん,udzuraさんに助けてもらいながら実装しました.
IPv6対応
これはユーザ/カーネル空間共に改修が必要でした.
ユーザ空間
- configにIPv6のアドレスが書けるようにする
- eBPF mapにIPv6のkeyを挿入できるようにする
カーネル空間
- IPv6用のmapのkeyを定義する
- mapのIPv6アドレスと照合させる
- Ring Bufferに流すデータのフォーマットを修正する
ドメインによる指定
こちらはユーザ空間のプログラムの改修だけで対応できます.
必要なのは
- configに書かれたドメインからIPアドレスを取得する
- 取得したアドレスをmapに挿入する
- 古いアドレスをmapから削除する
- 上記の処理を定期的に行う
ドメインに紐付いたIPアドレスが変わることもあるため,定期的な更新も実装する必要がありました.
反省点
TDDっぽい実装ができなかった
まず第一に,ユーザ空間で動くGoの部分に関してはもう少し細かい粒度でテストを書くべきだったと思っています.
第二に,言い訳にはなりますがeBPFプログラムのテストが難しかったという点があります.
以下,言い訳を連ねます.
- そもそもeBPFのテスト環境は整っていない
- bpfcovの導入は検討したが
- 別途ユーザ空間のコードを書く必要がある
- 環境構築が面倒 などの理由から断念した
- bpfcovの導入は検討したが
- eBPFの特性上,ユーザ/カーネルを跨ぐので切り分けが難しい
- その上今回はネットワークも跨ぐので難易度が上がる
感想
実装については,そもそもコードが読みやすかった事,本質的な部分に集中できるようなサポートをして頂けた事もあり,思っていたよりも戦えた感覚があります.
それ以外の点では,アルバイトをしていた頃には話したことが無かったメンバーとランチや面談で趣味の話が出来て非常に楽しかったです.
Comments