Hej Habr, mit navn er Ilya, jeg arbejder i platformsteamet hos Exness. Vi udvikler og implementerer de kerneinfrastrukturkomponenter, som vores produktudviklingsteams bruger.
I denne artikel vil jeg gerne dele min erfaring med at implementere krypteret SNI (ESNI) teknologi i infrastrukturen på offentlige hjemmesider.
Brugen af denne teknologi vil øge sikkerhedsniveauet, når du arbejder med et offentligt websted og overholde interne sikkerhedsstandarder vedtaget af virksomheden.
Først og fremmest vil jeg gerne påpege, at teknologien ikke er standardiseret og stadig er under udkast, men CloudFlare og Mozilla understøtter det allerede (i
Lidt teori
ESNI er en udvidelse til TLS 1.3-protokollen, der tillader SNI-kryptering i TLS-håndtrykket "Client Hello"-meddelelsen. Her er, hvordan Client Hello ser ud med ESNI-support (i stedet for den sædvanlige SNI ser vi ESNI):
For at bruge ESNI skal du bruge tre komponenter:
- DNS;
- Kundesupport;
- Support på serversiden.
DNS
Du skal tilføje to DNS-poster – AOg TXT (TXT-posten indeholder den offentlige nøgle, som klienten kan kryptere SNI med) - se nedenfor. Derudover skal der være opbakning DoH (DNS over HTTPS), fordi tilgængelige klienter (se nedenfor) ikke aktiverer ESNI-understøttelse uden DoH. Dette er logisk, da ESNI indebærer kryptering af navnet på den ressource, vi har adgang til, det vil sige, det giver ingen mening at få adgang til DNS over UDP. Desuden brugen
Tilgængelig i øjeblikket
CloudFlare
А indgang:
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, anmodning genereres i henhold til en skabelon _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."
}
Så fra et DNS-perspektiv bør vi bruge DoH (helst med DNSSEC) og tilføje to poster.
Kunde support
Hvis vi taler om browsere, så pt
Selvfølgelig skal TLS 1.3 bruges til at understøtte ESNI, da ESNI er en udvidelse til TLS 1.3.
Med det formål at teste backend med ESNI support implementerede vi klienten på go, Men mere om det senere.
Support på serversiden
I øjeblikket understøttes ESNI ikke af webservere som nginx/apache osv., da de arbejder med TLS via OpenSSL/BoringSSL, som ikke officielt understøtter ESNI.
Derfor besluttede vi at oprette vores egen front-end-komponent (ESNI reverse proxy), som ville understøtte TLS 1.3-terminering med ESNI og proxy HTTP(S)-trafik til upstream, som ikke understøtter ESNI. Dette gør det muligt at bruge teknologien i en allerede eksisterende infrastruktur uden at ændre på hovedkomponenterne – altså ved at bruge nuværende webservere, der ikke understøtter ESNI.
For klarhedens skyld er her et diagram:
Jeg bemærker, at proxyen er designet med mulighed for at afslutte en TLS-forbindelse uden ESNI, for at understøtte klienter uden ESNI. Kommunikationsprotokollen med upstream kan også være enten HTTP eller HTTPS med en TLS-version lavere end 1.3 (hvis upstream ikke understøtter 1.3). Denne ordning giver maksimal fleksibilitet.
Implementering af ESNI-støtte vedr go vi lånte fra
Til at generere ESNI-nøgler brugte vi
Vi testede bygningen med go 1.13 på Linux (Debian, Alpine) og MacOS.
Et par ord om operationelle funktioner
ESNI omvendt proxy giver metrics i Prometheus-format, såsom rps, upstream latency & svarkoder, mislykkede/vellykkede TLS-håndtryk og TLS-håndtryk varighed. Ved første øjekast virkede dette tilstrækkeligt til at evaluere, hvordan proxyen håndterer trafik.
Vi udførte også belastningstest før brug. Resultater nedenfor:
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
Vi udførte rent kvalitativ belastningstest for at sammenligne ordningen ved hjælp af ESNI omvendt proxy og uden. Vi "hældte" trafik lokalt for at eliminere "interferens" i mellemkomponenter.
Så med ESNI-understøttelse og proxying til upstream med HTTP, fik vi omkring ~550 rps fra én instans med det gennemsnitlige CPU/RAM-forbrug af ESNI reverse proxy:
- 80 % CPU-brug (4 vCPU, 4 GB RAM-værter, Linux)
- 130 MB Mem RSS
Til sammenligning er RPS for den samme nginx opstrøms uden TLS (HTTP-protokol)-terminering ~ 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
Tilstedeværelsen af timeouts indikerer, at der er mangel på ressourcer (vi brugte 4 vCPU'er, 4 GB RAM-værter, Linux), og faktisk er den potentielle RPS højere (vi modtog tal på op til 2700 RPS på mere kraftfulde ressourcer).
Afslutningsvis bemærker jeg at ESNI-teknologien ser ret lovende ud. Der er stadig mange åbne spørgsmål, for eksempel spørgsmålene om lagring af en offentlig ESNI-nøgle i DNS og roterende ESNI-nøgler - disse spørgsmål diskuteres aktivt, og den seneste version af ESNI-udkastet (i skrivende stund) er allerede
Kilde: www.habr.com