README.md gelöscht
This commit is contained in:
@@ -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://<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:
|
|
||||||
|
|
||||||
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://<domain>/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://<domain>/dnf/RPM-GPG-KEY-mirror` - der Public Key des Spiegels
|
|
||||||
- `https://<domain>/dnf/company-mirror.repo` (bzw. `$MIRROR_REPO_ID.repo`) -
|
|
||||||
die eine Repo-Datei, die Clients einbinden
|
|
||||||
- `https://<domain>/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` + `<pkgname>`**: 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 `<description>`, sonst schlaegt die betroffene Komponente
|
|
||||||
mit `description-missing` fehl. Ausserdem braucht JEDE Komponente - auch
|
|
||||||
eine echte, vom Hersteller mitgelieferte `.metainfo.xml` - ein
|
|
||||||
`<pkgname>`-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/<id>/`): 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
|
|
||||||
`<id>/`-Verzeichnisse und `<id>.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)
|
|
||||||
```
|
|
||||||
Reference in New Issue
Block a user