From a21098a8eaa53988e7ed286641af681122a18861 Mon Sep 17 00:00:00 2001 From: "David M. Klein" <4+leander19961@noreply.localhost> Date: Thu, 3 Sep 2026 14:25:18 +0200 Subject: [PATCH] =?UTF-8?q?README.md=20gel=C3=B6scht?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 275 ------------------------------------------------------ 1 file changed, 275 deletions(-) delete mode 100644 README.md diff --git a/README.md b/README.md deleted file mode 100644 index 8f95d95..0000000 --- a/README.md +++ /dev/null @@ -1,275 +0,0 @@ -# DNF-Spiegelserver fuer Pakete ohne Flatpak - -Ergaenzt den bestehenden Flathub-Filter-Spiegel um einen zweiten Weg fuer -Anwendungen, die es nur als RPM gibt. Beide sollen unter einer Domain laufen: - -- `https:///flatpak/...` - bereits vorhanden -- `https:///dnf/...` - dieser Stack - -## Funktionsprinzip - -Der `dnf-mirror`-Container laeuft dauerhaft und fuehrt alle -`SYNC_INTERVAL_SECONDS` (Default 6h) folgenden Zyklus aus: - -0. **packages.yaml aktualisieren**: `git fetch`/`reset --hard` gegen das in - `SOURCES_GIT_URL` konfigurierte Git-Repo, das eine Datei `packages.yaml` - enthaelt (siehe `sources-repo-template/`). Das passiert vor **jedem** - Sync-Lauf - neue/geaenderte Eintraege in diesem separaten Repo wirken also - spaetestens beim naechsten automatischen Lauf, ganz ohne Neubau/Neustart - des Containers. `packages.yaml` listet Fremdrepos mit ihrer URL und genau - den Paketnamen, die daraus geholt werden sollen, z.B.: - ```yaml - repos: - - id: vscode - baseurl: https://packages.microsoft.com/yumrepos/vscode - gpgkey: https://packages.microsoft.com/keys/microsoft.asc - packages: - - code - ``` - -Danach, pro dort definiertem Fremdrepo, zunaechst in einen PRIVATEN, -internen Zwischenspeicher (noch nicht oeffentlich sichtbar): - -1. **`dnf download`** holt aus dem Fremdrepo genau die unter `packages:` - gelisteten Paketnamen (keine automatische Abhaengigkeitsaufloesung, keine - sonstigen Pakete aus diesem Repo - siehe `sources-repo-template/README.md`). -2. **Filtern**: Jedes heruntergeladene Paket wird trotzdem gegen die - Paketnamen geprueft, die AlmaLinux selbst liefert (BaseOS/AppStream/ - Extras/CRB/EPEL - konfigurierbar ueber `ALMA_BASELINE_REPOIDS`). Ist der - Name dort vorhanden, wird das Paket verworfen: die Clients haben ihre - Alma-Repos ja selbst schon eingebunden und loesen es von dort auf (Schutz - gegen versehentliches Doppelspiegeln, falls jemand faelschlich ein - Alma-Paket in `packages.yaml` eintraegt). Ausnahmen lassen sich im - `force_include`-Feld von `packages.yaml` eintragen (z.B. wenn eine - Anwendung eine neuere Version einer Bibliothek braucht als Alma mitbringt). -3. **Umsignieren**: Die verbleibenden, tatsaechlich zu spiegelnden Pakete - werden mit `rpmsign --resign` komplett neu signiert - die Original- - Herstellersignatur wird ersetzt. Dadurch brauchen Clients nur **einen** - GPG-Key (den des Spiegelservers), egal von wie vielen verschiedenen - Herstellern die gespiegelten Pakete urspruenglich stammen. - -Erst danach, EINMALIG ueber alle Fremdrepos zusammen: - -4. **Zusammenfuehren**: Die signierten Pakete aller Fremdrepos werden zu - **einem gemeinsamen, oeffentlichen Repo** gemergt (`rsync --delete` ueber - alle privaten Zwischenspeicher hinweg in `/srv/dnf`). Ein Fremdrepo, das - in einem Sync-Lauf mal nicht erreichbar war, behaelt einfach seinen - letzten erfolgreichen Stand im gemeinsamen Repo; wird ein Fremdrepo oder - ein einzelnes Paket aus `packages.yaml` entfernt, verschwindet es beim - naechsten Lauf auch aus dem gemeinsamen Repo. -5. **Metadaten bauen**: `createrepo_c` fuer die eigentlichen Repo-Metadaten - des gemeinsamen Repos, dazu AppStream-Katalogdaten (`appstream-builder` - bzw. `appstreamcli compose`, je nachdem was im Image verfuegbar ist - - siehe unten), damit die Anwendungen mit Name/Icon/Beschreibung in - **Discover (GNOME Software)** auftauchen statt nur per `dnf`/`pkcon` auf - der Kommandozeile sichtbar zu sein. -6. `repomd.xml` wird zusaetzlich separat signiert (`repomd.xml.asc`), damit - Clients `repo_gpgcheck=1` nutzen koennen (schuetzt auch die Metadaten - selbst, nicht nur die einzelnen Pakete). -7. Eine fertige Client-`.repo`-Datei wird nach `/srv/dnf/$MIRROR_REPO_ID.repo` - geschrieben und ist unter `https:///dnf/$MIRROR_REPO_ID.repo` - abrufbar - das ist das **einzige** Repo, das Clients einbinden muessen. - -Der `nginx`-Container bekommt das Ausgabe-Volume read-only gemountet und -liefert es unter `/dnf/` aus. Er baut keine Pakete, sondern ist nur fuer den -Zugriff von aussen zustaendig. - -## Erste Einrichtung - -```sh -# 1. Paketliste: sources-repo-template/packages.yaml (angepasst) in ein -# EIGENES, separates Git-Repo pushen (siehe sources-repo-template/README.md) -cp sources-repo-template/packages.yaml /pfad/zum/eigenen/git-repo/packages.yaml -# Inhalt pruefen/anpassen (eigene Fremdrepos + Paketnamen eintragen), dann -# committen+pushen ins neue Git-Repo - -# 2. Compose-Stack konfigurieren -cp .env.example .env -# .env anpassen: mindestens MIRROR_DOMAIN, MIRROR_REPO_ID, GPG_NAME, GPG_EMAIL, -# SOURCES_GIT_URL - -# 3. Bei SSH-Zugriff aufs Git-Repo: Deploy-Key (nur Lesezugriff) ablegen -cp /pfad/zum/deploy-key ./git-ssh/id_ed25519 -chmod 600 ./git-ssh/id_ed25519 - -docker compose up -d --build -docker compose logs -f dnf-mirror # ersten Sync-Lauf beobachten -``` - -Der `./gpg`-Ordner neben der `docker-compose.yml` wird beim ersten Start vom -Container selbst befuellt (GPG-Schluesselpaar) - dort ist nichts manuell -vorzubereiten, aber der Ordner sollte auf dem Host entsprechend geschuetzt -(nicht world-readable) und **gesichert/gebackupt** werden: geht er verloren, -muessen alle Clients den neuen Public Key neu importieren. - -Nach dem ersten Lauf: - -- `https:///dnf/RPM-GPG-KEY-mirror` - der Public Key des Spiegels -- `https:///dnf/company-mirror.repo` (bzw. `$MIRROR_REPO_ID.repo`) - - die eine Repo-Datei, die Clients einbinden -- `https:///dnf/` - das gemeinsame Repository selbst, enthaelt die - Pakete aus allen in `packages.yaml` konfigurierten Fremdrepos - -## Neue Anwendungen spiegeln - -Fuer jede Anwendung ohne Flatpak einen Eintrag unter `repos:` in der -`packages.yaml` **im separaten Paketlisten-Git-Repo** ergaenzen (Fremdrepo- -URL + die gewuenschten Paketnamen unter `packages:`), committen, pushen - der -Container zieht das beim naechsten Sync-Lauf automatisch. Kein Zugriff auf -den Docker-Host/Compose-Stack noetig, das kann also z.B. per Merge-Request- -Review an ein anderes Team delegiert werden. - -## Clients anbinden - -Einmalig (per Ansible/Kickstart/Intune-Skript/Image-Build - vermutlich schon -Teil eurer bestehenden Entra-ID-Provisioning-Pipeline), z.B. mit dem -mitgelieferten Skript: - -```sh -./client/bootstrap.sh software.example.org -``` - -Das importiert den Mirror-GPG-Key und legt die **eine** `.repo`-Datei ab - -Clients sehen darin von Anfang an alle gespiegelten Anwendungen aus allen -konfigurierten Fremdrepos, ganz ohne pro Anwendung ein weiteres Repo -einbinden zu muessen. Rootlose Installation/Update/Deinstallation laeuft -danach ueber die bereits bestehenden Polkit-Regeln fuer PackageKit wie -gewohnt - hier ist nichts zusaetzlich noetig. - -## Bekannte Einschraenkungen - -- **AppStream/Discover**: Beide Werkzeuge extrahieren AppStream-Metadaten aus - Paketen, die selbst `.metainfo.xml`/`.desktop`-Dateien mitbringen. Bringt - eine Anwendung so etwas nicht mit, erscheint sie trotzdem installierbar, - aber ohne Katalogeintrag (Name/Icon/Beschreibung) in Discover - nur per - Suche nach dem Paketnamen. Das ist eine Eigenschaft des Quellpakets, keine - Konfigurationsluecke. - - AlmaLinux 9: `appstream-builder` (Paket `appstream-glib`, aus EPEL) liest - die `.rpm`-Dateien direkt ein - der laenger etablierte, gut erprobte Weg. - - AlmaLinux 10: `appstream-glib` ist dort (Stand jetzt) weder im - AlmaLinux- noch im EPEL-Repo paketiert. Stattdessen nutzt `sync.sh` - `appstreamcli compose` (Pakete `appstream` + `appstream-compose` - Letzteres - ist ein eigenes Subpaket, ohne das `appstreamcli compose` zur Laufzeit mit - "AppStream Compose binary ... was not found" scheitert - beide direkt aus - AlmaLinux 10). Da - dieses Kommando anders als `appstream-builder` auf einem Verzeichnisbaum - statt auf `.rpm`-Dateien arbeitet, entpackt `sync.sh` vorher die - relevanten Pfade (`usr/share/{applications,metainfo,icons,pixmaps}`) aus - allen Paketen in so einen Baum. Dieser Weg ist neuer/weniger erprobt als - der EL9-Pfad - nach der ersten echten Nutzung `docker compose logs - dnf-mirror` und die generierten Metadaten in Discover pruefen. - - **jpackage-basierte Java/JavaFX-Apps** (u.a. XPipe): Diese legen ihre - `.desktop`-Datei typischerweise NICHT unter `/usr/share/applications/` - ab, sondern direkt im eigenen Installationsverzeichnis (z.B. - `/opt/xpipe/xpipe.desktop`) und verschieben sie erst per `%post`-Skript - dorthin - das sieht `sync.sh` beim reinen Lesen des Pakets nicht. Der - `appstreamcli compose`-Pfad (AlmaLinux 10) durchsucht deshalb zusaetzlich - das gesamte Paket nach `.desktop`/`.metainfo.xml`/`.appdata.xml`-Dateien - ausserhalb der Standardpfade und holt genau diese gezielt nach. Der - `appstream-builder`-Pfad (AlmaLinux 9) hat diesen Zusatzschritt aktuell - NICHT - dort fehlt so einer Anwendung ggf. weiterhin der Katalogeintrag. - - **Fehlende `.metainfo.xml` + ``**: Hat eine Anwendung nur eine - `.desktop`-Datei, aber keine `.metainfo.xml` (der Normalfall bei - unabhaengigen Drittanbieter-RPMs), erzeugt `sync.sh` selbst eine minimale - Ersatz-Metainfo daraus (Name/Beschreibung/Icon/Kategorien aus der - `.desktop`-Datei). `appstreamcli compose` (AppStream 1.0+) verlangt dafuer - zwingend eine ``, sonst schlaegt die betroffene Komponente - mit `description-missing` fehl. Ausserdem braucht JEDE Komponente - auch - eine echte, vom Hersteller mitgelieferte `.metainfo.xml` - ein - ``-Element, damit PackageKit sie einem installierbaren RPM - zuordnen kann; Upstream-Metainfo-Dateien haben das so gut wie nie (kennen - den Distro-Paketnamen nicht), `sync.sh` ergaenzt es deshalb automatisch - beim Einsammeln der Dateien. - - Ist keins von beiden installiert, schlaegt die Erzeugung fehl, oder - bringt das Paket auch an keinem dieser Orte eine `.desktop`-/AppStream- - Datei mit, bleiben Pakete ganz normal per `dnf`/`pkcon` installierbar, - erscheinen aber ohne Katalogeintrag in Discover (siehe Log-Warnung). -- **PackageKit-Cache auf dem Client**: `dnf makecache` aktualisiert nur den - Cache des interaktiven `dnf`-Kommandos. PackageKit (und damit Discover) - haelt einen davon getrennten eigenen Metadaten-Cache und aktualisiert ihn - per Default nicht zuverlaessig von selbst zeitnah. `client/bootstrap.sh` - ruft deshalb zusaetzlich `pkcon refresh -c 1` auf - ohne das kann ein neu - eingebundenes Repo (oder neu hinzugekommene Pakete darin) in Discover trotz - korrekter AppStream-Daten verzoegert oder gar nicht auftauchen. -- **Namensbasierter Abgleich**: Paket X wird uebersprungen, sobald ein Paket - gleichen Namens in Alma existiert - unabhaengig von der Version. Falls eine - gespiegelte Anwendung eine neuere Version einer Alma-Bibliothek als - Abhaengigkeit braucht, dafuer `force_include` in `packages.yaml` nutzen. -- **Keine Abhaengigkeitsaufloesung**: `packages.yaml` listet die zu - spiegelnden Pakete explizit; `dnf download` holt genau diese Namen aus dem - jeweiligen Fremdrepo, ohne fehlende Abhaengigkeiten automatisch - nachzuladen. Fehlt einer gespiegelten Anwendung eine Bibliothek, die weder - aus Alma/EPEL noch aus demselben Fremdrepo kommt, muss sie explizit mit in - `packages:` aufgenommen werden. -- **Ein gemeinsames Repo, ein gemeinsamer Namensraum**: Alle Fremdrepos aus - `packages.yaml` werden zu einem einzigen Repo zusammengefuehrt. Zwei - verschiedene Fremdrepos mit einem gleichnamigen Paket (gleicher - .rpm-Dateiname) landen deshalb im selben Speicherplatz - das zuletzt - gesyncte gewinnt. Bei der Zusammenstellung von `packages.yaml` also darauf - achten, dass sich die gewaehlten Anwendungen nicht ueberschneiden. -- **GPG-Key-Ablage**: Der Signing-Key liegt (statt in einem anonymen Docker- - Volume) im Ordner `./gpg` direkt neben der `docker-compose.yml` - dadurch - laesst er sich mit normalen Host-Mitteln sichern/backupen/einschraenken. - Der Container startet dafuer intern kurz als root, chownt `./gpg` auf den - unprivilegierten `mirror`-User und wechselt dann per `setpriv` dauerhaft zu - diesem (siehe `mirror/entrypoint.sh`) - der eigentliche Sync inkl. `git`/ - `dnf`/`gpg` laeuft nie als root. -- **GPG-Passphrase**: Der Signing-Key wird standardmaessig ohne Passphrase - erzeugt, damit der Container vollautomatisch signieren kann. Der Schutz - liegt dann in den Dateirechten von `./gpg` (nicht world-readable lassen!). - Eine Passphrase ist in `run-as-mirror.sh` fuer die Schluesselerzeugung - vorbereitet, automatisches Signieren mit passphrasegeschuetztem Key ist - aber NICHT fertig verdrahtet (braucht zusaetzlich `gpg-preset-passphrase` - o.ae.) - fuer den unbeaufsichtigten Betrieb wird die leere Passphrase - empfohlen. -- **Git-Zugriff**: `SOURCES_GIT_URL` wird vor jedem Sync-Lauf gefetcht. Schlaegt - das fehl (Netzwerk, Auth), laeuft der Sync mit der letzten erfolgreich - geholten Paketliste weiter (nicht beim allerersten Lauf - dort bricht der - Sync-Zyklus ohne erreichbares Git-Repo komplett ab). Nur ein Lesezugriff - (Deploy-Key/Read-only-Token) ist noetig. -- **TLS**: Der `nginx`-Service ist bewusst schlank gehalten (Port 80). Fuer - TLS entweder Zertifikate mounten und einen `listen 443 ssl` Server-Block - ergaenzen, oder - passend zum Wunsch "eine Domain fuer beide Repos" - - diesen Stack hinter einem bereits vorhandenen Reverse Proxy/Loadbalancer - betreiben, der auch den Flatpak-Spiegel terminiert. -- **Upgrade von einer aelteren Version dieses Stacks** (mit einem Repo PRO - Fremdrepo unter `/dnf//`): Das Ausgabeformat hat sich grundlegend - geaendert (jetzt ein gemeinsames Repo direkt unter `/dnf/`). Vor dem - ersten Start mit dieser Version das `dnf-repo-data`-Volume einmalig leeren - (`docker compose down` + Volume-Inhalt loeschen), damit keine alten - `/`-Verzeichnisse und `.repo`-Dateien liegen bleiben, und - anschliessend allen Clients die neue, eine `.repo`-Datei ausrollen. -- Dieser Aufbau wurde nicht gegen echte Internet-Upstreams end-to-end - getestet (keine Docker-Umgebung/Internetzugriff in dieser Session - verfuegbar) - Skripte sind syntaktisch geprueft, der erste `docker compose - up` sollte aber mit echten Repo-Definitionen beobachtet werden. - -## Verzeichnisstruktur - -``` -docker-compose.yml -.env.example -gpg/ (leer, wird vom Container befuellt) - Signing-Key, - auf dem Host sichern/schuetzen -git-ssh/ optional: SSH-Deploy-Key fuers Paketlisten-Git-Repo -mirror/ - Dockerfile AlmaLinux-10-Image mit dnf-plugins-core, createrepo_c, - rpm-sign, appstream+appstream-compose/appstream-glib, - git, python3-pyyaml, jq - entrypoint.sh laeuft als root: chownt ./gpg, wechselt per setpriv - zu "mirror" - run-as-mirror.sh laeuft als "mirror": GPG-Key-Erzeugung/-Export, - Sync-Schleife - sync.sh Kernlogik: packages.yaml aus Git holen -> je - Fremdrepo dnf download -> filtern -> signieren - (privater Cache) -> alle Fremdrepos zu EINEM Repo - zusammenfuehren -> Metadaten -> Client-.repo - config/ - alma-baseline.repo.d/ optional: eigene Baseline-Repos fuer den Abgleich -sources-repo-template/ Vorlage fuer das SEPARATE Git-Repo mit der - Paketliste (packages.yaml) -nginx/ - conf.d/repo.conf liefert /dnf/... aus, Platzhalter fuer /flatpak/... -client/ - bootstrap.sh einmaliges Client-Onboarding (Key-Import + .repo) -```