Ciao Habr, mi chjamu Ilya, travagliu in a squadra di a piattaforma in Exness. Sviluppemu è implementemu i cumpunenti di l'infrastruttura core chì i nostri squadre di sviluppu di produttu utilizanu.
In questu articulu, vogliu sparte a mo sperienza di implementà a tecnulugia SNI (ESNI) criptata in l'infrastruttura di i siti web publichi.

L'usu di sta tecnulugia aumenterà u livellu di sicurità quandu travaglia cù un situ web publicu è rispettu i normi di sicurezza interna aduttati da a Cumpagnia.
Prima di tuttu, vogliu nutà chì a tecnulugia ùn hè micca standardizzata è hè sempre in u prugettu, ma CloudFlare è Mozilla sò digià supportatu (in ). Questu ci hà motivatu per un tali esperimentu.
Un pocu di tiurìa
ESNI hè una estensione di u protocolu TLS 1.3 chì permette a criptografia SNI in u messagiu "Client Hello" di TLS handshake. Eccu ciò chì Client Hello s'assumiglia cù u supportu ESNI (invece di u solitu SNI vedemu ESNI):

Per utilizà ESNI, avete bisognu di trè cumpunenti:
- DNS;
- supportu à i clienti;
- Supportu di u latu di u servitore.
DNS
Avete bisognu di aghjunghje dui registri DNS - Ae TXT (U registru TXT cuntene a chjave publica cù quale u cliente pò criptà SNI) - vede quì sottu. Inoltre, deve esse supportu DOH (DNS sopra HTTPS) perchè i clienti dispunibili (vede quì sottu) ùn permettenu micca u supportu ESNI senza DoH. Questu hè logicu, postu chì ESNI implica a criptografia di u nome di a risorsa chì accedemu, vale à dì, ùn hà micca sensu per accede à DNS per UDP. Inoltre, l'usu permette di prutege contra l'attacchi di avvelenamentu di cache in questu scenariu.
Attualmente dispunibule , trà elli:
CloudFlare (Verificate My Browser → Encrypted SNI → Learn More) chì i so servitori supportanu digià ESNI, vale à dì, per i servitori CloudFlare in u DNS avemu almenu dui registri - A è TXT. In l'esempiu quì sottu, dumandemu Google DNS (sopra HTTPS):
А entrata:
curl 'https://dns.google.com/resolve?name=www.cloudflare.com&type=A'
-s -H 'accept: application/dns+json'
{
"Status": 0,
"TC": false,
"RD": true,
"RA": true,
"AD": true,
"CD": false,
"Question": [
{
"name": "www.cloudflare.com.",
"type": 1
}
],
"Answer": [
{
"name": "www.cloudflare.com.",
"type": 1,
"TTL": 257,
"data": "104.17.210.9"
},
{
"name": "www.cloudflare.com.",
"type": 1,
"TTL": 257,
"data": "104.17.209.9"
}
]
}
TXT record, a dumanda hè generata secondu un mudellu _esni.FQDN:
curl 'https://dns.google.com/resolve?name=_esni.www.cloudflare.com&type=TXT'
-s -H 'accept: application/dns+json'
{
"Status": 0,
"TC": false,
"RD": true,
"RA": true,
"AD": true,
"CD": false,
"Question": [
{
"name": "_esni.www.cloudflare.com.",
"type": 16
}
],
"Answer": [
{
"name": "_esni.www.cloudflare.com.",
"type": 16,
"TTL": 1799,
"data": ""/wEUgUKlACQAHQAg9SiAYQ9aUseUZr47HYHvF5jkt3aZ5802eAMJPhRz1QgAAhMBAQQAAAAAXtUmAAAAAABe3Q8AAAA=""
}
],
"Comment": "Response from 2400:cb00:2049:1::a29f:209."
}Dunque, da una perspettiva DNS, duvemu aduprà DoH (preferibbilmente cù DNSSEC) è aghjunghje duie entrate.
Supportu à i clienti
Sè avemu parlatu di navigatori, allora in u mumentu . Eccu struzzioni nantu à cumu attivà u supportu ESNI è DoH in FireFox. Una volta chì u navigatore hè cunfiguratu, duvemu vede qualcosa cum'è questu:

