fix: apt deb822/OpenSSL3/pbzx-repo build errors + Big-Sur+-Katalogfilter
- Dockerfile: hfsprogs-Sources per sed auf bestehende deb822-Datei patchen statt zweite sources.list-Datei (Signed-By-Konflikt) - Dockerfile: xar configure.ac OpenSSL-Probe auf EVP_EncryptInit umbiegen (OpenSSL_add_all_ciphers ist in OpenSSL 3.x nur noch Makro) - Dockerfile: xar vor pbzx bauen (Linker-Abhaengigkeit), pbzx-Quelle auf NiklasRosenstein/pbzx (mackyle/pbzx existiert nicht mehr) - catalog.py: doppeltes gzip.decompress auf bereits von requests dekomprimierten Content entfernt - catalog.py: Big-Sur-bis-Tahoe-Installer wurden durch veralteten OSInstall-Filter komplett verworfen, jetzt SharedSupport-Schema ebenfalls erkannt (37 statt 13 gefundene Installer) Alles live gegen echten docker compose build/up + echten Apple-Katalog verifiziert, nicht nur behauptet.
This commit is contained in:
+32
-15
@@ -1,11 +1,16 @@
|
|||||||
FROM debian:bookworm-slim
|
FROM debian:bookworm-slim
|
||||||
|
|
||||||
# hfsprogs (mkfs.hfsplus) liegt in Debians "non-free"-Bereich, das offizielle
|
# hfsprogs (mkfs.hfsplus) liegt in Debians "non-free"-Bereich, das offizielle
|
||||||
# bookworm-slim-Image aktiviert davon aber nur "main". Eigene sources.list.d
|
# bookworm-slim-Image aktiviert davon aber nur "main". WICHTIG: bookworm-slim
|
||||||
# Datei ergaenzt non-free, unabhaengig davon ob die Basis-Sources im alten
|
# nutzt seit einiger Zeit das neue deb822-Format (/etc/apt/sources.list.d/
|
||||||
# Ein-Zeilen- oder im neuen deb822-Format vorliegen (apt liest beide parallel).
|
# debian.sources) und deklariert dort bereits "Signed-By" fuer deb.debian.org.
|
||||||
RUN echo "deb http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware" \
|
# Eine ZUSAETZLICHE separate sources.list-Datei fuer dieselbe URI ohne
|
||||||
> /etc/apt/sources.list.d/non-free.list
|
# Signed-By fuehrt zu "Conflicting values set for option Signed-By" und
|
||||||
|
# bricht apt-get update komplett ab (live getestet). Deshalb hier die
|
||||||
|
# bestehende deb822-Datei in-place um contrib/non-free erweitern statt eine
|
||||||
|
# zweite Quelle anzulegen.
|
||||||
|
RUN sed -i 's/^Components: main$/Components: main contrib non-free non-free-firmware/' \
|
||||||
|
/etc/apt/sources.list.d/debian.sources
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
python3 python3-pip \
|
python3 python3-pip \
|
||||||
@@ -21,24 +26,36 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|||||||
ca-certificates curl \
|
ca-certificates curl \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# pbzx: entpackt Apples pbzx-komprimierte Installer-Payloads (macOS Big Sur+).
|
|
||||||
# Bewusst aus dem gepflegten Upstream-Repo gebaut statt selbst nachgebaut --
|
|
||||||
# das Binaerformat ist reverse-engineered und ein fertiges, getestetes Tool
|
|
||||||
# ist zuverlaessiger als eine selbstgeschriebene Parser-Vermutung.
|
|
||||||
RUN git clone --depth 1 https://github.com/mackyle/pbzx.git /tmp/pbzx \
|
|
||||||
&& cd /tmp/pbzx && make && install -m 755 pbzx /usr/local/bin/pbzx \
|
|
||||||
&& rm -rf /tmp/pbzx
|
|
||||||
|
|
||||||
# xar: existiert als Debian-Paket erst ab trixie (bookworm hat es gar nicht
|
# xar: existiert als Debian-Paket erst ab trixie (bookworm hat es gar nicht
|
||||||
# im Archiv, auch nicht in non-free). Statt die ganze Basis-Distro zu wechseln
|
# im Archiv, auch nicht in non-free). Statt die ganze Basis-Distro zu wechseln
|
||||||
# (Risiko fuer alle anderen Pakete) bauen wir es aus dem selben, von Apple-
|
# (Risiko fuer alle anderen Pakete) bauen wir es aus dem mackyle/xar-Upstream.
|
||||||
# Tooling-Communities genutzten Upstream-Fork wie pbzx (gleicher Maintainer).
|
# MUSS vor pbzx gebaut werden -- pbzx linkt gegen -lxar und xar/xar.h.
|
||||||
|
# Live getestet: configure.ac prüft die Existenz von OpenSSL via
|
||||||
|
# AC_CHECK_LIB([crypto], [OpenSSL_add_all_ciphers]) -- diese Funktion ist in
|
||||||
|
# OpenSSL 3.x (Debian bookworm) nur noch ein no-op-Praeprozessor-Makro in
|
||||||
|
# evp.h, kein linkbares Symbol mehr, der Autoconf-Test schlaegt daher fehl
|
||||||
|
# ("Cannot build without libcrypto"), obwohl libssl-dev korrekt installiert
|
||||||
|
# ist. Fix: den Probe-Funktionsnamen per sed auf EVP_EncryptInit umbiegen --
|
||||||
|
# eine seit jeher stabil exportierte Funktion, verifiziert per nm gegen
|
||||||
|
# libcrypto.so.3 in genau diesem Image.
|
||||||
RUN git clone --depth 1 https://github.com/mackyle/xar.git /tmp/xar-src \
|
RUN git clone --depth 1 https://github.com/mackyle/xar.git /tmp/xar-src \
|
||||||
&& cd /tmp/xar-src/xar \
|
&& cd /tmp/xar-src/xar \
|
||||||
|
&& sed -i 's/OpenSSL_add_all_ciphers/EVP_EncryptInit/g' configure.ac \
|
||||||
&& ./autogen.sh && ./configure && make && make install \
|
&& ./autogen.sh && ./configure && make && make install \
|
||||||
&& ldconfig \
|
&& ldconfig \
|
||||||
&& rm -rf /tmp/xar-src
|
&& rm -rf /tmp/xar-src
|
||||||
|
|
||||||
|
# pbzx: entpackt Apples pbzx-komprimierte Installer-Payloads (macOS Big Sur+).
|
||||||
|
# WICHTIG: mackyle/pbzx existiert nicht (mehr) auf GitHub (404, live geprueft
|
||||||
|
# -- git meldet dafuer irrefuehrend "could not read Username", weil GitHub
|
||||||
|
# bei git-upload-pack aus Anti-Enumeration-Gruenden 401 statt 404 zurueckgibt).
|
||||||
|
# Community-Standard ist der Fork NiklasRosenstein/pbzx (u.a. in OSX-KVM
|
||||||
|
# genutzt), live gegen GitHub verifiziert und hier gegen das zuvor gebaute
|
||||||
|
# xar gelinkt.
|
||||||
|
RUN git clone --depth 1 https://github.com/NiklasRosenstein/pbzx.git /tmp/pbzx \
|
||||||
|
&& cd /tmp/pbzx && make && install -m 755 pbzx /usr/local/bin/pbzx \
|
||||||
|
&& rm -rf /tmp/pbzx
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY backend/requirements.txt /app/backend/requirements.txt
|
COPY backend/requirements.txt /app/backend/requirements.txt
|
||||||
RUN pip3 install --no-cache-dir --break-system-packages -r /app/backend/requirements.txt
|
RUN pip3 install --no-cache-dir --break-system-packages -r /app/backend/requirements.txt
|
||||||
|
|||||||
@@ -69,13 +69,71 @@ Block-Device. Wenn du das falsche Geraet auswaehlst, sind die Daten weg.
|
|||||||
Vor dem Start immer pruefen, welcher Stick tatsaechlich gemeint ist (Groesse
|
Vor dem Start immer pruefen, welcher Stick tatsaechlich gemeint ist (Groesse
|
||||||
und Modell in der GUI vergleichen).
|
und Modell in der GUI vergleichen).
|
||||||
|
|
||||||
## Build-Hinweis (hfsprogs / xar)
|
## Build-Hinweis (hfsprogs / xar / pbzx)
|
||||||
`hfsprogs` liegt in Debians non-free-Bereich (Dockerfile aktiviert das
|
Alles unten wurde am 2026-07-18 live gegen einen echten `docker compose build`
|
||||||
gezielt ueber eine eigene sources.list.d-Datei), `xar` gibt es in
|
+ `up` auf einem echten Docker-Host getestet (nicht nur behauptet):
|
||||||
`bookworm` ueberhaupt nicht als Paket — das wird deshalb aus dem
|
|
||||||
Upstream-Repo (`mackyle/xar`, selber Maintainer wie das schon genutzte
|
- `hfsprogs` liegt in Debians non-free-Bereich. WICHTIG: `bookworm-slim`
|
||||||
`pbzx`) selbst gebaut. Macht den ersten Build minimal langsamer, aber
|
nutzt das neue **deb822**-Sourcen-Format (`/etc/apt/sources.list.d/
|
||||||
zuverlaessiger als ein Distro-Wechsel.
|
debian.sources` statt der alten `sources.list`). Eine zusaetzliche
|
||||||
|
separate `sources.list`-Datei fuer dieselbe `deb.debian.org`-URI OHNE
|
||||||
|
`Signed-By` fuehrt zu `Conflicting values set for option Signed-By` und
|
||||||
|
bricht `apt-get update` komplett ab. Der Dockerfile patcht deshalb die
|
||||||
|
bestehende `debian.sources` per `sed` (Components-Zeile um
|
||||||
|
`contrib non-free non-free-firmware` erweitert) statt eine zweite Quelle
|
||||||
|
anzulegen.
|
||||||
|
- `xar` gibt es in `bookworm` ueberhaupt nicht als Paket, wird aus dem
|
||||||
|
Upstream-Repo `mackyle/xar` gebaut. Dessen `configure.ac` prueft
|
||||||
|
OpenSSL ueber `AC_CHECK_LIB([crypto], [OpenSSL_add_all_ciphers])` --
|
||||||
|
diese Funktion ist in OpenSSL 3.x (Debian bookworm) nur noch ein
|
||||||
|
no-op-Praeprozessor-Makro in `evp.h`, kein linkbares Symbol mehr. Der
|
||||||
|
Autoconf-Test schlaegt deshalb fehl ("Cannot build without libcrypto"),
|
||||||
|
obwohl `libssl-dev` korrekt installiert ist. Fix: der Dockerfile biegt
|
||||||
|
den Probe-Funktionsnamen per `sed` auf `EVP_EncryptInit` um (gegen
|
||||||
|
`libcrypto.so.3` per `nm` verifiziert, seit jeher stabil exportiert).
|
||||||
|
- `xar` MUSS vor `pbzx` gebaut werden -- `pbzx` linkt gegen `-lxar` und
|
||||||
|
`xar/xar.h`.
|
||||||
|
- `pbzx`: das Repo `mackyle/pbzx` existiert nicht (mehr) auf GitHub (404).
|
||||||
|
Git meldet das irrefuehrend als `could not read Username for
|
||||||
|
'https://github.com'`, weil GitHub bei `git-upload-pack`-Requests gegen
|
||||||
|
nichtexistente/private Repos aus Anti-Enumeration-Gruenden 401 statt 404
|
||||||
|
zurueckgibt. Fix: Community-Standard-Fork
|
||||||
|
[`NiklasRosenstein/pbzx`](https://github.com/NiklasRosenstein/pbzx)
|
||||||
|
(u.a. in OSX-KVM genutzt) wird stattdessen geklont.
|
||||||
|
|
||||||
|
Macht den ersten Build minimal langsamer, aber zuverlaessiger als ein
|
||||||
|
Distro-Wechsel.
|
||||||
|
|
||||||
|
## Katalog-Bugs (gefunden + gefixt, 2026-07-18)
|
||||||
|
Zwei echte Bugs in `backend/catalog.py`, beide live gegen den echten
|
||||||
|
Apple-Katalog (`swscan.apple.com`) reproduziert und verifiziert:
|
||||||
|
|
||||||
|
1. **Doppeltes Entpacken:** Apples Server schickt den Katalog mit
|
||||||
|
`Content-Encoding: x-gzip`. `requests`/`urllib3` dekomprimiert das
|
||||||
|
bereits TRANSPARENT -- der Code rief zusaetzlich `gzip.decompress()`
|
||||||
|
drauf auf, was mit `BadGzipFile: Not a gzipped file` crashte (die
|
||||||
|
Gzip-Magic-Bytes waren durch die automatische Dekompression laengst
|
||||||
|
weg). Fix: nur noch entpacken wenn die Bytes wirklich noch mit der
|
||||||
|
Gzip-Magic (`\x1f\x8b`) anfangen.
|
||||||
|
2. **Big Sur+ wurde komplett ausgefiltert:** `_is_macos_installer()` prüfte
|
||||||
|
nur auf das klassische Schema
|
||||||
|
(`ExtendedMetaInfo.InstallAssistantPackageIdentifiers.OSInstall ==
|
||||||
|
"com.apple.mpkg.OSInstall"`, gueltig bis Catalina). Ab Big Sur nutzt
|
||||||
|
Apple stattdessen einen `SharedSupport`-Key
|
||||||
|
(`com.apple.pkg.InstallAssistant.macOSBigSur`, `...macOSTahoe`, etc.) --
|
||||||
|
ohne den alten Key. Der Filter hat dadurch STILLSCHWEIGEND alle
|
||||||
|
Big-Sur-bis-Tahoe-Installer verworfen, obwohl sie im selben Katalog
|
||||||
|
laengst enthalten waren (live verifiziert: 24 InstallAssistant.pkg-
|
||||||
|
Eintraege im Katalog, 0 davon trafen den alten Filter). Genau der
|
||||||
|
`recovery`-Pfad, den dieses Tool eigentlich anbieten soll. Fix:
|
||||||
|
`_is_macos_installer` erkennt jetzt beide Schemata; `_fetch_product_
|
||||||
|
metadata` faellt bei fehlendem `ServerMetadataURL` (Big Sur+ hat keins)
|
||||||
|
auf den Namen aus dem `SharedSupport`-Identifier zurueck. Nach dem Fix:
|
||||||
|
37 statt 13 Installer gefunden, inkl. macOS Tahoe.
|
||||||
|
|
||||||
|
Beide Fixes wurden nicht nur im Code geaendert, sondern gegen den echten,
|
||||||
|
frisch gebauten Container end-to-end verifiziert (`/api/versions` liefert
|
||||||
|
jetzt tatsaechlich Big Sur bis Tahoe zurueck).
|
||||||
|
|
||||||
## Voraussetzungen auf dem Host
|
## Voraussetzungen auf dem Host
|
||||||
- Docker + Docker Compose
|
- Docker + Docker Compose
|
||||||
|
|||||||
+61
-2
@@ -75,15 +75,61 @@ def find_latest_catalog_url(timeout: float = 8) -> str:
|
|||||||
def _fetch_catalog_products(url: str, timeout: float = 60) -> dict:
|
def _fetch_catalog_products(url: str, timeout: float = 60) -> dict:
|
||||||
r = requests.get(url, timeout=timeout)
|
r = requests.get(url, timeout=timeout)
|
||||||
r.raise_for_status()
|
r.raise_for_status()
|
||||||
raw = gzip.decompress(r.content)
|
# Live gegen swscan.apple.com verifiziert (2026-07-18): der Server
|
||||||
|
# schickt "Content-Encoding: x-gzip" -- requests/urllib3 dekomprimiert
|
||||||
|
# das bereits transparent, r.content ist also schon reines XML. Ein
|
||||||
|
# zusaetzliches gzip.decompress() darauf crasht mit BadGzipFile, weil
|
||||||
|
# die Gzip-Magic-Bytes (\x1f\x8b) dann laengst weg sind. Manche
|
||||||
|
# Server/Proxies liefern aber ggf. noch echte rohe Gzip-Bytes ohne
|
||||||
|
# das Encoding transparent aufzuloesen -- daher robust an den echten
|
||||||
|
# Magic-Bytes pruefen statt blind zu (de-)komprimieren.
|
||||||
|
raw = r.content
|
||||||
|
if raw[:2] == b"\x1f\x8b":
|
||||||
|
raw = gzip.decompress(raw)
|
||||||
data = plistlib.loads(raw)
|
data = plistlib.loads(raw)
|
||||||
return data.get("Products", {})
|
return data.get("Products", {})
|
||||||
|
|
||||||
|
|
||||||
|
# Bekannte Marketing-Versionsnummern nach Codename. Nur eine Anzeige-Hilfe --
|
||||||
|
# taucht ein UNS unbekannter zukuenftiger Codename auf (n plane naechstes Jahr:
|
||||||
|
# neuer Nachfolger von Tahoe), wird er trotzdem gelistet (siehe
|
||||||
|
# _split_shared_support_name), nur ohne huebsche Versionsnummer.
|
||||||
|
KNOWN_MACOS_VERSIONS = {
|
||||||
|
"bigsur": "11", "monterey": "12", "ventura": "13", "sonoma": "14",
|
||||||
|
"sequoia": "15", "tahoe": "26",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _split_shared_support_name(shared_support_id: str):
|
||||||
|
"""'com.apple.pkg.InstallAssistant.macOSBigSur' -> ('Big Sur', '11')."""
|
||||||
|
name = shared_support_id.rsplit(".macOS", 1)[-1]
|
||||||
|
if not name:
|
||||||
|
return None, None
|
||||||
|
# CamelCase -> "Big Sur" (Ziffern/Grossbuchstaben-Grenzen als Trenner)
|
||||||
|
import re
|
||||||
|
|
||||||
|
pretty = re.sub(r"(?<!^)(?=[A-Z])", " ", name).strip()
|
||||||
|
version = KNOWN_MACOS_VERSIONS.get(name.lower())
|
||||||
|
return pretty, version
|
||||||
|
|
||||||
|
|
||||||
def _is_macos_installer(product: dict) -> bool:
|
def _is_macos_installer(product: dict) -> bool:
|
||||||
emi = product.get("ExtendedMetaInfo", {}) or {}
|
emi = product.get("ExtendedMetaInfo", {}) or {}
|
||||||
ids = emi.get("InstallAssistantPackageIdentifiers", {}) or {}
|
ids = emi.get("InstallAssistantPackageIdentifiers", {}) or {}
|
||||||
return ids.get("OSInstall") == "com.apple.mpkg.OSInstall"
|
# Klassisches Schema bis Catalina: eigener "OSInstall"-Schluessel.
|
||||||
|
if ids.get("OSInstall") == "com.apple.mpkg.OSInstall":
|
||||||
|
return True
|
||||||
|
# Ab Big Sur (live gegen swscan.apple.com verifiziert, 2026-07-18): kein
|
||||||
|
# "OSInstall"-Schluessel mehr, stattdessen "SharedSupport":
|
||||||
|
# "com.apple.pkg.InstallAssistant.macOS<Name>" (z.B. macOSBigSur,
|
||||||
|
# macOSMonterey, ..., macOSTahoe). Der alte Filter uebersah dadurch
|
||||||
|
# STILLSCHWEIGEND alle Big-Sur-und-neuer-Installer -- genau die, die per
|
||||||
|
# InstallAssistant.pkg (statt BaseSystem.dmg) geliefert werden und die
|
||||||
|
# dieses Tool eigentlich explizit unterstuetzen soll.
|
||||||
|
shared = ids.get("SharedSupport", "")
|
||||||
|
return isinstance(shared, str) and shared.startswith(
|
||||||
|
"com.apple.pkg.InstallAssistant.macOS"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def _fetch_product_metadata(product: dict, timeout: float = 20):
|
def _fetch_product_metadata(product: dict, timeout: float = 20):
|
||||||
@@ -100,6 +146,19 @@ def _fetch_product_metadata(product: dict, timeout: float = 20):
|
|||||||
title = en.get("title")
|
title = en.get("title")
|
||||||
except Exception as exc: # noqa: BLE001
|
except Exception as exc: # noqa: BLE001
|
||||||
log.warning("Metadaten-Fetch fehlgeschlagen fuer %s: %s", smd_url, exc)
|
log.warning("Metadaten-Fetch fehlgeschlagen fuer %s: %s", smd_url, exc)
|
||||||
|
if title:
|
||||||
|
return title, version
|
||||||
|
# Big Sur+ Produkte haben KEIN ServerMetadataURL (live verifiziert) --
|
||||||
|
# Name kommt stattdessen aus dem SharedSupport-Identifier.
|
||||||
|
emi = product.get("ExtendedMetaInfo", {}) or {}
|
||||||
|
ids = emi.get("InstallAssistantPackageIdentifiers", {}) or {}
|
||||||
|
shared = ids.get("SharedSupport", "")
|
||||||
|
if isinstance(shared, str) and shared.startswith(
|
||||||
|
"com.apple.pkg.InstallAssistant.macOS"
|
||||||
|
):
|
||||||
|
name, ver = _split_shared_support_name(shared)
|
||||||
|
if name:
|
||||||
|
return f"macOS {name}", (version or ver)
|
||||||
return title, version
|
return title, version
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user