Programmable Packet Filtering with Magic Firewall

クラウドフレアは、日々巧妙な攻撃から積極的にサービスを守っています。Magic Transitのユーザーの場合、DDoS保護対策が攻撃を検知してドロップする一方で、Magic Firewallによってパケットレベルのカスタムルールが可能なため、お客様はハードウェアファイアウォール機器を廃止してCloudflareのネットワークで悪意のあるトラフィックをブロックできるようになります。Session Initiation Protocol(SIP) などのプロトコルを標的とした攻撃が示すように、VoIPに対する攻撃の種類は最近のDDos攻撃やリフレクター攻撃として進化し続けています。このような攻撃に対抗するためには、従来のファイアウォールでは不可能なパケットフィルタリングの限界に挑戦する必要があります。私たちは、クラス最高の技術を取り入れ、それらを新しい方法で組み合わせることで、Magic Firewallを最も巧妙な攻撃にも耐えることができる、非常に高速で完全にプログラム可能なファイアウォールに変身させることができました。

マジカルウォール・オブ・ファイヤー

Magic Firewallは、Linuxのnftablesをベースに構築された分散型ステートレスパケットファイアウォールです。世界中のCloudflareデータセンターにあるすべてのサーバーで稼働しています。分離と柔軟性を提供するため、各お客様のnftablesルールはLinuxネットワークの名前空間内で構成されます。

This diagram shows how packets are processed by Magic Firewall on a Cloudflare server.

この図は、Magic Firewallを組み込んだ Magic Transitを使用した場合のパケットの一例を示しています。まず、パケットはサーバーに入り、DDoS防御が適用され、攻撃を可能な限り早期にドロップします。次に、パケットはお客様専用のネットワークネームスペースにルーティングされ、nftablesのルールがパケットに適用されます。この後、パケットはGREトンネルを経由して元の場所にルートバックされます。Magic Firewallのユーザーは、柔軟なWirefilter構文を使用してシングルAPIからファイアウォール文を構築することができます。さらに、ルールはCloudflareダッシュボードから、使いやすいUIのドラッグ&ドロップ要素を使用して設定することができます。

Magic Firewallは、様々なパケットパラメータにマッチする非常に強力な構文を提供しますが、nftablesが提供するマッチにも制限されます。多くのユースケースではこれで十分すぎるほどなのですが、私たちが望む高度なパケット解析やコンテンツマッチングを実装するには十分な柔軟性を備えているとは言えません。私たちはもっと大きな力を必要としていました。

eBPFの皆さん、こんにちは!Nftablesです。

Linux のネットワークにさらなるパワーを求める場合、Extended Berkeley Packet Filter ( eBPF  ) は当然の選択と言えます。eBPFでは、カーネル内で実行されるパケット処理プログラム を挿入でき、カーネル内実行の速度と、使い慣れたプログラミングパラダイムによる柔軟性を提供します。CloudflareはeBPF が大好きで、このテクノロジーは当社の多くの製品を実現する上で大きな変革をもたらしています。私たちがeBPFを使ってMagic Firewallのnftablesの使い方を拡張する方法を見つけたのも当然のことと言えます。つまり、テーブルの中でeBPFプログラムを使ってマッチングし、ルールとして連鎖させることができるようになるのです。こうすることで、既存のインフラとコードを維持したまま、さらに拡張することができます。私たちはケーキを持ちながら、さらにそれを食べることができるのです。

もしnftablesがeBPFをネイティブに活用できれば、この話はもっと短くなるはずですが、残念ながら、私たちは探求を続けなければなりませんでした。探索を始めるにあたって、iptablesがeBPFと統合されていることは知っています。例えば、次のコマンドでパケットをドロップするためにiptablesと固定されたeBPFプログラムを使用することができます。

iptables -A INPUT -m bpf --object-pinned /sys/fs/bpf/match -j DROP

このヒントのおかげで、正しい道に進むことができました。Iptablesは xt_bpf拡張を使用して、eBPFプログラムでマッチングします。この拡張機能はBPF_PROG_TYPE_SOCKET_FILTER eBPFプログラムタイプを使用し、ソケットバッファからパケット情報をロードして、コードに基づいた値を返すことができます。

iptablesがeBPFを使えることが分かっているのだから、それを使えばいいのでは?Magic Firewallは現在 nftablesを利用していますが、これは構文の柔軟性とプログラム可能なインターフェイスのため、私たちのユースケースには最適な選択です。したがって、xt_bpf拡張をnftablesで使用する方法を見つける必要があります。

このは、iptables、nftables、カーネル間の関係を説明するのに役立ちます。nftables APIはiptablesとnftの両方のユーザー空間プログラムで使用でき、 xtablesのマッチ(xt_bpf を含む)と通常のnftablesのマッチの両方を設定できます。

つまり、正しいAPIコール(netlink/netfilterメッセージ)があれば、nftablesルール内にxt_bpfマッチを埋め込むことができるのです。これを行うには、どのnetfilterメッセージを送信する必要があるかを理解する必要があります。straceやWiresharkなどのツールを使い、特にソースを使って、テーブルとチェーンを指定してeBPFルールを追加できるメッセージを作成することができました。