per verificà u navigatore.
Di sicuru, TLS 1.3 deve esse usatu per supportà ESNI, postu chì ESNI hè una estensione à TLS 1.3.
Per u scopu di pruvà u backend cù u supportu ESNI, avemu implementatu u cliente go, Ma più nantu à questu dopu.
Supportu di u latu di u servitore
Attualmente, ESNI ùn hè micca supportatu da i servitori web cum'è nginx / apache, etc., postu chì travaglianu cù TLS via OpenSSL / BoringSSL, chì ùn supportanu micca ufficialmente ESNI.
Dunque, avemu decisu di creà u nostru propiu cumpunente front-end (ESNI reverse proxy), chì sustene a terminazione TLS 1.3 cù ESNI è u trafficu HTTP (S) proxy à u upstream, chì ùn sustene micca ESNI. Questu permette di utilizà a tecnulugia in una infrastruttura digià esistente, senza cambià i cumpunenti principali - vale à dì, aduprà i servitori web attuali chì ùn sustene micca ESNI.
Per a chiarezza, quì hè un schema:

Aghju nutatu chì u proxy hè statu cuncepitu cù a capacità di finisce una cunnessione TLS senza ESNI, per sustene i clienti senza ESNI. Inoltre, u protocolu di cumunicazione cù upstream pò esse HTTP o HTTPS cù una versione TLS più bassa di 1.3 (se upstream ùn sustene micca 1.3). Stu schema dà a massima flessibilità.
Mise en œuvre du support ESNI sur go avemu prestitu da . Vogliu nutà subitu chì l'implementazione stessa hè abbastanza micca triviale, postu chì implica cambiamenti in a biblioteca standard. crypto/tls è dunque richiede "patching" GOROOT prima di l'assemblea.
Per generà e chjave ESNI avemu usatu (ancu l'idea di CloudFlare). Queste chjavi sò aduprate per a criptografia / decrittografia SNI.
Avemu pruvatu a custruzzione cù go 1.13. Linux (Debian, Alpine) è MacOS.
Uni pochi parolle nantu à e caratteristiche operative
U proxy inversu ESNI furnisce metriche in u formatu Prometheus, cum'è rps, latenza upstream è codici di risposta, handshake TLS falluti / successi è durata TLS handshake. À u primu sguardu, questu pareva abbastanza per valutà cumu u proxy gestisce u trafficu.
Avemu ancu realizatu teste di carica prima di l'usu. Risultati quì sottu:
wrk -t50 -c1000 -d360s 'https://esni-rev-proxy.npw:443' --timeout 15s
Running 6m test @ https://esni-rev-proxy.npw:443
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.77s 1.21s 7.20s 65.43%
Req/Sec 13.78 8.84 140.00 83.70%
206357 requests in 6.00m, 6.08GB read
Requests/sec: 573.07
Transfer/sec: 17.28MB Avemu fattu una prova di carica puramente qualitativa per paragunà u schema utilizendu un proxy inversu ESNI è senza. Avemu "versatu" u trafficu in u locu per eliminà "interferenza" in cumpunenti intermedi.
Dunque, cù u supportu ESNI è u proxy à upstream da HTTP, avemu circa ~ 550 rps da una istanza, cù u cunsumu mediu CPU / RAM di proxy inversu ESNI:
- 80% d'usu di a CPU (4 vCPU, 4 GB di RAM, Linux)
- 130 MB Mem RSS

Per paragone, RPS per u stessu nginx upstream senza terminazione TLS (protokollu HTTP) hè ~ 1100:
wrk -t50 -c1000 -d360s 'http://lb.npw:80' –-timeout 15s
Running 6m test @ http://lb.npw:80
50 threads and 1000 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 1.11s 2.30s 15.00s 90.94%
Req/Sec 23.25 13.55 282.00 79.25%
393093 requests in 6.00m, 11.35GB read
Socket errors: connect 0, read 0, write 0, timeout 9555
Non-2xx or 3xx responses: 8111
Requests/sec: 1091.62
Transfer/sec: 32.27MB A prisenza di timeouts indica chì ci hè una mancanza di risorse (avemu utilizatu 4 vCPU, host di 4 GB di RAM, Linux), è in fatti u putenziale RPS hè più altu (avemu ricevutu cifre finu à 2700 RPS nantu à risorse più putenti).
In cunclusioni, aghju nutatu chì a tecnulugia ESNI pare assai promettente. Ci sò sempre parechje dumande aperte, per esempiu, i prublemi di almacenà a chjave ESNI publica in u DNS è rotazione di e chjave ESNI - sti prublemi sò attivamente discututi, è l'ultima versione di u prugettu ESNI (à u mumentu di a scrittura) hè digià. .
Source: www.habr.com
