Skip to content

About

Selbstgehostete Arbeitsplätze im Browser - ein eigener Linux Docker-Container je Nutzer, Anwendungen einzeln gestreamt.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

Deutsch · English

OpenTerminalApps

OpenTerminalApps

Selbstgehostete Plattform, die jedem Nutzer einen eigenen Linux-Arbeitsplatz im Browser gibt. Darin sind seine Werkzeuge installiert — VS Code, VSCodium, JetBrains, Firefox, ein Terminal —, und jedes lässt sich einzeln formatfüllend streamen. Alle teilen sich ein Zuhause: dieselben Projekte, denselben SSH-Schlüssel, dieselbe Zwischenablage.

Daneben lassen sich einzelne Anwendungen als Wegwerf-Container starten und vorhandene Kasm-Images sowie ganze Registries einbinden — als Zusatz, nicht als Fundament.

Jeder Arbeitsplatz hängt in einem eigenen Netz hinter einem Router: kein Firmennetz, keine Nachbarsitzung, kein Wirt — bis jemand es ausdrücklich freigibt (firewall.md).

Stand: läuft und wird benutzt. 452 automatische Prüfungen, davon 107 in einem echten Browser. Was noch fehlt, steht offen in roadmap.md — nichts davon ist beschönigt.

Das Dashboard: eine laufende Sitzung mit neun Anwendungen, darunter die Kacheln der übrigen Arbeitsplätze

Der Start: die eigene laufende Sitzung mit ihren Anwendungen, was sie an Kernen und Speicher bekommen hat — und was sie im Netz darf. Darunter, was sich sonst noch starten lässt.


Schnellstart

Voraussetzungen auf einem Linux-Host: docker mit dem Compose-Plugin (docker compose version muss antworten), dazu git, make und openssl. Die Ports 8443 und 8081 müssen frei sein — 443 bleibt bewusst unbelegt, damit ein bestehendes Kasm daneben weiterlaufen kann. Beide sind über OTA_HTTPS_PORT und OTA_HTTP_PORT in deploy/.env änderbar.

Dazu 30000–30019 als Vorrat für Portfreigaben. Der Bildstrom braucht keinen eigenen Port: Selkies 2.0 überträgt über WebSockets durch denselben HTTPS-Port wie die Oberfläche. Nur Golden Images, die noch auf dem alten Basisimage mit Selkies 1.6.2 gebaut sind, brauchen 3478 (TURN) und 49160–49260/UDP. Steht OTA hinter einer Firewall mit Portweiterleitung, reicht nach aussen 443 — welche Ports wohin, und was dafür in deploy/.env gehört, steht in Kapitel 24. Und ein Adressbereich für die Arbeitsplatznetze, ab Werk 10.99.0.0/16 — er darf sich nicht mit dem Firmennetz überschneiden. Alles einstellbar, alles erklärt in deploy/.env.example und Kapitel 2.

git clone https://github.com/bmetallica/openterminalapps.git
cd openterminalapps

sudo make setup                  # .env mit Geheimnissen, Zertifikat, Verzeichnisse
sudo make up                     # Stack bauen und starten (beim ersten Mal einige Minuten)
sudo make admin NAME=deinname    # erstes Administratorkonto

Hinter einem Firmenproxy kommen drei Zeilen in deploy/.env dazu — OTA_HTTP_PROXY, OTA_HTTPS_PROXY und vor allem OTA_NO_PROXY, damit die Dienste untereinander nicht über den Proxy reden. Ohne Proxy ist nichts zu tun; das ist die Vorgabe. Handbuch, Kapitel 21 beschreibt auch, was OTA dabei nicht lösen kann: docker pull macht der Docker-Daemon und braucht seine eigene Konfiguration.

make setup erzeugt die Geheimnisse und trägt sie selbst ein — nachträglich ist nichts von Hand zu ergänzen. Ein zweiter Aufruf lässt vorhandene Werte unangetastet.

sudo braucht es, weil OTA Verzeichnisse unter /srv/ota anlegt und mit dem Docker-Socket spricht. Wer in der Gruppe docker ist und /srv/ota selbst angelegt hat, kann es weglassen.

make admin legt zwei Konten an und druckt beide Passwörter — sie werden nur dieses eine Mal gezeigt:

Konto Wo es liegt Wofür
<deinname> in Keycloak der Alltagszugang; die Anmeldung verlangt sofort ein eigenes Passwort
notfall lokal in OTA der Ausweg, wenn Keycloak einmal nicht antwortet

Das zweite ist keine Dreingabe: Ab dem Moment, in dem die Anmeldung über einen weiteren Dienst läuft, braucht es einen Weg herein, der ohne ihn funktioniert. Er ist unter /notfall erreichbar, und jede Anmeldung darüber steht im Protokoll.

https://<host>:8443/            # leitet zur zentralen Anmeldung weiter
https://<host>:8443/notfall     # der lokale Notzugang

Einmalig das Wurzelzertifikat importieren, dann warnt der Browser nicht mehr — auch nach jedem späteren Zertifikatswechsel:

sudo cp deploy/certs/ota-ca.crt /usr/local/share/ca-certificates/ota-ca.crt   # Linux, systemweit
sudo update-ca-certificates

Im Browser: Einstellungen → Zertifikate → Zertifizierungsstellen → deploy/certs/ota-ca.crt importieren, „Websites vertrauen" ankreuzen.

Läuft es?

make ps                          # alle Dienste sollten "healthy" sein
curl -k https://localhost:8443/healthz

Die Antwort nennt vier Dinge: db, agent, keycloak und den Gesamtzustand. Steht dort "keycloak":"nicht erreichbar", ist der Realm noch nicht eingerichtet oder der Dienst noch nicht oben — nachholen mit:

