Richten Sie einen „Standard“ -Nginx-Server für https richtig ein

71

Ich habe mehrere Server auf demselben Computer, einige nur mit http, einige mit http und https. In separaten Dateien sind mehrere Serverblöcke definiert, die in der Hauptkonfigurationsdatei enthalten sind.

Ich habe einen "Standard" -Server für http eingerichtet, der eine allgemeine "Wartungsseite" für Anforderungen bereitstellt, die keinem der anderen Servernamen in den anderen Konfigurationsdateien entsprechen. Der http-Standardserver funktioniert wie erwartet, er verwendet den Servernamen "_" und erscheint zuerst in der Liste der Includes (da ich festgestellt habe, dass bei doppelten Servernamen der zuerst angezeigte verwendet wird). Das funktioniert super.

Ich würde den gleichen genauen Serverblock erwarten (nur das Umschalten von "listen 80 default_server" auf "listen 443 default_server" und auch, anstatt die Seite "return 444" zu bedienen), tut dies jedoch nicht. Stattdessen scheint es, dass der neue Standard-https-Server tatsächlich alle eingehenden https-Verbindungen abruft und dazu führt, dass diese fehlschlagen, obwohl die anderen Serverblöcke geeignetere Servernamen für die eingehenden Anforderungen haben. Durch das Entfernen des neuen Standard-https-Servers wird das halbkorrekte Verhalten fortgesetzt: Die Websites mit https werden alle korrekt geladen. Websites ohne https werden jedoch alle an den ersten https-Server in den Include-Dateien weitergeleitet (wenn laut Dokumentation kein "default_server" angezeigt wird, ist der erste Serverblock "default").

Meine Frage ist also, wie man in nginx einen "Standardserver" für SSL-Verbindungen richtig definiert. Warum ist es so, dass wenn ich explizit einen "default_server" setze, es gierig wird und alle Verbindungen aufbaut, während wenn ich nginx implizit den "default server" bestimmen lasse, funktioniert es wie erwartet (mit dem falschen Server als Standard und den anderen realen Servern) sich richtig verhalten)?

Hier sind meine "Standardserver". HTTP funktioniert, ohne andere Server zu beschädigen. HTTP bricht andere Server und verbraucht alles.

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

Weiß jemand von euch, was hier falsch sein könnte?


quelle

Antworten:

27

In Ihrem "Standard" -Https-Block ist weder ssl_certificate noch ssl_certificate_key definiert. Obwohl Sie für dieses Standardszenario keinen echten Schlüssel haben oder möchten, müssen Sie dennoch einen konfigurieren, da andernfalls Nginx das von Ihnen beschriebene unerwünschte Verhalten aufweist.

Erstellen Sie ein selbstsigniertes Zertifikat mit dem allgemeinen Namen * und fügen Sie es in Ihre Konfiguration ein. Es funktioniert dann wie gewünscht.

Das "Standard" -Verhalten bei dieser Einstellung wäre, dass ein Browser eine Warnung erhalten würde, dass dem Zertifikat nicht vertraut werden kann. Wenn der Benutzer das Zertifikat ausnahmsweise hinzufügt, wird die Verbindung von nginx getrennt und der Standard des Browsers wird angezeigt Fehlermeldung "Konnte keine Verbindung herstellen".

Radmilla Mustafa
quelle
1
Ich habe es versucht, aber es funktioniert immer noch nicht: Alle SSL-Anfragen an die IP gehen an meinen anderen SSL-Host. Könnte ich noch etwas versuchen?
Michael Härtl
23

Ich habe es geschafft, mit nginx ein gemeinsames dediziertes Hosting auf einer einzelnen IP zu konfigurieren. Standardmäßiges HTTP und HTTPS, das eine 404 für eingehende unbekannte Domänen bereitstellt.

1 - Erstellen Sie eine Standardzone

Da nginx vhosts in ASCII-Reihenfolge lädt, sollten Sie eine 00-defaultDatei / einen symbolischen Link in Ihrem erstellen /etc/nginx/sites-enabled.

2 - Füllen Sie die Standardzone

Füllen Sie Ihre 00-defaultmit voreingestellten vhosts. Hier ist die Zone, die ich benutze:

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 - Selbstsigniertes Zertifikat erstellen, testen und neu laden

Sie müssen ein selbstsigniertes Zertifikat in erstellen /etc/nginx/ssl/nginx.crt.

