Назад | Перейти на главную страницу

Заблокировать прямой доступ к IP-адресу веб-сервера через HTTPS

Похожий на этот и этот вопрос Я хочу запретить пользователям использовать IP-адрес для доступа к моему серверу.

Для HTTP (порт 80) это работает нормально, но не для HTTPS. Так что пользователи все еще могут войти https://<myip> для доступа к веб-серверу, а nginx возвращает сертификат по умолчанию. Кстати, я использую HTTP2 в своих «обычных» серверных блоках (с моими доменными именами), поэтому я использую:

listen 443 ssl http2;
listen [::]:443 ssl http2;

Я пробовал это, чтобы заблокировать доступ к IP-адресу HTTPS сейчас:

server {
   listen 443 ssl;
   listen [::]:443 ssl;

   server_name _;
   return 444;
}

Однако, к сожалению, это блокирует все запросы HTTPS независимо от того, используется домен или нет. Я знаю, что может потребоваться заблокировать клиентов, не относящихся к SNI, поскольку они, конечно, не доставляют используемое доменное имя на сервер, поэтому меня это устраивает. (Я имею в виду, что клиенты, не поддерживающие SNI, в любом случае старые ...)

Я обычно предпочитаю блокировать это в nginx, но если у вас возникнут идеи по блокировке этого на уровне брандмауэра (iptables), я тоже буду рад их оценить. И если вы хотите спорить, почему блокировка с помощью iptables лучше, вы также можете сделать это и убедить меня также блокировать HTTP [или все другие] запросы к IP. В общем, сброс соединения - это нормально (как и код состояния nginx 444).

Однако есть одно требование: я не хочу явно указывать IP-адрес сервера в конфигурации, поскольку это динамический IP-адрес, а сервер использует динамическую службу DNS.

Короче говоря, вот чего я хочу достичь:

Редактировать: Еще одна неудачная попытка. Я пытался следовать это предложение и используйте этот фрагмент конфигурации, который логически выглядит хорошо:

if ($host != "example.com") {
        return 444;
}

Это также в основном работает, но когда я получаю доступ к «https: //», я вижу, что nginx сначала уже отправляет сертификат HTTPS (содержащий имя домена), и только когда я пропускаю предупреждение о соединении, я вижу, что он блокирует доступ. Это логично, поскольку nginx может читать заголовок Host только при наличии HTTPS-соединения, но в этот момент nginx уже отправляет сертификат сервера, содержащий имя домена, поэтому теперь у пользователя есть имя домена и он может повторно подключиться, используя его, создавая весь IP-адрес. блокировка бесполезна. Меня также немного беспокоит аспект производительности этого решения, поскольку он заставляет nginx проверять заголовок хоста для каждого запроса, поэтому я все еще ищу решение здесь.

Сегодня я столкнулся с такой же проблемой с этим блоком:

server {
  listen 443 ssl default_server;
  server_name <SERVER-IP>;
  return 444;
}

В журналах nginx говорится:

"ssl_certificate" не определен в сервере, прослушивающем порт SSL во время установления связи SSL

Как вы упомянули, похоже, это также отключает другие серверные блоки. Это можно исправить, используя (самоподписанный) сертификат для IP-адреса сервера. Я сделал это с помощью openssl:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout privateKey.key -out certificate.crt -subj '/CN=<SERVER-IP>'

Затем измените серверный блок на:

server {
    listen 443 ssl default_server;
    server_name <server-ip>;
    ssl_certificate         /path/to/certificate.crt;
    ssl_certificate_key     /path/to/privateKey.key;
    return 444;
}

Когда кто-то использует SERVER-IP через https для доступа к серверу, nginx представляет (самоподписанный) сертификат, а НЕ сертификат доменного имени, который вы хотите скрыть.

Кстати, сделайте свой server-block-with-IP default_server. Таким образом, клиент, у которого отключен SNI, получит IP-сертификат, а НЕ сертификат доменного имени. Это также можно проверить с помощью openssl (опция -servername, которая включает SNI, опущена):

openssl s_client -connect <SERVER-IP>:443

Попробуйте это - бит default_server является важной частью. Если это не сработает, обновите свой вопрос, указав конфигурацию для других серверов, прослушивающих 443 / SSL.

server {
  listen 443 ssl default_server;
  listen [::]:443 ssl default_server; # not sure if you want/need it here as well. Try both ways.

 server_name _;
 return 444;
}