Wartungsseite für alle Apache vhosts eines Webservers realisieren

Über eine Lösung um einen einzelnen Apache Vhost mit einer Wartungsseite auszustatten habe ich bereits vor einigen Jahren hier geschrieben.

Jetzt stand ich vor der Aufgabe wegen Wartungsarbeiten auf meinem Webserver eine Wartungsseite für alle dort gehosteten Apache Vhosts zu schalten. Davor sitzt ein Gate-Server mit Apache und mod_proxy. Eine Lösung das zu realisieren wäre auf dem Gate-Server mit mod_rewrite nach dem Vorhandensein einer Maintenance-Datei zu schauen und wenn die Bedingung zutrifft alle Anfragen auf die Wartungsseite weiterzuleiten. Eine solche Lösung ist in Ansätzen unter anderem hier skizziert. Unschön finde ich dabei die unnötig vielen Prüfungen nach der Maintenance-Datei.

Ich habe nun eine andere Lösung realisiert, die in meinen Augen deutlich einfacher und eleganter ist. Auf dem Gate-Server lasse ich einen Apache-Vhost auf einem gesonderten Port laufen auf dem die Wartungsseite angezeigt wird. Für den Fall, dass ich die Wartungsseite vorschalten möchte, leite ich einfach allen HTTP und HTTPS Traffic auf die IP des Gate-Servers und den gesonderten Port um. Mit nur einer iptables Regel ist somit für alle vhosts auf dem Webserver eine Wartungsseite vorgeschaltet:

iptables -t nat -A OUTPUT -p tcp -d INTERNALWEBSERVERIP --match multiport --dports 80,443 -j DNAT --to-destination EXTERNALGATESERVERIP:PORT

Bei dem Apache Vhost auf dem Gateserver ist noch zu beachten, dass man nach Möglichkeit den aufrufenden Browsern / Bots etc. mitteilt, dass es sich um etwas temporäres handelt. Aus diesem Grund habe ich in dem Vhost zwei Dinge beachtet:

  1. Es wird ein HTTP Status Code 503 (Temporarily Unavailable) gesendet
  2. Es wird der HTTP Header Retry-After gesetzt

Im vhost sieht das wie folgt aus. Spannend sind die letzten 3 Zeilen. Meine Wartungsseite heißt übrigens auch 503.html

<VirtualHost *:PORT>
  ServerName maintenance.example.net
  ServerAdmin webmaster@example.net
 
  DocumentRoot "/var/www/maintenance"
 
  <Directory "/var/www/maintenance">
    Options -Indexes
    AllowOverride None
    Require all granted
  </Directory>
 
 
  ErrorLog "/var/log/apache2/maintenance.example.net_error.log"
  CustomLog "/var/log/apache2/maintenance.example.net_access.log" combined 
 
  RedirectMatch 503  ^/(?!503.html)  
  ErrorDocument 503 /503.html
  Header always set Retry-After "18000"
</VirtualHost>

Remote Kommandos auf Server via xinetd ausfuehren / Adminfun

Es gibt Sachen die machen Spass, z.B. diese Spielerei. Es geht darum auf einem Server ein Remotekommando / Shellskript via Webbrowser auszufuehren, ohne das dafuer irgendeine grosse Software-Remotemanagement-Loesung laufen muss. Die Antwort ist der gute alte (x)inetd verbunden mit einem kleinen PHP-Skript. Ich kommentiere das jetzt hier nicht weiter sondern schreibe es einfach mal runter:

Als erstes muessen die Programme installiert werden:

 aptitude install xinetd php5-cli

Danach die folgende Datei unter /etc/xinetd.d/test ablegen

# description: xinetd + php = fun
service test
{
        socket_type     = stream        
        protocol	= tcp
	wait            = no
        user            = root
	server		= /usr/bin/php5
	server_args	= /root/test.php
	disable		= no
}

Anschliessend die /etc/services editieren und die folgende Zeile einfuegen:

test		44444/tcp			# test

und nun noch diese PHP-Datei unter /root/test.php ablegen:

<?php
echo "+++ xinetd + php = fun +++
 
";
 
