Hallo Habr, mein Name ist Ilya, ich arbeite im Plattformteam bei Exness. Wir entwickeln und implementieren die zentralen Infrastrukturkomponenten, die unsere Produktentwicklungsteams verwenden.
In diesem Artikel möchte ich meine Erfahrungen mit der Implementierung der verschlüsselten SNI-Technologie (ESNI) in die Infrastruktur öffentlicher Websites teilen.
Durch den Einsatz dieser Technologie wird das Sicherheitsniveau bei der Arbeit mit einer öffentlichen Website erhöht und die internen Sicherheitsstandards des Unternehmens eingehalten.
Zunächst möchte ich darauf hinweisen, dass die Technologie nicht standardisiert ist und sich noch im Entwurf befindet, CloudFlare und Mozilla sie jedoch bereits unterstützen (in
Ein bisschen Theorie
ESNI ist eine Erweiterung des TLS 1.3-Protokolls, die eine SNI-Verschlüsselung in der TLS-Handshake-Nachricht „Client Hello“ ermöglicht. So sieht Client Hello mit ESNI-Unterstützung aus (anstelle des üblichen SNI sehen wir ESNI):
Um ESNI nutzen zu können, benötigen Sie drei Komponenten:
- DNS;
- Kundendienst;
- Serverseitige Unterstützung.
DNS
Sie müssen zwei DNS-Einträge hinzufügen – AUnd TXT (Der TXT-Eintrag enthält den öffentlichen Schlüssel, mit dem der Client SNI verschlüsseln kann) – siehe unten. Darüber hinaus muss es Unterstützung geben DoH (DNS über HTTPS), da verfügbare Clients (siehe unten) die ESNI-Unterstützung ohne DoH nicht aktivieren. Dies ist logisch, da ESNI eine Verschlüsselung des Namens der Ressource impliziert, auf die wir zugreifen. Das heißt, es macht keinen Sinn, über UDP auf DNS zuzugreifen. Darüber hinaus ist die Verwendung
Derzeit verfügbar
CloudFlare
А Rekord:
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 Datensatz, Anfrage wird nach einer Vorlage generiert _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."
}
Aus DNS-Sicht sollten wir also DoH verwenden (vorzugsweise mit DNSSEC) und zwei Einträge hinzufügen.
Kundendienst
Wenn wir über Browser sprechen, dann im Moment
Zur Unterstützung von ESNI muss natürlich TLS 1.3 verwendet werden, da ESNI eine Erweiterung von TLS 1.3 ist.
Um das Backend mit ESNI-Unterstützung zu testen, haben wir den Client implementiert go, Aber dazu später mehr.
Serverseitige Unterstützung
Derzeit wird ESNI nicht von Webservern wie Nginx/Apache usw. unterstützt, da diese mit TLS über OpenSSL/BoringSSL arbeiten, die ESNI offiziell nicht unterstützen.
Aus diesem Grund haben wir beschlossen, eine eigene Front-End-Komponente (ESNI-Reverse-Proxy) zu erstellen, die die TLS 1.3-Terminierung mit ESNI unterstützt und den HTTP(S)-Verkehr an den Upstream weiterleitet, der ESNI nicht unterstützt. Dies ermöglicht den Einsatz der Technologie in einer bereits bestehenden Infrastruktur, ohne die Hauptkomponenten zu ändern – also unter Verwendung aktueller Webserver, die ESNI nicht unterstützen.
Zur Verdeutlichung hier ein Diagramm:
Ich stelle fest, dass der Proxy so konzipiert wurde, dass er eine TLS-Verbindung ohne ESNI beenden kann, um Clients ohne ESNI zu unterstützen. Außerdem kann das Kommunikationsprotokoll mit Upstream entweder HTTP oder HTTPS mit einer TLS-Version niedriger als 1.3 sein (wenn Upstream 1.3 nicht unterstützt). Dieses Schema bietet maximale Flexibilität.
Implementierung der ESNI-Unterstützung auf go wir haben etwas geliehen
Zur Generierung von ESNI-Schlüsseln haben wir verwendet
Wir haben den Build mit go 1.13 unter Linux (Debian, Alpine) und MacOS getestet.
Ein paar Worte zu den Betriebsfunktionen
Der ESNI-Reverse-Proxy stellt Metriken im Prometheus-Format bereit, wie z. B. RPS, Upstream-Latenz und Antwortcodes, fehlgeschlagene/erfolgreiche TLS-Handshakes und TLS-Handshake-Dauer. Auf den ersten Blick schien dies ausreichend zu sein, um zu beurteilen, wie der Proxy mit dem Datenverkehr umgeht.
Wir haben vor der Verwendung auch einen Belastungstest durchgeführt. Ergebnisse unten:
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
Wir haben rein qualitative Lasttests durchgeführt, um das Schema mit und ohne ESNI-Reverse-Proxy zu vergleichen. Wir haben den Datenverkehr lokal „geschüttet“, um „Störungen“ bei Zwischenkomponenten auszuschließen.
Mit ESNI-Unterstützung und Proxying zum Upstream von HTTP erreichten wir also etwa ~550 rps von einer Instanz, mit dem durchschnittlichen CPU-/RAM-Verbrauch des ESNI-Reverse-Proxys:
- 80 % CPU-Auslastung (4 vCPU, 4 GB RAM-Hosts, Linux)
- 130 MB Mem RSS
Zum Vergleich: RPS für denselben Nginx-Upstream ohne TLS-Terminierung (HTTP-Protokoll) beträgt ~ 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
Das Vorhandensein von Zeitüberschreitungen weist darauf hin, dass es an Ressourcen mangelt (wir haben 4 vCPUs, 4 GB RAM-Hosts, Linux verwendet), und tatsächlich ist das potenzielle RPS höher (wir haben Werte von bis zu 2700 RPS bei leistungsstärkeren Ressourcen erhalten).
Abschließend stelle ich fest dass die ESNI-Technologie recht vielversprechend aussieht. Es gibt noch viele offene Fragen, zum Beispiel die Fragen der Speicherung des öffentlichen ESNI-Schlüssels im DNS und rotierender ESNI-Schlüssel – diese Fragen werden aktiv diskutiert, und die neueste Version des ESNI-Entwurfs (zum Zeitpunkt des Verfassens dieses Artikels) ist bereits verfügbar
Source: habr.com