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.

Mac OS X Yosemite ignoriert DNS Suchdomains

Der lokale DNS Resolver in Mac OS X 10.10 (Yosemite) berücksichtigt die eingetragenen Suchdomains (mal wieder) nicht, wenn der gesuchte Hostname bereits einen Punkt enthält. Leider hat sich auch der Resolver geändert, so dass die Tipps aus 2011 hier auch nicht mehr helfen.

Mit dem Update auf 10.10.1 gibt es aber wieder eine Option, mit der man diese Funktion wieder herstellen kann:

sudo defaults write \
        /System/Library/LaunchDaemons/com.apple.discoveryd.plist \
        ProgramArguments -array-add "--AlwaysAppendSearchDomains"

Danach einfach noch den discoveryd durchstarten

sudo launchctl unload -w \
     /System/Library/LaunchDaemons/com.apple.discoveryd.plist
sudo launchctl load -w \
     /System/Library/LaunchDaemons/com.apple.discoveryd.plist

und danach funktioniert es wieder wie gewünscht.

DynDNS Alternative selbst gebaut in 10 Minuten

Da die Firma DynDNS aufgrund eines Versäumnisses meinerseits diverse meiner kostenlosen Accounts rückstandsfrei eliminiert hat, habe ich mich gestern spontan entschlossen, den Service einfach selbst nachzubauen. Zumindest mit so viel (bzw. wenig) Funktionalität, wie ich wirklich benötige.

Man braucht folgendes:

  • Sowas wie einen „Root-Server“ mit einer öffentlichen IP-Adresse (z.B. 123.234.231.132) und einer Domain (z.B. meinedomain.de
  • Die Möglichkeit, eigene DNS-Einträge in der Domain vorzunehmen (genauer gesagt NS-Einträge für eigene Nameserver)
  • Auf dem „Root-Server“:
    • MySQL
    • PowerDNS
    • Apache mit mod_cgi

In meinem Fall war auf dem Server bereits Debian 6 (squeeze) mit MySQL und Apache installiert. PowerDNS fehlte noch.

Als erstes habe ich PowerDNS inklusive seinem MySQL-Backend installiert. Bei der Gelegenheit kann man auch direkt die später nötigen Perl-Module mit installieren:

aptitude install pdns-server pdns-backend-mysql libdbd-mysql-perl libcgi-pm-perl

PowerDNS wird die Frage stellen, an welches Interface (IP-Adresse) er sich binden soll. Das muss jeder selbst entscheiden. Es sollte nur ein DNS-Name (A-record) auf diese IP-Adresse zeigen, den müssen wir nämlich später in den SOA-Records verwenden.

Anschließend muss auf dem MySQL-Server eine Datenbank pdns mit ein paar Tabellen erzeugt werden. Das geht mit folgendem SQL-Schnipsel:

CREATE DATABASE pdns;
USE pdns;

CREATE TABLE domains (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(255) NOT NULL,
  master varchar(20) DEFAULT NULL,
  last_check int(11) DEFAULT NULL,
  type varchar(6) NOT NULL,
  notified_serial int(11) DEFAULT NULL,
  account varchar(40) DEFAULT NULL,
  change_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  UNIQUE KEY name_index (name)
) ENGINE=InnoDB;

CREATE TABLE records (
  id int(11) NOT NULL AUTO_INCREMENT,
  domain_id int(11) DEFAULT NULL,
  name varchar(255) DEFAULT NULL,
  type varchar(6) DEFAULT NULL,
  content varchar(255) DEFAULT NULL,
  ttl int(11) DEFAULT NULL,
  prio int(11) DEFAULT NULL,
  username varchar(12) DEFAULT NULL,
  password varchar(64) DEFAULT NULL,
  change_date timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (id),
  KEY rec_name_index (name),
  KEY nametype_index (name,type),
  KEY domain_id (domain_id)
) ENGINE=InnoDB;

CREATE TABLE supermasters (
  ip varchar(25) NOT NULL,
  nameserver varchar(255) NOT NULL,
  account varchar(40) DEFAULT NULL
);

GRANT SELECT ON supermasters TO pdns@localhost IDENTIFIED BY 'SuperGehaimesPasswort';
GRANT ALL ON domains TO pdns@localhost IDENTIFIED BY 'SuperGehaimesPasswort';
GRANT ALL ON records TO pdns@localhost IDENTIFIED BY 'SuperGehaimesPasswort';

Anschließend legen wir eine Domain an, z.B. dyn.meinedomain.de, und darin dann ein paar nötige Records:

INSERT INTO domains (name, type) VALUES ('dyn.meinedomain.de', 'NATIVE');

INSERT INTO records (domain_id, name, type, content) VALUES
(1, 'dyn.meinedomain.de', 'SOA', 'dns.meinedomain.de hostmaster.meinedomain.de 2013121900 1800 300 604800 3600'),
(1, 'dyn.meinedomain.de', 'NS',  'dns.meinedomain.de'),
(1, 'dyn.meinedomain.de', 'A',   '123.234.231.132');

Dabei sollte dns.meinedomain.de durch den DNS-Namen der IP-Adresse ersetzt werden, auf der der vorhin installierte PowerDNS zu erreichen ist. dyn.meinedomain.de ist die Subdomain, unter der später die dynamisch zu erreichenden Hosts angelegt werden.

Der A-Record (letzter Datensatz) ist später für den Apache nötig und sollte daher auch auf die IP-Adresse zeigen, auf der der Apache sitzt.

Nun muss PowerDNS noch konfiguriert werden, so dass er auch das MaSQL-Backend benutzt. Das geht, indem in der Datei /etc/powerdns/pdns.d/pdns.local folgende Zeilen ergänzt werden:

# MySQL Configuration
launch=gmysql
gmysql-host=localhost
gmysql-dbname=pdns
gmysql-user=pdns
gmysql-password=SuperGehaimesPasswort

Nach einem PowerDNS-Neustart mit service pdns restart sollte MySQL mit SHOW PROCESSLIST; ein paar Verbindungen des Users pdns anzeigen:

mysql> show processlist;
+-------+------+-----------+------+---------+------+-------+------------------+
| Id    | User | Host      | db   | Command | Time | State | Info             |
+-------+------+-----------+------+---------+------+-------+------------------+
|  7104 | pdns | localhost | pdns | Sleep   |  160 |       | NULL             |
|  7105 | pdns | localhost | pdns | Sleep   |   64 |       | NULL             |
|  7106 | pdns | localhost | pdns | Sleep   |   37 |       | NULL             |
|  7108 | pdns | localhost | pdns | Sleep   |  129 |       | NULL             |
| 30246 | root | localhost | NULL | Query   |    0 | NULL  | show processlist |
+-------+------+-----------+------+---------+------+-------+------------------+
5 rows in set (0.00 sec)

mysql>

So. Wenn das geklappt hat, sollte man mit dem Kommando dig @123.234.231.132 dyn.meinedomain.de any die in der Datenbank angelegten Einträge zu sehen bekommen.

# dig @123.234.231.132 dyn.meinedomain.de any

; <<>> DiG 9.7.3 <<>> @123.234.231.132 dyn.meinedomain.de any
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 6595
;; flags: qr aa rd; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 0
;; WARNING: recursion requested but not available

;; QUESTION SECTION:
;dyn.meinedomain.de.		IN	ANY

;; ANSWER SECTION:
dyn.meinedomain.de.	3600	IN	NS	dns.meinedomain.de.
dyn.meinedomain.de.	3600	IN	SOA	dns.meinedomain.de. hostmaster.meinedomain.de. 2013121900 1800 300 604800 3600
dyn.meinedomain.de.	3600	IN	A	123.234.231.132

;; Query time: 8 msec
;; SERVER: 123.234.231.132#53(123.234.231.132)
;; WHEN: Thu Dec 19 14:27:43 2013
;; MSG SIZE  rcvd: 140

Wenn das so weit klappt, können wir in die DNS-Konfiguration des Providers gehen und die Subdomain an unseren Server delegieren. Das geht ganz einfach, indem man im DNS-Zonefile der Zone meinedomain.de folgenden Record hinzufügt:

dyn                      IN NS      dns.meinedomain.de.

Jetzt (bzw. nachdem die TTL beim angefragten Cache abgelaufen ist) werden DNS-Anfragen für [irgendwas].dyn.meinedomain.de an unseren PowerDNS-Server dns.meinedomain.de weiter geleitet.

Wir gehen nochmal in die Datenbank und legen dort einen A-Record für den ersten DynDNS-Host an, z.B. so:

INSERT INTO records (domain_id, name, type, content, username, password) VALUES
(1, 'test.dyn.meinedomain.de', 'A', '127.0.0.1', 'benutzer', 'Gehaim');

Benutzername und Passwort werden später im DynDNS-Client angegeben. Die IP-Adresse ist hier erstmal 127.0.0.1. Man sollte den Record dann auch mit dig test.dyn.meinedomain.de abfragen können.

Jetzt brauchen wir ein Update-Script. Ich habe das Verzeichnis /opt/dyn erzeugt und darin die Datei update, ein Perl-Script mit folgendem Inhalt:

#!/usr/bin/perl

use strict;
use warnings;
use CGI;
use DBI;
use utf8;

binmode STDOUT, ':encoding(UTF-8)';

my $q = new CGI;
my $ip = $q->param('ip');
my $user = $q->param('user');
my $pass = $q->param('pass');
my $host = $q->param('host');

unless (
    $ip   =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ and
    $user =~ /^[a-zA-Z0-9\-\._]+$/ and
    $pass !~ /['"\t]/ and
    $host =~ /^\S+\./
) {
    print $q->header( -status => '400 Bad Request' );
    exit;
}

my $dbh = DBI->connect("DBI:mysql:database=pdns;host=localhost", 'pdns', 'SuperGehaimesPasswort', {'RaiseError' => 1, 'mysql_enable_utf8' => 1});
my $db_update = $dbh->prepare('UPDATE records SET content = ? WHERE name = ? AND TYPE = ? AND username = ? AND password = ?');

eval {
    $db_update->execute($ip, $host, 'A', $user, $pass);
};
if ($@) {
    warn 'SQL Error: ' . $@;
    print $q->header( -status => '500 Internal Server Error' );
    $dbh->disconnect;
    exit;
}

$dbh->disconnect;

print $q->header( -status => '200 OK' );

Die Datei muss ausführbar sein, also sollte man nach dem Anlegen chmod +x update machen!

Anschließend wird der Apache konfiguriert, indem man die Datei /etc/apache2/conf.d/dyn mit folgendem Inhalt anlegt:

Alias /dyn /opt/dyn

<Directory /opt/dyn>
    <FilesMatch ^(update)$>
        SetHandler cgi-script
        Options +ExecCGI
    </FilesMatch>
</Directory>

Das sorgt dafür, dass das Update-Script unter der URL http://dyn.meinedomain.de/dyn/update erreichbar ist. Es erwartet vier Parameter:

  • IP-Adresse
  • Benutzername
  • Passwort
  • Hostname

Folgendermaßen sieht also ein Aufruf aus:

http://dyn.meinedomain.de/dyn/update?ip=127.0.0.2&user=benutzer&pass=Gehaim&host=test.dyn.meinedomain.de

Ein Aufruf dieser URL sollte dafür sorgen, dass der A-Record für test.dyn.meinedomain.de aktualisiert wird und ab sofort auf 127.0.0.2 zeigt.

In meiner Fritz!Box 7390 kann ich als DynDNS-Provider "Benutzerdefiniert" wählen und dann die entsprechende URL als Update-URL mit Platzhaltern eintragen. Das sieht dann so aus:

http://dyn.meinedomain.de/dyn/update?ip=<ipaddr>&user=<username>&pass=<pass>&host=<domain>

Das Feld "Domainname" wird mit test.dyn.meinedomain.de gefüllt, Benutzername und Passwort mit benutzer und Gehaim.

Ein anderer Router im Dunstkreis meiner Administration hat ein altes FreeWRT. Dort habe ich den veralteten ez-ipupdate einfach abgeschaltet und durch das Skript /etc/ppp/ip-up.d/zzz_dyn_dns ersetzt, was folgenden Inhalt hat:

#!/bin/sh
# pppd provides $IFNAME and $IPREMOTE amd $IPLOCAL
wget -O /dev/null -q http://dyn.meinedomain.de/dyn/update?ip=$IPLOCAL\&user=benutzer\&pass=Gehaim\&host=test.dyn.meinedomain.de

Fertig.

Man kann in der Datenbank im Feld change_date ablesen, wann der Client das letzte Mal aktualisiert hat. Die Fritz!Box 7390 kann auch per HTTPS aktualisieren, der wget Client im FreeWRT will das nicht. Damit kann ich aber leben. Benutzername und Passwort werden ohnehin als GET-Parameter in der URL übertragen. Wobei es dem Perl-Script update eigentlich egal sein dürfte, ob die Parameter als GET oder POST kommen.

Was noch fehlt ist ein schickes kleines Web-Interface. Vielleicht kommt ja eines über die Feiertage... 😉

Seagate SeaTools von USB Stick booten

Mein Problem war heute folgendes: Der smartd meldet mir seit einigen Tagen per Mail so merkwürdige Sachen zu zwei meiner drei Platten aus dem ZFS-Pool:

The following warning/error was logged by the smartd daemon:

Device: /dev/sdd [SAT], 256 Currently unreadable (pending) sectors

bzw.

The following warning/error was logged by the smartd daemon:

Device: /dev/sdd [SAT], 256 Offline uncorrectable sectors

…was mich ein wenig beunruhigt.

Aus zwei Gründen habe ich die Platten (Seagate Barracuda Green 2TB) mal mit dem Programm SeaTools von Seagate getestet. Zum Einen sagt Seagate hier, dass die Werte von Fremd-Software (z.B. dem smartd) nicht unbedingt korrekt interpretiert werden. Zum Anderen hilft mit vielleicht ein Fehlercode aus den SeaTools bei der Abwicklung der Reklamation.

Problematisch an meinem Home Server ist, dass er weder ein Disketten- noch ein CD-Laufwerk hat. Also will ich am liebsten vom USB Stick booten. Die SeaTools gibt es zwar als ISO-Image, das bootet aber nicht, wenn man es direkt mit dd auf den Stick schreibt. Man muss folgendes tun:

Zunächst wird das herunter geladene ISO-Image (als root) gemountet:

mount SeaToolsDOS223ALL.ISO /mnt

Anschließend kann man die im gemounteten SeaTools-Image (unter /mnt) enthaltene Datei SeaTools.ima direkt mit dd auf den Stick schreiben:

dd if=/mnt/SeaTools.ima of=/dev/stick

…wobei /dev/stick natürlich durch das korrekte Device zu ersetzen ist!

Fertig. Der Stick sollte – ein geeignetes BIOS vorausgesetzt – nun die SeaTools booten.

Zu guter letzt sollte man noch aufräumen:

umount /mnt

Mittlere Maustaste der Logitech Zone Touch Mouse T400 unter Ubuntu

Meine neu erworbene Logitech Zone Touch Mouse T400 hat grundsätzlich ein kleines Makel: Die mittlere Maustaste a.k.a. Wischzone ist zweigeteilt. Der größere, vordere Teil dieser Fläche ist zum Scrollen gedacht. Ein Klick darauf simuliert das Drücken der Windows-Taste. Möchte man die „mittlere Maustaste“ Drücken, so muss man den Finger ziemlich krümmen und den hinteren, zum Handballen zeigenden Teil der Wischfläche drücken. Das ist unpraktisch.

Die Lösung für Linux ist folgende: Man mappt die (linke) Windows-Taste (Keycode 133) einfach auf die mittlere Maustaste und schon ist es egal, an welcher Stelle man die Wischfläche drückt. Das geht so:

xmodmap -e 'keycode 133 = Pointer_Button2'
xkbset m
xkbset exp =m

Damit’s nach einem Neustart nicht wieder weg ist, kann man das in ein Script schreiben, z.B. so:

#!/bin/bash
sleep 1
xmodmap -e 'keycode 133 = Pointer_Button2'
xkbset m
xkbset exp =m

Das macht man dann mit chmod +x ausführbar und weist seinen Window Manager an, es beim Login auszuführen.

Das sleep 1 braucht das Script, damit es vor dem Ausführen der Befehle eine Sekunde wartet. Vorher sind nämlich die nötigen Xorg-Komponenten noch nicht geladen und das Script täte dann einfach nichts. Bei mir reicht eine Sekunde, eventuell muss man die Zahl auch etwas erhöhen, je nach Performance des Rechners.

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.

Koffein für den Berglöwen

Folgendes Problem hatte ich seit dem Upgrade auf Mac OS Mountain Lion: Das System scheint wesentlich agressiver zu sein was das Einschlafen (idle sleep) angeht. Während beispielsweise meine Macports neu kompilierten schlief das System regelmäßig nach der in den Energie-Einstellungen festgelegten Zeit ein, da ich es nicht anderweitig nutzte.

Abhilfe schafft das von Apple mitgelieferte Kommandozeilen-Tool caffeinate.

Mit caffeinate kann man Prozesse einfach so ausführen wie man das von sudo kennt, also beispielsweise so:

caffeinate sudo port upgrade outdated

Man schreibt caffeinate einfach vor den Befehl, der auf jeden Fall abgeschlossen werden soll, bevor das System einschläft. Das klappt auch in Kombination mit sudo, time etc.

Ich nutze caffeinate auch gerne, um im Nachhinein dafür zu sorgen, dass der Rechner eine Weile an bleibt, das geht so:

caffeinate -s -t 3600

Dieser Befehl sorgt dafür, dass das System eine Stunde (3600 Sekunden) lang nicht einschläft. Der Bildschirm wird allerdings einschlafen – was ja bei der Ausführung von Jobs im Hintergrund nicht weiter stören dürfte.

caffeinate kennt noch ein paar weitere Optionen. Welche, verrät uns die Manpage (die man mit man caffeinate aufruft):

Available options:

     -d      Create an assertion to prevent the display from sleeping.

     -i      Create an assertion to prevent the system from idle sleeping.

     -s      Create an assertion to prevent the system from sleeping. This assertion is valid only when system is running on AC power.

     -u      Create an assertion to declare that user is active. If the display is off, this option turns the display on and prevents
             the display from going into idle sleep. If a timeout is not specified with '-t' option, then this assertion is taken with
             a default of 5 second timeout.

     -t      Specifies the timeout value in seconds for which this assertion has to be valid. The assertion is dropped after the speci-
             fied timeout. Timeout value is not used when an utility is invoked with this command.

Alternativer Heimnetzzugang

Da seit Freitag Morgen mein Unitiymedia Zugang gestört zu sein scheint (Kabelmodem bekommt bei der Frequenzsuche immer Timeouts) und Unitymedia natülich über das E-Mail-/Web-Formular nicht reagiert hat, konnte die Störung erst Freitag Nachmittag gemeldet werden. Da ich nicht davon aussgehe, das der Anschluß innerhalb der versprochenen 48 Stunden repariert wird, habe ich mich nach einem alternativen Netzzugang umgesehen. Da ich noch einen Linux Server laufen habe, der u.a DNS, DHCP, DB, IPv6-Tunnelendpunkt und Mediendaten hostet, lagt die Idee nah, diesen via UMTS ans Netz anzubinden und vorübergehend als Router zu nutzen, damit alle Geräte im Heimnetz wieder Netzzugang haben.

Die Anbindung des Mobiltelefons via USB-Tethering war äußerst einfach. Nachdem das Telefon, ein Xperia Pro mit CyanogenMod 10, USB-Tethering aktiviert hatte, wurde dieses als Netzwerkgerät an dem Debian Server erkannt (usb0). Um darüber ein Netzverbindung zu erhalten, muss dieses

  • mit DHCP aktivert konfiguriert,
  • die aktive Defaultroute deaktiviert und
  • Routing und Masquerading aktiviert werden.

Ganz Debian-Like kann man das in /etc/network/interfaces erledigen, wobei 10.0.7.1 die Gateway Adresse von eth0 ist, die normalerweise verwendet wird (genauer ist es die Adresse der FritzBox):

iface usb0 inet dhcp
        pre-up ip route del 0.0.0.0 via 10.0.7.1 || true
        up iptables -t nat -A POSTROUTING -j MASQUERADE
        up sysctl -w net.ipv4.conf.all.forwarding=1
        down sysctl -w net.ipv4.conf.all.forwarding=0
        down iptables -t nat -D POSTROUTING -j MASQUERADE
        post-down ip route add 0.0.0.0 via 10.0.7.1 || true

Mit

ifup usb0

fährt man die alternative Verbidung noch und mit

ifdown usb0

wieder runter.

Jetzt noch im DHCP-Server die Gatewayaddresse auf die Adresse des Servers ändern und schon haben (nach DHCP RENEWAL) alle Heimnetzgeräte wieder Netzzugang.


Leider war die Verbindung so gut, dass nach kurzer Zeit das Freivolumen des Mobiltelefons erschöpft war und nur noch als 56K Modem funktionierte, was wirklich nicht (mehr) sehr gut für eine Heimnetzanbindung taugt 🙂

Da ich aber noch über einen FONIC SurfStick mit Tagesflatrate besitze, muss dieser jetzt angebunden werden. Die Tagsflatrate hat ein Tageslimit von 500MB und ein Monatslimit von 5GB (und falls sich jemand zu einen Stick zulegt, kann er oder sie gerne meine Nummer „017678444083“ als Werbung angeben; damit erhalten beide Seiten je ein 5€ Guthaben).

Zurück zur Konfiguration. Der SurfStick verhält sich wie ein Modem also ist das Paket wvdial eine gute Wahl. Für die Enstsperrung der PIN, ist das Paket comgt eine Möglichkeit, meine Konfiuration kommt aber mitterweile ohne diese Pakt aus.

Die wvdial-Konfiguration liegt in /etc/wvdial.conf und sieht so aus

[Dialer SurfStick]
Modem = /dev/ttyUSB0
ISDN = off
Modem Type = USB Modem
Baud = 115200
Init = ATZ
Init2 = ATQ0 V1 E1 S0=0 &C1 &D2 +FCLASS=0
Init3 = AT+CGDCONT=1,"IP","pinternet.interkom.de"
Init4 = AT^SYSCFG=2,2,3FFFFFFF,2,4
Init5 =
Init6 =
Init7 =
Init8 =
Init9 =
Phone = *99#
Phone1 =
Phone2 =
Phone3 =
Phone4 =
Dial Prefix =
Dial Attempts = 1
Dial Command = ATDT
Ask Password = off
Password = ppp
Username = ppp
Auto Reconnect = on
Abort on Busy = off
Carrier Check = on
Check Def Route = on
Abort on No Dialtone = on
Stupid Mode = on
Idle Seconds = 0
Auto DNS = 0
Check DNS = 0

Da ich das Ganze aber nicht in der Shell starten und dann irgendwie mühsam in den Hintergrund legen möchte, wird diese Verbindung auch via interfaces aktiviert und deaktiviert. Hierzu sind die folgenden Eintragungen nötig:

iface ppp0 inet wvdial
        provider SurfStick
        pre-up ip route del 0.0.0.0 via 10.0.7.1 || true
        pre-up echo AT+CPIN=1111 > /dev/ttyUSB0
        up iptables -t nat -A POSTROUTING -j MASQUERADE
        up sysctl -w net.ipv4.conf.all.forwarding=1
        down sysctl -w net.ipv4.conf.all.forwarding=0
        down iptables -t nat -D POSTROUTING -j MASQUERADE
        post-down ip route add 0.0.0.0 via 10.0.7.1 || true

Dann kann man mit

ifup ppp0

die alternative Verbidung hochfahren und mit

ifdown ppp0

wieder runterfahren.

TimeMachine auf Debian squeeze mit netatalk 2.2

Beim Aufsetzen meines neuen Heimservers als Ersatz für das qnap TS-412 (mit dem ich aus verschiedenen Gründen unzufrieden bin) wollte ich die praktische TimeMachine-Funktion nicht verlieren und habe mich etwas schlau gemacht.

Unter Linux gibt es netatalk, mit dessen Hilfe AppleTalk etc. zur Verfügung steht. Mac OS (ab 10.7 alias „Lion“) verlangt nach mindestens Version 2.2, unter Debian squeeze gibt es diese Version aber noch nicht. Also habe ich sie kurzerhand selbst kompiliert. Folgendes sollten wir vorher machen, um die Dependencies zu installieren:

aptitude install avahi-daemon avahi-utils \
  libavahi-client-dev libcrack2-dev libgcrypt11-dev \
  libssl-dev libwrap0-dev libkrb5-dev libdb4.8-dev \
  db4.8-util libpam-cracklib

Nach dem Herunterladen der aktuellen netatalk-Version (bei mir 2.2.1) von http://netatalk.sourceforge.net/ und dem Entpacken wird das Paket dann mit folgenden Befehlen kompiliert und installiert:

./configure \
  --with-shadow \
  --with-pam \
  --enable-fhs \
  --enable-tcp-wrappers \
  --enable-timelord \
  --enable-overwrite \
  --with-pkgconfdir=/etc/netatalk \
  --enable-krb4-uam \
  --enable-krbV-uam \
  --with-cnid-dbd-txn \
  --with-libgcrypt-dir \
  --with-cracklib=/var/cache/cracklib/cracklib_dict \
  --enable-ddp \
  --enable-zeroconf \
  --enable-debian
make
make install

Die configure-Optionen habe ich aus dem Debian-Source-Päckchen der Version auf squeeze übernommen und sinngemäß ergänzt bzw. korrigiert, da sich die Optionen der neueren Version etwas unterscheiden.

Hier muss man unter Ubuntu 12.04 LTS einsteigen:

Anschließend ist noch etwas Konfigurationsarbeit zu tun. Zunächst sollte ein Verzeichnis oder vielleicht besser eine gemountete, eigene Partition für TimeMachine-Backups vorhanden sein. Anschließend aktivieren wir in der Datei /etc/default/netatalk folgende Optionen:

ATALKD_RUN=yes
ATALK_BGROUND=yes

Danach sollten wir noch folgendes tun:

echo "- -tcp -noddp -uamlist uams_dhx.so,uams_dhx2.so -nosavepassword -mimicmodel RackMac" >> /etc/netatalk/afpd.conf

echo "
# TimeMachine Volume
/opt/timemachine/$u TimeMachine options:tm,usedots,upriv allow:@users" >> /etc/netatalk/AppleVolumes.default

Letzteres sorgt dafür, dass /opt/timemachine/$u ($u = Username des vom Mac aus an dem Share angemeldeten Benutzers) als Volume „TimeMachine“ freigegeben wird. Erlaubt sind alle Benutzer, die Mitglied der Gruppe users sind (allow:@users).

Nun kann netatalk gestartet werden:

/etc/init.d/netatalk start

netatalk – genauer gesagt der atalkd – benötigt einige Zeit um zu starten. Der Start erfolgt im Hintergrund. Man sollte jetzt etwa eine Minute vergehen lassen vor dem Versuch, mit dem Mac eine Verbindung zum Volume herzustellen.

Damit das Ganze auch nach einem Neustart wieder funktioniert, müssen wir noch folgendes tun:

update-rc.d netatalk defaults

iMovie – Projekte auf Netzwerk-Share auslagern

Im Zuge des Verschiebens vieler meiner Daten vom iMac auf ein externes Storage ist mir aufgefallen, dass iMovie offensichtlich nicht so gerne mit Netzwerk-Shares zusammen arbeitet.

Abhilfe schafft folgender Befehl im Terminal:

defaults write -app iMovie allowNV -bool true

Einziges Problem ist nachher vielleicht die Zugriffsgeschwindigkeit. Video ist ja bandbreitenintensiv. Zum Bearbeiten kopiere ich die Sachen also lieber auf die lokale Platte, aber für’s Archiv kann ruhig das Netzwerk-Share her halten.