Alle Beiträge von adlerweb

[nvidia/qt5/kdenlive] Segfault on various KDE-software using NVIDIA 361.16

Uhm crap. The last system update just pulled a new beta version of NVIDIAs binary driver. Usually not that much of a deal, I went for testing due to a bug in their stable driver some years ago and never ran into a problem. Well, at least until now.  This time the testing-curse found its way into my system and I found myself no longer able to start my video editor kdenlive. Segmentation Fault. I suspected my setup first since I am running a rather unusual multihead configuration but could also with just one monitor I ran into the same problem. Well, looks like I have to dig deeper. Some gdb later at least a clue:

Program received signal SIGSEGV, Segmentation fault.
0x00007ffff0b640c5 in XScreenCount (dpy=0x0) at Macros.c:109
109	int XScreenCount(Display *dpy) { return (ScreenCount(dpy)); }
(gdb) bt
#0  0x00007ffff0b640c5 in XScreenCount (dpy=0x0) at Macros.c:109
#1  0x00007fffea7f50da in glXGetClientString () from /usr/lib/libGLX.so.0
#2  0x00007ffff7fed3b3 in ?? () from /usr/lib/qt/plugins/xcbglintegrations/libqxcb-glx-integration.so
#3  0x00007ffff7fed5b1 in ?? () from /usr/lib/qt/plugins/xcbglintegrations/libqxcb-glx-integration.so
#4  0x00007ffff686fc7b in QSGRenderLoop::instance() () from /usr/lib/libQt5Quick.so.5
#5  0x00007ffff68a19e5 in QQuickWindowPrivate::init(QQuickWindow*, QQuickRenderControl*) () from /usr/lib/libQt5Quick.so.5
#6  0x00007ffff694b68d in QQuickView::QQuickView(QWindow*) () from /usr/lib/libQt5Quick.so.5
#7  0x00000000006bd190 in ?? ()
#8  0x00000000006c3d7b in ?? ()
#9  0x00000000007af3cc in ?? ()
#10 0x000000000046346b in ?? ()
#11 0x00007ffff188e610 in __libc_start_main () from /usr/lib/libc.so.6
#12 0x0000000000463949 in _start ()

So XScreenCount or libGLX is the suspect. In my case GLX is provided by NVIDIAs binary driver (nouveau had limited multihead support last time I checked). Also some QT5-stuff is mentioned in the backtrace.

A bit of search engine voodoo later it turns out NVIDIA already acknowledged the problem. Aaron Plattner writes:

I reproduced the problem and tracked it down to this buggy code in Qt5’s qxcbglxintegration.cpp:

static bool vendorChecked = false;
    static bool glxPbufferUsable = true;
    if (!vendorChecked) {
        vendorChecked = true;
        const char *glxvendor = glXGetClientString(glXGetCurrentDisplay(), GLX_VENDOR);
        if (glxvendor && !strcmp(glxvendor, "ATI"))
            glxPbufferUsable = false;
    }

When this code is called during sddm-greeter startup, there’s no current GLX context, so this gets called with a NULL argument.

While here SDDM is not the problem I think kdenlive, which uses similar libraries, runs into the same problem. He also pushed a corresponding patch to NVIDIAs GIT-repository. Sadly the current HEAD is not 100% ABI compatible with the official driver release. Also I really didn’t want to get into my distributions xorg-packaging-foo. Ultimately I reverted to the current NVIDIA stable version 358.16 to get back into business. So – lesson confirmed: Beta releases can fix problems or cause problems. But who doesn’t like a bit of stability-gambling, right?

MySQL: Wuchernde Log-Dateien (mysqld-bin.xxx) zähmen

Je nach Konfiguration können MySQL-Server bei vielen Anfragen die Festplatte mit Binärdateien des Namens mysqld-bin.(zahl) füllen. Hintergrund: In diesen Dateien zeichnet der Server alle Anfragen auf, welche den Datenbestand verändern (also INSERT, UPDATE, etc). Das kann z.B. zur Datenrettung bei Abstürzen oder zur Replikation bei der Verwendung mehrerer Server hilfreich sein. Hat man jedoch nur beschränkt Speicherplatz zur Verfügung können diese Dateien zum Problem werden.

Log einmalig löschen

Benötigt man nur einmalig den Speicherplatz kann man sich mit einem passenden MySQL-Befehl behelfen. Hierzu schaut man sich die durchnummerierten Log-Dateien an und überlegt sich einen Zeitpunkt, der als neuen Startpunkt dienen soll. Transaktionen vor diesem Zeitpunkt können bei Ausfällen ggf. Beschädigt werden, daher empfielt es sich einen Zeitpunkt zu wählen, der kurz vor dem letzten Backup liegt. Der Befehl lautet dann z.B.:

PURGE BINARY LOGS BEFORE '2015-12-08 12:34:56';

Log abschalten

Duch Abschaltung der Log-Funktion ist das Problem schnell und dauerhaft behoben, allerdings verliert man die Möglichkeit mehrere Server zu verwenden und kann unter Umständen bei Ausfällen (Absturz, Stromausfall, etc) Daten verlieren. Zum Abschalten öffnet man die Konfigurationsdatei im Editor seiner Wahl – diese findet sich üblicherweise unter /etc/mysql/my.cnf oder /etc/my.cnf. Im Abschnitt [mysqld] sollte ein Eintrag „log-bin“ zu finden sein, eventuell noch mit einer Zahl oder einem Dateinamen dahinter. Diesen inaktiviert man durch Vorstellen eines Raute-Zeichens:

[mysqld]
...
#log-bin

Nachdem der MySQL-Server neu gestartet wurde legt er keine weiteren Dateien an. Alte Einträge müssen ggf. wie weiter oben beschrieben entfernt werden.

Loggröße einschränken

Ein guter Mittelweg ist es meist Logs anzulegen, um alle damit verbundenen Optionen nutzen zu können, das Alter jedoch zu beschränken. Ich mache täglich eine Sicherung, damit ist sichergestellt, dass ich Daten mit >24h nicht aus dem Log widerherstellen muss. Um bei Störungen des Backup jedoch etwas Puffer zu erhalten lasse ich Logs 3 Tage stehen. Hierzu nutzt man die oben beschriebene Konfigurationsdatei und sucht im Abschnitt [mysqld] nach dem Eintrag „expire_logs_days“. Ist er vorhanden kann er passend modifiziert werden, andernfalls fügt man die Zeile in der Nähe von log-bin ein. Um Logs nach 3 Tagen zu löschen lautet die Konfiguration:

[mysqld]
...
log-bin
expire_logs_days = 3

Nach einem Neustart des MySQL-Servers werden ältere Logs automatisch gelöscht, zukünftig werden sie nur noch 3 Tage aufbewahrt.

Durch die Änderung konnte ich „mal schnell“ mehr als 10GB an alten Logs vom betroffenen Server kratzen – bei einem embedded-System mit 16GB Speicher nicht grade unerheblich.

Gentoo dev-lang/yasm-1.2.0-r1: Segfault während compile

Narf? Segfault bei der Gentoo-Grundinstallation? Unschön. Verursacher ist dev-lang/yasm-1.2.0-r1, welches irgendwo als dependency gezogen wurde. Der Code ist dabei nicht sehr hilfreich:

built perfect hash table of size 512
./re2c -b -o gas-token.c ./modules/parsers/gas/gas-token.re
./re2c -b -o nasm-token.c ./modules/parsers/nasm/nasm-token.re
Makefile:4780: recipe for target 'gas-token.c' failed
make: *** [gas-token.c] Segmentation fault
make: *** Deleting file 'gas-token.c'
make: *** Waiting for unfinished jobs....
Makefile:4783: recipe for target 'nasm-token.c' failed
make: *** [nasm-token.c] Segmentation fault
make: *** Deleting file 'nasm-token.c'

Auch dmesg macht nicht viel schlauer:

[*] re2c[*]: segfault at 0 ip 00007f3cc90441d0 sp 00007fffaa8fdee0 error 4 in libc-2.20.so[7f3cc8fda000+18e000]
[*] re2c[*]: segfault at 0 ip 00007ff5681921d0 sp 00007ffccd9b4300 error 4 in libc-2.20.so[7ff568128000+18e000]

Libc sollte funktionieren, alle anderen Pakete zeigten keine Fehler, aber zur Sicherheit mal neu kompilieren: Keine Änderung. Auch mein Versuch die als unstable markierte Version dev-lang/yasm-1.3.0 zu verwenden wirft die gleiche Meldung. Müssen wir wohl tiefer rein.

Erster Schritt: Handarbeit. Im passenden Ordner unter /var/tmp/Portage führe ich make selbst aus – kein Fehler. Schlecht, denn ein Fehler den man nicht reproduzieren kann, kann man auch nur schwer beheben. Also anderer Weg: In /usr/portage/dev-lang/yasm finden sich die passenden ebuilds – mit ebuild *1.2.0* compile lässt sich der Vorgang starten, mit anderen Befehlen am Ende auch selektiv einzelne Schritte durchführen. Treffer, hier tritt der Fehler auch auf. Nachdem spielen mit der make.conf (Stichwort CFLAGS, USE, …) keine Bessung zeigte bleibt dann nur ein Blick ins Innere. re2c wird aus eigenen Quellen gebaut und ist nicht mit dev-util/re2c verbunden. Ich nenne nach einem fehlgeschlagenen Compile die re2c-Binary um und lege unter selben Namen ein Script an, welches die original-Binary mit strace zur Fehlersuche aufruft:

#!/bin/bash
strace ./re2c.org $*

Ergebnis:

open("/tmp/tmpfuxtE34", O_RDWR|O_CREAT|O_EXCL, 0600) = -1 EACCES (Permission denied)
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} ---
+++ killed by SIGSEGV +++
./re2c: line 2:  6931 Segmentation fault      strace ./re2c.org $*

Also hat es mit /tmp zu tun? Die Datei existiert nicht, schreibrechte sollte der Prozess auch haben. /tmp ist auf dem System nicht speziell eingerichtet, es liegt auf der root mit ext4 und rw,relatime,data=ordered. Auf anderen Systemen, welche keine Probleme haben, liegt es im RAM. Ist eventuell ext4 und dessen Funktionsumfang das Problem? Also schnell ein mount -t tmpfs -o noexec,nosuid tmp /tmp hinterher und nochmal versucht – fehlerfrei. Vermutlich fehlt in ext4 eine Dateioperation oder irgendwo kracht es beim asynchronen Betrieb zwischen löschen und neu erstellen. Warum es ohne portage/ebuild funktioniert ist dann immer noch die Frage. Wie auch immer: tmpfs stand ohnehin noch auf der Todo-Liste, worksforme.

