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://<domain>/flatpak/...- bereits vorhandenhttps://<domain>/dnf/...- dieser Stack
Funktionsprinzip
Der dnf-mirror-Container laeuft dauerhaft und fuehrt alle
SYNC_INTERVAL_SECONDS (Default 6h) folgenden Zyklus aus:
- packages.yaml aktualisieren:
git fetch/reset --hardgegen das inSOURCES_GIT_URLkonfigurierte Git-Repo, das eine Dateipackages.yamlenthaelt (siehesources-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.yamllistet Fremdrepos mit ihrer URL und genau den Paketnamen, die daraus geholt werden sollen, z.B.: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:
dnf downloadholt aus dem Fremdrepo genau die unterpackages:gelisteten Paketnamen (keine automatische Abhaengigkeitsaufloesung, keine sonstigen Pakete aus diesem Repo - siehesources-repo-template/README.md).- 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 inpackages.yamleintraegt). Ausnahmen lassen sich imforce_include-Feld vonpackages.yamleintragen (z.B. wenn eine Anwendung eine neuere Version einer Bibliothek braucht als Alma mitbringt). - Umsignieren: Die verbleibenden, tatsaechlich zu spiegelnden Pakete
werden mit
rpmsign --resignkomplett 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. - Metadaten bauen:
createrepo_cfuer die eigentlichen Repo-Metadaten,appstream-builderfuer AppStream-Katalogdaten, damit die Anwendungen mit Name/Icon/Beschreibung in Discover (GNOME Software) auftauchen statt nur perdnf/pkconauf der Kommandozeile sichtbar zu sein. repomd.xmlwird zusaetzlich separat signiert (repomd.xml.asc), damit Clientsrepo_gpgcheck=1nutzen koennen (schuetzt auch die Metadaten selbst, nicht nur die einzelnen Pakete).- Eine fertige Client-
.repo-Datei wird nach/srv/dnf/<id>.repogeschrieben und ist unterhttps://<domain>/dnf/<id>.repoabrufbar.
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
# 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://<domain>/dnf/RPM-GPG-KEY-mirror- der Public Key des Spiegelshttps://<domain>/dnf/vscode.repo- fertige Repo-Datei fuer Clientshttps://<domain>/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:
./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(Paketappstream-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_includeinpackages.yamlnutzen. - Keine Abhaengigkeitsaufloesung:
packages.yamllistet die zu spiegelnden Pakete explizit;dnf downloadholt 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 inpackages:aufgenommen werden. - GPG-Key-Ablage: Der Signing-Key liegt (statt in einem anonymen Docker-
Volume) im Ordner
./gpgdirekt neben derdocker-compose.yml- dadurch laesst er sich mit normalen Host-Mitteln sichern/backupen/einschraenken. Der Container startet dafuer intern kurz als root, chownt./gpgauf den unprivilegiertenmirror-User und wechselt dann persetprivdauerhaft zu diesem (siehemirror/entrypoint.sh) - der eigentliche Sync inkl.git/dnf/gpglaeuft 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 inrun-as-mirror.shfuer die Schluesselerzeugung vorbereitet, automatisches Signieren mit passphrasegeschuetztem Key ist aber NICHT fertig verdrahtet (braucht zusaetzlichgpg-preset-passphraseo.ae.) - fuer den unbeaufsichtigten Betrieb wird die leere Passphrase empfohlen. - Git-Zugriff:
SOURCES_GIT_URLwird 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 einenlisten 443 sslServer-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 upsollte 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)