Правильная настройка сервера nginx по умолчанию для https

71

У меня есть несколько серверов, работающих на одной машине, некоторые только с http, некоторые с http и https. Есть несколько серверных блоков, определенных в отдельных файлах, которые включены в основной файл конфигурации.

Я настроил сервер «по умолчанию» для http, который будет обслуживать общую «страницу обслуживания» для запросов, которые не совпадают ни с одним из имен server_names в других файлах конфигурации. Сервер http по умолчанию работает как положено, он использует имя_сервера "_" и появляется первым в списке включений (потому что я заметил, что в случае дублирования имен серверов на разных серверах используется тот, который появляется первым). Это прекрасно работает.

Я ожидал бы такой же точный блок сервера (только переключая «listen 80 default_server» на «listen 443 default_server», а также вместо обслуживающей страницы «return 444»), однако это не так. Вместо этого кажется, что новый сервер https по умолчанию фактически захватывает все входящие соединения https и вызывает их сбой, хотя другие блоки сервера имеют более подходящие имена_сервера для входящих запросов. Удаление нового https-сервера по умолчанию приведет к возобновлению полукорректного поведения: все сайты с https будут загружаться правильно; но все веб-сайты без https будут перенаправлены на первый https-сервер во включаемых файлах (что в соответствии с документацией, если «default_server» не появляется, то первым появившимся блоком сервера будет «default»).

Итак, мой вопрос, как правильно определить «сервер по умолчанию» в nginx для ssl-соединений? Почему, когда я явно устанавливаю «default_server», он становится жадным и захватывает все соединения, тогда как когда я неявно позволяю nginx определять «сервер по умолчанию», он работает так, как я ожидал (с неверным сервером, установленным по умолчанию, и другими реальными серверами ведет себя правильно)?

Вот мои "серверы по умолчанию". Http работает без взлома других серверов. Https ломает другие серверы и потребляет все.

server {
    listen 443 ssl default_server;
    server_name _;

    access_log /var/log/nginx/maintenance.access.log;
    error_log /var/log/nginx/maintenance.error.log error;

    return 444;
}

server {
    listen *:80 default_server;
    server_name _;
    charset utf-8;

    access_log /var/log/nginx/maintenance.access.log;
    error_log /var/log/nginx/maintenance.error.log error;

    root /home/path/to/templates;

    location / {
        return 503;
    }

    error_page 503 @maintenance;

    location @maintenance {
        rewrite ^(.*)$ /maintenance.html break;
    }
}

Кто-нибудь из вас понимает, что здесь может быть не так?


источник

Ответы:

27

У вас нет ssl_certificate или ssl_certificate_key, определенных в вашем блоке https по умолчанию. Хотя у вас нет или вам не нужен реальный ключ для этого сценария по умолчанию, вам все равно нужно настроить один или иначе nginx будет иметь нежелательное поведение, которое вы описываете.

Создайте самозаверяющий сертификат с общим именем * и подключите его к вашей конфигурации, и он начнет работать так, как вы хотите.

Поведение «по умолчанию» при такой настройке будет означать, что браузер получит предупреждение о том, что сертификат не может быть доверенным, если пользователь добавит сертификат в качестве исключения, соединение будет разорвано с помощью nginx, и они увидят браузер по умолчанию Сообщение об ошибке «Не удалось подключиться».

Радмилла Мустафа
источник
1
Я пробовал это, но это все еще не работает: все запросы ssl к IP идут к моему другому хосту ssl. Что-нибудь еще, что я мог попробовать?
Михаэль Хартл
23

Мне удалось настроить общий выделенный хостинг на одном IP с nginx. HTTP и HTTPS по умолчанию, обслуживающие 404 для неизвестных входящих доменов.

1 - Создать зону по умолчанию

Так как nginx загружает vhosts в порядке ascii, вы должны создать 00-defaultфайл / символическую ссылку на свой /etc/nginx/sites-enabled.

2 - заполнить зону по умолчанию

Заполните ваш по 00-defaultумолчанию vhosts. Вот зона, которую я использую:

server {
    server_name _;
    listen       80  default_server;
    return       404;
}


server {
    listen 443 ssl;
    server_name _;
    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    return       404;
}

3 - Создайте самоподписанный сертификат, протестируйте и перезагрузите

