Як абараніць свой публічны сайт з ESNI

Прывітанне Хабр, мяне клічуць Ілля, я працую ў платформеннай камандзе кампаніі Exness. Мы распрацоўваем і ўкараняем базавыя інфраструктурныя кампаненты, якія выкарыстоўваюць нашы прадуктыўныя каманды распрацоўкі.

У гэтым артыкуле я б хацеў падзяліцца досведам укаранення тэхналогіі encrypted SNI (ESNI) у інфраструктуры публічных вэб-сайтаў.

Як абараніць свой публічны сайт з ESNI

Выкарыстанне гэтай тэхналогіі дасць магчымасць павысіць узровень бяспекі пры рабоце з публічным вэб-сайтам і адпавядаць унутраным стандартам бяспекі, прынятым у Кампаніі.

Першым чынам, жадаю звярнуць увагу, што тэхналогія не стандартызаваная і ўсё яшчэ знаходзіцца ў драфце, аднак CloudFlare і Mozilla ужо падтрымліваюць яе (у чарнавік01). Гэта і матывавала нас на такі эксперымент.

крыху тэорыі

ЭСНІ - гэта пашырэнне да пратаколу TLS 1.3, якое дазваляе шыфраваць SNI у паведамленні "Client Hello" TLS handshake. Вось, як выглядае Client Hello з падтрымкай ESNI (замест звыклага SNI мы бачым ESNI):

Як абараніць свой публічны сайт з ESNI

 Каб выкарыстоўваць ESNI, неабходны тры складнікі:

  • DNS; 
  • Падтрымка з боку кліента;
  • Падтрымка з боку сервера.

DNS

Неабходна дадаць два DNS запісы - A, І TXT (TXT запіс змяшчае публічны ключ, з дапамогай якога кліент можа зашыфраваць SNI) - гл. ніжэй. Акрамя таго, павінна быць падтрымка DoH (DNS over HTTPS), бо даступныя кліенты (гл. ніжэй) не актывуюць падтрымку ESNI без DoH. Гэта лагічна, бо ESNI мае на ўвазе шыфрацыю імя рэсурса, да якога мы звяртаемся, гэта значыць бессэнсоўна звяртацца да DNS па UDP. Больш за тое, выкарыстанне DNSSEC дазваляе абараніцца ад "cache poisoning" нападаў у гэтым сцэнары.

На дадзены момант даступна некалькі DoH правайдэраў, сярод іх:

CloudFlare заяўляе (Check My Browser → Encrypted SNI → Learn More), што іх серверы ўжо зараз падтрымліваюць ESNI, гэта значыць для сервераў CloudFlare у DNS мы маем прынамсі два запісы – А і TXT. У прыкладзе ніжэй мы запытваем Google DNS (over HTTPS): 

А запіс:

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) і дадаць два запісы. 

Падтрымка з боку кліента

Калі мы гаворым аб браўзэрах, то на сённяшні момант падтрымка рэалізаваная толькі ў FireFox. Тут прыведзена інструкцыя, як актываваць падтрымку ESNI і DoH у FireFox. Пасля таго, як браўзэр настроены, мы павінны ўбачыць прыкладна такую ​​карціну:

Як абараніць свой публічны сайт з ESNI

Спасылка для праверкі браўзэра.

Зразумела, для падтрымкі 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. 

Для навочнасці прывядзем схему:

Як абараніць свой публічны сайт з ESNI

Адзначу, што проксі думаў з магчымасцю тэрмінаваць TLS злучэнне без ESNI, для падтрымкі кліентаў без ESNI. Таксама, пратакол зносін з апстрымам можа быць як HTTP, так і HTTPS c версіяй TLS ніжэй 1.3 (калі апстрым не падтрымлівае 1.3). Такая схема дае максімальную гнуткасць.

Рэалізацыю падтрымкі ESNI на go мы запазычылі ў CloudFlare. Адразу адзначу, што сама рэалізацыя дастаткова нетрывіяльная, бо мае на ўвазе змены ў стандартнай бібліятэцы. crypto/tls і таму патрабуе «патчынгу» ГАРУТ перад зборкай.

Для генерацыі ESNI ключоў мы выкарыстоўвалі esnitool (таксама стварэнне CloudFlare). Дадзеныя ключы выкарыстоўваюцца для шыфрацыі/дэшыфрацыі SNI.
Мы пратэсціравалі зборку з выкарыстаннем 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

Як абараніць свой публічны сайт з ESNI

Для параўнання, 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 ужо 7.

Крыніца: habr.com

Дадаць каментар