## read parameter
$handle = fopen('php://stdin','r');
$input = fgets($handle);
fclose($handle);
 
## split get parameter 
$out1=str_replace("GET /", "", $input);
$out2=str_replace(" HTTP/1.1", "", $out1);
$parameter=explode("/", $out2);
 
## check if we shall do sth. and if we know it
if (trim($parameter[0]) == "doStuff") {
 
  if (empty($parameter[1])) {
    echo "no job start requested...
    ";
  }
 
  elseif (! trim(empty($parameter[1])) AND trim($parameter[1]) == "psaux") {
    $out = shell_exec('ps aux');
    echo "$out";
  }
 
}
else {
  echo "no job start requested...
";
}
?>

Ich denke die PHP-Datei erklaert sich von selber und kann beliebig erweitert werden. Ein Aufruf von http://example.net:44444/doStuff/psaux gibt dann die Ausgabe des Befehls „ps aux“ wieder. Nun sind bei dem shell_exec keine Grenzen gesetzt… ;-) Wichtig daran ist, dass wir die GET Parameter via stdin auslesen, da der xinetd das Zeugs ja nicht per GET an das php-Skript weiterleitet. Aber was solls… Der Kreativitaet sind da keine Grenzen gesetzt.

Ich wuerde natuerlich noch den Zugriff ueber irgendeine iptables Regel beschraenken ala:

iptables -A INPUT -p tcp -m tcp --dport 44444 -s 1.2.3.4 -j ACCEPT
iptables -A INPUT -p tcp -m tcp --dport 44444 -j REJECT

Und ueber Security von wegen das Skript als root laufen zu lassen usw. reden wir hier nicht weiter, es geht ja auch nicht darum das als Superloesung zu verkaufen, sondern einfach als simple kleine Adminspielerei ;-)

KVM, interne Maschine, RDP Zugriff von extern

Wenn man mit KVM eine Maschine virtualisiert hat, und diese dann in ein eigenes internes Netz haengt, z.B. 192.168.X.X, dann ist es zwar normalerweise moeglich via NAT ins Internet zu gehen, aber wie komme ich von extern auf diese Maschine. Konkrete Fragestellung bei mir war: Wie komme ich von extern per Remotedesktop auf die Maschine?

  • per SSH mit X-Forwarding auf der physikalischen einloggen und dann mit rdesktop, Beispiel:
 rdesktop -k de -u MYUSERNAME -g 1280x1024 192.168.2.2
  • mit netten iptables Regeln

Als erstes generell erlauben, dass Traffic auch an die internen Maschinen weitergeleitet werden darf:

iptables -I FORWARD -d 192.168.2.0/24 -j ACCEPT

Danach den entsprechenden Dienst einrichten, in diesem Fall RDP:

iptables -t nat -A PREROUTING -p tcp -d 1.2.3.4 --dport 3389 -j DNAT --to-destination 192.168.2.2:3389

Die iptables Regeln habe ich bereits vor einiger Zeit von meinen Cousin Emil Wagner per Mail bekommen. Danke dafuer!

check_rogue – Nagios Plugin zum erkennen von rogue DHCP

Bereits vor einiger Zeit schrieb ich darueber, dass wir bei uns im Wohnheim Probleme mit einem rogue DHCP hatten. Vorlon schrieb in einem Comment, das auch das SANS Diary das Problem beschreibt. Auch Heise war das ganze inzwischen einen Artikel wert! Wir haben bei uns im Internettutorium verschiedene Gegenmassnamen ergriffen, die fuer andere evtl. auch von Interesse sein koennen, deswegen an dieser Stelle mal kurz festgehalten:

Sperrung via iptables

Bei uns in der Firewall wird die gesamte Range in der die „boesen“ DNS-Server stehen auf Port 53 gesperrt. Der Zugriff wird geloggt, und stuendlich checken wir die Logfiles, ob es einen Zugriff gab. Wenn ja, dann gibts ne Mail.

Die iptables-Regeln im Firewall Skript lauten wie folgt:

