Archiv der Kategorie: PC-Kram

Meine Lieblingsbeschäftigung :)

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.

BitBastelei #164 – Reparatur: HP PSC Tinten-Multifunktionsgerät (PSC 1315)

BitBastelei #164 - Reparatur: HP PSC Tinten-Multifunktionsgerät (PSC 1315)

(318.5 MB) 00:10:57

2015-09-06 10:00 🛈

Aktuelle Tintenstrahldrucker haben meist einen überschaubaren Lebenszyklus: Kaufen, paar Seiten Drucken und bei leerer Patrone entsorgen. Der vorliegende HP PSC 1315 hat da mit über 10 Jahren ein beträchtliches Alter erreicht – und das obwohl es ein eher günstiges Gerät war. Doch nun scheint Schluss zu sein: Der Druckkopf brauch 30 Sekunden für eine Zeile und einschalten ist auch nur noch sporadisch möglich. Da ich aber auch keine wirklichen Alternativen gewissenhaft empfehlen kann werfen wir mal einen Blick drauf – vielleicht ist der Fehler ja noch recht einfach zu beheben.

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

BitBastelei #163 – Dell PowerConnect 2724 12V-Umbau

BitBastelei #163 - Dell PowerConnect 2724 12V-Umbau

(491.3 MB) 00:33:16

2015-08-30 10:00 🛈

Und wieder ein Bauteil, welches an die Solaranlage soll: Ein alter 24-Port-Switch der Serie Dell PowerConnect-Serie. Leider intern nicht auf 12V wie der bereits umgestellte SMC-Switch, aber auch das sollte sich wohl lösen lassen.

Weitere Links:

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

„Vernünftiges“ TLS (SSL/HTTPS/…) mit Apache

Langsam aber sicher setzt sich die verschlüsselte Kommunikation per TLS auch auf Internetseiten (HTTPS) durch. Gut konfiguriert verhindert sie effektiv das Mitlesen der gesendeten Informationen und Einschleusen fremder Codestücke. Wichtig hierbei ist, dass der zugehörige Webserver richtig konfiguriert ist. Ich werde hier auf den aktuellsten Ableger des Apache HTTPd, die Version 2.4, eingehen. Alle Einstellungen werden im jeweiligen virtual Host vorgenommen und sollten teilweise bereits vorhanden sein. Bei Arch Linux befindet sich die SSL-Konfiguration unter /etc/httpd/conf/extra/httpd-ssl.conf.

Zertifikatstheorie

TLS, und damit auch HTTPS, arbeiten mit einem hierarchisch organisierten System. Viele Browser und Betriebssysteme liefern eine Liste mit vertrauenswürdigen Organisationen mit – ist das eigene Zertifikat nicht, zumindest indirekt, von einer dieser Organisationen unterschrieben, so wird eine Sicherheitswarnung angezeigt. Ob dies Sinnvoll ist kann kritisch beäugt werden, vor allem wenn man bedenkt, dass z.B. auch diverse Banken, Regierungen oder Organisationen, welche in der Vergangenheit eher durch Sicherheitsmängel aufgefallen sind, auf dieser Liste stehen. Generell erstell man selbst einen Key, den privaten Schlüssel des Zertifikates, und leitet hiervon einen Request (CSR) ab. Letzterer – und nur dieser – wird an die Zertifizierungsstelle (CA) zum Unterschreiben gegeben. Nachdem die eigene Identität nachgewiesen wurde wird aus Request und Unterschrift das eigentliche, öffentliche Zertifikat, welches das eigene, zweiteilige Schlüsselpaar komplettiert. Als Betreiber hat man hier mehrere Möglichkeiten ein solches SSL-Zertifikat zu erhalten:

  • Kaufen. Die diversen Organisationen bieten ihre Unterschriften üblicherweise gegen Gebühr an. Für eine eizelne Domain ohne Zusatzfunktionen sind etwa 15€/Jahr fällig, möchte man Subdomains oder erweiterte Sicherheitsfunktionen kann der Preis schnell auf über 1000€/Jahr steigen.
  • Selber machen. „Selbstsignierte“ Zertifikate sind prinzipiell auch möglich, hier wird aber eine Sicherheitswarnung angezeigt. Der Nutzer muss selbst bestätigen, dass er dem Zertifikat vertraut. Für interne Systeme, auf denen man zentral ein Zertifikatsvertrauen ausrollen kann kein Problem, ansonsten eher nur für eigene Testsysteme geeignet.
  • StartSSL. StartCom stellt mit StartSSL kostenfrei SSL-Zertifikate zur Verfügung. Die Israelische Firma wird von allen gängigen Browsern als Vertrauenswürdig eingestuft. Bedenklich ist jedoch, dass das zurückziehen im Falle eines Sicherheitsproblem Gebühren verursacht und so dazu verleitet bei Problemen die Augen zuzukneifen.
  • CACert …ist eine freie, communitybasierte Zertifizierungsstelle. Auch hier lassen sich kostenfrei Zertifikate signieren, die Qualität der Signatur steigt mit der Anzahl der Personen, welche die Identität prüfen. Leider ist CACert auf vielen Systemen nicht hinterlegt, somit werden auch hier ohne manuellen Eingriff Sicherheitswarungen angezeigt.
  • Let’s Encrypt geht einen ähnlichen, allerdings weniger Community-Zentrierten weg. Sie möchte automatisiert und kostenfrei einfache SSL-Zertifikate bereitstellen. Dahinter stehen Internetgrößen wie Mozilla, Akamai, Cisco oder die EFF, es ist also davon auszugehen, dass die Vertrauensstellungen machbar sein sollen. Warum soll? Let’s Encrypt wird erst mitte Oktober 2015 starten.

