Funktion des Proxy-Servers
Da weder der Spring selbst noch der Dacia-Server in der Lage sind, zeitgesteuert zu arbeiten, und ein Handy nun mal zur Maschinensteuerung nicht gemacht wurde, wurde aus diesem Grunde der Proxy-Server entwickelt. Er kann mehrere Springs gleichzeitig managen und übernimmt die Kommunikation mit dem Dacia-Server. Damit das funktioniert, benötigt der Proxy eine Datenbank. Und zwar keine relationale Datenbank wie MariaDB, sondern eine objektorientierte Datenbank. Ich habe mich für MongoDB entschieden. Sie lässt sich leicht installieren und konfigurieren. Diese Datenbank läuft bei mir auf einem Linux mit ARM-Prozessor, also einem kleinen Pi. Der ist sehr sparsam, was den Stromverbrauch angeht, und auch ausreichend schnell für diese Aufgabe. Die Datenbank ist sozusagen der Dreh- und Angelpunkt. Der Client kommuniziert mit der Datenbank, und der Proxy kommuniziert mit der Datenbank. Es findet also keine direkte Kommunikation zwischen dem Proxy-Programm und der App statt. Alles läuft über die Datenbank. Der Client holt sich die Daten von der Datenbank und schreibt sie auch in diese hinein. Der Proxy holt sich die Daten vom Dacia-Server und schreibt sie in die Datenbank. Er holt sich auch die Daten von der Datenbank, wertet diese aus und sendet dann die Befehle an den Dacia-Server.
Die Verbindung zwischen der App und der Datenbank ist TLS-verschlüsselt. Das Ganze funktioniert nur mit öffentlichen Zertifikaten, sonst nicht. Deshalb wird auch ein Name benötigt, über den auf die Datenbank zugegriffen werden kann. Dieser Name ist dann über ein Zertifikat abgesichert. Da ich keine feste IP habe, benutze ich einen DynDNS-Dienst. Welcher Dienst verwendet wird, ist egal; ich habe mich für ydns entschieden. Es geht aber auch jeder andere Dienst. Das Zertifikat wird von Let's Encrypt erstellt, das genauso wie der DynDNS-Dienst, die Datenbank und das Linux nichts kostet. Folgende Schritte müssen durchgeführt werden.
Die Verbindung zwischen der App und der Datenbank ist TLS-verschlüsselt. Das Ganze funktioniert nur mit öffentlichen Zertifikaten, sonst nicht. Deshalb wird auch ein Name benötigt, über den auf die Datenbank zugegriffen werden kann. Dieser Name ist dann über ein Zertifikat abgesichert. Da ich keine feste IP habe, benutze ich einen DynDNS-Dienst. Welcher Dienst verwendet wird, ist egal; ich habe mich für ydns entschieden. Es geht aber auch jeder andere Dienst. Das Zertifikat wird von Let's Encrypt erstellt, das genauso wie der DynDNS-Dienst, die Datenbank und das Linux nichts kostet. Folgende Schritte müssen durchgeführt werden.
- MongoDB installieren
- MongoDB-Admin erstellen
- MongoDB-Authentifizierung konfigurieren
- MongoDB-Netzwerk freigeben
- Einen DynDNS-Provider suchen & eine Domain erzeugen
- Webserver installieren und HTTP für die Domain einrichten
- Certbot installieren
- Zertifikate mit Certbot erzeugen
- Batch für die Erzeugung des MongoDB-Zertifikates erzeugen
- Automatische Erneuerung der Zertifikate einrichten
- MongoDB für die TLS-Verschlüsselung konfigurieren
- Den Proxy auf den Server kopieren
- Die Konfig-Datei des Proxy ausfüllen
- Den Proxy für wiederkehrenden Aufruf einrichten
- Den Client anbinden
Das sieht erstmal viel aus, ist jedoch längst nicht so schlimm. Die Skripte, die gebraucht werden, liefere ich alle mit. Die Änderungen, die gebraucht werden, liefere ich auch mit.
Installation
Zunächst wird natürlich ein Betriebssystem benötigt. Das kann ein Linux unter ARM oder x86 sein oder ein Windows. Solltet ihr den Proxy für Windows benötigen, so schreibt mich über das Springforum an. Da ich selbst nicht mit Windows arbeite, kopiere ich nicht immer den Proxy in den Download-Bereich.Wie wird nun der Proxy für die App installiert?
Der Proxy selbst braucht keine Installation. Er wird nur auf das System kopiert und läuft dann sofort. Das ist alles.
MongoDB
Um die MongoDB zu installieren, bin ich nach dieser Anleitung vorgegangen. Das hat gut funktioniert. Zu der Datenbank wird auch die Shell für den Zugriff auf die Datenbank benötigt, die mongosh Shell.Der MongoDB-Admin
Nachdem die Datenbank installiert wurde, muss ein Admin-User angelegt werden mit Passwort. Das geht folgendermaßen:Mit mongosh auf den Server zugreifen. Zuerst müssen die zwei Befehle ausgeführt werden auf dem Server, auf dem MongoDB und mongosh installiert worden sind:
- mongosh
- use admin
db.createUser(
{ user: "derAdmin",
pwd: "dasPasswort",
roles:[{role: "userAdminAnyDatabase" ,db:"admin" }]})
Dann in die Shell kopieren und fertig. Sollte keine Fehlermeldung erscheinen, hat es funktioniert. Wir können einfach mit
- show users
Konfiguration anpassen
Nun muss nur noch die Konfiguration von MongoDB in /etc/mongod.conf angepasst werden, damit die Anmeldung auch funktioniert. Es müssen zwei Punkte angepasst werden, damit die Anmeldung mit einem Namen von einem anderen Rechner aus funktioniert. Das Netzwerk muss angepasst werden, damit die Anmeldung von einem entfernten Rechner erlaubt wird. Das sind die Eintragungen unter # network interfaces. Das kann so reinkopiert werden, oder die Punkte in der Datei einfach entsprechend anpassen. Und genau das wird auch mit dem Punkt security gemacht. Den Punkt security suchen, das Kommentarzeichen entfernen und einschalten. Sollte der Punkt security nicht vorhanden sein, einfach neu einfügen. Denkt daran, dass das eine Systemdatei ist, die nur mit Root-Berechtigungen angepasst werden kann. Also z.B. mit sudo nano /etc/mongod.conf die Datei editieren und anpassen:# network interfaces
net:
port: 27017
bindIp: 0.0.0.0
security:
authorization: enabled
Wenn das gemacht wurde muß der Dienst neu gestartet werden mit
- sudo systemctl restart mongod
- mongosh -u derAdmin -p dasPasswort ipadresse:27017/admin
Die Verschlüsselung
Wir haben jetzt die Datenbank angelegt, einen Admin-User mit Passwort erzeugt und können uns aus der Ferne damit anmelden. Ein Problem gibt es aber noch: Die Verbindung ist unverschlüsselt. Alle Daten werden im Klartext übertragen. Deshalb muss das Ganze noch verschlüsselt werden. Das ist wichtig, da nur Daten zwischen der Datenbank und den Handys hin und her fließen. Die Mobilgeräte sprechen nur mit der Datenbank, nicht mit dem eigentlichen Proxy. Damit wir das Ganze verschlüsseln können, brauchen wir einen Domain-Namen für den Datenbankserver. Dieser Name wird über ein Zertifikat abgesichert. Das Ganze kann man komplett kostenlos und sicher machen.Der Domainname
Da MongoDB für die Verschlüsselung nur offizielle Domain-Namen erlaubt, benötigen wir nun zwingend einen offiziellen Namen für die Datenbank. Da die wenigsten von euch sicherlich eine feste IP haben, wird ein DynDNS-Dienst benötigt. Es gibt viele kostenlose Angebote, z.B. duckdns, IPv64.net, ydns... Wenn ihr euch für einen entschieden habt, sucht euch einen schönen Namen aus, und los geht's. Vermutlich wird bei euch auch jede Nacht die IP geändert. Ich teste alle 5 Minuten, ob die IP sich geändert hat, und wenn ja, dann wird das Aktualisierungsscript aufgerufen. Das mache ich mit folgendem Script:Zur Info: So ein Script ist nicht notwendig. Jeder Provider zeigt Euch wie ihr die domain immer aktuell haltet. Ich möchte nicht so oft beim Anbieter anbimmeln, deshalb habe ich es geschrieben.
#!/bin/bash
# Die eigene öffentliche IP wird abgefragt
my_ip=$(wget -4 -O - -q icanhazip.com)
#Die IP für euren Domainnamen wird abgefragt
target_ip=$(ping -c 1 euerdomainname.xx.yy | awk -F'[()]' '{print $2}' | head -n 1)
dt=$(date +%c)
#echo $dt "meine IP" $my_ip "Domain-IP" $target_ip >>/pfad/ip.log
if [ "$my_ip" != "$target_ip" ]; then
echo $dt "meine IP" $my_ip "Domain-IP" $target_ip >>/pfad/ip.log
#Die IP s sind nicht gleich Das Script zum anpassen der ip yds wird aufgerufen
/pfad/ydns
#echo $dt "meine IP" $my_ip "Domain-IP" $target_ip >>/pfad/ip.log
exit
fi
echo $dt >>/pfad/ip.log
Mit ipV6 sieht es entsprechend anders aus. ich werde das für IPv6 auch noch mal posten.
Es ist auch möglich, alle 5 Minuten direkt das Aktualisierungsscript /pfad/ydns aufzurufen. Die DynDNS-Anbieter sind so schlau, dass sie erkennen, wenn die IP sich nicht geändert hat. Ich möchte da jedoch nicht immer anklopfen, damit die Server immer eine schnelle Reaktionszeit haben.
Der folgende Batch setzt das Zertifikat für MongoDB richtig zusammen aus den Let's Encrypt-Zertifikaten. Ich habe ihn erneuerungZertifikat genannt. Den Ordner ydns mit dem Pfad /etc/ssl/ydns habe ich für das Zertifikat für den MongoDB-Server abgelegt:
#!/bin/bash
cat /etc/letsencrypt/live/springcontrol.ydns.eu/fullchain.pem /etc/letsencrypt/live/springcontrol.ydns.eu/privkey.pem > /etc/ssl/ydns/cert-chain-key.pem
echo "Deploy-Hook wurde ausgeführt am $(date)" >> /var/log/certbot-deploy-hook.log
systemctl restart mongod
Das Zertifikat
Wenn das mit dem Namen nun geklärt wurde, dann muss noch das Zertifikat erzeugt werden. Zum einen kann man diese natürlich kaufen, das ist jedoch nicht nötig. Es gibt auch kostenlose Anbieter, die für unsere Zwecke völlig ausreichen. Die Zertifikate können z.B. von Let's Encrypt verwendet werden. Das Zertifikat alleine reicht noch nicht. Da die kostenlosen Zertifikate oft nur eine kurze Zeit gelten, sollte der Austausch automatisch erfolgen. Das funktioniert wunderbar mit certbot. Damit wir ein Zertifikat bekommen, müssen wir die Domain über HTTP erreichbar machen. Dazu brauchen wir leider noch einen Webserver. Ich selbst nehme gerne nginx. Eine Anleitung zur Installation & Konfiguration findet ihr z.B. hier.Also müssen folgende Punkte gemacht werden:
- Webserver installieren, z.B. nginx
- Eure Domain über HTTP erreichbar machen
- Certbot installieren
- sudo certbot --nginx (erzeugt euch für die Domain ein Zertifikat. Das wird für MongoDB benötigt)
Wenn alles geklappt hat, dann sollte ein Ordner /etc/letsencrypt erzeugt worden sein, in dem die Zertifikate sind.
Nun kommt die Zusammensetzung des Zertifikates für MongoDB.
Die Reihenfolge, in der das Zertifikat für MongoDB aufgebaut sein muss, ist folgende:
- Das Zertifikat. Das enthält den öffentlichen Schlüssel.
- Das Chain-Zertifikat. Das ist das Verbindungszertifikat zwischen dem Zertifikat und dem Root-Zertifikat.
- Der private Schlüssel.
Damit das alles rechtzeitig erneuert wird,
0 */12 * * * root test -x /usr/bin/certbot -a \! -d /run/systemd/system && perl -e 'sleep int(rand(43200))' && certbot -q renew --no-random-sleep-on-renew && certbot renew --quiet --post-hook "/etc/erneuerungZertifikat"
Dieser Teil der Zeile oben certbot renew --quiet --post-hook "/etc/erneuerungZertifikat" habe ich angefügt damit das Zertifikat für mongo auch erneuert wird. Der Teil davor wird automatisch von certbot erzeugt.
Nun muß nur noch die Konfiguration von mongodb /etc/mongod.conf an einer angepasst werden. der Punkt #network interfaces wird nun erweitert:
# network interfaces
net:
port: 27017
bindIp: 0.0.0.0
tls:
mode: requireTLS
certificateKeyFile: /etc/ssl/ydns/cert-chain-key.pem
CAFile: /etc/ssl/ydns/root.pem
allowConnectionsWithoutCertificates: true
Danach den Neustart von mongodb nicht vergessen!
Überprüfen kann man den Zugriff ganz einfach:
- mongosh --tls -u derAdmin -p dasPasswort derdomainname.xx:27017/admin
Der Proxy
Kommen wir nun zum eigentlichen Teil, zum Proxy. Dieser wird einfach genommen und kopiert, z.B. in das Homeverzeichnis eines Users.Wenn der Proxy einfach gestartet wird, ohne irgendetwas zu tun, dann erzeugt er die Datei config.json und beendet sich wieder:
{
"id":1,
"dbAdresse":"www.meinedomain.xyz",
"dbPort":27017,
"adminName":"meinAdmin",
"adminPasswort":"pw",
"creatorName":"userCreatorUser",
"creatorPasswort":"pw"
}
Die "id":1 Bezeichnung einfach so lassen, dann entsprechend den Admin-Namen und Passwort eintragen, sowie die Domain. Der creatorName ist ein User, der nur das Recht hat, User anzulegen. Das ist sozusagen ein öffentlicher User. Dieser Name und das Passwort werden an die Leute weitergegeben, die den Proxy benutzen sollen. Dieser User erzeugt dann den eigentlichen User, mit dem gearbeitet werden soll. Für diesen User ist in der App ein extra Feld vorgesehen. In der App werden also zwei User eingetragen:
- Der öffentliche creatorName-User mit Passwort. Dieser User erlaubt das Anlegen des eigentlichen persönlichen Users.
- Der eigentliche private User. Dieser User kommuniziert mit dem Proxy. Ist dieser User angelegt, dann hat der öffentliche User in der App keine Bedeutung mehr.
Der Proxy hat nun zwei Möglichkeiten zu laufen:
- Er wird mit dem Parameter cron gestartet und läuft dann so lange, bis er wieder beendet wird.
- Er wird in der crontab eingetragen und soll jede Minute gestartet werden. Er beendet sich nach jedem Lauf selbst und wird immer wieder neu gestartet. Das hat den Vorteil, dass ein Absturz oder Speicherüberlauf nicht auftreten wird, da er sich beim Starten immer neu initialisiert.