GoSuda

gosuda.org/chromenet: Stack Jaringan Chromium untuk Go, Dibangun Secara Reproducible dari Rilis yang Dipatok

By Lemon Mint views ...

gosuda.org/chromenet adalah http.RoundTripper Go yang mengirim permintaan melalui stack jaringan milik Chromium sendiri. Stack tersebut adalah //net dari rilis resmi Chromium: implementasi HTTP/1.1, HTTP/2, dan QUIC/HTTP/3, BoringSSL, konfigurasi TLS Chrome, serta Chrome Root Store. Stack ini berjalan dalam proses worker terpisah, chrome_net_server, dan sisi Go tetap murni Go (CGO_ENABLED=0), berkomunikasi dengan worker melalui RPC shared-memory HQQ.

1go get gosuda.org/chromenet@v0.1.10157081033800

Mengapa Menjalankan Stack Jaringan Chromium

Program Go yang berkomunikasi dengan web menggunakan net/http menampilkan dirinya sebagai klien Go kepada server. Itu adalah default yang tepat untuk lalu lintas antarlayanan. Namun, itu menjadi pilihan yang salah ketika perilaku server bergantung pada jenis klien yang dikiranya sedang dihadapi: endpoint yang hanya dapat diakses dari browser, middleware anti-bot, CDN yang menyajikan versi protokol berbeda untuk sidik jari TLS dan HTTP/2 yang berbeda, serta pengujian yang harus mereproduksi pengalaman browser sungguhan.

Pustaka standar Go tidak dapat memberikan perilaku wire browser. crypto/tls memiliki urutan cipher-suite dan kumpulan ekstensi sendiri, dan net/http memiliki pengaturan HTTP/2 sendiri. Pustaka yang meniru browser dengan menyalin byte ClientHello-nya harus terus disalin ulang setiap rilis. chromenet mengambil jalur lain: ia tidak meniru stack jaringan Chromium, melainkan menjalankannya.

Diukur pada engine darwin/arm64 terhadap layanan fingerprint publik (tls.peet.ws), konfigurasi default melaporkan:

PropertiNilai yang teramati
Protokol yang dinegosiasikanh2
JA4t13d1516h2_8daaf6152771_806a8c22fdea
Fingerprint HTTP/2 (format Akamai)1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p
User-AgentChrome desktop dengan versi mayor engine, misalnya Chrome/157.0.0.0

Dengan ForceHTTP3, permintaan ke cloudflare-quic.com dan www.google.com dijawab melalui HTTP/3. Angka-angka ini berasal dari satu platform pada satu hari. Engine adalah stack itu sendiri, sehingga angka tersebut berubah ketika rilis Chromium yang di-pin berubah.

Cara Menggunakan

NewTransport mengembalikan http.RoundTripper. Gunakan di mana pun Anda akan menggunakan http.Transport:

 1package main
 2
 3import (
 4	"fmt"
 5	"io"
 6	"net/http"
 7
 8	"gosuda.org/chromenet"
 9)
10
11func main() {
12	client := &http.Client{Transport: chromenet.NewTransport(chromenet.Options{})}
13
14	resp, err := client.Get("https://example.com/")
15	if err != nil {
16		panic(err)
17	}
18	defer resp.Body.Close()
19
20	body, _ := io.ReadAll(resp.Body)
21	fmt.Println(resp.Proto, resp.StatusCode, len(body))
22}

Pada permintaan pertama, modul menulis binary worker tertanam untuk platform Anda ke direktori cache pengguna, memverifikasi digest SHA-256-nya dengan gosuda.org/hqq/secureexec, menjalankannya dengan lifeline induk (--parent-stdin, sehingga worker keluar ketika proses Anda keluar), dan membuka session pool. Tidak ada yang perlu diinstal.

