Ubuntu-Server mit SSL Nginx PHP MySQL einrichten

Dies ist eine kurze Anleitung zum Einrichten eines Ubuntu-Servers als Webserver mit MySQL und PHP, der Webseiten über gesicherte HTTPS-Verbindungen ausliefert. Die Konfigurtion eignet sich zum Beispiel zum Betreiben einer sicheren Wordpress-Installation oder anderer PHP-Anwendungen. Durch die HTTPS-Verbindung mit SSL/TLS werden Passwörter und andere Daten nur verschlüsselt übertragen.

Voraussetzung: ein Server mit Ubuntu Linux und mit eigener Domain, die auf die IP-Adresse des Servers eingerichtet ist, um darüber später die Webseite aufzurufen. Außerdem, eine sichere Einrichtung des Ubuntu-Servers gegen "Hacking"-Angriffe.

Als Webserver wird Nginx installiert, und über PHP-FPM mit der PHP-Anwendung verbunden. Das hat den Vorteil, dass der Webserver kein eigenes PHP-Modul laden muss, sondern unabhängig von der Geschwindigkeit der PHP-Anwendungen erreichbar bleibt.

MySQL installieren

Für viele Web-Anwendungen, zum Beispiel um ein Wordpress-Blog selbst zu betreiben, wird eine MySQL-Datenbank benötigt. Die Server-installation von MySQL ist relativ einfach. Während der Installation von MySQL muss ein Passwort für den Datenbank-"root"-Nutzer festgelegt werden.

root@server:~# apt-get install mysql-server

Nach erfolgreicher MySQL-Installation sollten noch zwei Skripte ausgeführt werden, die einige wichtige Sicherheitseinstellungen am MySQL-Server vornehmen.

root@server:~# mysql_install_db
root@server:~# mysql_secure_installation

PHP installieren

Nun wird PHP installiert, einschließlich PHP-FPM (FastCGI Process Manager) und der PHP Grafik-Bibliothek.

root@server:~# apt-get install php5-fpm php5-mysql php5-gd

Außerdem sollten folgende Einstellungen in der PHP-Konfiguration angepasst werden.

root@server:~# vim /etc/php5/fpm/php.ini

cgi.fix_pathinfo = 0
expose_php = Off
post_max_size = 8M
upload_max_filesize = 8M

Die ersten beiden Einstellungen erhöhen die Sicherheit, die letzten beiden setzen die maximale Größe für hochgeladene Dateien, zum Beispiel Fotos. Abschließend den Process Manager neu starten, um die geänderten Einstellungen zu laden.

root@server:~# service php5-fpm restart

Nginx installieren

Als letztes wird der Nginx-Webserver installiert und eingerichtet (ein eventuell vorhandener Apache2-Server wird entfernt). In der Konfigurationsdatei wird ebenfalls die maximale Größe für hochgeladene Dateien angepasst.

root@server:~# apt-get remove apache2
root@server:~# apt-get install nginx
root@server:~# vim /etc/nginx/nginx.conf

client_max_body_size 8M

Damit ist Nginx bereits installiert und eine HTML-Testseite sollte über die IP-Adresse des Servers erreichbar sein. Um den Webserver über die vorhandene Domain erreichbar zu machen, wird eine Nginx-Konfiguration angelegt. Dort wird auch PHP aktiviert, oder es kann eine vorhandele Ruby- oder Python-Anwendung verbunden werden.

Statt "example.com" sollte hier natürlich jeweils die eigene Domain verwendet werden!

root@server:~# vim /etc/nginx/sites-available/example.com

server {
    listen *:80;
    listen [::]:80;

    server_name example.com *.example.com;
    root /var/www/html;
    index index.php index.html;
    client_max_body_size 8m;

    location ~ /\. { deny all; }  # versteckte Dateien nie ausliefern.

    location / { try_files $uri $uri/ =404; }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        if (!-f $document_root$fastcgi_script_name) { return 404; }

        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/var/run/php5-fpm.sock;

        client_max_body_size 8m;
    }
}

root@server:~# ln -s /etc/nginx/sites-available/example.com /etc/nginx/sites-enabled/
root@server:~# service nginx restart

Alle Dateien mit der Endung ".php" werden als PHP-Skript ausgeführt. Alle Dateipfade, die mit einem Punkt beginnen (versteckte Dateien in Linux), sind gesperrt für den Webbrowser.

Nun ein Test, ob PHP ausgeführt wird. Dazu wird im eingestellten root-Verzeichnis eine PHP-Datei angeĺegt, dem Benutzer "www-data" (der Standard-Name des Webservers) die nötigen Zugriffsrechte gegeben, und dann die Domain in Webbrowser aufgerufen.

root@server:~# echo '<?php echo("Hello, world!") ?>' >/var/www/html/index.php
root@server:~# chown www-data:benutzer /var/www/html/index.php

Wenn alles gutgegangen ist, sollte im Webbroser nur "Hello, world!" angezeigt werden. Wenn nichts oder ein Fehler angezeigt wird, dann findet man in den folgenden Logdateien Hinweise zu möglichen Ursachen.

root@server:~# less -f /var/log/nginx/error.log
root@server:~# less -f /var/log/nginx/access.log
root@server:~# less -f /var/log/php5-fpm.log
root@server:~# less -f /var/log/syslog.log

