gosuda.org/chromenet: Pinned Bir Sürümden Yeniden Üretilebilir Biçimde Oluşturulmuş, Go için Chromium Ağ Yığını

gosuda.org/chromenet, Chromium'un kendi ağ yığını üzerinden istekleri gönderen bir Go http.RoundTripper uygulamasıdır. Yığın, resmi bir Chromium sürümünden alınan //net bileşenidir: HTTP/1.1, HTTP/2 ve QUIC/HTTP/3 uygulamaları, BoringSSL, Chrome'un TLS yapılandırması ve Chrome Root Store. Ayrı bir worker süreci olan chrome_net_server içinde çalışır; Go tarafı saf Go olarak kalır (CGO_ENABLED=0) ve worker ile HQQ paylaşımlı bellek RPC'si üzerinden haberleşir.
1go get gosuda.org/chromenet@v0.1.10157081033800
Neden Chromium'un Ağ Yığınını Çalıştırmalı
net/http ile web'e erişen bir Go programı, sunucuya kendisini bir Go istemcisi olarak tanıtır. Bu, servisler arası trafik için doğru varsayılandır. Ancak sunucunun davranışı, karşısındaki istemcinin türüne bağlı olduğunda yanlış bir seçimdir: tarayıcı kontrollü uç noktalar, bot karşıtı ara katmanlar, farklı TLS ve HTTP/2 parmak izlerine farklı protokol sürümleri sunan CDN'ler ve gerçek bir tarayıcının deneyimini birebir yeniden üretmesi gereken testler bu kapsamdadır.
Go standart kütüphanesi, bir tarayıcının ağ düzeyindeki davranışını taklit edemez. crypto/tls kendi cipher-suite sıralamasına ve extension kümesine sahiptir, net/http ise kendi HTTP/2 ayarlarını kullanır. Bir tarayıcıyı, ClientHello baytlarını kopyalayarak taklit eden kütüphaneler ise her sürümde kopyalamaya devam etmek zorunda kalır. chromenet ise farklı bir yol izler: Chromium ağ yığınını taklit etmez, doğrudan çalıştırır.
darwin/arm64 motorunda, herkese açık bir parmak izi servisine (tls.peet.ws) karşı yapılan ölçümlerde varsayılan yapılandırma şu sonuçları verir:
| Özellik | Gözlemlenen değer |
|---|---|
| Müzakere edilen protokol | h2 |
| JA4 | t13d1516h2_8daaf6152771_806a8c22fdea |
| HTTP/2 parmak izi (Akamai biçimi) | 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p |
| User-Agent | Motorun ana sürümüne ait masaüstü Chrome, örneğin Chrome/157.0.0.0 |
ForceHTTP3 etkinleştirildiğinde, cloudflare-quic.com ve www.google.com adreslerine yapılan istekler HTTP/3 üzerinden yanıtlandı. Bu sayılar tek bir platformdan, tek bir günde elde edilmiştir. Motor, yığının kendisi olduğundan, sabitlenmiş Chromium sürümü değiştiğinde bu sayılar da değişir.
Kullanım
NewTransport, bir http.RoundTripper döndürür. http.Transport kullanabileceğiniz her yerde bunu kullanabilirsiniz:
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}
İlk istekte modül, gömülü worker ikilisini platformunuz için kullanıcı önbellek dizinine yazar, SHA-256 özetini gosuda.org/hqq/secureexec ile doğrular, onu bir üst süreç yaşam bağıyla (--parent-stdin, böylece programınız sonlandığında worker da sonlanır) başlatır ve bir oturum havuzu açar. Herhangi bir şeyin kurulması gerekmez.
http.Client'ın değiştirilmeden çalışabilmesi için bazı davranışlar http.Transport ile aynıdır:
- Transport yönlendirmeleri takip etmez ve çerez saklamaz.
http.Clientisehttp.Transportile olduğu gibi bunların ikisini de yapar. - gzip, deflate, Brotli veya zstd ile sıkıştırılmış yanıtlar çözülmüş olarak gelir ve
resp.Uncompresseddeğeri true olur. - Bir istek, yanıt gövdesi kapatılana kadar bir motor oturumunu tutar. Gövdeleri kapatın.
- İstek bağlamını iptal etmek, Chromium içindeki isteği de iptal eder.
- Ağ hataları, Chromium hata kodunu ve adını taşıyan
*client.NetErrordeğerleridir:
1var ne *client.NetError
2if errors.As(err, &ne) {
3 fmt.Println(ne.Code, ne.Name) // -105 ERR_NAME_NOT_RESOLVED
4}
Seçenekler
| Alan | Etki |
|---|---|
UserAgent | Varsayılan Chrome User-Agent değerini değiştirir. İstek başlığındaki User-Agent yine de öncelikli olur. |
AcceptLanguage | en-US,en;q=0.9 değerini değiştirir. |
Proxy | http://127.0.0.1:8080 veya socks5://127.0.0.1:1080 gibi Chromium proxy kuralları. Boş bırakılırsa doğrudan bağlanır. |
DisableHTTP2, DisableQUIC | Bu protokolleri kapatır. |
ForceHTTP3 | Alt-Svc duyurusunu beklemeden her origin için QUIC kullanır. |
InsecureSkipVerify | Herhangi bir sunucu sertifikasını kabul eder. |
Pool | Kullanılacak motor oturumları. Nil ise, eşzamanlı 64 isteğe izin veren client.DefaultPool() kullanılır. |
Eşit seçeneklere sahip transport'lar tek bir Chromium URLRequestContext paylaşır; bu nedenle bağlantı havuzları, HTTP/2 oturumları ve QUIC oturumları istekler arasında yeniden kullanılır. HQQ_CHROME_NET_SERVER=/path/to/chrome_net_server ayarlandığında gömülü worker yerine belirtilen ikili kullanılır; örneğin yerel bir derleme için.
Nasıl Çalışır
| Katman | Burada çalışan |
|---|---|
| Programınız | http.Client ve chromenet.Transport, saf Go. |
| Süreç sınırı | HQQ paylaşımlı bellek RPC'si. Arayüz chrome_net.idl dosyasıdır ve hqq-bindgen tarafından Go istemcisine ve C sunucu katmanına derlenir. |
chrome_net_server | Chromium üzerinde küçük bir C++ katmanı: her //net nesnesine sahip tek bir ağ iş parçacığı ve yanıt gelene kadar bir isteği bekleyen oturum iş parçacıkları. |
Chromium //net | URLRequestContext, URLRequest, HTTP/2 ve QUIC oturumları, BoringSSL. |
IDL'de önemli olan altı işlem vardır: key=value seçeneklerinden bir motor oluşturmak, istek başlığı (METHOD URL ve başlık satırları) ile gövde uzunluğunu vererek bir istek başlatmak, istek gövdesi parçalarını yazmak, yanıt başlığını beklemek, çözülmüş gövdeyi okumak ve tutamaçları (handle) yok etmek. Bir istek tutamacı @cancel tanımlar; böylece terk edilen bir çağrı, isteğin tamamlanmasına kadar çalışmasına izin vermek yerine Chromium içinde isteği keser.
Yığını bir worker sürecinde izole etmek, paketleme kadar bir güvenlik özelliğidir de. Chromium'un ağ kodu, açık internetten gelen güvenilmeyen girdileri ayrıştırır. Çöktüğünde Go servisiniz onunla birlikte çökmez: çağrı bir hatayla başarısız olur, oturum atılır ve istemci bir sonraki ihtiyaçta yeni bir worker başlatır.
Yeniden Üretilebilir Biçimde Derlenen, SHA-256 ile Sabitlenen
İçinde Chromium bulunan 25 MB'lık bir ikili, tam olarak neyin içine girdiğini söyleyebildiğiniz sürece güvenilirdir. chromenet, gosuda.org/sqlite3'ün SQLite'ı izlediği gibi Chromium Stable sürümünü izler: kaynak asla vendor edilmez, yalnızca pin'lenir.
| Pin | Konum |
|---|---|
Chromium sürümü ve resmi -lite kaynak arşivi | third_party/chromium/VERSION |
| Arşivin ve o sürümün pin'lediği her araç zincirinin SHA-256 değerleri: clang, Rust, GN, ninja, Windows clang çalışma zamanı, xwin | third_party/chromium/SHA256 |
| xwin ile alınan MSVC CRT ve Windows SDK sürümleri | third_party/chromium/WINSDK |
Sandbox çalışma zamanı (rungv) sürümü, konuk imaj özeti, Debian snapshot tarihi | scripts/engine/BUILDENV |
| HQQ C çalışma zamanı | go.mod ve go.sum |
Mevcut pin Chromium 157.0.8103.38 sürümüdür. Pin'ine uymayan bir indirme derlemeyi durdurur. Sürüm, Chromium'a tam olarak bir yama uygular: Linux'ta chrome_net_server, NSS olmadan derlenir ve sunucu sertifikalarını yalnızca Chrome Root Store'a karşı doğrular; bu sayede ikili, libc dışında hiçbir şeye ihtiyaç duymaz.
Linux ve Windows motorları, gvx sandbox'larının içinde derlenir: sabitlenmiş bir konuk imajına sahip, ayrıcalıksız süreçlerdir ve ana makinede bir daemon bulundurmazlar. Windows motorları, Linux üzerinde Chromium'un clang-cl ve lld-link araçlarıyla, xwin'in Microsoft'tan aldığı Windows SDK'ya karşı çapraz derlenir. macOS motorları ise macOS runner'ında yerel olarak derlenir; çünkü Chromium'un macOS derlemesi Xcode gerektirir.
Aynı commit'in yeniden derlenmesi, ayrı CI çalışmaları arasında bayt bayt aynı ikilileri üretti. Sürüm ikilileri, motoru monorepo'ya ekleyen commit için daha önce bir CI çalışmasının derlediği ikililerle, altı platformun tamamında bayt bayt aynıdır.
| Platform | İkili boyutu |
|---|---|
linux/amd64 | 27.9 MB |
linux/arm64 | 27.4 MB |
darwin/amd64 | 26.2 MB |
darwin/arm64 | 23.9 MB |
windows/amd64 | 27.3 MB |
windows/arm64 | 25.8 MB |
Linux motorları yalnızca libc, libm, libdl, libpthread ve libgcc_s kütüphanelerine bağlıdır ve glibc 2.31 veya daha yenisini gerektirir.
Chromium'un ve Sürümlerin İzlenmesi
Zamanlanmış bir iş akışı, Chromium Stable sürümünü her altı saatte bir kontrol eder. Yeni bir sürüm bulunduğunda pin'leri yeniden yazar, chromium/<version> dalında altı motorun tamamını derleyip test eder, ikilileri onaylar ve pin'leri gömülü ikililer, özetleri ve lisans metinleriyle birlikte tek bir commit'te main dalına işler. Monorepo'da dosya bazlı OWNERS tanımları, iş akışının botuna yalnızca bu dosyaların güncellenme yetkisini verir; böylece güncelleme bir insan adımı olmadan birleşir. Ancak derleme tarifinin kendisindeki değişiklikler (Windows SDK pin'i ve yama) hâlâ bir sahibin onayını gerektirir.
Her sürüm ikilisi, onu derleyen iş akışından bir derleme kaynak doğrulama (build provenance attestation) taşır:
1gh attestation verify chrome_net_server_linux_amd64 --repo gosuda/chromenet
Her ikilinin içerdiği bileşenlerin lisans metinleri (Chromium, BoringSSL, QUICHE ve derleme grafiğinin geri kalanı), Chromium'un kendi tools/licenses/licenses.py aracıyla üretilir ve sürümle birlikte NOTICE_<os>_<arch>.txt dosyaları olarak dağıtılır.
Sınırlamalar
DialWebSocket, saf Go ile yazılmış bir RFC 6455 istemcisidir ve Chromium yığınını kullanmaz.DialWebTransport, süreç içi bir uygulamadır. Bir sunucuya ulaşmaz.- Varsayılan JA4 ve HTTP/2 parmak izleri, sabitlenmiş Chromium sürümüne aittir. Chromium bunları sürümden sürüme değiştirir ve
chromenetbu değişiklikleri takip eder. - macOS ikilileri ad-hoc imzalıdır ve notarize edilmemiştir.
- Bir worker süreci bellek tüketir. Her motor ikilisi diskte 24 ila 28 MB yer kaplar ve Chromium çalışma zamanı bir iş parçacığı havuzu ile bir ağ iş parçacığı başlatır.
- Bir motor oturumu, yanıt gövdesi kapatılana kadar tutulur. Yanıt gövdelerini sızdıran bir program havuzu tüketir.
Kaynak
Depo github.com/gosuda/chromenet adresinde yer alır ve gosuda monorepo'sundaki //net/chromenet dizininden yansıtılır. Derleme tarifi scripts/engine içinde, motor katmanı engine/ içinde ve arayüz chrome_net.idl içinde bulunur.