ArchLinux: Update sperrt root-Login per SSH

Ouch – nicht schön. Nach dem Update einer remote-Kiste war der Login per SSH nicht mehr möglich. Schuld ist offenbar eine Änderung der SSH-Defaults: Während bisher der root-Login auch ohne explizite Angabe in der Konfiguration erlaubt war, muss dies nun in der Konfiguration mit einem PermitRootLogin yes manuell zugelassen werden. Sofern kein anderes Konto besteht heißt das nach einem Update dann: KVM oder Remote-Hands müssen her um den Eintrag zu setzen und den Server so wieder verwaltbar zu machen. Ein Hinweis vorab wäre natürlich einfacher gewesen :/.

Edit: Zu früh gemault: Im Announcement der OpenSSH-Leute ist die Änderung beschrieben, RTFM hätte also geholfen…

Potentially-incompatible Changes

[…]
* The default for the sshd_config(5) PermitRootLogin option has
changed from „yes“ to „prohibit-password“.

Arduino: boolean vs. byte

Möchte man einen einfachen Status im an/aus-Format notieren ist üblicherweise ein Boolean der Dateityp der Wahl. Technisch gesehen unterstützt der bei Arduino verwendete ATMega jedoch immer nur 8-Bit-Register, sodass auch ein Boolean (1-Bit-Wert) intern meist als Byte (8-Bit-Wert) abgespeichert wird. Klingt nicht sehr effizient, aber kann man per Hand tatsächlich „besser“ arbeiten?

Gehen wir von einem einfachen Beispiel aus: Wir möchten mehrere dieser Zustände speichern und auf deren Basis eine Entscheidung treffen. Für dieses Beispiel wird der Wert immer invertiert – in diesem Fall könnte man es auch kompakter Lösen, aber da es nur als Platzhalter für komplexeren Code gilt lasse ich das mal so stehen. Alle Beispiele sind Auszüge eines größeren Programms, die absoluten Größen also ohne weitere Aussagekraft.

Erster Versuch: Mit Boolean

boolean dir1 = false;
boolean dir2 = false;
boolean dir3 = false;
//[...]
if(dir1) {
  //Aktionen
  dir1 = false;
}else{
  dir1 = true;
}

//23.644 Byte Flash, 1.537 Byte RAM

Zum Vergleich mit einem einzigen „byte“-Wert, dessen einzelne Bits als Informationsspeicher verwendet werden sollen:

byte dir = 0x00;
//[...]
if(dir & 0x01) {
  //Aktionen
  bitSet(dir, 0x01);
}else{
  bitClear(dir, 0x01);
}

//23.650 Byte Flash, 1.535 Byte RAM

Schaut man auf das eigentliche Programm gewinnt die Variante mit Boolean – 6 Byte kleiner. Nicht wirklich verwunderlich – hat man ein ganzes Byte müsste sich direkt ein Sprung bei !=0 durchführen lassen. Mit Byte-Teilen ist ein Vergleich oder ein vorheriges UND nötig, welches zusätzliche Befehle und somit auch Speicher und CPU-Takte verschlingt. Schaut man jedoch auf den RAM dreht sich das Bild: Hier ist das einzelne Byte sparsamer. 2 Byte kommen mir jedoch etwas klein vor – ich fürchte hier hat der Compiler mit seinen Optimierungen einiges der boolean-Logik geschmissen, denn theoretisch sollte zur ersten Variante deutlich mehr Abstand sein.

Wie auch immer: Selbst ohne große Analyse zeigt sich, was ich schon erwartet hatte: Boolean spart Flash, Byte spart RAM. Welche Variante die sinnvollere ist hängt letztendlich vom jeweiligen Projekt ab. Da mir der RAM ausging war die Umstellung auf Byte eine gute Möglichkeit etwas zu sparen. Möglicherweise hätte es auch gereicht den Compiler nicht mit -Os anzuweisen, einen möglichst kleinen Code zu generieren.

Online ZVEI/5-Ton-Folgen Tongeber via HTML5/JS

tl;dr: Am Ende des Artikels kann man Tonfolgen abspielen

Kennt wer 5-Ton-Folgen? Nein? Dann holen wir mal wieder aus: Viele Rettungsorganisationen (DRK, ASB, JUH, MHD, Feuerwehren, THW, DLRG, Polizei, etc) arbeiten nach wie vor mit analogen Funkgeräten um untereinander zu kommunizieren. Auch die Alarmierung ist vielfach hierüber geregelt. Um eine selektive Alarmierung zu ermöglichen werden diese in Gruppen zusammengefasst und erhalten eine „Rufnummer“ (ID, Schleife). Um diese anzusprechen werden auf dem jeweils zuständigen Funkkanal Töne gesendet, welche den Zahlen entsprechen. In Deutschland sind diese Nummern 5 Ziffern lang und nach dem Standard des Zentralverbandes Elektrotechnik- und Elektronikindustrie (ZVEI) kodiert. Eine Sonderstellung nehmen Sirenen ein, diese reagieren – im Gegensatz zu den Sonstigen Funkempfängern – nicht direkt auf die Nummern, sondern benötigen einen zusätzlichen Doppelton um die Auslösung zu bestätigen. Zwar soll das Konzept bis spätestens 2010 auf ein digitales Verfahren (TETRA) umgestellt werden, offensichtlich machen sich in vielen Bereichen jedoch diverse Schwächen dieses Systems, welches auf der Handytechnik der frühen 2000er basiert, bemerkbar.

