Files
dnf-repo-test/README.md
T

9.7 KiB

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 vorhanden
  • https://<domain>/dnf/... - dieser Stack

Funktionsprinzip

Der dnf-mirror-Container laeuft dauerhaft und fuehrt alle SYNC_INTERVAL_SECONDS (Default 6h) folgenden Zyklus aus:

  1. 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.:
    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/<id>.repo geschrieben und ist unter https://<domain>/dnf/<id>.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

# 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 Spiegels
  • https://<domain>/dnf/vscode.repo - fertige Repo-Datei fuer Clients
  • https://<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 (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)