ChatGPT Desktop Updates mit /tmp noexec unter Fedora
Einleitung
Ich benutze aktuell viel Codex (den Agenten von ChatGPT) und das natürlich auf meinem Fedora 44 System. Problem nur: Es gab bis vor einigen Wochen gar keinen Linux Client für Codex. Mittlerweile gibt es einen, aber wieder mal nur ein .deb ... so weit so obvious.
Glücklicherweise hat die Community das Problem mal wieder gelöst und einen Installer erstellt, der den Codex-Desktop auch auf andere Linux Distros bringt. Vor dem Linux Client haben sie das macOS .DMG Paket als Grundlage benutzt und daraus ein Linux Paket gebaut. Und mittlerweile bauen sie einfach das Linux Paket entsprechend um.
Und dann kommt Fedora :D
Auf Fedora 44 ist /tmp schon im Standard als eigenes tmpfs mit noexec eingehängt. Dafür muss man das System nicht einmal extra härten. Das ist eine sinnvolle Sicherheitsmaßnahme: Dateien dürfen dort abgelegt, aber nicht direkt als Programme gestartet werden. Das war beim bisherigen Aufbau mit macOS Wrapper ein echtes Problem.
Der Linux-Wrapper codex-desktop-linux hat seine Paketquelle inzwischen glücklicherweise geändert. Seit Version 0.11.x wird keine macOS-DMG mehr konvertiert. Stattdessen dient OpenAIs signiertes offizielles Linux-Paket als Quelle. noexec /tmp bleibt dennoch relevant, weil die strukturelle Prüfung des extrahierten ELF-Payloads Ausführbarkeit voraussetzt.
Dieser Artikel beschreibt zuerst die aktuelle, geprüfte Methodik mit automatischem exec-fähigem Build-Verzeichnis. Die frühere DMG-/Node.js-Lösung bleibt darunter als historischer Abschnitt erhalten, damit ältere Installationen und Logs weiterhin nachvollziehbar sind. /tmp bleibt bei allen beschriebenen Lösungen unverändert auf noexec (natürlich!).
Stand: 19. August 2026
Aktuelle Methodik: signiertes offizielles Linux-Paket
Der aktuelle Wrapper lädt die stabile APT-Metadatenkette von OpenAI und baut daraus weiterhin ein getrennt gekennzeichnetes Community-Paket. Paketname und Kommando bleiben codex-desktop, der Installationspfad bleibt /opt/codex-desktop, und der Desktop-Eintrag heißt ChatGPT Community.
Die Vertrauenskette ist dabei enger als bei der früheren DMG-Konvertierung:
InReleasedes stabilen OpenAI-APT-Repositorys wird mit dem fest hinterlegten Repository-Schlüssel geprüft.- Größe und SHA-256 der passenden
Packages-Datei werden validiert. - Es wird genau ein Paket
chatgptfüramd64oderarm64ausgewählt; Paketname, Architektur, Metadaten, Größe und SHA-256 werden geprüft. dpkg-deb -xextrahiert ausschließlich den Datenpayload. Upstream-Maintainer-Skripte werden nicht ausgeführt.- Ohne aktivierte Linux-Zusatzfeatures bleibt
resources/app.asarbytegleich zum offiziellen Paket.
Neue Ausprägung des noexec-Problems
Der aktuelle Installer legte sein Arbeitsverzeichnis zunächst weiterhin mit mktemp -d unter /tmp an. Der offizielle ELF-Payload wird beim Paketbau nicht gestartet. Die strukturelle Prüfung verwendet jedoch test -x für /usr/lib/chatgpt/ChatGPT und weitere ausführbare Dateien. Auf einem noexec-Mount liefert diese Prüfung trotz Dateimodus 0755 den Wert false; ein direkter Start endet mit Status 126.
Dadurch erschien die irreführende Meldung:
Official package payload is missing executable /usr/lib/chatgpt/ChatGPT
Eine identische Testdatei lieferte unter ~/.cache/codex-desktop/build-tmp dagegen test -x=true und Exit-Status 0. Also ist wiedermal nicht der offizielle Payload defekt, sondern nur sein temporärer Ausführungskontext ungeeignet.
Automatischer exec-fähiger Build-Pfad
Der portierte Fix prüft den tatsächlichen Ausführungskontext mit einer kurzlebigen Probe und entscheidet direkt in install.sh:
- Ein brauchbares geerbtes
TMPDIRwird unverändert verwendet. CODEX_BUILD_TMPDIRkann einen anderen Pfad explizit vorgeben; ein unbrauchbarer Override bricht früh mit einer klaren Meldung ab.- Bei
noexecwird automatisch${XDG_CACHE_HOME:-$HOME/.cache}/codex-desktop/build-tmpangelegt und mit Modus0700verwendet. - Das gewählte
TMPDIRwird an alle Kindprozesse exportiert und das eigentliche Arbeitsverzeichnis gezielt dort erzeugt.
Der Fix ist Bestandteil des minimalen Update-Builders im RPM. Damit verwenden manuelle Builds, der dauerhaft laufende Update-Daemon und direkte Aufrufe von codex-update-manager check-now dieselbe Logik. Der frühere systemd-User-Drop-in ist nicht mehr erforderlich.
Geprüfter Migrationsstand vom 19. August 2026
Komponente | Geprüfter Wert |
|---|---|
Wrapper |
|
lokaler |
|
offizielles Linux-Paket |
|
offizieller Paket-Hash |
|
| keine Features aktiv; SHA-256 |
installiertes RPM |
|
RPM-Hash |
|
Bestanden haben 39 Trust-/Patch-/Feature-Basistests, 51 von 51 Smoke-Tests, ein vollständiger Build mit absichtlich gesetztem TMPDIR=/tmp, die Runtime-Diagnose, die RPM-Digestprüfung und rpm -V. Anschließend hat der neue Update-Manager selbst die signierten Metadaten geladen, den offiziellen Payload geprüft, ein weiteres RPM gebaut und dieses installiert. Ein zweiter Check ohne TMPDIR und ohne systemd-Drop-in endete mit idle, ohne Kandidat und ohne Fehler.
Den bisherige Drop-in habe ich als 10-executable-build-tmp.conf.retired-20260819 recoverbar aufbewahrt. Der Dienst läuft nach daemon-reload ohne diesen Override. Vor der Migration wurde außerdem das letzte funktionierende RPM mit SHA-256 57202821e2b6aaa53ee1666e766a04d3f41c1c2f57ab4a2c952d0d5133d30de9 als Rückfallmöglichkeit gesichert.
Upstream-Nachweis und Code
Die kontrollierte Migration folgt Issue #1371. Die Reparaturen für die offiziellen Pakete 26.814.41407 und 26.814.41957 wurden mit PR #1373 und PR #1376 in main aufgenommen.
Die lokalen Anpassungen, Prüfdaten und der exakt erzeugte Patch liegen im privaten Forgejo-Repository codex-projekte/chatgpt-work-linux, Commit c49db7f. Besonders relevant sind die Migrationsdokumentation und der Patch für die offizielle Linux-Methode. Das Repository liegt auf meinem privaten git Server (Anleitung hier im Wiki).
Ausgangsumgebung der früheren DMG-Methode
Komponente | Verwendete Umgebung |
|---|---|
Betriebssystem | Fedora Linux 44 |
temporäres Dateisystem |
als
mit
|
Anwendung | ChatGPT Desktop über
|
Update-Dienst |
als systemd-Userdienst |
alternativer Build-Pfad |
|
Historisches Fehlerbild der DMG-Methode
Der Desktop meldet bei einem gefundenen Update zunächst, dass aus dem neuen Upstream-DMG ein lokales Linux-Paket vorbereitet wird. Kurz darauf folgt nur noch:
The local rebuild failed: install.sh failed during local rebuild
Im eigentlichen Build-Log steht beispielsweise:
Managed Node.js runtime copied from /opt/codex-desktop/update-builder/node-runtime
Managed Node.js runtime is missing node: /tmp/tmp.86Yk8CgVpa/node-runtime/bin/node
Das Log des Update-Dienstes liegt normalerweise hier:
tail -n 100 ~/.local/state/codex-update-manager/service.log
Die Logs des einzelnen Build-Versuchs liegen unter:
~/.cache/codex-update-manager/workspaces/<VERSION>/logs/install.log
Historische technische Ursache
Der Installer erzeugt sein Arbeitsverzeichnis ursprünglich mit einem einfachen:
WORK_DIR="$(mktemp -d)"
Ist kein TMPDIR gesetzt, verwendet mktemp den systemweiten Pfad /tmp. Der Installer kopiert anschließend den mitgelieferten Node.js-Runtime nach ${WORK_DIR}/node-runtime und startet ihn während des Builds mehrfach.
Die normalen Unix-Dateirechte können dabei korrekt aussehen. Das noexec-Flag des Mounts verhindert die Ausführung trotzdem. Die folgende Meldung spricht deshalb von einer fehlenden Node-Datei, obwohl eigentlich der Mount die Ausführung verweigert.
Der in der Projektdokumentation beschriebene manuelle Export von TMPDIR hilft für einen Build in derselben Shell. Ein systemd-Userdienst erbt diese interaktive Shell-Umgebung jedoch nicht zuverlässig. Zusätzlich startet der Paket-Launcher die Prüfung über eine transiente Unit namens codex-update-manager-launch-check.service. Die In-App-Update-Bridge kann codex-update-manager check-now sogar direkt aus dem Desktop-Prozess aufrufen. Ein Drop-in ausschließlich für codex-update-manager.service erreicht diese beiden Pfade nicht.
Historische Updater-Pfade
In der aktuellen Paketarchitektur sind mindestens drei Ausführungskontexte relevant:
- der dauerhafte Userdienst
codex-update-manager.service, - die beim Programmstart mit
systemd-run --usererzeugte Unitcodex-update-manager-launch-check.service, - der direkte Aufruf durch die In-App-Update-Bridge.
Die beiden letzten Varianten erklären, warum der Fehler nach einem Neustart trotz korrekt geladenem Service-Drop-in erneut auftreten kann. Die robuste Lösung muss daher im Installer selbst liegen: Er prüft sein Arbeitsverzeichnis vor jedem Build auf tatsächliche Ausführbarkeit und wählt bei Bedarf selbstständig den privaten Cache-Pfad.
Historischen Mount- und Dienststand prüfen
Die Mount-Optionen von /tmp zeigt findmnt übersichtlich an:
findmnt -T /tmp -no TARGET,FSTYPE,OPTIONS
Eine passende Ausgabe sieht beispielsweise so aus:
/tmp tmpfs rw,nosuid,nodev,noexec,seclabel,size=...
Den aktuellen Zustand des Updaters zeigt:
systemctl --user show codex-update-manager.service \
-p ActiveState -p SubState -p Environment -p DropInPaths
Historischen Build-Pfad vorbereiten
Das alternative Verzeichnis liegt bewusst im benutzerspezifischen Cache und bekommt den Modus 0700:
BUILD_TMP="${XDG_CACHE_HOME:-$HOME/.cache}/codex-desktop/build-tmp"
install -d -m 0700 "$BUILD_TMP"
stat -c '%A %a %U:%G %n' "$BUILD_TMP"
Erwartet wird eine Ausgabe mit drwx------ beziehungsweise Modus 700.
Vor der weiteren Einrichtung kann man zusätzlich prüfen, ob auf diesem Dateisystem wirklich Programme gestartet werden dürfen:
probe="$(mktemp "$BUILD_TMP/.codex-exec-probe.XXXXXX")"
printf '%s\n' '#!/bin/sh' 'exit 0' > "$probe"
chmod 0700 "$probe"
"$probe" && echo 'Ausführung möglich'
rm -f "$probe"
Historischer systemd-Teil-Workaround
Das folgende Drop-in ist weiterhin als Diagnose und Teil-Workaround sinnvoll. Es gilt jedoch ausschließlich für den dauerhaft laufenden Updater-Dienst. Dazu sind keine Root-Rechte erforderlich:
BUILD_TMP="${XDG_CACHE_HOME:-$HOME/.cache}/codex-desktop/build-tmp"
DROPIN_DIR="$HOME/.config/systemd/user/codex-update-manager.service.d"
install -d -m 0700 "$BUILD_TMP"
mkdir -p "$DROPIN_DIR"
printf '[Service]\nEnvironment="TMPDIR=%s"\n' "$BUILD_TMP" \
> "$DROPIN_DIR/10-executable-build-tmp.conf"
chmod 0644 "$DROPIN_DIR/10-executable-build-tmp.conf"
systemctl --user daemon-reload
systemctl --user restart codex-update-manager.service
Der Dienst übernimmt die Einstellung bei jedem Start. Die transiente Startprüfung und direkte In-App-Aufrufe sind damit jedoch nicht abgedeckt. Der Drop-in allein ist daher kein vollständiger Auto-Update-Workaround.
Historische Lösung kontrollieren
Nach dem Neustart kann die neue Umgebung im dauerhaften Dienst sichtbar sein, ohne dass sie für alle Update-Prüfungen gilt:
systemctl --user show codex-update-manager.service \
-p ActiveState -p SubState -p Environment -p DropInPaths
Die Teilkonfiguration enthält unter anderem:
ActiveState=active
SubState=running
Environment=TMPDIR=/home/<BENUTZER>/.cache/codex-desktop/build-tmp
Bei einem Build, der tatsächlich in diesem Dienst läuft, liegt das kurzlebige Arbeitsverzeichnis dann beispielsweise hier:
~/.cache/codex-desktop/build-tmp/tmp.YxD9pAlGXz/
Bei einer späteren Kontrolle starteten neue Prüfungen dennoch wieder unter /tmp/tmp.…. Das Journal ordnete diese Läufe eindeutig der transienten Unit codex-update-manager-launch-check.service zu. Der dauerhafte Dienst besaß gleichzeitig das korrekte TMPDIR. Damit war die Begrenzung des Drop-ins reproduzierbar nachgewiesen.
systemctl --user show-environment | grep '^TMPDIR='
journalctl --user-unit codex-update-manager-launch-check.service -n 50 --no-pager
Fehlt TMPDIR in der systemd-Benutzerumgebung, startet die transiente Unit ohne diese Variable. Ein globales Setzen wäre möglich, wirkt aber auf sämtliche Userdienste und löst den direkten In-App-Pfad nicht zuverlässig. Deshalb bleibt der Installer-Fix die bevorzugte Lösung.
Historischen Workaround wieder entfernen
Das Drop-in lässt sich ohne Änderung an der Paketinstallation zurückbauen:
rm -f "$HOME/.config/systemd/user/codex-update-manager.service.d/10-executable-build-tmp.conf"
systemctl --user daemon-reload
systemctl --user try-restart codex-update-manager.service
Das Cache-Verzeichnis wird dabei absichtlich nicht automatisch gelöscht. Nach Abschluss aller Builds kann es bei Bedarf separat entfernt werden.
Warum /tmp nicht mit exec einhängen?
Ein Remount von /tmp mit exec würde zwar den Build ermöglichen, schwächt aber eine systemweite Sicherheitsvorgabe für eine lokale Anforderung einer einzelnen Anwendung ab. Der benutzerspezifische Pfad begrenzt die Änderung auf den Build-Prozess und behält die Fedora-Härtung bei.
Auch ein globaler TMPDIR-Export ist weiter gefasst als nötig. Das systemd-Drop-in wirkt nur auf den dauerhaften Update-Dienst und ist deshalb allein nicht ausreichend. Der angepasste Installer ist robuster, weil er unabhängig vom aufrufenden Prozess selbst einen sicheren Build-Pfad auswählt.
Historisches Skript und früherer Quellcode-Patch
Der idempotente Installer, ein Diagnose- und Rückbauskript sowie ein Patch für codex-desktop-linux liegen in meinem privaten Forgejo-Repository:
codex-projekte/chatgpt-work-linux
Das Repository ist derzeit privat. Der Artikel enthält deshalb alle für den Workaround notwendigen Schritte zusätzlich vollständig und eigenständig.
Der Quellcode-Patch erweitert den Installer um folgende Punkte:
- explizites
CODEX_BUILD_TMPDIRals Override, - tatsächliche Ausführungsprobe für ein geerbtes
TMPDIR, - automatischer Fallback auf
~/.cache/codex-desktop/build-tmp, - Verzeichnisrechte
0700, - Export von
TMPDIRan alle nachfolgenden Build-Werkzeuge, - verständliche Fehlermeldung, wenn kein ausführbares Arbeitsverzeichnis verfügbar ist.
Umsetzung der früheren DMG-Methode
Der korrigierte Quellstand wird erst durch den Paketwechsel im laufenden System wirksam. Das neue Paket ist inzwischen installiert; der mitgelieferte /opt/codex-desktop/update-builder/install.sh enthält die automatische Auswahl eines ausführbaren Build-Verzeichnisses.
- Erledigt: aktueller Upstream-Stand mit aktueller DMG und deaktivierten optionalen Features gebaut.
- Erledigt: Akzeptanzbericht, RPM-Digests und Update-Builder geprüft.
- Erledigt: neues RPM über dnf installiert.
- Erledigt: exakter transienter Launch-Check ohne TMPDIR mit Status 0/SUCCESS ausgeführt; dabei entstand kein neuer Workspace.
- Erledigt: Desktop-App vollständig neu gestartet; keine Fehlermeldung beim Start. Der automatische Launch-Check um 12:29 Uhr endete mit
Result=successundExecMainStatus=0.
rpm -qip ./dist/codex-desktop-*.rpm
rpm -qlp ./dist/codex-desktop-*.rpm | grep '/opt/codex-desktop/update-builder/install.sh'
# Die Anwendung vor diesem Schritt beenden:
sudo dnf install ./dist/codex-desktop-*.rpm
grep -n 'configure_executable_build_tmpdir' \
/opt/codex-desktop/update-builder/install.sh
Lokaler Stand vom 9. August 2026: Der vollständige Build gegen Upstream-DMG 26.803.41515 wurde ohne Integritätsfehler akzeptiert. Das RPM codex-desktop-2026.08.09.100802-f8a5a74a.noexec1.x86_64.rpm mit SHA-256 85c0eb42c0a76d1869d30cafa83d524dbfe3b55d439d72a0c4e4a10362ad181b ist installiert. Der Updater meldet idle ohne Fehler. Der exakte transiente Launch-Check lief ohne geerbtes TMPDIR erfolgreich und erzeugte keinen neuen Workspace. Nach dem vollständigen Neustart erschien keine Update-Fehlermeldung. Der automatische Launch-Check endete mit Result=success und ExecMainStatus=0; der Updater blieb idle und erzeugte keinen neuen Workspace. Damit ist der reguläre Autoupdate-Startpfad abschließend verifiziert.
Der lokale Paketwechsel ist der sofort verfügbare Weg. Parallel sollte der Fix upstream übernommen werden, damit künftige offizielle Pakete keinen zusätzlichen lokalen Patch benötigen.
No comments to display
No comments to display