LAMP-Server unter Ubuntu 18.04 mit Let’s Encrypt SSL installieren und absichern

Hinweis: Das Tutorial wurde auf einem Hetzner CX11-Cloud Server erstellt. Ich bekomme von Hetzner keine Provision, kann deren Cloud Server Angebot aus einer Erfahrung aber wirklich empfehlen.

SSH und sudo

Erstellt man bei Hetzner einen Cloud-Server, dann erhält man eine E-Mail mit dem root-Kennwort. Beim ersten Login via SSH muss man das root-Kennwort ändern.

Der root-Login via SSH ist allerdings nicht empfehlenswert, weshalb im ersten Schritt der root-Login unterbunden wird und stattdessen sudo eingerichtet wird. Im folgenden verwende ich als Benutzername my_user, dieser ist natürlich entsprechend anzupassen.

Starten wir – zunächst den Benutzer my_user erstellen:

adduser my_user

Anschließend den Benutzer der sudo-Gruppe hinzufügen:

usermod -a -G sudo my_user

Nun wird die SSH-Config angepasst, um den Root-Login zu unterbinden:

nano /etc/ssh/sshd_config

Dort PermitRootLogin auf no setzen:

PermitRootLogin no

Anschließend SSH neustarten:

sudo service ssh restart

Nun sollte die bestehende SSH-Verbindung getrennt und als my_user neu aufgebaut werden.

Updates einspielen

Im nächsten Schritt werden die ausstehenden Updates installiert:

sudo apt-get update
sudo apt-get upgrade -y

Apache installieren

Nachdem das System nun aktuell sein sollte, wird der Apache Webserver installiert:

sudo apt-get install apache2 -y

Nach der Installation muss die gewünschte Domain eingerichtet werden, im folgenden Beispiel wird die Domain example.com genutzt:

sudo mkdir -p /var/www/example.com/html

Die Verzeichnisberechtigung muss dann entsprechend angepasst werden:

sudo chown -R $USER:$USER /var/www/example.com/html

Nun die entsprechende VHost-Konfiguration anlegen:

sudo nano /etc/apache2/sites-available/example.com.conf

Folgende Konfiguration muss nun in die Datei eingefügt werden:

<VirtualHost *:80>
    ServerAdmin admin@example.com
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example.com/html
    ErrorLog ${APACHE_LOG_DIR}/error.log
    CustomLog ${APACHE_LOG_DIR}/access.log combined

    <Directory /var/www/example.com/html>
        Options +FollowSymLinks -Indexes -Multiviews 
        Require all granted
    </Directory>
</VirtualHost>

Anschließend die Seite aktivieren und die Default-Seite deaktivieren:

sudo a2ensite example.com.conf
sudo a2dissite 000-default.conf

Da wir HTTP/2 nutzen möchten, müssen wir nun das entsprechende Modul aktivieren:

sudo a2enmod http2

Außerdem muss das entsprechende Protokoll konfiguriert werden:

sudo nano /etc/apache2/apache2.conf

Dort ganz ans Ende folgendes hinzufügen:

Protocols h2 h2c http/1.1

Da das prefork modul mit HTTP/2 nicht kompatibel ist, würde nun folgende Fehlermeldung in den Apache-Logs erscheinen.

[Wed Oct 03 07:05:08.757770 2018] [http2:warn] [pid 30565] AH10034: The mpm module (prefork.c) is not supported by mod_http2. The mpm determines how things are processed in your server. HTTP/2 has more demands in this regard and the currently selected mpm will just not do. This is an advisory warning. Your server will continue to work, but the HTTP/2 protocol will be inactive.

Um das zu vermeiden wechseln wir von mpm_prefork zu mpm_event:

sudo a2dismod mpm_prefork 
sudo a2enmod mpm_event

Anschließend starten wir den Apache neu:

sudo systemctl restart apache2

Nun sollte der Apache Webserver noch angepasst werden, so dass er nicht unnötig viele Informationen preisgibt, die bei Angriffen gegen den Webserver genutzt werden können. Dazu folgende Config-Datei erstellen:

sudo nano /etc/apache2/conf-available/zzz-custom.conf

Und diesen Inhalt einfügen:

ServerTokens Prod
ServerSignature Off
TraceEnable Off
Options all -Indexes
Header unset ETag
Header always unset X-Powered-By
FileETag None

Nun das Headers-Modul und die neue Konfiguration aktivieren:

sudo a2enmod headers
sudo a2enconf zzz-custom.conf

Anschließend den Apache Webserver erneut neustarten:

sudo systemctl restart apache2

PHP installieren

Damit HTTP/2 funktioniert, muss PHP in der FastCGI-Version (FPM) eingebunden werden. Das normale Apache-Modul funktioniert hier leider nicht. Um PHP zu installieren, wie folgt vorgehen:

sudo apt-get install php-fpm -y

sudo a2enmod proxy_fcgi setenvif
sudo a2enconf php7.2-fpm 

sudo service apache2 restart
sudo service php7.2-fpm restart

Die relevante php.ini liegt nun unter /etc/php/7.2/fpm/php.ini.

Weitere PHP-Module lassen sich nach Bedarf mit folgendem Befehl installieren, gd (als Beispiel) muss dabei gegen das gewünschte Modul ersetzt werden:

sudo apt-get install php-gd -y

Sofern Uploads benötigt werden, sollte man anschließend noch upload_max_filesize und post_max_size erhöhen, dazu die php.ini bearbeiten:

sudo nano /etc/php/7.2/fpm/php.ini

Und folgende Werte:

; Maximum allowed size for uploaded files.
; http://php.net/upload-max-filesize
upload_max_filesize = 2M

;[...]

; Maximum size of POST data that PHP will accept.
; Its value may be 0 to disable the limit. It is ignored if POST data reading
; is disabled through enable_post_data_reading.
; http://php.net/post-max-size
post_max_size = 8M

in mindestens 32 MB abändern:

; Maximum allowed size for uploaded files.
; http://php.net/upload-max-filesize
upload_max_filesize = 32M

;[...]

; Maximum size of POST data that PHP will accept.
; Its value may be 0 to disable the limit. It is ignored if POST data reading
; is disabled through enable_post_data_reading.
; http://php.net/post-max-size
post_max_size = 32M

PHP-Dateiberechtigungen

php-fpm nutzt standardmäßig den Benutzer www-data und die Gruppe www-data. Da wir beim Anlegen des VHosts vorhin die Dateien und Verzeichnisse dem User my_user zugewiesen haben, wird es zu Problemen kommen, sofern PHP Ordner oder Dateien erstellen oder ändern muss. Dieses Problem kann man beheben, indem man Benutzer und Gruppe in der Datei www.conf anpasset:

sudo nano /etc/php7.2/fpm/pool.d/www.conf

Alternativ kann man auch die Berechtigungen des VHosts ändern. Der empfohlene Weg hier hängt von der jeweiligen Situation ab. Betreibe ich den Webserver alleine? Nutzen unterschiedliche User den Webserver? Läuft auf dem Webserver nur eine Web-Applikation oder mehrere? Pauschal kann man hier keine Best Practice nennen, hier empfehle ich weiter zu recherchieren, um für die jeweilige Situation die beste Lösung anzuwenden.

HTTPS / Let’s Encrypt Zertifikat einrichten

Um HTTPS einzurichten, muss zunächst das für Let’s Encrypt benötigte Repository hinzugefügt werden:

sudo add-apt-repository ppa:certbot/certbot

Dann Certbots Apache-Paket installieren:

sudo apt-get install python-certbot-apache -y

Die gewünschte Domain haben wir ja weiter oben bereits konfiguriert, daher können wir direkt das Zertifikat abrufen:

sudo certbot --apache -d example.com -d www.example.com

Hier muss man zunächst seine E-Mailadresse angeben, anschließend kommt folgende Abfrage:

Please choose whether or not to redirect HTTP traffic to HTTPS, removing HTTP access.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1: No redirect - Make no further changes to the webserver configuration.
2: Redirect - Make all requests redirect to secure HTTPS access. Choose this for
new sites, or if you're confident your site works on HTTPS. You can undo this
change by editing your web server's configuration.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Select the appropriate number [1-2] then [enter] (press 'c' to cancel): 

Hier kann man mit 1 oder 2 auswählen, ob alle Zugriffe auf HTTPS umgeleitet werden sollen oder ob nicht.

Anschließend sollte man noch mit nachfolgendem Befehl testen, ob das automatische Erneuern der Zertifikate funktioniert:

sudo certbot renew --dry-run

MySQL installieren

Um die MySQL-Datenbank zu installieren, reicht folgender Befehl:

sudo apt-get install mysql-server php-mysql -y

Anschließend sollte der MySQL-Server abgesichert werden, dazu folgendne Befehl eingeben:

sudo mysql_secure_installation

Bei der Frage:

VALIDATE PASSWORD PLUGIN can be used to test passwords
and improve security. It checks the strength of password
and allows the users to set only those passwords which are
secure enough. Would you like to setup VALIDATE PASSWORD plugin?

Press y|Y for Yes, any other key for No:

Muss man für sich entscheiden, ob man sichere Passwörter wünscht oder ob nicht. Grundsätzlich spricht vieles für die sicheren Passwörter, wenn allerdings ein Script oder Paket einen User automatisch anlegen möchte (beispielsweise phpMyAdmin) und das generierte Passwort nicht den Richtlinien entspricht, dann werden Fehler auftreten.

Alle anderen Punkte sollten selbsterklärend sein und können bestätigt / akzeptiert werden.

Noch ein Hinweis, per Default wird der MySQL-root-User per Socket authentifiziert. So reicht ein:

sudo mysql

Um als root auf die MySQL-Datenbank zuzugreifen. Möchte man das ändern und sich lieber per Passwort authentifizieren, dann muss das entsprechend umgestellt werden. Dazu via mysql-cli folgende Befehle ausführen:

ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'my_password';
FLUSH PRIVILEGES;
exit

Die Empfehlung ist, für jede Webapplikation einen eigenen Benutzer und eine eigene Datenbank anzulegen. Das geschieht mit folgenden Befehlen:

sudo mysql

dort dann:

CREATE DATABASE my_webapp_db;
GRANT ALL PRIVILEGES on my_webapp_db.* to 'my_webapp_user'@'localhost' identified by 'my_password';
FLUSH PRIVILEGES;
EXIT

my_webapp_db, my_webapp_user und my_password müssen natürlich angepasst werden.

Anschließend erneut die Datenbank neu starten:

sudo systemctl restart mysql

Server weiter absichern

Der Server ist jetzt grundsätzlich einsatzbereit, aber noch nicht wirklich sicher. Die nachfolgenden Abschnitte zeigen Maßnahmen auf, die die Sicherheit weiter erhöhen, aber ggf. auch zu Problemen führen können.

Firewall ufw einrichten

Ubuntu bietet mit ufw eine einfache Möglichkeit, eine Firewall (im Hintergrund iptables) einzurichten. In unserem Fall benötigen wir Apache (Port 80 und 443) sowie SSH. Aus diesem Grund fügen wir die entsprechenden Profile hinzu und aktivieren anschließend die Firewall: 

sudo ufw allow in "Apache Full"
sudo ufw allow ssh
sudo ufw enable

Automatische Updates aktivieren

Um automatisch Sicherheitsupdates einzuspielen, folgenden Befehl ausführen:

sudo apt-get install unattended-upgrades

Mit Hilfe der Datei /etc/apt/apt.conf.d/50unattended-upgrades kann man konfigurieren, welche Udpates autoamtisch eingespielt werden sollen.

Fail2ban installieren und konfigurieren

Zunächst fail2ban installieren:

sudo apt-get install fail2ban

Anschließend eine Kopie der Konfig-Datei anlegen:

sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local

Nun die Kopie bearbeiten:

sudo nano /etc/fail2ban/jail.local

Der Block unter [sshd] sollte wie folgt aussehen (entsprechend anpassen):

[sshd]

enabled  = true
port     = ssh
filter   = sshd
logpath  = /var/log/auth.log
maxretry = 5
bantime = 600

Anschließend Fail2ban neu starten:

sudo systemctl restart fail2ban

Und den automatischen Start beim Boot aktivieren:

sudo systemctl enable fail2ban

mod_security

Um die Sicherheit weiter zu erhöhen, kann man mit mod_security eine Web Application Firewall (WAF) einrichten. Dadurch können einige Angriffe, wie SQL-Injections, Session Hijacking, Cross Site Scripting, etc. abgewehrt werden.

Aber Achtung: Auch eigene Maßnahmen, wie beispielsweise die Installation von WordPress Plugins, etc. können mod_security anspringen lassen. Wenn mod_security bei eigentlich gut gemeinten Aktionen fälschlicherweise einen Sicherheitsverstoß erkennt, dann wird die Administration deutlich verkompliziert. Aus diesem Grund sollte man mod_security mit Vorsicht genießen.

Überwiegen die Vorteile des Einsatzes von mod_security , muss zunächst das erforderliche Paket installiert und aktiviert werden:

sudo apt-get install libapache2-mod-security2 -y
sudo a2enmod security2

Anschließend folgenden Befehl ausführen:

sudo mv /etc/modsecurity/modsecurity.conf-recommended /etc/modsecurity/modsecurity.conf

Nun die Datei modsecurity.conf bearbeiten:

sudo nano /etc/modsecurity/modsecurity.conf

Und darin folgende Zeile:

SecRuleEngine DetectionOnly

ändern in:

SecRuleEngine On

Anschließend einen Regelsatz von Github laden:

sudo git clone https://github.com/SpiderLabs/owasp-modsecurity-crs.git /usr/share/modsecurity-crs
cd /usr/share/modsecurity-crs
sudo mv crs-setup.conf.example crs-setup.conf

Nun die Konfiguration des Apache Moduls um die soeben geladenen Regeln erweitern, dazu:

sudo nano /etc/apache2/mods-enabled/security2.conf

Und den vorhanden Inhalt durch den erweiterten Inhalt ersetzen:

<IfModule security2_module>
        # Default Debian dir for modsecurity's persistent data
        SecDataDir /var/cache/modsecurity

        # Include all the *.conf files in /etc/modsecurity.
        # Keeping your local configuration in that directory
        # will allow for an easy upgrade of THIS file and
        # make your life easier
        IncludeOptional /etc/modsecurity/*.conf

        # Include OWASP ModSecurity CRS rules if installed
        IncludeOptional /usr/share/modsecurity-crs/owasp-crs.load

        IncludeOptional /usr/share/modsecurity-crs/*.conf
        IncludeOptional /usr/share/modsecurity-crs/rules/*.conf
</IfModule>

Nun erneut den Apache neustarten:

sudo systemctl restart apache2

mod_evasive

Mit mod_evasive kann man die Sicherheit des Webservers weiter erhöhen, da es vor DoS (Denial of Service), DDos (Distributed Denial of Service) und Brute Force Angriffen schützen kann.

Achtung: Auch dieses Modul kann zu Problemen beim Betrieb und der Administration von Web-Anwendungen führen. Daher sollte man Nutzen / Risiko genau abwägen.

Wenn das Modul eingerichtet werden soll, muss es zunächst installiert werden:

sudo apt-get install libapache2-mod-evasive -y

Bei der Installation von Postfix kann man entweder no configuration oder internet site auswählen. 

Anschließend muss mod_evasive konfiguriert werden, dazu:

sudo nano /etc/apache2/mods-enabled/evasive.conf

Und den vorhandenen Inhalt durch diesen ersetzen:

<IfModule mod_evasive20.c>
    DOSPageCount        5
    DOSSiteCount        50
    DOSPageInterval     1
    DOSSiteInterval     1
    DOSBlockingPeriod   600
    DOSLogDir           "/var/log/mod_evasive"
</IfModule>

Anschließend das Log-Verzeichnis erstellen:

sudo mkdir /var/log/mod_evasive
sudo chown -R www-data: /var/log/mod_evasive

Nun noch Apache neustarten:

sudo systemctl restart apache2

PHP absichern

Wenn die gewünschte Web-Anwendungen den Upload von Dateien nicht vorsieht, kann der Upload komplett deaktiviert werden – um so auch das Hochladen potentiell gefährlicher Dateien zu unterbinden. Dazu 

Alternativ kann man, sofern Uploads nicht benötigt werden, diese auch komplett deaktivieren – was die Sicherheit erhöht. Dazu in der php.ini:

sudo nano /etc/php/7.2/fpm/php.ini

file_uploads auf Off setzen:

file_uploads=Off

Anschließend den Apache Webserver neu starten:

sudo systemctl restart apache2

Um PHP weiter abzusichern, kann man gefährliche Funktionen deaktivieren.

Achtung: Viele Anwendungen sind auf einige dieser Funktionen angewiesen, weshalb man genau prüfen sollte, was man hier deaktiviert. Um die Funktionen zu deaktivieren, die php.ini bearbeiten:

sudo nano /etc/php/7.2/fpm/php.ini

dort dann disable_functions auf folgenden Wert setzen (ggf. erweitern / reduzieren):

disable_functions = show_source,system,shell_exec,passthru,exec,phpinfo,popen,proc_open,allow_url_fopen,curl_exec,curl_multi_exec

Außerdem sollte man, sofern nicht benötigt, die folgenden Werte setzen, um Remote PHP Codeausführung zu deaktivieren:

allow_url_fopen=Off
allow_url_include=Off

Noch weiter erhöhen kann man die Sicherheit von PHP, indem open_basedir gesetzt wird, um den Pfad festzulegen, in dem PHP auf Dateien zugreifen kann:

open_basedir="/var/www/example.com/html"

Hinweis: Sobald open_basedir gesetzt ist, kann es zu Problemen beim Betrieb von Webanwendungen kommen. WordPress kann dann beispielsweise nicht mehr in das /tmp-Verzeichnis schreiben und erfordert das manuelle Setzen eines neuen tmp-Verzeichnisses. Die Einstellung sollte man daher mit Bedacht setzen und das Resultat ausgiebig prüfen.

MySQL absichern

Da das LOAD DATA-Kommando ein potentielles Sicherheitsrisiko bergen kann (siehe hier), sollte man es deaktivieren, sofern man es nicht benötigt:

sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf

dann dort unter dem [mysqld] Block folgendes hinzufügen:

local-infile=0

Fertig

Das Server-Setup ist nun fertig. Sollten sich im Tutorial Fehler eingeschlichen haben oder es bessere Empfehlungen / Tipps geben, würde ich mich über einen entsprechenden Kommentar freuen.

Schreibe einen Kommentar