Прывітанне Хабр, мяне клічуць Ілля, я працую ў платформеннай камандзе кампаніі 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) - гл. ніжэй. Акрамя таго, павінна быць падтрымка DoH (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 ужо
Крыніца: habr.com