gosuda.org/chromenet: Chromium hálózati veremje Go-hoz, reprodukálhatóan építve egy rögzített kiadásból

gosuda.org/chromenet egy Go http.RoundTripper, amely a kéréseket a Chromium saját hálózati veremén keresztül küldi el. A verem a hivatalos Chromium-kiadás //net komponense: ennek HTTP/1.1, HTTP/2 és QUIC/HTTP/3 implementációi, a BoringSSL, a Chrome TLS-konfigurációja és a Chrome Root Store. Külön munkafolyamatban fut, a chrome_net_server nevű folyamatban, a Go oldal pedig tiszta Go marad (CGO_ENABLED=0), és a munkafolyamattal az HQQ osztott memórián alapuló RPC-n keresztül kommunikál.
1go get gosuda.org/chromenet@v0.1.10157081033800
Miért futtassuk a Chromium hálózati vermét
Egy Go program, amely a net/http csomaggal kommunikál a weben, Go kliensként mutatkozik be a szerver felé. Ez a helyes alapbeállítás szolgáltatások közötti forgalomhoz. Helytelen viszont akkor, ha a szerver viselkedése attól függ, milyen típusú kliensnek gondolja a másik felet: böngészőhöz kötött végpontok, botellenes köztes szoftverek, olyan CDN-ek, amelyek eltérő TLS- és HTTP/2-ujjlenyomatokhoz eltérő protokollverziókat szolgálnak ki, valamint tesztek, amelyeknek egy valódi böngésző tapasztalatait kell reprodukálniuk.
A Go standard könyvtára nem tudja visszaadni a böngésző hálózati viselkedését. A crypto/tls saját titkosítócsomag-sorrenddel és kiterjesztéskészlettel dolgozik, a net/http pedig saját HTTP/2-beállításokkal. A böngészőt ClientHello-bájtjainak másolásával utánzó könyvtárak kiadásról kiadásra másolni kényszerülnek. A chromenet más utat választ: nem utánozza a Chromium hálózati vermét, hanem futtatja azt.
Az darwin/arm64 motoron, egy nyilvános ujjlenyomat-szolgáltatás (tls.peet.ws) ellen mérve, az alapértelmezett konfiguráció a következőket jelzi:
| Tulajdonság | Megfigyelt érték |
|---|---|
| Egyeztetett protokoll | h2 |
| JA4 | t13d1516h2_8daaf6152771_806a8c22fdea |
| HTTP/2 ujjlenyomat (Akamai formátum) | 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p |
| User-Agent | asztali Chrome a motor főverziójával, például Chrome/157.0.0.0 |
A ForceHTTP3 beállítással a cloudflare-quic.com és a www.google.com címre irányuló kéréseket HTTP/3-on válaszolták meg. Ezek a számok egy platformon, egy napon mérve születtek. A motor maga a verem, ezért ezek a számok változnak, amikor a rögzített Chromium-kiadás megváltozik.
Használat
A NewTransport egy http.RoundTrippert ad vissza. Bárhol használhatjuk, ahol http.Transport-ot használnánk:
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}
Az első kérésnél a modul kiírja a beágyazott, az adott platformhoz tartozó munkafolyamat-bináris fájlt a felhasználói gyorsítótár könyvtárába, ellenőrzi annak SHA-256 ellenőrző összegét a gosuda.org/hqq/secureexec csomaggal, elindítja szülői életjellel (--parent-stdin, így kilép, amikor a folyamata kilép), majd megnyit egy munkamenet-készletet. Semmit sem kell telepíteni.
Néhány viselkedés az http.Transport mintáját követi, így az http.Client változatlanul működik:
- A Transport nem követi az átirányításokat, és nem tárol sütiket. Ezt a
http.Clientteszi meg, ahogy azhttp.Transport-tal is. - A gzip, deflate, Brotli vagy zstd tömörítéssel érkező válaszok dekódolva érkeznek, és a
resp.Uncompressedértéke igaz. - Egy kérés addig tart egy motormunkamenetet, amíg a válasz törzse le nem záródik. A törzseket mindig zárjuk le.
- A kérés kontextusának megszakítása a kérést a Chromiumon belül is megszakítja.
- A hálózati hibák
*client.NetErrorértékek, amelyek tartalmazzák a Chromium hibakódját és nevét:
1var ne *client.NetError
2if errors.As(err, &ne) {
3 fmt.Println(ne.Code, ne.Name) // -105 ERR_NAME_NOT_RESOLVED
4}
Beállítások
| Mező | Hatás |
|---|---|
UserAgent | Lecseréli az alapértelmezett Chrome User-Agent értéket. A kérés User-Agent fejléce továbbra is felülírja. |
AcceptLanguage | Lecseréli a en-US,en;q=0.9 értéket. |
Proxy | Chromium proxyszabályok, például http://127.0.0.1:8080 vagy socks5://127.0.0.1:1080. Üres érték esetén közvetlen kapcsolat. |
DisableHTTP2, DisableQUIC | Kikapcsolják a protokollokat. |
ForceHTTP3 | Minden eredethez QUIC-et használ, anélkül hogy megvárná az Alt-Svc hirdetést. |
InsecureSkipVerify | Bármely szerver tanúsítványát elfogadja. |
Pool | A használandó motormunkamenetek. Nil esetén a client.DefaultPool() használatos, amely 64 egyidejű kérést enged. |
Az azonos beállításokkal rendelkező transportok egyetlen Chromium URLRequestContext-et osztanak meg, így a kapcsolatkészletek, HTTP/2-munkamenetek és QUIC-munkamenetek a kérések között újrahasznosulnak. A HQQ_CHROME_NET_SERVER=/path/to/chrome_net_server beállítás a beágyazott munkafolyamatot helyettesíti, például egy helyi build használatával.
Működés
| Réteg | Mi fut ott |
|---|---|
| Az Ön programja | http.Client és chromenet.Transport, tiszta Go. |
| Folyamathatár | HQQ osztott memórián alapuló RPC. Az interfész a chrome_net.idl, amelyet a hqq-bindgen fordít le a Go kliensbe és a C szerveroldali összekötő kódba. |
chrome_net_server | Egy kis C++ réteg a Chromium felett: egy hálózati szál, amely minden //net objektumot birtokol, és munkamenet-szálak, amelyek blokkolva várnak a kérésre, amíg a hálózati szál válaszol. |
Chromium //net | URLRequestContext, URLRequest, HTTP/2 és QUIC munkamenetek, BoringSSL. |
Az IDL hat lényeges műveletet tartalmaz: motor létrehozása key=value beállításokból, kérés indítása a fejléccel (METHOD URL plusz fejlécsorok) és a törzs hosszával, a kérés törzsének darabjainak írása, a válasz fejlécére várakozás, a dekódolt törzs olvasása, valamint a kezelők megsemmisítése. A kérésfogantyú deklarálja a @cancel jelölést, így egy elhagyott hívás a Chromiumon belül megszakítja a kérést, ahelyett hogy befejezéséig futna.
A verem munkafolyamatba való izolálása ugyanannyira biztonsági tulajdonság, mint csomagolási kérdés. A Chromium hálózati kódja a nyílt internetről érkező, ellenőrizetlen bemenetet dolgoz fel. Ha összeomlik, a Go szolgáltatás nem omlik össze vele együtt: a hívás hibával végződik, a munkamenetet eldobják, és a kliens a következő igénynél új munkafolyamatot indít.
Reprodukálhatóan épített, SHA-256 szerint rögzített
Egy 25 MB-os bináris fájl, amely a Chromiumot tartalmazza, csak akkor megbízható, ha pontosan meg tudjuk mondani, mi került bele. A chromenet úgy követi a Chromium Stable kiadásokat, ahogy a gosuda.org/sqlite3 a SQLite-ot: a forrást soha nem vendorizálja, csak rögzíti.
| Rögzítés | Hely |
|---|---|
A Chromium-kiadás és annak hivatalos -lite forrásarchívuma | third_party/chromium/VERSION |
| Az archívum és a kiadás által rögzített összes eszközlánc SHA-256 értéke: clang, Rust, GN, ninja, a Windows clang futtatókörnyezet, xwin | third_party/chromium/SHA256 |
| Az xwinen keresztül letöltött MSVC CRT és Windows SDK verziók | third_party/chromium/WINSDK |
A homokozó futtatókörnyezet (rungv) kiadása, a vendég image digest és a Debian snapshot dátuma | scripts/engine/BUILDENV |
| A HQQ C futtatókörnyezet | go.mod és go.sum |
A jelenlegi rögzítés a Chromium 157.0.8103.38. Amelyik letöltés nem egyezik a rögzítésével, az megállítja a buildet. A kiadás pontosan egy foltot alkalmaz a Chromiumra: Linuxon a chrome_net_server NSS nélkül épül, és a szerver-tanúsítványokat kizárólag a Chrome Root Store ellen ellenőrzi, így a bináris fájlhoz a libc-n kívül semmi más nem kell.
A Linux és Windows motorok gvx homokozókban épülnek: privilégiumok nélküli folyamatokban, rögzített vendég image-dzsel és host-daemon nélkül. A Windows motorokat Linuxon keresztetordítják (cross-compile) a Chromium clang-cl és lld-link eszközeivel, a xwin által a Microsofttól letöltött Windows SDK ellen. A macOS motorok natívan épülnek macOS runneren, mert a Chromium macOS-build Xcode-ot igényel.
Ugyanabból a commitból végzett újraépítések külön CI-futtatások között bájtra azonos binárisokat adtak. A kiadási binárisok bájtra azonosak azokkal, amelyeket egy korábbi CI-futtatás épített ahhoz a commithoz, amely a motort a monorepóba bevezette, mind a hat platformon.
| Platform | Bináris méret |
|---|---|
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 |
A Linux motorok csak a libc, libm, libdl, libpthread és libgcc_s könyvtáraktól függenek, és glibc 2.31 vagy újabb verziót igényelnek.
A Chromium és a kiadások követése
Egy ütemezett munkafolyamat hatóránként ellenőrzi a Chromium Stable kiadásait. Új kiadás esetén átírja a rögzítéseket, felépíti és teszteli mind a hat motort a chromium/<version> ágon, hitelesíti a binárisokat, majd a rögzítéseket egyetlen commitban, a beágyazott binárisokkal, azok digestjeivel és a licencszövegekkel együtt a main ágra commitolja. A monorepóban a fájlonkénti OWNERS bejegyzések pontosan ezekre a fájlokra adnak jogot a munkafolyamat botjának, így a frissítés emberi lépés nélkül kerül be, ugyanakkor az építési recept maga (a Windows SDK rögzítése és a folt) változtatásaihoz továbbra is tulajdonos jóváhagyása szükséges.
Minden kiadási bináris hordoz egy build-eredet attesztációt attól a munkafolyamattól, amely felépítette:
1gh attestation verify chrome_net_server_linux_amd64 --repo gosuda/chromenet
Az egyes binárisokban található összetevők licencszövegeit (Chromium, BoringSSL, QUICHE és a build-gráf többi része) a Chromium saját tools/licenses/licenses.py eszköze generálja, és a kiadással együtt NOTICE_<os>_<arch>.txt néven szállítjuk.
Korlátok
- A
DialWebSockettiszta Go RFC 6455 kliens, és nem használja a Chromium vermet. - A
DialWebTransportfolyamaton belüli megvalósítás. Nem éri el a szervert. - Az alapértelmezett JA4- és HTTP/2-ujjlenyomatok a rögzített Chromium-kiadásé. A Chromium ezeket kiadásonként megváltoztatja, a
chromenetpedig követi ezt. - A macOS binárisok ad-hoc aláírtak, és nincsenek notarizálva.
- A munkafolyamat-folyamatok memóriát igényelnek. Minden motorbináris 24–28 MB a lemezen, a Chromium futtatókörnyezete pedig szálkészletet és hálózati szálat indít.
- A motormunkamenet addig foglalt, amíg a válasz törzse le nem záródik. Az a program, amely nem zárja le a válasz törzseit, kimeríti a készletet.
Forrás
A tároló a github.com/gosuda/chromenet címen érhető el, a gosuda monorepó //net/chromenet könyvtárából tükrözve. Az építési recept a scripts/engine könyvtárban található, a motor összekötő kódja az engine/ könyvtárban, az interfész pedig a chrome_net.idl fájlban.