nginx: Як запобігти точно встановленому блоку серверів SSL виконувати роль каталізатора для всіх SSL


17

У мене є веб-сервер з багатьма віртуальними серверами. Лише 1 з них - SSL. Проблема полягає в тому, що немає блоку серверів catchall для прослуховування SSL, будь-який https-запит на інші сайти обслуговується блоком 1 SSL.

Моя конфігурація, по суті, виглядає так:

# the catch all
server {
  listen 80 default;

  # I could add this, but since I have no default cert, I cannot enable SSL,
  # and this listen ends up doing nothing (apparently).
  # listen 443; 

  server_name _;
  # ...
}

# some server
server {
  listen 80;
  server_name server1.com;
  # ...
}

# some other server ...
server {
  listen 80;
  server_name server2.com;
  # ...
}

# ... and it's https equivalent
server {
  listen 443;
  ssl on;
  server_name server2.com;
  # ...
}

Оскільки для 443 немає слухача за замовчуванням, такий запит, як https://server1.comі раніше, обслуговується server2.comблоком https. Це випливає з логіки server_nameв документах.

Якщо немає відповідності, блок {...} сервера у файлі конфігурації буде використовуватися на основі такого порядку:

  1. блок сервера з відповідною директивою прослуховування, позначеною як [за замовчуванням | default_server]
  2. перший серверний блок із відповідною директивою прослуховування (або неявне прослуховування 80;)

Яке переважне рішення цієї проблеми? Чи потрібно мені налаштовувати фіктивний сертифікат для мого спіймання всього блоку сервера, щоб я міг слухати номер 443 і обробляти погані запити? Чи є параметр, про який я не знаю, що змушує точно збігати ім'я хоста server?


Що ви хочете, коли люди намагаються отримати доступ до інших сайтів за допомогою https?
Девід Шварц

В ідеалі або я хотів би, щоб nginx взагалі не обслуговував https, якщо ім'я хоста не збігається, або перенаправляти його на той самий хост.
числа1311407

Відповіді:


9

В ідеалі або я хотів би, щоб nginx взагалі не обслуговував https, якщо ім'я хоста не збігається, або перенаправляти його на той самий хост.

Ні це неможливо. Підключення від клієнта, який переходить на https://foo.example.com/, не може бути прийнято нічим іншим, як сертифікатом SSL з "foo.example.com" як одним із його імен. Немає можливості перенаправляти, поки не буде прийнято з’єднання SSL.

Якщо ви налаштовуєте кожен сайт для SSL, користувач, який натискає помилку сертифіката, отримає потрібний вам сайт. Якщо ви налаштуєте веб-сайт "схопити всіх" для SSL, який надає лише сторінку помилок, і налаштувати віртуальний хостинг на основі імен для одного сайту, який повинен підтримувати SSL, ви можете обслуговувати сторінку помилок клієнтам.

Віртуальний хостинг SSL та HTTP просто не грають добре разом.


Це те, що я зібрав, прочитавши документи. Я просто сподівався, що я щось пропустив. Мене взагалі не хвилює попередження про SSL. Я просто не хочу, щоб хтось входив на server1.com і опинявся на домашній сторінці server2.com ... Чи справді немає способу сказати nginx, щоб не прийняв запит?
числа1311407

Якщо він не приймає запит, перший сайт не працюватиме. Він повинен прийняти запит, щоб дізнатися, на який сайт користувач намагається отримати доступ.
Девід Шварц

2
"З'єднання від клієнта, який переходить до foo.example.com, не може бути прийнято нічим іншим, як сертифікатом SSL з" foo.example.com "як одним із його імен." - Це неправильно, сервер прийме запит, і клієнт повинен переконатися, що запитуваний DN відповідає сертифікату DN сервера.
ColinM

4

Єдиний спосіб зробити це створити самопідписаний сертифікат SSL і використовувати його для отримання контролю над вхідними https-запитами. Ви можете створити свій самопідписаний сертифікат SSL декількома простими кроками, викладеними в цій публікації .

Скажімо, ви створюєте самопідписаний сертифікат з ім'ям файлу server.crt. Потім ви додасте в конфігурацію nginx таке:

server {
    listen  443;

    ssl    on;
    ssl_certificate         /etc/nginx/ssl/server.crt;
    ssl_certificate_key     /etc/nginx/ssl/server.key;

    server_name server1.com;

    keepalive_timeout 60;
    rewrite ^       http://$server_name$request_uri? permanent;
}

Ви все одно отримаєте попереджувальне повідомлення браузера SSL, але принаймні ви матимете контроль над тим, що буде далі.


1
Я згоден з цим. Існує проблема в тому, що веб-переглядач показує попереджувальне повідомлення про ненадійні сертифікати, але якщо ви лише намагаєтесь не допустити, щоб користувачі перейшли на https: // <ip-address>, щоб отримати сервер, настільки ж недійсний сертифікат для одного з ваших справжніх vhosts (недійсний оскільки ім'я хоста не збігатиметься), то вам краще подавати їм недійсний сертифікат фіктивного підпису. Такий вид говорить їм: "тут нічого дивитись, навіть сертифікат від іншого хоста".
Даніель Ф

2

Додайте блок загального сервера та код статусу повернення 444. Він повідомляє nginx закрити з'єднання перед надсиланням будь-яких даних.

server {
    listen 443 default_server ssl;
    server_name _;
    return 444;
}

1

У ці дні ви можете використовувати розширення індикації імені сервера TLS (SNI, RFC 6066). Слухач HTTPS зможе розпізнати доменне ім'я, перш ніж подавати відповідний сертифікат.

Це означає, що вам потрібно мати сертифікати для ВСІХ доменів, і коли SNI використовується для розпізнавання одного з інших доменів, ви можете просто перенаправити HTTP 301 на незашифровану версію HTTP, якщо ім'я сервера не відповідає тій, якій потрібно шифрування.

Більше інформації про SNI можна знайти в документації на nginx http://nginx.org/en/docs/http/configuring_https_servers.html


0

Зіставити запитуване ім'я хоста на дійсні імена хостів у http {}блоці:

map $ssl_server_name $correct_hostname_example {
  default 0;
  example.com 1;
  www.example.com 1;
}

А потім у server {}блоці вбити з'єднання з неправильним іменем хоста:

if ($correct_hostname_example = 0) {
  return 444;
}

Використовуйте декілька карт за необхідності для декількох блоків сервера. З'єднання все одно буде встановлено за допомогою одного з ваших сертифікатів, але якщо цей останній блок присутній у кожному блоці сервера, який обслуговує SSL, то ви ефективно «блокуєте» з'єднання з недійсними іменами хостів. Це може знадобитися лише в першому блоці сервера, але додавання його до кожного блоку серверів гарантуватиме, що порядок не має значення.

$ssl_server_nameМінлива присутній в Nginx 1.7 або вище.


0

Ось як я вирішив проблему:

  1. Створіть самопідписаний сертифікат:

openssl req -nodes -x509 -newkey rsa:4096 -keyout self_key.pem -out self_cert.pem -days 3650

  1. Скопіюйте його там, де його може знайти NginX:

cp self*.pem /etc/nginx/ssl/

  1. Налаштування маршруту загального переліку:
server {
    listen 443 default_server ssl;

    ssl on;
    ssl_certificate /etc/nginx/ssl/self_cert.pem;
    ssl_certificate_key /etc/nginx/ssl/self_key.pem;

    return 301 http://$host;
}

Що це буде робити: він подасть вам попередження (ніяк не обійти його) на будь-якому сервері, який не має власного сертифіката, але попередження не скаже неправильну назву сертифіката. Якщо користувач натисне "відвідати все-таки", він буде переспрямований на не-ssl версію веб-сайту, який ви ввели.

застереження :

якщо ваш веб-сайт із підтримкою SLL визначає лише www.example.com(а не example.com), то ваш загальнодоступний маршрут в кінцевому підсумку подаватиме https://example.comсертифікат самопідписання та відповідне попередження.


-2

Перенаправлення на http:

server {
    listen       443;
    server_name  *.com;
    rewrite        ^ http://$host$request_uri? permanent;
}    

Повернення 404:

server {
    listen       443;
    server_name  *.com;
    return 404;
}    

1
Це все одно призведе до попередження SSL, оскільки тунель SSL потрібно встановити відбувається, перш ніж відбудеться перенаправлення HTTP. Дивіться прийняту відповідь Девіда Шварца.
cjc
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.