PHP-Seiten sicher über SSL/TLS ausliefern

Seit Ende 2015 gibt es mit LetsEncrypt einen gemeinnützigen Anbieter kostenloser SSL-Zertifikate, die einfach zu installieren sind. Die Organisation wird von der Linux Foundation, der Mozilla Foundation, dem EFF, der Internet Society, und einer Vielzahl bekannter Unternehmen unterstützt.

Um ein SSL-Zertifikat zu installieren, muss zunächst ein Programm von LetsEncrypt installiert werden, mit dem dann die Zertifikate erstellt und abgerufen werden.

Bisher bietet nur Debian Linux eine Installation über die Paketverwaltung an. Ubuntu sollte bald folgen. Bis dahin muss noch manuell aus dem Github-Konto installiert werden.

root@server:~# cd /opt
root@server:~# git clone https://github.com/letsencrypt/letsencrypt
root@server:~# cd letsencrypt
root@server:~# ./letsencrypt-auto --help
root@server:~# cd ~

Wenn letsencrypt-auto zum ersten Mal aufgerufen wird, werden alle benötigten Dateien zunächst in einer Python "virtuellen Umgebung" eingerichtet. Danach können über den Befehl /opt/letsencrypt/letsencrypt-auto neue SSL-Zertifikate beantragt und installiert werden.

Der bereits installierte Nginx-Webserver muss kurz angehalten werden.

root@server:~# service nginx stop
root@server:~# /opt/letsencrypt/letsencrypt-auto certonly --standalone --standalone-supported-challenges http-01 --agree-tos --text -d example.com -d www.example.com -d mail.example.com

Zur Erklärung: Zertifikate können wahlweise über die Methode --standalone oder --webroot beantragt werden. Die Methode --standalone ist unkomplizierter in der Einrichtung, hat aber den Nachteil, dass ein vorhandener Webserver kurz angehalten werden muss.

Mit der Option -d werden dem Zertifikat weitere Domains hinzugefügt. Hierbei müssen Subdomains explizit angegeben werden, da LetsEncrypt bisher keine Wildcard-Domains unterstützt.

Ein Problem bei LetsEncrypt-Zertifikaten ist die Laufzeit. Nach spätestens drei Monaten muss ein Zertifikat erneuert werden. Das kann jedoch einfach automatisiert über ein Bash-Skript erfolgen.

root@server:~# vim /opt/certs_renew.sh

#!/bin/bash
service nginx stop
/opt/letsencrypt/letsencrypt-auto --renew certonly --standalone --agree-tos --text --standalone-supported-challenges tls-sni-01 -d example.com -d www.example.com -d mail.example.com 2>&1 | mail -s "letsencrypt output" ich@example.com
service nginx start

Nun muss das Skript nur noch regelmäßig durch einen Cronjob des "root"-Nutzers ausgeführt werden, um das Zertifikat zu erneuern. Das sollte ruhig einmal die Woche passieren, denn wirklich erneuert wird das Zertifikat nur, wenn weniger als 2 Monate bis zum Laufzeitende verbleiben.

root@server:~# crontab -e

0 3 * * 3 /opt/certs_renew.sh

Dadurch wird das Zertifikat jede Woche um drei Uhr nachts überprüft und wenn nötig erneuert.

Nginx einrichten um die PHP-Webseite über HTTPS zugänglich machen

Abschließend muss das SSL-Zertifikat noch mit dem Nginx-Webserver verbunden werden. Das erfordert nur einige wenige Änderungen in der Nginx-Konfiguration der Domain.

In den ersten zwei Zeilen wird der Port ":80" durch ":443 ssl" ersetzt. Port 80 ist der Standard-Port für HTTP Web-Verbindungen, und Post 443 ist der Standard-Port für verschlüsselte HTTPS Web-Verbindungen.

Ab "ssl on;" in Zeile 11 wird die TLS-Konfiguration eingefügt. Hier muss "example.com" natürlich wieder durch die eigene Domain ersetzt werden.

root@server:~# vim /etc/nginx/sites-available/example.com

server {
    listen *:443 ssl;
    listen [::]:443 ssl;

    server_name example.com *.example.com;
    root /var/www/html;
    index index.php index.html;

    client_max_body_size 8m;  # default: 1m

    ssl on;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_session_cache shared:SSL:20m;
    ssl_session_timeout 10m;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_ciphers 'ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5';
    ssl_prefer_server_ciphers on;

    add_header Strict-Transport-Security "max-age=31536000; includeSubdomains";
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;

    location ~ /\. { deny all; }

    location / { try_files $uri $uri/ =404; }

    location ~ \.php$ {
        fastcgi_split_path_info ^(.+?\.php)(/.*)$;
        if (!-f $document_root$fastcgi_script_name) { return 404; }

        include fastcgi_params;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_pass unix:/var/run/php5-fpm.sock;

        client_max_body_size 8m;
    }
}

root@server:~# service nginx restart

Nun sollte die Test-PHP-Datei nur noch über eine HTTPS-Verbindung erreichbar sein, und damit immer verschlüsselt übertragen werden. Im Webbrowser wird die Domain nun mit https://example.com geöffnet.