sudo make identity               # Realm, Clients, Rollen; idempotent

make up tut das von selbst und wartet dafür, bis Keycloak antwortet (beim ersten Start rund eine halbe Minute).

Wenn etwas klemmt: make logs und Handbuch, Kapitel 12 — dort stehen echte Fehler aus dem Betrieb mit Symptom, Ursache und Reparatur.

Der erste Arbeitsplatz

Das Basisimage baut make up beim ersten Start mit — ota/base-desktop:1, Debian 13 + XFCE + Selkies, ohne KasmVNC. Das dauert einige Minuten und passiert nur, wenn es fehlt; danach steht es unter Workspaces zur Auswahl. Neu bauen geht jederzeit von Hand:

scripts/build-desktop-image.sh --pruefen      # baut und misst 19 Punkte gegen den Vertrag mit dem Agent

Ein Abbild von Kasm braucht es dafür nicht. Die bleiben möglich, sind aber die Ausnahme — für fertige Kataloge und für den Umstieg (Kapitel 9).

Danach: Workspaces → Anlegen — der Knopf legt sofort eine Vorlage an und nimmt dafür das eigene Basisimage, sonst ein anderes, das nach einem Arbeitsplatz aussieht. Im Editor der Gruppe users zuweisen und einschalten, dann unter Start starten und über Software → Im Image nachsehen die Anwendungen freigeben. Ausführlich in Handbuch, Kapitel 2.

Aktualisieren

Eine neue Fassung aus dem Repository holen:

cd openterminalapps
sudo make backup          # erst sichern, dann ändern
git pull
sudo make update          # .env ergänzen, bauen, starten

Ab dem ersten Schritt zeigt jede OTA-Seite (Dashboard, Anmeldung, jede Sitzung) ein Banner „Achtung! OTA Update im Gange, bitte laden Sie alle OTA-Seiten neu, sobald diese Meldung verschwunden ist.“ Es verschwindet, sobald die neue API gesund ist; scheitert das Update, bleibt es stehen (Kapitel 11).

make update macht drei Dinge in dieser Reihenfolge, und die Reihenfolge ist nicht beliebig:

  1. Fehlende Einstellungen ergänzen. Bringt eine Fassung neue Werte in deploy/.env.example mit, trägt make update sie mit sinnvollen Vorgaben nach. Vorhandene Werte bleiben unangetastet — auch die Geheimnisse. Passierte das erst nach dem Bauen, startete ein Dienst gegen eine Variable, die es noch nicht gibt.
  2. Bauen und starten. Die Datenbank wandert dabei von selbst mit: Alembic beim Start der API, und fehlende Spalten zieht OTA aus dem Modell nach. Es gibt keinen Migrationsschritt von Hand.
  3. Sagen, was nicht von selbst passiert. Siehe unten.

Was ein Update nicht anfasst

Warum, und was zu tun ist
Laufende Sitzungen Sie behalten ihr Abbild, bis sie beendet werden. Das ist Absicht: Niemand soll mitten in der Arbeit unterbrochen werden. Wer die Neuerungen sofort will, beendet seinen Arbeitsplatz und startet ihn neu.
Das Basisimage Wird nicht bei jedem Update neu gebaut — es dauert Minuten, und die meisten Fassungen fassen es nicht an. make update prüft aber, ob die Bauanleitung neuer ist als das gebaute Abbild, und sagt es dann. Bauen mit scripts/build-desktop-image.sh --pruefen.
Die Arbeitsplatz-Images Ein neues Basisimage wirkt erst, wenn die davon abgeleiteten Images neu gebaut werden — in der Verwaltung unter Software.
deploy/.env Wird nur ergänzt, nie überschrieben. Wer eine Einstellung ändern will, tut das selbst; die Vorlage daneben erklärt jede.

Später auf einen Firmenproxy umstellen

Derselbe Weg, im laufenden Betrieb und ohne Neuaufsetzen: die drei Zeilen in deploy/.env eintragen, sudo make update. Dienste und neue Sitzungen übernehmen ihn sofort; laufende behalten ihren Stand bis zum nächsten Start. Das Basisimage muss dafür nicht neu gebaut werden — der Proxy steckt nie im Image. Zurückstellen ist derselbe Weg.

Gemessen in beide Richtungen; die Stolperstellen stehen in Kapitel 21.

Hinter einer Firewall mit NAT

Nutzer kommen von aussen über die Adresse der Firewall, OTA steht dahinter, vielleicht mit einem Reverse Proxy davor und dem Verzeichnis auf der anderen Seite. Nach aussen weiterzuleiten ist 443 (auf den Proxy, mit WebSockets — auch der Bildstrom geht seit Selkies 2.0 dort durch); ins äussere Netz braucht Keycloak 636 zum Verzeichnis. Nur solange Golden Images mit Selkies 1.6.2 laufen, kommt 3478/TCP direkt auf den OTA-Host dazu, und in deploy/.env zwei TURN-Adressen, die der Firewall und die eigene:

OTA_TURN_HOST=10.50.0.33        # die Firewall — so sehen die Browser den TURN
OTA_TURN_BIND=192.168.1.22      # der Host — daran bindet sich coturn
OTA_TURN_PROTOCOL=tcp
OTA_TURN_ICE_POLICY=relay
OTA_TRUSTED_PROXIES=192.168.1.23/32

Eine Hairpin-NAT an der Firewall braucht es nicht: Der Router der Arbeitsplätze biegt die veröffentlichte Adresse selbst auf den Host um. Die CA des Verzeichnisses gehört nach deploy/keycloak-truststore/. Vollständige Portlisten, ein Beispiel für nginx und die Prüfung: Kapitel 24.

Root-Arbeitsplätze freischalten

Root und Docker im Arbeitsplatz brauchen Sysbox auf dem Host. Das Paket will bei der Installation Docker neu starten und schlägt vor, dafür alle Container zu löschen — das Skript umgeht das und fasst keinen Container an:

sudo scripts/sysbox-einrichten.sh --pruefen          # Voraussetzungen ansehen
sudo scripts/sysbox-einrichten.sh --live-restore     # einrichten
sudo make up                                         # baut ota/base-desktop-root:1

Dann eine Vorlage der Klasse „Root mit Docker" anlegen und das Recht „Root-Arbeitsplatz nutzen" an die Gruppen vergeben, die es bekommen sollen. Ausführlich in Kapitel 25.

Den Spiegel der Paketquelle einschalten

Die eigene Paketquelle läuft immer — über sie kommt ota-selkies, der Streaming-Server, in die Arbeitsplätze. Ein Spiegel von Debian 13 und Dockers Paketquelle ist ein Zusatz, ab Werk aus. Ein Vollspiegel braucht grob 100 bis 130 GB:

# in deploy/.env
OTA_REPO_SPIEGEL=1
OTA_REPO_ROOT=/srv/ota/repo
sudo make update

Dann unter Verwaltung → Paketquellen einmal abgleichen. Ausführlich in Kapitel 26.

Wenn etwas schiefgeht

sudo make ps              # welcher Dienst steht nicht?
sudo make logs            # was sagt er?

Die Sicherung von oben spielt scripts/restore-db.sh zurück. Die Profile der Nutzer liegen unter /srv/ota/profiles und werden von einem Update nicht angefasst — sie überstehen auch ein vollständiges Neuaufsetzen des Stacks.

Ein Rückschritt auf die vorige Fassung ist git checkout <alter-stand> && sudo make update. Was dabei nicht zurückwandert, ist die Datenbank: Neue Spalten bleiben stehen. Sie stören eine ältere Fassung nicht, aber Daten, die es vorher nicht gab, sind dann eben da.

Was es kann

Der Arbeitsplatz

  • Ein Container je Nutzer, jede Anwendung auf ihrem eigenen Bildschirm, gestartet erst bei Bedarf
  • Jede Anwendung in einem eigenen Browsertab, mit eigener Adresse — und als Verknüpfung auf dem Desktop ablegbar (PWA)
  • Der ferne Bildschirm wächst mit dem Fenster, kein schwarzer Rand, keine Skalierung
  • Zwischenablage in beide Richtungen, auch zwischen zwei Anwendungen im selben Container
  • Klassischer XFCE-Desktop als zusätzliche Ansicht
  • Root-Arbeitsplatz für Entwickler: root, Docker und docker compose im Container — unter Sysbox, also ohne --privileged (root im Container ist auf dem Host unprivilegiert). Beenden hält an statt zu löschen: Was installiert wird, bleibt, unter fester Adresse; „Neu aufsetzen" holt ein neues Basisimage. Die inneren Container gehen durch denselben Router und dieselben Netzregeln (Kapitel 25)
  • Eigene Paketquelle (immer an): eigene .deb-Pakete per Drag & Drop, festgehaltene Stände mit Zurückdrehen und reproduzierbaren Images, und das Paket ota-selkies, das den Streaming-Server in Images bringt und Root-Arbeitsplätze beim Fortsetzen anhebt. Ein Spiegel von Debian 13 samt Sicherheitsupdates und Dockers Quelle ist ein Zusatz (ab Werk aus): Arbeitsplätze installieren dann auch, wenn draussen etwas fehlt — Betriebsart „Spiegel zuerst" oder „Nur Spiegel". Abgeglichen wird von Hand; das Alter des Spiegels wird gelb und rot (Kapitel 26)

Verwaltung

  • Ressourcen je Nutzer und Workspace: Nutzer A bekommt 2 Kerne, Nutzer B einen
  • Kontingent je Zuhause und eine Untergrenze für den freien Plattenplatz — eine verständliche Ablehnung statt eines Containers, der mitten in der Arbeit beim Schreiben stehenbleibt. Speicherabbilder abgestürzter Programme (core.<pid>) füllen kein Zuhause mehr: Neue Container erzeugen keine, und was in bestehenden noch entsteht, räumt OTA vor jeder Messung und täglich weg (Kapitel 11)
  • Sichtbarkeit je Anwendung und Gruppe, für den Fall, dass eine Lizenz nicht für alle reicht
  • Zwei-Faktor je Gruppe erzwingbar, wahlweise mit Passkey (Fingerabdruck, Gesicht, Sicherheitsschlüssel) oder Einmalkennwort — angeboten, nicht verlangt; /healthz und /metrics für die Überwachung
  • Nutzer, Gruppen und Rechte; Administratoren sind in ihrem eigenen Container root
  • Betrieb → Arbeitsplätze: alle Arbeitsplätze aller Nutzer, laufend und angehalten, mit Platz und „zuletzt aktiv"; Starten, Anhalten, Löschen — und ein root-Webterminal im Browser, dessen Eingaben je Arbeitsplatz protokolliert und exportierbar sind
  • Zentrale Anmeldung über Keycloak, mitgeliefert im Stack — oder ein vorhandenes anbinden. OTA ist dessen Verwalter: Konten, Gruppen und die AD-Anbindung richtet man in OTAs Oberfläche ein, die Keycloak-Konsole bleibt für den Alltag zu — erreichbar ist sie trotzdem (/auth/admin/), und das mit Absicht: Wer sie zumauert, sperrt sich im Fehlerfall selbst aus. Was der Betrieb dafür schuldet, steht in Kapitel 18
  • Ein Eingang für alle: Ein Konto, das in OTA angelegt wird, entsteht in Keycloak — mit dem Passwort, das die Verwaltung vergibt. Passwort und zweiter Faktor ändert jeder unter Mein Konto, das dafür zu Keycloak führt (bei Verzeichniskonten: im Verzeichnis). Alte lokale Konten ziehen beim nächsten Anmelden mit ihrem Passwort um; lokal bleibt nur das Notfallkonto
  • Notzugang unter /notfall: ein lokales Konto, das ohne Keycloak funktioniert. Ohne ihn wäre eine Anlage nach einer kaputten Anmeldekonfiguration nicht mehr zu betreten. Davor eine Bremse (zehn Versuche je Minute und Absender), und die Sperre nach Fehlversuchen gilt je (Konto, Absender) — sie lässt sich damit nicht gegen einen Kollegen richten
  • Ein Verzeichniseintrag kann kein bestehendes Konto von selbst übernehmen, auch nicht mit demselben Namen. Die Verwaltung kann aber ein Domänenkonto einem bestehenden Konto zuordnen: Danach meldet sich das alte Konto über das Verzeichnis an und behält Container, Zuhause und Ablagen.
  • OTA-Git (Forgejo), im Menü ein- und ausschaltbar:
    • Läuft unter einer eigenen Subdomain hinter demselben Eingang wie OTA.
    • Anmeldung mit den OTA-Zugangsdaten über Keycloak, nur für berechtigte Gruppen.
    • OTA legt die Konten an, sperrt statt zu löschen. OTA-Admins sind auch dort Admins.
    • Arbeitsplätze und Root-Arbeitsplätze bekommen Git vorkonfiguriert: Token je Arbeitsplatz, widerrufen bei Ende; SSH nur im Containernetz. Andere Git-Server bleiben unberührt.
    • Siehe Kapitel 27.
  • IDE-AI, unter Einstellungen ein- und ausschaltbar: ein KI-Assistent (Continue) in VS Code, VSCodium und PyCharm, verbunden mit dem eigenen KI-Server (vLLM, llama.cpp oder jeder andere mit OpenAI-Schnittstelle):
    • Modelle als Auswahlliste, je Modell Kontextfenster und Antwortlänge; dazu Werkzeuge (Agent-Modus), Temperatur, Zeitlimit, Regeln und berechtigte Gruppen.
    • Die Pakete kommen aus der eigenen Paketquelle in jeden Arbeitsplatz mit passendem Editor, auch in Ubuntu- und Kasm-Images; Root-Arbeitsplätze heben sich beim Fortsetzen an.
    • Die Firewall gibt den KI-Server frei, solange IDE-AI an ist. Telemetrie von Continue aus.
    • Siehe Kapitel 28.
  • Softwaremonitoring: Stündlich wird erfasst, welche Anwendungen in jedem laufenden Arbeitsplatz installiert sind (apt, Startmenü, Flatpak, Snap), ohne Versionen. Dazu gibt es eine Whitelist, befüllbar aus dem aktuellen Stand. Was nicht darauf steht, sehen Administratoren auf dem Dashboard und geben es per Knopfdruck frei. Wird es deinstalliert, verschwindet die Meldung von selbst
  • Fremde Web-Anwendungen im Katalog (Open WebUI, Grafana, …): OTA legt den OIDC-Zugang an und entscheidet, wer die Kachel sieht und wer sich dort anmelden darf. Keycloak prüft das selbst, auch beim direkten Aufruf der Adresse; ohne Gruppe kommt niemand hinein. Die OTA-Gruppen stehen im Token, sodass die Anwendung daran etwas hängen kann, etwa einen MCP-Server in Open WebUI. Was jemand darin darf, entscheidet die Anwendung. OTA baut ihr Rechtemodell nicht nach
  • Gruppen aus dem Verzeichnis (OpenLDAP oder AD): Eine OTA-Gruppe bezieht ihre Mitglieder aus LDAP-Gruppen, zusätzlich zu den von Hand Aufgenommenen. Das gilt sofort bei der Anmeldung und per Abgleich im einstellbaren Takt (15 Min. bis 1 Tag) oder auf Knopfdruck. Wer im Verzeichnis fehlt, wird deaktiviert, nie gelöscht. Konten ohne Gruppe sehen ein leeres Dashboard mit Hinweis
  • Anmeldefrist einstellbar (30 min bis 48 h), rollend — wer arbeitet, wird nicht abgemeldet
  • Oberfläche auf Deutsch und Englisch, umschaltbar oben rechts; eine Standardsprache für alle, die nicht selbst umschalten (Einstellungen → Sprache), die auch für die Anmeldeseite von Keycloak und das Handbuch gilt. Dunkel oder hell (oder wie der Rechner) — beides umschaltbar auch vor der Anmeldung
  • Das Handbuch liegt im Programm, auf Deutsch und Englisch, gefiltert nach Rechten
  • Mein Konto für jeden: Passwort selbst ändern, Zwei-Faktor mit Rückfallcodes einrichten

Das Netz der Arbeitsplätze

  • Ein eigenes Netz je Arbeitsplatz, alle enden in einem Router. Kein Firmennetz, keine Nachbarsitzung, kein Wirt — und das nicht, weil eine Regel es verbietet, sondern weil es keinen anderen Weg gibt: Die Netze sind internal, Docker richtet dort weder NAT noch Standardroute ein
  • Der Grundregelsatz steht sichtbar in der Oberfläche — was OTA für sich selbst öffnet (TURN, die eigene Adresse, Namensdienst, Proxy, Zeitserver), je Zeile mit Ziel, Ports, Grund und Herkunft. Abgeleitet aus der .env, deshalb zu sehen und nicht zu ändern
  • Profile je Vorlage in drei Stufen: abgeschottet (nur was OTA braucht), internet (die Vorgabe) und aus — letzteres hebt alles auf, verlangt eine Begründung und steht im Protokoll
  • Freigaben nach Adresse, Bereich oder Name, global für alle oder je Profil. Namen funktionieren, weil der Router zugleich der Namensdienst ist: Was er beantwortet, trägt er selbst in die Regel ein — Freigabe und Verbindung stammen aus derselben Auskunft
  • „+ NAT": einen Port eines Arbeitsplatzes zeitlich begrenzt über den Wirt veröffentlichen, für den, der seine eigene Anwendung vorführen will. Der Ablauf wird durchgesetzt, nicht nur angezeigt
  • Feste Adressen je Mensch und Vorlage — auch nach Feierabend und nach einem Neustart. Ohne das liesse sich eine vorgelagerte Firewall nicht auf einen Arbeitsplatz einstellen
  • Übersicht mit Durchsatz und verworfenen Paketen je Arbeitsplatz. Ein Portscan sieht in dieser Zahl genau so aus, wie er ist

Der Netz-Bildschirm: Grundregelsatz, wer gerade unter welcher Adresse arbeitet, Portfreigaben und globale Freigaben

Was ohne Zutun gilt, steht ausgeklappt da — mit Grund und Herkunft je Zeile. Darunter, wer gerade wo arbeitet, mit Durchsatz und verworfenen Paketen, und der Knopf für eine befristete Portfreigabe.

Wo ein Paket endet, und warum

Ein Paket geht ins Internet durch, eines ins Firmennetz wird im Router verworfen, und zum Nachbarn führt gar keine Leitung

Die Vorgabe. Die dritte Zeile ist die wichtigste: Zum Nachbarn wird nichts verworfen — dorthin führt gar keine Leitung. Eine Regel kann falsch sein, ein fehlender Weg nicht.

Von aussen kommt ein Paket auf den Wirt und wird über den Router in den Arbeitsplatz weitergeleitet; ein nicht freigegebener Port wird verworfen

Und die Gegenrichtung: Eine Portfreigabe („+ NAT") ist der einzige Weg von aussen hinein — über einen benannten Port, und nur solange die Frist läuft. Die übrigen drei Diagramme — abgeschottet, aus und eine Freigabe nach Namen — stehen in Handbuch, Kapitel 23. Erzeugt werden sie von scripts/netzfluss.py; wer Bewegung abgestellt hat, sieht dasselbe Bild mit stehenden Paketen.

Software in die Arbeitsplätze bringen

  • Pakete anklicken, Image bauen, Fassung aktivieren — mit Protokoll und Rückrollen
  • Pakete werden vorher geprüft: kennt das Image den Namen, und taugt er überhaupt? (Ubuntus firefox ist nur ein Verweis auf ein Snap und im Container nutzlos)
  • Rezepte für alles, was kein einfaches Paket ist — mit geführtem Bauer für eigene
  • Anwendungen im Image finden: OTA liest die .desktop-Dateien und schlägt Name, Zeichen und Startbefehl vor. Niemand muss wissen, wo eine Binärdatei liegt
  • Das echte Symbol aus dem Paket — den Fuchs, nicht einen Kreis. OTA sucht es dort, wo die Freedesktop-Spezifikation es hinlegt, rechnet es auf 128 Pixel herunter (VSCodium liefert 428 KB) und liefert es zwischenspeicherbar aus
  • Drei Ablagen: die gemeinsame für Dateien, die in jeden Arbeitsplatz sollen (im Container nur lesbar), eine eigene je Nutzer unter /mnt/austausch — beschreibbar, der Weg hinein und wieder heraus —, und ein Laufwerk je Gruppe unter /mnt/gruppen/<name>: dieselben Dateien für ein Team. Alle drei auch in der Kontrollleiste einer laufenden Session, mit Ziehen und Ablegen. Über ein Gruppenlaufwerk entscheidet die Mitgliedschaft und sonst nichts — auch ein Administrator kommt nur an die Gruppen, in denen er selbst ist
  • Skeleton-Profil je Workspace: womit ein Zuhause anfängt. Einzelne Pfade auf Wunsch bei jedem Start durchgesetzt — die Ausnahme, nicht die Regel. Dazu je Anwendung ein eigener Teilbaum, der erst kommt, wenn diese Anwendung zum ersten Mal startet: Wer nur das Terminal benutzt, braucht die Einstellungen der Entwicklungsumgebung nicht in seinem Zuhause
  • Session einfrieren: im eigenen Arbeitsplatz einrichten, Vorschau ansehen, als neue Fassung übernehmen. Das Home bleibt draussen, Geheimnisse werden markiert, die sudo-Ausnahme entfernt
  • Skript beim Sessionstart je Workspace, für alles, was ins Home gehört, aber nicht ins Image
  • Einmal-Skripte je Workspace: laufen je Nutzer genau einmal — für eine Umstellung im Zuhause, die das Skeleton nicht mehr erreicht und die das Startskript sonst bei jedem Start wiederholte

Betrieb

  • Eigenes Basisimage ota/base-desktop: Debian 13 + XFCE + Selkies, ohne Anwendung und ohne fremde Streaming-Software — H.264 über WebSockets statt rechteckiger Ausschnitte über RFB. Selkies 2.0 liegt als Fork in OTAs Repository (third_party/selkies/), damit ein Wechsel oben — wie das Verschwinden von 1.6.2 am 2026-10-07 — den Bau nicht mehr bricht. Das Konto heisst ota und wohnt unter /home/ota; kein Bestandteil trägt „Kasm" im Namen. scripts/build-desktop-image.sh --pruefen misst 18 Punkte gegen den Vertrag mit dem Agent
  • Der ältere Weg bleibt — ota/base-xfce (Ubuntu + KasmVNC) für Images von Kasm, die kein Selkies mitbringen. Umschaltbar je Arbeitsplatz unter Streaming; scripts/build-base-image.sh --pruefen prüft ihn weiterhin
  • Firmenproxy durchgängig: beim Bauen der Dienste, beim Bauen der Images, in jeder Sitzung und in API und Agent. Auch dort, wo keine Umgebungsvariable hinreicht — apt bekommt seine eigene Konfiguration. Umschaltbar im laufenden Betrieb, ohne Neuaufsetzen; ohne Proxy ist nichts zu tun
  • Stückliste je Image (make sbom) in SPDX und CycloneDX — gebraucht, sobald ein Image das Haus verlässt
  • Eigene Registry im Stack; fehlt ein Image lokal, wird es beim Start von dort geholt
  • Sicherung und Wiederherstellung von Profil, Container und Datenbank, manuell und nach Plan
  • HTTPS ab Werk, eigene kleine CA, austauschbar oder hinter einem Reverse Proxy
  • Kein Nachladen von fremden Hosts. Schriften liegen bei, die Oberfläche fordert nichts aus dem Internet an — sie sieht offline und hinter einem Firmenproxy gleich aus, und die IP-Adresse keines Nutzers verlässt das Haus
  • Nichts wächst ohne Grenze. Container-Protokolle sind auf 10 MB × 3 gedeckelt — für die Dienste und für jeden Arbeitsplatz. Im Protokoll der Datenbank verfallen Verhaltensdaten (Anmeldungen, Sitzungen, App-Starts) nach 90 Tagen, Verwaltungsvorgänge nach 365; aufgeräumt wird täglich, und das Aufräumen steht selbst im Protokoll
  • Läuft neben einer bestehenden Kasm-Installation auf demselben Host, ohne dort etwas umzustellen

Ein Blick in die Verwaltung

Die Workspace-Liste mit Kapazitätsanzeige: freier Arbeitsspeicher, Zusage je Session, Kerne Der Editor einer Vorlage mit Anzeigename, Image, Betriebsart, Streaming und Netz
Was der Host trägt. Die zweite Anzeige steht rot: Liefen alle vier Vorlagen gleichzeitig, wäre die Maschine überbucht. Das sieht man vorher, nicht im OOM-Kill. Die Vorlage. Ein Bildschirm je Arbeitsplatz — Image, Betriebsart, Streaming-Weg und was er im Netz darf.
Der Software-Reiter: Pakete und Rezepte auswählen, Image bauen Die Image-Liste des Hosts, gefiltert nach Herkunft
Software hinein. Pakete anklicken oder ein Rezept wählen, dann bauen; das Protokoll läuft live mit. Darunter der kurze Weg: eine Session einfrieren. Die Images. Was auf dem Host liegt, woher es kam, wer es benutzt — und was sich gefahrlos entfernen lässt.
Die Sicherung: was gesichert wird, Zeitplan und Aufbewahrung Das Handbuch im Programm, gefiltert nach Rechten
Die Sicherung. Profile, Container, Datenbank und Inhalte — von Hand oder nach Plan, mit Aufbewahrungsregel. Das Handbuch liegt im Programm. Welche Kapitel jemand sieht, entscheiden seine Rechte.

Die Bilder entstehen mit make bilder — tests/screenshots.mjs meldet sich an, fährt die Bildschirme ab und räumt hinterher auf. Zwei Dinge tut es dabei nicht: sich auf eine fremde Sitzung schalten (es startet eine eigene) und Bildschirme mit Personenlisten aufnehmen.

Wie es gebaut ist

Der Aufbau: Browser über Traefik zu Oberfläche und API, der Agent als einziger mit Docker-Socket, je Sitzung ein eigenes Netz, alle enden im Router

Zwei Aussagen stecken in diesem Bild, und beide sind die Antwort auf einen Befund:

Nur agent fasst Docker an. Die API verarbeitet Nutzereingaben und bekommt den Socket deshalb nicht — dieselbe Trennung gilt für das Dateisystem des Hosts.

Verzeichnis Inhalt
web/ Oberfläche (React, TypeScript, handgeschriebenes CSS, keine UI-Bibliothek)
api/ REST-API, Anmeldung, Rechte, Sessions (FastAPI, PostgreSQL)
agent/ Einziger Dienst mit Docker-Zugriff; Container, Displays, Images, Ablage
deploy/ Compose-Stack, Traefik, Registry, Zertifikate
firewall/ Der Router aller Arbeitsplätze: nftables, NAT, Namensdienst, Portfreigaben
extension/ Firefox-Erweiterung für die Zwischenablage
docs/wiki/ Handbuch — wird im Programm als Hilfe ausgeliefert; Englisch in docs/wiki/en/
images/ Eigene Basisimages: base-desktop (Debian + XFCE + Selkies, Vorgabe) und base-xfce (Ubuntu + KasmVNC)
tests/, scripts/ Prüfungen, Zertifikat, Migration aus Kasm, Stückliste

Dokumentation

  • Handbuch — Bedienung, Verwaltung, Betrieb, Fehlersuche (28 Kapitel; English)
  • plan.md — Architektur und die Begründungen dahinter, samt der Sackgassen
  • docs/adr/ — Entscheidungen, die teuer rückgängig zu machen sind, mit den Alternativen, die nicht getragen hätten
  • roadmap.md — Umsetzungsstand, ehrlich
  • dsgvo.md — welche personenbezogenen Daten wo liegen, wie lange, wer sie sieht — und die vier Stellen, an denen heute etwas fehlt
  • firewall.md — die Netzabsicherung der Arbeitsplätze: ein Netz je Sitzung, ein Router davor, Profile und Portfreigaben in der Oberfläche — samt der vier Dinge, die beim Bauen anders kamen als geplant

Ein Hinweis zur Fehlersuche: Kapitel 12 beschreibt echte Fehler aus dem Betrieb mit Symptom, Ursache und Reparatur — darunter mehrere, die tagelang nach etwas anderem aussahen, als sie waren.

Prüfungen

make test

452 Prüfungen in sieben Suiten, jede stellt ihren Vorzustand selbst her:

Suite Prüft
test-authz.sh Ein normaler Nutzer kann beweisbar nichts Administratives tun und an keinem fremden Bildschirm sitzen; dazu Container-Härtung, Kennzahlen, Kontingente und zweiter Faktor
test-ressourcen.sh Geänderte CPU und RAM erreichen vorhandene Container, laufend und pausiert; RAM unter dem Verbrauch wartet bis zum nächsten Start, die CPU nicht
test-software.sh Softwaremonitoring: Die Erfassung liest einen laufenden Arbeitsplatz (Debian-Pakete wie apt-mark showmanual, Starteinträge systemweit und im Zuhause, versteckte nicht); nach dem Befüllen der Whitelist keine Meldung; eine neue Anwendung wird mit Nutzer gemeldet; deinstalliert oder freigegeben verschwindet die Meldung, entzogen kommt sie wieder
test-git.sh OTA-Git: Ein- und Ausschalten (Daten bleiben), Subdomain hinter Traefik mit OTA-CA, OTA unberührt, Eingänge für Arbeitsplätze nicht auf dem Host; Anmeldung über Keycloak nur für berechtigte Gruppen, vorab angelegtes Konto wird verknüpft, Admins sind Admins, gesperrt statt gelöscht; im Arbeitsplatz clone/push ohne Passwort, eigene .gitconfig gewinnt, fremder Server bekommt nichts, SSH über 2222, Token widerrufen bei Ende, Root-Arbeitsplatz mit neuem Token nach dem Fortsetzen; andere Subdomain; nginx als Reverse Proxy; Sicherung mit Repositories
test-ide-ai.sh IDE-AI: Modelle vom KI-Server abrufen, falsche Eingaben abgewiesen, API-Schlüssel bleibt auf dem Server, Testfrage; Pakete in der Paketquelle, nur der Agent bekommt die Datei; in jeder Vorlage mit Editor config.yaml mit Marke, Kontextfenster und Antwortlänge, Telemetrie aus, Continue in VS Code/VSCodium und JetBrains verknüpft, Firewall lässt den KI-Server durch, Frage aus dem Arbeitsplatz bekommt Antwort, eigene config.yaml bleibt; ausgeschaltet ist alles wieder weg
test-konten.sh Konten der zentralen Anmeldung: Anlegen in OTA legt in Keycloak an (samt Pflicht zum Passwortwechsel), lokal kommt es nicht herein; Ändern, Passwort, zweiter Faktor, Sperren und Löschen wirken in Keycloak; Mein Konto führt über Keycloak; ein lokales Bestandskonto zieht mit seinem Passwort um, das Notfallkonto nicht
test-clipboard-bridge.sh Kopieren zwischen zwei Anwendungen im selben Arbeitsplatz: beide Richtungen, Umlaute, ein Bild, ein Megabyte, nach Pause, und abgeschaltet
test-i18n (scripts/i18n-pruefen.py) Zweisprachigkeit: Jeder Text der Oberfläche und jede Meldung des Servers hat einen englischen Eintrag, kein deutscher Text geht an der Übersetzung vorbei, und jedes Kapitel des Handbuchs gibt es auf Englisch mit demselben Aufbau
tests/e2e.mjs Die Oberfläche in einem echten Browser — bis zur Frage, ob der Stream wirklich verbindet
test-verzeichnis.sh Gruppen aus dem Verzeichnis: beide LDAP-Gruppenformen werden erkannt, eine gekoppelte OTA-Gruppe bekommt ihre Mitglieder bei der Anmeldung und beim Abgleich, von Hand Aufgenommene bleiben, eine LDAP-Gruppe „admins“ macht niemanden zum Admin, Konten ohne Gruppe bekommen den Hinweis; eine Anwendung lässt nur ihre Gruppen herein, über das Formular und die SSO-Sitzung, ohne Gruppe niemanden; wer im Verzeichnis fehlt, wird deaktiviert
test-verknuepfen.sh Domänenkonto einem bestehenden Konto zuordnen: Danach gibt es nur das alte Konto, mit derselben Kennung, Sitzung und demselben Zuhause, unter dem Namen aus dem Verzeichnis; das Konto aus der ersten Domänenanmeldung ist weg; das alte Passwort und das alte Keycloak-Konto gelten nicht mehr; das Notfallkonto lässt sich nicht verknüpfen; für Konten aus Keycloak und für lokale Bestandskonten
test-ldap.sh Verzeichnis-Anbindung über Keycloak gegen ein echtes OpenLDAP im Container — vor allem, dass ein Verzeichniseintrag kein lokales Konto übernimmt und ein Ausfall den Notzugang nicht mitreisst
test-streaming.sh Der Medienweg mit einer eigenen Vorlage auf dem aktuellen Basisimage: Ein Browser in einem fremden Netz zählt die dekodierten Bilder — auf dem Arbeitsplatz und auf dem Bildschirm einer Anwendung (Selkies 2.0, WebSockets durch Traefik); Umlaute, Shift und AltGr einer deutschen Tastatur kommen richtig an. Ist TURN eingerichtet (nur noch für Golden Images mit Selkies 1.6.2), wird er mitgeprüft, samt Sperrliste und NAT
test-firewall.sh Die Netzabsicherung, von innen gemessen: Nachbar, Wirt, Firmennetz, TURN, Namensdienst, Internet je Stufe, Freigabe nach Namen, Portfreigabe — und alles noch einmal nach einem Neustart des Routers
test-root-arbeitsplatz.sh Der Root-Arbeitsplatz: root auf dem Host unprivilegiert (Sysbox), Docker und Compose im Container, Netzregeln auch für die inneren Container, Anhalten und Fortsetzen mit erhaltenem Stand, Neu aufsetzen, Webterminal samt Protokoll und Export, kein Terminal in Dienste des Stacks, Golden Images mit USER 1000. Ohne Sysbox übersprungen
test-repo.sh Die eigene Paketquelle: Signatur, Hochladen (doppelt 409, kaputt 422), Snapshots samt Schutz vor dem Löschen, ein Debian-13-Arbeitsplatz installiert ein eigenes Paket von hier, „Nur Spiegel" ruft deb.debian.org nicht mehr, der Bildbauer trägt ein und wieder aus, ein Ubuntu-Image bleibt unberührt, ein Root-Arbeitsplatz übernimmt beim Fortsetzen die aktuelle Einstellung. Die Spiegelteile nur mit Spiegel
test-selkies-paket.sh Das Paket ota-selkies: liegt in der Paketquelle, hebt einen Arbeitsplatz auf einem alten Golden Image mit Selkies 1.6.2 an, der Bildbauer hebt ein Golden Image an, ein Root-Arbeitsplatz hebt sich beim Fortsetzen an und behält, was darin installiert war
test-backup.sh Sicherung und Wiederherstellung von Profil, Container und Datenbank. Beendet dafür Sitzungen — nur die eigenen, und prüft das ausdrücklich nach

Die Zugangsdaten der Prüfung stehen in deploy/.env und nicht im Quelltext; die Reihen lesen sie von dort selbst, ein make davor ist also nicht nötig.

Daneben steht make messung — keine Prüfung, sondern eine Messung: Sie vergleicht die beiden Streaming-Wege unter derselben Last und sagt, was eine Sitzung an CPU, Bandbreite und Reaktionszeit kostet. Sie dauert eine Viertelstunde und braucht eine ruhige Maschine, deshalb läuft sie nicht bei make test mit. Ergebnisse und Auswertung: Kapitel 20.

Ein voller Lauf dauert rund eine halbe Stunde: Er startet Container, friert ein Image ein und misst im Browser nach. Jede Suite lässt sich einzeln starten (bash scripts/test-authz.sh).

Die Reihe für den Medienweg gibt es, weil die teuersten Fehler dieses Projekts genau dort lagen und keine andere sie gefunden hätte: ein TURN-Server hinter einer Docker-Bridge, der die falsche Absenderadresse verschickt; ein DTLS-Paket, das an einer kleinen MTU zerschellt. Beide sahen im Browser gleich aus — „Waiting for stream" — und standen in keinem Protokoll.

Lizenz

Apache-2.0 (siehe LICENSE). Gewählt, weil OTA Infrastruktur ist: keine Auflagen für den Betreiber, ausdrückliche Patentfreigabe, und dieselbe Wahl wie beim nächstverwandten offenen Projekt (Apache Guacamole).

OTA enthält keine fremde Software: Abhängigkeiten werden beim Bauen geholt, Bestandteile zur Laufzeit als Container-Images bezogen. Sie behalten ihre eigenen Lizenzen und werden von OTA nicht neu lizenziert — aufgeschlüsselt in drei Ebenen in THIRD-PARTY-NOTICES.md, knapp in NOTICE.

Ein Arbeitsplatz-Image ist ein zusammengesetztes Werk: OTA-Konfiguration, Selkies (MPL-2.0, von OTA verändert), libx264 (GPL-2.0+), XFCE, hunderte Distributionspakete, die installierten Anwendungen. Es ist nicht „Apache-2.0".

Das Basisimage darf weitergegeben werden — mit vier Pflichten: Quellen für die GPL-Teile anbieten, die fünf Selkies-Patches beilegen (MPL-2.0 wirkt dateiweise), Lizenztexte im Image lassen und eine Stückliste mitliefern. Fertige Arbeitsplätze mit Microsoft VS Code oder Google Chrome dagegen nicht — dieselben mit VSCodium und Firefox schon.

Für die Anwendungen in einem Golden Image gilt deren eigene Lizenz. Bei Microsoft VS Code ist der Betrieb im eigenen Unternehmensnetz ausdrücklich erlaubt, die Weitergabe an Dritte nicht. Mit Originalzitaten geprüft in Handbuch, Kapitel 13 — keine Rechtsberatung, aber am Lizenztext und nicht aus dem Gedächtnis.

„Kasm" ist eine Marke von Kasm Technologies. OTA ist kein Kasm-Produkt und steht in keiner Verbindung zu Kasm Technologies.

About

Selbstgehostete Arbeitsplätze im Browser - ein eigener Linux Docker-Container je Nutzer, Anwendungen einzeln gestreamt.

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages