Beiträge mit dem Tag howto

Kleine Helfer beim PHP Debugging

Erstellt von am Sonntag, 24 April, 2011

Beim Erstellen von Anwendungen/Webseiten auf  PHP-Basis ist es recht praktisch, wenn man ohne großen Aufwand an bestimmten Stellen eines Skriptes die Variablen auslesen oder den Inhalt eines Arrays prüfen kann.

Die einfachste Methode dabei wäre natürlich, einfach mit echo $value bzw. print_r($array) den Wert ausgeben zu lassen. Das führt dann allerdings schnell dazu, dass zig echo-Befehle im Quellcode “rumliegen”, die man später wieder entfernen muss. Dazu kommt, dass in jedem Skript die Ausgabe neu definiert werden muss – also auch irgendwo immer Mehrarbeit und Zeitaufwand.

Wer sich ansatzweise auskennt weiß: Oft benötigte Funktionen / Codezeilen lagert man aus und verwendet sie wieder. Nachfolgend kurz meine Methode – die natürlich nicht der Weisheit letzter Schluss ist und auch mal wieder erweitert werden müsste. Wer sie für sich verwenden möchte: Viel Erfolg damit! Wer Anregungen, Ideen, Kritik hat: Her damit! :)

Idee und Umsetzung

Mein Wunsch war, auf jeder Seite meiner Anwendung einen Button zur Verfügung zu haben, über den ich den aktuellen Status/Inhalt von Arrays, Sessions, Konstanten etc. ein- und wieder ausblenden kann. Die Ausgabe sollte in einem extra Layer erfolgen, damit das Layout der Anwendung nicht gestört wird.

Zuerst das Grundgerüst:

<script type="text/javascript">

<!--
function show_hide()	{
	if(document.getElementById("debug").style.visibility == "visible") {
		document.getElementById("debug").style.visibility = "hidden";
	}
	else {
		document.getElementById("debug").style.visibility = "visible";
	}
}
//-->
</script>

<input type="button" value="Debug" onclick="javascript: show_hide();">

<div id="debug" style="position: absolute; top: 35px; left: 10px; min-width: 200px; height: 600px; visibility: hidden; overflow-y: scroll; border: solid 5px #c00; background: #fff; padding: 5px;">
	<table width="100%" style="border-collapse: collapse; background-color: #fff;">
		<tr>
			<td width="100%" style="font-family: consolas; font-size: 80%;">
				<pre>Hier kommt der PHP-Teil rein...</pre>
			</td>
		</tr>
	</table>
</div>

Die Abfrage der Daten in PHP erledigen dann die folgenden Zeilen, diese werden im HTML Grundgerüst zwischen <pre> + </pre> eingefügt.

<?php

$titleStart = '<p style="padding: 5px; background-color: #ccc; border-bottom: solid 3px #39c; font-size: 10pt;">';
$titleEnd = '</p>';

echo $titleStart.'DEBUG-Array-Daten'.$titleEnd;
print_r($debugInfo);
echo $titleStart.'SESSION-Daten'.$titleEnd;
print_r($_SESSION);
echo $titleStart.'GET-Daten'.$titleEnd;
print_r($_GET);
echo $titleStart.'POST-Daten'.$titleEnd;
print_r($_POST);

echo $titleStart.'SERVER[]-Daten'.$titleEnd;
print_r($_SERVER);
echo $titleStart.'ENV[]-Daten'.$titleEnd;
print_r($_ENV);

echo $titleStart.'deklarierte KLASSEN + KLASSENMETHODEN'.$titleEnd;
$arrClasses = get_declared_classes();
foreach($arrClasses AS $nClass) {
	$classList[$nClass] = get_class_methods($nClass);
}
print_r($classList);

echo $titleStart.'FUNKTIONEN'.$titleEnd;
$arrFunctions = get_defined_functions();
print_r($arrFunctions);

echo $titleStart.'KONSTANTEN'.$titleEnd;
$arrConstants = get_defined_constants();
print_r($arrConstants);
?>

Erläuterungen

Im HTML-Teil wird kurz das DIV “debug” mit den notwendigen Positionsangaben versehen. Bei mir soll der EIN-/AUS-Button in der linken oberen Ecke zu sehen sein. Das kleine JavaScript schaltet das DIV dann halt sichtbar oder nicht sichtbar, je nach aktuellem Status.

Das Array $debugInfo fülle ich im Quellcode wo notwendig mit den gewünschten Daten. Das ist zwar auch nicht groß anders, als einfach ein echo zu notieren, allerdings gefällt mir die Ausgabe in dem extra Debug-Layer besser, als wenn das Seitenlayout von der Debug-Ausgabe unterbrochen werden würde.

$_SESSION, $_GET und $_POST liefern die entsprechenden Daten, über $_SERVER und $_ENV hat man auch die Umgebungsvariablen immer im Blick.

Das Array $classList wird über eine Kombination der PHP-Funktionen get_declared_classes() und get_class_methods() befüllt. So lässt sich recht einfach kontrollieren, ob eine selbstdefinierte Klasse initialisiert ist und die enthaltenen Funktionen bereitgestellt wurden.

get_defined_functions() liefert alle zur Laufzeit verfügbaren Funktionen und get_defined_constants() dann logischerweise alle definierten Konstanten.

Einbindung

Damit die anzuzeigenden Daten in jedem Bereich der Anwendung abgerufen werden können, binde ich diese am Ende der index.php der Anwendung vor dem </body> ein – so ist sichergestellt, dass bereits alle Daten geladen wurden. Dazu genügen die folgenden Zeilen:

<?php
	#--> Debugging DIV anzeigen
	include 'debug_info.php';
?>

So sieht’s dann letztlich im Einsatz aus:

Erweiterungsmöglichkeiten

In der aktuellen Form hat das Skript einen Nachteil: es werden ALLE Klassen und Klassenfunktionen angezeigt, ALLE Konstanten, ALLE Funktionen. Die selbstdefinierten Sachen sind dann jeweils am Ende der Liste zu finden, was die Übersicht erschwert.

Das könnte man durch einen Filter umgehen, der eben nur die eigens definierten Klassen, Funktionen etc. darstellt. Die restlichen Werte (Systemstandards, von PHP vordefinierte Klassen, Konstanten etc.) ließen sich optional einblenden.

Optional möchte ich hier auch noch auf die Firefox AddOns Firebug und FirePHP hinweisen. Die Verwendung von FirePHP ist zwar deutlich komplexer, mag aber lohnend sein, sofern man noch mehr Möglichkeiten im Debugging nutzen möchte.

Hilfreiche weiterführende Infos zur Verwendung von FirePHP finden sich etwa bei bueltge, dem WordPress-Gott :) –> FirePHP verwenden, eine Einführung

fail2ban – die phpMyAdmin-Sucher bannen

Erstellt von am Freitag, 1 April, 2011

Schaut man sich bei Langeweile mal die Datei [/var/log/apache2/error.log] an, finden sich oft Einträge in der abgebildeten Form.

[error] [client 91.121.xxx.xxx] File does not exist: /var/www/PMA
[error] [client 91.121.xxx.xxx] File does not exist: /var/www/dbadmin
[error] [client 91.121.xxx.xxx] File does not exist: /var/www/pma
[error] [client 91.121.xxx.xxx] File does not exist: /var/www/dbadmin

Irgendwelche Spaßvögel versuchen hier also tapfer das phpMyAdmin-Verzeichnis zu finden – oder auch andere Sachen, wie etwa RoundCube, Foren-Verzeichnisse usw.

Da derartige unnötigen Log-Einträge auf Dauer einfach nur die Übersicht kaputtmachen und irgendwie schlichtweg nerven, wollte ich die gerne künftig weitgehend unterbinden. Nur wie?

fail2ban bringt’s

Des Admins Freund ist hier fail2ban, ein Tool, welches es erlaubt, basierend auf bestimmten Filterregeln Logfiles zu prüfen und bei einem auf den Filter passenden Fund eine bestimmte Aktion auszuführen. fail2ban bringt dabei einige vordefinierte Regeln mit, netterweise auch schon für mein spezielles Problemchen. In der Standard-Einstellung ist die Regel jedoch nicht aktiv – und dazu leider in der Version von fail2ban, die mit Debian Squeeze verfügbar ist, auch noch fehlerhaft. Also ist Nacharbeit angesagt…

filter.d/apache-nohome.conf

Hauptsächlich wichtig zur Konfiguration von fail2ban ist die [/etc/fail2ban/jail.conf] und die einzelnen Dateien unter [/etc/fail2ban/filter.d/].

Der für mich notwendige Filter ist in der Datei [/etc/fail2ban/filter.d/apache-nohome.conf] hinterlegt – das sieht dann so aus:

# Fail2Ban configuration file
#
# Author: Yaroslav O. Halchenko <debian@onerussian.com>
#
# $Revision: 716 $
#

[Definition]

# Option: failregex
# Notes.: regex to match failures to find a home directory on a server, which
# became popular last days. Most often attacker just uses IP instead of
# domain name -- so expect to see them in generic error.log if you have
# per-domain log files.
# Values: TEXT
#
# failregex = [[]client <HOST>[]] File does not exist: .*/~.*
failregex = [[]client (?P<host>\S*)[]] File does not exist:

# Option: ignoreregex
# Notes.: regex to ignore. If this regex matches, the line is ignored.
# Values: TEXT
#
ignoreregex =

Die wichtige Zeile ist dabei normalerweise diese hier (von fail2ban vorgegeben):

failregex = [[]client <HOST>[]] File does not exist: .*/~.*

Anhand dieses netten regulären Ausdrucks checkt nun fail2ban die error-Logs auf Einträge in denen der String “File does not exist” vorkommt. Testen kann man das mit dem Tool fail2ban-regex etwa wie folgt:

fail2ban-regex /var/log/apache2/error.log '[[]client <HOST>[]] File does not exist: .*/~.*'

Hier stellte sich bei mir dann Überraschung ein – es wurden keine Treffer gefunden und das trotz deutlich mehr als 100 vorhandenen Einträgen in dieser Form! Irgendwas läuft da also schief. Nur was? Es kann an sich ja nur der reguläre Ausdruck sein… Also einmal den Spürhund Google befragen – und siehe da, da ist tatsächlich ein Fehler drin.

Der korrekte Ausdruck muss wie folgt aussehen, dann funktioniert das auch:

failregex = [[]client (?P<host>\S*)[]] File does not exist:

[Update]

Unter folgendem Link ist auch noch mal eine Config genannt, bei der gezielt auf bestimmte Ordnernamen geprüft wird:

http://www.foosel.org/blog/2008/04/banning_phpmyadmin_bots_using_fail2ban

Außerdem hier noch einige Beispiele direkt aus dem Fail2Ban Wiki:

http://www.fail2ban.org/wiki/index.php/Apache

[Update Ende]

So, damit ist der Filter definiert. Damit fail2ban den Filter auch nutzt, geht’s nun zur jail.conf

jail.conf

[apache-nohome]
enabled = true
port = http
filter = apache-nohome
logpath = /var/log/apache*/*error.log
bantime = 604800
maxretry = 3

bantime gibt dabei an, für wie viele Sekunden eine IP gesperrt werden soll, wenn der Filter greift. Ich bin der Meinung, erwähnten Spaßvögeln soll für eine Woche der Spaß vergehen, daher setze ich die bantime auf 604800 Sekunden.

maxretry wiederum bestimmt, wie oft der Fehler auftreten darf, bevor die Sperre aktiviert wird.

Damit fail2ban die geänderte config berücksichtigt fehlt nur noch ein kurzer Neustart des Dienstes:

/etc/init.d/fail2ban restart

Erfolgs-Kontrolle

Das war’s auch schon – versucht nun jemand das phpMyAdmin-Verzeichnis oder irgendeine beliebige andere Anwendung im Webspace zu finden, ist nach 3 Versuchen Schluss mit lustig.

Kontrollieren lässt sich der Erfolg in der Datei [/var/log/fail2ban.log], hier finden sich bei einem Treffer Einträge ähnlich diesen hier:

fail2ban.actions: WARNING [apache-nohome] Ban 211.220.194.217
fail2ban.actions: WARNING [apache-nohome] Ban 41.203.119.18
fail2ban.actions: WARNING [apache-nohome] Ban 114.207.245.190

Schon werden die anfangs genannten Einträge im Apache Error-Logfile deutlich seltener auftreten, denn nach 3-maligem Auftreten sperrt ja nun fail2ban. Habe fertig!

3 Tipps für Linux-Suchtis

Erstellt von am Dienstag, 10 August, 2010

Kleine Skripte erleichtern das Admin-Leben ungemein. Mir geht’s dann oft nur so, dass ich nach einer gewissen Zeit überlege: “Wie war das gleich noch??”

Einige praktische Infos habe ich daher in diesem Beitrag zusammengefasst – für mich… und alle anderen die evtl. schon genau danach gesucht haben. ;)

Berechtigungen für Dateien und Unterordner rekursiv setzen

Sind die Berechtigungen in einem Ordner für Unterordner oder Dateien vollkommen verwahrlost, weil ein User z. B. in seinem Home-Verzeichnis hoffnungslos gewildert hat, lassen sich die Standard-Berechtigungen schnell wieder herstellen.

Man navigiert in den gewünschten Ordner und führt folgenden Befehl aus, um alle Ordner rekursiv  auf die Berechtigung “755” zu setzen:

find . -type d -exec chmod 755 {} \;

Um alle Dateien rekursiv auf644” zu setzen, wandelt man den Befehl leicht ab:

find . -type f -exec chmod 644 {} \;

Ausschlaggebend ist hier der Buchstabe nach type – “d” = directory und entsprechend  “f” = file.

Netzwerklaufwerke über SSH einbinden mit SSHFS

Die Backups der Server-Daten landen bei mir auf einem Netzlaufwerk, welches ich der Verschlüsselung wegen über SSH einbinde. Idealerweise wird die entsprechende Resource bereits beim Booten als Mount-Point bereitgestellt. Das lässt sich mit SSHFS und einem passenden Eintrag in die fstab sehr elegant  erledigen.

Erstmal SSHFS + Abhängigkeiten installieren:

apt-get install sshfs

Und dann den Eintrag in der fstab für’s “automount”:

sshfs#user_name@server_adresse:/pfad/auf/dem/server /mnt/mountpoint fuse auto,uid=XXXX,gid=XXXX,umask=0,allow_other 0 0

Genannte Angaben sind natürlich nur beispielhaft, “uid=XXXX” und “gid=XXXX” müssen natürlich durch die passenden Daten ersetzt werden. Die Anregung habe ich übrigens aus der Strato FAQ, da ich das bereits früher dieses Jahr getestete HiDrive für die Backups nutze. Unter genanntem Link gibt’s dann eben auch eine vollständige Anleitung.

Wichtiger Hinweis ist da übrigens, einen Eintrag in der /etc/rc.local vorzunehmen, damit das Laufwerk auch wirklich beim Neubooten eingebunden wird:

Sofern der automatische Mountvorgang nach einem Neustart nicht erfolgreich ist, kann es daran liegen, dass das Laufwerk beim Abarbeiten von “fstab” noch nicht bereit ist. Als Abhilfe tragen Sie bitte einfach in die Datei “/etc/rc.local” vor der Zeile “exit 0″ die beiden folgenden Zeilen ein:

sleep 20
mount -a

Den Wert 20 können Sie schrittweise verringern, bis es gerade noch reicht, um Ihr HiDrive nach einem Neustart erfolgreich zu mounten.

Und siehe da, tatsächlich – nach 2-3 Neustarts klappte es dann auch – bei mir steht der Wert jetzt auf 8 Sekunden.

Mail-Versand bei SSH-Login

Eine sehr praktische Sache fand ich den folgenden Tipp, den mir ein Kollege in einer Fachzeitschrift gezeigt hat. Der bewirkt, dass nach einem erfolreichen SSH-Login sofort eine Mail an eine frei festzulegende E-Mail-Adresse geschickt wird. Praktisch, weil ich auf einem Server, den ich betreue, durchaus den SSH-Zugang für bestimmte User erlaube (gezwungenermaßen), aber schon gerne wissen möchte wer denn da dran war, wenn was nicht mehr funktioniert. Nachher will’s ja immer keiner gewesen sein…

Folgende Zeile kommt dafür einfach in die Datei /etc/profile und schon weiß ich, wer’s dann nicht gewesen sein will:

echo 'Login on' `hostname` `date` `who` | mail -s "Login on `hostname` `who | awk '{print $5}'`" email@domain.de

Soweit so gut – hoffe zuvor genannte shortys helfen auch anderen.