Die Fortsetzung unseres Neos/Flow Meetups zur Entwicklung einer mit NFC gesteuerten Raspberry Pi Stechuhr lies, im Vergleich zum korrespondierenden Blog-Post, nicht lange auf sich warten. Entschuldigung für die Verzögerung!
Recap Teil 1
In Teil 1 unserer Raspberry-Pi-Bastelaktion trafen wir auf ein unerwartetes Problem:
Lesen eigens beschriebener Chips mit dem NFC-Modul RFID RC522
Nach über 4h abendlicher Bastelei waren wir nicht in der Lage dies schnell zu beheben.
Während des zweiten Bastel-Abends trafen wir auf weitere technische Herausforderungen, die wir fernab solcher Veranstaltungen und parallel zur Entstehung von Teil 2 lösen mussten.
Problem 1: Auslesen eigens beschriebener RFID-Chips mit NFC-Modul RFID RC522
In Teil 1 kamen wir zu dem Schluss, uns nach einer neuen NFC Bibliothek zum Auslesen der Chips umzuschauen, da die ursprünglich verwendete unsere selbstbeschriebenen im NDEF Format nicht lesen konnte.
Nachdem wir einige Nächte darüber geschlafen hatten, fiel uns eine einfachere Lösung ein - die interne Seriennummer der RFID-Chips.
Jeder RFID-Chip besitzt eine eineindeutige Seriennummer. Diese lesen wir nun aus und ordnen sie innerhalb des Flow Controllers dem jeweiligen Sandstormer zu.
Da es sich bei dieser Zuordnung um keine sicherheitsrelevanten Informationen wie bspw. Passwörter oder SSH-Keys handelt, nutzten wir kurzerhand die settings.yaml
Aus sicherheitstechnischen Gründen hat Mozilla sämtliche Extensions entfernt, die Remote-Code ausführen. Dies betrifft auch das von uns genutzte Add-On. Deshalb haben wir diesen Part an die aktuellen Gegebenheiten angepasst. Die aktualisierte Lösung findet ihr hier:
Analog unserer bisherigen Zeiterfassung soll auch der Raspberry Pi bei Anhalten des individuellen RFID Chips die korrespondierende Zeiterfassungs-Website des Sandstormers öffnen.
Standardmäßig ist es jedoch nicht möglich einzelne Browsertabs zu steuern und sie neue URLs aufrufen zu lassen, wie in unserem Fall nötig. Der Browser kann lediglich beim initialen Start oder Öffnen eines neuen Tabs eine spezifische URL öffnen. Da der Raspberry Pi durch seine weniger potente Hardware jedoch sehr lange brauch, um Firefox zu schließen und wieder zu starten, ist dies keine Option für den operativen Betrieb. Auch das Öffnen dutzender Tabs verlangsamt den Pi sukzessive und macht ihn damit unbrauchbar.
Die Lösung liefert das Addon Remote Control for Firefox. Es erweitert den Raspberry Pi Firefox um die benötigte Funktionalität. Via Telnet schicken wir JS Code an den Pi, die die Seite laden bzw. neuladen lassen.
Der angepasste Python Code sieht nun folgendermaßen aus:
#!/usr/bin/env python
# -*- coding: utf8 -*-
import RPi.GPIO as GPIO
import MFRC522
import signal
import datetime
import telnetlib
class Control(object):
def __init__(self, port=32000):
self.host = 'localhost'
self.port = port
def reload(self):
tn = telnetlib.Telnet(self.host, self.port)
tn.write("reload\n")
result = tn.read_until('\n')
return result
def load(self, url):
tn = telnetlib.Telnet(self.host, self.port)
tn.write('window.location="%s"' % url)
result = tn.read_until('\n')
return result
continue_reading = True
lastUid = [0, 0, 0, 0, 0]
timeToDelete = 0
# Capture SIGINT for cleanup when the script is aborted
def end_read(signal, frame):
global continue_reading
continue_reading = False
GPIO.cleanup()
# Hook the SIGINT
signal.signal(signal.SIGINT, end_read)
# Create an objecet of the class MFRC522
MIFAREReader = MFRC522.MFRC522()
c = Control()
# This loop keeps checking for chips. If one is near it will get the UID and authenticate
while continue_reading:
(status, TagType) = MIFAREReader.MFRC522_Request(MIFAREReader.PICC_REQIDL)
# If a card is found
if timeToDelete != 0 and timeToDelete < datetime.datetime.now():
timeToDelete = 0
lastUid = [0, 0, 0, 0, 0]
c.load('https://www.your-url.de')
# Get the UID of the card
(status, uid) = MIFAREReader.MFRC522_Anticoll()
# If we have the UID, continue
if status == MIFAREReader.MI_OK:
if lastUid != uid:
uidStrings = []
for part in uid:
uidStrings.append(str(hex(part)).replace('0x', ''))
# we only use the first 4 entries
uidStrings.pop(4)
uidString = '-'.join(uidStrings)
lastUid = uid
timeToDelete = datetime.datetime.now() + datetime.timedelta(0, 5)
url = "http://www.your-url.de/workingtime/dongle/toggle.html?rfidToken=" + uidString
c.load(url)
Problem 3: Kubernetes
Parallel zum Projekt zogen wir unsere komplette Infrastruktur um. Statt Heroku setzen wir nun auf Kubernetes.
Unser Intranet ist durch einen SSO (Single Sign-on) geschützt, welches bisher keine Ausnahmen für externe Zugriffe außerhalb der bekannten Strukturen erlaubte. Dahingehend mussten wir unsere Nginx Ingress Controller Konfiguration anpassen.
Dabei haben wir einige Stolpersteine mitgenommen. In der Konfiguration wurde "ssl: true" aus der Build-Variablen entfernt. Dies hatte zur Folge, dass der "tls"-Block aus Ingress entfernt wurde und es daher keine SSL-Konfiguration gab.
Der 2. Ingress muss darüber hinaus auch als SSL markiert werden, was den Einsatz von "kubernetes.io/tls-acme" notwendig macht sowie den "tls:"-Block, der sagt, für welche Domains Zertifikate ausgestellt werden sollen.
Erschwert wurde das Gesamtkonstruktut dadurch, dass Kube-Lego deprecated ist und auf Kubernetes 1.9 nicht mehr supported wird. Dahingehend mussten wir alternativ erst auf cert-manager updaten.
Problem 4: Skript im Autostart
Last but not least - Der Pi soll ohne weiteres Zutun unsererseits nach einem Neustart von alleine wieder einsatzbereit sein. Dafür wurde Firefox in den Autostart gesetzt und unser eigener System Service frankWalter.service implementiert (s.u.)
Um den System Service ausführen zu können, wird ein neuer Benutzer benötigt. Dabei ist auch darauf zu achten, dass der Nutzer die Rechte hat den Code ausführen zu dürfen.