VPN & Proxy

SNI

Server Name Indication — расширение TLS, которым клиент открытым текстом сообщает нужный домен, что позволяет держать много HTTPS-сайтов на одном IP.

Что такое SNI

SNI (Server Name Indication, «указание имени сервера») — расширение TLS, которым клиент в самом первом сообщении рукопожатия (ClientHello) сообщает, к какому домену он подключается. Передаётся до того, как согласовано шифрование, — то есть открытым текстом.

Проблема, которую SNI решает, — «курица и яйцо». На одном IP-адресе может жить сотня сайтов. По HTTP сервер узнаёт нужный из заголовка Host, но при HTTPS ему надо предъявить сертификат раньше, чем он увидит хоть один HTTP-заголовок: сертификат отдаётся в рамках рукопожатия, а заголовки идут уже внутри шифрованного канала. Без SNI сервер попросту не знает, чей сертификат отдавать. SNI приносит имя домена в первом же пакете.

Где SNI в рукопожатии

Клиент                                     Сервер
  │
  │  ClientHello
  │    server_name: api.example.com    ← SNI, открытым текстом
  │    supported_versions, cipher_suites
  │──────────────────────────────────────────►│
  │                                           │ выбирает server-блок
  │                                           │ и нужный сертификат
  │  ServerHello + Certificate (CN=api.example.com)
  │◄──────────────────────────────────────────│
  │  ... ключи согласованы, дальше всё зашифровано

SNI на практике: виртуальный хостинг

server {
    listen 443 ssl;
    server_name shop.example.com;
    ssl_certificate     /etc/letsencrypt/live/shop.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/shop.example.com/privkey.pem;
}

server {
    listen 443 ssl;
    server_name api.example.com;
    ssl_certificate     /etc/letsencrypt/live/api.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
}

server {
    listen 443 ssl default_server;   # сюда попадёт клиент без SNI или с чужим именем
    server_name _;
    return 444;
}

Оба сайта слушают один адрес и один порт — Nginx выбирает нужный блок по SNI. Без этого расширения каждому HTTPS-сайту требовался бы отдельный адрес, а IPv4 давно в дефиците.

Как посмотреть SNI

# какой сертификат сервер отдаст для конкретного имени
openssl s_client -connect 192.0.2.10:443 -servername api.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -dates

# запрос на конкретный IP с нужным SNI и Host — до переключения DNS
curl -sv --resolve api.example.com:443:192.0.2.10 https://api.example.com/

Уберите -servername — и сервер отдаст сертификат из default_server. На этом ловится добрая половина жалоб вида «сертификат не тот»: клиент (старая библиотека, самописный скрипт, древний Java-клиент) не отправляет SNI, и сервер отвечает дефолтным блоком.

SNI виден всем на пути

Ключевое свойство: содержимое HTTPS зашифровано, а имя домена в ClientHello — нет. Любое устройство на маршруте — корпоративный шлюз, оборудование оператора, ТСПУ — видит, к какому домену идёт соединение, хотя ни URL, ни данных не видит.

На том же свойстве строятся полезные вещи. Балансировщик может маршрутизировать TCP по имени домена вообще без расшифровки — сертификаты остаются на бэкендах:

stream {
    map $ssl_preread_server_name $backend {
        api.example.com   10.0.0.11:8443;
        shop.example.com  10.0.0.12:8443;
        default           10.0.0.10:8443;
    }

    server {
        listen 443;
        ssl_preread on;
        proxy_pass $backend;
    }
}

ECH: шифрование SNI

Открытый SNI — известная утечка приватности: он раскрывает домен всем на пути. Закрывает её расширение ECH (Encrypted Client Hello) в TLS 1.3; более ранний черновик назывался ESNI. Идея: клиент берёт публичный ключ из DNS-записи (HTTPS RR), шифрует им внутренний ClientHello с настоящим именем, а снаружи остаётся имя-обёртка. Работает, только когда поддержку включили обе стороны — на практике в первую очередь крупные CDN, — и зависит от того, что DNS-запись получена без подмены, обычно через DoH.

Связанные термины

  • TLS — протокол, расширением которого является SNI
  • SSL-сертификат — то, что сервер выбирает по SNI
  • Nginx — выбор server-блока и SNI-роутинг
  • ТСПУ — оборудование, которому SNI виден в открытом виде
  • DNS — через него раздаются ключи ECH
  • Домен — то самое имя, которое едет в ClientHello

Готовы запустить GPU-задачу?

Запустить GPU-сервер