05 Raspberry Pi - nginx konfigurieren (SSL mit letsencrypt)

in #deutsch2 years ago (edited)

Raspberry_Pi_Logo_nginx.png

In diesem Artikel beschreibe ich wie du nginx auf deinem Raspberry Pi installierst und zeige dir eine mögliche Konfiguration.

Nginx ist eine gute Wahl als Webserver auf einem Raspberry Pi, da Nginx deutlich ressourcenschonender arbeitet als z.B. Apache.

Nginx installieren

Nginx ist in den offizielen Paketquellen enthalten und lässt sich einfach installieren mit:
sudo apt install nginx
Wenn alles geklappt hat und dein Raspberry Pi korrekt erreichbar ist, solltest du eine default Seite angezeigt bekommen, wenn du den Raspberry Pi über einen Browser mit einer Adresse oder der IP besuchst.
nginx_welcome.png

Nginx allgemein konfigurieren

Zuerst kannst du einige allgemeine Einstellungen für Nginx in /etc/nginx/nginx.conf überprüfen.

  • user www-data;
    www-data ist der user unter dem nginx läuft. Die Dateien in deinem webroot sollten diesem Nutzer gehören. Das erreichst du mit
    sudo chown -R www-data:www-data /var/www/
  • worker_processes auto;
    Diese Option bestimmt wie viele Threads nginx eröffnet. auto ist hier in den meisten Fällen die richtige Einstellung.
  • server_tokens off;
    Möglicherweise findest du diese Option auskommentiert oder du musst sie selber im http-Block hinzufügen. Diese Einstellung sorgt dafür, dass nginx bei Fehlern nicht mehr seine Versionsnummer preisgibt und ist damit ein Sicherheitsfeature.

Nginx virtual host Konzept

Ich habe über die Zeit eine nginx hosts Konfiguration entwickelt, die mir für alle meine Anwendungen volle Kompatibilität bietet. Wenn du nur einen Webserver hinter einer Domain laufen lassen möchtest, ist diese Konfiguration möglicherweise etwas overkill für dich. Ich zeige dir trotzdem welche Vorteile du damit hast. Ich nutze in dieser Konfiguration mehrere virtual hosts um folgenden Aufbau zu erreichen:

nginx_konf.png

  • Gatewayhost:
    Hier trifft der Besucher auf deinen Webserver, typischerweise auf Port 80 für http und Port 443 für https. In diesem virtual host ist einerseits eine Weiterleitung aktiv, die alle Anfragen über http an Port 80 auf https über Port 443 umleitet. Außerdem ist hier die gesamte Konfiguration für SSL. Der Vorteil ist dabei, dass die Konfiguration für SSL nur in einer Datei beschrieben wird und nicht für jeden host eine eigene SSL Konfiguration benötigt wird. Weiterhin wird sichergestellt, dass jede Kommunikation verschlüsselt abläuft, da hosts hinter dem Gatewayhost somit nicht unverschlüsselt erreichbar sind.
  • domain-Auflösung:
    Alle Anfragen werden nun an einen Port weitergeleitet, z.B. 81. An Port 81 lauschen nun mehrere hosts, die alle auf verschiedene Domainnamen reagieren.
  • Reverse Proxy:
    Wenn hinter einer Domain verschiedene Services laufen sollen, können weiterhin noch Reverse Proxys über Pfadauflösung konfiguriert werden.

SSL mit Letsencrypt einrichten

nginx vorbereiten

Um ein Zertifikat von Letsencrypt zu beziehen wird zuerst ein virtual host für Letsencrypt erstellt. Dies wird in der Weise getan, die bereits der oben benannten Struktur genügt. Die Dateinahmen beschreiben bei mir entweder den Service oder die domain. Außerdem enthalten sie den jeweiligen Port auf den der host lauscht, um nicht durcheinander zu kommen. Anlegen des Gatewayhosts in der Datei:
etc/nginx/sites-available/gateway_80_443.conf

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    root /var/www;
    server_name raspberrypi.abcdefghijklmn.myfritz.net;

    location ^~ /.well-known/acme-challenge {
        proxy_pass http://127.0.0.1:82;
        proxy_redirect off;
    }       
}

Hinter Port 82 verbirgt sich also der letsencrypt host. Dieser wird angelegt in der Datei:
/etc/nginx/sites-available/letsencrypt_82.conf

