Programmable Packet Filtering with Magic Firewall

Cloudflare secara aktif melindungi layanan dari serangan canggih hari demi hari. Untuk pengguna Magic Transit, perlindungan DDoS mendeteksi dan menghentikan serangan, sementara Magic Firewall mengizinkan aturan tingkat paket khusus, memungkinkan pelanggan untuk tidak menggunakan perangkat firewall perangkat keras dan memblokir traffic berbahaya di jaringan Cloudflare. Jenis serangan dan kecanggihan serangan terus berkembang, seperti yang ditunjukkan oleh DDoS dan serangan refleksi baru-baru ini terhadap layanan VoIP yang menargetkan protokol seperti Session Initiation Protocol (SIP). Melawan serangan ini membutuhkan dorongan batas penyaringan paket di luar kemampuan firewall tradisional. Kami melakukan ini dengan mengambil teknologi kelas terbaik dan menggabungkannya dengan cara baru untuk mengubah Magic Firewall menjadi firewall yang sangat cepat dan dapat diprogram agar dapat bertahan bahkan dari serangan paling canggih sekalipun.

Magical Walls of Fire

Magic Firewall adalah firewall paket stateless terdistribusi yang dibangun di atas nftables Linux. Ini berjalan pada setiap server, di setiap pusat data Cloudflare di seluruh dunia. Untuk memberikan isolasi dan fleksibilitas, aturan nftables setiap pelanggan dikonfigurasi dalam namespace jaringan Linux mereka sendiri.

Diagram ini menunjukkan aktivitas contoh paket saat menggunakan Magic Transit, yang memiliki Magic Firewall bawaan. Pertama, paket masuk ke server dan perlindungan DDoS diterapkan, yang menghentikan serangan sedini mungkin. Selanjutnya, paket dirutekan ke namespace jaringan khusus pelanggan, yang menerapkan aturan nftables ke paket. Setelah ini, paket dirutekan kembali ke asal melalui terowongan GRE. Pengguna Magic Firewall dapat membuat pernyataan firewall dari API tunggal, menggunakan sintaks Wirefilter yang fleksibel. Selain itu, aturan dapat dikonfigurasi melalui dasbor Cloudflare, menggunakan elemen seret dan lepas UI yang ramah.

Magic Firewall menyediakan sintaks yang sangat kuat untuk pencocokan pada berbagai parameter paket, tetapi juga terbatas pada kecocokan yang disediakan oleh nftables. Meskipun hal tersebut lebih dari cukup untuk berbagai kasus penggunaan, Magic Firewall tidak memberikan fleksibilitas yang cukup untuk menerapkan penguraian paket lanjutan dan pencocokan konten yang kami inginkan. Kami membutuhkan lebih banyak kekuatan.

Halo eBPF, temui Nftables!

Saat ingin menambahkan lebih banyak kekuatan untuk kebutuhan jaringan Linux Anda, Extended Berkeley Packet Filter (eBPF) adalah pilihan yang tepat. Dengan eBPF, Anda dapat menyisipkan program pemrosesan paket yang dijalankan di kernel, memberi Anda fleksibilitas paradigma pemrograman yang sudah dikenal dengan kecepatan eksekusi di dalam kernel. Cloudflare menyukai eBPF dan teknologi ini telah mengubah banyak produk kami. Tentu saja, kami ingin menemukan cara untuk menggunakan eBPF guna memperluas penggunaan nftables di Magic Firewall. Ini berarti dapat mencocokkan dengan menggunakan program eBPF dalam tabel dan rantai sebagai aturan. Dengan melakukan ini, kita dapat memiliki kue dan memakannya juga, dengan menjaga infrastruktur dan kode yang ada, dan memperluasnya lebih jauh.

Jika nftables dapat memanfaatkan eBPF secara alami, cerita ini akan jauh lebih pendek; sayangnya, kami harus melanjutkan pencarian kami. Untuk memulai pencarian, kami tahu bahwa iptables terintegrasi dengan eBPF. Misalnya, seseorang dapat menggunakan iptables dan program eBPF yang disematkan untuk menjatuhkan paket dengan perintah berikut:

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

Petunjuk ini membantu menempatkan kami di jalan yang benar. Iptables menggunakan ekstensi xt_bpf untuk dicocokkan dengan program eBPF. Ekstensi ini menggunakan jenis program eBPF BPF_PROG_TYPE_SOCKET_FILTER, yang memungkinkan kita memuat informasi paket dari buffer soket dan mengembalikan nilai berdasarkan kode kami.

Karena kami tahu iptables dapat menggunakan eBPF, mengapa tidak menggunakannya saja? Magic Firewall saat ini memanfaatkan nftables, yang merupakan pilihan tepat untuk kasus penggunaan kami karena fleksibilitasnya dalam sintaks dan antarmuka yang dapat diprogram. Jadi, kita perlu menemukan cara untuk menggunakan ekstensi xt_bpf dengan nftables.

