Piwik – Basisinstallation

Zur gezielten Optimierung von Websites und insbesondere Webshops sind möglichst detaillierte Statistiken über die Besucher und Zugriffe erforderlich. Ein zur Erfassung und Auswertung von Webzugriffen häufig genutztes Analysewerkzeug ist das quelloffene Programm Piwik.

Piwik bietet im Vergleich zum Marktführer in diesem Bereich Google Analytics die Möglichkeit, die erfassten Daten auf dem eigenen Server zu speichern. Hierdurch kann der Betreiber die Einhaltung datenschutzrechtlicher Bestimmungen sicherstellen.

Wir befassen uns im nachfolgenden Beitrag mit den grundlegenden Schritten bei der Installation und Konfiguration eines Piwik-Servers für einen möglichst sicheren Betrieb. In einem Folgeartikel werden wir dann die Verknüpfung mit einer zu analysierenden Website und weitere Konfigurationsmöglichkeiten behandeln.

Voraussetzungen

Piwik ist eine webbasierte PHP-Anwendung, die Nutzer- und weitere Daten in einer MySQL– oder MariaDB-Datenbank ablegt. Grundlage für einen Piwik-Server ist daher häufig das Softwarepaket LAMP mit Linux, Apache, MySQL und PHP, das beispielsweise in dem Beitrag zur LAMP-Server-Installation beschrieben wird.

Unser Testsystem läuft unter Ubuntu Server 14.04 in der Domain test.local. Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

Basissystem installieren

Installation vorbereiten

Die wesentlichen serverseitigen Voraussetzungen für den Betrieb von piwik werden im Kapitel Piwik Requirements der ausführlichen Originaldokumentation aufgeführt. Neben dem vorhandenen LAMP-Server benötigen wir vier zusätzliche PHP-Module, die wir mit apt install installieren:

apt install php5-cli php5-curl php5-gd php5-geoip

Als weitere Vorbereitung legen wir eine eigene MySQL-Datenbank piwik und einen Datenbankbenutzer piwik mit passenden Rechten an:

mysql -p -u root
mysql> CREATE DATABASE piwik;
mysql> GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES ON piwik.* TO 'piwik'@'localhost' IDENTIFIED BY '***';

Piwik installieren

Nach den Vorbereitungsarbeiten laden wir den gepackten Quelltext piwik.zip der aktuellen Piwik-Version, zum Erscheinungsdatum dieses Artikels 2.16.1, vom Download-Server https://builds.piwik.org und entpacken ihn an geeigneter Stelle, in unserem Beispiel im Verzeichnis /usr/share/piwik. Danach löschen wir die überflüssige Datei How to install Piwik.html und setzen die Zugriffsrechte für die Ordner und Dateien. Hierbei erlauben wir sicherheitshalber nur dem Eigentümer root Vollzugriff auf das Piwik-Verzeichnis. Die Apache-Gruppe www-data erhält ausschließlich lesenden Zugriff. Die zugehörigen Anweisungen lauten:

mkdir -p /root/sources  
wget -P /root/sources https://builds.piwik.org/piwik.zip 
apt install unzip
unzip -o -u /root/sources/piwik.zip -d /usr/share
rm /usr/share/"How to install Piwik.html"
chown -R root:www-data /usr/share/piwik
find /usr/share/piwik \( -type d -exec chmod 0750 {} + \) -o \( -type f -exec chmod 0640 {} + \)

Die Zugriffsrechte und andere Sicherheitseinstellungen müssen später noch „aufgeweicht“ werden. Auf die Details werden wir an entsprechender Stelle eingehen.

Apache einrichten

Für unseren Piwik-Server richten wir eine eigene, ausschließlich per HTTPS erreichbare Subdomain piwik.test.local ein, die durch eine HTTP-Basisauthentifizierung geschützt ist. Weiterhin nutzen wir die Sicherheitsheader aus Webserver Apache absichern und setzen die in PHP absichern beschriebenen Sicherungsmaßnahmen um. Die Virtual-Host-Konfigurationsdatei für Apache erstellen wir in Anlehnung an die Konfiguration für PhpSecInfo im letztgenannten Beitrag. Zusätzliche Maßnahmen wie die Änderung des HTTPS-Port oder die Nutzung eines zufallsgenerierten Namens für die Subdomain sind Gegenstand eines Folgebeitrags.

Die Modifikationen der zentralen PHP-Konfigurationsdatei /etc/php5/apache2/php.ini sind hier der Vollständigkeit halber noch einmal aufgeführt:

...
upload_tmp_dir = /xxx/
open_basedir = /xxx/
file_uploads = Off
allow_url_fopen = Off
disable_functions = ${disable_functions}posix_setpgid,exec,ftp_login,mysql_pconnect,apache_setenv,popen,posix_getpwuid,posix_setsid,passthru,escapeshellcmd,ini_alter,ftp_raw,ftp_nb_fput,ini_restore,shell_exec,ftp_get,proc_get_status,highlight_file,proc_close,proc_terminate,syslog,ftp_connect,posix_uname,ini_get_all,proc_open,posix_kill,escapeshellarg,ftp_rawlist,posix_setuid,openlog,php_uname,system,ftp_exec,posix_mkfifo,proc_nice,ftp_put

Die komplette Apache-Konfigurationsdatei /etc/apache2/sites-available/piwik.conf lautet:

<VirtualHost *:443>
  CustomLog ${APACHE_LOG_DIR}/access-piwik.log vhost_combined
  DocumentRoot /usr/share/piwik
  ErrorLog ${APACHE_LOG_DIR}/error-piwik.log
  Header always set Content-Security-Policy "default-src 'self'; img-src 'self' data:; script-src 'unsafe-eval' 'unsafe-inline' 'self'; style-src 'unsafe-inline' 'self'"
  Header always set Strict-Transport-Security "max-age=31536000"
  Header always set X-Content-Type-Options "nosniff"
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set X-Xss-Protection "1; mode=block"
  ServerAdmin webmaster@test.local
  ServerName piwik.test.local
  SSLCertificateFile /etc/ssl/certs/piwik.test.local.pem
  SSLCertificateKeyFile /etc/ssl/private/piwik.test.local.key
  SSLEngine on
  <Directory /usr/share/piwik>
    AuthBasicProvider file
    AuthName "piwik"
    AuthType Basic
    AuthUserFile /etc/apache2/.htpasswd_piwik
    php_admin_value open_basedir /usr/share/piwik/
    Require valid-user
  <Directory>
<VirtualHost>

Die Einträge für den Content-Security-Policy-Header wurden mit der Trial-and-Error-Methode mit dem Firefox-Add-on Firebug bestimmt.

Nach der Aktivierung der Piwik-Konfiguration und dem Neustart von Apache mit

a2ensite piwik.conf
service apache2 restart

und eventuell erforderlichen Anpassungen an dem / den DNS-Server/n können wir Piwik konfigurieren.

Piwik konfigurieren

Basiskonfiguration

Die grundlegenden Einstellungen von Piwik werden beim erstmaligen Aufruf im Browser mit

https://piwik.test.local/

in mehreren aufeinanderfolgenden Schritten festgelegt.

Den ersten Konfigurationsversuch quittiert die Piwik-Benutzeroberfläche mit der Fehlermeldung

An error occurred
The directory "/usr/share/piwik/tmp/cache/tracker/" does not exist and could not be created.

Ursache sind fehlende Schreibrechte der Gruppe www-data für den Ordner für temporäre Dateien /usr/share/piwik/tmp/, die wir folgendermaßen aktivieren:

chmod 0770 /usr/share/piwik/tmp

Beim erneuten Aufruf von https://piwik.test.local/ meldet Piwik beim zweiten Schritt, der Systemprüfung, dass ein Schreibzugriff auf das Verzeichnis /usr/share/piwik/config, in dem die Konfigurationsdatei config.ini.php abgelegt wird, nicht möglich ist. Zusätzlich werden noch zwei Warnungen ausgegeben:

Tracker-Status 500 GET-Request zu piwik.php fehlgeschlagen.
shell_exec Sie müssen diese eingebaute Funktion aktivieren.

Um den Schreibzugriff zu ermöglichen, ändern wir die Rechte analog zum Verzeichnis tmp:

chmod 0770 /usr/share/piwik/config

Wie ein Blick in die Apache-Logdatei /var/log/apache2/error-piwik.log zeigt, ist der Grund für die Warnmeldung zum Tracker-Status die noch nicht existierende Konfigurationsdatei:

... PHP Fatal error:  Uncaught exception 'Exception' with message 'The configuration file {/usr/share/piwik/config/config.ini.php} has not been found or could not be read.' ...

Da config.ini.php erst nach dem kompletten Durchlauf der Konfigurationsschritte angelegt wird, können wir diese, allerdings unkritische Warnmeldung nicht verhindern.

Die PHP-Funktion shell_exec zu aktivieren, deren Nutzung wir zuvor mit der disable_functions-Anweisung in php.ini unterbunden haben, ist aus Sicherheitsgründen nicht ratsam. Während der Tests für diesen Beitrag waren trotz deaktivierter shell_exec-Funktion keine Fehler oder sonstigen Auffälligkeiten bei Piwik festzustellen.

Beim dritten Anlauf kann die Basiskonfiguration vollständig durchgeführt werden. Auf der Seite Datenbank einrichten geben wir den Datenbanknamen piwik sowie den zugehörigen Benutzer piwik nebst Passwort ein. Für den Hauptadministrator super setzen wir ebenfalls ein Passwort und hinterlegen eine E-Mail-Adresse, z. B. piwik@test.local. Die Felder auf der Seite Website hinzufügen füllen wir mit den Daten der zu analysierenden Website, hier https://www.test.local:

Name der Website test
URL der Website https://www.test.local
Zeitzone der Website Berlin
Ecommerce Dies ist keine Ecommerce Seite

Für alle anderen Einstellungen können wir die Default-Werte übernehmen. Nachdem die Einrichtungsschritte erfolgreich abgearbeitet sind, erscheint der Piwik-Anmeldebildschirm für den Benutzer super.

Zum Abschluss schützen wir noch die Piwik-Konfiguration vor Überschreiben:

chmod 0750 /usr/share/piwik/config
chown root:www-data /usr/share/piwik/config/config.ini.php
chmod 0640 /usr/share/piwik/config/config.ini.php

Piwik anpassen

Die bereits bei der Erstinstallation durchlaufene Systemprüfung ist ein guter Ausgangspunkt für die weiteren Anpassungsarbeiten. Ein Aufruf mit

https://piwik.test.local/index.php?module=Installation&action=systemCheckPage

liefert neben der schon bekannten Warnmeldung zu shell_exec zwei Kommentare zum MySQL-Befehl LOAD DATA INFILE und zur Standorterkennung:

... Die Benutzung von LOAD DATA INFILE erhöht die Geschwindigkeit des Piwik Archivierungsprozesses erheblich. ...
... Standorterkennung funktioniert, aber Sie nutzen keinen der empfohlenen Anbieter. ...

LOAD DATA INFILE ermöglichen

Mit der LOAD DATA INFILE-Funktion werden temporäre CSV-Dateien aus dem Verzeichnis /usr/share/piwik/tmp/assets in die Datenbank eingelesen. Damit die Funktion genutzt werden kann, erhält der Datenbankbenutzer piwik mit der GRANT FILE-Anweisung Lese- und Schreibrechte für alle Dateien im Zugriffsbereich des MySQL-Servers:

mysql -p -u root
mysql> GRANT FILE ON *.* TO 'piwik'@'localhost';

Die temporären CSV-Dateien legt Piwik bereits mit Vollzugriff für alle an, also auch für den Benutzer mysql, mit dessen Rechten der MySQL-Server läuft. Darüber hinaus muss für die übergeordneten Ordner bis hin zum Installationsverzeichnis das Ausführen-Recht gesetzt sein, das einen Verzeichniswechsel ermöglicht. Da wir keine zusätzlichen separaten Rechte für den Nutzer mysql einrichten können, erlauben wir allen Benutzern das Ausführen auf Verzeichnisebene:

chmod 0751 /usr/share/piwik
chmod 0771 /usr/share/piwik/tmp
chmod 0751 /usr/share/piwik/tmp/assets

Falls wie auf unserem Testsystem die Sicherheitssoftware AppArmor läuft, kann der Dateizugriff für MySQL zusätzlich eingeschränkt sein. Zeigt die Statusabfrage

aa-status

den mysqld-Prozess in der Liste der überwachten Anwendungen unter … processes are in enforce mode an, müssen die hinterlegten AppArmor-Regeln geändert werden. Hierzu ergänzen wir die Konfigurationsdatei /etc/apparmor.d/usr.sbin.mysqld um eine Zeile, die mysqld den Lesezugriff auf die Dateien im Unterverzeichnis assets erlaubt:

...
/usr/sbin/mysqld {
  ...
  /usr/share/piwik/tmp/assets/* r,
  ...

Zur Übernahme der Änderung starten wir AppArmor neu:

service apparmor restart

In How do I get LOAD DATA INFILE to work on my server? wird vorgeschlagen, auch die Option local-infile in der MySQL-Konfigurationsdatei /etc/mysql/my.cnf zu setzen. Dies ist in unserem Fall nicht erforderlich, da Piwik den damit möglichen alternativen Aufruf LOAD DATA LOCAL INFILE bei funktionierendem Dateizugriff mit LOAD DATA INFILE nicht verwendet.

Standorterkennung verbessern

Zur Standorterkennung empfiehlt Piwik die Nutzung der GeoIP-Datenbank mit Hilfe des PECL-Moduls geoip. Die erforderliche PHP-Erweiterung php5-geoip haben wir bereits im Rahmen der Installationsvorbereitungen installiert.

Das geoip-Modul liest die Zuordnung von IP-Adressen zu geografischen Orten aus einer Datei mit dem Namen GeoIPCity.dat. Der Ablageort dieser Datei, hier beispielsweise das Verzeichnis /usr/share/piwik/misc, wird im Parameter geoip.custom_directory in der PHP-Konfigurationsdatei /etc/php5/apache2/php.ini übergeben:

geoip.custom_directory=/usr/share/piwik/misc

Nach dem Neustart von Apache mit

service apache2 restart

steht die geänderte Konfiguration zur Verfügung.

Piwik unterstützt unter anderem die kostenfreie GeoIP-Datenbank GeoLite City von MaxMind, die wir wie folgt herunterladen, entpacken und mit passenden Zugriffsrechten als Datei /usr/share/piwik/misc/GeoIPCity.dat speichern können:

wget -P /root/sources https://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
gunzip /root/sources/GeoLiteCity.dat.gz
mv /root/sources/GeoLiteCity.dat /usr/share/piwik/misc/GeoIPCity.dat
chown root:www-data /usr/share/piwik/misc/GeoIPCity.dat
chmod 0640 /usr/share/piwik/misc/GeoIPCity.dat

Auf der Piwik-Konfigurationsseite für die Standorterkennung

https://piwik.test.local/index.php?module=UserCountry&action=adminIndex

stellen wir schließlich noch GeoIP (PECL) ein. Die auf derselben Seite angebotene automatische Aktualisierung für GeoIP-Datenbanken nutzen wir nicht. Stattdessen kann man bei Bedarf die Befehle zur Installation von GeoIPCity.dat in einem Shellskript zusammenfassen und in regelmäßigen zeitlichen Abständen per Cron-Job aufrufen.

Weitere Details zur Standorterkennung mit GeoIP-Datenbanken können dem Abschnitt Geo Locate your visitors und den FAQs How do I install the GeoIP Geo location PECL extension? und How do I get the GeoIP databases to improve accuracy of Country detection, and detect visitors’ Cities and Regions? der Piwik-Dokumentation entnommen werden.

Piwik absichern

Die von den Entwicklern als sinnvoll erachteten Sicherheitsmaßnahmen sind in der Anleitung How to configure Piwik for security zusammengefasst, auf die wir nachfolgend auszugsweise eingehen.

Die Empfehlungen, Piwik in einer separaten MySQL-Datenbank mit einem eigenen Datenbankbenutzer und Passwort zu installieren sowie auschließlich Verbindungen per HTTPS zuzulassen, haben wir umgesetzt. Die Einrichtung einer automatischen Umleitung von HTTP auf HTTPS in der Piwik-Konfiguration ist nicht erforderlich, da die Website nicht per HTTP erreichbar ist. Der Zugriff auf Piwik ist durch eine HTTP-Basisauthentifizierung geschützt. Mit den Ausnahmeregelungen für die Dateien piwik.php und piwik.js und den URL index.php?module=CoreAdminHome&action=optOut werden wir uns in einem Folgeartikel befassen.

Die Sicherung der Konfigurationsdatei config.ini.php und der Piwik-Datenbank kann im Rahmen eines Systembackups mit einem der gängigen Datensicherungsprogramme/-verfahren erfolgen. Einen Überblick gibt z. B. der Wiki-Beitrag zur Ubuntu-Datensicherung.

Um die Datenbank in einem konsistenten Zustand zu speichern, erzeugen wir vor dem eigentlichen Systembackup mit dem Programm mysqldump eine lokale Backupdatei piwik.sql. Diese Datei sollte außerhalb des durch Apache lesbaren Piwik-Ordners abgelegt werden. In unserem Beispiel nutzen wir das Homeverzeichnis des Benutzers theo:

mysqldump --opt --password --user=piwik piwik > /home/theo/piwik.sql

Es bietet sich an, die lokale Datenbanksicherung mit einem Cron-Job zu automatisieren und beispielsweise täglich auszuführen.

Zur Absicherung der PHP-Umgebung empfehlen die Entwickler das SecurityInfo-Plugin, das wie die anderen Piwik-Plugins im Unterordner plugins des Installationsverzeichnisses abgelegt wird. Damit neue Plugins über die Piwik-Benutzeroberfläche installiert werden können, müssen dieser Ordner und zusätzlich die Konfigurationsdatei config.ini.php zum Schreiben für www-data freigegeben werden:

chmod 0770 /usr/share/piwik/plugins
chown www-data:www-data /usr/share/piwik/config/config.ini.php

Das Plugin können wir dann über den Marketplace

https://piwik.test.local/index.php?module=CorePluginsAdmin&action=marketplace

installieren und aktivieren.

Nach der Installation stellt man die Rechte mit

chmod 0750 /usr/share/piwik/plugins
chown -R root:www-data /usr/share/piwik/plugins/SecurityInfo
find /usr/share/piwik/plugins/SecurityInfo \( -type d -exec chmod 0750 {} + \) -o \( -type f -exec chmod 0640 {} + \)
chown root:www-data /usr/share/piwik/config/config.ini.php

wieder auf sicherere Werte ein.

Ein Aufruf von SecurityInfo mit

https://piwik.test.local/index.php?module=SecurityInfo&action=index

liefert folgende Meldungen:

... WARNING: /usr/share/piwik/plugins/SecurityInfo/PhpSecInfo/Test/CGI/force_redirect.php(89): Warning - ini_get_all() has been disabled for security reasons ...
.. PHP You are running PHP 5.5.9-1ubuntu4.17. The latest version of PHP is 5.6.24. ...
... upload_tmp_dir unable to retrieve file permissions on upload_tmp_dir ...

Die erste Warnung scheint ein Fehler im SecurityInfo-Plugin zu sein. Die PHP-Version 5.5.9 ist Bestandteil des LAMP-Servers unter Ubuntu 14.04 und ließe sich durch eine manuelle Installation auf einen aktuelleren Stand bringen. Auf upload_tmp_dir kann Piwik nicht zugreifen, da wir gemäß dem Beitrag PHP absichern für diesen Ordner einen nicht-existierenden Namen /xxx/ gewählt haben.

Ein vergleichender Aufruf des SecurityInfo zugrunde liegenden Analyseprogramms PhpSecInfo meldet auf dem Testsystem einen 100%-Erfolg, wobei auch die genutzte PHP-Version nicht als kritisch eingestuft wird. Wir können daher an dieser Stelle von einem möglichst sicher eingerichteten Piwik-Server ausgehen.

Piwik-Update

Sobald ein Programmupdate bereitsteht, erscheint auf der Benutzeroberfläche ein entsprechender Vermerk mit einem Link zu Piwiks Ein-Klick-Aktualisierung. Für diese Aktualisierung benötigt der Apache-Prozess Schreibrechte für das Piwik-Verzeichnis, die wir z. B. wie folgt setzen können:

chown -R www-data:www-data /usr/share/piwik

Nach dem Update der Programmdateien und der Aktualisierung der Datenbank über die Piwik-Oberfläche richten wir die ursprünglichen Berechtigungen wieder ein:

chown -R root:www-data /usr/share/piwik
find /usr/share/piwik \( -type d -exec chmod 0750 {} + \) -o \( -type f -exec chmod 0640 {} + \)
chown -R www-data:www-data /usr/share/piwik/tmp/*
find /usr/share/piwik/tmp/* -type f -exec chmod 0644 {} +
chmod 0751 /usr/share/piwik
chmod 0771 /usr/share/piwik/tmp
chmod 0751 /usr/share/piwik/tmp/assets
find /usr/share/piwik/tmp/sessions/* -type f -exec chmod 600 {} +
find /usr/share/piwik/tmp/templates_c/* -type d -exec chmod 0755 {} +

Signalisiert die Piwik-Benutzeroberfläche eine Updatemöglichkeit für ein Plugin wie beispielsweise SecurityInfo, lauten die Aufrufe zum Setzen der Berechtigungen vor dem Update

chown www-data:www-data /usr/share/piwik/plugins
chown -R www-data:www-data /usr/share/piwik/plugins/SecurityInfo

und nach dem Update

chown root:www-data /usr/share/piwik/plugins
chown -R root:www-data /usr/share/piwik/plugins/SecurityInfo
find /usr/share/piwik/plugins/SecurityInfo \( -type d -exec chmod 0750 {} + \) -o \( -type f -exec chmod 0640 {} + \)

Ubuntu Server – DNS-Server Bind mit mehreren Domains

Mit dem DNS(„Domain Name System“)-Server Bind kann man neben der Namensauflösung in einfachen Netzwerken mit nur einem Domainnamen auch komplexere Strukturen mit mehreren Domains realisieren.

Aufbauend auf dem Grundlagenbeitrag über Bind werden wir nachfolgend exemplarisch in einem lokalen Netzwerk die drei Domainnamen test.local, example.local und dummy.local einrichten. Hierbei gehen wir der Übersichtlichkeit halber nicht auf Konfigurationsmöglichkeiten für externe Zugriffe aus dem Internet ein.

Voraussetzungen

Wir haben unseren Server z. B. gemäß den Anleitungen zur Ubuntu-Server-Basisinstallation und zur Bind-Installation im lokalen Netzwerk 192.168.0.0/24 installiert und konfiguriert und durch Updates / Upgrades auf den neuesten Stand gebracht. Der DNS-Server server.test.local hat die IPv4-Adresse 192.168.0.1, der Mailserver mail.test.local ist unter 192.168.0.2 erreichbar. Die drei Domains und je ein Server server sind folgenden Adressen zugeordnet:

IP-Adresse   Domain        Server
192.168.0.1  test.local    server.test.local
192.168.0.11 example.local server.example.local
192.168.0.21 dummy.local   server.dummy.local

Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

Zonendateien ergänzen

Für eine Bind-Konfiguration mit drei Domainnamen benötigen wir insgesamt vier Zonendateien. Eine „Forward Lookup“-Zonendatei pro Domain dient dabei der Auflösung von Namen in entsprechende IP-Adressen. In einer gemeinsamen „Reverse Lookup“-Zonendatei ordnen wir umgekehrt den Adressen Domainnamen zu.

„Forward Lookup“-Zonendateien anlegen

Analog zur Datei db.test.local erzeugen wir für die Domains example.local und dummy.local zunächst „Forward Lookup“-Zonendateien mit passenden Zugriffsrechten:

root@server:~# touch /etc/bind/db.example.local /etc/bind/db.dummy.local
root@server:~# chown root:bind /etc/bind/db.example.local /etc/bind/db.dummy.local

Ohne Einträge für weitere Rechner / Subdomains neben einem Server pro Domain und dem Mailserver sehen die Zonendateien für test.local und die neuen Zonen wie folgt aus:

; Zonendatei db.test.local für Zone test.local
$TTL 604800
@      SOA server.test.local. root.test.local. (
           2016062701 ; Serial
           604800     ; Refresh
           86400      ; Retry
           2419200    ; Expire
           604800 )   ; Minimum
@      NS  server.test.local.
@      A   192.168.0.1
@      MX  10 mail.test.local.
server A   192.168.0.1
mail   A   192.168.0.2
; Zonendatei db.example.local für Zone example.local
$TTL 604800
@      SOA server.test.local. root.test.local. (
           2016062701 ; Serial
           604800     ; Refresh
           86400      ; Retry
           2419200    ; Expire
           604800 )   ; Minimum
@      NS  server.test.local.
@      A   192.168.0.11
@      MX  10 mail.test.local.
server A   192.168.0.11
; Zonendatei db.dummy.local für Zone dummy.local
$TTL 604800
@      SOA server.test.local. root.test.local. (
           2016062701 ; Serial
           604800     ; Refresh
           86400      ; Retry
           2419200    ; Expire
           604800 )   ; Minimum
@      NS  server.test.local.
@      A   192.168.0.21
@      MX  10 mail.test.local.
server A   192.168.0.21

Der Aufbau der „Forward Lookup“-Zonendateien db.example.local und db.dummy.local ist dem von db.test.local sehr ähnlich. Die Resource Records vom Typ SOA, NS und MX sind identisch, da alle Domains denselben Nameserver und Mailserver nutzen und der Administrator für alle Zonen die gleiche E-Mail-Adresse root@test.local hat. Nur die Resource Records vom Typ A unterscheiden sich entsprechend der weiter oben beschriebenen Zuordnung der Domainnamen und Server zu den IP-Adressen.

Es ist natürlich auch möglich, unterschiedlichen Domains dieselbe IP-Adresse zuzuweisen. Neben den A-Records sind dazu die nachfolgend behandelten PTR-Records in der „Reverse Lookup“-Zonendatei anzupassen.

„Reverse Lookup“-Zonendatei erweitern

Die „Reverse Lookup“-Zonendatei db.0.168.192 müssen wir nun noch um Resource Records vom Typ PTR für die beiden Server in den Domains example.local und dummy.local erweitern. Die komplette Datei hat dann folgenden Aufbau:

; Zonendatei db.0.168.192 für Netzwerk 192.168.0.0/24 
$TTL 604800
@  SOA server.test.local. root.test.local. (
       2016062701 ; Serial
       604800     ; Refresh
       86400      ; Retry
       2419200    ; Expire
       604800 )   ; Minimum
@  NS  server.test.local.
1  PTR server.test.local.
2  PTR mail.test.local.
11 PTR server.example.local.
21 PTR server.dummy.local.

Konfigurationsdatei ergänzen

Damit die zusätzlichen „Forward Lookup“-Zonendateien von Bind berücksichtigt werden, ergänzen wir abschließend die Konfigurationsdatei /etc/bind/named.conf.local um entsprechende zone-Definitionen:

zone "test.local" {
  type master;
  file "/etc/bind/db.test.local";
};
zone "example.local" {
  type master;
  file "/etc/bind/db.example.local";
};
zone "dummy.local" {
  type master;
  file "/etc/bind/db.dummy.local";
};
zone "0.168.192.in-addr.arpa" {
  type master;
  file "/etc/bind/db.0.168.192";
};

Bind testen

Die geänderte Konfiguration können wir mit den im Artikel zur Bind-Installation beschriebenen Tests überprüfen.

Zunächst kontrollieren wir die Syntax der Zonendateien mit named-checkzone:

root@server:~# named-checkzone test.local /etc/bind/db.test.local
root@server:~# named-checkzone example.local /etc/bind/db.example.local
root@server:~# named-checkzone dummy.local /etc/bind/db.dummy.local
root@server:~# named-checkzone 0.168.192.in-addr.arpa. /etc/bind/db.0.168.192

Nach dem Neustart von Bind mit

root@server:~# service bind9 restart

testen wir unsere Konfiguration mit dem Programm dig:

root@server:~# dig test.local ANY
root@server:~# dig example.local ANY
root@server:~# dig dummy.local ANY
root@server:~# dig -x 192.168.0.1

Ob die Namensauflösung auf den Clients funktioniert, können wir mit dem ping-Befehl prüfen, unter Linux z. B. mit den Aufrufen:

hans@client:~ ping -c 4 test.local
hans@client:~ ping -c 4 example.local
hans@client:~ ping -c 4 dummy.local

Ubuntu Server – PHP absichern

Für den Betrieb von Websites wird häufig der Webserver Apache in Kombination mit der Skriptsprache PHP eingesetzt. Nach der Standardinstallation, z. B. zusammen mit dem Softwarepaket LAMP, enthält die PHP-Umgebung allerdings einige mehr oder weniger sicherheitskritische Voreinstellungen.

Zur Absicherung von PHP werden wir in diesem Beitrag sicherheitsrelevante Direktiven in der zentralen Konfigurationsdatei php.ini anpassen. Wir folgen hierbei den Empfehlungen des Analyseprogramms PhpSecInfo, das wir vorab auf unserem Testsystem mit Ubuntu Server 14.04 installieren. Die von PhpSecInfo vorgeschlagenen Änderungen können ein sicherheitsbewusstes Programmdesign natürlich nicht ersetzen, stellen aber eine sinnvolle Ergänzung dar.

Die beschriebenen Maßnahmen sind recht restriktiv und müssen sicherlich in vielen Fällen, insbesondere bei nicht selbst programmierten Webanwendungen, aufgeweicht werden. Hier hilft oft nur ein kompletter Funktionstest und / oder eine Analyse des Programmcodes.

Voraussetzungen

Wir haben unseren Server z. B. gemäß den Anleitungen zur Ubuntu-Server-Basisinstallation und zur LAMP-Server-Installation installiert und konfiguriert und durch Updates / Upgrades auf den neuesten Stand gebracht. Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

PhpSecInfo installieren

Zur Installation von PhpSecInfo benötigen wir die Versionsverwaltung Git. Falls Git noch nicht auf unserem Server läuft, müssen wir das Programm zunächst z. B. mit apt installieren:

root@server:~# apt install git

Danach kopieren wir mit git clone die Paketquellen in das (frei wählbare) Verzeichnis /usr/share/phpsecinfo, das dabei automatisch angelegt wird:

root@server:~# git clone https://github.com/bigdeej/PhpSecInfo /usr/share/phpsecinfo

Bei einer eventuellen zukünftigen Aktualisierung auf eine neue Programmversion können Updates mit git pull eingespielt werden:

root@server:~# git -C /usr/share/phpsecinfo pull

Zur Sicherheit erhält nur der Benutzer root Vollzugriff auf den Ordner /usr/share/phpsecinfo. Den Mitgliedern der Gruppe www-data, also auch dem Apache-Serverdienst, gestatten wir nur lesenden Zugriff:

root@server:~# chown -R root:www-data /usr/share/phpsecinfo
root@server:~# find /usr/share/phpsecinfo \( -type d -exec chmod 0750 {} + \) -o \( -type f -exec chmod 0640 {} + \)

Damit die Testergebnisse von PhpSecInfo und somit eventuelle Sicherheitslücken unserer PHP-Installation nicht öffentlich zugänglich sind, erstellen wir eine separate, ausschließlich per HTTPS erreichbare Subdomain phpsecinfo.test.local, die durch eine HTTP-Basisauthentifizierung geschützt ist. Zusätzlich nutzen wir die in Ubuntu Server 14.04 – Webserver Apache absichern behandelten Sicherheitsheader.

Die Virtual-Host-Konfiguration für Apache orientiert sich am Beitrag über die sichere Installation von phpMyAdmin. Auf „Security by Obscurity“-Maßnahmen wie einen geänderten HTTPS-Port oder einen zufallsgenerierten Namen für die Subdomain verzichten wir der Übersichtlichkeit halber. Da PhpSecInfo Inline-CSS verwendet, ist ein zusätzlicher Eintrag style-src 'self' 'unsafe-inline' in der Content Security Policy nötig. Die resultierende Konfigurationsdatei /etc/apache2/sites-available/phpsecinfo.conf lautet:

<VirtualHost *:443>
  CustomLog ${APACHE_LOG_DIR}/access-phpsecinfo.log vhost_combined
  DocumentRoot /usr/share/phpsecinfo
  ErrorLog ${APACHE_LOG_DIR}/error-phpsecinfo.log
  Header always set Content-Security-Policy "default-src 'self'; style-src 'self' 'unsafe-inline'"
  Header always set Strict-Transport-Security "max-age=31536000"
  Header always set X-Content-Type-Options "nosniff"
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set X-Xss-Protection "1; mode=block"
  ServerAdmin webmaster@test.local
  ServerName phpsecinfo.test.local
  SSLCertificateFile /etc/ssl/certs/phpsecinfo.test.local.pem
  SSLCertificateKeyFile /etc/ssl/private/phpsecinfo.test.local.key
  SSLEngine on
  <Directory /usr/share/phpsecinfo>
    AuthBasicProvider file
    AuthName "phpsecinfo"
    AuthType Basic
    AuthUserFile /etc/apache2/.htpasswd_phpsecinfo
    Require valid-user
  </Directory>
</VirtualHost>

Abschließend aktivieren wir die PhpSecInfo-Konfiguration mit a2ensite und starten Apache neu:

root@server:~# a2ensite phpsecinfo.conf
root@server:~# service apache2 restart

Um die neue Subdomain ansprechen zu können, sind eventuell noch systemspezifische Anpassungen an dem / den DNS-Server/n erforderlich, auf die wir hier nicht weiter eingehen.

PHP-Umgebung mit PhpSecInfo analysieren und anpassen

Zur Analyse unserer PHP-Installation rufen wir PhpSecInfo im Browser auf:

https://phpsecinfo.test.local

Auf unserem Testsystem mit Ubuntu Server 14.04 und PHP 5.5.9 wurden insgesamt 39 Warnungen und 6 Bemerkungen zu möglichen Sicherheitsrisiken in den Bereichen Core, Session, Dir und Functions ausgegeben:

...
Test Results Summary
Test    Result
Pass    22 out of 67 (32.84%)
Notice  6 out of 67 (8.96%)
Warning 39 out of 67 (58.21%)
Score   37.31%

Details zu den Einträgen liefert ein Klick auf den jeweiligen Verweis More information. Im Bereich Functions wird leider statt einer Erläuterung nur eine Not Found-Fehlermeldung ausgegeben. Informationen über die einzelnen Funktionen kann man stattdessen im PHP-Handbuch nachschlagen.

Die aufgeführten Empfehlungen können wir durch Ändern von Direktiven in der zentralen Konfigurationsdatei /etc/php5/apache2/php.ini umsetzen. Da bei mehrfacher Verwendung derselben Direktive das letzte Auftreten ausgewertet wird, setzen wir die Anweisungen gebündelt an das Dateiende und ersparen uns so nebenbei das Auskommentieren der Originaleinträge:

...
upload_tmp_dir = /xxx/
open_basedir = /xxx/
file_uploads = Off
allow_url_fopen = Off
disable_functions = ${disable_functions}posix_setpgid,exec,ftp_login,mysql_pconnect,apache_setenv,popen,posix_getpwuid,posix_setsid,passthru,escapeshellcmd,ini_alter,ftp_raw,ftp_nb_fput,ini_restore,shell_exec,ftp_get,proc_get_status,highlight_file,proc_close,proc_terminate,syslog,ftp_connect,posix_uname,ini_get_all,proc_open,posix_kill,escapeshellarg,ftp_rawlist,posix_setuid,openlog,php_uname,system,ftp_exec,posix_mkfifo,proc_nice,ftp_put

Im Verzeichnis upload_tmp_dir werden Uploads zwischengespeichert, bevor sie von einem PHP-Skript weiterverarbeitet werden. open_basedir schränkt den PHP-Dateizugriff auf den angegebenen Ordner ein. Für beide Verzeichnisse wählen wir den nicht-existierenden Ordnernamen /xxx/ und erzwingen damit, dass bei Bedarf in einer Apache-Website-Konfiguration passende Verzeichnisse explizit angegeben werden müssen.

Per Voreinstellung erlaubt PHP das Hochladen von Dateien, in der Regel in einem Formular im Webbrowser. file_uploads = Off unterbindet diese Möglichkeit und verringert so die Angriffsfläche für bösartige Attacken.

Mit den Dateifunktionen von PHP können auch externe Daten, beispielsweise von anderen Websites, gelesen werden. Die Anweisung allow_url_fopen = Off beschränkt den Zugriff auf lokale Dateien und verhindert das sonst auf diesem Weg mögliche Einschleusen von Schadcode.

Funktionen, die zum Sprachumfang von PHP gehören, aber z. B. aus Sicherheitsgründen nicht bereitgestellt werden sollen, werden mit der Direktive disable_functions deaktiviert. Gemäß den Ausführungen in der PHP-Dokumentation zur Prozesskontrolle PCNTL sind die PCNTL-Funktionen mit dem Präfix pcntl hier bereits per Komma getrennt aufgelistet. Mit ${disable_functions} greifen wir auf diese ursprüngliche Liste zu und ergänzen sie um die Vorschläge von PhpSecInfo.

Neben der zentralen Einstellung in php.ini können Direktiven auch in Benutzerskripten, in .htaccess-Dateien, in Apache-Konfigurationsdateien und – nur für CGI/FastCGI-SAPI – in der .user.ini eingesetzt werden. Einen Überblick gibt die Liste der php.ini-Direktiven der PHP-Dokumentation.

Durch die Anweisung open_basedir = /xxx/ haben wir uns auch den Zugang zu PhpSecInfo versperrt. Mit einem zusätzlichen php_admin_value-Eintrag in der Apache-Konfigurationsdatei phpsecinfo.conf geben wir das Verzeichnis /usr/share/phpsecinfo/ wieder für PHP frei:

...
<Directory /usr/share/phpsecinfo>
  ...
  php_admin_value open_basedir /usr/share/phpsecinfo/
</Directory>

Zur Übernahme der Änderungen ist ein Neustart von Apache erforderlich:

root@server:~# service apache2 restart

Beim zweiten Testlauf mit der modifizierten php.ini-Datei gibt es nur noch einen Hinweis auf die veraltete PHP-Version 5.5.9. Da mit dem LAMP-Server unter Ubuntu 14.04 keine aktuellere Version installiert wird, können wir dies leider nicht bzw. nur mit einer manuellen Installation von PHP ändern.

Die Core-Sicherheitswarnungen zum Benutzer / zur Gruppe www-data des Apache-Servers mit einer (angeblich) privilegierten user_id / group_id verschwinden mit der Deaktivierung der Funktion exec in disable_functions. Da www-data neben anderen wichtigen Benutzerkonten und Gruppen auf jedem Ubuntu-System eine einheitliche user_id und group_id von 33 zugeordnet wird, sollten wir diese allerdings selbst bei Nutzung von exec nicht ändern.

Die Bemerkungen zu save_path und is_root_read in den Bereichen Session und Dir werden wegen der Änderung der Einstellung in open_basedir nicht mehr angezeigt.

Ubuntu Server 14.04 – Webserver Apache absichern

Durch die zunehmende Anzahl und gleichzeitig steigende Qualität böswilliger Angriffe auf Websites werden immer wieder neue Schwachstellen in Webserverkonfigurationen offengelegt. Neben der als selbstverständlich anzusehenden Basisabsicherung mit dem verschlüsselnden „HyperText Transfer Protocol Secure“ HTTPS sollten daher zusätzliche Sicherheitsvorkehrungen getroffen werden. Um zeitnah auf neue Sicherheitslücken reagieren zu können, sind die Schutzmethoden durch regelmäßige Tests zu überprüfen und bei Bedarf anzupassen und / oder zu ergänzen.

Im nachfolgenden Beitrag werden wir den Webserver Apache ausgehend von einer HTTPS-Basiskonfiguration auf Sicherheit hin optimieren. Serverseitig schließen wir hierzu alle Übertragungsprotokolle bis auf TLS 1.2 aus und lassen nur Verschlüsselungs- und Authentifizierungverfahren mit ephemeralen Elliptic Curve Diffie-Hellman-Schlüsseln zu. Damit gewährleisten wir „Perfect Forward Secrecy“ PFS und verhindern DROWN-, Poodle- und Logjam-Attacken. Durch das Einfügen zusätzlicher HTTP-Antwortheader aktivieren wir clientseitig „HTTP Strict Transport Security“ HSTS zur Abwehr von Man-in-the-Middle-Angriffen und schützen uns mit einer „Content Security Policy“ CSP in Kombination mit weiteren Headern vor Clickjacking- und Cross-Site-Scripting-Attacken. Zur Analyse der Wirksamkeit der Schutzmaßnahmen nutzen wir den SSL Server Test von Qualisys und den HTTP-Headertest SecurityHeaders.io von Scott Helme.

[Update 29.08.2016 Anfang]
Einen weiteren Online-Sicherheitscheck, in den auch Ergebnisse anderer Tests einfließen, bietet Mozilla mit dem Projekt Observatory an. Von diesem Programm gibt es auch eine Kommandozeilenversion httpobs, die für einen regelmäßigen automatisierten Check genutzt werden kann.
[Update 29.08.2016 Ende]

Dieser Artikel ist eine Ergänzung des Beitrags zur LAMP-Server-Installation. Hierin haben wir in der Apache-Konfiguration einen virtuellen Host www.test.local für die HTTPS-Standardwebsite eingerichtet. Eine automatische Umleitung von http://www.test.local auf https://www.test.local verhindert unverschlüsselte Zugriffe. Die Testdomain test.local dient dabei lediglich als Platzhalter für eine reale Domain.

Voraussetzungen

Wir haben unseren Server z. B. gemäß den Anleitungen zur Ubuntu-Server-Basisinstallation und zur LAMP-Server-Installation installiert und konfiguriert und durch Updates / Upgrades auf den neuesten Stand gebracht. Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

Website mit SSL Server Test analysieren

Für eine Sicherheitscheck unserer Website www.test.local eignet sich der SSL Server Test von Qualisys, den wir im Browser wie folgt aufrufen:

https://www.ssllabs.com/ssltest/analyze.html?d=www.test.local&latest

Die Analyse kann wegen des Umfangs der durchgeführten Prüfungen mehrere Minuten dauern, liefert dafür aber eine Vielzahl konkreter Hinweise auf mögliche Schwachstellen. Bei unserem Testsystem erhielten wir folgende Sicherheitswarnungen:

...
Protocols
...
SSL 3 2   INSECURE 	Yes
...
Cipher Suites (sorted by strength as the server has no preference; deprecated and SSL 2 suites at the end)
...
TLS_RSA_WITH_RC4_128_SHA (0x5)   INSECURE 	128
...
TLS_ECDHE_RSA_WITH_RC4_128_SHA (0xc011)   ECDH secp256r1 (eq. 3072 bits RSA)   FS   INSECURE 	128
...
Protocol Details
...
POODLE (SSLv3) 	Vulnerable   INSECURE (more info)   SSL 3: 0xa
...
RC4 	Yes   INSECURE (more info)
...
Forward Secrecy 	With some browsers (more info)
...

Apache-SSL-Konfiguration anpassen

Zur Erhöhung der Sicherheit nehmen wir zunächst zwei Änderungen in der Konfigurationsdatei /etc/apache2/mods-available/ssl.conf des SSL-Moduls von Apache vor:

...
#SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5
SSLCipherSuite EECDH:!SHA1
...
#SSLProtocol all
SSLProtocol TLSv1.2
...

Mit der SSLCipherSuite-Anweisung legen wir fest, welche kombinierten Verschlüsselungs- und Authentifizierungverfahren („Cipher Suites“) unser Server dem Client für eine gesicherte SSL/TLS-Verbindung anbietet. Apache greift dabei auf die vom Programmpaket OpenSSL zur Verfügung gestellten Cipher-Suites zurück, die wir uns mit openssl ciphers anzeigen lassen können:

root@server:~# openssl ciphers -v

Die SSLCipherSuite-Option EECDH beschränkt die Schlüsselaustauschverfahren auf ephemerale Elliptic Curve Diffie-Hellman-Schlüssel und gewährleistet damit „Perfect Forward Secrecy“ PFS: potentielle Angreifer können mit einem ihnen bekannten Schlüssel einer vorhergehenden Verbindung nicht die zukünftige Kommunikation entschlüsseln. Der Diffie-Hellman-Algorithmus auf der Basis diskreter Logarithmen (Option DHE) ermöglicht ebenfalls PFS, ist aber in bestimmten Konfigurationen anfällig für die in dem Konferenzbeitrag Imperfect Forward Secrecy: How Diffie-Hellman Fails in Practice beschriebene Logjam-Attacke. Da er zudem langsamer als die Version mit elliptischen Kurven ist, werden DHE-Verfahren nicht mit aufgelistet.

Die zusätzliche Option !SHA1 schließt den unsicheren Secure Hash-Algorithmus SHA-1 als Hashfunktion für die Integritätssicherung aus.

Durch die Anweisung SSLProtocol TLSv1.2 wird statt der standardmäßig in Apache freigeschalteten Protokolle SSLv3, TLSv1, TLSv1.1 und TLSv1.2 nur noch das TLS-Protokoll in der aktuell sichersten Version 1.2 genutzt.

Die beschriebenen Konfigurationsänderungen orientieren sich an den Empfehlungen der technischen Richtlinie TR-02102-2 zur Verwendung von Transport Layer Security (TLS) des BSI und der RFC 7525: Recommendations for Secure Use of Transport Layer Security (TLS) and Datagram Transport Layer Security (DTLS) der Internet Engineering Task Force IETF. Eine detaillierte Beschreibung der SSL-Modul-Konfiguration enthält die Apache-Dokumentation über mod_ssl.

HTTP Strict Transport Security aktivieren

Üblicherweise gibt ein Benutzer im Webbrowser eine Webadresse ohne Angabe des Protokolls ein und ruft sie damit über das nicht-verschlüsselnde „HyperText Transfer Protocol“ HTTP auf. Damit die Datenübertragung trotzdem abgesichert stattfindet, wird in einer typischen Apache-SSL-Konfiguration die Anfrage serverseitig z. B. mit Hilfe der Anweisung Redirect von HTTP auf HTTPS umgeleitet. Die zunächst zum Webserver aufgebaute unverschlüsselte HTTP-Verbindung ist hierbei anfällig für einen Man-in-the-Middle-Angriff. Zusätzlichen Schutz gegen eine solche Attacke bietet das Verfahren „HTTP Strict Transport Security“ HSTS, das eine clientseitige Umleitung im Webbrowser bewirkt.

Zur Einrichtung von HSTS aktivieren wir zunächst mit

root@server:~# a2enmod headers

das Apache-Modul mod_headers und ergänzen dann im VirtualHost-Block der HTTPS-Standardwebsite-Konfigurationsdatei /etc/apache2/sites-available/default-ssl.conf:

<VirtualHost *:443>
  ...
  Header always set Strict-Transport-Security "max-age=31536000"
  ...
</VirtualHost>

Mit der Header-Anweisung wird ein Webbrowser beim ersten Aufruf unserer Website angewiesen, für die durch die Option max-age definierte Zeitspanne von 31536000 Sekunden = ca. 1 Jahr Zugriffe ausschließlich per HTTPS durchzuführen. Hierzu speichert er diese Information zwischen und ersetzt im vorgegebenen Zeitraum automatisch alle HTTP-Aufrufe durch entsprechende HTTPS-Aufrufe. Bei weiteren Besuchen durch denselben Webbrowser wird der Header erneut übertragen und die Information aktualisiert. Da der erste Website-Aufruf unverschlüsselt stattfinden kann, besteht zu diesem Zeitpunkt noch die Gefahr eines Man-in-the-Middle-Angriffs. Daher sollte man für max-age möglichst große Werte wählen.

Sicherheitsmaßnahmen überprüfen

Nach einem Neustart von Apache zur Übernahme unserer Änderungen mit

root@server:~# service apache2 restart

lassen wir den SSL Server Test ein zweites Mal durchlaufen und erhalten folgende Hinweise zu Clients, die keine der vom Server angebotenen Cipher-Suites unterstützen:

...
Handshake Simulation
Android 2.3.7   No SNI 2		Protocol or cipher suite mismatch
Android 4.0.4 	Protocol or cipher suite mismatch
Android 4.1.1 	Protocol or cipher suite mismatch
Android 4.2.2 	Protocol or cipher suite mismatch
Android 4.3 	Protocol or cipher suite mismatch
...
Baidu Jan 2015 	Protocol or cipher suite mismatch
...
IE 6 / XP   No FS 1	  No SNI 2		Protocol or cipher suite mismatch
IE 7 / Vista 	Protocol or cipher suite mismatch
IE 8 / XP   No FS 1	  No SNI 2		Protocol or cipher suite mismatch
IE 8-10 / Win 7  R		Protocol or cipher suite mismatch
...
IE 10 / Win Phone 8.0 	Protocol or cipher suite mismatch
...
Java 6u45   No SNI 2		Protocol or cipher suite mismatch
Java 7u25 	Protocol or cipher suite mismatch
...
OpenSSL 0.9.8y 	Protocol or cipher suite mismatch
...
Safari 5.1.9 / OS X 10.6.8 	Protocol or cipher suite mismatch
...
Safari 6.0.4 / OS X 10.8.4  R		Protocol or cipher suite mismatch
...

Auf den ersten Blick schließt man durch die beschriebenen restriktiven Sicherheitsmaßnahmen relativ viele Clients aus. Im Wesentlichen handelt es sich allerdings um veraltete Versionen von Clientsoftware. Wer auf Kompatibilität zu nicht unterstützten Browser-/Betriebsystem-Kombinationen angewiesen ist, kann natürlich die Apache-Konfiguration entsprechend anpassen. Empfehlenswert ist aber stattdessen ein Update der betroffenen Software auf eine aktuellere und sicherere Version.

Ergänzende Tests mit SecurityHeaders.io durchführen

Neben Strict-Transport-Security für HSTS können weitere sicherheitsrelevante HTTP-Antwortheader zusätzlichen Schutz bieten. Hinweise hierauf erhalten wir mit dem HTTP-Headertest SecurityHeaders.io von Scott Helme:

https://securityheaders.io/?q=https://www.test.local/

Die für unser Testsystem relevanten Auszüge aus den Ergebnissen lauten:

...
Raw Headers
...
Server Apache
...
X-Powered-By PHP/5.5.9-1ubuntu4.14
...
Missing Headers
Content-Security-Policy ...
Public-Key-Pins ...
X-Frame-Options	...
X-XSS-Protection ...
X-Content-Type-Options ...
...

„Security by Obscurity“ einrichten

Die Einträge für Server und X-Powered-By in der Raw Headers-Sektion lassen sich mit „Security by Obscurity“-Maßnahmen optimieren, mit denen Angriffe auf unseren Webserver durch Unterdrücken von Headerinformationen erschwert werden.

Falls man in der Apache-Konfigurationsdatei /etc/apache2/conf-available/security.conf den Eintrag ServerTokens OS bereits in ServerTokens Prod geändert hat, wird im Server-Header nur noch der Name Apache übertragen. Ein vollständiges Löschen des Headers z. B. mit Header always unset Server ist bei der verwendeten Apache-Version 2.4.7 leider nicht möglich.

Die Ausgabe des Headers X-Powered-By können wir hingegen durch Ändern des Eintrags expose_php in der PHP-Konfigurationsdatei /etc/php5/apache2/php.ini komplett unterbinden:

...
;expose_php = On
expose_php = Off
...

Zusätzliche Sicherheitsheader definieren

Fehlende sicherheitstechnisch wichtige Header sind in der Rubrik Missing Headers aufgelistet. Die Wirksamkeit dieser Sicherheitsheader hängt davon ab, ob und wie sie vom verwendeten Browser unterstützt werden.

Wie Strict-Transport-Security definieren wir die zusätzlichen Header Content-Security-Policy, X-Frame-Options, X-Xss-Protection und X-Content-Type-Options in der HTTPS-Standardwebsite-Konfigurationsdatei /etc/apache2/sites-available/default-ssl.conf:

<VirtualHost *:443>
  ...
  Header always set Content-Security-Policy "default-src 'self'"
  Header always set X-Frame-Options "SAMEORIGIN"
  Header always set X-Xss-Protection "1; mode=block"
  Header always set X-Content-Type-Options "nosniff"
  ...
</VirtualHost>

Zur Aktivierung erfolgt danach noch der obligatorische Neustart von Apache:

root@server:~# service apache2 restart

Der Content-Security-Policy-Header erfordert die meisten Anpassungsarbeiten und wird deshalb zum Schluss behandelt. Die veraltete Alternative X-Content-Security-Policy sollte nicht mehr verwendet werden. Auf das mit der Erweiterung Public Key Pinning Extension for HTTP im Header Public-Key-Pins mögliche „HTTP Public Key Pinning“ HPKP werden wir in einem gesonderten Beitrag eingehen.

X-Frame-Options-Header

Mit X-Frame-Options legen wir fest, unter welchen Voraussetzungen unsere Seiten in andere Websites mit HTML-Anweisungen wie <frame> oder <iframe> eingebettet werden dürfen. Der Parameter SAMEORIGIN lässt dies nur innerhalb unserer eigenen Domain zu. Hiermit verhindern wir das sogenannte „Clickjacking“, bei dem ein Angreifer seine Internetseiten im Browser mit unseren Seiten optisch überlagert. Mausklicks oder Tastatureingaben auf einer scheinbar von uns stammenden Seite könnten dann unerwünschte Aktionen auf der darunterliegenden Seite des Angreifers auslösen. X-Frame-Options wird als Quasistandard von allen führenden Browsern unterstützt.

X-Xss-Protection-Header

Die Browser Internet Explorer, Chrome und Safari enthalten einen nicht standardisierten XSS-Filter, der das sogenannte „Cross-Site-Scripting“ verhindern soll. Beim Cross-Site-Scripting versucht ein Angreifer, ein in der Regel in JavaScript geschriebenes Skript an eine Webseite zu übertragen, die Benutzereingaben z. B. aus Webformularen verarbeitet. Werden die Eingabedaten von der Webanwendung nicht oder nur unzureichend überprüft und an anderer Stelle ungefiltert an den Browser ausgegeben, kann das Skript mit dem eventuellen Schadcode ausgeführt werden.

Der XSS-Filter ist per Voreinstellung aktiviert, kann aber vom Benutzer in den Browseroptionen abgeschaltet werden. Um ihn zu (re)aktivieren, verwenden wir den Header X-Xss-Protection mit dem Wert 1. Durch den zusätzlichen Parameter mode=block wird die Ausgabe einer mit JavaScript „verunreinigten“ Internetseite im Browser unterdrückt und nicht versucht, die Seite vom Skriptcode gereinigt auszugeben.

X-Content-Type-Options-Header

Der Header X-Content-Type-Options mit dem (einzig möglichen) Wert nosniff erzwingt, dass vom Webserver ausgelieferte Dateien stets gemäß dem im zugehörigen Header Content-Type angegebenen Dateityp („MIME-Type“) verarbeitet werden. Er verhindert im Internet Explorer und in Chrome das sogenannte „MIME-Sniffing“, mit dem die Browser versuchen, den korrekten MIME-Type bei fehlenden oder (vermeintlich) falschen Content-Type-Angaben durch Analyse des Dateiinhalts zu ermitteln.

Das eigentlich hilfreiche MIME-Sniffing kann in bestimmten Szenarien eine Sicherheitslücke darstellen. Der Artikel Bypassing Content Security Policy with a JS/GIF Polyglot von Ajin Abraham beschreibt beispielsweise einen Cross-Site-Scripting-Angriff mit Hilfe einer Datei im GIF-Format, die gleichzeitig JavaScript-Code enthält.

Content-Security-Policy-Header

Die „Content Security Policy“ CSP bildet ein Regelwerk, das festlegt, welche Inhalte aus welchen Quellen von einer Webanwendung genutzt werden dürfen. Die Inhalte können dabei Bilder, Skripte, Stylesheets und weitere Objekte sein.

Eine Content Security Policy besteht aus einer Anweisung oder einem Satz von Anweisungen, die durch Semikolons voneinander getrennt sind. Mit der einfachen CSP script-src ’self‘ erlauben wir z. B. die Ausführung von Skripten (script-src) nur aus Dateien, die sich auf unserer (’self‘) Website befinden. Als sinnvoller Grundschutz eignet sich der Content-Security-Policy-Header default-src ’self‘, mit dem wir für alle Objekttypen (default-src) ausschließlich domain-eigene Inhalte zulassen.

Content Security Policy für andere Webanwendungen festlegen

Eine CSP für selbst programmierte Webanwendungen zu definieren, gestaltet sich in der Regel noch recht einfach. Bei Programmen, die man aus anderen Quellen installiert hat, ist häufig eine tiefergehende Analyse erforderlich. Hilfreich sind hierbei Webentwicklungswerkzeuge wie das Firefox-Add-on Firebug. Hiermit können wir eventuell auftretende Fehlermeldungen auswerten und die CSP ausgehend von der Grundeinstellung default-src ’self‘ schrittweise erweitern und modifizieren.

Für die Horde Groupware Webmail Edition, das Datenbank-Administrationswerkzeug phpMyAdmin und das Content-Management-System WordPress wurden auf diese Weise die folgenden CSP ermittelt:

#Horde 5.2.1:
default-src 'self'; img-src 'self' data:; script-src 'unsafe-eval' 'unsafe-inline' 'self'; style-src 'unsafe-inline' 'self'

#phpMyAdmin 4.0.10:
default-src 'self'; script-src 'unsafe-eval' 'unsafe-inline' 'self'; style-src 'unsafe-inline' 'self'; img-src 'self' data:

#WordPress 4.4.2:
default-src 'self'; font-src https://fonts.gstatic.com data:; img-src 'self' https://secure.gravatar.com data:; script-src 'unsafe-eval' 'unsafe-inline' 'self'; style-src 'unsafe-inline' 'self' https://fonts.googleapis.com

Auf eine detaillierte Erläuterung einzelner Anweisungen wird an dieser Stelle verzichtet. Eine ausführliche Beschreibung findet man beispielsweise in der Spezifikation Content Security Policy Level 2 des World Wide Web Consortium W3C.

Zum Schutz vor Cross-Site-Scripting sind bei einer CSP per Voreinstellung Inline-Skripte, also z. B. in HTML eingebetteter JavaScript-Code, deaktiviert. Um den vollen Funktionsumfang nutzen zu können, muss diese Schutzfunktion allerdings bei allen drei aufgeführten Webanwendungen mit der script-src-Option ‚unsafe-inline‘ wieder aufgehoben werden. Hierdurch sind XSS-Attacken durch das Einschleusen von Skripten in manipulierten Benutzereingaben möglich. Um diese Sicherheitslücke zu schließen, müsste der JavaScript-Code konsequent vom HTML-Code getrennt und zusammen mit Eventhandlern wie onClick in eigene Dateien ausgelagert werden.

Ubuntu Server 14.04 – phpMyAdmin installieren und absichern

phpMyAdmin ist ein in der Skriptsprache PHP geschriebenes Werkzeug zur Administration von MySQL– und MariaDB-Datenbankservern. Die webbasierte Benutzeroberfläche ermöglicht die Datenbankverwaltung ohne detaillierte Kenntnisse des SQL-Befehlssatzes. phpMyAdmin setzt einen Webserver mit PHP-Unterstützung voraus, der z. B. im Softwarepaket LAMP enthalten ist.

Nachfolgend werden wir phpMyAdmin unter Ubuntu Server 14.04 mit installiertem LAMP-Server einrichten und mit verschiedenen Maßnahmen wie ausschließlicher HTTPS-Übertragung und HTTP-Authentifizierung absichern. Abschließend werden wir eine spezielle phpMyAdmin-Subdomain anlegen, die nur über einen Nicht-Standard-HTTPS-Port erreichbar ist. Darüber hinausgehende empfehlenswerte Sicherheitsvorkehrungen sind Gegenstand des Artikels Ubuntu Server 14.04 – Webserver Apache absichern.

Voraussetzungen

Wir haben unseren Server z. B. gemäß den Anleitungen zur Ubuntu-Server-Basisinstallation und zur LAMP-Server-Installation konfiguriert und durch Updates / Upgrades auf den neuesten Stand gebracht. Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

phpMyAdmin installieren

Die phpMyAdmin-Installation erfolgt unter Ubuntu Server 14.04 am einfachsten mit dem apt-Befehl:

root@server:~# apt install phpmyadmin

Während des Installationsvorgangs wird eine Auswahl von Webservern angezeigt, die auf Wunsch automatisch konfiguriert werden können. Hier kreuzen wir apache2 an ([*]). Die Abfrage, ob die Datenbank für phpMyAdmin mit dbconfig-common konfiguriert werden soll, beantworten wir mit Nein, da wir die Einrichtung manuell vornehmen werden.

Zur Sicherheit gestatten wir nur dem Eigentümer root Vollzugriff auf den Standard-Installationsordner /usr/share/phpmyadmin und erlauben der Apache-Gruppe www-data ausschließlich lesenden Zugriff:

root@server:~# chown -R root:www-data /usr/share/phpmyadmin
root@server:~# find /usr/share/phpmyadmin \( -type d -exec chmod 0750 {} + \) -o \( -type f -exec chmod 0640 {} + \)

Bei der automatischen Webserver-Konfiguration wird die Konfigurationsdatei /etc/phpmyadmin/apache.conf erzeugt, unter /etc/apache2/conf-available/phpmyadmin.conf ein Link auf diese Datei angelegt und die Konfiguration in Apache aktiviert. Ist der externe Zugriff auf das Verzeichnis /usr/share, wie im Artikel zur LAMP-Server-Installation beschrieben, aus Sicherheitsgründen gesperrt, müssen wir den Unterordner phpmyadmin durch folgenden zusätzlichen Eintrag in apache.conf freigeben:

...
<Directory /usr/share/phpmyadmin>
  ...
  Require all granted
  ...
</Directory>
...

Nach einem Neustart von Apache mit

root@server:~# service apache2 restart

können wir phpMyAdmin zum ersten Mal im Browser aufrufen:

http://www.test.local/phpmyadmin

Nach der Anmeldung mit Benutzername und Passwort für den MySQL-Server erhalten wir die Fehlermeldung:

Die Erweiterung mcrypt fehlt. Bitte die PHP-Konfiguration überprüfen.

Das fehlende PHP-Modul Mcrypt aktivieren wir mit php5enmod und starten danach Apache neu:

root@server:~# php5enmod mcrypt
root@server:~# service apache2 restart

Zuätzlich wird von phpMyAdmin folgende Meldung angezeigt:

Der phpMyAdmin-Konfigurationsspeicher ist nicht vollständig konfiguriert, einige erweiterte Funktionen wurden deaktiviert. Klicken Sie hier, um herauszufinden warum.

Nach Anklicken des Links erhält man eine Liste der Zusatzfunktionen, die mit dem phpMyAdmin-Konfigurationsspeicher genutzt werden können, und eine Kurzanleitung zur schrittweisen Einrichtung des Speichers.

Zunächst erstellen wir eine zentrale Datenbank phpmyadmin mit den Tabellen für den Konfigurationsspeicher. Im zugehörigen Abschnitt über den phpMyAdmin configuration storage in der Originaldokumentation wird auf eine SQL-Skriptdatei create_tables.sql verwiesen, die diesen Vorgang automatisiert. Diese Skriptdatei befindet sich bei Ubuntu 14.04 allerdings nicht im Unterordner sql des Installationsverzeichnisses /usr/share/phpmyadmin, sondern im Verzeichnis /usr/share/doc/phpmyadmin/examples. Da die Datei in gzip-komprimierter Form vorliegt, müssen wir sie vor der Nutzung entpacken:

root@server:~# gunzip /usr/share/doc/phpmyadmin/examples/create_tables.sql.gz

Zur Ausführung des Skripts starten wir den MySQL-Client als Benutzer root und laden create_tables.sql mit der Anweisung source:

root@server:~# mysql -p -u root
Enter password: ***
...
mysql> source /usr/share/doc/phpmyadmin/examples/create_tables.sql
...

Danach legen wir einen speziellen Benutzer pma, den sogenannten „Control User“, mit passenden Zugriffsrechten auf die Datenbank phpmyadmin an und beenden den MySQL-Client:

mysql> GRANT SELECT, INSERT, DELETE, UPDATE ON phpmyadmin.* TO 'pma'@'localhost' IDENTIFIED BY '***';
mysql> exit

Abschließend aktivieren wir die gewünschten Zusatzfunktionen durch Anpassen der zugehörigen Parameter in der Konfigurationsdatei config.inc.php. Diese Datei liegt bei Ubuntu 14.04 im Verzeichnis /etc/phpmyadmin. Die Anpassungen nehmen wir im Abschnitt Storage database and tables vor, in dem bereits auskommentierte Mustereinträge hinterlegt sind. In der Variablen $cfg['Servers'][$i]['pmadb'] speichern wir den Namen der Konfigurationsspeicher-Datenbank phpmyadmin. Die übrigen Einträge $cfg['Servers'][$i]['…'] beinhalten die für die erweiterten Funktionen benötigten Tabellen. Zur Aktivierung entfernen wir die Kommentarzeichen // und ergänzen noch einen Unterstrich hinter dem Präfix pma, da die Namen der mit dem Skript create_tables.sql angelegten Tabellen an dieser Stelle nur einen statt zwei Unterstriche enthalten. Wollen wir den vollen erweiterten Funktionsumfang nutzen, sind folgende Änderungen in config.inc.php erforderlich:

...
/* Storage database and tables */
// $cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
$cfg['Servers'][$i]['pmadb'] = 'phpmyadmin';
// $cfg['Servers'][$i]['bookmarktable'] = 'pma_bookmark';
$cfg['Servers'][$i]['bookmarktable'] = 'pma__bookmark';
// $cfg['Servers'][$i]['relation'] = 'pma_relation';
$cfg['Servers'][$i]['relation'] = 'pma__relation';
// $cfg['Servers'][$i]['table_info'] = 'pma_table_info';
$cfg['Servers'][$i]['table_info'] = 'pma__table_info';
// $cfg['Servers'][$i]['table_coords'] = 'pma_table_coords';
$cfg['Servers'][$i]['table_coords'] = 'pma__table_coords';
// $cfg['Servers'][$i]['pdf_pages'] = 'pma_pdf_pages';
$cfg['Servers'][$i]['pdf_pages'] = 'pma__pdf_pages';
// $cfg['Servers'][$i]['column_info'] = 'pma_column_info';
$cfg['Servers'][$i]['column_info'] = 'pma__column_info';
// $cfg['Servers'][$i]['history'] = 'pma_history';
$cfg['Servers'][$i]['history'] = 'pma__history';
// $cfg['Servers'][$i]['table_uiprefs'] = 'pma_table_uiprefs';
$cfg['Servers'][$i]['table_uiprefs'] = 'pma__table_uiprefs';
// $cfg['Servers'][$i]['tracking'] = 'pma_tracking';
$cfg['Servers'][$i]['tracking'] = 'pma__tracking';
// $cfg['Servers'][$i]['designer_coords'] = 'pma_designer_coords';
$cfg['Servers'][$i]['designer_coords'] = 'pma__designer_coords';
// $cfg['Servers'][$i]['userconfig'] = 'pma_userconfig';
$cfg['Servers'][$i]['userconfig'] = 'pma__userconfig';
// $cfg['Servers'][$i]['recent'] = 'pma_recent';
$cfg['Servers'][$i]['recent'] = 'pma__recent';
...

Damit die neue Konfiguration wirksam wird, melden wir uns schließlich noch in phpMyAdmin ab und wieder an.

phpMyAdmin absichern

Zur Absicherung von phpMyAdmin folgen wir den Empfehlungen aus dem Abschnitt Securing your phpMyAdmin installation der Originaldokumentation.

Die Empfehlung, die phpMyAdmin-Website nur per „HyperText Transfer Protocol Secure“ HTTPS zur Verfügung zu stellen und falls möglich „HTTP Strict Transport Security“ HSTS zu verwenden, können wir in Anlehnung an die Beschreibung zur LAMP-Server-Installation und den Artikel über die Absicherung des Webservers Apache umsetzen. Aus Gründen der Übersichtlichkeit gehen wir an dieser Stelle nicht weiter auf Details ein. Die veraltete Option $cfg['ForceSSL'] sollte man gemäß der phpMyAdmin-Dokumentation zu ForceSSL stattdessen nicht mehr nutzen.

Da wir Konfigurationsänderungen in der Datei config.inc.php direkt vornehmen, benötigen wir das Unterverzeichnis setup nicht und können es wie empfohlen löschen:

root@server:~# rm -r /usr/share/phpmyadmin/setup

Dadurch werden auch die zugehörigen Directory-Blöcke in der Apache-Konfigurationsdatei /etc/phpmyadmin/apache.conf überflüssig und können entfernt / auskommentiert werden:

...
#<Directory /usr/share/phpmyadmin/setup>
#  <IfModule mod_authn_file.c>
#  AuthType Basic
#  AuthName "phpMyAdmin Setup"
#  AuthUserFile /etc/phpmyadmin/htpasswd.setup
#  </IfModule>
#  Require valid-user
#</Directory>
...
#<Directory /usr/share/phpmyadmin/setup/lib>
#  Order Deny,Allow
#  Deny from All
#</Directory>

Bei dieser Gelegenheit können wir auch gleich die veraltete Syntax (siehe Apache-Dokumentation Upgrading to 2.4 from 2.2) im Directory-Block /usr/share/phpmyadmin/libraries ersetzen:

...
<Directory /usr/share/phpmyadmin/libraries>
#  Order Deny,Allow
#  Deny from All
  Require all denied
</Directory>
...

Die Änderungen werden nach einem Neustart von Apache übernommen:

root@server:~# service apache2 restart

Da die empfohlene Cookie-Authentifizierung mit Abfrage des MySQL-Benutzernamens und des Passworts voreingestellt ist, müssen wir an der Authentifizierungsmethode nichts ändern.

Falls Zugriffsbeschränkungen für Benutzer und / oder IP-Adressen gewünscht werden, kann man diese mit dem Parameter $cfg['Servers'][$i]['AllowDeny']['rules'] in Kombination mit $cfg['Servers'][$i]['AllowDeny']['order'] realisieren. Die Beschränkungen sind speziell auf den jeweiligen Anwendungsfall abzustimmen, sodass wir hier kein allgemeines Konfigurationsbeispiel angeben.

Zur weiteren Absicherung können wir eine HTTP-Basisauthentifizierung (siehe Apache-How-To Authentication and Authorization) einrichten, die der Cookie-Authentifizierung vorgeschaltet ist. Hierzu ergänzen wir den Directory-Block /usr/share/phpmyadmin in /etc/phpmyadmin/apache.conf:

...
<Directory /usr/share/phpmyadmin>
  ...
  #Require all granted
  AuthBasicProvider file
  AuthName "phpmyadmin"
  AuthType Basic
  AuthUserFile /etc/apache2/.htpasswd_phpmyadmin
  Require valid-user
  ...
</Directory>
...

Nach dem Speichern der Änderungen starten wir den Apache-Webserver neu:

root@server:~# service apache2 restart

Die Anweisungen für die Basisauthentifizierung sind auf die vorinstallierten Apache-Module mod_auth_basic, mod_authn_core, mod_authn_file und mod_authz_core verteilt. Mit AuthType legen wir den Authentifizierungstyp, hier Basic, fest. AuthBasicProvider file bewirkt, dass zur Authentifizierung auf die in AuthUserFile angegebene Benutzerdatei /etc/apache2/.htpasswd_phpmyadmin zurückgegriffen wird. Durch die Anweisung Require valid-user statt des auszukommentierenden Require all granted erhalten nur die in der Benutzerdatei zusammen mit ihren Passwörtern aufgeführten Benutzer Zugriff auf phpMyAdmin. In AuthName ist schließlich der Name phpmyadmin hinterlegt, der bei der Benutzer-/Passwortabfrage des Browsers angezeigt wird.

Zum Anlegen und Verwalten der Benutzerdatei .htpasswd_phpmyadmin können wir das Programm htpasswd aus der Sammlung mit Webserver-Hilfsprogrammen apache2-utils verwenden, die wir vorab noch installieren:

root@server:~# apt install apache2-utils
root@server:~# htpasswd -B -c /etc/apache2/.htpasswd_phpmyadmin theo
New password: ***
Re-type new password: ***

Der obige Aufruf von htpasswd erzeugt (Option -c) eine Textdatei /etc/apache2/.htpasswd_phpmyadmin mit einem Eintrag für den Benutzer theo und dem eingegebenen Passwort in verschlüsselter Form. Zur Passwortverschlüsselung kommt als sicherste der von htpasswd angebotenen Methoden bcrypt (Option -B) zum Einsatz. Weitere Benutzer können durch Programmaufrufe ohne die Option -c angelegt werden. Die Wahl der Benutzernamen und Passwörter ist dabei völlig unabhängig von der Benutzerverwaltung des Betriebssystems.

„Security by Obscurity“ einrichten

Durch die folgenden zusätzlichen „Security by Obscurity“-Maßnahmen können wir böswillige Attacken auf unsere phpMyAdmin-Installation erschweren.

Wie ein Auszug aus der Apache-Logdatei eines Servers im produktiven Betrieb zeigt, versuchen Angreifer systematisch Zugriff auf das phpMyAdmin-Installationsverzeichnis zu erlangen, wobei sie neben dem Standardordner phpmyadmin auch ähnliche Ordnernamen durchprobieren:

...
[...] [authz_core:error] [pid ...] [client ...] AH01630: client denied by server configuration: /var/www/phpMyAdmin
[...] [authz_core:error] [pid ...] [client ...] AH01630: client denied by server configuration: /var/www/phpmyadmin
[...] [authz_core:error] [pid ...] [client ...] AH01630: client denied by server configuration: /var/www/pma
[...] [authz_core:error] [pid ...] [client ...] AH01630: client denied by server configuration: /var/www/myadmin
[...] [authz_core:error] [pid ...] [client ...] AH01630: client denied by server configuration: /var/www/MyAdmin
...

Um den unerwünschten Zugang schwieriger zu gestalten, nennen wir das Unterverzeichnis phpmyadmin um, vorzugsweise in eine zufällige Zeichenfolge, die wir z. B. mit einem Passwortgenerator erzeugen. In der Apache-Konfigurationsdatei /etc/phpmyadmin/apache.conf ist dazu der Alias-Eintrag zu ändern. In unserem Beispiel wählen wir einen Namen, der aus einer Kombination aus 20 Großbuchstaben, Kleinbuchstaben und Ziffern besteht:

...
#Alias /phpmyadmin /usr/share/phpmyadmin
Alias /lRxm5RDkHRy2O7riVue9 /usr/share/phpmyadmin
...

Nach dem obligatorischen Neustart von Apache mit

root@server:~# service apache2 restart

ist phpMyAdmin nun unter

https://www.test.local/lRxm5RDkHRy2O7riVue9

zu erreichen.

Eine weitergehende „Security by Obscurity“-Maßnahme ist das Einrichten eines zusätzlichen virtuellen Apache-Hosts für eine eigene phpMyAdmin-Subdomain, die nur über einen vom HTTPS-Standard-Port 443 abweichenden Port wie z. B. 42443 ansprechbar ist. Hierzu erstellen wir in Analogie zur Datei default-ssl.conf aus dem Artikel zur LAMP-Server-Installation eine HTTPS-Konfigurationsdatei /etc/apache2/sites-available/lRxm5RDkHRy2O7riVue9.conf und ergänzen diese um Einträge aus /etc/phpmyadmin/apache.conf:

<VirtualHost *:42443>
  CustomLog ${APACHE_LOG_DIR}/access-phpmyadmin.log vhost_combined
  DocumentRoot /usr/share/phpmyadmin
  ErrorLog ${APACHE_LOG_DIR}/error-phpmyadmin.log
  Header always set Strict-Transport-Security "max-age=31536000"
  ServerAdmin webmaster@test.local
  ServerName lRxm5RDkHRy2O7riVue9.test.local
  SSLCertificateFile /etc/ssl/certs/lRxm5RDkHRy2O7riVue9.test.local.pem
  SSLCertificateKeyFile /etc/ssl/private/lRxm5RDkHRy2O7riVue9.test.local.key
  SSLEngine on
  <Directory /usr/share/phpmyadmin>
    AuthBasicProvider file
    AuthName "phpmyadmin"
    AuthType Basic
    AuthUserFile /etc/apache2/.htpasswd_phpmyadmin
    DirectoryIndex index.php
    Options FollowSymLinks
    Require valid-user
    <IfModule mod_php5.c>
      AddType application/x-httpd-php .php
      php_flag magic_quotes_gpc Off
      php_flag track_vars On
      php_flag register_globals Off
      php_admin_flag allow_url_fopen Off
      php_value include_path .
      php_admin_value upload_tmp_dir /var/lib/phpmyadmin/tmp
      php_admin_value open_basedir /usr/share/phpmyadmin/:/etc/phpmyadmin/:/var/lib/phpmyadmin/:/usr/share/php/php-gettext/:/usr/share/javascript/
    </IfModule>
  </Directory>
  <Directory /usr/share/phpmyadmin/libraries>
    Require all denied
  </Directory>
</VirtualHost>

Danach aktivieren wir die Konfiguration mit a2ensite und deaktivieren mit a2disconf den Link /etc/apache2/conf-available/phpmyadmin.conf auf die ursprüngliche Konfigurationsdatei /etc/phpmyadmin/apache.conf:

root@server:~# a2ensite lRxm5RDkHRy2O7riVue9.conf
root@server:~# a2disconf phpmyadmin.conf

Die Zertifikatsdateien lRxm5RDkHRy2O7riVue9.test.local.pem und lRxm5RDkHRy2O7riVue9.test.local.key kann man für ein lokales Netzwerk nach der Anleitung im Artikel Certificate Authority mit OpenSSL einrichten erzeugen. Die Vorgehensweise zur Generierung von Zertifikaten für den öffentlichen Internetzugang beschreibt der Beitrag SSL-Zertifikate mit Let’s Encrypt. Wir nutzen hier das Skript genservcert.sh aus erstgenanntem Artikel:

root@server:~# /root/genservcert.sh lRxm5RDkHRy2O7riVue9.test.local "Theo Test" ca_test.local
...
Enter pass phrase for /etc/ssl/private/ca_test.local.key: ***

Damit Apache den Port 42443 auch abfragt, fügen wir in der Port-Konfigurationsdatei /etc/apache2/ports.conf noch zwei Einträge Listen 42443 hinzu:

...
<IfModule ssl_module>
  Listen 443
  Listen 42443
</IfModule>
<IfModule mod_gnutls.c>
  Listen 443
  Listen 42443
</IfModule>
...

Schließlich starten wir Apache neu:

root@server:~# service apache2 restart

Nach eventuell für die neue Subdomain lRxm5RDkHRy2O7riVue9.test.local erforderlichen Anpassungsarbeiten an dem / den DNS-Server/n können wir die phpMyAdmin-Benutzeroberfläche mit

https://lRxm5RDkHRy2O7riVue9.test.local:42443

im Browser aufrufen.

Ubuntu Server 14.04 – SSL-Zertifikate mit Let’s Encrypt

Zur gesicherten Übertragung von Webseiten mit dem „HyperText Transfer Protocol Secure“ HTTPS werden SSL(„Secure Sockets Layer“)- bzw. TLS(„Transport Layer Security“)-Zertifikate benötigt. Diese werden von vertrauenswürdigen Zertifizierungsstellen ausgegeben und sind überwiegend kostenpflichtig. Seit Ende 2015 gibt es mit Let’s Encrypt eine interessante Alternative zu kommerziellen Anbietern.

Nachfolgend werden wir Let’s Encrypt für die Generierung von Zertifikaten auf einem Ubuntu-Server-14.04-System einrichten und eine HTTPS-Musterkonfiguration für den Webserver Apache anlegen. Um die Zertifikatsverwaltung zu erleichtern, erstellen wir ein Skript zur automatischen Zertifikatserneuerung. Abschließend behandeln wir die Aktualisierung und die Deinstallation des Let’s-Encrypt-Clients.

Die Anleitung bezieht sich auf Let’s Encrypt in der Beta-Phase Stand Januar 2016. Weitere Einzelheiten und aktuelle Änderungen enthält die Original-Dokumentation.

Voraussetzungen

Wir haben unseren Server z. B. gemäß den Anleitungen zur Ubuntu-Server-Basisinstallation und zur LAMP-Server-Installation konfiguriert und durch Updates / Upgrades auf den neuesten Stand gebracht. Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

Let’s Encrypt installieren

Let’s Encrypt verwaltet Zertifikate mit Hilfe eines speziellen ACME(„Automatic Certificate Management Environment“)-Clients. Unter Ubuntu Server 14.04 kann der Let’s-Encrypt-Client (noch) nicht mit der Paketverwaltung APT („Advanced Packaging Tool“) installiert werden. Stattdessen benötigen wir die Versionsverwaltung Git:

root@server:~# apt install git

Mit git clone kopieren wir die Paketquellen in das (frei wählbare) Verzeichnis /root/sources/letsencrypt, das während des Vorgangs – falls noch nicht vorhanden – automatisch angelegt wird:

root@server:~# git clone https://github.com/letsencrypt/letsencrypt /root/sources/letsencrypt

Die Installation wird beim erstmaligen Aufruf des Let’s-Encrypt-Clients mit letsencrypt-auto gestartet. Mit der Option ‐‐help können wir uns hierbei gleich einen ersten Überblick über die Aufrufparameter verschaffen:

root@server:~# /root/sources/letsencrypt/letsencrypt-auto --help

Während des Installationsvorgangs erscheinen folgende Warnmeldungen:

...
Updating letsencrypt and virtual environment dependencies...../root/.local/share/letsencrypt/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
/root/.local/share/letsencrypt/local/lib/python2.7/site-packages/pip/_vendor/requests/packages/urllib3/util/ssl_.py:90: InsecurePlatformWarning: A true SSLContext object is not available. This prevents urllib3 from configuring SSL appropriately and may cause certain SSL connections to fail. For more information, see https://urllib3.readthedocs.org/en/latest/security.html#insecureplatformwarning.
  InsecurePlatformWarning
.
...

Grund ist, dass bei Ubuntu Server 14.04 mit APT als aktuellste Python-Version nur 2.7.6 installiert wird, letsencrypt-auto aber Version 2.7.9 oder höher erwartet. Die obigen Meldungen traten bei dem für diesen Artikel verwendeten Testsystem allerdings nur beim ersten Start von Let’s Encrypt auf und können daher vermutlich ignoriert werden.

SSL-Zertifikat erstellen

Zum Beantragen eines SSL-Zertifikats und zur automatischen Installation der zugehörigen Schlüssel- und Zertifikatsdateien auf unserem Server rufen wir letsencrypt-auto mit dem Parameter certonly und der Option standalone auf. Vorab müssen wir den Webserver Apache anhalten und danach wieder starten, da der Let’s-Encrypt-Client zur Validierung der Domain den Port 80 nutzt:

root@server:~# service apache2 stop
root@server:~# /root/sources/letsencrypt/letsencrypt-auto certonly --standalone --agree-tos --email webmaster@test.local --domain www.test.local --rsa-key-size 4096
root@server:~# service apache2 start

Mit der Option agree-tos stimmen wir den Nutzungsbedingungen zu. An die per email übergebene E-Mail-Adresse werden z. B. Benachrichtigungen zu anstehenden Zertifikatserneuerungen geschickt. Diese beiden Optionen müssen nur beim ersten Aufruf angegeben werden.

Der verwendete Domainname www.test.local und die E-Mail-Adresse webmaster@test.local sind Platzhalter, die wir durch die realen Daten unseres eigenen Servers ersetzen. Zertifikatsanfragen für nicht öffentlich zugängliche Domains wie test.local werden von Let’s Encrypt (sinnvollerweise) abgewiesen.

Die Option rsa-key-size 4096 erhöht die Schlüssellänge des privaten Schlüssels von den voreingestellten 2048 bit auf sicherere 4096 bit.

Da wir alle Optionen direkt beim Aufruf von letsencrypt-auto certonly übergeben, erhalten wir nicht die ansonsten angezeigten Menüs.

Apache konfigurieren

Die von Let’s Encrypt erzeugten Schlüssel und Zertifikate werden in /etc/letsencrypt/live/www.test.local als symbolische Links auf die jeweils aktuellen Versionen hinterlegt. Die Datei privkey.pem beinhaltet den privaten Schlüssel, die Datei cert.pem das Serverzertifikat. In chain.pem werden alle Zertifikate der vom Root-Zertifikat ausgehenden Zertifikatskette mit Ausnahme des Serverzertifikats gespeichert. fullchain.pem enthält zusätzlich das Serverzertifikat.

Abhängig vom Versionsstand benötigt Apache neben der Schlüsseldatei unterschiedliche Zertifikatsdateien. Für das für diesen Artikel genutzte Testsystem mit Apache 2.4.7 lauten die entsprechenden Einträge in der Konfigurationsdatei:

...
SSLCertificateChainFile /etc/letsencrypt/live/www.test.local/chain.pem
SSLCertificateFile /etc/letsencrypt/live/www.test.local/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/www.test.local/privkey.pem
...

Bei neueren Versionen kommt man mit zwei Zeilen aus:

...
SSLCertificateFile /etc/letsencrypt/live/www.test.local/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/www.test.local/privkey.pem
...

In Anlehnung an den Artikel zur LAMP-Server-Installation sieht die komplette Definition eines virtuellen Hosts www.test.local in der HTTPS-Standardkonfigurationsdatei /etc/apache2/sites-available/default-ssl.conf dann beispielsweise folgendermaßen aus:

<VirtualHost *:443>
  CustomLog ${APACHE_LOG_DIR}/access-html-ssl.log vhost_combined
  DocumentRoot /var/www/html
  ErrorLog ${APACHE_LOG_DIR}/error-html-ssl.log
  ServerAdmin webmaster@test.local
  ServerName www.test.local
  SSLCertificateChainFile /etc/letsencrypt/live/www.test.local/chain.pem
  SSLCertificateFile /etc/letsencrypt/live/www.test.local/cert.pem
  SSLCertificateKeyFile /etc/letsencrypt/live/www.test.local/privkey.pem
  SSLEngine on
  <Directory /var/www/html>
    Require all granted
  </Directory>
</VirtualHost>

Schließlich aktivieren wir noch, falls nicht bereits vorher geschehen, die HTTPS-Konfiguration und starten den Webserver neu:

root@server:~# a2ensite default-ssl.conf
root@server:~# service apache2 restart

Website testen

Für einen einfachen Test unserer Website können wir sie in einem Webbrowser aufrufen. In der Adressleiste von Firefox sollte dann z. B. links neben der Adresse ein grünes Sperrschloss angezeigt werden. Mit einem Klick auf die Ausrufezeichen-Schaltfläche vor dem Schloss erhalten wir weitere Details über unser Let’s-Encrypt-Zertifikat.

Zertifikate erneuern

Die von Let’s Encrypt ausgestellten SSL-Zertifikate sind für einen Zeitraum von 90 Tagen gültig. Zur Zertifikatserneuerung ruft man den Let’s-Encrypt-Client erneut auf:

root@server:~# /root/sources/letsencrypt/letsencrypt-auto certonly --standalone --domain www.test.local --rsa-key-size 4096 --renew-by-default

Die beiden Angaben agree-tos und email können entfallen, da die zugehörigen Werte beim ersten Aufruf von letsencrypt-auto gespeichert werden. Mit der zusätzlichen Option renew-by-default wird eine Erneuerung ohne weitere Rückfragen erzwungen.

Damit nicht versehentlich Zertifikate ablaufen und ungültig werden, bietet es sich an, die manuelle Zertifikatserneuerung zeitgesteuert zu automatisieren. Dazu legen wir ein kleines Shellskript /root/letsencrypt_renew.sh an:

#!/bin/bash
if test $(find /etc/letsencrypt/live/$1 -mtime +$2 -name privkey.pem); then
  service apache2 stop;
  /root/sources/letsencrypt/letsencrypt-auto certonly --standalone --domain $1 --rsa-key-size 4096 --renew-by-default
  service apache2 start;
fi

Das Skript überprüft, ob die Schlüsseldatei privkey.pem für den als ersten Aufrufparameter übergebenen Domainnamen $1 älter als die Anzahl von Tagen im zweiten Aufrufparameter $2 ist und startet in diesem Fall die Erneuerungsanfrage.

letsencrypt_renew.sh muss für den Benutzer root ausführbar sein, daher ändern wir noch die Zugriffsrechte:

root@server:~# chmod 0700 /root/letsencrypt_renew.sh

Unter Ubuntu ermöglicht der Cron-Dienst das Ausführen von Befehlen, Skripten und Programmen zu vorgegebenen Zeitpunkten. Die zugehörigen Angaben werden dabei in sogenannten „crontab“-Dateien hinterlegt, die benutzerspezifisch angelegt werden können. Für den Benutzer root rufen wir hierzu den Befehl crontab mit der Editier-Option -e wie folgt auf:

root@server:~# crontab -e -u root

Beim ersten Start wird der Editor zur Bearbeitung abgefragt und diese Information für weitere Aufrufe gespeichert. Nach der Editor-Auswahl ergänzen wir am Ende der Datei folgende Zeile:

...
00 03 * * * /root/letsencrypt_renew.sh www.test.local 30

Hiermit weisen wir den Cron-Dienst an, täglich um 3 Uhr morgens zu prüfen, ob das Zertifikat für die Domain www.test.local älter als 30 Tage ist, um es dann gegebenenfalls zu erneuern. Wir orientieren uns hierbei an der Let’s-Encrypt-Dokumentation, die zur Vermeidung von Problemen einen Zeitraum von einem Monat zwischen zwei Erneuerungsanfragen nennt.

Let’s-Encrypt-Client aktualisieren

Bei jedem Aufruf von letsencrypt-auto wird geprüft, ob eine neuere Version des Let’s-Encrypt-Clients vorliegt und dieser dann gegebenenfalls aktualisiert. Falls keine automatische Aktualisierung gewünscht ist, kann man stattdessen das Programm letsencrypt verwenden, das dieselben Parameter und Optionen nutzt. Das Update erfolgt dann wie die ursprüngliche Installation mit Hilfe der Versionsverwaltung Git. Dazu rufen wir git pull mit dem Installationsverzeichnis /root/sources/letsencrypt als Parameter auf:

root@server:~# git -C /root/sources/letsencrypt pull

Let’s Encrypt deinstallieren

Für den unwahrscheinlichen Fall, dass wir Let’s Encrypt nicht mehr benötigen, müssen wir die Deinstallation mangels eines automatisierten Verfahrens manuell durchführen. Hierzu löschen wir das Installationsverzeichnis sowie die Ordner für die Konfiguration, die Statusinformationen, die Logdateien und die Paketquellen:

root@server:~# rm -r /root/.local/share/letsencrypt /etc/letsencrypt /var/lib/letsencrypt /var/log/letsencrypt /root/sources/letsencrypt

Falls die Versionsverwaltung Git nicht von anderer Software genutzt wird, kann man sie mit apt remove ebenfalls entfernen:

root@server:~# apt remove git

Hinweise auf die von Let’s Encrypt zusätzlich installierten Programmpakete finden sich im Bootstrap-Skript /root/sources/letsencrypt/bootstrap/_deb_common.sh. Vor der Deinstallation dieser Programme sind natürlich etwaige Abhängigkeiten zu anderen Softwarepaketen zu prüfen.

Ubuntu Server 14.04 – LAMP-Server installieren und konfigurieren

Basis für viele Webauftritte mit dynamischen Inhalten ist das Softwarepaket LAMP. Der LAMP-Server läuft unter dem Betriebssystem Linux und beinhaltet den Webserver Apache, das Datenbanksystem MySQL und die Skriptsprache PHP.

Im Folgenden werden wir LAMP auf einem Server mit der Linux-Variante Ubuntu Server 14.04 installieren und danach Apache und MySQL für einen sicheren Betrieb anpassen. Hierzu realisieren wir ein restriktives Apache-Sicherheitsmodell und richten eine automatische Umleitung ein, die den Zugriff auf die Standard-Website nur über das verschlüsselnde „HyperText Transfer Protocol Secure“ HTTPS zulässt. Neben dieser Grundabsicherung sind weitergehende Maßnahmen empfehlenswert, die im Artikel Ubuntu Server 14.04 – Webserver Apache absichern beschrieben werden.

Voraussetzungen

Wir haben unseren Server z. B. gemäß der Anleitung zur Basisinstallation für Ubuntu Server 14.04 installiert und durch Updates / Upgrades auf den neuesten Stand gebracht. Der Server hat die IPv4-Adresse 192.168.0.1 und den Rechnernamen server. Er gehört zur Domain test.local und ist auch unter www.test.local erreichbar. Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

LAMP-Server installieren

Zur Installation des LAMP-Servers benötigen wir nur einen Befehl:

root@server:~# apt install lamp-server^

Wichtig ist die Angabe des Caret-Zeichens ^ am Ende. Die Anweisung apt install wirkt damit im Wesentlichen wie ein Aufruf von tasksel install. Es wird nicht nur ein Programmpaket eingerichtet, sondern alle Pakete, die für eine bestimmte Serveraufgabe, hier LAMP, erforderlich sind.

Während des Installationsvorgangs wird ein Passwort für den MySQL-Benutzer root abgefragt, das wir unbedingt setzen sollten, da das root-Konto über erweiterte Rechte verfügt und man damit das Datenbanksystem komplett administrieren kann.

Alternativ können wir die Programmpakete auch einzeln installieren:

root@server:~# apt install apache2 libapache2-mod-php5 mysql-server php5-mysql

Webserver Apache konfigurieren

Für die Dauer der Konfigurationsarbeiten ist es empfehlenswert, den Apache-Server zu deaktivieren. Hierzu stoppen wir den entsprechenden Dienst apache2:

root@server:~# service apache2 stop

Standard-Website ersetzen

Apache kann mit Hilfe sogenannter „virtueller Hosts“ mehrere Websites verwalten, deren Dateien in Unterordnern des Verzeichnisses /var/www abgelegt werden können. Bei der Installation wird im Ordner /var/www/html eine Standard-Website mit der Datei index.html eingerichtet. Diese Datei ersetzen wir zunächst durch die Ordner und Dateien unseres Webauftritts. Dann beschränken wir mit den folgenden Befehlen den Vollzugriff auf den Benutzer root und erlauben zusätzlich Mitgliedern der Gruppe www-data, mit deren Rechten der Apache-Serverdienst läuft, lesenden Zugriff:

root@server:~# chown -R root:www-data /var/www/html
root@server:~# find /var/www/html \( -type d -exec chmod 0750 {} + \) -o \( -type f -exec chmod 0640 {} + \)

Restriktives Sicherheitsmodell umsetzen

Das per Apache-Voreinstellung realisierte Sicherheitsmodell erlaubt externe Zugriffe auf die Verzeichnisse /usr/share und /var/www und definiert zusätzliche dort zulässige Aktionen. Bei unserem restriktiven Ansatz werden wir den Verzeichniszugriff zunächst komplett sperren und nur bei Bedarf Ordner für Websites explizit freigeben. Hierzu führen wir die nachfolgenden Änderungen in der Hauptkonfigurationsdatei /etc/apache2/apache2.conf durch:

...
<Directory />
        #Options FollowSymLinks
        Options None
        AllowOverride None
        Require all denied
</Directory>

#<Directory /usr/share>
#       AllowOverride None
#       Require all granted
#</Directory>

#<Directory /var/www/>
#       Options Indexes FollowSymLinks
#       AllowOverride None
#       Require all granted
#</Directory>
...

Die Einstellungen für einzelne Verzeichnisse werden bei Apache innerhalb von Directory-Blöcken definiert. Im ersten Directory-Block werden durch die Anweisung Require all denied alle externen Zugriffe auf das Wurzelverzeichnis / inklusive aller Unterordner gesperrt. Diese Sperre wird mit Require all granted für die Verzeichnisse /usr/share und /var/www nachfolgend wieder aufgehoben. Um dies zu verhindern, kommentieren wir die beiden zugehörigen Blöcke mit # komplett aus.

In der Options-Anweisung legt man fest, welche zusätzlichen Aktionen in einem Verzeichnis erlaubt sind. Der Parameter FollowSymLinks innerhalb der Wurzelverzeichniskonfiguration ermöglicht Apache, symbolischen Links zu folgen. Da durch diese und andere Optionen in bestimmten Szenarien Sicherheitslücken entstehen können, deaktivieren wir sie mit Options None komplett.

Neben der Definition in Apache-Konfigurationsdateien können Verzeichniseinstellungen auch in .htaccess-Dateien im jeweiligen Website-Ordner gespeichert werden. Dies ist beispielsweise sinnvoll, falls der Website-Betreiber bei einem angemieteten Server keinen direkten Zugriff auf die Webserverkonfiguration hat. In der Apache-Anleitung zu .htaccess wird empfohlen, .htaccess-Dateien aus Performance- und Sicherheitsgründen möglichst nicht zu verwenden. Dieses Verhalten wird bereits während der Installation mit der Anweisung AllowOverride None als Standard festgelegt.

Apache-Einstellungen für Standard-Website ändern

Die Eigenschaften der Standard-Website von Apache werden in der Datei /etc/apache2/sites-available/000-default.conf definiert. Deren Inhalt ersetzen wir durch folgende Anweisungen:

<VirtualHost *:80>
  CustomLog ${APACHE_LOG_DIR}/access-html.log vhost_combined
  DocumentRoot /var/www/html
  ErrorLog ${APACHE_LOG_DIR}/error-html.log
  ServerAdmin webmaster@test.local
  ServerName www.test.local
  <Directory /var/www/html>
    Require all granted
  </Directory>
</VirtualHost>

Die spezifischen Angaben zu einzelnen Websites sind in VirtualHost-Blöcken zusammengefasst. In unserem Beispiel wertet Apache die Konfigurationseinträge zwischen <VirtualHost *:80> und </VirtualHost> aus, sobald eine Anfrage an den unter ServerName angegebenen virtuellen Host www.test.local an Port 80 gestellt wird. Port 80 wird standardmäßig vom „HyperText Transfer Protocol“ HTTP zur Übermittlung von Webseiten genutzt. Statt des Platzhalters * kann man auch eine dedizierte IP-Adresse angeben, an der der Server auf Anfragen reagieren soll.

In CustomLog und ErrorLog legen wir die Logdateien für Zugriffe und Fehler / Warnungen fest. Falls Zugriffe vom Standardformat abweichend gespeichert werden sollen, kann man die CustomLog-Anweisung mit einem der in apache2.conf vordefinierten Protokollformate ergänzen, in unserem Beispiel vhost_combined. Die Logdateinamen wählen wir abweichend von den voreingestellten Namen other_vhosts_access.log und error.log, um bei mehreren Websites eine getrennte Auswertung zu ermöglichen.

In ServerAdmin ist die E-Mail-Adresse hinterlegt, die Apache als Kontaktmöglichkeit in Fehlermeldungen an einen Client, in der Regel also einen Webbrowser, einfügt.

Der Eintrag DocumentRoot beinhaltet den Standardordner für Website-Inhalte /var/www/html, für den ein zusätzlicher Directory-Block mit verzeichnisbezogenem Inhalt definiert ist. Die Anweisung Require all granted bewirkt, dass alle Zugriffe auf Dateien innerhalb dieses Verzeichnisses inklusive eventueller Unterordner zulässig sind.

Weitere Details zu den Konfigurationsparametern finden sich in den Abschnitten Apache-Kernfunktionen und Log Files der Apache-Dokumentation.

Optional zusätzlichen Standardhost definieren

Falls der Apache-Server eine Anfrage erhält, die zu keinem virtuellen Host passt, werden die Einstellungen des ersten in der Konfiguration aufgeführten Hosts verwendet, der den Standardhost darstellt. Ruft ein Client also die Website server.test.local oder direkt die IP-Adresse 192.168.0.1 auf, nutzt Apache automatisch die Daten für www.test.local. Möchte man stattdessen unpassende Zugriffe mit einer Fehlermeldung im Webbrowser quittieren, bietet sich die Anlage eines speziellen Standardhosts vor allen anderen VirtualHost-Definitionen in 000-default.conf an:

<VirtualHost *:80>
  CustomLog ${APACHE_LOG_DIR}/access.log vhost_combined
  DocumentRoot /var/www
  ErrorLog ${APACHE_LOG_DIR}/error.log
  ServerName dummy
</VirtualHost>
...

Als DocumentRoot geben wir das vor externen Anfragen geschützte Verzeichnis /var/www an. Der Eintrag bei ServerName ist nicht von Bedeutung, sollte aber selbstverständlich nicht mit einem der anderen virtuellen Hosts übereinstimmen.

Die Definition unseres Standardhosts richtet sich nach der Apache-Dokumentation zur Unterstützung namensbasierter virtueller Hosts:

Innerhalb jedes <VirtualHost>-Blocks benötigen Sie zumindestens eine ServerName-Anweisung, um zu bestimmen, welcher Host bedient wird, und eine DocumentRoot-Anweisung, um anzugeben, wo im Dateisystem der Inhalt des Hosts abgelegt ist.

Es scheint aber möglich zu sein, einen Standard-VirtualHost-Block auch ohne DocumentRoot und ServerName anzulegen. Zumindest lief unser Testsystem selbst mit einer komplett leeren VirtualHost-Definition noch einwandfrei.

Apache-Zugriff per HTTPS aktivieren

Das Protokoll HTTP hat den Nachteil, dass Webseiten im Klartext übertragen werden. Um sensible Daten wie Passwörter besser zu schützen, eignet sich die Weiterentwicklung „HyperText Transfer Protocol Secure“ HTTPS. HTTPS sichert die Inhalte durch Verschlüsselung mit SSL („Secure Sockets Layer“) bzw. TLS („Transport Layer Security“) und gewährleistet zudem deren Integrität. Voraussetzung für die Nutzung von SSL / TLS ist ein von einer Zertifizierungsstelle ausgestelltes digitales (Server-)Zertifikat. Dieses beinhaltet eine Datei mit dem eigentlichen Zertifikat und eine Datei mit dem zugehörigen privaten Schlüssel.

Damit Apache Anfragen per HTTPS bearbeiten kann, müssen wir vorab mit a2enmod das Modul mod_ssl aktivieren:

root@server:~# a2enmod ssl

Die HTTPS-Einstellungen der Apache-Standard-Website sind in der Datei /etc/apache2/sites-available/default-ssl.conf gespeichert, die wir in Anlehnung an die HTTP-Konfigurationsdatei 000-default.conf ohne optionalen Standardhost aufbauen:

<VirtualHost *:443>
  CustomLog ${APACHE_LOG_DIR}/access-html-ssl.log vhost_combined
  DocumentRoot /var/www/html
  ErrorLog ${APACHE_LOG_DIR}/error-html-ssl.log
  ServerAdmin webmaster@test.local
  ServerName www.test.local
  SSLCertificateFile /etc/ssl/certs/www.test.local.pem
  SSLCertificateKeyFile /etc/ssl/private/www.test.local.key
  SSLEngine on
  <Directory /var/www/html>
    Require all granted
  </Directory>
</VirtualHost>

In der VirtualHost-Anweisung tragen wir statt 80 den Standardport für HTTPS-Verbindungen 443 ein. Mit SSLEngine on wird die Verarbeitung von HTTPS-Anfragen aktiviert. In SSLCertificateFile wird der Speicherort des Serverzertifikats angegeben. SSLCertificateKeyFile enthält den Dateinamen des zugehörigen privaten Schlüssels.

Die Dateien www.test.local.pem und www.test.local.key haben wir in unserem Beispiel nach der Anleitung Ubuntu Server 14.04 – Certificate Authority mit OpenSSL einrichten erzeugt. Sie eignen sich allerdings nur für den Einsatz in einem lokalen Netzwerk. Bei einem öffentlich zugänglichen Webserver benötigen wir ein Zertifikat einer anerkannten Zertifizierungsstelle, das von gängigen Webbrowsern ohne zusätzliche Maßnahmen als vertrauenswürdig eingestuft wird. Solch ein Zertifikat kann man entweder von kommerziellen Anbietern oder kostenlos von alternativen SSL-Zertifizierungstellen wie Let’s Encrypt beziehen. Die Zertifikatserstellung mit Let’s Encrypt ist Gegenstand des Beitrags Ubuntu Server 14.04 – SSL-Zertifikate mit Let’s Encrypt.

Im Verzeichnis /etc/apache2/sites-available können Konfigurationsdateien für eine oder mehrere Websites und verschiedene Protokolle abgespeichert und bei Bedarf mit dem Befehl a2ensite aktiviert werden. Hiermit wird im Verzeichnis /etc/apache2/sites-enabled ein symbolischer Link auf die Konfigurationsdatei erzeugt. Die HTTP-Standardkonfiguration 000-default.conf ist bereits nach der Installation wirksam. Die HTTPS-Standardkonfiguration default-ssl.conf aktivieren wir mit:

root@server:~# a2ensite default-ssl.conf

Ausführliche Informationen über die SSL- bzw. TLS-Unterstützung von Apache sind in der Dokumentation über mod_ssl nachzulesen.

Automatische Umleitung von HTTP auf HTTPS einrichten

Bei Webbrowsern wird die Eingabe einer Webadresse ohne Angabe des Protokolls üblicherweise automatisch durch das Voranstellen von http:// zu einem „Uniform Resource Locator“ URL ergänzt. Dieser Komfort geht allerdings auf Kosten der Sicherheit, da nicht das verschlüsselnde Protokoll HTTPS genutzt wird. Man kann das Problem serverseitig mit Hilfe des Apache-Moduls mod_rewrite lösen, das eine Umleitung von URLs ermöglicht.

Die Aktivierung von mod_rewrite erfolgt wie beim SSL-Modul mittels a2enmod:

root@server:~# a2enmod rewrite

Zur URL-Umleitung ergänzen wir die Datei /etc/apache2/sites-available/000-default.conf mit folgenden Anweisungen innerhalb des VirtualHost-Blocks für /var/www/html:

<VirtualHost *:80>
  ...
  RewriteEngine on
  RewriteCond %{HTTPS} off
  RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
  ...
</VirtualHost>

Mit RewriteEngine on wird mod_rewrite aktiviert. RewriteCond bestimmt, unter welchen Bedingungen eine Umleitung stattfindet. In unserem Fall wird mit %{…} die Servervariable HTTPS abgefragt, die den Wert off hat, falls die Verbindung nicht mit SSL / TLS aufgebaut wurde. Ist diese Bedingung erfüllt, werden mit RewriteRule alle Dateien (.*) auf den URL https://%{HTTP_HOST}%{REQUEST_URI} umgeleitet. Die Servervariable HTTP_HOST beinhaltet hierbei den Servernamen, beispielsweise www.test.local. Im Platzhalter REQUEST_URI ist der Dateiname mit vollständiger Pfadangabe, z. B. /sub/index.html, abgelegt.

Die Anweisung RewriteRule enthält am Ende zusätzliche durch Kommas getrennte Parameter, die sogenannten „RewriteRule Flags“, in eckigen Klammern. Der „Redirect“-Parameter R meldet dem aufrufenden Programm mit dem Statuscode 301, dass die Webseite dauerhaft unter der neuen Adresse zu erreichen ist, auf die die Umleitung verweist. Suchmaschinen können so beim Crawlen die neue statt der ursprünglichen Adresse hinterlegen. Der „Last“-Parameter L sorgt dafür, dass eventuell vorhandene weitere RewriteRule-Befehle nicht abgearbeitet werden. Er wäre also streng genommen in unserer einfachen Konstellation nicht erforderlich.

Alternativ zur Lösung mit mod_rewrite kann man auch die Redirect-Anweisung aus dem vorinstallierten Apache-Modul mod_alias verwenden. Im VirtualHost-Block für /var/www/html wäre dazu folgende Zeile einzufügen:

<VirtualHost *:80>
  ...
  Redirect permanent / https://www.test.local/
  ...
</VirtualHost>

„Security by Obscurity“-Maßnahmen

Die nachfolgenden „Security by Obscurity“-Maßnahmen bewirken keine erhöhte Sicherheit im eigentlichen Sinne, können aber potentiellen Angreifern die Arbeit durch das Vorenthalten von Informationen ein wenig erschweren.

Um die Ausgabe von Apache auf das Nötigste zu beschränken, führen wir in der Datei /etc/apache2/conf-available/security.conf folgende Änderungen durch:

...
#ServerTokens OS
ServerTokens Prod
...
#ServerSignature On
ServerSignature Off
...

Die Option ServerTokens bestimmt den Umfang der im Nachrichtenkopf („Header“) der Antwort auf eine HTTP-GET-Anfrage eines Clients enthaltenen Serverinformationen. Mit der Voreinstellung ServerTokens OS überträgt Apache im Eintrag Server des Antwort-Headers den Namen des Webservers, dessen Versionsnummer und das Betriebssystem, beim Testsystem Apache/2.4.7 (Ubuntu). Nach der Änderung auf Prod reduziert sich die Ausgabe auf den Namen Apache. Bei der Einstellung ServerTokens Full würde der Server-Eintrag zusätzlich noch Informationen über die verwendete PHP-Version preisgeben.

Die Option ServerSignature beinflusst die Ausgabe von Serverinformationen an Client-Programme wie z. B. Fehlermeldungen im Webbrowser. Durch das Ausschalten mit dem Wert Off wird eine Signatur wie Apache/2.4.7 (Ubuntu) Server at server.test.local Port 80 vollständig unterdrückt.

In der Regel wird bei Eingabe eines Verzeichnisnamens im Webbrowser die Startseite des Verzeichnisses wie index.html oder index.php ausgegeben. Zulässige Namen für Startseiten sind in der Datei /etc/apache2/mods-available/dir.conf unter dem DirectoryIndex-Eintrag gespeichert. Falls keine der dort aufgeführten Dateien existiert, liefert Apache in der Ausgangskonfiguration für die Ordner innerhalb des Website-Verzeichnisses /var/www/ einen automatisch generierten Index mit dem Verzeichnisinhalt zurück. Diese Daten können wichtige Anhaltspunkte für einen Angriff auf unseren Server geben und sollten daher nicht öffentlich gemacht werden.

Die Ausgabe des Verzeichnisindex wird durch den Parameter Indexes im Konfigurationseintrag Options gesteuert. Durch die Angabe von Options None in der Hauptkonfigurationsdatei /etc/apache2/apache2.conf haben wir die automatische Anzeige des Verzeichnisinhalts bereits abgeschaltet und es sind keine weiteren Schritte erforderlich.

Website freigeben

Bevor wir unsere Website für den öffentlichen Betrieb freigeben, testen wir noch die Syntax der Konfigurationsdateien mit dem „Apache HTTP server control interface“ apache2ctl:

root@server:~# apache2ctl configtest

Sollte das Ergebnis nicht Syntax OK lauten, korrigieren wir den / die Fehler anhand der angezeigten Fehlermeldung(en) und lassen den Test erneut durchlaufen. Nach erfolgreicher Syntaxprüfung können wir dann den anfangs gestoppten Apache-Dienst starten:

root@server:~# service apache2 start

Falls Apache bereits läuft, führen wir stattdessen einen Neustart durch:

root@server:~# service apache2 restart

MySQL-Server anpassen

Sicherheit erhöhen

Zum Installationsumfang von MySQL gehört das Sicherheitsskript mysql_secure_installation, mit dem man den Datenbankserver durch Ändern verschiedener Einstellungen zusätzlich absichern kann. Die Möglichkeiten sind im Einzelnen:

  1. Ein Passwort für den Benutzer root setzen.
  2. Anonyme Benutzerkonten entfernen.
  3. Den Zugriff auf das root-Konto aus dem Netzwerk unterbinden und auf localhost begrenzen.
  4. Testdatenbanken löchen.

Bei der für diesen Artikel genutzten MySQL-Version 5.5.46 waren die Maßnahmen 1, 2 und 4 nicht nötig, da während der LAMP-Installation das root-Passwort bereits gesetzt wurde und weder anonyme Benutzerkonten noch Testdatenbanken angelegt wurden. Die Zugriffsbeschränkung für den Benutzer root unter Punkt 3 realisieren wir wie folgt:

root@server:~# mysql -p -u root
Enter password: ***
...
mysql> DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1');
mysql> exit

Bei anderen MySQL-Versionen können zusätzlich die Maßnahmen 2 und / oder 4 erforderlich sein. Die entsprechenden Befehle in der MySQL-Kommandozeile lauten:

...
mysql> DELETE FROM mysql.user WHERE User='';
mysql> DROP DATABASE test;
mysql> DELETE FROM mysql.db WHERE Db='test'
...

Möchte man die Änderungen lieber interaktiv durchführen, kann man natürlich auch das Sicherheitsskript ausführen:

root@server:~# mysql_secure_installation

Warnungen und Fehler beseitigen

Treten beim Start / Neustart des MySQL-Servers Probleme auf, so werden diese als Warn- und Fehlermeldungen in Logdateien protokolliert. In der Ausgangskonfiguration von MySQL finden sich hier zwei – allerdings unkritische – Warnungen.

In der Logdatei /var/log/mysql/error.log ist die nachfolgende Meldung aufgeführt:

...
... [Warning] Using unique option prefix myisam-recover instead of myisam-recover-options is deprecated and will be removed in a future release. Please use the full name instead.
...

Die Logdatei /var/log/upstart/mysql.log enthält den Eintrag:

...
... [Warning] Using unique option prefix key_buffer instead of key_buffer_size is deprecated and will be removed in a future release. Please use the full name instead.
...

Um die beiden Warnmeldungen abzustellen, ändern wir die entsprechenden Optionsnamen in der MySQL-Konfigurationsdatei /etc/mysql/my.cnf:

...
#key_buffer             = 16M
key_buffer_size         = 16M
...
#myisam-recover         = BACKUP
myisam-recover-options  = BACKUP
...

Abhängig von der Serverkonfiguration kann in /var/log/upstart/mysql.log folgender Fehler auftauchen, der den Serverbetrieb aber ebenso wenig beeinträchtigt wie die beiden vorangehend genannten Warnungen:

...
^G/usr/bin/mysqladmin: connect to server at 'localhost' failed
error: 'Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (...)'
Check that mysqld is running and that the socket: '/var/run/mysqld/mysqld.sock' exists!
...

Ursache ist das in der Upstart-Konfigurationsdatei /etc/init/mysql.conf definierte Skript post-start. Dieses Skript wird aufgerufen, nachdem der MySQL-Serverprozess /usr/sbin/mysqld in den Status spawned übergegangen ist und prüft, ob der Datenbankserver erreichbar ist. Nach erfolgreichem Abarbeiten des Skripts wechselt der Prozess in den Status started und ist einsatzbereit.

Das Problem tritt auf, wenn der MySQL-Server sich beim Aufruf von post-start noch nicht vollständig im Status spawned befindet und daher mit mysqladmin ping nicht ansprechbar ist. Abhilfe schafft das Einfügen einer Wartezeit am Anfang von post-start mit dem Befehl sleep. Bei dem für diesen Beitrag eingesetzten Server erwies sich ein Wert von 3 Sekunden als ausreichend:

...
post-start script
   sleep 3
...

Damit die Änderungen wirksam werden, starten wir den MySQL-Server neu:

root@server:~# service mysql restart

Ubuntu Server 14.04 – SAN-Zertifikate erstellen

Stellt man mit einer eigenen Certificate Authority („CA“) mit OpenSSL ein Serverzertifikat aus, so gilt dieses standardmäßig nur für die im Common Name („CN“) des Zertifikats angegebene Domain. Bei einem Server mit mehreren vollqualifizierten Domainnamen („Fully Qualified Domain Name“, „FQDN“) wie www.test.local, mail.test.local usw., muss daher für jeden FQDN eine eigenes Zertifikat erzeugt werden.

Man kann sich die Zertifikatserstellung erleichtern, indem man die Erweiterung Subject Alternative Name („SAN“) des in RFC 5280: Internet X.509 Public Key Infrastructure Certificate and Certificate Revocation List (CRL) Profile der Internet Engineering Task Force IETF spezifizierten Standards X.509 nutzt. Damit können in nur einem SAN-Zertifikat alle Domains aufgelistet werden, für die das Zertifikat gültig sein soll. Neben Domainnamen sind hier u. a. auch Einträge für IP-Adressen und E-Mail-Adressen möglich.

Die alternativ einsetzbaren Wildcard-Zertifikate sind nicht so flexibel, da sie nur eine Ebene einer einzigen Domain umfassen. Ein Zertifikat für *.test.local schützt z. B. die FQDN www.test.local und mail.test.local, nicht aber test.local und www.de.test.local oder ftp.dummy.private. Vorteilhaft gegenüber SAN-Zertifikaten ist, dass ein Wildcard-Zertifikat nicht bei jeder Änderung der Domainliste neu ausgestellt werden muss.

Die folgende Anleitung zum Anlegen von SAN-Zertifikaten mit OpenSSL ist eine Ergänzung zum Beitrag Ubuntu Server 14.04 – Certificate Authority mit OpenSSL einrichten.

Voraussetzungen

Wir haben unseren Server z. B. gemäß der Anleitung zur Basisinstallation für Ubuntu Server 14.04 installiert und durch Updates / Upgrades auf den neuesten Stand gebracht. Der Server hat den Rechnernamen server und gehört zur Domain test.local. Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

OpenSSL gehört zum Installationsumfang von Ubuntu Server 14.04 und sollte daher bereits auf unserem Server vorhanden sein. Falls nicht, können wir es wie folgt nachinstallieren:

root@server:~# apt install openssl

OpenSSL-Konfiguration anpassen

Um mit OpenSSL SAN-Zertifikate ausstellen zu können, ist eine modifizierte Version der Konfigurationsdatei /etc/ssl/openssl.cnf erforderlich. Neben einigen globalen Anweisungen nach dem Schema Name = Wert am Anfang enthält diese Datei im Wesentlichen Konfigurationabschnitte mit Überschriften in der Form [ Abschnitt ], unter denen mehrere Anweisungen zusammengefasst sind.

Als Grundlage erstellen wir zunächst mit

root@server:~# cp /etc/ssl/openssl.cnf /etc/ssl/openssl-san.cnf

eine Kopie der Konfigurationsdatei und ergänzen openssl-san.cnf dann wie folgt:

...
[ req ]
...
# req_extensions = v3_req # The extensions to add to a certificate request
req_extensions = v3_req # The extensions to add to a certificate request
...
[ v3_req ]
...
subjectAltName = @alt_names
...
[ alt_names ]
DNS.1 = test.local
DNS.2 = www.test.local
DNS.3 = mail.test.local

Damit der Inhalt des Abschnitts [ v3_req ] mit Zertifikats-Erweiterungen bei einer Zertifikatsanfrage berücksichtigt wird, entfernen wir das Kommentarzeichen # vor der Anweisung req_extensions = v3_req im Konfigurationabschnitt [ req ]. In [ v3_req ] fügen wir einen zusätzlichen Eintrag subjectAltName für die Domainnamen ein, für die das SAN-Zertifikat gelten soll. Statt die Domains in einer durch Komma getrennten Aufzählung anzugeben, werden sie der Übersichtlichkeit halber in einem eigenen neuen Konfigurationabschnitt [ alt_names ] aufgelistet, auf den wir mit @alt_names verweisen. Pro Domain tragen wir hier eine Zeile mit dem Aufbau DNS.x = FQDN mit x als fortlaufender Nummerierung ein. Ein im Common Name aufgeführter Domainname wird bei einem vorhandenen subjectAltName-Eintrag ignoriert und muss daher bei Bedarf ebenfalls in der Liste angegeben werden.

Subject Alternative Names und weitere Zertifikats-Erweiterungen von OpenSSL werden in x509v3_config detaillierter beschrieben.

Serverzertifikat erstellen

Die folgenden Ausführungen basieren auf dem Artikel Ubuntu Server 14.04 – Certificate Authority mit OpenSSL einrichten und beschreiben das Vorgehen in komprimierter Form. Vorausgesetzt wird, dass ein Root-Zertifikat / CA-Zertifikat /etc/ssl/certs/ca_test.local.pem mit dem privaten Schlüssel /etc/ssl/private/ca_test.local.key einer eigenen Certificate Authority existiert, mit dem wir selbst signierte Zertikate generieren können.

Wir erstellen unser SAN-Serverzertifikat in drei Schritten.

Zunächst legen wir einen privaten Schlüssel server.test.local.key ohne Verschlüsselung durch eine Passphrase an:

root@server:~# openssl genrsa -out /etc/ssl/private/server.test.local.key 4096

Mit diesem Schlüssel generieren wir dann eine Zertifikatsanfrage server.test.local.csr:

root@server:~# openssl req -config /etc/ssl/openssl-san.cnf -key /etc/ssl/private/server.test.local.key -new -out /etc/ssl/private/server.test.local.csr -sha512 -subj "/CN=server.test.local/O=Theo Test" 

Mit der Option -config /etc/ssl/openssl-san.cnf nutzt OpenSSL hierbei die geänderte Konfigurationsdatei.

Abschließend erzeugen wir mit der Zertifikatsanfrage das Serverzertifikat server.test.local.pem:

root@server:~# openssl x509 -CA /etc/ssl/certs/ca_test.local.pem -CAcreateserial -CAkey /etc/ssl/private/ca_test.local.key -days 3650 -extensions v3_req -extfile /etc/ssl/openssl-san.cnf -in /etc/ssl/private/server.test.local.csr -out /etc/ssl/certs/server.test.local.pem -req -sha512
...
Enter pass phrase for /etc/ssl/private/ca_test.local.key: ***

Die Option -extfile verweist auf die Datei mit den Zertifikats-Erweiterungen, in unserem Fall /etc/ssl/openssl-san.cnf. Mit der Option -extensions legen wir den in dieser Datei zu nutzenden Abschnitt mit Erweiterungen fest, hier v3_req.

Die Ausgabe des Serverzertifikats mit

root@server:~# openssl x509 -in /etc/ssl/certs/server.test.local.pem -noout -text

sollte nun die Liste mit den Domainnamen enthalten:

...
X509v3 Subject Alternative Name: 
    DNS:test.local, DNS:www.test.local, DNS:mail.test.local
...

Wenn unser SAN-Zertifikat wie gewünscht aussieht, können wir die nicht mehr benötigte Zertifikatsanfrage löschen:

root@server:~# rm /etc/ssl/private/server.test.local.csr

Ubuntu Server 14.04 – Certificate Authority mit OpenSSL einrichten

Um Anwendungen mit SSL („Secure Sockets Layer“) bzw. TLS („Transport Layer Security“) zu verschlüsseln, werden digitale Zertifikate benötigt. Diese werden von einer Zertifizierungsstelle („Certificate Authority“, „CA“) ausgegeben, die die Korrektheit der Zertifikatsdaten durch eine Signierung mit ihrem eigenen privaten Schlüssel sicherstellt. Client-Anwendungen wie Webbrowser oder Mail-Programme können die Gültigkeit der ihnen von Server-Anwendungen wie Webservern oder Mailservern vorgelegten Zertifikate mit dem zugehörigen öffentlichen Schlüssel der Certificate Authority prüfen. Die Zertifikate sind hierbei ausgehend von einem sog. „Root“-Zertifikat in einer Zertifikatskette hierarchisch aufgebaut. Falls eine Anwendung das Root-Zertifikat als vertrauenswürdig einstuft, akzeptiert sie auch alle anderen von der Certificate Authority signierten Zertifikate.

Digitale Zertifikate kommerzieller Anbieter sind in der Regel kostenpflichtig und z. B. für den Einsatz in einem lokalen Netzwerk oft nicht erforderlich. Hier können wir stattdessen mit dem Programmpaket OpenSSL eine eigene kostenlose Certificate Authority einrichten und selbst signierte Zertikate („Self-signed Certificates“) erstellen. Zur sicheren Kommunikation müssen wir in den Anwendungen dann nur noch unser Root-Zertifikat integrieren.

Üblicherweise besteht eine Zertifikatskette aus einem Root-Zertifikat, einem Zwischenzertifikat und einem Serverzertifikat für die eigentliche Anwendung. Da mit einer eigenen Certificate Authority in der Regel nur eine überschaubare Anzahl von Serverzertifikaten ausgestellt wird, werden wir nachfolgend auf das Erzeugen eines Zwischenzertifikats verzichten.

Voraussetzungen

Wir haben unseren Server z. B. gemäß der Anleitung zur Basisinstallation für Ubuntu Server 14.04 installiert und durch Updates / Upgrades auf den neuesten Stand gebracht. Der Server hat den Rechnernamen server und gehört zur Domain test.local. Es existiert ein Benutzer theo, der Mitglied der Gruppe sudo ist und somit über Administratorrechte verfügt. Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

OpenSSL gehört zum Installationsumfang von Ubuntu Server 14.04 und sollte daher bereits auf unserem Server vorhanden sein. Falls nicht, können wir es wie folgt nachinstallieren:

root@server:~# apt install openssl

Root-Zertifikat für eigene Certification Authority anlegen

Privaten Schlüssel generieren

Für unser Root-Zertifikat und auch die Serverzertifikate benötigen wir einen privaten Schlüssel, den wir mit der Anweisung openssl genrsa erzeugen:

root@server:~# openssl genrsa -aes256 -out /etc/ssl/private/ca_test.local.key 4096
Enter pass phrase for /etc/ssl/private/ca_test.local.key: ***
Verifying - Enter pass phrase for /etc/ssl/private/ca_test.local.key: ***

Hiermit erhalten wir einen RSA(„Rivest, Shamir, Adleman“)-Schlüssel ca_test.local.key mit einer Länge von 4096 bit. Die Schlüssellänge liegt damit über dem in den technischen Richtlinien TR-02102 des BSI empfohlenen Wert von 2000 bzw. 3000 bit.

Der private Schlüssel ist durch eine Passphrase mit dem AES(„Advanced Encryption Standard“)-Verfahren mit 256 bit verschlüsselt. Zur Speicherung nutzen wir das Schlüsselverzeichnis /etc/ssl/private, das bereits bei der Installation von OpenSSL angelegt wurde. Bei erhöhten Sicherheitsanforderungen kann man die Schlüsseldatei stattdessen auch auf einem externen Datenträger wie einem USB-Stick ablegen, den man gut verschlossen lagert und nur bei Bedarf an den Server anschließt.

Root-Zertifikat erzeugen

Mit dem privaten Schlüssel können wir nun mit dem Befehl openssl req ein neues Root-Zertifikat / CA-Zertifikat ca_test.local.crt mit einer Gültigkeitsdauer von 3650 Tagen, also ca. 10 Jahren erstellen:

root@server:~# openssl req -days 3650 -extensions v3_ca -key /etc/ssl/private/ca_test.local.key -new -out /usr/share/ca-certificates/ca_test.local.crt -sha512 -subj "/CN=Test CA/O=Theo Test" -x509 
Enter pass phrase for /etc/ssl/private/ca_test.local.key: ***

Als Speicherort wählen wir das Verzeichnis /usr/share/ca-certificates, in dem sich auch die zusammen mit OpenSSL installierten offiziellen CA-Zertifikate befinden.

Zur Gewährleistung der Integrität unserer Zertifikats nutzen wir als Hashfunktion SHA(„Secure Hash Algorithm“)-2 mit 512 bit. Die unsicherere Funktion SHA-1 sollte man nach den Empfehlungen des NIST („National Institute of Standards and Technology“) nicht mehr verwenden. Der aktuell sicherste Algorithmus SHA-3 wird von OpenSSL derzeit noch nicht unterstützt.

Um die normalerweise zu beantwortenden Detailfragen zum Zertifikatsinhaber zu umgehen, werden die einzelnen Antworten durch / getrennt als sogenannter „Distinguished Name“ (DN) der Option -subj übergeben. Der Distinguished Name wird zur eindeutigen Identifizierung eines Zertifikats genutzt. Wir geben hier nur den einzigen Pflichteintrag „Common Name“ (CN) für den Zertifikatsnamen und den „Organization Name“ (O) für den Namen des Zertifikatausstellers an. Weitere optionale Angaben sind:

  • /C=: Country Name
  • /ST=: State or Province Name
  • /L=: Locality Name
  • /OU=: Organizational Unit Name

In diversen Anleitungen wird beim obigen Aufruf von openssl req als weitere Option -nodes („no DES“) angegeben. Gemäß dem OpenSSL-Handbuch ist dies aber wohl nicht notwendig, da wir keinen privaten Schlüssel erzeugen:

-nodes
if this option is specified then if a private key is created it will not be encrypted.

Zertifikatsliste ergänzen

Mit dem folgenden Befehl aktualisieren wir die Liste aller CA-Zertifikate, die im Verzeichnis /usr/share/ca-certificates enthalten sind:

root@server:~# dpkg-reconfigure ca-certificates

Die Nachfrage, ob wir neuen Zertifikaten von Zertifizierungsstellen vertrauen möchten, beantworten wir mit Fragen. In der nun erscheinenden Auflistung wählen wir unser Root-Zertifikat ca_test.local.crt aus und bestätigen die Auswahl mit Ok. Hiermit wird das Zertifikat an die Datei ca-certificates.crt im Zertifikatsverzeichnis /etc/ssl/certs angehängt, die Konfiguration in /etc/ca-certificates.conf aktualisiert und in /etc/ssl/certs ein symbolischer Link ca_test.local.pem erzeugt.

Root-Zertifikat überprüfen

Zum Testen lassen wir uns das Root-Zertifikat abschließend mit openssl x509 ausgeben:

root@server:~# openssl x509 -in /etc/ssl/certs/ca_test.local.pem -noout -text

Client-Installation vorbereiten

Als Vorbereitung für die Installation auf den Client-Rechnern kopieren wir das Zertifikat in ein Heimat-Verzeichnis eines Serverbenutzers, auf das wir extern zugreifen können, in unserem Beispiel theo:

root@server:~# cp /usr/share/ca-certificates/ca_test.local.crt /home/theo

Nun können wir per FTP oder Fernzugriff mit SCP („Secure Copy“) die Datei ca_test.local.crt auf den Client übertragen.

Root-Zertifikat auf Client hinterlegen

Damit Anwendungen auf den Client-Rechnern die von unserer Certificate Authority erstellten Serverzertifikate akzeptieren, müssen wir das Root-Zertifikat im jeweiligen Programm hinterlegen. Wir werden im Folgenden die Vorgehensweise exemplarisch für Windows beschreiben.

Vorausgesetzt, wir haben den Fernzugriff entprechend Ubuntu Server 14.04 – Fernzugriff einrichten konfiguriert, können wir als Benutzer hans die Zertifikatsdatei mit dem SCP-Programm PSCP herunterladen:

C:\Windows\System32>c:\users\hans\pscp -i c:\users\hans\id_rsa.ppk -P 42022 theo@server.test.local:ca_test.local.crt c:\users\hans\ca_test.local.crt

Unter Windows (ab Version 98) werden Informationen über Zertifikate in der Zertifikatsverwaltung gespeichert, die wir in der Eingabeaufforderung wie folgt starten:

C:\Windows\system32>certmgr.msc

Im Ordner Vertrauenswürdige Stammzertifizierungsstellen erreichen wir nach einem Rechtsklick auf den Unterordner Zertifikate unter Alle AufgabenImportieren… den Zertifikatimport-Assistenten und können die einzelnen Importschritte ausführen. Unser hinterlegtes Root-Zertifikat wird nun u. a. in den Webbrowsern Chrome und Internet Explorer sowie in Outlook berücksichtigt.

Andere Anwendungen wie der Webbrowser Firefox oder der E-Mail-Client Thunderbird verwalten die Zertifikate eigenständig. Bei der Version 41 von Firefox installieren wir das Root-Zertifikat beispielsweise unter EinstellungenErweitertZertifikate anzeigenZertifizierungsstellenImportieren….

Serverzertifikat erstellen

Ein selbst signiertes Serverzertifikat erzeugen wir in drei Schritten. Die einzelnen Schritte beschreiben wir nachfolgend am Beispiel eine Zertikats für www.test.local.

Privaten Schlüssel anlegen

Zuerst generieren wir analog zum Vorgehen beim Root-Zertifikat einen privaten Schlüssel www.test.local.key:

root@server:~# openssl genrsa -out /etc/ssl/private/www.test.local.key 4096

Wir verzichten hierbei auf die Verschlüsselung mit einer Passphrase, da wir ansonsten bei jedem Start der Server-Anwendung ein Passwort eingeben müssten.

Zertifikatsanfrage generieren

Im zweiten Schritt erzeugen wir eine Zertifikatsanfrage („Certificate Signing Request“, „CSR“) www.test.local.csr. Die Anweisung hierfür sieht ähnlich wie der Befehl zum Anlegen des Root-Zertifikats aus. Die Optionen -days, -extensions und -x509 geben wir allerdings nicht mit an:

root@server:~# openssl req -key /etc/ssl/private/www.test.local.key -new -out /etc/ssl/private/www.test.local.csr -sha512 -subj "/CN=www.test.local/O=Theo Test" 

Bei einem Serverzertifikat muss der Common Name CN exakt mit dem vollqualifizierten Domainnamen FQDN („Fully Qualified Domain Name“) des Servers übereinstimmen. Eine Client-Anwendung würde das Zertifikat z. B. für den Server ftp.test.local nicht akzeptieren, selbst wenn er unter derselben IP-Adresse wie www.test.local erreichbar wäre. Die Angabe mehrerer Domains ist mit SAN(„Subject Alternative Name“)-Zertifikaten möglich, auf die wir in dem gesonderten Beitrag Ubuntu Server 14.04 – SAN-Zertifikate erstellen näher eingehen.

Serverzertifikat erzeugen

Mit der Zertifikatsanfrage erhalten wir schließlich mittels openssl x509 das Serverzertifikat www.test.local.pem im Zertifikatsverzeichnis /etc/ssl/certs:

root@server:~# openssl x509 -CA /etc/ssl/certs/ca_test.local.pem -CAcreateserial -CAkey /etc/ssl/private/ca_test.local.key -days 3650 -in /etc/ssl/private/www.test.local.csr -out /etc/ssl/certs/www.test.local.pem -req -sha512
...
Enter pass phrase for /etc/ssl/private/ca_test.local.key: ***

Beim Ausstellen eines Zertifikats durch unsere Certificate Authority wird eine Seriennummer verwendet, die bei jeder Nutzung um 1 erhöht wird und in einer Datei gespeichert wird. Mit der Option -CAcreateserial legen wir fest, dass die Seriennummern-Datei mit einer Zufallszahl angelegt wird, falls sie noch nicht existiert. Der Dateiname wird dabei aus dem Namen der Root-Zertifikats-Datei ohne Dateiendung und der Endung .srl gebildet. In unserem Fall lautet er mit vollständiger Pfadangabe also /etc/ssl/certs/ca_test.local.srl.

Die Zertifikatsanfrage wird nun nicht mehr benötigt und kann gelöscht werden:

root@server:~# rm /etc/ssl/private/www.test.local.csr

Abschließend kontrollieren wir noch unser Serverzertifikat:

root@server:~# openssl x509 -in /etc/ssl/certs/www.test.local.pem -noout -text

Serverzertifikats-Skript

Muss man mehr als ein oder zwei Serverzertifikate erzeugen, lohnt sich die Anlage eines Shellskripts, hier genservcert.sh:

root@server:~# touch /root/genservcert.sh
root@server:~# chmod 0700 /root/genservcert.sh

Ein einfaches Skript, das die oben beschriebenen Schritte zusammenfasst, kann wie folgt aussehen:

#!/bin/bash
# genservcert.sh
private="/etc/ssl/private"
certs="/etc/ssl/certs"
openssl genrsa -out $private/$1.key 4096
openssl req -key $private/$1.key -new -out $private/$1.csr -sha512 -subj "/CN=$1/O=$2"
openssl x509 -CA $certs/$3.pem -CAcreateserial -CAkey $private/$3.key -days 3650 -in $private/$1.csr -out $certs/$1.pem -req -sha512
rm $private/$1.csr
openssl x509 -in $certs/$1.pem -noout -text

Ein Aufruf für den Server mail.test.local lautet dann beispielsweise:

root@server:~# /root/genservcert.sh mail.test.local "Theo Test" ca_test.local
...
Enter pass phrase for /etc/ssl/private/ca_test.local.key: ***

Ubuntu Server 14.04 – DNS-Server Bind im lokalen Netzwerk

Um Rechner in einem Netzwerk mit Domainnamen wie client.test.local statt IP-Adressen wie 192.168.0.101 ansprechen zu können, ist eine Zuordnung von Namen und Adressen erforderlich. Diese Zuordnung kann im einfachsten Fall durch Einträge in einer Datei, unter Linux z. B. /etc/hosts, erfolgen. Nachteilig ist bei diesem Verfahren, dass die Datei auf jedem Client-Rechner vorhanden sein muss und bei Änderungen alle Dateien zu aktualisieren sind.

Sinnvoll ist in Netzwerken mit mehr als einer Handvoll Rechnern der Einsatz eines DNS(„Domain Name System“)-Servers zur Namensauflösung. Dieser wird zentral eingerichtet und administriert.

Nachfolgend werden wir den DNS-Server / Nameserver Bind installieren und konfigurieren. Wir setzen Bind dabei nur für die Auflösung von Domainnamen aus unserem lokalen Netzwerk ein. Ein externer Zugriff aus dem Internet wird nicht eingerichtet. Eine ausführliche Dokumentation zu Bind ist das BIND 9 Administrator Reference Manual.

Voraussetzungen

Wir haben unseren Server z. B. gemäß der Anleitung zur Basisinstallation für Ubuntu Server 14.04 im lokalen Netzwerk 192.168.0.0/24 installiert und durch Updates / Upgrades auf den neuesten Stand gebracht. Der Server hat die IPv4-Adresse 192.168.0.1, der Router für die Internetverbindung die Adresse 192.168.0.254. Unsere Rechner haben feste IP-Adressen, das DHCP („Dynamic Host Configuration Protocol“) ist nicht aktiviert. Die Befehle führen wir mit Root-Rechten aus, z. B. nach Öffnen einer Rootshell mit sudo -i.

DNS-Server Bind installieren

Wir installieren den DNS-Server Bind mit folgender Anweisung auf unserem Server:

root@server:~# apt install bind9

Erreichbarkeit auf IPv4 beschränken

In der Standardkonfiguration löst Bind Domainnamen für die beiden Protokolle IPv4 und IPv6 auf. Falls wir ins unserem lokalen Netzwerk nur IPv4 verwenden und kein IPv6 benötigen, sollten wir in der Datei /etc/default/bind9 die Startoptionen ändern:

...
#OPTIONS="-u bind"
OPTIONS="-4 -u bind"
...

Dies kann die Arbeit von Bind deutlich beschleunigen.

Zusätzlich passen wir den Eintrag listen-on-v6 in der Optionsdatei /etc/bind/named.conf.options wie folgt an:

options {
        ...
        //listen-on-v6 { any; };
        listen-on-v6 { none; };
        ...
};

Optionsdatei anpassen

In der Optionsdatei /etc/bind/named.conf.options fügen wir im options-Block folgende Optionen hinzu:

options {
        ...
        listen-on { 192.168.0.1; };
        forwarders { 8.8.8.8; 8.8.4.4; };
        forward only;
};

Die erste Option beschränkt die Namensauflösung auf Anfragen an die Adresse 192.168.0.1 und somit auf Clients unseres lokalen Netzwerks 192.168.0.0/24. Das ist sinnvoll, falls der Server beispielsweise gleichzeitig als Gateway arbeitet und über mehrere Netzwerkkarten/-interfaces verfügt.

Mit der zweiten und dritten Option werden Namen, die nicht von Bind aufgelöst werden können, d. h. in der Regel Anfragen, die nicht in das lokale Netz gehen, direkt an die DNS-Server 8.8.8.8 und 8.8.4.4 von Google weitergeleitet.

Da unser Server nicht aus dem Internet erreichbar ist, ist eine Einschränkung des Client-Zugriffs z. B. mit allow-query { 192.168.0.0/24; }; nicht notwendig. Die Weitergabe von Zoneninformationen an andere DNS-Server müssen wir ebenfalls nicht beschränken oder mit der Option allow-transfer { none; }; unterbinden.

Zonendateien anlegen

Der Hauptteil der Konfiguration von Bind wird in den Zonendateien vorgenommen. Für unser lokales Netzwerk benötigen wir zwei Zonendateien, die wir im Verzeichnis /etc/bind anlegen. In der Datei db.test.local konfigurieren wir die „Forward Lookup“-Zone zur Auflösung von Rechnernamen in IP-Adressen, in der Datei db.0.168.192 die entsprechende „Reverse Lookup“-Zone für die Auflösung von IP-Adressen in Rechnernamen.

„Forward Lookup“-Zonendatei

Zunächst erzeugen wir die „Forward Lookup“-Zonendatei db.test.local mit den passenden Zugriffsrechten:

root@server:~# touch /etc/bind/db.test.local
root@server:~# chown root:bind /etc/bind/db.test.local

Für den vereinfachten Fall eines Netzwerks mit einem Server server und nur einem Client client reichen folgende Einträge in db.test.local aus:

$TTL   604800
@      SOA server.test.local. root.test.local. (
           2015082401 ; Serial
           604800     ; Refresh
           86400      ; Retry
           2419200    ; Expire
           604800 )   ; Minimum
@      NS  server.test.local.
@      A   192.168.0.1
server A   192.168.0.1
client A   192.168.0.101

Die Zonendatei besteht im wesentlichen aus sogenannten „Resource Records“, die zusammen die DNS-Zone, in unsererem Fall die Domain test.local, komplett definieren. Die Reihenfolge der Resource Record-Angaben spielt dabei keine Rolle. Eine detaillierte Referenz findet sich in RFC 1035: Domain Names – Implementation and Specification der Internet Engineering Task Force IETF.

Mit der $TTL(„Time to Live“)-Anweisung am Anfang legen wir global fest, wie lange die Resource Records gültig sind. In unserem Beispiel verbleiben die Informationen 604800 Sekunden = 1 Woche im Cache des anfragenden Clients oder eines anderen anfragenden DNS-Servers. Dieser Wert kann innerhalb eines Resource Records individuell überschrieben werden.

Die Resource Records haben allgemein das Format Name TTL Klasse Typ Daten. Den optionalen Eintrag TTL haben wir weggelassen, so dass für jeden Resource Record die globale Einstellung 604800 Sekunden gilt. Die optionale Klasse ist bei allen Resource Records der Default-Wert IN und muss daher ebenfalls nicht angegeben werden.

Der erste Resource Record vom Typ SOA („Start of a Zone of Authority“) muss genau einmal in der Zonendatei vorkommen und enthält die wesentlichen Informationen über die Zone. Das @-Zeichen am Anfang dient als Platzhalter für den Zonennamen, bei uns also test.local. Die Daten bestehen aus dem Nameserver server.test.local, der E-Mail-Adresse des Administrators root@test.ibf.local, bei der das @-Zeichen durch einen Punkt ersetzt ist, der Seriennummer und verschiedenen eingeklammerten Zeitangaben. Die zusätzlichen Punkte am Ende des Nameservers und der E-Mail-Adresse kennzeichnen die Angabe eines vollqualifizierten Domainnamens FQDN („Fully Qualified Domain Name“). Eine Besonderheit ist, dass der SOA-Record auf mehrere Zeilen verteilt ist und wir daher Klammern verwenden müssen. Kommentare wie z. B. Serial werden mit einem Semikolon abgetrennt.

Falls wir neben unserem (primären) DNS-Server einen oder mehrere sekundäre DNS-Server einsetzen, müssen wir die Seriennummer Serial bei jeder Änderung der Zonendatei erhöhen. Hiermit signalisieren wir sekundären DNS-Servern, dass sie einen Zonentransfer durchführen sollen, bei dem die aktuellen Zonendaten mit dem primären DNS-Server abgeglichen werden. Ein gebräuchliches Schema für die Seriennummer ist yyyymmddvv, wobei yyyymmdd für das Datum im Format Jahr yyyy, Monat mm und Tag dd und vv für die unterschiedlichen Dateiversionen eines Tages stehen.

Die Werte für Refresh, Retry, Expire und Minimum entsprechen wie der Eintrag $TTL den Vorgaben aus der Beispieldatei /etc/bind/db.local und werden in RFC 1035 genauer beschrieben.

Der zweite Resource Record vom Typ NS („Authoritative Name Server“) muss mindestens einmal in der Zonendatei enthalten sein. Hier hinterlegen wir den FQDN des Nameservers server.test.local unserer Zone test.local, wie beim SOA-Record mit einem zusätzlichen Punkt am Ende.

Die weiteren Resource Records sind jeweils vom Typ A („Host Address“) und können in beliebiger Anzahl vorkommen. Mit ihnen ordnen wir dem Zonennamen test.local (Platzhalter @) und den Rechnern server und client in unserem lokalen Netzwerk die IP-Adressen 192.168.0.1, 192.168.0.1 und 192.168.0.101 zu. Für die Einträge server und client müssen wir nicht die FQDN benutzen, da Namen ohne Punkt am Ende automatisch durch den Zonennamen ergänzt werden.

Falls es in unserem lokalen Netz einen Mail-Server mail.test.local mit der IP-Adresse 192.168.0.2 gibt, ergänzen wir die „Forward Lookup“-Datei noch um 2 Einträge:

...
@      MX  10 mail.test.local.
mail   A   192.168.0.2
...

Der Resource Record vom Typ MX („Mail Exchange“) enthält einen ganzzahligen Wert für die Priorität des Mail-Servers, hier 10, und seinen Domainnamen mail.test.local. Beim Einsatz mehrerer Mail-Server werden diese in der Reihenfolge steigender Prioritätswerte genutzt. Da der Domainname des MX-Records auf einen A-Record verweisen muss, definieren wir diesen ensprechend in der nächsten Zeile. Der Mail-Server kann natürlich auch identisch mit unserem Server sein. In diesem Fall ersetzen wir den Eintrag 192.168.0.2 durch 192.168.0.1. Unser Server wäre dann unter server.test.local und mail.test.local ansprechbar.

„Reverse Lookup“-Zonendatei

Als zweite Datei legen wir die „Reverse Lookup“-Zonendatei db.0.168.192 mit den entsprechenden Berechtigungen an:

root@server:~# touch /etc/bind/db.0.168.192
root@server:~# chown root:bind /etc/bind/db.0.168.192

Die „Reverse Lookup“-Zonendatei ist ähnlich aufgebaut wie die „Forward Lookup“-Zonendatei db.test.local. Für unser Beispiel mit Mail-Server sieht sie folgendermaßen aus:

$TTL 604800
@   SOA server.test.local. root.test.local. (
        2015082401 ; Serial
        604800     ; Refresh
        86400      ; Retry
        2419200    ; Expire
        604800 )   ; Minimum
@   NS  server.test.local.
1   PTR server.test.local.
2   PTR mail.test.local.
101 PTR client.test.local.

Statt der A-Records für die Auflösung von Namen in IP-Adressen werden Resource Records vom Typ PTR („Pointer“) für die umgekehrte Zuordnung von IP-Adressen zu vollqualifizierten Domainnamen verwendet. Die IP-Adressen notieren wir hierbei nicht vollständig, sondern nur ihren Hostanteil (d. h. den letzten Block) ohne den Netzanteil (die ersten 3 Blöcke) 192.168.0.. MX-Records werden in der „Reverse Lookup“-Zonendatei nicht angegeben.

Konfigurationsdatei ergänzen

Die Zuordnung der Informationen aus den beiden Zonendateien erfolgt in der Konfigurationsdatei /etc/bind/named.conf.local, die bereits während der Installation von Bind angelegt wird. Jede Zonendatei wird dabei mit Hilfe einer zone-Definition eingebunden, die unterschiedliche Optionen beinhalten kann:

...
zone "test.local" {
  type master;
  file "/etc/bind/db.test.local";
};
zone "0.168.192.in-addr.arpa" {
  type master;
  file "/etc/bind/db.0.168.192";
};

Mit der Option type legen wir unseren Server als primären DNS-Server (master) fest. Mit file ordnen wir der Zone eine Zonendatei zu. Die in der zweiten Zonenendefinition verwendete in-addr.arpa-Domäne ist eine spezielle Domäne für „Reverse Lookups“ von IP-Adressen. Für den Netzanteil einer Adresse gibt es hierbei pro Block eine Subdomain-Ebene, die in umgekehrter Reihenfolge notiert werden. Für den Adressbereich 192.168.0.0192.168.0.255 unseres IPv4-Netzwerks ist die Subdomain somit 0.168.192.in-addr.arpa. Ausführlichere Informationen enthält z. B. RFC 1035.

Bind testen

Vor dem Neustart von Bind überprüfen wir die Zonendateien mit dem Programm named-checkzone, das zusammen mit Bind installiert wird, auf Syntax- und Integritätsfehler:

root@server:~# named-checkzone test.local /etc/bind/db.test.local
root@server:~# named-checkzone 0.168.192.in-addr.arpa. /etc/bind/db.0.168.192

Nachdem eventuelle Fehler behoben sind, starten wir Bind neu:

root@server:~# service bind9 restart

Wie den Clients müssen wir auch unserem Server noch den neuen DNS-Server mitteilen. Hierzu ändern wir den dns-nameservers-Eintrag in der Datei /etc/network/interfaces:

...
#dns-nameservers 8.8.8.8 8.8.4.4
dns-nameservers 192.168.0.1 8.8.8.8 8.8.4.4
...

Damit die geänderten Einstellungen wirksam werden, starten wir den Netzwerk-Service neu:

root@server:~# /etc/init.d/networking restart

Nun sollten wir unsere Domain mit ping erreichen können:

root@server:~# ping -c 4 test.local

Falls dies nicht funktioniert, müssen wir die Netzwerkschnittstelle, hier eth0, zunächst stoppen und danach neu starten:

root@server:~# ifdown eth0 && ifup eth0

eth0 ist bei Bedarf durch den entsprechenden Eintrag für die Netzwerkschnittstelle in /etc/network/interfaces zu ersetzen.

Abschließend testen wir unsere Konfiguration mit dem Programm dig („Domain Information Groper“), das diverse Informationen von DNS-Servern abfragt:

root@server:~# dig test.local ANY
root@server:~# dig -x 192.168.0.1

Mit dem ersten Aufruf werden alle (Abfrageoption ANY) Resource Records der „Forward Lookup“-Zone ausgegeben. Der zweite Aufruf mit der Option -x liefert die Resource Records der „Reverse Lookup“-Zone.

Clients konfigurieren

Für die betriebssystemabhängige Client-Konfiguration werden in der Regel folgende Daten benötigt:

  • DHPC: deaktiviert
  • IPv4-Adresse: 192.168.0.xxx
  • Subnetzmaske 255.255.255.0
  • Gateway: 192.168.0.254
  • Nameserver: 192.168.0.1
  • Weitere Nameserver: 8.8.8.8, 8.8.4.4

Details finden sich in der Dokumentation des jeweiligen Betriebssystems.

Zum Testen nutzen wir ping, unter Linux beispielsweise mit dem Aufruf:

hans@client:~ ping -c 4 test.local

Unter Windows geben wir in der Eingabeaufforderung entsprechend ein:

C:\Windows\System32>ping test.local