Mehrere PHP-Versionen im Apache 2.4 unter Ubuntu 14.04

Ubuntu 14.04 kommt mit Apache 2.4.7 und PHP 5.5.9. Auf der Suche nach einer Möglichkeit, für einzelne VirtualHosts auf einem Hosting-System PHP 7.1 zur Verfügung stellen zu können bin ich auf diverse Anleitungen im Netz gestoßen und möchte hier die für mich am besten funktionierende Variante vorstellen.

Prinzipiell gibt es die Möglichkeit, über ein PPA die PHP-Version im Ubuntu 14.04 zu aktualisieren. Dann hat man allerdings nur noch die neuere PHP-Version, womit (besonders beim Sprung auf PHP 7) alte Websites nicht unbedingt klar kommen. Also bleibt nur die Alternative, PHP 7 selbst zu kompilieren.

Ich habe das tar.gz Archiv (Version 7.1.5) von php.net herunter geladen und auf den Server ins Verzeichnis /usr/local/src/php7 kopiert. Nach dem Entpacken hat man den Quellcode im Verzeichnis /usr/local/src/php7/php-7.1.5:

cd /usr/local/src/php7
tar xzf php-7.1.5.tar.gz
cd php-7.1.5

Der sicherlich komplexeste Schritt folgt nun, nämlich die Konfiguration von PHP. Hier müssen die Parameter an die Anforderungen angepasst werden. Es gibt eine ganze Menge an Optionen, die jeweils auch die passenden Quellen voraussetzen, die natürlich vorher installiert werden sollten. Man kann sich die Optionen mit folgendem Befehl ansehen:

./configure --help | less

Ich habe mit folgender Konfiguration gestartet:

apt-get install libxml2-dev libbz2-dev libcurl4-openssl-dev \
libjpeg-dev libpng-dev libfreetype6-dev libgmp3-dev \
libc-client-dev libicu-dev libsasl2-dev unixodbc-dev \
libpspell-dev libsnmp-dev libxslt-dev

./configure \
--with-libdir=lib64 \
--cache-file=../config.cache \
--prefix=/usr/local/php7-cgi \
--with-config-file-path=/usr/local/php7-cgi/etc \
--disable-debug --disable-rpath \
--with-pic --with-bz2 --with-curl \
--with-freetype-dir=/usr/local/php7-cgi \
--with-png-dir=/usr/local/php7-cgi \
--with-gd --enable-gd-native-ttf \
--with-gettext --with-gmp --with-iconv \
--with-jpeg-dir=/usr/local/php7-cgi \
--with-openssl --with-pspell --with-pcre-regex --with-zlib \
--enable-exif --enable-ftp --enable-sockets \
--enable-sysvsem --enable-sysvshm --enable-sysvmsg --enable-wddx \
--with-kerberos --with-unixODBC=/usr \
--enable-shmop --enable-calendar \
--with-libxml-dir=/usr/local/php7-cgi \
--enable-pcntl --enable-mbstring --enable-mbregex --enable-bcmath \
--with-xmlrpc --with-mysqli --with-snmp \
--enable-soap --with-xsl --enable-xmlreader --enable-xmlwriter \
--without-gdbm --enable-pdo --with-pdo-mysql \
--with-pear=/usr/local/php7-cgi/pear \
--without-sqlite3 --without-pdo-sqlite \
--with-config-file-scan-dir=/usr/local/php7-cgi/php.d \
--enable-intl --enable-cgi

Hier wird an mehreren Stellen ein Pfad festgelegt, damit sich das installierte PHP 7 nicht mit dem im System vorhandenen beißt. Ich habe mir /usr/local/php7-cgi als Pfad ausgesucht. Sofern man am Ende des configure-Laufs keine Fehler feststellen kann, sollte man mit folgenden Befehlen kompilieren, testen und installieren können:

make
make test
make install

Nach erfolgreicher Installation kopieren wir noch die PHP-Konfiguration vom PHP 5.5.9 als Startpunkt für PHP 7:

cd /usr/local/php7-cgi/etc
cp -p /etc/php5/apache2/php.ini .
cat /etc/php5/apache2/conf.d/05-opcache.ini >> php.ini