Warum ich mich damit beschäftigte? Nunja, einige Arbeitskollegen sind etwas Feuerwehrverrückt, in den jeweiligen Funkzentralen aktiv und können die für sie zuständigen Tonreihenfolgen aus dem Kopf. Und zucken so schön zusammen, wenn diese unvermittelt durch das Büro hallen. Also zumindest bis sie realisieren, dass dies technisch gar nicht möglich ist, denn in einem echten Alarmfall würde ihr Melder erst mal einen Alarmton generieren. Trotzdem: Praktischer Wecker.

Programme um diese Tonreihenfolgen zu erzeugen gibt es vielfach im Netz, aber ich wollte halt mal etwas Anderes, flexibel sein und war ohnehin für meine Wohnungsbeschallung mit HTML5 am experimentieren. Ergebnis ist ein HTML5-Basierter Tongeber – alles läuft im Browser, keine vorberechneten Töne.

Technischer Hinweis: Das Script ist schon etwas älter, einige Befehle des HTML5-Codes sind inzwischen als deprecated markiert und müssten mal erneuert werden.
Rechtlicher Hinweis: Kollegen über die Boxen nerven OK, sich am Funk zu vergreifen gefährdet jedoch Menschenleben und führt zu entsprechenden Strafen.
Rechtlicher Hinweis 2: Das Script hat keine Zulassung, also liebe FEZs: Bleibt bei eurer Software/Testsendern, auch wenn sie nicht so stabil sind 😉

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
		<meta http-equiv="content-type" content="text/html; charset=utf-8">
		<meta name="author" content="2014 Florian Knodt - www.adlerweb.info">
		<meta http-equiv="content-language" content="de">
		<meta name="generator" content="OnlineZVEIGenerator 0.1">
		<title>Online ZVEI Generator</title>
	</head>
    <body>	
		<script>
			
			var osc1,osc2;
			var sendstr = [];
			var cur = -1;
			
			var contextSwitcher = (
			window.mozAudioContext ||
			window.webkitAudioContext ||
			window.AudioContext ||
			window.oAudioContext ||
			window.msAudioContext
			);
		
			if (contextSwitcher) {
			var context = new contextSwitcher();
			}
			
			function toneStart(freq1, freq2) {
				osc1 = context.createOscillator();
				osc1.type = 'sine';
				osc1.frequency.value = freq1;
				gainNode = context.createGain ? context.createGain() : context.createGainNode();
				osc1.connect(gainNode,0,0);
				gainNode.connect(context.destination);
				gainNode.gain.value = .1;
				osc1.start ? osc1.start(0) : osc1.noteOn(0)
			
				if (freq2 > 0) { //Wenn Doppelfrequenzton
					osc2 = context.createOscillator();
					osc2.type = 'sine';
					osc2.frequency.value = freq2;
					gainNode = context.createGain ? context.createGain() : context.createGainNode();
					osc2.connect(gainNode);
					gainNode.connect(context.destination);
					gainNode.gain.value = .1;
					osc2.start ? osc2.start(0) : osc2.noteOn(0)
				}
			}
			
			function toneStop() {
				if(osc1) osc1.disconnect();
				if(osc2) osc2.disconnect();
			}
			
			function toneSequence() {
				toneStop();
				if (sendstr.length <= 0) {
					return;
				}
				ncur = sendstr.shift();
				
				if (cur == ncur) {
					cur = "R";
				}else{
					cur = ncur;
				}
				
				wait = 0;
				if (cur == "p") {
					//Pause 600ms
					wait = 600;
				}else if (cur == "F") {
					//Sirenenton Feueralarm
					toneStart(675,1240);
					wait = 5000;
				}else if (cur == "P") {
					//Sirenenton Probe
					toneStart(675,1860);
					wait = 5000;
				}else if (cur == "Z") {
					//Sirenenton Zivilschutz Alarm
					toneStart(675,825);
					wait = 5000;
				}else if (cur == "W") {
					//Sirenenton Zivilschutz Warnung
					toneStart(675,2280);
					wait = 5000;
				}else if (cur == "E") {
					//Sirenenton Zivilschutz Entwarnung
					toneStart(675,1010);
					wait = 5000;
				}else if (cur == "w") {
					//Weckton
					sendstr.unshift("WT", "WP", "WT", "WP", "WT", "WP", "WT", "WP", "WT", "WP", "WT", "WP", "WT", "WP", "WT", "WP", "WT", "WP", "WT", "WP", "WT", "WP", "WT", "WP");
				}else if (cur == 0) {
					toneStart(2400,0);
					wait = 70;
				}else if (cur == 1) {
					toneStart(1060,0);
					wait = 70;
				}else if (cur == 2) {
					toneStart(1160,0);
					wait = 70;
				}else if (cur == 3) {
					toneStart(1270,0);
					wait = 70;
				}else if (cur == 4) {
					toneStart(1400,0);
					wait = 70;
				}else if (cur == 5) {
					toneStart(1530,0);
					wait = 70;
				}else if (cur == 6) {
					toneStart(1670,0);
					wait = 70;
				}else if (cur == 7) {
					toneStart(1830,0);
					wait = 70;
				}else if (cur == 8) {
					toneStart(2000,0);
					wait = 70;
				}else if (cur == 9) {
					toneStart(2200,0);
					wait = 70;
				}else if (cur == "R") {
					toneStart(2600,0);
					wait = 70;
				}else if (cur == "WT") {
					toneStart(2600,0);
					wait = 200;
				}else if (cur == "WP") {
					wait = 200;
				}
				setTimeout(toneSequence, wait);
			}
			
			function toneFromForm() {
				str = document.getElementById("alarmstr").value;
				
				for (var i = 0, len = str.length; i < len; i++) {
					sendstr.push(str[i]);
				}
				
				toneSequence();
			}
			
			function strAdd(str) {
				document.getElementById("alarmstr").value += str;
			}
		</script>
		
		<h1>Javascript/WebAudioAPI ZVEI Generator</h1>
		<hr>
		<button id="feuer" class="beginDial" onclick="strAdd('F')">Feuer</button>
		<button id="probe" class="beginDial" onclick="strAdd('P')">Probe</button>
		<button id="zsa" class="beginDial"   onclick="strAdd('Z')">Zivilschutzalarm</button>
		<button id="zsw" class="beginDial"   onclick="strAdd('W')">Zivilschutzwarnung</button>
		<button id="zse" class="beginDial"   onclick="strAdd('E')">Zivilschutzentwarnung</button>
		<button id="zse" class="beginDial"   onclick="strAdd('w')">Weckton</button>
		<button id="zse" class="beginDial"   onclick="strAdd('p')">Pause</button>
		<hr>
		<input type="text" id="alarmstr" name="alarmstr" size="100" value="p12345p12345pFp54321p54321pw">
		<button id="zse" class="beginDial"   onclick="toneFromForm()">Alarm senden</button>
		<hr>
		<h2>Bedienhinweise</h2>
		Für ein korrektes Format folgendes beachten:
		<ul>
			<li>Vor dem Alarm müssen ~600ms Stille sein - ggf. Pause einfügen</li>
			<li>Eine ID besteht aus 5 Stellen</li>
			<li>Nach einer ID müssen ~600ms Stille sein - Pause einfügen</li>
			<li>IDs werden üblicherweise zwei mal wiederholt</li>
			<li>Nach zwei Aussendungen der ID kann optional ein Weckton oder Sirenenton gewählt werden</li>
			<li>Probe sowie Zivilschutzalarme sind abgekündigt</li>
		</ul>
		<hr>
		<h2>Technische Hinweise</h2>
		<ul>
			<li>Eine ID entspricht 70ms</li>
			<li>Eine Pause entspricht 600ms</li>
			<li>Folgen 2 gleiche Zeichen aufeinander wird automatisch ein Widerholton generiert</li>
			<li>Als Weckton wird der Wiederholton (2600Hz) mit 200ms-Muster verwendet</li>
			<li>Töne werden per WebAudioAPI generiert, die Qualität ist vom Browser abhängig</li>
		</ul>
    </body>
