Gestion des distributions¶
Un guide conceptuel de la manière dont Repod modélise les distributions, pourquoi cette distinction est importante, et comment les paquets circulent entre elles.
Qu'est-ce qu'une distribution ?¶
Une distribution dans Repod représente une version cible de système d'exploitation. Lorsque vous uploadez ou importez un paquet, vous l'affectez à une distribution spécifique. Seuls les clients configurés pour cette distribution verront le paquet.
Le concept correspond directement à la notion de cible du gestionnaire de paquets natif :
Une distribution APT est un codename (jammy, noble,
bookworm). Il apparaît dans l'entrée sources.list :
En coulisses, reprepro gère une arborescence de répertoires sous
/repos/dists/jammy/ contenant InRelease, Release, et l'index
Packages, servie par depot-apt.
Une distribution RPM est un composant de chemin (almalinux9,
fedora, rocky9). Il apparaît dans le fichier .repo :
En coulisses, createrepo_c gère une arborescence de répertoires
sous /repos/rpm/almalinux9/x86_64/ contenant repodata/repomd.xml
et les fichiers RPM, servie par depot-rpm.
Une distribution Alpine est également un composant de chemin
(alpine3.20). Elle apparaît dans /etc/apk/repositories :
En coulisses, apk index gère une arborescence de répertoires sous
/repos/apk/alpine3.20/main/x86_64/ contenant APKINDEX.tar.gz et
les fichiers .apk, servie par depot-apt sous /apk/.
Pourquoi les distributions sont importantes¶
Compatibilité binaire¶
Un .deb construit pour Ubuntu 22.04 (jammy) peut ne pas s'installer
correctement sur Ubuntu 24.04 (noble) — version de glibc différente,
Python par défaut différent, ABI de bibliothèque SSL différente. La même
règle s'applique entre distributions basées sur RPM (AlmaLinux 8 vs. 9)
et versions Alpine (changements d'ABI musl entre alpine3.18 et
alpine3.21). Garder les distributions séparées empêche des paquets
non compatibles d'atteindre le mauvais système d'exploitation.
Précision des CVE¶
Grype utilise l'ID de distribution (almalinux:9, rockylinux:9,
opensuse/leap:15.6, etc., configuré par codename dans
services/distributions_rpm.py) pour filtrer les avis CVE. Une
vulnérabilité peut être corrigée dans AlmaLinux 9 mais pas dans
AlmaLinux 8 — sans le bon contexte de distribution, Grype pourrait
retourner des faux positifs ou manquer des avis pertinents.
Pour Ubuntu/Debian, Grype établit une corrélation avec les Ubuntu Security Notices (USN) et les entrées du Debian Security Tracker par version, ce qui nécessite de la même manière que le codename soit connu.
Contrôle du déploiement progressif¶
Les distributions servent de paliers d'étagement. Un motif courant :
La promotion de paquet déplace un binaire entre distributions sans re-upload ni re-scan (la décision CVE est préservée). Voir Promouvoir des paquets.
Distributions prises en charge¶
Lesquelles sont actives dépend de REPO_FORMAT (apt, rpm, apk,
both, ou all) — voir
Démarrage rapide — Étape 2.
| Codename | OS | Architecture |
|---|---|---|
jammy |
Ubuntu 22.04 LTS | amd64 |
noble |
Ubuntu 24.04 LTS | amd64 |
focal |
Ubuntu 20.04 LTS | amd64 |
bookworm |
Debian 12 | amd64 |
| Codename | OS | Architecture | ID de distro Grype |
|---|---|---|---|
almalinux8 |
AlmaLinux 8 | x86_64 |
almalinux:8 |
almalinux9 |
AlmaLinux 9 | x86_64 |
almalinux:9 |
rocky8 |
Rocky Linux 8 | x86_64 |
rockylinux:8 |
rocky9 |
Rocky Linux 9 | x86_64 |
rockylinux:9 |
centos-stream9 |
CentOS Stream 9 | x86_64 |
centos:9 |
oraclelinux8 |
Oracle Linux 8 | x86_64 |
oraclelinux:8 |
fedora |
Fedora 42 | x86_64 |
fedora:42 |
opensuse-leap-15.6 |
openSUSE Leap 15.6 | x86_64 |
opensuse/leap:15.6 |
opensuse-tumbleweed |
openSUSE Tumbleweed | x86_64 |
opensuse/tumbleweed:latest |
| Codename | OS | Architecture |
|---|---|---|
alpine3.18 |
Alpine Linux 3.18 | x86_64 |
alpine3.19 |
Alpine Linux 3.19 | x86_64 |
alpine3.20 |
Alpine Linux 3.20 | x86_64 |
alpine3.21 |
Alpine Linux 3.21 | x86_64 |
Organisation du système de fichiers des dépôts¶
Comprendre la disposition sur disque aide au dépannage ou à
l'intégration avec d'autres outils. Les trois arborescences vivent côte
à côte sous /repos/ dans le même conteneur backend — lesquelles
existent dépend de REPO_FORMAT.
/repos/
├── conf/
│ └── distributions ← configuration de reprepro
├── db/ ← base de données interne de reprepro
├── dists/
│ ├── jammy/
│ │ ├── InRelease ← index signé GPG
│ │ ├── Release
│ │ ├── Release.gpg
│ │ └── main/
│ │ ├── binary-amd64/
│ │ │ ├── Packages
│ │ │ ├── Packages.gz
│ │ │ └── Packages.xz
│ │ └── Contents-amd64.gz
│ ├── noble/
│ ├── focal/
│ └── bookworm/
└── pool/
└── main/
└── n/nginx/
└── nginx_1.24.0-1_amd64.deb
Le répertoire pool/ est partagé entre les distributions. Un
binaire de paquet est stocké une seule fois ; la commande
includedeb de reprepro crée l'entrée d'index qui le rend visible
dans une distribution spécifique.
/repos/rpm/
├── almalinux9/
│ └── x86_64/
│ ├── repodata/
│ │ ├── repomd.xml ← index principal
│ │ ├── repomd.xml.asc ← signature GPG détachée
│ │ ├── primary.xml.gz ← métadonnées de paquets
│ │ ├── filelists.xml.gz
│ │ └── other.xml.gz
│ └── nginx-1.24.0-1.el9.ngx.x86_64.rpm
├── rocky9/
│ └── x86_64/
└── fedora/
└── x86_64/
Chaque distribution RPM possède sa propre arborescence complète. Contrairement à APT, les paquets RPM ne sont pas dédupliqués entre distributions — chaque distribution conserve sa propre copie du binaire.
Promouvoir des paquets¶
La promotion déplace un paquet d'une distribution vers une autre sans re-upload ni re-scan. L'enregistrement de décision CVE et sa justification sont préservés.
# Promouvoir nginx de jammy (dev/QA) vers noble (production)
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/api/v1/distributions/promote \
-d '{"package":"nginx","from_dist":"jammy","to_dist":"noble"}'
Ce qui se passe en interne :
- Repod copie le binaire
.debdanspool/(ou crée un lien dur). reprepro includedeb <to_dist> <path>ajoute le paquet à l'index de la distribution cible.- reprepro re-signe le fichier
InReleasepour la distribution cible.
- Repod copie le fichier
.rpmvers le répertoire de la distribution cible. createrepo_c --updaterégénère l'indexrepodata/.- La signature GPG
repomd.xml.ascest régénérée.
- Repod copie le fichier
.apkvers le répertoire de la distribution cible. apk indexrégénère et re-signeAPKINDEX.tar.gz.
Rôle requis
La promotion requiert le rôle maintainer ou admin.
Migrer des paquets entre distributions¶
La migration copie tous les paquets d'une distribution vers une autre. Utile lors de la mise à niveau de votre base de système d'exploitation sur l'ensemble de votre infrastructure.
# Migrer tous les paquets de focal vers jammy
curl -X POST -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
http://localhost:8000/api/v1/distributions/migrate \
-d '{"from_dist":"focal","to_dist":"jammy"}'
La distribution source n'est pas supprimée
Après la migration, les paquets existent dans les deux distributions. Retirez les paquets de la distribution source manuellement si nécessaire.
Filtres de contenu (listes allow/deny)¶
Les filtres de contenu restreignent quels paquets peuvent entrer dans une distribution — un équivalent léger des filtres de Content View de Katello, scopé aux noms de paquets (correspondance exacte, wildcard glob, ou regex). Une distribution sans filtres se comporte exactement comme avant (ouverte à tout paquet) — les filtres sont strictement opt-in, aucun déploiement existant ne change de comportement en mettant à niveau.
Quand un filtre s'applique¶
Un filtre est évalué avant que tout fichier ne touche le disque —
à l'upload, à l'import depuis Internet, et à la promotion/migration
vers la distribution cible. Un paquet rejeté n'est jamais écrit dans
pool/, jamais indexé, et n'atteint jamais l'arborescence
Packages/repomd.xml/APKINDEX de la distribution. Ceci diffère du
workflow de revue CVE (pending_review) : un filtre est un verrou de
politique binaire, pas une file d'approbation humaine.
Repod n'a pas de « Library » non filtrée à la Katello se trouvant derrière une « Content View » publiée — l'upload/import valide et publie déjà en une seule étape. Les filtres s'attachent donc directement à la distribution qu'ils protègent, pas à un objet de vue composable séparé.
Ordre d'évaluation¶
Si une distribution possède des règles allow, un paquet doit
correspondre à au moins l'une d'entre elles, ou il est rejeté. Ensuite,
indépendamment du résultat de l'allow, toute règle deny
correspondante l'emporte — deny a toujours le dernier mot.
aucune règle → autorisé
des règles allow existent,
aucune correspondance → rejeté
correspondance allow,
correspondance deny aussi → rejeté (deny l'emporte)
correspondance allow,
aucune correspondance deny → autorisé
aucune règle allow,
correspondance deny → rejeté
aucune règle allow,
aucune correspondance deny → autorisé
Gérer les filtres¶
# Bloquer un paquet spécifique par nom exact
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
http://localhost:8000/api/v1/distributions/jammy/filters \
-d '{"rule_type":"deny","match_type":"exact","pattern":"telnet","description":"Protocole non sécurisé, banni par la politique"}'
# Bloquer toute la famille de paquets -dev avec un glob
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
http://localhost:8000/api/v1/distributions/jammy/filters \
-d '{"rule_type":"deny","match_type":"glob","pattern":"*-dbg"}'
# Restreindre une distribution de production à une allowlist explicite (regex)
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
http://localhost:8000/api/v1/distributions/noble/filters \
-d '{"rule_type":"allow","match_type":"regex","pattern":"^(nginx|openssl|curl|ca-certificates)$"}'
# Lister les règles actuelles
curl -H "Authorization: Bearer $TOKEN" http://localhost:8000/api/v1/distributions/jammy/filters
# Supprimer une règle
curl -X DELETE -H "Authorization: Bearer $TOKEN" \
http://localhost:8000/api/v1/distributions/jammy/filters/{rule_id}
Rôle requis
Ajouter, supprimer ou balayer des filtres requiert le rôle
admin — la lecture des règles actuelles ne requiert que l'accès
à la distribution (le même RBAC que
GET /distributions/{codename}/packages).
Prévisualiser avant de valider¶
Testez ce qu'une règle ferait — contre un nom de paquet existant ou hypothétique — sans rien sauvegarder :
curl -X POST -H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
http://localhost:8000/api/v1/distributions/jammy/filters/preview \
-d '{"name":"telnet"}'
# → {"allowed": false, "reason": "'telnet' blocked by rule deny/exact 'telnet'", "matched_rule": {...}}
Ce même endpoint est ce qu'appelle le bloc « Tester un paquet » de l'interface, dans l'onglet Distributions → (sélectionner une distribution) → Filtres, afin que vous puissiez valider l'effet d'une règle avant de l'ajouter, ou comprendre pourquoi un upload réel a été rejeté.
Les filtres ne sont jamais rétroactifs par eux-mêmes¶
Ajouter une règle à une distribution qui contient déjà des paquets correspondants ne retire rien automatiquement — un filtre est une politique tournée vers l'avenir, pas une purge silencieuse. Pour réconcilier le contenu existant avec les règles actuelles, exécutez un balayage explicite :
# Exécution à blanc (par défaut) — liste ce qui serait retiré, ne change rien
curl -X POST -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/api/v1/distributions/jammy/filters/sweep"
# Application — retire réellement les paquets non conformes de CETTE distribution uniquement
curl -X POST -H "Authorization: Bearer $TOKEN" \
"http://localhost:8000/api/v1/distributions/jammy/filters/sweep?dry_run=false"
L'étape d'application ne touche jamais que la seule distribution
balayée — un paquet retiré de jammy à cause du filtre de jammy
reste intact dans noble ou toute autre distribution.
Initialiser les distributions¶
Les distributions sont initialisées automatiquement au premier
démarrage pour chaque format activé par REPO_FORMAT. Si vous avez
besoin de les réinitialiser (par exemple après une restauration de
sauvegarde sans les arborescences de distribution) :
Ajouter des distributions non prises en charge¶
Les listes de distributions sont définies dans le code source du backend :
backend/services/distributions_apt.py— codenames APTbackend/services/distributions_rpm.py— codenames RPM + ID de distro Grypebackend/services/distributions_apk.py— codenames APK
Ajouter une nouvelle distribution requiert :
- Modifier le fichier
distributions_*.pyconcerné pour ajouter le nouveau codename / les métadonnées. - Pour APT, ajouter également le codename à
conf/distributions. - Reconstruire l'image backend :
docker compose build backend-api. - Appeler
POST /api/v1/distributions/initpour créer la structure sur disque.
Ceci est délibérément verrouillé derrière un changement de code pour prévenir une prolifération accidentelle de distributions en production.
Support des architectures¶
La version actuelle ne prend en charge que amd64 (APT) /
x86_64 (RPM, APK). Le support ARM (arm64 / aarch64) est prévu
pour une version future — l'endpoint d'upload rejette actuellement les
paquets aux architectures non prises en charge.