Hallo Habr, myn namme is Ilya, ik wurkje yn it platfoarmteam by Exness. Wy ûntwikkelje en implementearje de kearnynfrastruktuerkomponinten dy't ús produktûntwikkelingsteams brûke.
Yn dit artikel wol ik myn ûnderfining diele mei it ymplementearjen fan fersifere SNI (ESNI) technology yn 'e ynfrastruktuer fan iepenbiere websiden.

It gebrûk fan dizze technology sil it nivo fan feiligens ferheegje by it wurkjen mei in iepenbiere webside en foldwaan oan ynterne feiligensnoarmen oannommen troch it bedriuw.
Alderearst wol ik derop wize dat de technology net standerdisearre is en noch yn it ûntwerp is, mar CloudFlare en Mozilla stypje it al (yn ). Dit motivearre ús foar sa'n eksperimint.
In bytsje teory
ESNI is in útwreiding foar it TLS 1.3-protokol dat SNI-fersifering mooglik makket yn it TLS-handshake "Client Hello"-berjocht. Hjir is hoe Client Hello derút sjocht mei ESNI-stipe (ynstee fan 'e gewoane SNI sjogge wy ESNI):

Om ESNI te brûken, hawwe jo trije komponinten nedich:
- DNS;
- Client stipe;
- Server side stipe.
DNS
Jo moatte twa DNS-records tafoegje - Aen TXT (It TXT-record befettet de iepenbiere kaai wêrmei de kliïnt SNI kin fersiferje) - sjoch hjirûnder. Dêrneist moat der stipe komme DoH (DNS oer HTTPS) om't beskikbere kliïnten (sjoch hjirûnder) ESNI-stipe net ynskeakelje sûnder DoH. Dit is logysk, om't ESNI fersifering ymplisearret fan 'e namme fan' e boarne dy't wy tagong krije, dat is, it hat gjin sin om tagong te krijen ta DNS fia UDP. Boppedat, it gebrûk kinne jo beskermje tsjin cache fergiftiging oanfallen yn dit senario.
Op it stuit beskikber , ûnder harren:
CloudFlare (Kontrolearje Myn blêder → Fersifere SNI → Mear ynformaasje) dat har servers al ESNI stypje, dat is, foar CloudFlare-tsjinners yn 'e DNS hawwe wy op syn minst twa records - A en TXT. Yn it foarbyld hjirûnder freegje wy Google DNS (oer HTTPS):
А yngong:
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, fersyk wurdt oanmakke neffens in sjabloan _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."
}
Dat, út in DNS-perspektyf, moatte wy DoH brûke (leafst mei DNSSEC) en twa yngongen tafoegje.
Klanteservice
As wy it oer browsers hawwe, dan op it stuit . Hjir binne ynstruksjes oer hoe't jo ESNI- en DoH-stipe kinne aktivearje yn FireFox. Sadree't de browser is konfigureare, soene wy wat as dit moatte sjen:

om de browser te kontrolearjen.
Fansels moat TLS 1.3 brûkt wurde om ESNI te stypjen, om't ESNI in útwreiding is foar TLS 1.3.
Foar it doel fan it testen fan 'e backend mei ESNI-stipe, hawwe wy de klant ymplementearre op go, Mar dêr letter mear oer.
Server side stipe
Op it stuit wurdt ESNI net stipe troch webservers lykas nginx/apache, ensfh., Om't se wurkje mei TLS fia OpenSSL/BoringSSL, dy't ESNI net offisjeel stypje.
Dêrom hawwe wy besletten om ús eigen front-end komponint te meitsjen (ESNI reverse proxy), dy't TLS 1.3 beëiniging soe stypje mei ESNI en proxy HTTP(S) ferkear nei de streamop, dy't ESNI net stipet. Hjirmei kinne jo de technology brûke yn in al besteande ynfrastruktuer, sûnder de haadkomponinten te feroarjen - dat is, brûk aktuele webservers dy't ESNI net stypje.
Foar dúdlikens is hjir in diagram:

Ik merk op dat de proxy is ûntwurpen mei de mooglikheid om in TLS-ferbining sûnder ESNI te beëinigjen, om kliïnten sûnder ESNI te stypjen. Ek kin it kommunikaasjeprotokol mei streamop of HTTP of HTTPS wêze mei in TLS-ferzje leger as 1.3 (as streamop 1.3 net stipet). Dit skema jout maksimale fleksibiliteit.
Ymplemintaasje fan ESNI-stipe op go wy liend fan . Ik wol direkt opmerke dat de ymplemintaasje sels frij net-triviaal is, om't it feroaringen yn 'e standertbibleteek giet krypto/tls en fereasket dêrom "patching" GOROOT foar gearkomste.
Om ESNI-kaaien te generearjen hawwe wy brûkt (ek it brainchild fan CloudFlare). Dizze kaaien wurde brûkt foar SNI-fersifering / dekodearring.
Wy hawwe de build testen mei go 1.13 op Linux (Debian, Alpine) en MacOS.
In pear wurden oer operasjonele funksjes
ESNI omkearde proxy leveret metriken yn Prometheus-formaat, lykas rps, streamôfwerts latency en antwurdkoades, mislearre / suksesfolle TLS-handshakes en TLS-handshake-duer. Op it earste each, dit like genôch om te evaluearjen hoe't de proxy omgiet ferkear.
Wy hawwe ek load testen útfierd foar gebrûk. Resultaten hjirûnder:
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
Wy hawwe suver kwalitative loadtesten útfierd om it skema te fergelykjen mei ESNI reverse proxy en sûnder. Wy "goaten" ferkear lokaal om "ynterferinsje" yn tuskenkomponinten te eliminearjen.
Dat, mei ESNI-stipe en proxying nei streamop fan HTTP, krigen wy sawat ~ 550 rps fan ien eksimplaar, mei it gemiddelde CPU / RAM-konsumpsje fan ESNI reverse proxy:
- 80% CPU-gebrûk (4 vCPU, 4 GB RAM-hosts, Linux)
- 130 MB Mem RSS

Foar fergeliking is RPS foar deselde nginx streamop sûnder TLS (HTTP-protokol) beëiniging ~ 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
De oanwêzigens fan time-outs jout oan dat der in tekoart oan boarnen is (wy brûkten 4 vCPU, 4 GB RAM hosts, Linux), en eins is de potinsjele RPS heger (wy krigen sifers oant 2700 RPS op machtiger boarnen).
Ta beslút, ik notearje dat ESNI technology sjocht der hiel kânsryk. D'r binne noch in protte iepen fragen, bygelyks de problemen fan it bewarjen fan de iepenbiere ESNI-kaai yn 'e DNS en rotearjende ESNI-kaaien - dizze problemen wurde aktyf besprutsen, en de lêste ferzje fan it ESNI-ûntwerp (op it momint fan skriuwen) is al .
Boarne: www.habr.com
