gosuda.org/chromenet: Sieťový zásobník Chromium pre Go, vytvorený reprodukovateľne z pevne určeného vydania

gosuda.org/chromenet je Go http.RoundTripper, ktorý odosiela požiadavky cez vlastný sieťový zásobník Chromium. Zásobník je //net z oficiálneho vydania Chromium: jeho implementácie HTTP/1.1, HTTP/2 a QUIC/HTTP/3, BoringSSL, konfigurácia TLS prehliadača Chrome a Chrome Root Store. Beží v samostatnom pracovnom procese chrome_net_server a strana Go zostáva čisto Go (CGO_ENABLED=0), pričom s pracovným procesom komunikuje prostredníctvom RPC so zdieľanou pamäťou HQQ.
1go get gosuda.org/chromenet@v0.1.10157081033800
Prečo spúšťať sieťový zásobník Chromium
Program v Go, ktorý komunikuje s webom pomocou net/http, sa voči serveru prezentuje ako klient Go. Je to správny predvolený postup pre komunikáciu medzi službami. Je to však nevhodné vtedy, keď správanie servera závisí od toho, za aký typ klienta ho považuje: koncové body chránené kontrolou prehliadača, middleware proti botom, CDN, ktoré poskytujú rôzne verzie protokolov podľa odtlačkov TLS a HTTP/2, a testy, ktoré musia reprodukovať to, čo zažíva skutočný prehliadač.
Štandardná knižnica Go nedokáže poskytnúť sieťové správanie prehliadača. crypto/tls má vlastné poradie šifrovacích sád a množinu rozšírení a net/http má vlastné nastavenia HTTP/2. Knižnice, ktoré napodobňujú prehliadač kopírovaním bajtov ClientHello, ich kopírujú vydanie za vydaním. chromenet ide inou cestou: nenapodobňuje sieťový zásobník Chromium, ale spúšťa ho.
Meraním na darwin/arm64 s verejnou službou na zisťovanie odtlačkov (tls.peet.ws) predvolená konfigurácia hlási:
| Vlastnosť | Pozorovaná hodnota |
|---|---|
| Dohodnutý protokol | h2 |
| JA4 | t13d1516h2_8daaf6152771_806a8c22fdea |
| Odtlačok HTTP/2 (formát Akamai) | 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p |
| User-Agent | desktopový Chrome s hlavnou verziou enginu, napr. Chrome/157.0.0.0 |
S ForceHTTP3 boli požiadavky na cloudflare-quic.com a www.google.com zodpovedané cez HTTP/3. Tieto čísla pochádzajú z jednej platformy v jeden deň. Engine je samotný zásobník, takže sa zmenia vždy, keď sa zmení pripnuté vydanie Chromium.
Použitie
NewTransport vracia http.RoundTripper. Použite ho všade, kde by ste použili 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}
Pri prvej požiadavke modul zapíše vloženú binárku pracovného procesu pre vašu platformu do používateľského adresára cache, overí jej digest SHA-256 pomocou gosuda.org/hqq/secureexec, spustí ju s rodičovským životným prepojením (--parent-stdin, takže skončí, keď skončí váš proces) a otvorí pool relácií. Nič nie je potrebné inštalovať.
Niekoľko správaní nasleduje http.Transport, aby http.Client fungoval bez zmien:
- Transport nesleduje presmerovania a neuchováva cookies.
http.Clientrobí obe veci, rovnako ako prihttp.Transport. - Odpovede komprimované pomocou gzip, deflate, Brotli alebo zstd prichádzajú dekódované a
resp.Uncompressedje true. - Požiadavka drží jednu reláciu enginu, kým sa telo odpovede nezatvorí. Telá zatvárajte.
- Zrušenie kontextu požiadavky zruší požiadavku vnútri Chromium.
- Sieťové chyby sú hodnoty
*client.NetErrorobsahujúce chybový kód Chromium a jeho názov:
1var ne *client.NetError
2if errors.As(err, &ne) {
3 fmt.Println(ne.Code, ne.Name) // -105 ERR_NAME_NOT_RESOLVED
4}
Možnosti
| Pole | Účinok |
|---|---|
UserAgent | Nahradí predvolený User-Agent Chrome. Hlavička požiadavky User-Agent má stále prednosť. |
AcceptLanguage | Nahradí en-US,en;q=0.9. |
Proxy | Pravidlá proxy Chromium, napríklad http://127.0.0.1:8080 alebo socks5://127.0.0.1:1080. Prázdna hodnota znamená priame pripojenie. |
DisableHTTP2, DisableQUIC | Vypnú protokoly. |
ForceHTTP3 | Použije QUIC pre každý pôvod bez čakania na oznámenie Alt-Svc. |
InsecureSkipVerify | Prijme ľubovoľný certifikát servera. |
Pool | Relácie enginu na použitie. Nil používa client.DefaultPool(), ktorý povoľuje 64 súbežných požiadaviek. |
Transporty s rovnakými možnosťami zdieľajú jeden Chromium URLRequestContext, takže connection pooly, relácie HTTP/2 a relácie QUIC sa znovu používajú naprieč požiadavkami. Nastavenie HQQ_CHROME_NET_SERVER=/path/to/chrome_net_server nahradí vložený pracovný proces, napríklad lokálnym buildom.
Ako to funguje
| Vrstva | Čo tam beží |
|---|---|
| Váš program | http.Client a chromenet.Transport, čisté Go. |
| Hranica procesov | RPC so zdieľanou pamäťou cez HQQ. Rozhranie je chrome_net.idl, ktoré hqq-bindgen skompiluje do klienta v Go a C glue kódu servera. |
chrome_net_server | Malá vrstva v C++ nad Chromium: jedno sieťové vlákno, ktoré vlastní všetky objekty //net, a vlákna relácií, ktoré blokujú, kým sieťové vlákno neodpovie na požiadavku. |
Chromium //net | URLRequestContext, URLRequest, relácie HTTP/2 a QUIC, BoringSSL. |
IDL obsahuje šesť operácií, ktoré sú dôležité: vytvorenie enginu z možností key=value, spustenie požiadavky s jej hlavičkou (METHOD URL plus riadky hlavičiek) a dĺžkou tela, zápis častí tela požiadavky, čakanie na hlavičku odpovede, čítanie dekódovaného tela a zničenie handle. Handle požiadavky deklaruje @cancel, takže opustené volanie preruší požiadavku vnútri Chromium namiesto toho, aby ju nechalo bežať až do konca.
Izolácia zásobníka v pracovnom procese je rovnako bezpečnostná vlastnosť ako vlastnosť balíkovania. Sieťový kód Chromium spracúva nedôveryhodný vstup z otvoreného internetu. Ak zlyhá, vaša služba v Go s ním nespadne: volanie zlyhá s chybou, relácia sa zahodí a klient pri ďalšej potrebe spustí nový pracovný proces.
Reprodukovateľne zostavené, pripnuté pomocou SHA-256
Binárka s veľkosťou 25 MB, ktorá obsahuje Chromium, je dôveryhodná len vtedy, ak viete presne povedať, čo do nej vstúpilo. chromenet sleduje Chromium Stable rovnako, ako gosuda.org/sqlite3 sleduje SQLite: zdroj sa nikdy nevendoruje, iba pripne.
| Pin | Kde |
|---|---|
Vydanie Chromium a jeho oficiálny archív zdrojov -lite | third_party/chromium/VERSION |
| SHA-256 archívu a každého nástroja, ktorý vydanie pripína: clang, Rust, GN, ninja, runtime clang pre Windows, xwin | third_party/chromium/SHA256 |
| Verzie MSVC CRT a Windows SDK sťahované cez xwin | third_party/chromium/WINSDK |
Vydanie sandbox runtime (rungv), digest obrazu hosťa, dátum snapshotu Debianu | scripts/engine/BUILDENV |
| C runtime HQQ | go.mod a go.sum |
Aktuálny pin je Chromium 157.0.8103.38. Stiahnutie, ktoré nezodpovedá svojmu pinu, zastaví zostavenie. Vydanie aplikuje na Chromium presne jednu záplatu: na Linuxe sa chrome_net_server zostavuje bez NSS a overuje certifikáty serverov iba voči Chrome Root Store, takže binárka potrebuje okrem libc nič iné.
Engine pre Linux a Windows sa zostavujú vnútri sandboxov gvx: neprivilegovaných procesov s pripnutým obrazom hosťa a bez démona na hostiteľovi. Engine pre Windows sa krížovo kompilujú na Linuxe pomocou clang-cl a lld-link od Chromium proti Windows SDK, ktoré xwin sťahuje od Microsoftu. Engine pre macOS sa zostavujú natívne na macOS runneri, pretože zostavenie Chromium pre macOS vyžaduje Xcode.
Opätovné zostavenie rovnakého commitu vytvorilo bajtovo identické binárky naprieč oddelenými behmi CI. Vydané binárky sú bajtovo identické s tými, ktoré zostavil skorší beh CI pre commit, ktorý dostal engine do monorepa, na všetkých šiestich platformách.
| Platforma | Veľkosť binárky |
|---|---|
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 |
Engine pre Linux závisia iba od libc, libm, libdl, libpthread a libgcc_s a vyžadujú glibc 2.31 alebo novšiu verziu.
Sledovanie Chromium a vydaní
Naplánovaný workflow kontroluje Chromium Stable každých šesť hodín. Pri novom vydaní prepíše piny, zostaví a otestuje všetkých šesť enginov vo vetve chromium/<version>, atestuje binárky a commitne piny spolu s vloženými binárkami, ich digestami a licenčnými textami do main v jednom commite. V monorepe súbory OWNERS pre jednotlivé súbory udeľujú botovi workflowu presne tieto súbory, takže aktualizácia sa dostane do repozitára bez ľudského kroku, zatiaľ čo zmeny samotného receptu zostavenia (pin Windows SDK a záplata) stále vyžadujú vlastníka.
Každá vydaná binárka nesie atestáciu pôvodu zostavenia z workflowu, ktorý ju zostavil:
1gh attestation verify chrome_net_server_linux_amd64 --repo gosuda/chromenet
Licenčné texty obsahu každej binárky (Chromium, BoringSSL, QUICHE a zvyšok zostavovacieho grafu) generuje vlastný nástroj Chromium tools/licenses/licenses.py a dodávajú sa s vydaním ako NOTICE_<os>_<arch>.txt.
Obmedzenia
DialWebSocketje čistý Go klient RFC 6455 a nepoužíva sieťový zásobník Chromium.DialWebTransportje implementácia v rámci procesu. Na server nedosiahne.- Predvolené odtlačky JA4 a HTTP/2 sú odtlačky pripnutého vydania Chromium. Chromium ich mení vydanie za vydaním a
chromenetich sleduje. - Binárky pre macOS sú podpísané ad-hoc a nie sú notarizované.
- Pracovný proces stojí pamäť. Každá binárka enginu má na disku 24 až 28 MB a runtime Chromium spúšťa thread pool a sieťové vlákno.
- Relácia enginu je držaná, kým sa telo odpovede nezatvorí. Program, ktorý necháva telá odpovedí neuzavreté, vyčerpá pool.
Zdroj
Repozitár je github.com/gosuda/chromenet, zrkadlený z //net/chromenet v monorepe gosuda. Recept zostavenia sa nachádza v scripts/engine, glue enginu v engine/ a rozhranie v chrome_net.idl.