Witaj Habr, mam na imię Ilya, pracuję w zespole platformowym w Exness. Opracowujemy i wdrażamy podstawowe komponenty infrastruktury, z których korzystają nasze zespoły ds. rozwoju produktów.
W tym artykule chciałbym podzielić się swoimi doświadczeniami z wdrażania technologii szyfrowanej SNI (ESNI) w infrastrukturze publicznych stron internetowych.
Zastosowanie tej technologii zwiększy poziom bezpieczeństwa podczas pracy z ogólnodostępnym serwisem internetowym oraz zgodność z wewnętrznymi standardami bezpieczeństwa przyjętymi przez Spółkę.
Na wstępie zaznaczę, że technologia nie jest ustandaryzowana i jest jeszcze w fazie roboczej, ale CloudFlare i Mozilla już ją wspierają (w
Trochę teorii
ESNI to rozszerzenie protokołu TLS 1.3, które umożliwia szyfrowanie SNI w wiadomości „Client Hello” podczas uzgadniania TLS. Oto jak wygląda Client Hello ze wsparciem ESNI (zamiast zwykłego SNI widzimy ESNI):
Aby korzystać z ESNI, potrzebujesz trzech komponentów:
- DNSy;
- Wsparcie klienta;
- Wsparcie po stronie serwera.
DNS
Musisz dodać dwa rekordy DNS – Ai TXT (Rekord TXT zawiera klucz publiczny, za pomocą którego klient może zaszyfrować SNI) - patrz poniżej. Poza tym musi być wsparcie Doh (DNS przez HTTPS), ponieważ dostępni klienci (patrz poniżej) nie umożliwiają obsługi ESNI bez DoH. Jest to logiczne, ponieważ ESNI implikuje szyfrowanie nazwy zasobu, do którego uzyskujemy dostęp, to znaczy nie ma sensu uzyskiwać dostępu do DNS przez UDP. Co więcej, użycie
Aktualnie dostępne
CloudFlare
А wejście:
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 rekordu, żądanie generowane jest według szablonu _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."
}
Zatem z punktu widzenia DNS powinniśmy użyć DoH (najlepiej z DNSSEC) i dodać dwa wpisy.
Wsparcie klienta
Jeśli mówimy o przeglądarkach, to w tej chwili
Oczywiście do obsługi ESNI należy używać protokołu TLS 1.3, ponieważ ESNI jest rozszerzeniem protokołu TLS 1.3.
Na potrzeby testów backendu z obsługą ESNI zaimplementowaliśmy klienta na go, Ale o tym później.
Wsparcie po stronie serwera
Obecnie ESNI nie jest obsługiwane przez serwery internetowe, takie jak nginx/apache itp., ponieważ współpracują z TLS poprzez OpenSSL/BoringSSL, które oficjalnie nie obsługują ESNI.
Dlatego zdecydowaliśmy się stworzyć własny komponent front-endowy (odwrotne proxy ESNI), który obsługiwałby terminację TLS 1.3 z ruchem ESNI i proxy HTTP(S) do upstream, który nie obsługuje ESNI. Pozwala to na wykorzystanie technologii w już istniejącej infrastrukturze, bez zmiany głównych komponentów – czyli wykorzystania obecnych serwerów WWW, które nie obsługują ESNI.
Dla jasności oto diagram:
Zwracam uwagę, że serwer proxy został zaprojektowany z możliwością zakończenia połączenia TLS bez ESNI, aby obsługiwać klientów bez ESNI. Ponadto protokołem komunikacyjnym z upstream może być HTTP lub HTTPS z wersją TLS niższą niż 1.3 (jeśli upstream nie obsługuje wersji 1.3). Ten schemat zapewnia maksymalną elastyczność.
Wdrożenie wsparcia ESNI w dniu go pożyczyliśmy od
Do wygenerowania kluczy ESNI użyliśmy
Przetestowaliśmy tę kompilację przy użyciu wersji 1.13 na systemach Linux (Debian, Alpine) i MacOS.
Kilka słów o funkcjach operacyjnych
Odwrotne proxy ESNI zapewnia metryki w formacie Prometheus, takie jak liczba obrotów na sekundę, opóźnienia i kody odpowiedzi nadawczego, nieudane/udane uzgadnianie TLS i czas trwania uzgadniania TLS. Na pierwszy rzut oka wydawało się to wystarczające, aby ocenić, jak serwer proxy obsługuje ruch.
Przed użyciem przeprowadziliśmy również testy obciążeniowe. Wyniki poniżej:
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
Przeprowadziliśmy czysto jakościowe testy obciążenia, aby porównać schemat z użyciem odwrotnego proxy ESNI i bez niego. „Wylewaliśmy” ruch lokalnie, aby wyeliminować „interferencję” w elementach pośrednich.
Tak więc, dzięki obsłudze ESNI i proxy do przesyłania danych z HTTP, uzyskaliśmy około 550 obr/s z jednej instancji, przy średnim zużyciu procesora/RAM przez odwrotne proxy ESNI:
- 80% użycia procesora (4 vCPU, 4 GB RAM hosty, Linux)
- 130 MB pamięci RSS
Dla porównania, RPS dla tego samego upstream nginx bez zakończenia TLS (protokół HTTP) wynosi ~ 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
Obecność timeoutów wskazuje na brak zasobów (użyliśmy 4 vCPU, 4 GB RAM hostów, Linux), a w rzeczywistości potencjalny RPS jest wyższy (otrzymaliśmy dane do 2700 RPS na mocniejszych zasobach).
Podsumowując, zauważam że technologia ESNI wygląda całkiem obiecująco. Nadal pozostaje wiele otwartych kwestii, np. kwestia przechowywania publicznego klucza ESNI w DNS i rotacji kluczy ESNI – te kwestie są aktywnie omawiane, a najnowsza wersja projektu ESNI (w momencie pisania tego tekstu) jest już
Źródło: www.habr.com