Skip to content

Sonicwall Rant

^ v M ><
Meine Verachtung für proprietäre Netzwerk"sicherheits"produkte ist über die letzten Tage wieder deutlich gestiegen. Objekt meines diesmaligen Hasses ist der Müll von Sonicwall, der sich somit von mir aus gleich auf neben Fortinet auf der Elektroschrotthalde einfinden darf.

Und zwar ging es darum, zwei bisher als Single Point of Failure betriebene Dienste mittels bestehender Loadbalancer-Infrastruktur auf Basis von Keepalived zu ausfallsicheren Clustern zu verbinden. Soweit so einfach, die redundanten Server installiert (bzw den Hot Standby auf Funktionstüchtigkeit überprüft), dem Loadbalancer zwei neue Virtual IPs hinzugefügt und die jeweiligen Maschinen zu Clustern verbunden.

Ein kurzer interner Funktionstest zeigt: Tut perfekt.

Als nächstes werden die geclusterten Dienste öffentlich freigegeben, dafür muss erstmal ein weiteres Loch in die Firewall gestanzt werden. Keine Sache für den ersten Cluster (erste virtuelle IP) und eigentlich auch nicht für den zweiten Cluster (zweite virtuelle IP). Als nächstes erfolgte natürlich der Funktionstest von extern, was beim ersten Cluster schlagartig mit Erfolg beschieden war.

Nicht so aber beim zweiten Cluster. Da war einfach keine Verbindung zu erstellen. Nun, woran könnte das liegen? Die Firewall-Regeln sahen alle gut aus, hmmm, kann das am Cluster liegen? Das ist ja doch eine komplexe Konfiguration mit Anpassungen auf Seiten des Loadbalancers und den geclusterten Maschinen. Also nochmals rigoros alles überprüft. Schaut gut aus. Und intern funktioniert's ja. Also doch die Firewall? Nochmals die Regeln überprüft... Mir sagen lassen, dass ich halt die Firewallregel irgendwie(tm) falsch erstellt hätte und sie nochmals prüfen solle. Gut. Nochmals unnötig Zeit auf die Überprüfung der Firewallregeln verschwendet. Die sehen einfach(tm) identisch zum anderen neuen Dienst aus. Die Regeln und das Netzwerk-Objekt gelöscht. Die Regeln und das Netzwerk-Objekt neu erstellt. Mir erneut sagen lassen, dass wohl die Regeln irgendwie(tm) falsch erstellt seien, da Sonicwall sowas ja noch nie gemacht hätte. Weiter am Loadbalancer debuggt und mit tcpdump und telnet herumgespielt. Erwartetes Ergebnis: Der Verbindungsaufbau von extern erreicht nicht einmal den Loadbalancer. Also zurück zur Firewall...

Und dann habe ich folgenden Versuch unternommen: Die Cluster-IP wurde von xxx.xxx.xxx.xxx zu xxx.xxx.xxx.xxy geändert. Danach wurde in der Firewall das Netzwerk-Objekt ebenfalls auf diese IP angepasst (also die eigentliche Regel habe ich nicht angefasst). Und zuletzt nochmals einen Test von aussen gefahren: BOOM! Katsching! Zugriff!

Als nächstes habe ich folglich *sämtliche* Firewallregeln darauf überprüft, ob sie Einfluss auf die zuerst genutzte IP haben könnten. Ich bin nicht fündig geworden.

Mit anderen Worten: Die bescheuerte Sonicwall diskriminiert ohne wirklichen Anlass eine gewisse IP-Adresse. Aber hey, was ist von einem Produkt zu erwarten, dessen Name mit NSA beginnt? Vermutlich sah die IP-Adresse einfach zu arabisch aus. Die ist ja schliesslich komplett in arabischen Ziffern notiert....

Jedenfalls verdient Sonicwall für solche Bugs in ihren überteuerten Produkten von mir einen Mittelfinger und ein "Fuck You", das Linus Torvalds Mittelfinger gegen nVidia wie einen Kindergeburtstag aussehen lässt. Es kann nicht angehen, dass meine teure Lebens- und Arbeitszeit verschwendet wird und versucht wird mein Selbstvertrauen kaputtzumachen.

Let's Encrypt

^ v M ><
Heute habe ich auf dem Server neue SSL-Zertifikate von Let's Encrypt installiert. Bislang habe ich selbstsignierte Zertifikate genutzt, um sichere Übertragung zu ermöglichen ohne dafür unverschämte Mengen Geld an dubiose Certificate Authorities zum Fenster hinauswerfen zu müssen. Let's Encrypt bietet kostenlose Zertifikate an und wird von den wichtigsten Browserherstellern unterstützt. Somit sollte nichts mehr dagegen sprechen, mein Blog über https aufzurufen.

Vorteil: Die NSA kann nicht mehr wissen, was genau gelesen wird :-)

s9y Twitterplugin Hack für Vermeidung von SSL

^ v M ><
Das Twitter-Plugin für mein Blog kündigt neue Einträge auf Twitter an, von wo sie dann wiederum auf Facebook veröffentlicht werden können. Leider mit einem Nachteil: Es wird immer das Schema verwendet (also http oder https), womit auch der Eintrag erstellt wurde. Es ist somit nicht möglich, für die Veröffentlichung http oder https zu erzwingen. In meinem Fall wäre es schön, wenn immer eine URL beginnend mit http:// verwendet werden würde, da ich zwar die Einträge über das verschlüsselte https:// schreibe, dafür aber ein selbstsigniertes Zertifikat benutze, das bei anderen Leuten eine Fehlermeldung im Browser erzeugt. Dies möchte ich nicht ändern, da kostenpflichtige Zertifikate einfach zu teuer sind, kostenlose Zertifikate wie z.B. von StartSSL meinen Ansprüchen nicht genügen und ich bei Mozilla's Let's Encrypt nicht wirklich Lust habe, Skripte zu installieren, die mir zuletzt noch die Webserver-Konfiguration zerschiessen.

Lösung: Ein schneller Hack des Plugins, um die veröffentlichte URL auf http:// festzunageln. Nachteil: Wenn ich das Plugin aktualisiere, muss ich daran denken, den Hack erneut vorzunehmen.

In der Datei serendipity_event_twitter.php muss in der Funktion generate_article_url die Zeile
$server = $urlparts['scheme'] . '://' . $urlparts['host'];
ersetzt werden durch:
$server = 'http://' . $urlparts['host'];

Dieser Eintrag dient mal wieder der Dokumentation und als Erinnerung an mich selbst, sowie als Erklärung für alle die sich wundern, warum der Artikel über Kuala Lumpur sich ohne Fehlermeldung aufrufen lässt :-)

Lazy mount für NFS

^ v M ><
Mit den Standardoptionen macht ein Systemstart mit per NFS verbundenen Laufwerken nur so lange Spass, wie der Server erreichbar ist. Ist der Server bzw das Netzwerk nicht erreichbar, so führen die Standardoptionen dazu, dass Debian während dem Systemstart minutenlang versucht zu mounten - und in der Zwischenzeit nicht weiterbooten will. Die Mountversuche lassen sich auch nicht mit CTRL-C oder anderen Tastenkombinationen abbrechen. Da kann man nur warten und sich ärgern... oder die /etc/fstab gleich von Anfang an mit den richtigen Optionen ausstatten.

Jetzt schauen meine Einträge für NFS-mounts so aus:
nas:/storage /mnt/storage nfs4 bg,retry=0,timeo=2,_netdev 0 0

Was passiert hier? Die Freigabe /storage vom Server nas wird nach /mnt/storage per NFS4 eingebunden. Schlägt der erste Mountversuch fehl (retry=0), geht der Mountprozess in den Hintergrund (bg) und versucht es dort weiter. Als Fehlschlag wird der Mountversuch gewertet, wenn die Antwort vom Server nicht innert 0.2s eintrifft (timeo=2). retry=0 zu setzen ist ganz wichtig, da der Wert bei Verwendung von bg standardmässig auf 10000 Minuten gesetzt ist. Da wartet man im dümmsten Fall fast eine Woche lang.
_netdev sorgt dafür, dass ein Mountversuch erst stattfindet, nachdem das Netzwerk gestartet wurde.

Sehr nützlich ist das auch für Notebooks.

Falls das Serverlog daraufhin Fehlermeldungen der Art "rpc-srv/tcp: nfsd: sent only 180288 when sending 524352 bytes - shutting down socket" enhält, sollte der Wert für timeo etwas erhöht werden.

Lesematerial dazu:
man 5 mount

Mal wieder Software-RAID retten

^ v M ><
Diesmal handelt es sich um eine Disk aus einem dubiosen NAS. Anscheinend pappt das /dev/sda1 und /dev/sda2 zu einem RAID-1 zusammen. Dies wird wohl so gemacht, um das System elegant aktualisieren zu können. Jedenfalls, musste da mal Hand an die Systemdaten angelegt werden.

Nun, Standardübung, nehmen wir mdadm:
mdadm --assemble /dev/md/0 /dev/sdb1 /dev/sdb2
mdadm: no recogniseable superblock on /dev/sdb1
mdadm: /dev/sdb1 has no superblock - assembly aborted

Wie? Kein Superblock? jaaa aber... parted ist da anderer Meinung:
parted /dev/sdb print
Model: WDC WD10 EARX-00N0YB0 (scsi)
Disk /dev/sdb: 1000GB
Sector size (logical/physical): 512B/512B
Partition Table: gpt

