Erste IoT-Schritte: Laufende Temperaturmessung mit dem Arduino und ThingSpeak

Bei meinem letzten Beitrag zum Thema hatte ich schon beschrieben, wie man den Arduino mit Hilfe eines E32-Shields ins WLAN bringt. Nun sollte das Ganze einen nützlichen Zweck erfüllen. Für den Anfang reichte mir der Anschluss eines einfach DS18B20-Temperatursensors und die fortlaufende Dokumentation der Messung bei einem IoT-Server. Natürlich kann man auch selbst einen MQTT-Server (sprich: Mosquito-Server) aufsetzen und verwenden, aber ThingSpeak bietet sich hier einfach aus mehreren Gründen an. Vor allem auch deshalb, weil es bereits eine fertige Bibliothek für den Arduino gibt, sodass man die Befehle zum Senden der Werte auf einem hohen Abstraktionslevel belassen kann.

Wie so oft gilt auch hier: Natürlich gibt es das alles schon und es ist auch alles im Netz frei verfügbar und dokumentiert. Aber es kostet dann doch relativ viel Aufwand, alles zusammen zu tragen und im Detail zu verstehen. Daher schreibe ich meine Vorgehensweise hier strukturiert auf. Ebenfalls gilt: Natürlich braucht der wahre Profi den Arduino gar nicht dafür, ein kleiner ESP8266 genügt ebenso. Aber es geht ja auch ein wenig um den Spaß und um das Verständnis des Ganzen, und dafür ist der Arduino einfach besser geeignet. Natürlich wäre das Gleiche auch mit einem Raspberry Pi machbar, der große Vorteil hier liegt darin, dass man sich ein schönes Python-MQTT-Skript schreiben kann und die Fallen vom Arduino-C etwas umschifft.

Doch zurück zum Arduino-Beispiel. Die Verkabelung der Hardware ist – basierend auf der bereits bestehenden Kombination aus Arduino und ESP-Shield – denkbar einfach. Der Temperatursensor bringt drei Kabel mit, die an 3,3V- (rot), GND- (schwarz) und einen beliebigen Digital-PIN (gelb) des Arduino bzw. des aufgesteckten Shields angeschlossen werden. Fertig.

Die eigentliche Kunst liegt also in der Software. Als Basis nehme ich den verkürzten Sketch aus dem WLAN-Anschluss-Beispiel:

#include "WiFiEsp.h"
#include "SoftwareSerial.h"

SoftwareSerial Serial1(3, 2); // RX, TX

char ssid[] = "MeinTollesWLAN";
char pass[] = "**********";
int status = WL_IDLE_STATUS;
WiFiEspClient client;

void setup(void) {
Serial.begin(9600);
Serial1.begin(9600);
WiFi.init(&Serial1);

while ( status != WL_CONNECTED) {
Serial.print("Verbindungsaufbau zu ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
}

Serial.println("Verbindung hergestellt!");
Serial.println();
printWLAN();
Serial.println();
}

void loop(void) {
}

void printWLAN()
{
IPAddress ip = WiFi.localIP();
Serial.print("IP-Adresse: ");
Serial.println(ip);
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
long rssi = WiFi.RSSI();
Serial.print("Signalstaerke (RSSI): ");
Serial.println(rssi);
}

Wie man leicht sieht, macht der Sketch vorerst nichts abgesehen vom Verbindungsaufbau zum WLAN. Soweit so gut. Beginnen wir mit den Bibiotheken und Konstanten, die wir für alles Weitere benötigen. Die Bibliotheken heißen „OneWire“ (Temperatursensor) und „ThingSpeak“ (Verbindung zum IoT-Server).

ThingSpeak kann man auf der folgenden Seite herunterladen und dann der (sehr kurzen) Installationsanleitung folgen:

https://github.com/mathworks/thingspeak-arduino

Und wenn man gerade schon dabei ist, verfährt man ebenso mit der hier erhältlichen OneWire-Library.

Welche Konstanten werden nun benötigt? Zum einen die Nummer des PINs, an dem das gelbe Datenkabel des Sensors angeschlossen wurde. In meinem Beispiel ist das die Nr. 5. Um mit ThingSpeak arbeiten zu können, muss zudem ein Account bei dem Dienst angelegt werden. Nach dem Login kann dann ein einzelner Channel erstellt werden, der künftig die Daten entgegen nimmt. Die Nummer des Channels sowie unser APIKey von ThingSpeak sind die letzten benötigten Konstanten:

#include "OneWire.h"
#include "ThingSpeak.h"

int Sensor_Pin = 5;
unsigned long Channel = 123456789abcdef;
const char * APIKey = "************";

Bei unserer Messung verwenden wir ein Objekt aus der Klasse OneWire, dem als Parameter die Nummer des PINs übergeben wird:

OneWire ds(Sensor_Pin);

In der setup-Funktion wird die Kommunikation mit dem ThingSpeak-Server initialisiert, dabei wird die WLAN-Verbindung als Übertragungsweg übergeben:

ThingSpeak.begin(client);

Kommen wir zur Loop-Funktion. Diese soll im Grunde folgende Elemente enthalten: Messen, Ausgeben, Übertragen, Warten. Das Messen ist dabei mit riesigem Abstand die komplexeste Aufgabe und wird daher in eine eigene Funktion „getTemp“ ausgelagert. Der Rest ist relativ einfach. Damit nur echte Messwerte eingetragen werden, verwende ich „-100“ als Fehlerwert, alles darüber hinaus wird an ThingSpeak übertragen. Dabei müssen Channel, das Datenfeld (in unserem Fall einfach das einzige, also 1), der gemessene Wert sowie der APIKey übertragen werden. ThingSpeak kann man nicht mit beliebig vielen Werten fluten, 20 Sekunden Wartezeit zwischen den Messungen sind hier i.d.R. angemessen. Somit ergibt sich die Loop-Funktion:

void loop(void) {
float temperatur = getTemp();
Serial.println(temperatur);
if ( temperatur > -100) {
ThingSpeak.writeField(Channel, 1, temperatur, APIKey);
}
delay(20000);
}

Tja, und nun geht’s ans Eingemachte, namentlich um die Funktion „getTemp“. Ich gebe zu, dass ich – der ich nie wirklich C gelernt habe – dann doch einige Zeit intensiv darüber nachdenken musste, um die gefundenen Programmierbeispiele zu verstehen. Ich habe sie hier auf das Nötigste gekürzt und versuche sie zu erläutern.

Wir benötigen zwei Byte-Arrays namens „addr“ (für die Adressdaten des Sensors, es könnte mehrere geben) und „data“ (für die Messwerte). Zudem gilt es, ein paar Fehler abzufangen, z.B. Fehler in der Prüfsumme (CRC) oder gar einen nicht gefundenen oder nicht unterstützten Adapter. In all diesen Fällen wird unser Fehlerwert „-100“ zurückgegeben:


byte data[12];
byte addr[8];

if ( !ds.search(addr)) {
ds.reset_search();
return -100;
}

if ( OneWire::crc8( addr, 7) != addr[7]) {
Serial.println("CRC fehlerhaft!");
return -100;
}

if ( addr[0] != 0x10 && addr[0] != 0x28) {
Serial.print("Kein Sensor erkannt");
return -100;
}

Durch den Aufruf von „ds.search(addr)“ wird der Array praktischerweise direkt mit den Adressdaten des Sensors gefüllt, sodass wir nun – da keine Fehler aufgetreten sind – damit arbeiten können. Die nächsten Schritte sind im Einzelnen: Reset der Kommunikation, Auswahl des Sensors, Durchführen einer Messung und schließlich das Auslesen der Werte aus einem Zwischenspeicher, Speichern der Werte in unserem Datenarray. Anschließend kann wieder ein Reset der Suche nach Sensoren erfolgen.

ds.reset();
ds.select(addr);
ds.write(0x44); // Kommando: Messung durchfuehren
ds.reset();
ds.select(addr);
ds.write(0xBE); // Kommando: Werte auslesen
for (int i = 0; i < 9; i++) {
data[i] = ds.read();
}
ds.reset_search();

Fast fertig. Doch unsere Messwerte sind noch ein wenig „kryptisch“ und entsprechen nicht gerade dem, was wir aufzeichnen wollen. Die eigentlich interessanten Werte „MSB“ (most significant byte) und „LSB“ (least significant byte) stecken in unseren Datenfeldern 1 bzw. 0:

byte MSB = data[1];
byte LSB = data[0];

Sie enthalten die gemessene Temperatur in Binärdarstellung, wie ein Blick in das Datenblatt des DS18B20 verrät:

Um daraus nun einen „gewohnten“ Temperaturwert zu erhalten, bedarf es einer bitweisen Verschiebung des MSB um 8 Stellen nach links und einer bitweisen Verknüpfung mit dem LSB (und gleichzeitig einer Umwandlung in eine Fließkommazahl zur Basis 10):

float tempRead = ((MSB << 8) | LSB);

Wie man dem Datenblatt entnehmen kann, enthält das Ganze aber auch Nachkommastellen, das wurde bei der Umwandlung nicht berücksichtigt. Durch welche Zahl muss nun geteilt werden? Da unsere eigentliche „Basis“ (die 2^0 – Stelle) an vierter Position befindet, ist die Zahl um den Faktor 2^4 = 16 zu hoch. Es folgt:

float TemperatureSum = tempRead / 16;
return TemperatureSum;

Fertig! Hier noch einmal der komplette Sketch, viel Spaß beim Ausprobieren:


#include "OneWire.h"
#include "ThingSpeak.h"
#include "WiFiEsp.h"
#include "SoftwareSerial.h"

int Sensor_Pin = 5;
unsigned long Channel = 123456789abcdef;
const char * APIKey = "************";

OneWire ds(Sensor_Pin);

SoftwareSerial Serial1(3, 2); // RX, TX

char ssid[] = "MeinTollesWLAN";
char pass[] = "**********";
int status = WL_IDLE_STATUS;
WiFiEspClient client;

void setup(void) {
Serial.begin(9600);
Serial1.begin(9600);
WiFi.init(&Serial1);
ThingSpeak.begin(client);

while ( status != WL_CONNECTED) {
Serial.print("Verbindungsaufbau zu ");
Serial.println(ssid);
status = WiFi.begin(ssid, pass);
}

Serial.println("Verbindung hergestellt!");
Serial.println();
printWLAN();
Serial.println();
}

void loop(void) {
float temperatur = getTemp();
Serial.println(temperatur);
if ( temperatur > -100) {
ThingSpeak.writeField(Channel, 1, temperatur, APIKey);
}
delay(20000);
}

float getTemp(){

byte data[12];
byte addr[8];

if ( !ds.search(addr)) {
ds.reset_search();
return -100;
}

if ( OneWire::crc8( addr, 7) != addr[7]) {
Serial.println("CRC fehlerhaft!");
return -100;
}

if ( addr[0] != 0x10 && addr[0] != 0x28) {
Serial.print("Kein Sensor erkannt");
return -100;
}

ds.reset();
ds.select(addr);
ds.write(0x44); // Kommando: Messung durchfuehren
ds.reset();
ds.select(addr);
ds.write(0xBE); // Kommando: Werte auslesen

for (int i = 0; i < 9; i++) {
data[i] = ds.read();
}

ds.reset_search();

byte MSB = data[1];
byte LSB = data[0];

float tempRead = ((MSB << 8) | LSB);
float TemperatureSum = tempRead / 16;
return TemperatureSum;
}

void printWLAN()
{
IPAddress ip = WiFi.localIP();
Serial.print("IP-Adresse: ");
Serial.println(ip);
Serial.print("SSID: ");
Serial.println(WiFi.SSID());
long rssi = WiFi.RSSI();
Serial.print("Signalstaerke (RSSI): ");
Serial.println(rssi);
}

Probleme mit Time Machine und APFS unter High Sierra

Auf Macs mit interner SSD wird bei der Installation von High Sierra das Filesystem auf APFS geändert. Danach funktioniert u.a. Time Machine etwas anders. Es wird zuerst ein Snapshot angelegt, von dem dann das Backup gesichert wird. Nach erfolgreichem Backup sollte der Snapshot eigentlich wieder entfernt werden.
Das scheint nicht immer zu funktionieren. Bei mir wuchs unter macOS 10.13.2 die Zahl der Backups so lange, bis die interne SSD voll war. Ich gehe davon aus, dass das ein Bug ist, der in einem Update behoben wird. In der Zwischenzeit helfe ich mir mit diesem Shellscript:

% cat bin/tm-cleanup.sh
#!/bin/bash

mount | grep "Macintosh HD"| cut -f 1 -d' ' | xargs \ 
-n 1 umount

tmutil thinLocalSnapshots / 10000000000 4

Wenn man das Script mit

sudo bin/tm-cleanup.sh

aufruft, werden alle noch gemounteten Snapshots entmountet und danach entfernt.

NB: das funktioniert so nur, wenn die interne SSD den Namen „Macintosh HD“ trägt, was der Default ist. Ggf. muss der Name im Script angepasst werden.

ESP-12E WLAN-Shield auf dem Arduino Uno

Im Bestreben, einen Arduino Uno für diverse Zwecke drahtlos ans Netz zu bringen, stolperte ich bei EBay über das ESP-12E-Shield, welches selbst wiederum auf dem bekannten ESP8266-Microcontroller basiert. Incl. Versand aus dem fernen Osten bekommt man das gute Stück für unter 10 Euro, sodass ich es auf einen Versuch ankommen ließ. Einige Wochen später kam das Päckchen an, allerdings nur das Board selbst, ohne jeglichen Beipackzettel. Nun gut, das allwissende Internet hält ja mit Sicherheit entsprechendes Knowhow bereit. Wie ich dann feststellen musste: Jein. Wie so oft findet man alle nötigen Informationen, muss sie sich aber einigermaßen mühsam zusammenstellen und sortieren. Daher möchte ich hier das Verfahren nochmal sauber dokumentieren, für alle die vor dem gleichen Problem stehen.

Warnhinweis: Das hier beschriebene Verfahren habe ich selbst mit meinen genannten Bauteilen durchgeführt und es klappte problemlos und offenbar ohne Schäden. Dennoch keine Garantie, dass das immer und überall so sein muss. Also: Alles geschieht auf eigene Gefahr!

Schritt 1: Anpassung der Baud-Rate

Quelle: https://claus.bloggt.es/2017/01/14/using-esp8266-shield-esp-12e-elecshop-ml-by-wangtongze-with-an-arduino-uno/

Offensichtlich kommunizieren Arduino und ESP-12E bzw. ESP8266 deutlich stabiler mit einer Baud-Rate von 9600. Daher muss noch vor dem Einsatz des Shields diese über den Debug-Port umgestellt werden. Dazu nimmt man vier kleine Jumper Wire m/w und schließt sie wie folgt an, ohne das Shield auf den Arduino zu stecken:

Debug Port TX => Uno Pin 1 (TX)
Debug Port RX => Uno Pin 0 (RX)
Debug Port 5V => Uno 5V
Debug Port GND => Uno GND

Die vier Dip-Schalter auf dem ESP-12E können auf „Off“ bleiben. Anschließend den Arduino per USB an den Rechner anschließen, in der IDE die serielle Konsole aufrufen (Option „Both NL & CL“ und Baud-Rate 115200) und dieses Kommando durchgeben:

AT+UART_DEF=9600,8,1,0,0

Anschließend alles schließen und den Arduino wieder vom PC lösen. Die Kabel werden nicht mehr benötigt und können abgezogen werden.

Schritt 2: Shield aufstecken und anschließen

Quelle: https://arduino.stackexchange.com/questions/24919/how-to-connect-wi-fi-shield-esp-12e-esp8266-uart-wifi-wireless-shield-with-ardui

Das Shield wird nun wie vorgesehen auf den Arduino gesteckt, sodass alle Pins in die Steckerleisten des Arduino passen. Dann benötigen wir wieder zwei Jumper Wire m/w und verbinden:

Debug Port RX => Pin 2 des Arduino (der sich ja nun auf dem Shield selbst befindet)
Debug Port TX => Pin 3 des Arduino (der sich ja nun auf dem Shield selbst befindet)

Hier zur Verdeutlichung ein Foto:

Schritt 3: Nötige Library einbinden

Quelle: https://github.com/bportaluri/WiFiEsp/wiki

Den Arduino nun wieder per USB an den Rechner anschließen und die IDE aufrufen. Es wird die passende Bibliothek benötigt, damit man das WLAN-Modul ansteuern kann. Dazu das Menü „Sketch -> Include Library -> Manage Libraries…“ aufrufen, dort nach „WiFiESP“ suchen und das Paket „WiFiEsp by bportaluri“ installieren.

Schritt 4: Testskript anpassen und mit WLAN verbinden

Quelle: https://github.com/bportaluri/WiFiEsp/blob/master/examples/ConnectWPA/ConnectWPA.ino

Das nötige Testskript kann man hier finden. Einfach per Copy&Paste in einen neuen Sketch einfügen und die folgenden Zeilen anpassen:

Zeile 16 => hier die korrekten Ports eintragen, in unserem Fall „Serial1(3, 2)“
Zeile 19 => die SSID des eigenen WLANs eintragen
Zeile 20 => den Key des eigenen WLANs eintragen

Den Sketch nun kompilieren, hochladen und mit dem seriellen Monitor das Ergebnis beobachten. Idealerweise wird dort nun der Erfolg und die vom WLAN-Router erhaltene IP vermeldet.

sshblack under macOS Sierra

When you run a computer that is SSH-enabled and open to access from the internet, you should use safe passwords (or even better: private key authentication) and a restrictive sshd.conf. But even with that, the barrage of malicious connection attempts can cause performance issues.

I have been using sshblack for many years on my Mac to battle that issue: when an IP address makes multiple failed attempts to login, it gets banned for a while. The popular fail2ban does that for Linux systems, but it doesn’t work on Mac OS X/macOS.

sshblack always needed some hand-holding with new OS releases, but with Sierra there was an entirely new challenge: there are no log files for sshd anymore! In previous versions, sshblack would look in /var/log/system.log for failed login attempts. The file still exists, but most logging in Sierra uses Apple’s new Unified Logging and Tracing System. That means the only way to access logging for sshd is either Console.app or the log CLI command. Using log with its stream parameter to get real-time logging data incurs a huge performance penalty, so the only way to go seems to be log show. So instead of tailing a log file, the script now has this definition:

my ($LOG) = ‚/usr/bin/log show –style syslog –last 1m |‘;

I added a sleep 60 statement to the loop and removed the code meant to deal with log rotation. Seems to work fine.

UniNow – Bequem durch’s Studium?

UniNow liefert genau das, was viele Studierende sich schon lange wünschen: Eine App fürs Studium, die alles kann, unter einer Oberfläche. Sich nicht mehr an gefühlt tausend unterschiedlichen Systemen anmelden zu müssen, die zudem für eine mobile Ansicht häufig nicht geeignet sind. Toll.

Der Haken an der Sache? Die App stammt von keiner Uni, sondern von einem Drittanbieter. Diesem Anbieter müsst Ihr die Zugangsdaten zu Eurem Studierendenaccount anvertrauen, an dem mittlerweile eine ganze Reihe von Diensten hängen: Mail, Klips, Ilias, Wlan, VPN, Eduroam, SOFS, Softwareshop, lizenzierte Zugänge u.s.w. Dienste also, die sehr persönliche Daten beinhalten bzw. Dienste mit hohem Missbrauchspotenzial. Mit Euren Zugangsdaten wird sich UniNow dann mittels seiner Server bei den unterschiedlichen Diensten der Uni anmelden und Eure Daten abfischen. Dass die Weitergabe der Accountdaten gegen jegliche Benutzungsordnungen verstoßen, eine Exmatrikulation bzw. fristlose Kündigung zur Folge haben kann, soll hier jetzt gar nicht das Thema sein. Vielmehr soll es um den Schutz Eurer Daten gehen und Euren eigenverantwortlichen Umgang mit diesen.

Die Uni Köln betreibt ziemlich viel Aufwand, um Eure Daten vor unberechtigten Zugriffen zu schützen. Es ist zudem penibel reglementiert, was gespeichert werden darf und wer zu welchem Zweck Einsicht in Eure Daten erhält. Sobald an der Uni irgendetwas mit personenbezogenen Daten passieren soll, müssen Datenschutzbeauftragter und ggf. Personalrat in langen Verfahren diesem zustimmen. Ein Beispiel dazu aus dem Tagesgeschäft des Helpdesk: Diesen erreichen häufig Anfragen, Euch Euer Passwort des Studierendenaccounts zuzumailen, da Ihr dieses vergessen habt. Das wird aus vielerlei Gründen nicht passieren: Der Helpdesk hat keine Einsicht auf diese Daten. Selbst wenn, wäre das nutzlos, da das Passwort nur verschlüsselt vorliegt. Und zu guter Letzt würde der Helpdesk solch sensiblen Daten nie über eine ungesicherte Mailverbindung hinauspusten.

All solche Bemühungen um Datenschutz würden natürlich ad absurdum geführt, wenn Ihr freiwillig Eure Daten aus der Hand gebt. Als Ausweg aus diesem Dilemma bliebe letztendlich nur der Rückgriff auf eine „vertrauenswürdige“ App bzw. einer App, die wenigstens den gleichen Datenschutzbestimmungen unterliegt, wie alle anderen Dienste der Uni auch. Eine solche App gibt es aber noch nicht. Die Gründe hierfür sind vielfältig. Positiv ist zu erwähnen, dass die Uni Köln – über das RRZK sowie das Prorektorat für Lehre und Studium – zur Zeit gemeinsam mit anderen Hochschulen am Projekt „StApps“ arbeitet, welches die Erstellung eigener Uni-Apps zum Ziel hat. Mehrere Mitarbeiter wollen dafür sorgen, dass wir Euch diese App möglichst bald bereitstellen können.

Challenges with the SM-X Layer 2/3 EtherSwitch Service Module in a 4331 router

It was my task to replace old Cisco 2811 Routers with a new generation. The WAN connection at one branch office had been upgraded to 50 Mbit, but the crypto performance of the old router was too low for the bandwidth. We decided to use the Cisco 4331 with the SM-X EtherSwitch Service Module (SM-X-ES3-16-P ).

Until I had a running system, there were some major challenges I had to deal with. Since there was not that much documentation available and not so many public support cases that had been solved, I will present my experiences here to help others to avoid the problems.

The first problem I had, was to find the right image. In the download section on the cisco.com page there is no section for the SM-X-ES3-16-P. Instead I had to take an image from the 3560-X series.

Unfortunately the image I downloaded (c3560e-universalk9-mz.152-4.E1) lead to my first bug. After every reboot of the switch, interface vlan 1 was in shutdown state. The switch commented this with following output:

SETUP: new interface vlan 1 placed in „shutdownstate.

This seems to be a bug that was fixed in version c3560e-universalk9-mz.152-4.E2 . After I installed this version, the output was still there but vlan 1 went directly to the up state. However before I used this release, I tried the suggested release c3560e-universalk9-mz.150-2.SE9 . Unfortunately the switch does not boot with this release and keeps rebooting. The only way to interrupt the boot process is to force a password recovery from the router!:

hw-module subslot 1/0 error-recovery password_reset

The switch then gets to the rommon and you can decide which image to boot.

The next goal I wanted to achieve was the communication between the switch and the router. You can use the Ethernet-Internal interfaces for this. In the documentation of the switch module you can only find a difficult configuration using BVI interfaces. With recent images Cisco implemented a new command so that you can create SVIs on the router. The command is:

ethernet-internal subslot 1/0
platform switchport svi

After entering this command you have to reboot the router. When the router is up again, you are able to create SVIs with the command “Interface vlan 1” and use the internal ports as trunk ports. This has been described in this post: supportforums.cisco.com

Then I encountered a problem caused by our configuration. We use dhcp snooping in combination with ip source guard. For this combination it is necessary to use dhcp snooping with option 82. All our other switches are connected to catalyst switches, which act as layer 3 switches. On them you can define the trunk ports as trusted (“ip dhcp snooping trust”). On the router however this command is not available. So my dhcp requests were not redirected by the “ip helper address” command because of the option 82 information. Turning off option 82 allowed the dhcp requests to reach the dhcp server, but the dhcp replies then got stuck at the switch because without the option 82 information it does not know to which interface it should forward the packet. To solve this I activated option 82 again and on the router I entered following command:

ip dhcp relay information trust-all

This is the router equivalent to the “ip dhcp snooping trust”.

But still my setup was not working. No traffic was going through the internal ports after I reentered my full configuration. The reason for this seems to be another bug. I had activated rapid spanning tree “spanning-tree mode rapid-pvst” (while using default STP the problem did not occur). With the command “show spanning-tree” on the switch I found that the Gi0/18 was in “P2p Dispute” state. Comparing the output from switch and router I saw that both claimed to be the root bridge and had the same priority. However the mac address of the switch was lower so the behavior of the switch was totally correct. I fixed this by lowering the priority of the router and making it root bridge:

spanning-tree vlan 1-4094 root primary

Finally everything was working, but the debugging and searching for information had cost me several hours. I hope I can help others avoid having the same problems and getting stuck.

Verschlüsselung dank Let’s Encrypt

Dank der Initiative „Let’s Encrypt“ kann nun jede(r) Betreiber(in) von Webseiten diese kostenfrei auch per https bereitstellen, also den Transportweg mit SSL verschlüsseln. Dies war zwar bislang auch mit anderen Anbietern möglich, aber die damit erstellten Zertifikate waren nicht in den gängigen Browsern vertreten, sodass beim ersten Besuch der Seite stets eine Warnmeldung erschien. Bei „Let’s Encrypt“ ist dies nicht mehr so.

Dementsprechend habe ich auch die Domains auf meinem eigenen privaten Server nun per https verfügbar gemacht. Eigentlich wollte ich an dieser Stelle eine kleine Anleitung verfassen, da ich mir meinen Erfolgsweg bei diversen Blog- und Foreneinträgen zusammengesucht hatte. Inzwischen habe ich aber einen Beitrag entdeckt, der exakt meine Vorgehensweise beschreibt, incl. mehrerer Domains in einem Server und Auto-Renew per Cronjob. Daher verweise ich unten auf diesen Artikel von Dominic Pratt und ergänze lediglich, dass man inzwischen das Auto-Renew auch einfacher machen kann, nämlich per Crontab-Eintrag á la:

30 2 * * 1 /opt/letsencrypt/letsencrypt-auto renew >> /var/log/le-renew.log

Zudem sollte man unbedingt darauf achten, ausschließlich sichere Protokolle und Cipher Suites zu aktivieren. Leider unterstützen nicht alle Server und Clients automatisch die neuesten und besten Einstellungen. Ein guter Kompromiss ist derzeit im Apache z.B.:

SSLProtocol all -SSLv2 -SSLv3
SSLCipherSuite HIGH:MEDIUM:!ADH:!MD5:!RC4

Damit erreicht man im SSL-Test (https://www.ssllabs.com/ssltest) immerhin Grade A. Besser ginge es noch bspw. mit der Aktivierung von Forward Secrecy.

Hier nun der angesprochene Artikel:

Let’s Encrypt nutzen – eine Anleitung

Sendmail and dual-stack IPv6

We’ve been using IPv6 for a few years, but for various reasons we hadn’t yet prepared our sendmail MTAs to use the new protocol. We run a dual-stack environment, so all servers need to use both IPv4 and IPv6. According to sendmail’s documentation it should be possible to use the host name for both daemons, so initially we tried it like that in our .mc file:

DAEMON_OPTIONS(`Name=MTA-v4,Family=inet,A=smtp-out.rrz.uni-koeln.de,M=fh')
DAEMON_OPTIONS(`Name=MTA-v6,Family=inet6,A=smtp-out.rrz.uni-koeln.de,M=fh')
DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA')
CLIENT_OPTIONS(`Name=MSA-v4,Family=inet,A=smtp-out.rrz.uni-koeln.de,M=h')
CLIENT_OPTIONS(`Name=MSA-v6,Family=inet6,A=smtp-out.rrz.uni-koeln.de,M=h')

That worked fine on a test system, but for reasons unknown it failed on our actual production server. It resulted in these error messages:

Mar 23 10:35:39 smtp-out.rrz.uni-koeln.de sendmail[6662]: NOQUEUE: SYSERR(root): opendaemonsocket: daemon MTA-v6: cannot bind: Address already in use
Mar 23 10:35:39 smtp-out.rrz.uni-koeln.de sendmail[6662]: daemon MTA-v6: problem creating SMTP socket
Mar 23 10:35:39 smtp-out.rrz.uni-koeln.de sendmail[6662]: NOQUEUE: SYSERR(root): opendaemonsocket: daemon MTA-v6: server SMTP socket wedged: exiting

We have now implemented a workaround: instead of the hostname we use the actual IPv6 address for the IPv6 daemon and client:

DAEMON_OPTIONS(`Name=MTA-v4,Family=inet,A=smtp-out.rrz.uni-koeln.de,M=fh')
DAEMON_OPTIONS(`Name=MTA-v6,Family=inet6,A=IPv6:2a00:a200:0:12::25,M=fh')
DAEMON_OPTIONS(`Port=smtp,Addr=127.0.0.1, Name=MTA')
CLIENT_OPTIONS(`Name=MSA-v4,Family=inet,A=smtp-out.rrz.uni-koeln.de,M=h')
CLIENT_OPTIONS(`Name=MSA-v6,Family=inet6,A=IPv6:2a00:a200:0:12::25,M=h')

One more tip: with an IPv4-only setup we used the ‚b‘ flag (use the incoming interface for sending the message) as modifier for the DAEMON_OPTIONS, but with dual-stack that doesn’t work: if you receive a message via IPv4 but have to send it via IPv6 (or vice versa), it will fail.

Web-Browser und -Server… was reden die da eigentlich??? Server-Sockets mit nc

Wer sich mit Webservern und -browsern beschäftigt, wird irgendwann an den Punkt kommen, wo man einfach mal wissen will, „Was zur Hölle der Browser und der Server da genau bereden“

Tools wie wget helfen einem zwar, zu überprüfen, ob ein Server eine Datei wirklich ausliefert, aber viele Funktionen werden im HTTP-Header ausgehandelt und die Server reagieren dabei speziell auf die unterschiedlichen, browserspezifischen Anfragen.

Wenn es also so aussieht, als würde der Server einem Firefox andere Daten schicken, als einem Chrome, möchte man sicherlich wissen, ob es wirklich so ist und wie sich die Kommunikation generell unterscheidet.

Hier kann ein Trick helfen, über den ich kürzlich gestolpert bin. Er beruht auf dem Befehl nc (netcat), der in vielen Konstellationen hilfreich sein kann.

nc (netcat)

nc öffnet entweder eine Verbindung zu einem Server-Port (z.b. dem HTTP-Port (80) eines Webservers) und verhält sich dabei sehr ähnlich zu telnet.
Im Gegensatz zu telnet kann nc jedoch auch selbst einen Socket öffnen und auf eingehende Anfragen antworten. Hierfür wird der Switch -l verwendet.

Egal, in welcher Richtung die Kommunikation aufgebaut wird, verbindet nc immer die Standard-Eingabe (stdin) mit dem Kommunikationspartner.
Wird der Befehl also einfach so verwendet, können Sie direkt mit der Tastatur eingeben, was dem Kommunikationspartner gesendet werden soll.
Startet man auf einem Rechner ein lauschenden Server-Socket auf Port 54321:

# nc -l 54321

… kann man sich von einem anderen Rechner aus damit verbinden:

# nc hostname.des.servers 54321

Nun hat man eine Verbindung, die man zum Chatten verwenden kann.

Auf dem üblichen Weg lassen sich per Pipe auch die Ausgaben anderer Befehle über diese Leitung zu übertragen, statt die Tastatur Eingabe zu verbinden.
Startet man den Server z.B. so:

# ls -l | nc -l 54321

… bekommt man beim Verbinden mit dem Socket das aktuelle Verzeichnis, in dem der Server gestartet wurde angezeigt. Danach schließt sich die Verbindung und sowohl Server- als auch Client-Prozesse werden gestoppt.

Sehen was der Browser sendet:

So ist nc also schon nützlich: Man kann einen Server-Socket öffnen und im Webbrowser dessen Adresse eingeben. Im obigen Beispiel also http://localhost:54321/. Zwar wird im Browser so nicht unbedingt etwas sinnvolles angezeigt, weil unser nc-Server kein HTTP spricht, aber auf der Konsole können wir nun sehen, welche Anfrage genau der Browser an unseren Server geschickt hat:

GET / HTTP/1.1
Host: localhost:54321
Connecdern: keep-alive
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36 Vivaldi/1.0.403.20
Accept-Encoding: gzip, deflate, sdch
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4
Cookie: _pk_id.2478.1fff=bda69bf5b5894886.1457526065.1.1457526065.1457526065.

Dies ist nun aber nur eine einzelne Anfrage und wir können nicht die Antwort des Servers sehen, der uns eigentlich interessiert.

Umleiten einer Verbindung

Hier kommt nun eine weitere praktische Zutat ins Spiel: Named Pipes. Während wir mit dem Pipe-Symbol (Senkrechter Strich: |) nur die Ausgabe eines Programms mit der Eingabe des direkt folgenden Programms verbinden können, werden Named Pipes im Filesystem abgelegt und können auf diese Weise angesprochen. Diese Pipes werden mit dem Befehl mkfifo angelegt, sie verhalten sich dabei weitgehend wie Dateien.

Named Pipes können wir nun dazu verwenden, zwei nc-Prozesse miteinander zu verbinden: Einen, der auf Verbindungen vom Browser wartet, einen der eine Verbindung zu einem Webserver herstellt:

Anlegen der Named Pipe (der Name und Pfad können frei gewählt werden):

# mkfifo /tmp/proxyfifo

Zwei nc-Prozesse starten und verbinden

# nc -l -p 54321 </tmp/proxyfifo | nc server.domain.de 80 >/tmp/proxyfifo

Löschen der Pipe:

#rm /tmp/proxyfifo

Statt http://server.domain.de geben wir nun im Browser ein: http://localhost:54321.

Der Browser zeigt uns nun die Webseite des Servers an. Da unsere nc-Prozesse aber nur jeweils eine Verbindung aufbauen und sich danach beenden, werden wahrscheinlich Bilder und CSS-Dateien nicht vollständig geladen. Beherrschen Browser und Webserver beide „Keep-Alive“ werden jedoch durchaus mehrere Ressourcen übertragen bevor die Verbindung beendet wird – nur halt nicht alle.

Dies ist bis jetzt noch wenig sinnvoll, aber da die Verbindung nun durch unsere Hände geleitet wird, können wir uns einklinken, und mitlesen, was die beiden Gesprächspartner miteinander austauschen.

Belauschen der Verbindung:

# mkfifo /tmp/proxyfifo
# nc -l -p 8080 </tmp/proxyfifo | tee /dev/stderr | nc server.domain.de 8480 | tee /dev/stderr >/tmp/proxyfifo
# rm /tmp/proxyfifo

Der Einfachheit halber werden hier beide Kommunikationsrichtungen per tee zusätzlich auf der Standard-Fehlerausgabe (stderr) ausgegeben, was zum Mitlesen ausreicht.

In manchen Fällen möchte man darüber hinaus in die Kommunikation Eingreifen und die übertragenen Daten „on-the-fly“ manipulieren, auch dies ist mit diesem Ansatz möglich.

Da der Browser im obigen Beispiel glaubt mit „localhost“ zu kommunizieren, überträgt er in seinem HTTP-Request einen falschen Host, dies könnte man beispielsweise mit sed korrigieren wollen:

# mkfifo /tmp/proxyfifo
# nc -l -p 8080 </tmp/proxyfifo | sed -u "s/Host: localhost:8080/Host: rrzk.uni-koeln.de/g" | tee /dev/stderr | nc rrzk.uni-koeln.de 80
# rm /tmp/proxyfifo

Mit diesem Beispiel kann man die Webseite des RRZK über den Aufruf http://localhost:8080 laden und sich anschauen welche Daten ausgetauscht werden. (Wie erwähnt wird die Seite so nicht vollständig geladen).

Aufräumen:

Damit keine Bruchstücke liegenbleiben sollte man eine Named Pipe immer wieder neu anlegen und nach dem Aufruf direkt löschen.

Andere Anwendungsgebiete:

nc kann hier mit jeder Art TCP/IP-Verbindung umgehen, es ist also nicht auf Webkommunikation begrenzt. Besonders bei Anwendungen mit dauerhaft gehaltenen Verbindungen kann nc hier sein volles Potenzial ausspielen. Dies kann besonders Interessant sein bei:

  • Mail-Servern
  • Chat-Servern
  • Spiele-Servern
  • generell allem, bei dem man eine Serveradresse angeben muss

Sie können so rausfinden, welches Protokoll Ihr Netzwerkdrucker spricht und ob eine angeblich verschlüsselte Verbindung wirklich verschlüsselt ist (nc zeigt Binärdaten, wie verschlüsselte oder komprimierte Verbindungen und Dateiinhalte natürlich nur in Form von Datenmüll an).

Fazit:

Mit nc lassen sich auf viele Weisen Daten sammeln, die einem bei der Fehlersuche und -analyse hilfreich sein können.
Obige Beispiele sind nur ein erster Ansatz, sicherlich gibt es noch raffiniertere Kombinationen. Schreiben Sie gerne Ihre eignen Tricks zu diesem Thema in die Kommentare.

Update bezüglich Microsoft Excel für Mac und ODBC-Zugriff auf MySQL-Server

Das Folgende ist ein Update für diesen alten Artikel. Ich nutze mittlerweile Microsoft Excel 15 für Mac und El Capitán. Microsoft Query ist kein eigenes Programm mehr, sondern es öffnet sich ein Fenster innerhalb von Excel, das diesen Namen trägt. Dabei handelt es sich im Gegensatz zu früher um ein 32-Bit-x86-Programm. Deshalb muss man nicht mehr mühevoll ein fat binary erzeugen, dafür gibt es neue Probleme. Da ich nirgendwo einen Artikel zu diesem Thema finden konnte, dachte ich mir, dass ich so vielleicht mindestens einer weiteren Person auf dem Planeten helfen kann.

Neben Excel braucht man:

  • die 32-Bit-Version des freien MySQL-ODBC-Connectors (Plattform Mac OS X, Mac OS X 10.7 (x86, 32-bit), DMG Archive, aktuelle Version: 5.3.4)
    NB: man muss den Installer per Rechtsklick öffnen, weil er nicht signiert ist.
  • den ODBC-Manager

Nachdem man beide Pakete installiert hat, muss man zunächst den Connector verschieben oder kopieren. Der Installer installiert ihn nach /usr/local/lib. Wenn man den Connector dort lässt, kann Excel ihn nicht öffnen. Im Systemlog findet man diesen Hinweis:

sandboxd[160] ([45299]): Microsoft Excel(45299) deny file-read-data /usr/local/lib/libmyodbc5w.so

Excel hat über seine Sandbox also keine Leserechte auf das Verzeichnis. Security kann so lästig sein 😉

Dieses Problem kann man im Terminal wie folgt beheben:

$ sudo cp -p /usr/local/lib/libmyodbc* /Library/ODBC/

Wenn man danach den ODBC-Manager (im Ordner Dienstprogramme) startet, sollte man so etwas im Treiber-Tab anlegen:

ODBC-Treiber

Danach muss man für die gewünschte MySQL-Verbindung einen System-DSN anlegen:

DSN anlegen

DSN bearbeiten

Diese Verbindung kann man danach in Excel verwenden:

Datenbankabfrage einfügen

Microsoft Query

Microsoft Query