</html>

…und direkt zum Testen:

Javascript/WebAudioAPI ZVEI Generator



Update 2018-03-16: Chrome hat die numerische Typzuordnung entfernt, statt osc*.type = 1 muss es nun osc*.type = 'sine' heißen. Danke an Stappi für den Hinweis.

Fehlersuche: Wer brauch schon einen korrekten Status?

Als Backupleitung dümpelt hier nach wie vor eine DSL-Leitung herum. Ein billiger Hama WLAN-Router, welcher sich selbst nur als „Wireless Router“ identifiziert und mit einem „GoAhead-Webs“ HTTP-Server ausgestattet ist sorgt für den nötigen PPPoE-Uplink und ist an ein dediziertes VLAN angebunden. Genutzt wird sie kaum – lediglich bei Ausfällen des Kabelanschlusses (100MBit/s vs. 2MBit/s) oder zum Synchronisieren meines Webservers im RZ mit dem heimischen Server zu Backupzwecken darf sie mal Daten transportieren. Zusätzlich läuft noch ein Freifunk-Router (Hooray for automatic Failover) sowie ein HTTP-Gateway, welches einige Infos meines Systems im Internet bereitstellt. Für letzteres ist auf meinem Server ein kleines Script aktiv, welches regelmäßig per PHP die Statusseite des Routers abruft, die IP mit der des letzten Durchlaufs vergleicht und ggf. dem DNS-Server meiner Domain eine passende Info weitergibt. Selfmade dynamic-DNS, wie hier schon mal vorgestellt.

Seit einigen Wochen war nun das HTTP-Gateway nicht mehr erreichbar. Eh, kommt auf die „müsste man mal“-Liste. Heute dann endlich Zeit gefunden und nicht schlecht gestaunt. Die erste und nicht grade unabwegige Vermutung: DSL weg. Leitungsfehler, hängender Router – es gibt viele mögliche Ursachen. Ein Traceroute von der zuständigen VM zeigt aber nichts ungewöhnliches: Der Request geht über den Hama-Router, den POP des DSL-Anbieters und kommt am Ziel an. Connect auf die IP? Eyeup, der HTTP-Server antwortet. Also doch das DDNS-Script kaputt?

Ein Blick auf die Statuskonsole des dynamic DNS Scriptes zeigte folgendes: „Last IP: **** – Current IP:“. Aha, also kann er die aktuelle IP nicht von der Statusseite des Routers ermitteln. Seltsam – es gab keine Firmwareupdates oder Ähnliches. Also mal schnell ein manueller Blick mit dem Browser auf den Billigrouter – „PPPoE disconnected“ vermeldet dieser stolz. Eh, wat? Nochmal aktualisieren: Tatsächlich. PPPoE disconnected, keine IP. Trotzdem routet die selbe Kiste fröhlich weiter über den DSL-Anschluss ins Internet.

Whatever, das Gerät hat wohl seine 10 Jahre auf dem Buckel, da lohnt eine wirkliche Fehlersuche nicht. Vor allem da die DTAG ja weiterhin mit All-IP droht, welches dann ohnehin zur Kündigung des Anschlusses führen wird – schlechtes VoIP können auch andere Anbieter günstiger. Einen Reboot später weißt der Router wieder, was er tut und meldet auch brav die aktuelle IP. Ich kann nur vermuten, dass hier intern irgend ein Puffer überläuft und die synchronisation zwischen System und HTTP-Server zerschießt.

SSH Root-Login auf Debian Jessie aktivieren

„Mal schnell“ einen Server mit Debian aufsetzen – kann ja nicht so schwer sein. Dachte ich. Eine Kleinigkeit ist jedoch zu beachten: Mit der tasksel-Auswahl „SSH-Server“ wird dieser zwar installiert und passend gestartet, standardmäßig ist der Login jedoch nur mit dem zusätzlich erstellten User möglich, nicht jedoch als root.

Da ich für den Server Remote-Scripte laufen habe, welche bisher nicht auf ein User->sudo-System ausgelegt sind, ist für mich ein SSH root-Login unumgänglich. Um diesen auf dem Debian-System zu aktivieren öffnet man die Datei /etc/ssh/sshd_config und entfernt bzw. kommentiert (# davor) die folgende Zeile:

PermitRootLogin without-password

Apache Reverse Proxy mit LDAP absichern

Apache als Reverse Proxy hatten wir jetzt ja schon öfter, jedoch immer nur als 1:1-Druchlass. Ab und an möchte man jedoch Systeme nur einem beschränken Nutzerkreis verfügbar machen. Der Vorteil einer Filterung auf dem Proxy selbst: Dieser ist meist sicherer als die nachgeschalteten Systeme und hat durch seine Position innerhalb der DMZ bei einem Einbruch weniger Zugriffe auf die internen Systeme als die endgültig erreichten Systeme. Als Nutzerdatenbank ist LDAP, z.B. bei der Nutzung von Microsofts „Active Directory“ sehr verbreitet.

Um den Ordner /test abzusichern lässt sich folgender Codeblock nutzen, welcher das vorherige ProxyPass/ProxyPassReverse ersetzt. Ohne Proxy ist der Block natürlich auch zur Absicherungen „normaler“ Webseiten nutzbar.

 #Test LDAP
<Location /testldap>
    Require all denied
    ProxyPass http://intern.adlerweb.info/test
    ProxyPassReverse http://intern.adlerweb.info/test
    AuthLDAPBindDN "CN=apacheuser,OU=Systemaccounts,OU=Intern,DC=Adlerweb;DC=info"
    AuthLDAPBindPassword "1234"
    AuthLDAPURL "ldap://dc1.intern.adlerweb.info/ou=Intern,dc=Adlerweb,dc=info?sAMAccountName?sub?(objectClass=*)"
    AuthType Basic
    AuthName "Adlerweb ADS Auth"
    AuthBasicProvider ldap
    # Important, otherwise "(9)Bad file descriptor: Could not open password file: (null)"
    AuthUserFile /dev/null
    require valid-user
</Location>

Hiermit dürfen alle Nutzer in der Organisationseinheit „Intern“ der Domäne „Adlerweb.info“ auf die Ressourcen zutreffen (valid-user).

Es sind auch spezifischere Zuweisungen möglich – über den Nutzernamen:

Require ldap-user "adlerweb"

…über die DN

Require ldap-dn CN=adlerweb,OU=Intern,dc=Adlerweb,dc=info

Für Gruppen gibt es mehrere Möglichkeiten:

Require ldap-group CN=Testgruppe,OU=Intern,dc=Adlerweb,dc=info

ist die klassische Methode, macht jedoch mit Active Directory Probleme: Nutzer in der genannten Gruppe haben fehlerfrei Zugriff, sind jedoch rekursive Gruppen vorhanden schlägt die Authentifizierung fehl. Als Ausweg kann man hier einen LDAP-Filter verwenden – nicht ganz so schön, dafür funktionierend:

Require ldap-filter memberof:1.2.840.113556.1.4.1941:=CN=Testgruppe,OU=Intern,dc=Adlerweb,dc=info

Quellen:
http://serverfault.com/a/424706

Outlook Anywhere & Co über Apache als Reverse Proxy

Microsoft Exchange ist ein in kleinen und mittelständischen Firmen verbreiteter Mail/Groupware-Server, welcher sich durch grafische Verwaltbarkeit und gute Integration mit den Office-Produkten des Herstellers auszeichnet. Aus Sicherheitsgründen kann es Sinn machen diesen Server nicht direkt ins Internet zu setzen, sondern die eingehenden Anfragen über ein vorgeschaltetes System zumindest grob filtern zu lassen. Ähnliches hatte ich für Webseiten bereits im Artikel „Apache als Reverse Proxy“ vorgestellt.

Exchange geht natürlich wieder eigene Wege – es werden viele Hardcoded-Pfade und ungewöhnliche Protokolltricks genutzt, welche entsprechend umgesetzt werden müssen. Hier gehe ich von einem Exchange-Server aus, welcher bereits vollständig für Zugriffe eingerichtet ist. Als Reverse Proxy kommt Apache 2.4.x zum Einsatz, Clients sind verschiedene Mobilgeräte (Android, iOS, Windows Phone) sowie Outlook 2013. Extern erreichbar ist der Proxy unter „mail.adlerweb.info„, der interne Server ist als „exchange1.lan.adlerweb.info“ bekannt.

Erster Schritt: OWA & Co.

OWA (Outlook Web Access) und OMA (Outlook Mobile Access) sind HTTP-basierte Browseroberflächen für den Postfachzugriff, vergleichbar mit den üblichen Webmailern vieler Anbieter. Da diese im Prinzip nur Webseiten sind ist die Konfiguration schnell erledigt. Zu beachten ist, dass Apache bei den URLs auf Groß- und Kleinschreibung achtet, wer also technikferne Benutzer hat sollte ggf. passend vorsorgen. Mit diesen Zeilen ist der Abruf per Browser schon mal möglich.

SSLProxyCACertificateFile /etc/ssl/private/interneca.crt

ProxyPreserveHost On
SSLProxyEngine on

# http://stackoverflow.com/questions/3889574/apache-and-mod-proxy-not-handling-http-100-continue-from-client-http-417
RequestHeader unset Expect early

# Rewrite the WWW-Authenticate header to strip out Windows Integrated Authentication (NTLM) and only use Basic-Auth -> http://social.technet.microsoft.com/Forums/exchange/en-US/7eb1f39f-c53f-49aa-9038-6962bfc386ca/autodiscover
SetEnvIf user-Agent ".*MSIE.*" value BrowserMSIE
 Header unset WWW-Authenticate
 Header add WWW-Authenticate "Basic realm=DomainLogon"

RequestHeader unset accept-encoding
ProxyPreserveHost On

#Outlook Web Access (Exchange >=2007)
ProxyPass /owa https://exchange1.lan.adlerweb.info/owa
ProxyPassReverse /owa https://exchange1.lan.adlerweb.info/owa
ProxyPass /OWA https://exchange1.lan.adlerweb.info/owa
ProxyPassReverse /OWA https://exchange1.lan.adlerweb.info/owa

#WAP Zugriff (Exchange=2003)
#ProxyPass /oma https://exchange1.lan.adlerweb.info/oma
#ProxyPassReverse /oma https://exchange1.lan.adlerweb.info/oma
#ProxyPass /OMA https://exchange1.lan.adlerweb.info/oma
#ProxyPassReverse /OMA https://exchange1.lan.adlerweb.info/oma

#Falls zuvor ein ISA/TMG im Einsatz war haben evtl einige User die HTML-Loginseite
#gespeichert - leiten wir passend weiter.
RewriteEngine on
RewriteRule   "^/CookieAuth.dll(.*)"  "/OWA/"  [R,L]

Zwieiter Schritt: Sonstiges

Es folgen weitere HTTP-Resourcen, welche keine spezielle Konfiguration benötigen. Dies umfasst z.B. öffentliche Ressourcen oder die Webseite zur Kennwortänderung

# OWA-URL fuer Exchange < =2007
ProxyPass /exchange https://exchange1.lan.adlerweb.info/exchange
ProxyPassReverse /exchange https://exchange1.lan.adlerweb.info/exchange
ProxyPass /Exchange https://exchange1.lan.adlerweb.info/exchange
ProxyPassReverse /Exchange https://exchange1.lan.adlerweb.info/exchange

# OWA Icons, CSS, JS, etc (Exchange >=2000 < =2007)
ProxyPass /exchweb https://exchange1.lan.adlerweb.info/exchweb
ProxyPassReverse /exchweb https://exchange1.lan.adlerweb.info/exchweb
ProxyPass /ExchWeb https://exchange1.lan.adlerweb.info/exchweb
ProxyPassReverse /ExchWeb https://exchange1.lan.adlerweb.info/exchweb

# Öffentliche Ordner (Exchange <=2003, >=2007 interne Umleitung auf /OWA)
ProxyPass /public https://exchange1.lan.adlerweb.info/public
ProxyPassReverse /public https://exchange1.lan.adlerweb.info/public
ProxyPass /Public https://exchange1.lan.adlerweb.info/public
ProxyPassReverse /Public https://exchange1.lan.adlerweb.info/public

# Einstellungen um per OWA das Kennwort zu aendern
ProxyPass /iisadmpwd https://exchange1.lan.adlerweb.info/iisadmpwd
ProxyPassReverse /iisadmpwd https://exchange1.lan.adlerweb.info/iisadmpwd

# Out of Office & Free/Busy-Infos (Exchange >=2007)
ProxyPass /EWS https://exchange1.lan.adlerweb.info/EWS
ProxyPassReverse /EWS https://exchange1.lan.adlerweb.info/EWS
ProxyPass /ews https://exchange1.lan.adlerweb.info/EWS
ProxyPassReverse /ews https://exchange1.lan.adlerweb.info/EWS

# Offline-Adressbuecher (Exchange >=2007)
ProxyPass /OAB https://exchange1.lan.adlerweb.info/OAB
ProxyPassReverse /OAB https://exchange1.lan.adlerweb.info/OAB
ProxyPass /oab https://exchange1.lan.adlerweb.info/OAB
ProxyPassReverse /oab https://exchange1.lan.adlerweb.info/OAB

# Unified Messaging (Exchange >=2007)
#ProxyPass /UnifiedMessaging https://exchange1.lan.adlerweb.info/UnifiedMessaging
#ProxyPassReverse /UnifiedMessaging https://exchange1.lan.adlerweb.info/UnifiedMessaging
#ProxyPass /unifiedmessaging https://exchange1.lan.adlerweb.info/UnifiedMessaging
#ProxyPassReverse /unifiedmessaging https://exchange1.lan.adlerweb.info/UnifiedMessaging

# Exchange Control Panel (Exchange >=2010)
ProxyPass /ECP https://exchange1.lan.adlerweb.info/ECP
ProxyPassReverse /ECP https://exchange1.lan.adlerweb.info/ECP
ProxyPass /ecp https://exchange1.lan.adlerweb.info/ECP
ProxyPassReverse /ecp https://exchange1.lan.adlerweb.info/ECP

Schritt 3: Auto-Discovery

Outlook nutzt eine fixe URL um Hinweise zur Selbstkonfiguration zu erhalten. Wer dies nutzt kann auch hier eine einfache HTTP-Weiterleitung bemühen:

# AutoDiscover externer Outlook-Client
ProxyPass /autodiscover https://exchange1.lan.adlerweb.info/autodiscover
ProxyPassReverse /autodiscover https://exchange1.lan.adlerweb.info/autodiscover
ProxyPass /Autodiscover https://exchange1.lan.adlerweb.info/Autodiscover
ProxyPassReverse /Autodiscover https://exchange1.lan.adlerweb.info/Autodiscover
ProxyPass /AutoDiscover https://exchange1.lan.adlerweb.info/AutoDiscover
ProxyPassReverse /AutoDiscover https://exchange1.lan.adlerweb.info/AutoDiscover

Schritt 4: Active Sync

Mobilgeräte verwenden häufig das „ActiveSync“-Protokoll. Im Prinzip auch HTTP, allerdings können einige Anforderungen den Exchange-Server ins Schwitzen bringen. Um hier den Apache nicht ungeduldig werden zu lassen wird der Timeout auf 5 Minuten erhöht.

# ActiveSync - PDA/Mobilsync & Push (Exchange >=2003)
# Timeout 5Min um dem Exchange etwas zeit zu geben
ProxyPass /Microsoft-Server-ActiveSync https://exchange1.lan.adlerweb.info/Microsoft-Server-ActiveSync connectiontimeout=600
ProxyPassReverse /Microsoft-Server-ActiveSync https://exchange1.lan.adlerweb.info/Microsoft-Server-ActiveSync

Schritt 5: RPC

Zuletzt folgt „das Monster“: Outlook selbst. Der Outlook-Client ist in der Lage seine RPC-Pakete über HTTP zu tunneln. Leider hält sich Microsoft hier (wie üblich) nicht an die gängigen Standards. Normalerweise wird eine HTTP-Verbindung geöffnet, der Request übermittelt und die Antwort empfangen. Outlook hingegen baut gleich 2 Verbindungen auf – auf einer wird gesagt „ich habe 1GB Daten“, dann werden 100 Byte als Anfrage gesendet und auf der Zweiten die Antwort erwartet. Da die 100 Byte weit von den angekündigten 1GB entfernt sind und die Info, dass nur ein Teil gesendet wird, seitens Outlook fehlt, wartet der Apache brav auf den Rest, Outlook sendet aber nichts ohne Antwort. Deadlock. Timeout.

Apache selbst geht hier den Weg namens „Pech gehabt„. Da die Microsoft-Methode viel Angriffsfläche bietet und nicht den Standards entspricht ist eine Nutzung offiziell nicht vorgesehen. Auch scheint Microsoft diese Standardverletzung patentiert zu haben, wer also etwas passendes Implementiert könnte ein böses Erwachen erleben. Ist man trotzdem nicht abgeschreckt und möchte Outlook weiterhin nutzen muss ein Addon Abhilfe schaffen: mod_proxy_msrpc. Für Arch Linux ist es jetzt im aktuellen AUR zu finden, alle Anderen können es mit wenigen Zeilen bauen:

cd /tmp
git clone git://github.com/bombadil/mod_proxy_msrpc
cd mod_proxy_msrpc/
./configure
make
cp src/.libs/mod_proxy_msrpc.so /usr/lib/httpd/modules/

Der Zielpfad der letzten Zeile kann sich je nach Distro unterscheiden. Debian/Ubuntu und Gentoo nutzen /usr/lib/apache2/modules/. In der httpd.conf muss analog der anderen Module auch dieses geladen werden:

LoadModule proxy_msrpc_module modules/mod_proxy_msrpc.so

Im VHost selbst leiten wir den virtuellen RPC-Ordner passend weiter und weisen das neue Modul an den Protokollmurks zuzulassen:

OutlookAnywherePassthrough On

ProxyPass /rpc https://exchange1.lan.adlerweb.info/rpc/
ProxyPassReverse /rpc https://exchange1.lan.adlerweb.info/rpc/

Schritt 6: Achja, TLS

Noch ein Hinweis für alle, die auf moderne Verschlüsselung setzen: Vergesst es. Outlook 2013 unterstützt lediglich TLS1.0 sowie SHA1, entsprechend alte Algorithmen müssen also erlaubt sein.

Quellen: