title: “Creating Dockerfile” date: 2023-09-12T13:01:00+03:00 draft: true

У нас есть очень простая ситуация. Есть один сайт с одним доменом, который хочет принимать подключения по SSL на 443 порту. И нам НЕ НУЖНО принимать подключения с любых других доменов, а также с IP адреса сервера по 80 и 443 портам. Как ни странно, решение данной ситуации оказывается совсем не простым.

Проблема заключается в особенностях обработки запросов у Nginx. Этот веб-сервер сначала принимает подключение по 443 порту, а затем выбирает какой server в конфигурации будет обрабатывать это подключение. Такое поведение приводит к следующим проблемам:

  1. В общем случае, все домены должны или поддерживать SSL, или не поддерживать SSL (чтобы не было ошибок или странного поведения браузера).
  2. Не получится сделать редирект для домена, к которому идет попытка подключиться по 443 порту, и который не имеет SSL сертификат
  3. Запрос на 443 порт по IP адресу сервера также приведет к проблемам: получить сертификат на IP нельзя, и придется создавать самоподписной сертификат для обработки запросов на 443 порт. А без сертификата при прописанном listen 443 nginx просто не запустится, будет выдавать ошибку. Потому что он уже принял запрос по 443 порту, и ему нужно дать какой-то сертификат.

Есть несколько способов обхода этой проблемы.

  1. Создать для каждого домена/IP самоподписной сертификат. Это не решит проблему “мусорных” доменов, которые указывают на ваш IP адрес, но решит проблему хотя бы на уровне ваших доменов и IP адреса сервера.
  2. Можно установить глобально для всех доменов (и ip адреса) какой-то сертификат, например, сертификат основного домена. Тогда nginx не будет ругаться на отсутствие сертификата для любых доменов (как прописанных в конфиге, так и не прописанных). Однако, будет ругаться браузер, который будет получать один и тот же сертификат для всех доменов.
  3. Если у вас Nginx версии начиная с 1.19.4 (вышла 27 октября 2020 года), то вы можете использовать директиву ssl_reject_handshake. Она была создана как раз для таких ситуаций:
# HTTP:80 = catch-all server block, resulting in a 444 response for unknown domains.
# HTTPS:443 = catch-all server block, resulting in reject SSL connection for unknown domains.

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    server_name "_";
    return 444; # Connection closed without response
}

server {
    listen 443 default_server;
    listen [::]:443 default_server;
    server_name "_";
    ssl_reject_handshake on; # Reject SSL connection
}