Virtuelle Maschine zum Entwickeln von PHP-Anwendungen

Ab und zu muss ich mit PHP beschäftigen. Viele Dinge auf eKiwi sind mit PHP entwickelt. Da ich es nicht mehr so oft verwende wie früher, habe ich mittlerweile keinen Webserver und kein MySQL mehr dauerhaft lokal eingerichtet. Trotzdem wollte ich natürlich schnell loslegen, falls mal wieder etwas zu tun ist, wie einen Fehler zu beheben. Virtuelle Maschinen bieten sich hierfür an. Einfach alles darin installiert und bei Bedarf gestartet.

Eine solche virtuelle Maschine habe ich für PHP zusammen gestellt:

PHP VM

In dieser laufen ein Apache Webserver, PHP mit XDebug, MySQL mit phpMyAdmin und Netbeans als Entwicklungsumgebung. Für den schnellen Zugriff auf den Code habe ich auch Git und Subversion installiert. Man kann also direkt loslegen. Informationen und Downloadmöglichkeiten findet ihr hier. Die VM ist für VMware, den kostenlosen Player gibt es hier.

Eigener DynDNS Dienst Dynalias für dynamische IP

Wer sich zu Hause ein Heimnetzwerk aufbaut mit z.B. einem NAS oder einem eigenen Webserver, kommt schnell in die Verlegenheit, dass er auch von außerhalb über das Internet auf seine Daten zugreifen möchte. Kernproblem dabei ist, dass die jeweiligen DSL-Provider eine tägliche Zwangstrennung vornehmen, welche mit einem Wechsel der IP-Adresse daher kommt, so dass ohne dem Wissen um die IP-Adresse kein Zugriff möglich ist. Das  Problem ist meistens schnell gelöst, da es im Internet jede Menge Anbieter für Dynamische IP-Adressen (Dyn DNS) gibt; viele auch kostenlos. Jedoch ist es ggf. nicht jedermanns Sache sich einem solchen Dienst anzuvertrauen, weil die Anbieter vielleicht im Ausland sitzen oder man ein ungutes Gefühl hat, was der Dienst alles protokolliert und ggf. auswertet.

Deshalb gibt es sicherlich genügend Gründe die Einrichtung eines eigenen Dyn DNS Dienst in Erwägung zu ziehen. In den folgenden Zeilen wird ein möglicher Lösungsvorschlag beschrieben. Die erläuterte Umsetzung ist eine einfache Variante ohne tiefe Eingriffe in die Serverkonfiguration des Webhosting-Paket und ist geeignet für alle Anwendungen auf Basis HTTP, womit ein Großteil wie Webserver, OwnCloud, Seafile u.ä. abgedeckt ist. 

Voraussetzungen

Was wird gebraucht? Es wird mindestens ein Webhosting Paket mit PHP- und MySQL-Unterstützung inkl. einer eigenen Domain benötigt. Es sollte zudem die Möglichkeit bestehen eigene Subdomains, wie z.B. myhomenet.mydomain.de, anzulegen.
Man sollte weiterhin einen Router zu Hause im Einsatz haben auf welchem man einen Dyn DNS Dienst konfigurieren kann. In diesem Artikel erfolgt die Konfiguration am Beispiel der Fritz!Box 7390.

Prinzip

Nach der Zwangstrennung durch den DSL-Provider erfolgt die neue Anmeldung und der Router erhält vom Provider eine neue IP-Adresse zugeordnet. Sofern ein DynDNS Dienst im Router konfiguriert ist, versucht sich der Router bei diesem Dienst anzumelden, so dass die aktuelle IP-Adresse bereitgestellt werden kann. Deshalb werden wir im ersten Schritt eine PHP-Datei schreiben, welche auf unserem Webhosting Paket mit statischer IP-Adresse läuft, die aktuelle IP des Routers erfasst und diese in eine Datenbank schreibt. Erfolgt dann der Zugriff über z.B. myhomenet.mydomain.de so soll dort eine index.php ausgeführt werden, welche uns an das Heimnetzwerk weiter dirigiert. Es wird bei dieser Lösung also nicht in den DNS-Server eingegriffen.

Datenbank einrichten

Zuerst richten wir uns eine eigene Datenbank dafür ein. Das kann manuell über den Admin-Bereich zum Verwalten der Datenbanken bei dem Webhosting-Paket machen.