Jetzt muss nur noch in einem gewünschten VirtualHost im Apache auf die neue PHP-Version verwiesen werden. Dazu benötigen wir folgende Zeilen in der VirtualHost-Konfiguration:

ScriptAlias /php7-bin /usr/local/php7-cgi/bin
Action application/x-httpd-php7 /php7-bin/php-cgi

<FilesMatch "\.php$">
    SetHandler application/x-httpd-php7
</FilesMatch>

<Directory "/usr/local/php7-cgi/bin">
    require all granted
</Directory>

Hier wird zunächst ein ScriptAlias definiert, auf welchen dann die Action Direktive verweist, sofern ein Dokument des Mime-Typs application/x-httpd-php7 verarbeitet wird. Der Mime-Typ wird mit SetHandler für alle Dateien gesetzt, deren Namen mit .php enden. Damit das PHP-Binary vom Apache auch ausgeführt werden kann, muss der Zugriff auf das Verzeichnis erlaubt sein. Das erledigt der letzte Block.

Fertig. Apache muss natürlich noch neu gestartet werden (apachectl graceful reicht). Ein Aufruf von phpinfo() sollte jetzt auf diesem VirtualHost die neue PHP-Version anzeigen.

Sollte es beim Apache-Neustart zu folgender Fehlermeldung kommen:

Invalid command 'Action', perhaps misspelled or defined by a module not included in the server configuration

so liegt dies daran, dass das Apache-Modul mod_actions fehlt. Es kann einfach mit folgendem Befehl aktiviert werden:

a2enmod actions
service apache2 restart

Anschließend sollte der Fehler nicht mehr auftreten.

Umstieg vom iMac auf Ubuntu/Xubuntu 13.04

Da die Tage meines schicken iMac bald gezählt sind und unter anderem wegen der Leistung des Systems jetzt mal so langsam eine Neuanschaffung nötig ist, habe ich mich bei dieser Gelegenheit entschieden, wieder auf einen Linux-Desktop zu setzen. Das hat viele verschiedene Gründe, die aber nicht Thema dieses Artikels sind.

Ein Hinweis vorab: Dieser Artikel weder eine vollständige Anleitung sein, wie man ein Xubuntu-System ordentlich aufsetzt, noch alle Schritte vollständig enthalten, die zum Erreichen der beschriebenen Ziele nötig sind. Viel mehr soll er eine kommentierte Sammlung einzelner Schritte sein, die leicht nach zu vollziehen sind und ein System meiner Meinung nach ausmachen.

In die engere Wahl kamen ohnehin nur Debian-basierte Systeme, so entschied ich mich letztendlich für ein Xubuntu 13.04. Das ist die Ubuntu-Variante mit XFCE als Desktop. Da ich auch ein großer Fan von ZFS, insbesondere der Variante für Linux bin, habe ich mir zum Ziel gesetzt, ZFS als Dateisystem zu verwenden – auch als Root-Dateisystem – was die Sache etwas spannend macht.

Die Installation von Ubuntu mit ZFS-Root ist hier ganz gut erklärt. Ich habe lediglich raring (13.04) statt precise (12.04 LTS) in Schritt 4.4 (debootstrap raring /mnt) verwendet. Hat reibungslos funktioniert.

Nun bin ich ja etwas pingelig bei der Konfiguration meines Desktops. Ich mag eigentlich so Automatismen gar nicht, die beispielsweise Netzwerk-Interfaces nach Belieben an- und ausschalten, einen über Updates informieren oder sie gar automatisch installieren, während ich gerade wichtigere Dinge zu tun habe. Ein schlankes System ohne viele unnötig installierte Pakete bringt auch Vorteile, z.B. den, dass man für nicht installierte Pakete auch keine Updates installieren muss… 😉

Deshalb möchte ich hier an der Stelle ansetzen, an der das HOWTO aufhört, nämlich der Konfiguration des installierten Systems:

cat <<EODATA > /etc/apt/sources.list
deb http://de.archive.ubuntu.com/ubuntu raring main restricted universe multiverse
deb http://de.archive.ubuntu.com/ubuntu raring-updates main restricted universe multiverse
deb http://de.archive.ubuntu.com/ubuntu raring-security main restricted universe multiverse
deb http://de.archive.ubuntu.com/ubuntu raring-proposed main universe restricted multiverse
EODATA

apt-get update
apt-get install aptitude
aptitude update
aptitude full-upgrade
aptitude install xubuntu-desktop xubuntu-artwork
aptitude install language-pack-de chromium-browser \
  chromium-browser-l10n firefox firefox-locale-de thunderbird \
  thunderbird-locale-de libreoffice libreoffice-l10n-de

Lokalisierung

debootstrap hat zwar anfangs alles Mögliche korrekt installiert, jedoch sind die Pakete nicht wirklich schön konfiguriert bzw. lokalisiert. Was man also zunächst tun muss ist, einen User anlegen und die locale-Einstellungen richtig setzen. Ich mag deutsche Programm-Ausgaben in der Konsole gar nicht, möchte aber grundsätzlich einen deutschen Desktop, deshalb setze ich LC_MESSAGES entsprechend:

cat <<EODATA > /etc/default/locale
LANGUAGE=en_US.UTF-8
LANG=en_US.UTF-8
EODATA

cat <<EODATA > /var/lib/locales/supported.d/local
en_US.UTF-8 UTF-8
en_US ISO-8859-1
en_US.ISO-8859-15 ISO-8859-15
de_DE.UTF-8 UTF-8
de_DE ISO-8859-1
de_DE@euro ISO-8859-15
EODATA

locale-gen

useradd -c "Robin Schroeder" -d /home/robin -s /bin/bash -m robin
passwd robin
su - robin

cat <<EODATA > .pam_environment
LANGUAGE=de_DE:de_CH:en
LANG=de_DE.UTF-8
EODATA

cat <<EODATA > .bash_local
export LC_MESSAGES=C
export TERM=xterm-256color
EODATA

cat <<EODATA >> .bashrc

# Read Robin's local definitions
if [ -f ~/.bash_local ]; then
    . ~/.bash_local
fi
EODATA

cat <<EODATA > .bash_aliases
alias l='ls -la --color=auto'
alias ..='cd ..'
EODATA

Hier greife ich zu einem kleinen Trick: Ich erzeuge mir die Datei .bash_local, in der ich LC_MESSAGES und TERM so verbiege, wie ich es gerne hätte. Damit die Datei von der Bash auch benutzt wird, erweitere ich die .bashrc entsprechend.

Sicherheit

Zunächst einmal möchte man als normaler Benutzer in die Gruppe sudo, damit man auch einzelne Befehle mit Superuser-Rechten ausführen darf:

aptitude install sudo
usermod -G sudo robin

Anschließend sollte man nachsehen, ob der Gruppe sudo auch die entsprechenden Rechte eingeräumt sind. Das geht mit visudo. Es öffnet sich die Konfigurationsdatei von sudo, in der man nach folgender Zeile suchen muss:

%sudo   ALL=(ALL:ALL) ALL

Die Zeile darf nicht auskommentiert sein. Ansonsten die Raute vorne weg machen und die Datei speichern und verlassen. Fertig.

SSH-Agent Timeout

Ich benutze SSH-Keys zum Login an anderen Maschinen. Das ist praktisch, weil man, wenn man einen ssh-agent laufen hat, die Passphrase nur einmal eingeben muss und der Agent sich die dann merkt. Leider tut er das für immer – zumindest so lange, wie man sich nicht vom Desktop abmeldet. Das empfinde ich als ziemlich unschön und korrigiere das gerne, indem ich den ssh-agent anweise, sich Passphrases nur 10 Minuten lang zu merken.

Der ssh-agent wird standardmäßig mit der X-Session gestartet. Um seine Parameter zu ändern, editiert man die Datei /etc/X11/Xsession.d/90x11-common_ssh-agent und korrigiert oben in der Datei eine Zeile. Es muss dann folgendes in der Zeile stehen:

SSHAGENTARGS="-t 600"

Anschließend startet der ssh-agent mit 600-Sekunden-Timeout für gespeicherte Keys.

ABER: Das reicht nicht – zumindest bei mir hat das Setzen des Timeouts für den ssh-agent allein nichts gebracht. Es gibt nämlich ein Problem mit dem gnome-keyring-daemon, der unter Umständen immer automatisch alle SSH-Keys aus ~/.ssh/ in den Agent lädt.

Die Lösung ist aber einfach: Man deaktiviert das Laden der Laufzeitumgebung für GNOME beim Login. Das geht in den XFCE-Einstellungen unter Sitzung und Startverhalten und hat bei mir bis jetzt fast keine negativen Folgen gehabt (siehe unten):

disable-gnome-runtime

Der gnome-keyring-daemon verhält sich jetzt nicht mehr so aggressiv, was das Laden von Keys angeht.

Aber dann gibt es da ein weiteres Problem. Die Lösung ist einfach:

perl -i -pe 's/^OnlyShowIn=(.*)/OnlyShowIn=$1XFCE;/' /etc/xdg/autostart/gnome-keyring-pkcs11.desktop

Hier wird einfach nur in der Datei /etc/xdg/autostart/gnome-keyring-pkcs11.desktop die Zeile, die mit OnlyShowIn= anfängt, um XFCE ergänzt. Anschließend meckert gnome-keyring nicht mehr über PKCS11.

SSH-Keys auf externem Medium

Des weiteren habe ich meine SSH-Keys gerne auf einem USB-Stick anstatt auf der Festplatte. Einerseits kann ich sie so mitnehmen auf andere Rechner, andererseits kann sie mir auch keiner klauen. Der USB-Stick wird dann sozusagen zum zweiten Faktor einer Zweifaktor-Authentifizierung, sofern der Server, zu dem man sich verbinden will, keine Authentifizierung mit Passwort zulässt. 😉

Damit die ssh den Key nicht in .ssh/ sucht, sondern auf dem USB-Stick, konfiguriere ich sie wie folgt:

cat <<EODATA > ~/.ssh/config
IdentityFile /media/keystore/.ssh/id_rsa
EODATA

Und Damit unter /media/keystore auch bequem der USB-Stick gemountet werden kann, finde ich nach dem Einstecken des Sticks seine UUID her aus (mit blkid) und erzeuge einen Eintrag in der /etc/fstab:

mkdir /media/keystore
blkid
    [hier dann die richtige Zeile finden
     mit dem Dateisystem des USB-Sticks]

cat <<EODATA >> /etc/fstab

# Keystore SD card
UUID=7b88fde8-4086-46f1-b35a-3174ee47fb02 /media/keystore ext2 ro,noexec,nodev,nosuid,user,noauto 0 0
EODATA

Wichtig ist hier, dass man das korrekte Dateisystem (bei mir ext2) angibt.

Der Stick kann jetzt von jedem User mit mount /media/keystore gemountet werden.

Meinen SSH-Public-Key kopiere ich gerne mit dem kleinen Utility ssh-copy-id auf „neue“ Maschinen, wenn bzw. bevor ich mich dort das erste Mal einlogge. Ich behalte jedoch gerne den Überblick darüber, auf welche Maschinen ich meinen Public-Key verteile habe, deshalb habe ich das Script leicht modifiziert:

mkdir ~/bin
cp /usr/bin/ssh-copy-id ~/bin/

Ich habe oben im Script eine Variable erfunden:

KEY_HOSTS_FILE="~/.ssh/key_hosts"

…sowie sie unten im Script benutzt. Und zwar in der Zeile, die den ssh Aufruf macht:

{ eval "$GET_ID" ; } | ssh -o UserKnownHostsFile=${KEY_HOSTS_FILE} $host "umask 077; test ........

Anschließend speichert ssh-copy-id die Host-Keys der Maschinen, auf die ich meinen Public-Key copiert habe, in der Datei .ssh/key_hosts anstatt wie bisher in .ssh/known_hosts. In der Datei stehen also idealerweise alle Maschinen, auf der mein Public-Key liegt.