Beberapa perilaku mengikuti http.Transport agar http.Client dapat bekerja tanpa perubahan:

  • Transport tidak mengikuti redirect dan tidak menyimpan cookie. http.Client melakukan keduanya, sebagaimana halnya dengan http.Transport.
  • Respons yang dikompresi dengan gzip, deflate, Brotli, atau zstd diterima dalam keadaan sudah didekode, dan resp.Uncompressed bernilai true.
  • Sebuah permintaan menahan satu session engine hingga body respons-nya ditutup. Tutup body.
  • Membatalkan context permintaan akan membatalkan permintaan di dalam Chromium.
  • Kegagalan jaringan direpresentasikan sebagai nilai *client.NetError yang membawa kode dan nama error Chromium:
1var ne *client.NetError
2if errors.As(err, &ne) {
3	fmt.Println(ne.Code, ne.Name) // -105 ERR_NAME_NOT_RESOLVED
4}

Opsi

FieldEfek
UserAgentMenggantikan User-Agent Chrome default. Header permintaan User-Agent tetap diutamakan.
AcceptLanguageMenggantikan en-US,en;q=0.9.
ProxyAturan proxy Chromium seperti http://127.0.0.1:8080 atau socks5://127.0.0.1:1080. Nilai kosong berarti koneksi langsung.
DisableHTTP2, DisableQUICMenonaktifkan protokol tersebut.
ForceHTTP3Menggunakan QUIC untuk setiap origin tanpa menunggu advertisement Alt-Svc.
InsecureSkipVerifyMenerima sertifikat server apa pun.
PoolSession engine yang digunakan. Nil memakai client.DefaultPool(), yang mengizinkan 64 permintaan bersamaan.

Transport dengan opsi yang sama berbagi satu URLRequestContext Chromium, sehingga connection pool, session HTTP/2, dan session QUIC digunakan ulang di antara permintaan. Mengatur HQQ_CHROME_NET_SERVER=/path/to/chrome_net_server menggantikan worker tertanam, misalnya dengan build lokal.

Cara Kerjanya

LapisanYang berjalan di sana
Program Andahttp.Client dan chromenet.Transport, murni Go.
Batas prosesRPC shared-memory HQQ. Antarmukanya adalah chrome_net.idl, yang dikompilasi oleh hqq-bindgen menjadi klien Go dan glue server C.
chrome_net_serverLapisan C++ kecil di atas Chromium: satu network thread yang memiliki setiap objek //net, dan session thread yang memblokir permintaan hingga network thread memberikan jawaban.
Chromium //netURLRequestContext, URLRequest, session HTTP/2 dan QUIC, BoringSSL.

IDL memiliki enam operasi yang penting: membuat engine dari opsi key=value, memulai permintaan dengan head-nya (METHOD URL ditambah baris header) dan panjang body, menulis potongan body permintaan, menunggu head respons, membaca body yang sudah didekode, dan menghancurkan handle. Handle permintaan mendeklarasikan @cancel, sehingga panggilan yang ditinggalkan akan menginterupsi permintaan di dalam Chromium alih-alih membiarkannya berjalan hingga selesai.

Mengisolasi stack dalam proses worker adalah properti keamanan sekaligus properti pengemasan. Kode jaringan Chromium mem-parsing input berbahaya dari internet terbuka. Jika ia crash, layanan Go Anda tidak ikut crash: pemanggilan gagal dengan error, session dibuang, dan klien memulai worker baru saat dibutuhkan lagi.

Dibangun Secara Reproducible, Di-pin dengan SHA-256

Binary 25 MB yang berisi Chromium hanya dapat dipercaya jika Anda dapat menyatakan dengan tepat apa yang masuk ke dalamnya. chromenet mengikuti Chromium Stable sebagaimana gosuda.org/sqlite3 mengikuti SQLite: sumbernya tidak pernah di-vendor, hanya di-pin.

PinLokasi
Rilis Chromium dan arsip sumber -lite resminyathird_party/chromium/VERSION
SHA-256 dari arsip dan setiap toolchain yang di-pin oleh rilis tersebut: clang, Rust, GN, ninja, runtime clang Windows, dan xwinthird_party/chromium/SHA256
Versi MSVC CRT dan Windows SDK yang diambil melalui xwinthird_party/chromium/WINSDK
Rilis runtime sandbox (rungv), digest guest image, tanggal snapshot Debianscripts/engine/BUILDENV
Runtime C HQQgo.mod dan go.sum

Pin saat ini adalah Chromium 157.0.8103.38. Unduhan yang tidak cocok dengan pin-nya akan menghentikan build. Rilis ini menerapkan tepat satu patch ke Chromium: di Linux, chrome_net_server dibangun tanpa NSS dan memverifikasi sertifikat server hanya terhadap Chrome Root Store, sehingga binary tidak membutuhkan apa pun selain libc.

Engine Linux dan Windows dibangun di dalam sandbox gvx: proses tanpa hak istimewa dengan guest image yang di-pin dan tanpa daemon host. Engine Windows dikompilasi silang di Linux menggunakan clang-cl dan lld-link milik Chromium terhadap Windows SDK yang diambil xwin dari Microsoft. Engine macOS dibangun secara native pada runner macOS, karena build macOS Chromium membutuhkan Xcode.

Membangun commit yang sama menghasilkan binary yang identik byte demi byte di antara CI run yang terpisah. Binary rilis identik byte dengan binary yang dibangun oleh CI run sebelumnya untuk commit yang memasukkan engine ke monorepo, di keenam platform.

PlatformUkuran binary
linux/amd6427.9 MB
linux/arm6427.4 MB
darwin/amd6426.2 MB
darwin/arm6423.9 MB
windows/amd6427.3 MB
windows/arm6425.8 MB

Engine Linux hanya bergantung pada libc, libm, libdl, libpthread, dan libgcc_s, serta membutuhkan glibc 2.31 atau yang lebih baru.

Melacak Chromium dan Rilis

Workflow terjadwal memeriksa Chromium Stable setiap enam jam. Untuk rilis baru, ia menulis ulang pin, membangun dan menguji keenam engine pada branch chromium/<version>, mengattest binary, lalu melakukan commit pin bersama binary tertanam, digest-nya, dan teks lisensi ke main dalam satu commit. Di monorepo, OWNERS per file hanya memberikan bot workflow akses ke file-file tersebut, sehingga pembaruan masuk tanpa langkah manusia, sementara perubahan pada resep build itu sendiri (pin Windows SDK dan patch) tetap membutuhkan owner.

Setiap binary rilis membawa build provenance attestation dari workflow yang membangunnya:

1gh attestation verify chrome_net_server_linux_amd64 --repo gosuda/chromenet

Teks lisensi untuk setiap komponen yang terdapat dalam binary (Chromium, BoringSSL, QUICHE, dan sisa build graph) dihasilkan oleh tools/licenses/licenses.py milik Chromium sendiri dan dikirimkan bersama rilis sebagai NOTICE_<os>_<arch>.txt.

Batasan

  • DialWebSocket adalah klien RFC 6455 murni Go dan tidak menggunakan stack Chromium.
  • DialWebTransport adalah implementasi in-process. Ia tidak menjangkau server.
  • Fingerprint JA4 dan HTTP/2 default adalah milik rilis Chromium yang di-pin. Chromium mengubahnya dari rilis ke rilis, dan chromenet mengikutinya.
  • Binary macOS ditandatangani secara ad-hoc dan tidak dinotarisasi.
  • Sebuah proses worker memakan memori. Setiap binary engine berukuran 24 hingga 28 MB di disk, dan runtime Chromium memulai thread pool serta network thread.
  • Session engine ditahan hingga body respons ditutup. Program yang membocorkan body respons akan menghabiskan pool.

Sumber

Repositori berada di github.com/gosuda/chromenet, dicerminkan dari //net/chromenet di monorepo gosuda. Resep build berada di scripts/engine, glue engine di engine/, dan antarmuka di chrome_net.idl.