Innerhalb der Datenbank erzeugt man eine Tabelle namens userip mit den Spalten id, username, password, domain und ip. Namen für Tabellen und Felder sind natürlich Schall und Rauch und können nach eigenem Gutdünken angepasst werden.

CREATE TABLE  `deinDatenbankname`.`userip` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`username` TEXT NOT NULL ,
`password` TEXT NOT NULL ,
`domain` TEXT NOT NULL ,
`ip` TEXT NOT NULL
) ENGINE = INNODB;

Alternativ kann man sich nun ein oder zwei PHP-Seiten bauen um die Datenbank-Einträge zu verwalten: neue User anlegen, löschen, editieren u.s.w. Der Einfachheit halber lege ich meinen Datensatz mittels phpMyAdmin an.

Das Eintragen des Passworts erfolgt selbstverständlich nicht im Klartext sondern als Hash. Dazu kann man z.B. die PHP-Funktion crypt() verwenden.

Das Anlegen des Datensatz kann mit folgendem SQL-Statement erfolgen, wobei wir den Wert für die IP-Adresse leer lassen.

INSERT INTO  `deinDatenbankname`.`userip` (
`id` ,
`username` ,
`password` ,
`domain` ,
`ip`
) VALUES (NULL ,  ‚yourusername‘, ‚yourpasswordhash‘,  ‚yourdomain‘,  “);

PHP-Seite für IP Aktualisierung

Nun legen wir eine php-Seite namens useripupdate.php an über welche sich der Router nach der Zwangstrennung und dem damit verbundenen IP-Wechsel wieder anmeldet. Die neu zugeteilte IP-Adresse wird bei dem zugehörigen User in der Datenbank aktualisiert.

  1: <?php
  2: //Datenbank Zugangsdaten
  3: $dbhost = "localhost";
  4: $dbname = "datenbankname";
  5: $dbuser = "datenbankbenutzer";
  6: $dbpass = "datenbankpasswort";
  7: //Datenbankverbindung herstellen
  8: $conn = mysql_connect($dbhost, $dbuser, $dbpass);
  9: if(! $conn )
 10: {
 11:   die('Konnte Datenbank nicht verbinden: ' . mysql_error());
 12: }
 13: $db_selected = mysql_select_db($dbname, $conn);
 14: if (!$db_selected) {
 15:     die ('Kann Datenbank nicht benutzen : ' . mysql_error());
 16: }
 17: 
 18: //Parameter per GET auslesen
 19: $user = $_GET['user'];
 20: $pass = $_GET['pass'];
 21: $ip = $_GET['myip'];
 22: //Passwort-Hash aus dem übergebenen Passwort erzeugen
 23: $passhash = crypt($pass, "NprsdkwfpavvGnDKZlS8qv");
 24: 
 25: //Datenbank-Abfrage zu übergebenen User
 26: $query = "SELECT id, password FROM userip WHERE username='".$user."'";
 28: $sql_fetch = mysql_query($query );
 29: if (!$sql_fetch) {
 30:     die ('Konnte Abfrage nicht ausführen: ' . mysql_error());
 31: }
 32: $row = mysql_fetch_row($sql_fetch);
 33: //Auslesen der Datenbank Id und des Passwort-Hash aus der Datenbank
 34: $dbID = $row[0]; //id
 35: $hashFromDB = $row[1]; //passwordhash aus Datenbank
 36: 
 37: //Passwort-Hash auf Richtigkeit überprüfen
 38: if($hashFromDB != $passhash){
 39: 	die ('Ungültige Passwort'); 
 40: } 
 41: else{
 42: 	//Wenn Passwort richtig ist, dann IP-Adresse in Datenbank aktualisieren
 43: 	$query = "UPDATE userip "."SET ip='".$ip."' WHERE id='".$dbID."'";
 44: 	$sql_update = mysql_query($query);
 45: 		if (!$sql_fetch) {
 46: 			die ('IP Update konnte nicht ausgeführt werden: ' . mysql_error());
 47: 		}
 48: 	//diese Ausgabe wird als Statusrückmeldung für die Fritz!Box benötigt
 49: 	echo ("good ".$ip);
 50: }
 51: //Datenbankverbindung schließen
 52: mysql_close($conn);
 53: ?>

