6.6. Kooperation über VNC (DaSo)

Ein weiteres Feature des edux-com Kommandozeilentools, ist die Implementation einer schnellen und einfachen Verbindung der Geräte durch “Virtual Network Computing” (VNC). Bei VNC handelt es sich um eine Software, mit der der Bildschirm eines Remote-Gerät übertragen werden und sogar in diesem mit den eigenen Peripheriegeräten eingegriffen werden kann. Genau wie bei SSH implementiert VNC eine Client-Server Architektur. Das bedeutet, dass ein Gerät die Rolle des Servers übernimmt und somit eine Instanz der graphischen Oberfläche zum Betrachten durch andere, nämlich durch die Clients, freigibt. Ein Client sendet seine eigenen Eingaben (Tastaturanschläge, Mausbewegungen) an den Server, welche dort als lokale Eingaben interpretiert werden.

vnc

Abb. 6.11 Client-Server-Architektur von VNC

Interessant im Kontext unseres Projektes ist ein solches Tool bei Situationen, in denen man jemandem schnell unter die Arme greifen muss, zum Beispiel einem verlorenem Schüler, der nicht genau weiß wo er hin klicken soll, oder um ein Gerät herunterzufahren welches am anderen Ende des Raums liegt.

Viele Features von VNC werden auch durch Webanwendungen wie Zoom, Jitsi oder Meetzi angeboten. Diese leisten teilweise sogar mehr wie pures VNC, allerdings benötigen alle diese Dienste einen Zugang zum Internet, und einen guten und stabilen obendrein. VNC läuft allein über das lokale Netzwerk und könnte somit auch über einen Handy-Hotspot problemlos laufen. Deshalb ist ein solches Tool optimal geeignet für unseren Anwendungszweck, da Schulnetzwerke und deren Internetzugang oftmals nicht die schnellsten sind.

Um nun eine vollständige Interaktion zwischen Geräten zu erlauben, muss also auf jedem ein solcher Server laufen.

Es gibt viele verschiedene VNC-Server welche sich für die Anwendungen dieses Projekts eignen. Letztendlich wurde sich für TigerVNC, ein Fork von TightVNC, entschieden. TigerVNC (so wie die meisten VNC-Server auch) bietet verschiedene Funktionen an, die sich als nützlich erweisen, aber auch einige Aspekte die für unseren Anwendungsfall umgangen werden müssen. Ein Beispiel dafür wäre das Setzen eines Passwortes. Dies wird in den meisten Fällen dringlichst empfohlen, ist aber im Kontext einer Schule nicht umsetzbar, da man nicht von Schülern erwarten kann sich ein Passwort für jedes einzelne Gerät zu merken. Würde man für jedes Gerät das gleiche setzen, so würde das Passwort innerhalb kürzester Zeit überall bekannt sein, was ein Passwort im allgemeinen direkt wieder unnötig macht. Ohne Sicherung ist allerdings jedes Gerät fähig sich zu einem Server zu Verbinden und dort allerlei Schade anzurichten.

Eine Lösung für dieses Problem wäre die Passwörter dynamisch generieren zu lassen und diese den Beteiligten mitzuteilen. Ein solcher Ansatz erfordert aber in Anwendung eine weitere Hürde, welche der Benutzer in der Graphischen Oberfläche überwältigen muss. Eine durchaus bessere Lösung ist aber, in den Einstellungen des Servers, Verbindungen von außen (also solche die nicht von localhost kommen) zu verbieten. Dies nimmt uns erstmal die Möglichkeit ein Gerät mit einem anderen zu verbinden, doch zum Glück können sich bereits alle Geräte per SSH verbinden.

Ssh erlaubt das erstellen einer Pipeline, durch welche Verbindungen auch über Netzwerke hinweg aufgebaut werden können. Somit kann ein VNC-Server, obwohl dieser nur über localhost erreichbar ist, von einem Remote-Gerät angesprochen werden. Die ganze Prozedur läuft wie folgt ab:

  • Es wird eine SSH-Verbindung zwischen den beiden Geräten aufgebaut. Der SSH-Client ist nun mit dem SSH-Server verbunden.

  • Eine Pipeline entsteht “zwischen” den Geräten. Beide Seiten der Pipeline sind mit einer Port Nummer versehen. Auf der Seite des Clients kann diese beliebig gewählt werden. Verbindungsversuche zu Server müssen nun nichtmehr mit der Server-IP und der Port Nummer des Dienstes, sondern mit localhost und der Port Nummer der Clientseite der Pipeline durchgeführt werden. Auf der Seite des Servers ist die Port Nummer der Pipeline die gleiche wie die des VNC-Servers.

  • Wird nun eine Verbindung zur Pipeline aufgebaut, werden alle Netzwerkpakete über diese Pipeline an den localhost des Remote-Gerätes weitergeleitet. Antworten bahnen sich ebenfalls durch die Pipeline.

vnc_ssh

Abb. 6.12 VNC-Verbindung über SSH-Pipeline

Um eine solche Pipeline aufzubauen, genügt es eine normale SSH-Verbindung aufzubauen mithilfe des “SSH” Befehls und der Option “-L”.

pipeline

Abb. 6.13 Befehl zum Aufbauen einer Pipeline

Die Option “-L” nimmt ein Argument mit folgendem Aufbau:

[Adresse des Clients]:[Port der Pipeline auf Clientseite]:`

[Adresse des Servers aus Sicht des SSH-Servers]:[Port des Servers]`

Wie in Abb. 6.13 zu sehen öffnet dieser Befehl eine Shell-Sitzung auf dem Remote-Host. Dies ist in unserem Fall nicht unbedingt gewünscht.

Um keine Shell zu bekommen, können wir die Option “-N” benutzen. Mit der zusätzlichen Option “-f” wird die Verbindung in den Hintergrund gestellt und unsere Shell ist frei für weiteres ausführen von Befehlen. Mit dieser Herangehensweise bleibt die Pipeline allerdings dauerhaft offen und belegt die Ports die ganze Zeit. Anstatt die Pipeline mühsam mit Signalen über ihre Prozess-ID zu beenden, kann man einen kleinen “Hack” anwenden, welcher auch in den letztendlichen Skripten benutzt wird.

Gibt man SSH einen Befehl zum Ausführen mit, so wird dieser auf dem Remote-Gerät ausgeführt und die Verbindung getrennt. Dieses Verhalten machen wir uns zunutze indem wir auf dem Remote-Gerät “sleep 60” aufrufen. Somit besteht die Verbindung nur diese 60 Sekunden, die wir den Host in schlaf versetzt haben und danach wird alles voneinander getrennt. Wenn sich aber nun während dieser 60 Sekunden eine Verbindung über die Pipeline aufbaut, so trennt sich SSH nicht voneinander, bis alle Pipeline Verbindungen geschlossen sind. Benutzen wir nun noch die “-f” Option und schon haben wir eine sich bei Nichtbenutzung selbst schließende Pipeline im Hintergrund laufen und 60 Sekunden Zeit sich über diese zu Verbinden.

ssh -f -L localhost:5901:localhost:5900 \
    192.168.122.138 \
    sleep 60

Nun konkret zu dem VNC-Server und VNC-Client. Wie oben Beschrieben fiel die Wahl des Servers auf TigerVNC, jedoch eine bestimmte Ausführung von diesem. Standardmäßig bietet TigerVNC eine eigene Session, in der eigene graphische Aufgaben durchgeführt werden können. Wir wollen allerdings den aktuellen Bildschirm der Geräte an andere übertragen. Hierfür muss der TigerVNC-scraping-server benutzt werden. Dieser ist darauf ausgelegt eine bereits laufende Session einzufangen und mit dieser zu arbeiten.

Wichtig beim Starten des Servers ist es die richtige Display-Variable einzutragen. In den meisten Fällen ist diese aber, bei einem einzigen aktiven Nutzer, “:0”. Vor allem wenn Befehle über SSH auf einem anderen Gerät ausgeführt werden, ist diese Variable nicht gesetzt und muss bei jedem Remote-Befehlsaufruf manuell spezifiziert werden. Der Befehl um den VNC-Server zu starten ist folgender:

vnc_start

Abb. 6.14 Befehl zum Starten des VNC-Servers

Mit “-display :0” wird wie eben erläutert der zu benutzende Bildschirm spezifiziert. “-SecurityTypes None” ermöglicht das Verbinden ohne Passwortauthentifizierung. Dieser Server ist nun nur über localhost erreichbar. (Für uns aber kein Problem mit SSH) Wir können unseren Server mit diesem Befehl anzeigen lassen:

running_vnc

Abb. 6.15 Listen aller laufenden VNC-Server

Bezüglich des Clients, ist es naheliegend, dass sich für ein rein Browser-basiertes Gerät, ein Client der im Webbrowser arbeitet eignet. Ein solcher Client ist noVNC. NoVNC liefert einen Webserver, der dann in einem gewöhnlichen Browser Tab angezeigt werden kann. Um diesen Server zu starten wird ein weiteres Programm namens “Websockify” verwendet und diesem die Daten von noVNC übergeben.

start_client

Abb. 6.16 Befehl zum Starten des VNC-Clients

Die Option “-D” ist ähnlich wie “-f” bei SSH und schiebt den Prozess in den Hintergrund. Man sieht in Abb. 6.16, wie mit der Option “–web=…” die Daten von noVNC angegeben werden und auch wie ein Timeout von 60 Sekunden festgelegt wird. Dies bewirkt ebenfalls wie SSH, dass der Prozess nach 60 Sekunden Nichtbenutzung beendet wird.

Anzumerken ist auch hier nochmal dass dem Client nicht die IP-Adresse und Port des VNC-Servers gegeben wird, sondern die IP-Adresse und Port der Clientseite der Pipeline. Diese muss natürlich zu dem Zeitpunkt des Verbindens auch bestehen.

Nach Ausführen des Befehls kann man über die URL http://localhost:6080/vnc.html?host=localhost&port=6080 auf das Webinterface von noVNC zugreifen. Läuft nun auch der VNC-Server auf dem Remote-Gerät, kann man auf “Verbinden” drücken und sieht ab sofort den Bildschirm des anderen Geräts und kann dieses auch fernsteuern.

Hängt man in die URL noch ein “autoconnect=true”, so wird dem Nutzer der Click auf “Verbinden” erspart: http://localhost:6080/vnc.html?autoconnect=true&host=localhost&port=6080

Nun haben wir alle nötigen Befehle für eine Automatisierung in unserem Script festgelegt. Die Ausführung einer “Kontrollübername” spielt sich wie folgt ab:

  • Der Kontrollierende startet über SSH den VNC-Server auf dem anderen Gerät

  • Falls dieser schon existiert, schlägt der Befehl fehl und es geht ganz normal weiter

  • Der Kontrollierende erstellt eine Pipeline von sich zu dem anderen Gerät

  • Hier ist es gut wenn der VNC-Server selbst per SSH gestartet wurde, da man sich dann sicher sein kann unter welchem Port dieser läuft und so die Pipeline richtig bauen kann

  • Der Kontrollierende startet bei sich noVNC über websockify und übergibt Adresse und Port der Pipeline

  • Der Kontrollierende starten einen Browser und/oder öffnet einen neuen Tab mit der oben beschriebenen URL

  • Konkret in diesem Projekt passiert dies über den Javascript-Code des eigens erstellten Webinterfaces

Ab sofort kann das andere Gerät kontrolliert werden.

Auf fast gleiche Weise kann auch eine simple Bildschirmübertragung gestartet werden, ohne die Möglichkeit die Maus und Tastatur des anderen zu kontrollieren.

Hierfür müssen beim starten des VNC-Servers ein paar weitere Optionen mitgegeben um Remote-Tastenanschläge und Mausbewegungen zu ignorieren. Diese sind “-AcceptPointerEvents=false” und “-AcceptKeyEvents=false”.

Außerdem sollte dieser “View-Only” VNC-Server auf einen anderen Port hören, damit er mit dem obigen Server nicht in Konkurrenz tritt. Dies wird mit der Option “-rfbport …” realisiert. Der endgültige Befehl sieht wie folgt aus:

view_only

Abb. 6.17 Befehl zum Starten eines “View-Only” VNC-Servers

In dem Projekt geschieht dieser Start des Servers freiwillig durch den Nutzer, im Gegensatz zu der erzwungenen Kontrollübername des obigen Scripts. Sobald ein Nutzer also einen solchen “View-Only” Server laufen hat, können andere im SSH-Netzwerk der Bildschirmübertragung zusehen. Doch wie wird herausgefunden wo gerade im Netzwerk ein VNC-Server läuft? Natürlich könnte man wieder über eine mitgegebene IP-Adresse dem Script direkt sagen wo es zu schauen hat. Der Einfachheit halber wird aber festgelegt, dass maximal ein Gerät im Netzwerk seinen Bildschirm so übertragen darf. Dies erlaubt das Verbinden ohne Mitgabe einer IP-Adresse, da man so alle bekannten Geräte im Netzwerk ablaufen kann und den einzigen der einen Server gestartet hat finden kann.

Diese Einschränkung bedeutet aber auch dass wann man selber einen “View-Only” Server starten möchte, vorher das Netzwerk nach jemandem absuchen muss, der bereits einen Server am laufen hat.

Das komplette Verfahren läuft also so ab:

Eine Bildschirmübertragung soll gestartet werden

  • Iteriere über alle bekannten Geräte im SSH-Netzwerk und schaue nach ob bei diesen ein Server bereits läuft

  • Hierfür kann der Befehl “x0vncserver -list -rfbport {spezifizierter Port}” benutzt werden

  • Wenn niemand gefunden, starte selber den “View-Only” Server. Ansonsten Abbruch.

  • ner Bildschirmübertragung soll zugeschaut werden

  • Iteriere über alle bekannten Geräte im SSH-Netzwerk und suche denjenigen bei dem ein “View-Only” Server läuft

  • Auch hier: “x0vncserver -list -rfbport {spezifizierter Port}”

  • Zu gefundener IP SSH-Pipeline aufbauen und mit Websockify noVNC starten. Danach auf die obige URL im Browser navigieren. Falls nichts gefunden Abbruch.