Wir basteln einen Request

Welchen weg auch immer man geht: Wir brauchen einen Key und den passenden Request. Fangen wir mit ersterem an:

cd /etc/ssl/private
openssl genrsa -out server.key 4096

Der Schlüssel nutzt den RSA-Algorithmus mit einer Länge von 4096 Bit. Empfohlen wird >=2048 Bit, wer aber nicht unbedingt auf die letzten CPU-Takte Performance schaut sollte diese zugunsten der vermutlich längeren Zukunftssicherheit investieren.

Auf Basis dieses Schlüssels wird nun der Request erstellt:

openssl req -new -key server.key -out server.csr -sha512

Die Attribute wie Adresse sollten passend den eigenen Angaben ausgefüllt werden. Wichtigster Punkt: Der „Common Name“ (CN) muss dem Servernamen entsprechen.

Diesen Request übersendet man nun der Zertifizierungsstelle, die Antwort wird als „server.crt“ im selben Verzeichnis gespeichert. Sollte man ein so genanntes „intermediate“ benötigen (Das eigene Zertifikat wurde von jemandem unterschrieben welcher selbst nicht vertrauenswürdig ist aber eine Unterschrift einer anderen, vertrauenswürdigen Stelle hat), so werden diese Zertifikate einfach am Ende der Textdatei drangehangen.

Apache-Konfiguration

Erster Schritt: Wir schalten „SSL“ (so hies TLS früher mal, Apache hat die Namen beibehalten) ein und geben den Pfad zum Zertifikat an.

SSLEngine on
    SSLCertificateFile      /etc/ssl/private/server.crt
    SSLCertificateKeyFile   /etc/ssl/private/server.crt

Als Nächstes werden die möglichen Protokollversionen und Algorithmen festgelegt. Bei ersterem sollte alles mit SSL abgeschaltet werden – diese haben bekannte Sicherheitslücken. Auch zu TLSv1 gibt es einige Bedenken, jedoch noch keinen praxisrelevanten Angriff – wer auf Nummer sicher gehen will kann auch dies Abschalten, jedoch sperrt man hiermit möglicherweise ältere Geräte und Browser aus. Bei den Ciphern ist die Reigenfolge entscheidend – der erste Eintrag, welcher von Server und Client unterstützt wird, wird verwendet. Einträge mit *DHE* bieten „perfect forward secrecy“ (PFS) – hierbei wird für jede Verbindung ein temporäres Passwort abgewandelt. Liest ein Bösewicht die verschlüsselten Daten mit kann er sie später, auch wenn er den Server knackt und so an die Schlüssel kommt, nicht mehr entschlüsseln. RC4 und MD5 werden wegen ihrer hohen Geschwindigkeit zwar gerne von Großkonzernen und einigen Banken verwendet, haben aber bekannte Lücken, sodass sogar das Bundesamt für Sicherheit in der Informationstechnik (BSI) vor deren Einsatz warnt. Die SSL-Kompression hat ebenfalls eine gewisse Angriffsfläche und sollte abgeschaltet werden. Wie immer gilt: Je mehr man auf Sicherheit Wert legt, desto mehr alte Geräte sperrt man aus.

    SSLProtocol             all -SSLv3 -TLSv1
    SSLCipherSuite          ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!3DES:!MD5:!PSK
    SSLHonorCipherOrder     on
    SSLCompression          off

