nginx: Jak zapobiegać działaniu dokładnie tego samego bloku serwera SSL jako blokady dla wszystkich SSL

17

Mam serwer WWW z wieloma serwerami wirtualnymi. Tylko 1 z nich to SSL. Problem polega na tym, że ponieważ nie istnieje blokujący serwer serwer nasłuchujący protokołu SSL, każde żądanie https do innych witryn jest obsługiwane przez 1 blok SSL.

Moja konfiguracja wygląda następująco:

# 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;
  # ...
}

Ponieważ teraz nie ma domyślnego nasłuchiwania dla 443, takie żądanie https://server1.comzostanie obsłużone przez server2.comblok https. Wynika to z logiki server_namew docs.

Jeśli nie ma dopasowania, zostanie użyty blok serwera {...} w pliku konfiguracyjnym w następującej kolejności:

  1. blok serwera z pasującą dyrektywą nasłuchującą oznaczoną jako [default | default_server]
  2. pierwszy blok serwera z pasującą dyrektywą Listen (lub niejawnie Listen 80;)

Jakie jest preferowane rozwiązanie tego problemu? Czy muszę skonfigurować atrapę certyfikatu do przechwytywania wszystkich bloków serwera, aby móc słuchać na 443 i obsługiwać złe żądania? Czy jest parametr, którego nie znam, a który wymusza dokładne dopasowanie nazwy hosta server?

numery1311407
źródło
Co chcesz się stać, gdy ludzie będą próbować uzyskać dostęp do innych witryn przy użyciu protokołu https?
David Schwartz
Idealnie byłoby, gdyby nginx w ogóle nie wyświetlał https, chyba że nazwa hosta jest zgodna, lub przekierowywał na http na tym samym hoście.
numery1311407

Odpowiedzi:

9

Idealnie byłoby, gdyby nginx w ogóle nie wyświetlał https, chyba że nazwa hosta jest zgodna, lub przekierowywał na http na tym samym hoście.

Nie jest to możliwe. Połączenie od klienta, które przechodzi do https://foo.example.com/, nie może być zaakceptowane przez nic poza certyfikatem SSL z „foo.example.com” jako jedną z jego nazw. Nie ma możliwości przekierowania, dopóki połączenie SSL nie zostanie zaakceptowane.

Jeśli skonfigurujesz każdą witrynę dla protokołu SSL, użytkownik, który kliknie błąd certyfikatu, otrzyma żądaną witrynę. Jeśli skonfigurujesz witrynę „catch all” dla protokołu SSL, która zawiera tylko stronę błędu, i skonfigurujesz wirtualny hosting oparty na nazwie dla jednej witryny, która ma obsługiwać protokół SSL, możesz wyświetlić stronę błędu klientom.

Wirtualny hosting SSL i HTTP po prostu nie działają dobrze razem.

David Schwartz
źródło
To właśnie zebrałem po przeczytaniu dokumentacji. Miałem tylko nadzieję, że coś przeoczyłem. W ogóle mnie nie obchodzą ostrzeżenia SSL. Po prostu nie chcę, aby ktoś wszedł na server1.com i znalazł się na stronie głównej server2.com ... Czy naprawdę nie ma sposobu, aby powiedzieć Nginx, aby nie zaakceptował żądania?
numery1311407,
Jeśli nie zaakceptuje żądania, pierwsza strona nie będzie działać. Musi zaakceptować żądanie, aby dowiedzieć się, do której strony użytkownik próbuje uzyskać dostęp.
David Schwartz
2
„Połączenie klienta, który przechodzi do foo.example.com, nie może być zaakceptowane przez nic poza certyfikatem SSL z„ foo.example.com ”jako jedną z jego nazw.” - To nie jest poprawne, serwer zaakceptuje żądanie i do klienta należy sprawdzenie, czy żądana nazwa wyróżniająca jest zgodna z nazwą wyróżniającą serwera.
ColinM
4

Jedynym sposobem jest utworzenie samopodpisanego certyfikatu SSL i wykorzystanie go do uzyskania kontroli nad przychodzącymi żądaniami https. Możesz utworzyć samopodpisany certyfikat SSL w kilku prostych krokach opisanych w tym poście .

Załóżmy, że tworzysz samopodpisany certyfikat z nazwą pliku server.crt. Następnie dołączysz do swojej konfiguracji 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;
}

Nadal będziesz otrzymywać ostrzeżenie SSL dotyczące przeglądarki, ale przynajmniej będziesz mieć kontrolę nad tym, co będzie dalej.

użytkownik1973679
źródło
1
Zgadzam się z tym. Występuje problem z wyświetlaniem przez przeglądarkę ostrzeżenia o niezaufanych certyfikatach, ale jeśli próbujesz tylko uniemożliwić użytkownikom przejście na https: // <adres_ip>, aby uzyskać serverd równie nieprawidłowy certyfikat dla jednego z twoich prawdziwych hostów vhost (nieprawidłowy ponieważ nazwa hosta się nie zgadza), lepiej jest podać im nieprawidłowy certyfikat z podpisem własnym. Ten rodzaj mówi im, że „nie ma tu nic do zobaczenia, nawet certyfikat z innego hosta”.
Daniel F
2

Dodaj blok serwera typu catch-all i zwróć kod stanu 444. Mówi on nginx, aby zamknął połączenie przed wysłaniem jakichkolwiek danych.

server {
    listen 443 default_server ssl;
    server_name _;
    return 444;
}
ATLief
źródło
1

Obecnie można korzystać z rozszerzenia TLS Server Name Indication (SNI, RFC 6066). Odbiornik HTTPS będzie mógł rozpoznać nazwę domeny przed podaniem odpowiedniego certyfikatu.

Oznacza to, że musisz mieć certyfikaty dla WSZYSTKICH domen, a gdy SNI jest używany do rozpoznania jednej z pozostałych domen, możesz po prostu użyć przekierowania HTTP 301 do niezaszyfrowanej wersji HTTP, chyba że nazwa serwera odpowiada tej, która potrzebuje szyfrowanie.

Więcej informacji o SNI dostępnych w dokumentacji nginx http://nginx.org/en/docs/http/configuring_https_servers.html

Jewgienij
źródło
0

Odwzoruj żądaną nazwę hosta na prawidłowe nazwy hostów w http {}bloku:

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

A następnie w server {}bloku zabij połączenia z niewłaściwą nazwą hosta:

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

W razie potrzeby używaj wielu map dla wielu bloków serwerów. Połączenie nadal będzie nawiązywane przy użyciu jednego z twoich certyfikatów, ale jeśli ten ostatni blok jest obecny w każdym bloku serwera obsługującym SSL, wówczas skutecznie „zablokujesz” połączenia z nieprawidłowymi nazwami hostów. Może to być konieczne tylko w pierwszym bloku serwera, ale dodanie go do każdego bloku serwera zapewni, że kolejność nie będzie miała znaczenia.

$ssl_server_nameZmienna występuje w nginx 1,7 lub więcej.

ColinM
źródło
0

Oto jak rozwiązałem problem:

  1. Utwórz samopodpisany certyfikat:

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

  1. Skopiuj go tam, gdzie NginX może go znaleźć:

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

  1. Ustaw trasę typu catch-all:
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;
}

Co to zrobi: wyświetli ostrzeżenie (nie można tego obejść) na dowolnym serwerze, który nie ma własnego certyfikatu, ale ostrzeżenie nie powie niewłaściwej nazwy certyfikatu. Jeśli użytkownik kliknie „mimo to odwiedź”, zostanie przekierowany do wersji strony, którą napisał.

zastrzeżenie :

jeśli twoja witryna obsługująca SLL definiuje www.example.com(a nie tylko example.com), wtedy twoja trasa catch-all skończy się https://example.comz certyfikatem z podpisem własnym i odpowiednim ostrzeżeniem.

ierdna
źródło
-2

Przekieruj na http:

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

Zwróć 404 :

server {
    listen       443;
    server_name  *.com;
    return 404;
}    
freestyler
źródło
1
Nadal będzie to powodować wyświetlanie ostrzeżenia SSL, ponieważ tunel SSL musi zostać ustanowiony, zanim nastąpi przekierowanie HTTP. Zobacz zaakceptowaną odpowiedź Davida Schwartza.
cjc