iptables -F ukr_dns
iptables -X ukr_dns
iptables -N ukr_dns
iptables -A ukr_dns -j LOG -m limit --limit 90/h --log-prefix "FW: MALDNS "
iptables -A ukr_dns -j REJECT
iptables -I FORWARD -p udp --dport 53 -d 85.255.112.0/255.255.255.0 -j ukr_dns
iptables -I FORWARD -p tcp --dport 53 -d 85.255.112.0/255.255.255.0 -j ukr_dns

Der dazugehoerige crontab-Einzeiler-Eintrag lautet wie folgt:

0 *     * * *    root    /bin/grep MALDNS /var/log/syslog | grep -v CRON | \
mail -e -s "MALDNS-Report /bin/date -I" vir-reports@mydomain.tld

check auf rogue DHCP mit Nagios

neben dieser mehr schadensbegrenzenden Massname wenn bereits etwas passiert ist, ueberpruefen wir unser Netz auf rogue DHCP Server mit einem Nagios-Skript. Das Skript ist ein Wrapper zu dhcp_probe.
Leider ist dhcp_probe nicht direkt fuer Debian Linux verfuegbar. Das bedeutet im Klartext, dass ein bisschen Handarbeit angesagt ist, zumal es auch unter Debian Linux erst nach einer Modifikation von libnet funktioniert.

Als erstes muss man natuerlich die benoetigten *-dev Pakete installieren und wenn nicht bereits passiert auch die build-essential

aptitude install libpcap-dev build-essential

Das installieren vom libnet-dev Paket aus apt wuerde das compilieren von dhcp_probe nicht zu einem Erfolg bewegen, da die in Debian etch enthaltene Version eine wichtige Funktion nicht mitbringt. Siehe dazu auch den entsprechenden Eintrag in der INSTALL.dhcp_probe. Deswegen lautet der naechste Schritt libnet herunterzuladen, entpacken und entsprechend zu patchen.

wget http://www.packetfactory.net/libnet/dist/libnet.tar.gz
tar -xzvf libnet.tar.gz

Wie in der bereits verlinkten INSTALL.dhcp_probe unter Punkt 2. beschrieben nun die beiden Dateien ./src/libnet_cq.c und ./include/libnet/libnet-functions.h bearbeiten, den entsprechenden Code unten einfuegen. Der Dreisatz aus ./configure, make sowie make install als root fuehrt einen dann zum gewuenschten Ergebnis.
Ab dieser Stelle ist nun auch ein compilieren von dhcp_probe von Erfolg gekroent.

wget http://www.net.princeton.edu/software/dhcp_probe/dhcp_probe-1.2.2.tar.gz

entpacken, compilieren und installieren. Wenn man bis zu diesem Punkt ohne Fehler gekommen ist, ist schon der grossteil der Arbeit geschafft. Es fehlt noch dhcp_probe zu konfigurieren. Eine Beispiel-Konfigurationsdatei liegt dem Quelltext mit bei. Einfach aus dem entpackten Verzeichnis heraus ein

cp extras/dhcp_probe.cf.sample /etc/dhcp_probe.cf

machen und durchlesen. Der wichtigste Eintrag ist der Punkt legal_server, bei dem evtl. intendiert vorhandene Server angegeben werden sollten. Uebrigens ein guter Punkt bei dem man testen kann, ob dhcp_probe auch gut funktioniert.

Wenn all diese Vorraussetzungen erfuellt sind, kommt es endlich zu dem Nagios wrapper Skript fuer dhcp_probe. Die folgende Datei /usr/lib/nagios/plugins/ ablegen:

Nun noch fix in Nagios ein command und definiert:

/etc/nagios2/commands.cfg

define command{
        command_name    check_rogue
        command_line    $USER1$/check_rogue
        }

und schon kann ich den Check auf dem Client einrichten, auf dem das Skript liegt und dhcp_probe installiert ist. Bei uns sieht das ganze dann so aus:

nag_rogue_ok

nag_rogue_detected

Ja, ich weiss, das das Plugin nicht schoen geschrieben ist, aber es tut seinen zweck. Und an dieser Stelle nochmal herzlichen Dank an Matthias, Alex und Sebastian bei der Hilfe das ganze zu entdecken und zu beheben etc. :-)