Number Start End Size File system Name Flags
3 15.7MB 528MB 513MB primary
1 528MB 2576MB 2048MB ext3 primary raid
2 2576MB 4624MB 2048MB ext3 primary raid
4 4624MB 1000GB 996GB ext4 primary

Was nun? Es handelt sich hierbei um ein Legacy-RAID ohne Superblock. Das setzt man nicht mit assemble sondern mit build zusammen:
mdadm --build /dev/md/0 --level=1 --raid-devices=2 /dev/sdb1 /dev/sdb2
mdadm: array /dev/md/0 built and started.

Nach der Resynchronisation erfolgt der erste mount-Versuch:
mount /dev/md/0 temp/
mount: unknown filesystem type 'linux_raid_member'

Na gut, erzwingen wir das Dateisystem:
mount /dev/md/0 temp/ -t ext3

Perfekt, alle Daten da :-)

Rewrite Spiele mit Apache

^ v M ><
Das Ziel: Existierende Dateien werden normal angezeigt. Bei nicht existierenden Dateien wird statt Error 404 der Erfolgsstatus 200 und ein Standard-Dokument ausgegeben.

Klingt einfach? Ist es nicht. Das liegt primär daran, dass dafür mod_rewrite braucht, wofür keine gescheite Dokumentation existiert (ist ja schön, wenn die offizielle Doku alle verfügbaren Variablen auflistet, sie aber nirgends erklärt...) und alle bestehenden Howtos irgendwo einen Fehler machen oder diesen zugegebenermassen sehr speziellen Fall überspringen.

So scheitern praktisch alles bestehenden Vorschläge an der fehlerhaften RewriteCond:
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d

-d und -f prüfen aber den absoluten Dateisystempfad, während REQUEST_FILENAME nur den Teil enthält, welcher in der URL nach dem Hostname steht, d.h. http://www.example.com/requested/filename.html. Somit werden diese Rewrite Conditions sowieso niemals zutreffen. Nun gibt es zwei Lösungen dafür. Entweder den ausschliessenden Weg, dass man alle existierenden Dateien von Hand explizit spezifiziert, z.B.
RewriteCond %{REQUEST_FILENAME} !^/robots.txt$
RewriteCond %{REQUEST_FILENAME} !^/favicon.ico$

Nur hat dies zwei Nachteile, es ist aufwändig und unflexibel. Na gut, die simplere Lösung ist es den vorangehenden Pfadnamen hinzuzufügen, dafür gibt es die glücklicherweise vom Namen her sprechende DOCUMENT_ROOT Variable (eine offizielle Dokumentation, wo diese beschrieben wird, ist aus irgendwelchen Gründen schwierig zu finden...), so dass eine funktionierende, flexible und universelle Regel etwa so aussehen könnte:
RewriteEngine on
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_FILENAME} !-f
RewriteRule ^.+$ /index.html [L]


virt-manager und Debian

^ v M ><
Dank virt-manager kann man auch mit kvm und xen per Klickibunti übers Netzwerk administrieren. Nur, unter Debian kann es vorkommen, dass alles korrekt eingerichtet ist (libvirtd läuft, User ist in der Gruppe libvirtd, /var/run/libvirt/libvirt-sock ist zugreifbar), trotzdem weigert sich der virt-manager mit der Meldung
"Unable to open a connection to the libvirt management daemon. Verify that: - The 'libvirtd' daemon has been started"

Liest man die Meldung weiter, so stösst man auf die Zeile
error: server closed connection: nc: invalid option -- 'U'

Die Lösung: Debian installiert standardmässig netcat-traditional. Das unterstützt aber die Option U nicht. Dafür wird netcat-openbsd benötigt. Folglich kann man das beheben mittels
aptitude install netcat-openbsd


Zum Glück ist Debian Stable ja sooo bugfrei.

IPv6

^ v M ><
Die Zukunft hat begonnen :-) Ich bin ab jetzt auch über IPv6 verbunden. Derzeit zwar nur per Tunnel, aber hoffentlich bald auch richtig.

Linux auf richtig grossen Partitionen installieren... trotz BIOS

^ v M ><
Schade, dass nagelneue Boards immer noch dieses eklige BIOS statt dem wesentlich moderneren EFI mitbringen. BIOS und Grub können nämlich nichts mit GPT anfangen, was für grosse Partitionen über 2TB aber notwendig ist. Und BIOS bzw MBR begrenzt die Partitionsgrösse auf anachronistische 2TB. Denn wenn man schon 4*2TB kauft, um ein anständiges NAS zu basteln, will man sich nicht lange mit Daten hin-und-her-Schaufelei von Partition A nach Partition B und zurück befassen. Doch auch dafür gibt es eine Lösung:

Es darf einfach keine Partition über 2TB Grösse existieren, auch nicht als Software-RAID-Verbund. Aber LVM darf sich darüber hinwegsetzen.

Daher mein Setup:
- Am Anfang der Platten eine kleine Partition von 128MB als RAID-1 für /boot
- Danach drei Partitionen à 666GB, welche jeweils als RAID-10 zusammengefasst werden
- Die drei RAID-10 Partitionen werden mittels LVM zu einem gigantischen logischen Volume zusammengefasst. Alternativ kann man natürlich auch ein RAID-5 nehmen, dann tauscht man 2TB mehr Festplattenplatz gegen massiven CPU-Overhead bei allen Zugriffen ein.

Zuerst mal mit einer Live-CD nach Wahl starten und die Disks vorbereiten. Zu beachten ist, dass so grosse Festplatten gerne mit 4k-Sektoren ausgeliefert werden:

cfdisk -h224 -s56 /dev/sda #erstellen von 4 Partitionen vom Typ FD (RAID autodetect) unter Beachtung der 4k-Sektorengrenze
sfdisk -d /dev/sda | sfdisk --force /dev/sdb #sfdisk mag die 4k-Sektorengrenzen nicht, daher erzwingen wir sie
sfdisk -d /dev/sda | sfdisk --force /dev/sdc
sfdisk -d /dev/sda | sfdisk --force /dev/sdd
mdadm --create --verbose /dev/md0 --level=1 --raid-devices=4 /dev/sd?1
mdadm --create --verbose /dev/md1 --level=10 --raid-devices=4 /dev/sd?5
mdadm --create --verbose /dev/md2 --level=10 --raid-devices=4 /dev/sd?6
mdadm --create --verbose /dev/md3 --level=10 --raid-devices=4 /dev/sd?7
pvcreate /dev/md[123]
vgcreate bigdisk /dev/md[123]
vgchange -a y bigdisk

Danach erstellt man ein paar logische Volumen nach Bedarf, startet die Setup-CD (in meinem Fall natürlich Debian Squeeze) und schon läuft die Sache rund. Partman wird sich zwar trotzdem beschweren, lässt aber die Installation zu. Und Grub installiert sich ganz normal im MBR.

Serverzurückgebastel

^ v M ><
mod_gnutls hab ich wieder durch mod_ssl ersetzen müssen. Grund:
[Tue Aug 17 09:21:32 2010] [notice] Apache/2.2.9 (Debian) mod_gnutls/0.5.1 configured -- resuming normal operations
page 7: illegal page type or format
PANIC: Invalid argument
PANIC: fatal region error detected; run recovery


Und die letzte Zeile wiederholt sich dann, bis das Logfile die Festplattenkapazität gesprengt hat. Dauert bei den paar freien GB auf dem vserver so ca 20 Minuten...

Umbauen und rumschrauben

^ v M ><
Ich hab ein Bisschen am Server umgebaut und in letzter Zeit einiger Änderungen vorgenommen:

1. Courier durch Dovecot ersetzt.
Dovecot ist viel cooler als Courier und kann viel mehr. Statt einem grausamen Flickwerk und Gebastel hab ich mit Dovecot eine Lösung, die mir alles mögliche aus einer Hand bietet. Postfix kann SASL-Authentifizierung direkt über Dovecot-SASL machen statt über diesen grauenhaften Courier-SASL-Umweg, bei dem sich Courier-SASL erst am IMAP-Server anmelden muss, weil Courier-SASL ja nicht mit verschlüsselten Passwörtern umgehen kann. Ausserdem bietet Dovecot eine integrierte Mailfilterung an und über ein Roundcube-Plugin kann man die Filter sogar Klickibunti setzen :-)

2. Postfix-policyd durch Spamhaus ersetzt.
Bislang hab ich zur simplen, ressourcenschonenend Spam-Bekämpfung auf Greylisting gesetzt. Mein Server ist nämlich massiv zu schwachbrüstig, als dass ich überhaupt nur dran denken könnte, Spamassassin einzusetzen. Das funktioniert zwar relativ gut, hält aber letztendlich nur Botspam fern. Stattdessen nutze ich nun zen.spamhaus.org, um mir unerwünschte Mailhosts vom Leib zu halten. Der Erfolg lässt sich in meiner Inbox sehen. Statt 1-3 Spams pro Tag hab ich seit zwei Monaten kein einziges mehr bekommen. Die Konfiguration ist denkbar einfach. Einfach in der /etc/postfix/main.cf die Direktive smtpd_client_restrictions um den Eintrag reject_rbl_client zen.spamhaus.org ergänzen und Postfix neu laden.