Erstellen Sie ein selbstsigniertes Standardzertifikat:

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

Nur eine Erinnerung:

  • Testen Sie die Nginx-Konfiguration vor dem erneuten Laden / Neustarten: nginx -t
  • Laden Sie ein Vergnügen neu: sudo service nginx reload

Ich hoffe es hilft.

Wenn nicht
quelle
2
Behebt die Browser-Warnung vor dem Besuch einer unsicheren Site nicht.
gdbj
5
Kann nicht aufgelöst werden, da ein Catch-All mit allen Domänen übereinstimmen muss. Kein SSL kann alle Domains mit einem Platzhalter versehen. Stellen Sie sich vor, wenn Sie die Adresse google.fr auf Ihrem Server fälschen, können Sie Ihren Server als google.fr authentifizieren. Es wäre ein ernstes Sicherheitsproblem :(
Wenn nicht
Ja, ich denke, das macht Sinn. Leider wird in Chrome eine beängstigende Warnung angezeigt, bevor die 404-Seite angezeigt wird. Dies ist schlimmer, als wenn der Server den Datenverkehr einfach ablehnt. Es sieht so aus, als wäre der Server falsch konfiguriert.
gdbj
Vielen Dank. Dies funktionierte für mich, außer dass ich default_serverfür Listen 443 hinzufügen musste und die IPv6-Adresse [::]: 80 und [::]: 443 mit hinzufügte default_server.
chmike
16

Grundsätzlich möchten wir unter allen Umständen vermeiden, dass die erste Serverdefinition in unserer Konfigurationsdatei als Catch-All-Server für SSL-Verbindungen dient. Wir alle wissen, dass dies der Fall ist (im Gegensatz zu http und der Verwendung von default_server config, die gut funktioniert).

Dies kann (noch) nicht deklarativ für SSL erreicht werden, daher müssen wir es mit einer IF codieren ...

Die Variable $hostist der Hostname aus der Anforderungszeile oder dem http-Header. Die Variable $server_nameist der Name des Serverblocks, in dem wir uns gerade befinden.

Wenn diese beiden also nicht gleich sind, haben Sie diesen SSL-Serverblock für einen anderen Host bereitgestellt, sodass dieser blockiert werden sollte.

Der Code enthält keine spezifischen Verweise auf Ihre Server-IP-Adressen, sodass er ohne Änderungen problemlos für andere Serverkonfigurationen wiederverwendet werden kann.

Beispiel:

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
quelle
kannst du erklären was das listen [::]:443 ssl http2;macht Ich habe Probleme, die Dokumentation dafür zu finden.
Andrew Brown
Ich glaube, ich habe es gefunden, musste nur wissen, wonach ich suchen sollte. IPV4- und IPV6-Richtlinien.
Andrew Brown
7

Um mehr über die Antwort von Radmilla Mustafa zu erfahren:

Nginx verwendet den 'Host'-Header, um den Servernamen abzugleichen. Es wird kein TLS-SNI verwendet. Dies bedeutet, dass nginx für einen SSL-Server eine SSL-Verbindung akzeptieren muss, was darauf hinausläuft, ein Zertifikat / einen Schlüssel zu haben. Das Zertifikat / der Schlüssel kann ein beliebiges sein, z. B. selbst signiert.

Siehe Dokumentation

Die Lösung lautet daher:

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
quelle
Ich bin der Meinung, dass dies die akzeptierte Antwort sein sollte. Danke vielmals.
aggregate1166877
2

An alle, die darüber so viel Haare verloren haben wie ich (die heute fast den ganzen Tag damit verbracht haben). Ich habe fast alles ausprobiert, und das, was dazu geführt hat, dass es endlich richtig funktioniert, war diese dumme Zeile:

ssl_session_tickets off;

Aufbauend auf der Antwort von Ifnot lautet mein Arbeitsbeispiel:

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

Ich habe keine Ahnung, warum dies notwendig war, das einzige Prinzip, das ich daraus ableitete, war, dass Nginx sich sehr seltsam verhält, wenn wir ihm nicht geben, was er will.

Marek Lisý
quelle
1
Du bist eine Legende.
Nizar Blond
1

Wenn Sie absolut sicher sein möchten, verwenden Sie separate IP-Adressen für Hosts, die nicht auf HTTPS antworten sollen, und für Hosts, die nicht antworten sollen. Dies behebt auch das Problem der Browserwarnung "Ungültiges Zertifikat".

Michael Hampton
quelle