Привет Хабр, меня зовут Илья, я работаю в платформенной команде компании Exness. Мы разрабатываем и внедряем базовые инфраструктурные компоненты, которые используют наши продуктовые команды разработки.
В этой статье я бы хотел поделиться опытом внедрения технологии encrypted SNI (ESNI) в инфраструктуре публичных веб-сайтов.
Использование этой технологии позволит повысить уровень безопасности при работе с публичным веб-сайтом и соответствовать внутренним стандартам безопасности, принятым в Компании.
Прежде всего, хочу обратить внимание, что технология не стандартизована и все еще находится в драфте, однако CloudFlare и Mozilla уже поддерживают ее (в
نظريي جو ھڪڙو
اي ايس اين آئي – это расширение к протоколу TLS 1.3, которое позволяет шифровать SNI в сообщении «Client Hello» TLS handshake. Вот, как выглядит Client Hello с поддержкой ESNI (вместо привычного SNI мы видим ESNI):
Чтобы использовать ESNI, необходимы три составляющие:
- DNS؛
- Поддержка со стороны клиента;
- Поддержка со стороны сервера.
DNS
Необходимо добавить две DNS записи – A۽ TXT (TXT запись содержит публичный ключ, с помощью которого клиент может зашифровать SNI) – см. ниже. Кроме того, должна быть поддержка ڊي ايڇ (DNS over HTTPS), так как доступные клиенты (см. ниже) не активируют поддержку ESNI без DoH. Это логично, так как ESNI подразумевает шифрацию имени ресурса, к которому мы обращаемся, то есть бессмысленно обращаться к DNS по UDP. Более того, использование
На текущий момент доступно
CloudFlare
А запись:
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 запись, запрос формируется по шаблону _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."
}
Итак, с точки зрения DNS, мы должны использовать DoH (желательно с DNSSEC) и добавить две записи.
Поддержка со стороны клиента
Если мы говорим о браузерах, то на сегодняшний момент
Разумеется, для поддержки ESNI должен быть использован TLS 1.3, так как ESNI – это расширение к TLS 1.3.
Для целей тестирования бэкенда с поддержкой ESNI мы реализовали клиента на go، پر انهي تي وڌيڪ بعد ۾.
Поддержка со стороны сервера
На текущий момент ESNI не поддерживается web-серверами типа nginx/apache и т.д., так как они работают с TLS посредством OpenSSL/BoringSSL, в которых ESNI официально не поддерживается.
Поэтому мы решили создать свой front-end компонент (ESNI reverse proxy), который бы поддерживал терминацию TLS 1.3 с ESNI и проксирование HTTP(S) траффика на апстрим, не поддерживающий ESNI. Это позволяет применять технологию в уже сложившейся инфраструктуре, без изменения основных компонентов – то есть использовать текущие web-серверы, не поддерживающие ESNI.
Для наглядности приведем схему:
Отмечу, что прокси задумывался с возможностью терминировать TLS соединение без ESNI, для поддержки клиентов без ESNI. Также, протокол общения с апстримом может быть как HTTP, так и HTTPS c версией TLS ниже 1.3 (если апстрим не поддерживает 1.3). Такая схема дает максимальную гибкость.
Реализацию поддержки ESNI на go мы позаимствовали у
Для генерации ESNI ключей мы использовали
Мы протестировали сборку с использованием go 1.13 на Linux (Debian, Alpine) и MacOS.
Пара слов об эксплуатационных особенностях
ESNI reverse proxy предоставляет метрики в формате Prometheus, например, такие, как rps, upstream latency & response codes, failed/successful TLS handshakes & TLS handshake duration. На первый взгляд это показалось достаточным для оценки того, как прокси справляется с трафиком.
Также перед использованием мы провели нагрузочное тестирование. Результаты ниже:
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
Нагрузочное тестирование мы проводили чисто качественное, для сравнения схемы с использованием ESNI reverse proxy и без. Мы «наливали» трафик локально для того, чтобы исключить «помехи» в промежуточных компонентах.
Итак, с поддержкой ESNI и проксированием на апстрим с HTTP, мы получили в районе ~ 550 rps с одного инстанса, при этом среднее потребление CPU/RAM ESNI reverse proxy:
- 80% CPU Usage (4 vCPU, 4 GB RAM хосты, Linux)
- 130 MB Mem RSS
Для сравнения, RPS для того же апстрима nginx без терминации TLS (HTTP протокол) ~ 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
Наличие таймаутов говорит о том, что есть нехватка ресурсов (мы использовали 4 vCPU, 4 GB RAM хосты, Linux), и по факту потенциальный RPS выше (мы получали цифры до 2700 RPS на более мощных ресурсах).
В заключение отмечу, что технология ESNI выглядит достаточно перспективно. Есть еще много открытых вопросов, например, вопросы хранения публичного ESNI ключа в DNS и ротирование ESNI-ключей – эти вопросы активно обсуждаются, а последняя версия драфта (на момент написания) ESNI уже
جو ذريعو: www.habr.com