# 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: 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. 4. **Metadaten bauen**: `createrepo_c` fuer die eigentlichen Repo-Metadaten, `appstream-builder` fuer AppStream-Katalogdaten, damit die Anwendungen mit Name/Icon/Beschreibung in **Discover (GNOME Software)** auftauchen statt nur per `dnf`/`pkcon` auf der Kommandozeile sichtbar zu sein. 5. `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). 6. Eine fertige Client-`.repo`-Datei wird nach `/srv/dnf/.repo` geschrieben und ist unter `https:///dnf/.repo` abrufbar. 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, 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/vscode.repo` - fertige Repo-Datei fuer Clients - `https:///dnf/vscode/` - das eigentliche Repository ## 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 vscode ``` Das importiert den Mirror-GPG-Key und legt die `.repo`-Datei ab. 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**: `appstream-builder` (Paket `appstream-glib`, aus EPEL) extrahiert 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. - **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. - **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. - 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-Image mit dnf-plugins-core, createrepo_c, rpm-sign, 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 -> dnf download je Fremdrepo -> filtern -> signieren -> 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) ```