3. Apaches mod_ssl durch mod_gnutls ersetzt.
Dies hat zwei Vorteile: Erstens weniger RAM-Verbrauch, da mod_gnutls minimal schlanker ist. Zweitens kann mod_gnutls Server Name Indication. Das heisst, dass ich für jeden Virtual Host ein eigenes SSL-Zertifikat verwenden kann. Das ist wichtig, weil ich nur eine IP habe, auf meinem Server aber mehrere Domänen mit SSL-Unterstützung laufen.

4. Das Blog hat zwei neue Plugins spendiert bekommen.
Schon seit einiger Zeit finden sich Buttons zu diversen Social Bookmark Seiten unter jedem Blog-Eintrag. Lediglich den Facebook-I-Like-Iframe hab ich aus Datenschutzgründen rauskommentiert. Facebook braucht nicht zu wissen, wer mein Blog besucht. Die restlichen Symbole werden von meinem Server ausgeliefert. Die Dienste bekommen folglich nur mit, dass jemand meine Seite besucht hat, wenn der Eintrag dort registriert wird.
Da ich mich bei Flattr angemeldet habe, um diverse meiner favorisierten News-Seiten und Blogs einfach und unbürokratisch finanziell unterstützen zu können, hab ich auch grad einen Flattr-Button bei mir eingebaut. Natürlich erwarte ich keine Einnahmen dadurch, dafür bin ich wirklich zu irrelevant. Mich stört auch, dass die Flattr-Buttons vom Flattr-Server geladen werden. Damit bekommt Flattr leider jedesmal mit, wer auf meine Seite zugreift. Ich muss mal schauen, ob es ein besseres Plugin gibt oder ob ich die Zeit finde, das Plugin mal zu hacken, so dass ein statisches Flattr-Symbol von meinem Server ausgeliefert wird. Denn wieso sollte ich Flattr anders behandeln als Facebook?

Verdammte Trötzel-PCs

^ v M ><
Mein Server ist zwar noch keine zwei Jahre alt, aber dass PCs in ein ausgesprochenes Trötzelalter geraten können, war mir noch nie so bewusst wie heute: Erneut ist mir eine Festplatte ausgestiegen (diese verdammten Seagates!!! Sobald die 2TB-Generation da ist, steig ich wieder auf Samsung um!). Na gut, kein Problem, ist ja alles RAID-1. Also Platte raus. Ein Plattenwechsel bedeutet aber: Runterfahren, rausnehmen, hochfahren.

Hmmm... OK! Bei der Gelegenheit kann ich ja grad noch ein Kernel-Update machen und die Netzwerk-Konfiguration anpassen. Ich möchte ein gebridgtes OpenVPN einsetzen, wofür ich also mein lokales Netzwerkinterface in eine Bridge integrieren muss. Unter Gentoo an sich kein Problem, die genaue Vorgehensweise steht in der /etc/conf.d/net.example drin. Diese wurde rasch angepasst, während der neue Kernel (2.6.29.2, bislang lief 2.6.29.1, ergo ein an sich gefahrloses Minor-Update) durchkompiliert wurde.

Danach wurde die Kiste runtergefahren, Platte rausgenommen, wieder hochgefahren, angepingt, nie Antwort erhalten. Jaaa, man kennt das ja, bestimmt hab ich mich irgendwo in der Netzwerk-Konfiguration vertippt. Also Monitor und Tastatur angeschlossen und ans lokale Debuggen gemacht. Und da zeigen sowohl brctl wie auch ifconfig, ebenso die Link-Status-LEDs an Netzwerkkarte und Switch, dass alles in bester Ordnung sei. Dennoch geht gar nix über br0 raus. Na schön, die Bridge wieder abgerissen und die Netzwerkschnittstelle normal betrieben. Immer noch tot. Na gut, da wird wohl zufällig die Onboard-Netzwerkkarte der Platte über den Jordan gefolgt sein. Aber das ist kein Problem, 100MBit-Karten hab ich wie Sand am Meer rumliegen. Eingebaut, hochgefahren, konfiguriert. Und wen wundert's? Natürlich ging kein Mucks raus über die Karte. Aber der Switch sagt: "doch, doch, ich hab ne 100MBit-Verbindung". Langsam wurde ich ärgerlich. Könnt's an Switch oder Kabel liegen? Folglich hab ich meinen Laptop geholt und anstelle des Servers angeschlossen. Die Infrastruktur zeigte nach kurzem Test ihre absolute Funktionstüchtigkeit. Also hab ich ein faules Ei von Ersatzkarte erwischt? Man weiss ja nie, folglich nächste Karte rein. Test natürlich negativ. Hmmm, OK, nächste Karte? Negativ. Karte in anderen PCI-Slot? Negativ. Nochmals die interne Karte? Heee, hoppla, geht wieder? WTF??? Bridge-Konfiguration? Na einwandfrei. Nach einem Reboot? Einwandfrei.