server {
    listen 127.0.0.1:82;
    server_name 127.0.0.1;  
    
    location ^~ /.well-known/acme-challenge {
        default_type text/plain;
        root /var/www/letsencrypt;
    }
}

Um diese hosts zu aktivieren müssen Links von /etc/nginx/sites-enable/ auf die eben erstellen Dateien erstellt werden. z.B. mit
sudo ln -s /etc/nginx/sites-available/* /etc/nginx/sites-enable/
Achte dabei darauf, dass sich kein Link mehr zu der default Seite in diesem Verzeichnis befindet. Außerdem sollte der Ordner letsencrypt im webroot erstellt werden
sudo mkdir /var/www/letsencrypt/
sudo chown -R www-data:www-data /var/www/

Wenn du alle Änderungen abgeschlossen hast, kannst du die Konfiguration testen mit:
sudo nginx -t
Wenn dies erfolgreich war übernimmst du die Einstellungen indem du nginx neustartest:
sudo service nginx restart

letsencrypt installieren

Nun ist alles bereit um letsencrypt zu nutzen. Installieren kannst du es mit
sudo apt install certbot

Zertifikat erstellen

Mit folgendem Befehl erstellt du dir ein Zertifikat:
sudo certbot certonly --webroot -w /var/www/letsencrypt -d raspberrypi.abcdefghijklmn.myfritz.net --rsa-key-size 4096
Du kannst auch weitere Domains angeben für die das Zertifikat gültig sein soll indem du weitere -d Optionen angibst. Die erste -d Option gibt den Hauptnamen des Zertifikats an, welcher auch den Pfad bestimmen wird unter dem die Zertifikate gespeichert werden. Die domain von einem http-Proxy, wie ich ihn hier beschrieben habe, muss nicht angegeben werden, da der http-Proxy eigene Zertifikate benutzt. Eine domain von einem Portmapper wie feste-ip.net kann nicht angegeben werden, da dir nur Subdomains zugewiesen werden und du im Besitz der Hauptdomain sein musst um Letsencrypt zu nutzen.

Diffie Hellman Parameter erstellen

Um Perfect Forward Secrecy zu ermöglichen, müssen Diffie Hellmann Parameter erstellt werden. Dieser Vorgang kann einige Stunden dauern. Du kannst die Datei auch auf einem leistungsstärkerem PC als dem Raspberry Pi erstellen, was die Erstellung beschleunigen wird. Dabei ist jedoch darauf zu achten, dass die erstellte Datei nicht unsicher über das Internet auf den Raspberry Pi übertragen wird. Als Ort für die Datei eignet sich z.B. /etc/nginx/ssl. Die Erstellung kann begonnen werden mit
sudo openssl dhparam -out /etc/nginx/ssl/dhparams.pem 2048
Für paranoidere Anwender sind auch noch sichere Parameter möglich mit:
sudo openssl dhparam -out /etc/nginx/ssl/dhparams.pem 4096
Die Erstellung dauert in dem Fall noch einmal deutlich länger. Es ist auch möglich den Befehl im Hintergrund auszuführen mit
sudo openssl dhparam -out /etc/nginx/ssl/dhparams.pem 2048&
Dann ist z.B. auch ein ausloggen möglich und du kannst zu einem späteren Zeitpunkt wiederkommen.

Zugriffsberechtigungen

Die Zertifikatsdateien sollten die korrekten Zugriffsberechtigungen haben:
sudo chmod 600 /etc/letsencrypt/live/raspberrypi.abcdefghijklmn.myfritz.net/*
sudo chmod 600 /etc/nginx/ssl/dhparams.pem

nginx für SSL konfigurieren

Der gatewayhost kann nun so geändert werden, dass jegliche Kommunikation über SSL läuft. Dazu wird wieder etc/nginx/sites-available/gateway_80_443.conf editiert.

server {
    listen 80 default_server;
    listen [::]:80 default_server;
    root /var/www;
    server_name example1.com example2.com raspberrypi.abcdefghijklmn.myfritz.net;

    location ^~ /.well-known/acme-challenge {
        proxy_pass http://127.0.0.1:81;
        proxy_redirect off;
    }   
    location = /robots.txt {
    allow all;
    log_not_found off;
    access_log off;
    }   
    location / {
        # Enforce HTTPS
        return 301 https://$server_name$request_uri;
        return 301 https://$server_addr$request_uri;
    }       
}

server {
    listen 443 ssl http2 default_server;
    listen [::]:443 ssl http2 default_server;
    server_name _;
    root /var/www;

    # Configure SSL
    ssl on;
    # Certificates used
    ssl_certificate /etc/letsencrypt/live/raspberrypi.abcdefghijklmn.myfritz.net/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/raspberrypi.abcdefghijklmn.myfritz.net/privkey.pem;

    # Not using TLSv1 will break:
    #   Android <= 4.4.40
    #   IE <= 10
    #   IE mobile <=10
    # Removing TLSv1.1 breaks nothing else!
        # There are not many clients using TLSv1.3 so far, but this can be activated with nginx v1.13
    ssl_protocols TLSv1.2;

    # Using the recommended cipher suite from: https://wiki.mozilla.org/Security/Server_Side_TLS
    ssl_ciphers 'ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK';

    # Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
    ssl_dhparam /etc/nginx/ssl/dhparams.pem;

    # Specifies a curve for ECDHE ciphers.
    # High security, but will not work with Chrome:
    #ssl_ecdh_curve secp521r1;  
    # Works with Windows (Mobile), but not with Android (DavDroid):
    #ssl_ecdh_curve secp384r1;
    # Works with Android (DavDroid):
    ssl_ecdh_curve prime256v1; 

    # Server should determine the ciphers, not the client
    ssl_prefer_server_ciphers on;

    # OCSP Stapling
    # fetch OCSP records from URL in ssl_certificate and cache them
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/raspberrypi.abcdefghijklmn.myfritz.net/fullchain.pem;
resolver 192.168.178.1;
  
    # SSL session handling
    ssl_session_timeout 24h;
    ssl_session_cache shared:SSL:50m;
    ssl_session_tickets off;
 
    # Add headers to serve security related headers
    # HSTS (ngx_http_headers_module is required)
    # In order to be recoginzed by SSL test, there must be an index.hmtl in the server's root
    add_header Strict-Transport-Security "max-age=63072000; includeSubdomains" always;
    add_header X-Content-Type-Options "nosniff" always;
    # Usually this should be "DENY", but when hosting sites using frames, it has to be "SAMEORIGIN"
    add_header Referrer-Policy "same-origin" always;
    add_header X-XSS-Protection "1; mode=block" always;
    add_header X-Robots-Tag none;
    add_header X-Download-Options noopen;
    add_header X-Permitted-Cross-Domain-Policies none;
 
    location ^~ / {
        client_max_body_size 10G;
        proxy_connect_timeout 3600;
        proxy_send_timeout 3600;
        proxy_read_timeout 3600;
        send_timeout 3600;
        proxy_buffering off;
        proxy_request_buffering off;
        proxy_max_temp_file_size 10240;
        proxy_set_header Host $host;
        proxy_redirect off;
        # Following 2 options are needed when using web sockets
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_pass http://127.0.0.1:81;
    }   
}

Weitere virtual hosts

Die Konfigurierung des gateway hosts ist nun abgeschlossen. Nun können die hosts definiert werden, mit denen ein Besucher am Ende auch interagiert. Um die verschiedenen Möglichkeiten der Konfigurierung zu verstehen, schau dir folgende Beispiele an:

server {
    listen localhost:81;
    server_name example1.com;
    root /var/www/example1.com/
    
    location = / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }
}
server {
    listen localhost:81;
    server_name  example2.com; 
    root /var/www/example2.com/;

    location = / {
        # First attempt to serve request as file, then
        # as directory, then fall back to displaying a 404.
        try_files $uri $uri/ =404;
    }

    location ^~ /service1 {
        proxy_pass http://127.0.0.1:12345;
    }   
}

Mit dieser Konfigurierung gibt es 3 verschiedene Möglichkeiten auf deinen Webserver zuzugreifen.

  1. https://example1.com
    Damit wird dir die index.html aus dem Verzeichnis /var/www/example1.com/ angezeigt.
  2. https://example2.com
    Hier wird die index.html aus dem Verzeichnis /var/www/example2.com/ angezeigt.
  3. https://example2.com/service1
    Nginx wird als reverse proxy genutzt und du greifst auf einen Webdienst zu, der hinter dem lokalen Port 12345 läuft.

Du wirst auf meinem Blog demnächst verschiedene Beispiele an Anwendungen finden inkl. der Konfigurationen für nginx.

Du findest diesen Artikel und die gesamte Raspberry Pi Reihe auch auf gldhnchn.de