GoSuda

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

By Lemon Mint views ...

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ágMegfigyelt érték
Egyeztetett protokollh2
JA4t13d1516h2_8daaf6152771_806a8c22fdea
HTTP/2 ujjlenyomat (Akamai formátum)1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p
User-Agentasztali 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.Client teszi meg, ahogy az http.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
UserAgentLecseréli az alapértelmezett Chrome User-Agent értéket. A kérés User-Agent fejléce továbbra is felülírja.
AcceptLanguageLecseréli a en-US,en;q=0.9 értéket.
ProxyChromium 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, DisableQUICKikapcsolják a protokollokat.
ForceHTTP3Minden eredethez QUIC-et használ, anélkül hogy megvárná az Alt-Svc hirdetést.
InsecureSkipVerifyBármely szerver tanúsítványát elfogadja.
PoolA 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étegMi fut ott
Az Ön programjahttp.Client és chromenet.Transport, tiszta Go.
FolyamathatárHQQ 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_serverEgy 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 //netURLRequestContext, 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ésHely
A Chromium-kiadás és annak hivatalos -lite forrásarchívumathird_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, xwinthird_party/chromium/SHA256
Az xwinen keresztül letöltött MSVC CRT és Windows SDK verziókthird_party/chromium/WINSDK
A homokozó futtatókörnyezet (rungv) kiadása, a vendég image digest és a Debian snapshot dátumascripts/engine/BUILDENV
A HQQ C futtatókörnyezetgo.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.

PlatformBináris méret
linux/amd6427,9 MB
linux/arm6427,4 MB
darwin/amd6426,2 MB
darwin/arm6423,9 MB
windows/amd6427,3 MB
windows/arm6425,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 DialWebSocket tiszta Go RFC 6455 kliens, és nem használja a Chromium vermet.
  • A DialWebTransport folyamaton 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 chromenet pedig 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.