NFTA_RULE_TABLE table
NFTA_RULE_CHAIN chain
NFTA_RULE_EXPRESSIONS | NFTA_MATCH_NAME
	NFTA_LIST_ELEM | NLA_F_NESTED
	NFTA_EXPR_NAME "match"
		NLA_F_NESTED | NFTA_EXPR_DATA
		NFTA_MATCH_NAME "bpf"
		NFTA_MATCH_REV 1
		NFTA_MATCH_INFO ebpf_bytes	

eBPFマッチを追加するためのnetlink/netfilterメッセージの構造は、上記の例のようになるはずです。もちろん、このメッセージは適切に埋め込まれ、一致したときの評決などの条件付きステップを含む必要があります。次のステップは、下の例のように `ebpf_bytes` の形式をデコードすることでした。

 struct xt_bpf_info_v1 {
	__u16 mode;
	__u16 bpf_program_num_elem;
	__s32 fd;
	union {
		struct sock_filter bpf_program[XT_BPF_MAX_NUM_INSTR];
		char path[XT_BPF_PATH_MAX];
	};
};

バイトのフォーマットは、struct xt_bpf_info_v1のカーネルヘッダー定義にあります。上記のコード例は、構造体の関連部分を示しています。

xt_bpfモジュールは生のバイトコードと、ピン留めされたebpfプログラムへのパスの両方をサポートしています。後者のモードは、ebpfプログラムをnftablesと結合するために使用した手法です。

この情報をもとに、ネットリンクメッセージを作成し、関連するデータフィールドを適切にシリアライズするコードを作成することができました。このアプローチは最初のステップに過ぎません。私たちは、カスタムのネットフィルタメッセージを送る代わりに、これを適切なツールに取り入れることも検討しています。

eBPFを追加するだけ

あとは、eBPFのプログラムを組み立てて、既存のnftablesのテーブルとチェーンにロードする必要がありました。eBPFを使い始めるのは少し難しいかもしれません。どのプログラムタイプを使えばいいのか?eBPFのプログラムをどのようにコンパイルし、ロードすればよいのか?私たちは、このプロセスを、いくつかの調査と研究によって開始しました。

まず、試しにサンプルプログラムを構築してみました。

SEC("socket")
int filter(struct __sk_buff *skb) {
  /* get header */
  struct iphdr iph;
  if (bpf_skb_load_bytes(skb, 0, &iph, sizeof(iph))) {
    return BPF_DROP;
  }

  /* read last 5 bytes in payload of udp */
  __u16 pkt_len = bswap_16(iph.tot_len);
  char data[5];
  if (bpf_skb_load_bytes(skb, pkt_len - sizeof(data), &data, sizeof(data))) {
    return BPF_DROP;
  }

  /* only packets with the magic word at the end of the payload are allowed */
  const char SECRET_TOKEN[5] = "xyzzy";
  for (int i = 0; i < sizeof(SECRET_TOKEN); i++) {
    if (SECRET_TOKEN[i] != data[i]) {
      return BPF_DROP;
    }
  }

  return BPF_OK;
}

ご紹介したのは、ペイロードの末尾にマジック文字列を持つパケットのみを受け付けるeBPFプログラムの一例です。これは、検索を開始する場所を見つけるために、パケットの全長をチェックする必要があります。わかりやすくするために、この例ではエラーチェックとヘッダーを省略しています。

プログラムができたら、次はそれを我々のツールに統合することでした。プログラムをロードするために、BCCやlibbpfなどいくつかの技術を試し、カスタムのローダーも作りました。最終的には、 ciliumのebpfライブラリを使うことにしました。私たちは制御プレーンのプログラムにGolangを使っており、このライブラリによってeBPFプログラムの生成、埋め込み、読み込みが簡単にできるからです。

# nft list ruleset
table ip mfw {
	chain input {
		#match bpf pinned /sys/fs/bpf/mfw/match drop
	}
}

プログラムをコンパイルしてピン留めすれば、netlinkコマンドを使ってnftablesにマッチを追加することができます。ルールセットをリストアップすると、マッチが存在することがわかります。これは信じられないことです。Magic Firewallのルールセット内に高度なマッチングを提供するカスタムCプログラムをデプロイできるようになったのです!

その他のマジック

eBPFをツールキットに加えることで、Magic Firewallはあなたのネットワークを悪者から守るための、より柔軟で強力な方法となります。パケットをより深く調べ、nftablesだけでは実現できないような複雑なマッチングロジックを実装することができるようになりました。私たちのファイアウォールはすべてのCloudflareサーバー上でソフトウェアとして動作しているため、迅速に反復して機能を更新することができます。

このプロジェクトの成果のひとつが、現在ベータ版として公開されているSIPプロテクションです。これはまだ始まりに過ぎません。現在、プロトコルの検証、高度なフィールドマッチング、ペイロードの調査、さらに大規模なIPリストのセットのサポートにeBPFを使用することを検討しています。

こちらもぜひご協力をお願いします。他の使用例やアイデアがある場合は、担当のアカウントチームに相談してください。この技術に興味を持たれた方は、ぜひ 私たちのチームに参加してください  !

TwitterでつぶやくHacker Newsで話し合うRedditで話し合う

CIO Week Magic Firewall Magic Transit セキュリティ VoIP

Twitterでフォロー

Chris J Arges |@ChrisArges

Cloudflare |Cloudflare