Вам нужно будет создать самозаверяющий сертификат в /etc/nginx/ssl/nginx.crt.

Создайте самозаверяющий сертификат по умолчанию:

sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout /etc/nginx/ssl/nginx.key -out /etc/nginx/ssl/nginx.crt

Просто напоминание:

  • Протестируйте конфигурацию nginx перед перезагрузкой / перезапуском: nginx -t
  • Перезагрузить наслаждайтесь: sudo service nginx reload

Надеюсь, это поможет.

Если не
источник
2
Не устраняет предупреждение браузера о посещении небезопасного сайта.
gdbj
5
Не может быть решена, так как универсальный элемент должен соответствовать любым доменам. Никакой SSL не может подстановить все домены. Представьте, что если вы подделываете адрес google.fr на своем сервере, вы сможете аутентифицировать свой сервер как google.fr. Это было бы серьезной проблемой безопасности :(
Ifnot
Я думаю, это имеет смысл. К сожалению, в Chrome вы видите страшное предупреждение перед тем, как увидеть страницу 404, что хуже, чем то, что сервер просто отклоняет трафик. Похоже, сервер неправильно настроен.
gdbj
Большое спасибо. Это сработало для меня, за исключением того, что мне пришлось добавить default_serverдля прослушивания 443, и я добавил IPv6-адрес [::]: 80 и [::]: 443 с default_server.
chmike
16

По сути, мы хотим любой ценой избежать того, чтобы первое определение сервера в нашем конфигурационном файле служило универсальным сервером для соединений SSL. Мы все знаем, что он делает это (в отличие от http и использует конфигурацию default_server, которая работает хорошо).

Это не может быть достигнуто декларативно для SSL (пока), поэтому мы должны закодировать его с помощью IF ...

Переменная $host- это имя хоста из строки запроса или заголовка http. Переменная $server_name- это имя блока сервера, в котором мы сейчас находимся.

Так что, если эти два не равны, то вы обслуживали этот блок сервера SSL для другого хоста, поэтому его следует заблокировать.

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

Пример:

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

    ###
    ### Section: SSL

    #
    ## Check if this certificate is really served for this server_name
    ##   http://serverfault.com/questions/578648/properly-setting-up-a-default-nginx-server-for-https
    if ($host != $server_name) {
        #return 404 "this is an invalid request";
        return       444;
    }

    ...
Рольф
источник
не могли бы вы объяснить, что listen [::]:443 ssl http2;делает? У меня проблемы с поиском документации для него.
Эндрю Браун
Я думаю, что нашел это, просто нужно было знать, что искать. Директивы IPV4 и IPV6.
Эндрю Браун
7

Чтобы уточнить ответ Радмиллы Мустафы:

Nginx использует заголовок «Host» для сопоставления имени сервера. Он не использует TLS SNI. Это означает, что для SSL-сервера nginx должен иметь возможность принимать SSL-соединение, которое сводится к наличию сертификата / ключа. Сертификат / ключ может быть любым, например, самоподписанным.

Смотрите документацию

Следовательно, решение:

server {
    server_name _;
    listen 80 default_server;
    listen 443 ssl default_server;

    ## To also support IPv6, uncomment this block
    # listen [::]:80 default_server;
    # listen [::]:443 ssl default_server;

    ssl_certificate <path to cert>;
    ssl_certificate_key <path to key>;
    return 404; # or whatever
}
andreycpp
источник
Я чувствую, что это должен быть принятый ответ. Большое спасибо.
aggregate1166877
2

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

ssl_session_tickets off;

Основываясь на ответе Ifnot , мой рабочий пример:

server {
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;
    server_name _;

    ssl_certificate /etc/nginx/ssl/nginx.crt;
    ssl_certificate_key /etc/nginx/ssl/nginx.key;
    ssl_session_tickets off;

    return 404;
}

Я понятия не имею, почему это было необходимо, единственный принцип, который я извлек из этого, заключался в том, что nginx ведет себя очень странно, когда мы не даем ему то, что он хочет.

Марек Лисы
источник
1
ты легенда
Низар Блонд
1

Если вы хотите быть абсолютно уверенным, то используйте отдельные IP-адреса для хостов, которые не должны отвечать по HTTPS, и хостов, которые должны. Это также решает проблему предупреждения браузера «неверный сертификат».

Майкл Хэмптон
источник