So ganz nebenbei bemerkt: Das WAN-Interface zum ADSL-Router funktionierte die ganze Zeit ohne zu mucken.

Na wie auch immer: Ich hab jetzt eine Stunde lang sinnlos rumgebastelt um ein durch den Computer selbständig generiertes Problem zu beheben, das sich ebenso selbständig gelöst hat, wie es entstanden ist. Arschloch-PC. Scheiss Trötzelkind (das Drecksding hat bei mir einen akuten Anfall von Koprolalie hervorgerufen). Nächstes Mal fliegt er echt zum Fenster raus!

Regelbasiertes Routing

^ v M ><
Seit kurzem hab ich den Bedarf, gewissen Netzwerkverkehr über ein VPN zu tunneln. Da die direkte Anbindung aber wesentlich schneller ist als die Anbindung des VPN-Servers, wäre es eine eher mässig produktive Massnahme, einfach allen Verkehr übers VPN laufen zu lassen. Aber unter Linux sind auch anspruchsvolle Routinglösungen genial einfach. Alles, was man so benötigt, hat man in der Regel standardmässig dabei, nämlich die Tools iptables und ip. Mittels iptables werden die umzuroutenden Pakete identifiziert und markiert, mittels ip wird eine Routing-Regel und ein Routing-Tabelleneintrag erstellt. Wirklich total simpel:
iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE
TCP_PORTS="993 5222"
for PORT in $TCP_PORTS; do
iptables -t mangle -A OUTPUT -p tcp --dport $PORT -j MARK --set-mark 1
done

In diesem Fall werden einfach alle Pakete markiert, welche an einen bestimmten TCP-Port gesendet werden. Bei Bedarf können natürlich problemlos weitere Regeln (auch für UDP, ICMP oder bestimmte Zieladressen) gesetzt werden. Iptables setzt der Kreativität eigentlich keine Grenzen. Damit auch die Antworten eintreffen, müssen die Pakete geNATet werden. Nun benötigt man noch eine passende Routenkonfiguration:
ip rule add fwmark 1 table 1
ip route add default dev tun0 table 1
ip route flush cache

Sollte der VPN-Tunnel mal zusammenbrechen, muss einfachh nach dem Wiederaufbau der VPN-Verbindung die Route nochmals neu gesetzt und der Puffer nochmals geleert werden. Das war's.

Apache mit voll funktionalem mod_chroot in Debian

^ v M ><
Apache ist ein äusserst exponierter Dienst. Grad auf Servern mit vielen Seiten und zahlreichen PHP-Skripten ist das durchaus die grösste Bedrohung für die Serversicherheit. Daher empfiehlt es sich, der Absicherung von Apache besonders viel Aufmerksamkeit zukommen zu lassen. Eine wichtige Methode der Absicherung ist das chroot, in dem der Apache in ein Unterverzeichnis des Systems gesperrt wird. Gelingt ein Angriff auf ein Webskript, so sind für den Angreifer noch immer weitere Hürden in den Weg gelegt, bevor er sich im System einnisten kann.

Relativ einfach lässt sich ein chroot mittels mod_chroot einrichten. Es gibt auch unzählige Howtos im Internet. Aber leider kein einziges brauchbares. Apache ins chroot sperren ist an sich keine Sache. Die nötigen Pakete installieren, dann noch zwei Konfigurationseinträge vornehmen und gut. Gut? Nein! Denn damit geht eigentlich gar nichts mehr, ausser der Auslieferung statischer Webseiten und ein Bisschen Basis-PHP. Email? Tot! ImageMagick? Tot! Das muss nicht sein. Daher mein Howto für Apache mit mod_chroot, mod_php5, suhosin und PHP im safe_mode:

Zuerst müssen die nötigen Pakete installiert werden:
aptitude install apache2 libapache2-mod-chroot libapache2-mod-php5 php5-suhosin php5-imagick imagemagick dash msttcorefonts

Weitere PHP-Module können nach Bedarf installiert werden, wie z.B. php5-gd php5-imap php5-ldap php5-mcrypt php5-mysql. Vorsicht ist bei suhosin geboten, einige grössere PHP-Applikationen bekommen dadurch Probleme. Entsprechende Workarounds müssen dann per individueller Konfiguration eingebaut werden. Die meisten wichtigeren Applikationen haben in ihrer Hilfe oder im Supportbereich entsprechende Informationen.

Ein erster Härtungsschritt ist, dass Apache der Zugang zu einer Shell entzogen wird. Eine Shell ist nicht notwendig und nur ein weiterer potentieller Unsicherheitsfaktor. Also:
usermod -s /bin/false www-data


Nun müssen die Module aktiviert werden:
a2enmod mod_chroot && a2enmod php5


In der Datei /etc/apache2/apache2.conf muss folgende Zeile eingefügt werden:
ChrootDir /var/www


Als nächster Schritt wird das chroot gebaut. Das ist ein sehr langer und aufwändiger Prozess. Denn ins chroot müssen sämtliche ImageMagick-Binaries sowie ein SMTP-Server samt ihrer Abhängigkeiten kopiert werden. Ich hab das chroot wie schon bei Teamspeak mit den Thread Local Storage Bibliotheken gebaut. XEN-User sollten davon Abstand nehmen und die "normalen" Bibliotheken verwenden. Als Mini-Mailserver habe ich mich für esmtp entschieden, wer andere vorlieben hat, darf sich natürlich auch gerne ssmtp anschauen oder versuchen mini_sendmail zum Laufen zu überreden.

Mailer vorbereiten

Leider akzeptiert Debian nur die Installation eines MTA gleichzeitig. Da man als Systemmailer wohl lieber Postfix oder Exim einsetzt, muss hier etwas getrickst werden. Verwendet hier bitte für den Download der Pakete (auch wenn sie recht klein sind) den Debian Mirror EURER Wahl, damit sich die Last entsprechend verteilt. Ausserdem muss auf die Versionsnummer acht gegeben werden, die kann sich natürlich mit der Zeit ändern. .deb-Pakete sind mit dem Packer ar gepackt, dieser sollte standardmässig auf jedem Debian-System installiert sein.
mkdir ~/esmtp
cd ~/esmtp
wget http://mirror.switch.ch/ftp/mirror/debian/pool/main/e/esmtp/esmtp_0.5.1-4.1_i386.deb
wget http://mirror.switch.ch/ftp/mirror/debian/pool/main/libe/libesmtp/libesmtp5_1.0.4-2_i386.deb
ar xv esmtp_0.5.1-4.1_i386.deb
tar -zxf data.tar.gz
ar xv libesmtp5_1.0.4-2_i386.deb
tar -zxf data.tar.gz


Basis-Chroot bauen

Bei sämtlichen Befehlen, die nun folgen, wird davon ausgegangen, dass man sich im Verzeichnis /var/www befindet. Ist dem nicht so, so stimmen natürlich die relativen Pfade nicht mehr und das Howto schlägt fehl. Wie schon eingangs erwähnt, sollten XEN-User aus Performancegründen nicht die TLS-Bibliotheken verwenden.
cd /var/www
mkdir bin etc lib tmp usr
mkdir -p lib/tls/i686/cmov usr/lib/i686/cmov var/lib/php5 var/log/apache2 var/run
chown www-data:www-data tmp
chown www-data:www-data var/lib/php5
chown www-data:www-data var/log/apache2
chown www-data:www-data var/run
touch var/run/apache2.pid
ln -sf var/run/apache2.pid /var/run/apache2.pid
cp /bin/dash bin
cd bin
ln -sf dash sh
cd ..
cp /etc/mime.types etc
grep www-data /etc/passwd > etc/passwd
cp /lib/ld-linux.so.2 lib
cp /lib/libgcc_s.so.1 lib
cp /lib/libnss_dns.so.2 lib
cp /lib/tls/i686/cmov/libc.so.6 lib/tls/i686/cmov
cp /lib/tls/i686/cmov/libdl.so.2 lib/tls/i686/cmov
cp /lib/tls/i686/cmov/libpthread.so.0 lib/tls/i686/cmov
cp /lib/tls/i686/cmov/libresolv.so.2 lib/tls/i686/cmov
cp /usr/lib/libz.so.1 usr/lib
cp /usr/lib/i686/cmov/libcrypto.so.0.9.8 usr/lib/i686/cmov
cp /usr/lib/i686/cmov/libssl.so.0.9.8 usr/lib/i686/cmov

Dash wird als Shell mit minimalem Speicherbedarf installiert. Leider benötigt esmtp zwingend eine Shell, ansonsten verweigert er den Dienst. Dies ist natürlich ein klarer Sicherheitsmangel, der durch Workarounds wie safe_mode behoben werden muss.

Nun wird der Mailer ins chroot kopiert:
cp ~/esmtp/usr/bin/esmtp bin
cp -r ~/esmtp/usr/lib usr/lib


Und als nächstes wird der Mailer konfiguriert. Unter Annahme, dass auf dem Server z.B. ein Postfix oder Exim als Mailserver läuft, muss dazu die Datei /var/www/etc/esmtprc mit folgendem Inhalt erstellt werden:
hostname = 127.0.0.1:25
starttls = disabled


Ausserdem ist jetzt der Zeitpunkt gekommen, um PHP mitzuteilen, wie es mailen soll. Ausserdem kann man bei der Gelegenheit auch weitere Absicherungen vornehmen und z.B. den safe_mode einschalten. Dazu müssen ein paar Direktiven in der Datei /etc/php5/apache2/php.ini angepasst oder eingefügt werden. Beachte, dass die Shell in /bin liegt, während als safe_mode_exec_dir /usr/bin konfiguriert wird. PHP-Skripte können also nicht auf die Shell zugreifen.
safe_mode = On
safe_mode_exec_dir = /usr/bin
expose_php = Off
display_errors = Off
log_errors = On
sendmail_path = /bin/esmtp -t

ImageMagick und Schriften

Es müssen ein paar weitere Bibliotheken sowie die benötigten ImageMagick-Binaries ins chroot kopiert werden:
mkdir -p usr/lib/mime/packages/
mkdir usr/bin
cp /lib/tls/i686/cmov/libm.so.6 lib/tls/i686/cmov/
cp /usr/lib/libMagick.so.9 usr/lib/
cp /usr/lib/liblcms.so.1 usr/lib/
cp /usr/lib/libtiff.so.4 usr/lib/
cp /usr/lib/libjasper-1.701.so.1 usr/lib/
cp /usr/lib/libjpeg.so.62 usr/lib/
cp /usr/lib/libpng12.so.0 usr/lib/
cp /usr/lib/libXext.so.6 usr/lib/
cp /usr/lib/libXt.so.6 usr/lib/
cp /usr/lib/libSM.so.6 usr/lib/
cp /usr/lib/libICE.so.6 usr/lib/
cp /usr/lib/libX11.so.6 usr/lib/
cp /lib/libbz2.so.1.0 usr/lib/
cp /usr/lib/libxml2.so.2 usr/lib/
cp /usr/lib/libfreetype.so.6 usr/lib/
cp /lib/tls/i686/cmov/libdl.so.2 usr/lib/
cp /usr/lib/libXau.so.6 usr/lib/
cp /usr/lib/libXdmcp.so.6 usr/lib/
cp /usr/lib/mime/packages/imagemagick usr/lib/mime/packages
cp /usr/bin/animate usr/bin
cp /usr/bin/compare usr/bin
cp /usr/bin/composite usr/bin
cp /usr/bin/conjure usr/bin
cp /usr/bin/convert usr/bin
cp /usr/bin/display usr/bin
cp /usr/bin/identify usr/bin
cp /usr/bin/import usr/bin
cp /usr/bin/mogrify usr/bin
cp /usr/bin/montage usr/bin

Wer noch Schriften braucht, kann diese nach Bedarf installieren. Die Dateinamen der Schriftdateien müssen komplett in Kleinbuchstaben gehalten sein.
mkdir -p usr/share/fonts/truetype/msttcorefonts/
cp /usr/share/fonts/truetype/msttcorefonts/verdana.ttf usr/share/fonts/truetype/msttcorefonts/

DocumentRoot

Der Startvorgang des Apache verläuft in etwa folgendermassen:
  1. Apache startet ausserhalb der chroot-Umgebung und wertet die Konfiguration in /etc/apache2 aus.
  2. Apache schreibt sein PID in die vorgegebene pid-Datei
  3. Apache prüft die Existenz der DocumentRoot-Verzeichnisse gemäss Konfiguration in den vhosts.
  4. Apache öffnet die Logfiles gemäss Konfiguration in den vhosts.
  5. Apache wechselt ins chroot.
  6. Apache öffnet die Sockets.
  7. Apache wechselt zu einem unpriviliegierten User.
  8. Apache ist bereit. Sämtliche Anfragen werden nun im chroot bearbeitet. Die Ausführung von PHP-Skripten findet nun im chroot statt.

Folglich muss von den DocumentRoot-Verzeichnissen eine Schatteninstallation und eine Life-Installation vorgenommen werden. Wird in der Apache-Konfiguration z.B. die Direktive
DocumentRoot /var/www/htdocs/myhost

eingetragen, müssen folgende Verzeichnisse erstellt werden:
mkdir -p /var/www/htdocs/myhost
mkdir -p /var/www/var/www/htdocs/myhost

Effektiv arbeiten wird Apache dann mit dem Verzeichnis /var/www/var/www/htdocs/myhost, dorthin gehören folglich alle HTML-Dateien.

Apache starten, stoppen, neu laden

Jetzt kann man mal die ganze Sache testen und Apache starten. Ich habe festgestellt, dass mod_chroot leider die reload- und restart-Befehle zerschiesst. Folglich bleiben nur noch start und stop übrig. Um eine geänderte Konfiguration einzulesen, bleibt folglich leider keine andere Möglichkeit, als Apache erst zu stoppen und dann wieder zu starten. Bei stärker frequentierten Seiten freuen sich da vermutlich nicht alle User darüber. Sorry, aber dafür gibt's wohl keine Lösung.
/etc/init.d/apache2 start
/etc/init.d/apache2 stop