Bei der Anmeldung mittels dieses Script werden drei Parameter Benutzername (user), Passwort (pass) und IP-Adresse (myip) übergeben, welche mit $_GET eingelesen werden. Es wird dann anhand des Benutzernamen der zugehörige Datenbankeintrag ausgelesen, geprüft ob die Hash-Werte der Passwörter übereinstimmen und dann die aktuell übergebene IP-Adresse in der Datenbank aktualisiert.

Router einrichten

Die Router – Einrichtung erfolgt in diesem Fall am Beispiel der FRITZ!Box 7390. Um in die entsprechenden Einstellungen für die Dynamic DNS zu gelangen muss man ggf. noch den Expertenmodus oder die Erweiterte Ansicht in der Fritz!Box aktivieren. Das macht man, nachdem man sich in der Fritz!Box eingeloggt hat, über das Menü System –> Ansicht.

Nun wechselt man in das Menü Internet –> Freigaben und wählt den Reiter Dynamic DNS an.

fritzbox_dynamicdns

Man aktiviert nun “Dynamic DNS benutzen” und stellt den “Dynamic DNS Anbieter” auf “Benutzerdefiniert”. In dem Feld Update-Url trägt man die Adresse zu der useripupdate.php ein mit Platzhalter für die Parameter username, pass und myip, was dann folgende Form hat:

http://sub.domain.de/useripupdate.php?user=<username>&pass=<pass>&myip=<ipaddr>

Es ist dann noch der Domainname einzutragen, wobei dieser nicht als Parameter übergeben wird. Bei Benutzername und Kennwort ist der in der Datenbank angelegt Benutzername und das dort hinterlegte Kennwort (nicht Hash) zu verwenden. Diese werden als Parameter von der Fritz!Box übergeben und in dem Script useripupdate.php verwendet.

Damit man auf den eigenen Webserver im Heimnetzwerk Zugriff hat, müssen ggf. noch entsprechende Portfreigaben gesetzt werden:

fritzbox_portfreigabe

PHP-Skript zur Weiterleitung

In den Pfad für unseren statischen Zugriff (myhomenet.mydomain.de) packen wir nun noch eine PHP-Datei namens index.php, welche anhand des Domainnamen aus der Datenbank die aktuelle IP-Adresse ausliest. Es wird dann der Domainname durch die aktuelle IP-Adresse ersetzt und es erfolgt die Weiterleitung an das Heimnetzwerk.

  1: <?php
  2: //Datenbank Zugangsdaten
  3: $dbhost = "localhost";
  4: $dbname = "datenbankname";
  5: $dbuser = "datenbankbenutzer";
  6: $dbpass = "datenbankpasswort";
  7: //Datenbankverbindung herstellen
  8: $conn = mysql_connect($dbhost, $dbuser, $dbpass);
  9: if(! $conn )
 10: {
 11:   die('Konnte Datenbank nicht verbinden: ' . mysql_error());
 12: }
 13: $db_selected = mysql_select_db($dbname, $conn);
 14: if (!$db_selected) {
 15:     die ('Kann Datenbank nicht benutzen : ' . mysql_error());
 16: }
 17: //aufgerufenen Domainnamen auslesen, da über diesen die aktuelle IP aus der Datenbank abgefragt wird
 18: $domain = $_SERVER['SERVER_NAME'];
 19: //Datenbank-Abfrage zur aktuellen IP
 20: $query = "SELECT ip FROM userip WHERE domain='".$domain."'";
 21: $sql_fetch = mysql_query($query );
 22: if (!$sql_fetch) {
 23:     die ('Konnte Abfrage nicht ausführen: ' . mysql_error());
 24: }
 25: $row = mysql_fetch_row($sql_fetch);
 26: //Auslesen der IP-Adresse aus der Datenbank
 27: $ip = $row[0]; //ip
 28: //eingegebene URL analysieren
 29: //hier muss in die Trickkiste gegriffen werden und hinter der Domain ein ? eingegeben werden,
 30: //in der Form http://subdomain.domain.de/?verzeichnis1/datei1.htm 
 31: //damit er nicht die Datei in der Domain sucht sondern beim Fragezeichen abricht und die index.php aufruft
 32: $actual_link = "http://$_SERVER[HTTP_HOST]$_SERVER[REQUEST_URI]";
 33: $new_url = str_replace($domain, $ip, $actual_link);
 34: $new_url = implode("", explode("?", $new_url, 2));
 35: //Weiterleitung an Heimentz IP
 36: header("Location: $new_url");
 37: //Datenbankverbindung schließen
 38: mysql_close($conn);
 39: ?>

Problem bei dieser Variante mit der Weiterleitung über ein PHP-Script ist, dass man Unterverzeichnisse und Dateien nicht einfach in der Form

myhomenet.mydomain.de/verzeichnis1/datei2.htm

aufrufen kann, da dies ins Leere läuft und die index.php nicht ausgeführt wird. Aus diesem Grund greift man hier in die Trickkiste und ruft die Seite wie folgt auf:

myhomenet.mydomain.de/?verzeichnis1/datei2.htm

Das Fragezeichen bewirkt, dass nur der Part vor dem Fragezeichen aufgerufen wird, wodurch die index.php aufgerufen wird. Für die Weiterleitung wird im Skript dann das erste Vorkommen des Fragezeichen für die Weiterleitungs-URL entfernt. Es ist wichtig, dass nur das erste Fragezeichen in der URI entfernt wird, da sonst keine dynamischen Webseiten mit Parameterübergaben funktionieren.

Unschön! Aber damit kann man leben

Zum Schluss sei noch erwähnt, dass man, wenn man alles erfolgreich eingerichtet hat, in der Fritz!Box die folgende Meldung in regelmäßigen Abständen erhält:

Dynamic DNS-Fehler: Die Dynamic DNS-Aktualisierung war erfolgreich, anschließend trat jedoch ein Fehler bei der DNS-Auflösung auf.

Diese Meldung bedeutet keine Einschränkung der Funktionalität. Kann aber vermutlich auch nicht beseitigt werden, zumindest ist mir noch keine Lösung bekannt. Wer eine Lösung weiß: her damit!

Nach der erfolgreichen Anmeldung versucht die Fritz!Box anscheinend “Anfragen” an den Dynamic DNS-Domainnamen zur Verifikation der IP zu stellen. Da bei der hier vorgestellten Methode der DNS-Server nicht angepasst wird, sondern nur eine Weiterleitung erfolgt, stellt die Fritz!Box eine Differenz fest, was sie zu dieser Meldung veranlasst.

Da mich interessiert hat, wie diese “Anfrage” näher aussieht habe ich mittels eines Sniffer den Datenverkehr aufgezeichnet und mir angeschaut. Darin war zu sehen, dass die Fritz!Box ein DNS-Query an einen DNS-Server sendet. Der Query lautet auf den in der Fritz!Box eingetragenen DNS-Domainnamen. Zum Query kommt dann als Antwort die DNS-Response, welche neben den DNS-Domainnamen die IP-Adresse zurückgibt, welche wie schon vermutet nicht mit der IP der Fritz!Box übereinstimmt.

Server auf Raspberry Pi einrichten

In den voran gegangenen Artikeln hatte ich beschrieben, wie man die Ersten Schritte mit dem Raspberry Pi unternimmt und wie man sich einen Fernzugriff einrichten kann. Hier soll nun eine kurze Anleitung folgen, wie man auf dem Raspberry Pi einen Server installiert.

Mit Kanonen auf Spatzen

Installiert werden soll die Grundausstattung als LAMP-Server (Linux, Apache, MySQL, PHP). Der eine oder andere mag jetzt denken ‘das arme kleine Teil! Da gibt’s doch leichtere Server-Alternativen!’  Aber wir geben uns die volle Dröhnung.

System – Update durchführen

Die meisten Dokus empfehlen erst einmal ein System-Update zu machen. Normalerweise würde ich sagen ein Update kann nicht schaden, aber mit solchen Aussagen sollte man vorsichtig sein.  Das Update führen wir mit folgenden Befehlen durch:

pi@raspberrypi ~ $ sudo apt-get update    
pi@raspberrypi ~ $ sudo apt-get upgrade 

Und nach jedem Update empfiehlt sich natürlich ein Neustart per

pi@raspberrypi ~ $ sudo reboot                 

Benutzer und Gruppe anlegen

Für unseren Serverdienst legen wir nun noch den Benutzer und die Gruppe www-data an und weisen den Benutzer der Gruppe per usermod zu. In den meisten Fällen wird der Benutzer schon existieren.

pi@raspberrypi ~ $ sudo useradd www-data                                
pi@raspberrypi ~ $ sudo groupadd www-date                             
pi@raspberrypi ~ $ sudo usermod -a -G www-data www-data  

Wer mehr über Benutzer und Gruppen unter Linux wissen möchte, findet bei ubuntuusers.de eine recht gute Erläuterung.

Installation von Apache, PHP und MySQL

Mit dem Kommando

pi@raspberrypi ~ $ sudo apt-get install apache2        

wird das Apache2 – Paket geholt und installiert um anschließend mit den Befehlen

pi@raspberrypi ~ $ sudo apt-get install php5              
pi@raspberrypi ~ $ sudo apt-get install php5-mysql  

PHP5 und PHP5-MySQL zu installieren. PHP5-MySQL enthält die Libraries für den Zugriff auf die MySQL-Datenbanken. Wer will, installiert sich noch zusätzlich PHP5-XCache, was die Ausführung von PHP–Seiten beschleunigt, da nach einmaliger Interpretation der PHP–Seite der Binärcode (PHP-Opcode) im Cache gehalten wird und somit nicht bei jedem neuen Aufruf der Interpreter neu bemüht werden muss.

pi@raspberrypi ~ $ sudo apt-get install php5-xchache  

Nun noch den My-SQL-Server installieren. Während der Installation wird man dann noch aufgefordert ein Passwort für den MySQL-Root-User zu vergeben, was empfohlen wird, aber nicht zwingend erforderlich ist.

pi@raspberrypi ~ $ sudo apt-get install mysql-server  

Und wer seine Datenbanken lieber über den Browser administriert installiert sich noch phpmyadmin:

pi@raspberrypi ~ $ sudo apt-get install phpmyadmin   

Bei der Installation kommen dann noch ein paar Zwischenfragen, wie ggf. für welchen WebServer phpmyadmin eingerichtet werden soll sowie zur Datenbank – Einrichtung.

Jetzt noch testen

Als erstes Testen wir mit der standardmäßig bei der Installation angelegten index.html Datei. Zuvor nehmen wir aber eine kleine Änderung mit dem Nano – Editor vor, mit welchem man auf Konsolenebene Textdateien anschauen und bearbeiten kann. Dieser Schritt ist nicht unbedingt nötig; wir machen es einfach, weil wir es können:

pi@raspberrypi ~ $ sudo nano /var/www/index.html  

nanoeditor

In meinem Fall habe ich mal ein “Hura! Es geht” eingefügt. Jetzt rufen wir nur noch mit der uns vom Raspberry bekannten IP-Adresse die Seite auf und staunen, dass es funktioniert!

browser_index

PHP testen wir indem wir uns mit dem Nano – Editor eine kleine php – Datei anlegen und diese dann aufrufen:

pi@raspberrypi ~ $ sudo nano /var/www/info.php   

nano_phpinfo

browser_infophp

Nun fehlt noch der Test zum Aufruf von phpMyAdmin. Das hat nicht auf Anhieb geklappt und ist erst mal fehlgeschlagen. Ursache ist, dass in der Apache-Konfig-Datei ein entsprechender Verweis fehlt. Also öffnet man z.B. wieder mittels Nano die Datei

/etc/apache2/apache2.conf

und fügt dort folgende Zeile ein:

Include /etc/phpmyadmin/apache.conf

Und nun noch phpMyAdmin im Browser aufrufen und sich freuen dass alles geht.

browser_phpmyadmin

HTML Dateien von PHP parsen lassen

Will man in eine HTML-Webseite nachträglich PHP-Code einbauen, muss man normalerweise die Dateien umbenennen, damit diese auf .php enden. 

Es geht aber auch einfacher mit einer .htaccess Datei, sofern man einen Apache-Webserver einsetzt. Eine Textdatei erstellen, folgenden Inhalt einfügen. Als .htaccess abspeichern und auf den Webspace laden:

# HTML-Dateien von PHP parsen 
AddType application/x-httpd-php .htm .html

Anschließend werden die .htm und .html Dateien wie .php Dateien behandelt.