GoSuda

gosuda.org/chromenet: Chromiums Network Stack for Go, bygget reproduserbart fra en Pinned Release

By Lemon Mint views ...

gosuda.org/chromenet er en Go-http.RoundTripper som sender forespørsler gjennom Chromiums egen nettverksstakk. Stakken er //net fra en offisiell Chromium-utgivelse: dens implementasjoner av HTTP/1.1, HTTP/2 og QUIC/HTTP/3, BoringSSL, Chromes TLS-konfigurasjon og Chrome Root Store. Den kjører i en egen arbeidsprosess, chrome_net_server, og Go-siden forblir ren Go (CGO_ENABLED=0), og kommuniserer med arbeidsprosessen via HQQ shared-memory RPC.

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

Hvorfor kjøre Chromiums nettverksstakk

Et Go-program som kommuniserer med nettet via net/http fremstår for serveren som en Go-klient. Dette er riktig standard for trafikk mellom tjenester. Det er feil når serverens oppførsel avhenger av hva slags klient den tror den kommuniserer med: endepunkter som er låst til nettleser, anti-bot-mellomvare, CDN-er som leverer ulike protokollversjoner til ulike TLS- og HTTP/2-fingeravtrykk, og tester som må gjenskape det en ekte nettleser opplever.

Go sitt standardbibliotek kan ikke gi deg en nettleser sin nettverksoppførsel. crypto/tls har sin egen rekkefølge for chiffersuiter og sitt eget sett med utvidelser, og net/http har sine egne HTTP/2-innstillinger. Biblioteker som etterligner en nettleser ved å kopiere dens ClientHello-bytes, må fortsette å kopiere, utgivelse etter utgivelse. chromenet tar en annen vei: den etterligner ikke Chromiums nettverksstakk, den kjører den.

Målt med darwin/arm64-motoren mot en offentlig tjeneste for fingeravtrykk (tls.peet.ws) rapporterer standardkonfigurasjonen:

EgenskapObservert verdi
Forhandlet protokollh2
JA4t13d1516h2_8daaf6152771_806a8c22fdea
HTTP/2-fingeravtrykk (Akamai-format)1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p
User-Agentdesktop-Chrome med motorens hovedversjon, f.eks. Chrome/157.0.0.0

Med ForceHTTP3 ble forespørsler til cloudflare-quic.com og www.google.com besvart over HTTP/3. Disse tallene stammer fra én plattform på én dag. Motoren er stakken, så tallene endres når den fastlåste Chromium-utgivelsen endres.

Bruk

NewTransport returnerer en http.RoundTripper. Bruk den der du ellers ville brukt 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}

Ved den første forespørselen skriver modulen den innebygde arbeidsbinærfilen for din plattform til brukerens cachekatalog, verifiserer SHA-256-digesten med gosuda.org/hqq/secureexec, starter den med en forelderlivline (--parent-stdin, slik at den avsluttes når prosessen din avsluttes), og åpner en sesjonspool. Det trenger ikke installeres noe.

Noen atferder følger http.Transport, slik at http.Client fungerer uendret:

  • Transporten følger ikke omdirigeringer og lagrer ingen informasjonskapsler. http.Client gjør begge deler, slik den gjør med http.Transport.
  • Svar komprimert med gzip, deflate, Brotli eller zstd kommer dekomprimert, og resp.Uncompressed er true.
  • En forespørsel holder én motorsesjon til responskroppen er lukket. Lukk kroppene.
  • Å avbryte forespørselskonteksten avbryter forespørselen inne i Chromium.
  • Nettverksfeil er *client.NetError-verdier som inneholder Chromiums feilkode og feilnavn:
1var ne *client.NetError
2if errors.As(err, &ne) {
3	fmt.Println(ne.Code, ne.Name) // -105 ERR_NAME_NOT_RESOLVED
4}

Alternativer

FeltEffekt
UserAgentErstatter standard Chrome-User-Agent. En User-Agent-forespørselsheader har fortsatt forrang.
AcceptLanguageErstatter en-US,en;q=0.9.
ProxyProxy-regler for Chromium, for eksempel http://127.0.0.1:8080 eller socks5://127.0.0.1:1080. Tom verdi betyr direkte tilkobling.
DisableHTTP2, DisableQUICSlår av protokollene.
ForceHTTP3Bruker QUIC for alle opprinnelser uten å vente på en Alt-Svc-annonsering.
InsecureSkipVerifyGodtar ethvert serversertifikat.
PoolMotorsesjoner som skal brukes. Nil bruker client.DefaultPool(), som tillater 64 samtidige forespørsler.

Transporter med like alternativer deler én Chromium-URLRequestContext, slik at tilkoblingspooler, HTTP/2-sesjoner og QUIC-sesjoner gjenbrukes på tvers av forespørsler. Å sette HQQ_CHROME_NET_SERVER=/path/to/chrome_net_server erstatter den innebygde arbeidsprosessen, for eksempel med en lokal build.

Slik fungerer det

LagHva som kjører der
Ditt programhttp.Client og chromenet.Transport, ren Go.
ProsessgrenseHQQ shared-memory RPC. Grensesnittet er chrome_net.idl, kompilert av hqq-bindgen til Go-klienten og C-serverlimet.
chrome_net_serverEn liten C++-lag oppå Chromium: én nettverkstråd som eier alle //net-objekter, og sesjonstråder som blokkerer på en forespørsel til nettverkstråden svarer.
Chromium //netURLRequestContext, URLRequest, HTTP/2- og QUIC-sesjoner, BoringSSL.

IDL-en har seks operasjoner som er viktige: opprette en motor fra key=value-alternativer, starte en forespørsel med dens hode (METHOD URL pluss headerlinjer) og kroppslengde, skrive forespørselskroppens biter, vente på responshodet, lese den dekomprimerte kroppen og destruere håndtakene. Et forespørselshåndtak deklarerer @cancel, så et forlatt kall avbryter forespørselen inne i Chromium i stedet for å la den kjøre til ende.

Å isolere stakken i en arbeidsprosess er like mye en sikkerhetsegenskap som en pakkeegenskap. Chromiums nettverkskode analyserer fiendtlig input fra det åpne internett. Hvis den krasjer, krasjer ikke Go-tjenesten din med: kallet feiler med en feil, sesjonen kasteres, og klienten starter en ny arbeidsprosess neste gang den trenger en.

Reproduserbart bygget, fastlåst med SHA-256

En 25 MB stor binærfil som inneholder Chromium er bare pålitelig hvis du kan si nøyaktig hva som gikk inn i den. chromenet følger Chromium Stable på samme måte som gosuda.org/sqlite3 følger SQLite: kildekoden blir aldri vendorisert, bare fastlåst.

FastlåsingPlassering
Chromium-utgivelse og dens offisielle -lite-kildearkivthird_party/chromium/VERSION
SHA-256 for arkivet og for hver verktøykjede som utgivelsen fastlåser: clang, Rust, GN, ninja, Windows clang-runtime, xwinthird_party/chromium/SHA256
MSVC CRT- og Windows SDK-versjoner hentet via xwinthird_party/chromium/WINSDK
Sandkassens kjøretids (rungv) utgivelse, gjesteavbildningens digest, Debian-snapshotdatoenscripts/engine/BUILDENV
HQQ C-kjøretidengo.mod og go.sum

Den gjeldende fastlåsingen er Chromium 157.0.8103.38. En nedlasting som ikke samsvarer med fastlåsingen stopper bygget. Utgivelsen bruker nøyaktig ett plaster på Chromium: på Linux bygges chrome_net_server uten NSS og verifiserer serversertifikater kun mot Chrome Root Store, slik at binærfilen ikke trenger noe utover libc.

Motorene for Linux og Windows bygges i gvx-sandkasser: prosesser uten privilegier med et fastlåst gjesteavbilde og ingen vertsdaemon. Windows-motorene krysskompileres på Linux med Chromiums clang-cl og lld-link mot Windows SDK som xwin henter fra Microsoft. macOS-motorene bygges naturlig på en macOS-runner, fordi Chromiums macOS-bygg krever Xcode.

Ny bygging av samme commit ga byte-identiske binærfiler på tvers av separate CI-kjøringer. Utgivelsesbinærfilene er byte-identiske med dem en tidligere CI-kjøring bygget for commit-en som innførte motoren i monorepoet, på alle seks plattformer.

PlattformBinærstørrelse
linux/amd6427.9 MB
linux/arm6427.4 MB
darwin/amd6426.2 MB
darwin/arm6423.9 MB
windows/amd6427.3 MB
windows/arm6425.8 MB

Linux-motorene avhenger bare av libc, libm, libdl, libpthread og libgcc_s, og krever glibc 2.31 eller nyere.

Følge Chromium og utgivelser

En planlagt arbeidsflyt sjekker Chromium Stable hver sjette time. For en ny utgivelse skriver den om fastlåsingene, bygger og tester alle seks motorene på en chromium/<version>-gren, attesterer binærfilene, og committer fastlåsingene sammen med de innebygde binærfilene, deres digester og lisenstekstene til main i én commit. I monorepoet gir OWNERS per fil arbeidsflytens bot nøyaktig disse filene, slik at oppdateringen går inn uten et menneskelig steg, mens endringer i selve byggeoppskriften (Windows SDK-fastlåsingen og plasteret) fortsatt krever en eier.

Hver utgivelsesbinærfil bærer en byggeprovenienssertifisering fra arbeidsflyten som bygget den:

1gh attestation verify chrome_net_server_linux_amd64 --repo gosuda/chromenet

Lisenstekstene for det hver binærfil inneholder (Chromium, BoringSSL, QUICHE og resten av byggegrafen) genereres av Chromiums egen tools/licenses/licenses.py og leveres med utgivelsen som NOTICE_<os>_<arch>.txt.

Begrensninger

  • DialWebSocket er en ren Go-klient for RFC 6455 og bruker ikke Chromium-stakken.
  • DialWebTransport er en implementasjon i samme prosess. Den når ikke en server.
  • Standard JA4- og HTTP/2-fingeravtrykk er de for den fastlåste Chromium-utgivelsen. Chromium endrer dem fra utgivelse til utgivelse, og chromenet følger med.
  • macOS-binærfilene er ad hoc-signert og ikke notarisert.
  • En arbeidsprosess koster minne. Hver motorbinærfil er 24 til 28 MB på disk, og Chromiums kjøretid starter en trådpool og en nettverkstråd.
  • En motorsesjon holdes til responskroppen er lukket. Et program som lekker responskropper vil tømme poolen.

Kilde

Repositoriet er github.com/gosuda/chromenet, speilet fra //net/chromenet i gosuda-monorepoet. Byggeoppskriften ligger i scripts/engine, motorlimet i engine/, og grensesnittet i chrome_net.idl.