gosuda.org/chromenet: Chromiumin verkkopino Go-kielelle, rakennettu toistettavasti lukitusta julkaisusta

gosuda.org/chromenet on Go-kielinen http.RoundTripper, joka lähettää pyynnöt Chromiumin omaa verkkopinoa käyttäen. Pino on //net virallisesta Chromium-julkaisusta: sen HTTP/1.1-, HTTP/2- ja QUIC/HTTP/3-toteutukset, BoringSSL, Chromen TLS-konfiguraatio ja Chrome Root Store. Se toimii erillisessä worker-prosessissa, chrome_net_server, ja Go-puoli pysyy puhtaana Go-koodina (CGO_ENABLED=0), kommunikoiden workerin kanssa HQQ -jaetun muistin RPC:n kautta.
1go get gosuda.org/chromenet@v0.1.10157081033800
Miksi ajaa Chromiumin verkkopinoa
Go-ohjelma, joka kommunikoi verkossa net/http:n avulla, esittäytyy palvelimelle Go-asiakkaana. Tämä on oikea oletus palvelusta toiseen -liikenteessä. Se on kuitenkin väärä valinta silloin, kun palvelimen käyttäytyminen riippuu siitä, minkälaiseksi asiakkaaksi se luulee yhteyden muodostajaa: selainrajoitetut päätepisteet, botinvastaiset välikerrokset, CDN-palvelut, jotka tarjoavat eri protokollaversioita eri TLS- ja HTTP/2-sormenjälkien mukaan, sekä testit, joiden on toistettava se, mitä todellinen selain kokee.
Go-standardikirjasto ei pysty tarjoamaan selaimen verkkokäyttäytymistä. crypto/tls:llä on oma salausjoukkojen järjestyksensä ja laajennusjoukkonsa, ja net/http:llä on omat HTTP/2-asetuksensa. Kirjastot, jotka jäljittelevät selainta kopioimalla sen ClientHello-tavut, joutuvat kopioimaan niitä jokaisen julkaisun jälkeen uudelleen. chromenet valitsee toisen tien: se ei jäljittele Chromiumin verkkopinoa, vaan ajaa sitä.
Mitattuna darwin/arm64-moottorilla julkista sormenjälkipalvelua (tls.peet.ws) vastaan oletuskonfiguraatio raportoi seuraavaa:
| Ominaisuus | Havaittu arvo |
|---|---|
| Neuvoteltu protokolla | h2 |
| JA4 | t13d1516h2_8daaf6152771_806a8c22fdea |
| HTTP/2-sormenjälki (Akamai-muoto) | 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p |
| User-Agent | työpöydän Chrome moottorin pääversiolla, esim. Chrome/157.0.0.0 |
ForceHTTP3-asetuksella pyynnöt osoitteisiin cloudflare-quic.com ja www.google.com saivat vastauksen HTTP/3:lla. Nämä luvut ovat peräisin yhdeltä alustalta yhdeltä päivältä. Moottori on itse pino, joten luvut muuttuvat, kun kiinnitetty Chromium-julkaisu muuttuu.
Käyttö
NewTransport palauttaa http.RoundTripper-rajapinnan toteuttavan olion. Sitä voi käyttää kaikkialla, missä käytettäisiin http.Transport:ia:
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}
Ensimmäisellä pyynnöllä moduuli kirjoittaa upotetun worker-binäärin alustallesi käyttäjän välimuistihakemistoon, varmistaa sen SHA-256-tiivisteen gosuda.org/hqq/secureexec-paketilla, käynnistää sen vanhempiprosessiin sidotulla elinyhteydellä (--parent-stdin, jolloin se sulkeutuu, kun prosessisi sulkeutuu) ja avaa istuntoaltaan. Mitään ei tarvitse asentaa.
Muutamat toiminnot noudattavat http.Transport:n käyttäytymistä, jotta http.Client toimii muuttumattomana:
- Transport ei seuraa uudelleenohjauksia eikä säilytä evästeitä.
http.Clienttekee molemmat, kutenhttp.Transport:n kanssa. - Gzip-, deflate-, Brotli- tai zstd-pakatut vastaukset saapuvat purettuina, ja
resp.Uncompressedon tosi. - Pyyntö pitää yhtä moottori-istuntoa varattuna, kunnes vastauksen runko suljetaan. Sulje rungot.
- Pyynnön kontekstin peruuttaminen peruuttaa pyynnön Chromiumin sisällä.
- Verkkovirheet ovat
*client.NetError-arvoja, jotka sisältävät Chromiumin virhekoodin ja nimen:
1var ne *client.NetError
2if errors.As(err, &ne) {
3 fmt.Println(ne.Code, ne.Name) // -105 ERR_NAME_NOT_RESOLVED
4}
Asetukset
| Kenttä | Vaikutus |
|---|---|
UserAgent | Korvaa oletusarvoisen Chrome-User-Agentin. Pyynnön User-Agent-otsake voittaa edelleen. |
AcceptLanguage | Korvaa arvon en-US,en;q=0.9. |
Proxy | Chromiumin välityspalvelusäännöt, kuten http://127.0.0.1:8080 tai socks5://127.0.0.1:1080. Tyhjä tarkoittaa suoraa yhteyttä. |
DisableHTTP2, DisableQUIC | Kytkevät protokollat pois päältä. |
ForceHTTP3 | Käyttää QUIC:iä jokaiselle origolle odottamatta Alt-Svc-ilmoitusta. |
InsecureSkipVerify | Hyväksyy minkä tahansa palvelinsertifikaatin. |
Pool | Käytettävät moottori-istunnot. Nil käyttää client.DefaultPool()-altaa, joka sallii 64 samanaikaista pyyntöä. |
Transportit, joilla on samat asetukset, jakavat yhden Chromiumin URLRequestContext-kontekstin, joten yhteysaltaat, HTTP/2-istunnot ja QUIC-istunnot käytetään uudelleen pyyntöjen välillä. Asettamalla HQQ_CHROME_NET_SERVER=/path/to/chrome_net_server upotettu worker korvataan, esimerkiksi paikallisella käännöksellä.
Toimintaperiaate
| Kerros | Mitä siellä ajetaan |
|---|---|
| Ohjelmasi | http.Client ja chromenet.Transport, puhdasta Go-koodia. |
| Prosessiraja | HQQ-jaetun muistin RPC. Rajapinta on chrome_net.idl, jonka hqq-bindgen kääntää Go-asiakkaaksi ja C-palvelinliimakoodiksi. |
chrome_net_server | Ohut C++-kerros Chromiumin päällä: yksi verkkosäie, joka omistaa kaikki //net-oliot, sekä istuntosäikeet, jotka odottavat pyyntöä, kunnes verkkosäie vastaa. |
Chromium //net | URLRequestContext, URLRequest, HTTP/2- ja QUIC-istunnot, BoringSSL. |
IDL:ssä on kuusi merkittävää operaatiota: luo moottori key=value-asetuksista, aloita pyyntö sen otsakkeella (METHOD URL plus otsakerivit) ja rungon pituudella, kirjoita pyynnön rungon osia, odota vastauksen otsaketta, lue purettu vastausrunko ja tuhoa kahvat. Pyyntökahva määrittelee @cancel-attribuutin, jolloin hylätty kutsu keskeyttää pyynnön Chromiumin sisällä sen sijaan, että se jatkuisi loppuun.
Pinon eristäminen worker-prosessiin on turvaominaisuus yhtä lailla kuin pakkausteknillinen ratkaisu. Chromiumin verkkokoodi jäsentää vihamielistä syötettä avoimesta internetistä. Jos se kaatuu, Go-palvelusi ei kaadu mukana: kutsu päättyy virheeseen, istunto hylätään, ja asiakas käynnistää uuden workerin seuraavan kerran, kun sitä tarvitaan.
Reprodusoituva rakentaminen, kiinnitys SHA-256:lla
25 MB:n binääri, joka sisältää Chromiumin, on luotettava vain, jos voit sanoa tarkasti, mitä siihen on kuulunut. chromenet seuraa Chromium Stable -julkaisua samalla tavalla kuin gosuda.org/sqlite3 seuraa SQLite:a: lähdekoodia ei koskaan vendoroida, vaan se ainoastaan kiinnitetään.
| Kiinnitys | Sijainti |
|---|---|
Chromium-julkaisu ja sen virallinen -lite-lähdearkisto | third_party/chromium/VERSION |
| Arkiston ja jokaisen työkaluketjun, jonka julkaisu kiinnittää (clang, Rust, GN, ninja, Windowsin clang-ajonaika, xwin), SHA-256 | third_party/chromium/SHA256 |
| MSVC CRT:n ja Windows SDK:n versiot, jotka haetaan xwin:n kautta | third_party/chromium/WINSDK |
Sandbox-ajonaikan (rungv) julkaisu, vierasjärjestelmän kuvan tiiviste, Debianin snapshot-päivämäärä | scripts/engine/BUILDENV |
| HQQ C -ajonaika | go.mod ja go.sum |
Nykyinen kiinnitys on Chromium 157.0.8103.38. Lataus, joka ei vastaa kiinnitystään, pysäyttää käännöksen. Julkaisu soveltaa Chromiumiin täsmälleen yhtä paikkaa: Linuxissa chrome_net_server käännetään ilman NSS:ää, ja palvelinsertifikaatit tarkistetaan vain Chrome Root Storea vastaan, joten binääri ei tarvitse muuta kuin libc:n.
Linux- ja Windows-moottorit rakennetaan gvx-hiekkalaatikoissa: etuoikeuksettomissa prosesseissa, joilla on kiinnitetty vierasjärjestelmän kuva eikä isäntäpuolen daemonia. Windows-moottorit käännetään ristiin Linuxissa Chromiumin clang-cl:llä ja lld-link:llä Windows SDK:ta vasten, jonka xwin hakee Microsoftilta. macOS-moottorit rakennetaan natiivisti macOS-ajurilla, koska Chromiumin macOS-käännös vaatii Xcoden.
Saman commitin uudelleenrakentaminen tuotti tavu tavulta identtisiä binäärejä eri CI-ajoissa. Julkaisubinäärit ovat tavu tavulta identtisiä niiden kanssa, jotka aiempi CI-ajo rakensi commitille, joka toi moottorin monorepoon, kaikilla kuudella alustalla.
| Alusta | Binäärin koko |
|---|---|
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-moottorit riippuvat vain kirjastoista libc, libm, libdl, libpthread ja libgcc_s, ja vaativat glibc 2.31:n tai uudemman version.
Chromiumin ja julkaisujen seuranta
Ajastettu työnkulku tarkistaa Chromium Stablen kuuden tunnin välein. Uuden julkaisun kohdalla se kirjoittaa kiinnitykset uudelleen, rakentaa ja testaa kaikki kuusi moottoria chromium/<version>-haarassa, varmentaa binäärit ja tekee yhden commitin, joka sisältää kiinnitykset yhdessä upotettujen binäärien, niiden tiivisteiden ja lisenssitekstien kanssa main-haaraan. Monorepossa tiedostokohtaiset OWNERS-tiedostot myöntävät työnkulun botille täsmälleen nämä tiedostot, joten päivitys etenee ilman ihmisen vaihetta. Muutokset itse rakennusohjeeseen (Windows SDK:n kiinnitys ja paikka) vaativat kuitenkin edelleen omistajan hyväksynnän.
Jokainen julkaisubinääri kantaa rakennusprovenienssin todistuksen siitä työnkulusta, joka sen rakensi:
1gh attestation verify chrome_net_server_linux_amd64 --repo gosuda/chromenet
Kunkin binäärin sisältämien komponenttien lisenssitekstit (Chromium, BoringSSL, QUICHE ja muu rakennusgraafi) generoi Chromiumin oma tools/licenses/licenses.py, ja ne toimitetaan julkaisun mukana tiedostoina NOTICE_<os>_<arch>.txt.
Rajoitukset
DialWebSocketon puhdasta Go-koodia oleva RFC 6455 -asiakas eikä käytä Chromiumin pinoa.DialWebTransporton prosessin sisäinen toteutus. Se ei saavuta palvelinta.- Oletusarvoiset JA4- ja HTTP/2-sormenjäljet ovat kiinnitetyn Chromium-julkaisun sormenjälkiä. Chromium muuttaa niitä julkaisusta toiseen, ja
chromenetseuraa muutoksia. - macOS-binäärit on allekirjoitettu ad hoc -tavalla eikä niitä ole notarisoitu.
- Worker-prosessi kuluttaa muistia. Kukin moottoribinääri on levyllä 24–28 MB, ja Chromiumin ajonaika käynnistää säiepoolin ja verkkosäikeen.
- Moottori-istunto pidetään varattuna, kunnes vastausrunko suljetaan. Ohjelma, joka vuotaa vastausrunkoja, tyhjentää altaan.
Lähde
Repositorio on github.com/gosuda/chromenet, joka peilataan gosuda-monorepon kohdasta //net/chromenet. Rakennusohje sijaitsee hakemistossa scripts/engine, moottoriliima hakemistossa engine/ ja rajapinta tiedostossa chrome_net.idl.