Bei neueren Servern kann man die Ladezeit des Besuchers noch etwas drücken: Dessen Browser geht üblicherweise hin und fragt nach der Verbindung bei der Zertifizierungsstelle, welche die Unterschrift geleistet hat, nach, ob diese überhaupt noch gültig ist. Mit OCSP-Stapeling fordert unser Server regelmäßig eine kleine, unterschriebene „Notiz“ der Zertifizierungsstelle an, in welchem die Gültigkeit mit der aktuellen Uhrzeit hinterlegt ist. Diese senden wir gleich mit – ist dem Browser diese Info aktuell genug und die Unterschrift OK muss er nicht extra nochmal nachfragen.

    SSLUseStapling          on
    SSLStaplingResponderTimeout 5
    SSLStaplingReturnResponderErrors off

Letzter Punkt: HSTS – HTTP Strict Transport Security. Hiermit sagen wir dem Browser, dass wir für die nächste Zeit ausschließlich per HTTPS arbeiten wollen. Alle zukünfigen Zugriffe werden automatisch per HTTPS abgewickelt, auch wenn der Nutzer nicht per Hand https anfordert. Im Gegensatz zu den gängigen HTTP-Umleitungen spart es einen unverschlüsselten Request, welcher ggf. auch als Angriffspunkt dienen kann. Wenn man seine Webseite absichtlich per HTTP und HTTPS anbieten möchte sollte man den Header natürlich überspringen.

    # HSTS (mod_headers is required) (15768000 seconds = 6 months)
    Header always set Strict-Transport-Security "max-age=15768000"

Der Rest
Eine gute Ressource um zwischen Sicherheit und Kompatibilität abzuwähen oder die Konfiguration für andere Webserver und Funktionen anzupassen ist der Mozilla SSL Configuration Generator, auf dessen Ausgaben auch das hiesige Beispiel basiert.

Sollen alle Anfragen über HTTPS laufen kann man den HTTP-Listener anweisen für alle Anfragen mir einer Umleitung auf die HTTPS-Seite zu antworten. Hierzu reicht folgende Zeile im zuständigen VHost:

Redirect / https://testserver.adlerweb.info/

Wer testen möchte, ob alles Funktioniert hat und welche Geräte und Browser zu alt sind um auf die Webseite zugreifen zu können, kann die SSL-Tester von SSLLabs nutzen – dieser generiert einen ausführlichen und gut verständlichen Report und gibt weitere Tipps bei möglichen Problemen.

MySQL: Anlegen neuer Benutzer schlägt fehl

Dann nur noch schnell einen neuen User für die Datenbank anlegen…oder auch nicht.

Unknown column 'plugin' in 'mysql.user'

Ursache ist, dass man zuvor ein MySQL-Update verpennt hat und lediglich die Binarys, nicht jedoch die Systemdatenbanken aktualisiert hat. Nachholen lässt sich dies auf der Konsole mit einem schnellen

mysql_upgrade -u root -p

Hierdurch werden die Tabellen auf den aktuellen Stand gebracht und das anlegen neuer Nutzer sollte wieder problemlos möglich sein.

PHP mail() – Header-Fehler in aktuellen Versionen

„Das hat aber früher funktioniert“! Exakt das dachte ich mir, als nach einem PHP-Update ein kleines „Aushilfsscript“ seinen Dienst verweigerte. Das Script nimmt eine fertige E-Mail, entfernt einige Header, welche das Quellsystem nicht Standardkonform einbringt, und sendet die gesäuberte Mail weiter. Um unnötiges Parsing zu vermeiden ging die komplette Mail in den Header.

$headers[] = 'From: Test <test@adlerweb.info>';
$headers[] = 'Cc: Test2 <test2@adlerweb.info>';
$headers[] = 'X-cleaned: cleaned';
//[…]
$headers[] = '  boundary="'.$boundary.'"';

//Neue Header und Content zusammenführen
$content = implode("\r\n", $headers)."\r\n\r\n".'-----'.implode('-----',$content);

//Mailversand
$check = mail($receiver, NULL, NULL, $content);

Nicht schön, da aber nur ersetzt/ergänzt wird muss nicht bei jeder Änderung der Quelle eine Anpassung erfolgen um Content und Header sauber zu trennen. Leider ist seit den letzten Versionen (vermutlich 5.4.43/5.5.26) diese Anwendung nicht mehr erlaubt. Hat man zwei Newlines (\r\n\r\n – genutzt zur Content-Trennung) im Header bricht PHP mit einer Fehlermeldung ab. Das Ganze soll dazu dienen Personen zu schützen, welche Fremddaten aus unsicheren Quellen ohne Eingabevalidierung nutzen und so ihre Anwendung verwundbar machen. Leider trifft es aber eben auch solche Anwendungen wie meine, was gerade bei solchen minor-Versions etwas unschön ist. Nunja, mein Script spricht nun direkt SMTP mit dem zuständigen Mailserver und umgeht so die PHP-mail()-Funktion. Problem solved.

Related: