Prawidłowe skonfigurowanie „domyślnego” serwera nginx dla https

70

Mam kilka serwerów działających na tym samym komputerze, niektóre tylko z http, niektóre z zarówno http, jak i https. Istnieje kilka bloków serwera zdefiniowanych w osobnych plikach, które są zawarte w głównym pliku konfiguracyjnym.

Skonfigurowałem „domyślny” serwer http, który będzie obsługiwał ogólną „stronę konserwacji” dla żądań, które nie pasują do żadnej z pozostałych nazw serwerów w innych plikach konfiguracyjnych. Domyślny serwer http działa zgodnie z oczekiwaniami, używa nazwy serwera „_” i pojawia się jako pierwszy na liście dołączeń (ponieważ zauważyłem, że w przypadku zduplikowanych nazw serwerów na serwerach używany jest ten, który pojawia się jako pierwszy). To działa świetnie.

Spodziewałbym się tego samego dokładnego bloku serwera (tylko przełączenie „Listen 80 default_server” na „Listen 443 default_server”, a także zamiast wyświetlania strony „return 444”), ale tak nie jest. Zamiast tego wydaje się, że nowy domyślny serwer https faktycznie przechwytuje wszystkie przychodzące połączenia https i powoduje ich awarię, chociaż inne bloki serwera mają bardziej odpowiednie nazwy_serwera dla przychodzących żądań. Usunięcie nowego domyślnego serwera https spowoduje wznowienie częściowo niepoprawnych zachowań: strony internetowe z https załadują się poprawnie; ale witryny bez https będą przekierowywane na pierwszy serwer https w plikach dołączanych (które zgodnie z dokumentami, jeśli nie pojawi się „serwer_ domyślny”, to pierwszy blok serwera, który się pojawi, będzie „domyślny”).

Moje pytanie brzmi: jaki jest prawidłowy sposób zdefiniowania „domyślnego serwera” w nginx dla połączeń ssl? Dlaczego po jawnym ustawieniu „default_server” staje się chciwy i pobiera wszystkie połączenia, a gdy domyślnie pozwalam nginx decydować o „domyślnym serwerze”, działa tak, jakbym się spodziewał (z niepoprawnym serwerem ustawionym jako domyślny i innymi prawdziwymi serwerami zachowuje się poprawnie)?

Oto moje „domyślne serwery”. HTTP działa bez niszczenia innych serwerów. Https psuje inne serwery i zużywa wszystko.

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

Czy ktoś z was widzi, co może być nie tak?


źródło

Odpowiedzi:

27

Nie masz zdefiniowanego żadnego ssl_certificate lub ssl_certificate_key w swoim „domyślnym” bloku https. Chociaż nie masz ani nie chcesz prawdziwego klucza dla tego domyślnego scenariusza, nadal musisz go skonfigurować, w przeciwnym razie nginx będzie miał niepożądane zachowanie, które opisujesz.

Utwórz samopodpisany certyfikat o nazwie zwyczajowej * i podłącz go do konfiguracji, a zacznie on działać tak, jak chcesz.

„Domyślnym” zachowaniem w tej konfiguracji byłoby to, że przeglądarka otrzyma ostrzeżenie, że certyfikatowi nie można ufać, jeśli użytkownik doda certyfikat jako wyjątek, połączenie zostanie zerwane przez nginx i zobaczy domyślną przeglądarkę Komunikat o błędzie „Nie można połączyć”.

Radmilla Mustafa
źródło
1
Próbowałem tego, ale nadal nie działa: Wszystkie żądania ssl do adresu IP są kierowane do mojego innego hosta ssl. Mogę jeszcze spróbować?
Michael Härtl,
22

Udało mi się skonfigurować współdzielony dedykowany hosting na jednym adresie IP za pomocą nginx. Domyślne HTTP i HTTPS obsługujące 404 dla nieznanych domen przychodzących.

1 - Utwórz strefę domyślną

Ponieważ nginx ładuje vhosty w kolejności ascii, powinieneś utworzyć 00-defaultplik / dowiązanie symboliczne do swojego /etc/nginx/sites-enabled.

2 - Wypełnij strefę domyślną

Wypełnij swoje 00-defaultdomyślne vhosty. Oto strefa, z której korzystam:

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 - Utwórz samopodpisany certyfikat, przetestuj i załaduj ponownie

Musisz utworzyć samopodpisany certyfikat w /etc/nginx/ssl/nginx.crt.

Utwórz domyślny certyfikat z podpisem własnym:

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

Tylko przypomnienie:

  • Przetestuj konfigurację nginx przed ponownym załadowaniem / ponownym uruchomieniem: nginx -t
  • Załaduj ponownie: sudo service nginx reload

Mam nadzieję, że to pomoże.

Jeśli nie
źródło
2
Nie rozwiązuje ostrzeżenia przeglądarki o niezabezpieczonej witrynie.
gdbj
4
Nie można rozwiązać, ponieważ catch-all musi pasować do dowolnej domeny. Żaden SSL nie może używać symboli wieloznacznych we wszystkich domenach. Wyobraź sobie, że jeśli fałszujesz adres google.fr na swoim serwerze, możesz uwierzytelnić swój serwer jako google.fr. Byłoby to poważny problem zabezpieczeń :(
Ifnot
Tak, myślę, że to ma sens. Niestety w Chrome wyświetla się straszne ostrzeżenie, zanim zobaczysz stronę 404, co jest gorsze niż po prostu odrzucenie ruchu przez serwer. Wygląda na to, że serwer jest źle skonfigurowany.
gdbj
Dziękuję Ci bardzo. To działało dla mnie, tyle że musiałem dodać default_serverdo nasłuchiwania 443 i dodałem adres IPv6 [::]: 80 i [::]: 443 z default_server.
chmike
16

Zasadniczo chcemy za wszelką cenę uniknąć, aby pierwsza definicja serwera w naszym pliku konfiguracyjnym służyła jako serwer typu catch-all-server dla połączeń SSL. Wszyscy wiemy, że to robi (w przeciwieństwie do http i używania konfiguracji default_server, która działa dobrze).

Nie można tego osiągnąć deklaratywnie dla protokołu SSL (jeszcze), więc musimy go zakodować za pomocą IF ...

Zmienna $hostto nazwa hosta z wiersza żądania lub nagłówka http. Zmienna $server_nameto nazwa bloku serwera, w którym się teraz znajdujemy.

Więc jeśli te dwa nie są sobie równe, podałeś ten blok serwera SSL dla innego hosta, więc powinien zostać zablokowany.

Kod nie zawiera konkretnych odniesień do adresów IP serwera, dzięki czemu można go łatwo wykorzystać do innych konfiguracji serwera bez modyfikacji.

Przykład:

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

    ...
Rolf
źródło
czy możesz wyjaśnić, co to listen [::]:443 ssl http2;robi? mam problem ze znalezieniem dokumentacji na ten temat.
Andrew Brown,
Myślę, że go znalazłem, po prostu musiałem wiedzieć, czego szukać. Dyrektywy IPV4 i IPV6.
Andrew Brown,
7

Aby rozwinąć więcej informacji na temat odpowiedzi Radmilli Mustafa:

Nginx używa nagłówka „Host” do dopasowania nazwy serwera. Nie używa TLS SNI. Oznacza to, że w przypadku serwera SSL nginx musi być w stanie zaakceptować połączenie SSL, co sprowadza się do posiadania certyfikatu / klucza. Certyfikat / klucz może być dowolny, np. Z podpisem własnym.

Zobacz dokumentację

Dlatego rozwiązaniem jest:

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
źródło
Uważam, że powinna to być zaakceptowana odpowiedź. Wielkie dzięki.
aggregate1166877
2

Dla każdego, kto stracił tyle włosów na tym co ja (spędziłem dziś na tym prawie cały dzień). Próbowałem prawie wszystkiego, a to, co sprawiło, że w końcu działało poprawnie, to ta głupia linia:

ssl_session_tickets off;

Opierając się na odpowiedzi Ifnota , moim działającym przykładem jest:

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

Nie mam pojęcia, dlaczego było to konieczne, jedyną zasadą, którą z tego wyprowadziłem, było to, że Nginx zachowuje się bardzo dziwnie, gdy nie dajemy mu tego, czego chce.

Marek Lisý
źródło
1
jesteś legendą.
Nizar Blond
1

Jeśli chcesz być absolutnie pewien, użyj osobnych adresów IP dla hostów, które nie powinny odpowiadać na HTTPS i hostów, które powinny. Rozwiązuje to również problem z ostrzeżeniem przeglądarki „nieprawidłowy certyfikat”.

Michael Hampton
źródło