Diagram ini membantu menjelaskan hubungan antara iptables, nftables dan kernel. nftables API dapat digunakan oleh program iptables dan nft userspace, dan dapat mengonfigurasi kecocokan xtables (termasuk xt_bpf) dan kecocokan nftables normal.

Ini berarti bahwa dengan panggilan API yang tepat (pesan netlink/netfilter), kita dapat menyematkan kecocokan xt_bpf ke dalam aturan nftables. Untuk melakukan ini, kita perlu memahami pesan netfilter mana yang perlu kita kirim. Dengan menggunakan alat seperti strace, Wireshark, dan terutama menggunakan sumber di mana kita dapat membuat pesan yang dapat menambahkan aturan eBPF dengan tabel dan rantai.

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	

Struktur pesan netlink/netfilter untuk menambahkan kecocokan eBPF akan terlihat seperti contoh di atas. Tentu saja, pesan ini perlu disematkan dengan benar dan menyertakan langkah bersyarat, seperti putusan, ketika ada kecocokan. Langkah selanjutnya adalah penguraian kode format ebpf_bytes seperti yang ditunjukkan pada contoh di bawah ini.

 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];
	};
};

Format byte dapat ditemukan dalam definisi header kernel dari struct xt_bpf_info_v1. Contoh kode di atas menunjukkan bagian yang relevan dari struktur.

Modul xt_bpf mendukung bytecode mentah, serta jalur ke program ebpf yang disematkan. Modus selanjutnya adalah teknik yang kami gunakan untuk menggabungkan program ebpf dengan nftables.

Dengan informasi ini kami dapat menulis kode yang dapat membuat pesan netlink dan membuat serial dengan benar dari setiap bidang data yang relevan. Pendekatan ini hanyalah langkah pertama, kami juga sedang mempertimbangkan untuk memasukkan ini ke dalam alat yang tepat daripada mengirim pesan netfilter kustom.

Cukup tambahkan eBPF

Sekarang kami perlu membuat program eBPF dan memuatnya ke dalam tabel dan rantai nftables yang ada. Mulai menggunakan eBPF bisa menjadi sedikit menakutkan. Jenis program mana yang ingin kami gunakan? Bagaimana kami mengkompilasi dan memuat program eBPF kami? Kami memulai proses ini dengan melakukan beberapa eksplorasi dan penelitian.

Pertama kami membuat program contoh untuk mencobanya.

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;
}

Potongan skrip di atas adalah contoh program eBPF yang hanya menerima paket yang memiliki rangkaian ajaib di akhir payload. Ini memerlukan pemeriksaan total panjang paket untuk menemukan di mana memulai pencarian. Untuk kejelasan, contoh ini menghilangkan pemeriksaan kesalahan dan header.

Setelah kami memiliki program, langkah selanjutnya adalah mengintegrasikannya ke dalam alat kami. Kami mencoba beberapa teknologi untuk memuat program, seperti BCC, libbpf, dan kami bahkan membuat pemuat khusus. Pada akhirnya, kami menggunakan pustaka ebpf cilium, karena kami menggunakan Golang untuk program bidang kontrol dan pustaka memudahkan untuk menghasilkan, menyematkan, dan memuat program eBPF.

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

Setelah program dikompilasi dan disematkan, kami dapat menambahkan kecocokan ke dalam nftables menggunakan perintah netlink. Daftar aturan menunjukkan bahwa terdapat kecocokan. Ini luar biasa! Kami sekarang dapat memberlakukan program C khusus untuk menyediakan pencocokan tingkat lanjut di dalam kumpulan aturan Magic Firewall!

Lebih Banyak Keajaiban

Dengan tambahan eBPF ke toolkit kami, Magic Firewall adalah cara yang lebih fleksibel dan kuat untuk melindungi jaringan Anda dari aktor jahat. Kami sekarang dapat melihat lebih dalam ke dalam paket dan menerapkan logika pencocokan yang lebih kompleks daripada yang dapat disediakan oleh nftables saja. Karena firewall kami berjalan sebagai perangkat lunak di semua server Cloudflare, kami dapat dengan cepat mengulangi dan memperbarui fitur.

Salah satu hasil dari proyek ini adalah perlindungan SIP, yang saat ini dalam versi beta. Itu hanya permulaan. Kami sedang menjajaki penggunaan eBPF untuk validasi protokol, pencocokan bidang lanjutan, melihat payload, dan mendukung kumpulan daftar IP yang lebih besar.

Kami juga menyambut bantuan Anda di sini! Jika Anda memiliki kasus penggunaan dan ide lain, harap diskusikan dengan tim akun Anda. Jika menurut Anda teknologi ini menarik, bergabunglah dengan tim kami!