Nexus (dépôts internes)¶
Rôle¶
Sonatype Nexus Repository Manager (10.100.2.178) est le miroir/proxy d'artefacts central de
toute la flotte Indio : paquets RPM (Rocky/EPEL/CRB + une douzaine de dépôts tiers), registres
Docker, tarballs internes, paquets Python/npm. C'est la brique qui permet à des VM en zones sans
sortie Internet directe (DMZ, SOC) de récupérer tout ce dont elles ont besoin via un chemin réseau
interne unique, exposé par HAProxy sous repo.infra.infra.
Comme Guacamole, Nexus est géré entièrement hors IaC :
aucun projet Terraform ou Ansible dédié dans /root — c'est une brique d'infrastructure à part
entière, configurée uniquement via son UI/API. Le RPM indio-baseline-config (voir
baseline-config.md) consomme Nexus mais ne le configure pas.
Architecture¶
Exposition réseau¶
- Accès direct :
10.100.2.178:8081(UI/API Nexus, zone admin). - Accès flotte : via la VIP HAProxy
repo.infra.infra(10.100.2.130) — terminaison SSL côté HAProxy, backend Nexus en clair. Le servicerepo(port 443) proxifie tout le chemin/repository/<nom>/..., ce qui couvre la totalité des dépôts à accès "path-based" ci-dessous en une seule règle. - 5 registres Docker à port HAProxy dédié (8083-8087) : nécessaire car
containerd/kubelet(cluster Kubernetes) gèrent mal le mode path-based de Nexus pour le formatdocker— voir haproxy.md pour la configuration des frontends.
flowchart LR
subgraph Clients
FLEET["Flotte Rocky<br/>(indio.repo, template doré)"]
K8S["Cluster Kubernetes<br/>(containerd)"]
DMZ["Proxies DMZ<br/>(Squid, unbound)"]
end
FLEET -->|HTTPS 443, path-based| VIP["VIP HAProxy<br/>repo.infra.infra"]
DMZ -->|HTTPS 443, path-based<br/>ne peut pas sortir via sa propre VIP| VIP
K8S -->|HTTPS 8083-8087, 1 port/registre| VIP
VIP --> NEXUS["Nexus 10.100.2.178:8081"]
NEXUS -->|proxy| UP1["Upstreams publics<br/>(Rocky, EPEL, Docker Hub, GHCR, Quay...)"]
NEXUS --> HOSTED[("Dépôts hosted<br/>(indio-custom, tarballs internes...)")]
Les 25 dépôts (vérifiés en direct le 2026-07-19)¶
Écart avec la documentation historique
Un souvenir daté du rebuild SOC (2026-07-07) documentait 10 dépôts créés à l'époque, dont
elastic-9x et cassandra-41x — une correction ultérieure (07-08) notait que elastic-9x
n'était en fait jamais vraiment utilisé (paquets Elastic servis directement via le Squid
whitelisté). L'état actuel ne contient ni elastic-9x ni cassandra-41x : à la place,
un dépôt nommé elk (même remoteUrl, artifacts.elastic.co) est présent — probablement
recréé/renommé depuis sans qu'aucun souvenir ne documente ce changement. Le code/l'état réel
fait foi ; cette page reflète les 25 dépôts constatés aujourd'hui, pas l'historique.
Paquets système (yum, proxy vers un upstream public)
| Dépôt | Upstream | Usage |
|---|---|---|
rocky |
download.rockylinux.org |
Miroir Rocky Linux (BaseOS/AppStream/Extras/CRB, consommés via des sous-chemins précis, voir indio.repo) |
epel |
dl.fedoraproject.org |
EPEL 9 |
postgresql |
download.postgresql.org |
Dépôt PGDG (voir postgresql.md) |
corretto |
yum.corretto.aws |
JDK Amazon Corretto (repodata sous x86_64/, pas à la racine) |
syslog-ng |
download.copr.fedorainfracloud.org |
COPR czanik/syslog-ng412 (voir syslogng.md) — hôte de téléchargement, pas le frontend web copr.fedorainfracloud.org (protégé par anti-bot, inutilisable en remoteUrl) |
docker-ce |
download.docker.com |
Paquets docker-ce/containerd (RPM, distinct des registres Docker ci-dessous) |
zabbix |
repo.zabbix.com/zabbix/8.0/unstable/rhel/9/ |
Créé le 2026-07-16 pour affranchir les 4 VM proxy DMZ de la boucle sur leur propre VIP Squid — bénéficie à toute la flotte (voir zabbix.md) |
kubernetes-rpm |
pkgs.k8s.io/core:/stable:/v1.33/rpm/ |
kubeadm/kubelet/kubectl (voir k8s.md) |
elk |
artifacts.elastic.co |
Paquets Elastic (voir écart ci-dessus) |
Dépôts internes (yum/raw, hosted — paquets maison, poussés par upload)
| Dépôt | Format | Usage |
|---|---|---|
indio-custom |
yum hosted | Paquets internes génériques, notamment indio-baseline-config (voir baseline-config.md) — non signé (gpgcheck=0) |
strangebee-rpm |
yum hosted | RPM TheHive/Cortex (StrangeBee) |
grafana-rpm |
yum hosted | RPM Grafana — contournement du blocage Fastly de rpm.grafana.com |
kafka-tarball |
raw hosted | Tarball Apache Kafka |
k8s-manifests |
raw hosted | Manifests Kubernetes internes |
glpi-tarball |
raw hosted | Archive GLPI |
ocs-tarball |
raw hosted | Archive OCS Inventory |
ocs-agent-rpm |
raw hosted | Agent Unix OCS Inventory (tarball, malgré le nom — consommé par le rôle Ansible ocs_agent, voir baseline-config.md) |
Registres Docker
| Dépôt | Upstream | Port HAProxy | Détail |
|---|---|---|---|
dockerhub |
registry-1.docker.io |
443 (path-based) | Créé le 07-07, historique, pathEnabled=true |
docker-proxy |
registry-1.docker.io |
8083 | Port dédié pour containerd/kubelet (node, nginx, alpine, kaniko) |
k8s-proxy |
registry.k8s.io |
8084 | kube-apiserver, coredns, pause, etcd |
ghcr-proxy |
ghcr.io |
8085 | ArgoCD, External Secrets |
quay-proxy |
quay.io |
8086 | Prometheus, MetalLB, Calico/Tigera |
docker-internal |
— (hosted) | 8087 | Images internes/custom, forceBasicAuth=true |
Gestionnaires de paquets applicatifs
| Dépôt | Format | Upstream |
|---|---|---|
pypi |
pypi proxy | pypi.org |
npm |
npm proxy | registry.npmjs.org |
Provisioning Terraform¶
Non applicable. Aucun projet /root/nexus n'existe — vérifié explicitement (absence du dossier).
Nexus est une VM/service géré hors IaC, au même titre que Guacamole.
Configuration Ansible¶
Non applicable côté serveur (aucun rôle ne configure Nexus lui-même). Côté client, en revanche, plusieurs projets Ansible consomment Nexus :
/root/baseline/ansible(rôleszabbix_agent,ocs_agent) — voir baseline-config.md.- Le RPM
indio-baseline-configdéposeindio.repo(6 sections, toutes via Nexus) sur chaque VM clonée depuis le template doré — voir baseline-config.md et packer-template.md.
Procédure manuelle¶
Nexus étant piloté à 100% hors code, voici le mode opératoire réel (API REST, admin/mot de passe
géré via le mécanisme de secrets Indio — jamais la valeur en clair ici) pour les opérations
courantes, tel que pratiqué historiquement sur ce serveur :
1. Créer un dépôt yum proxy :
curl -sk -u admin:'<mot de passe>' -X POST \
https://10.100.2.178:8081/service/rest/v1/repositories/yum/proxy \
-H 'Content-Type: application/json' \
-d '{
"name": "<nom>",
"online": true,
"storage": {"blobStoreName": "default", "strictContentTypeValidation": true},
"proxy": {"remoteUrl": "https://<upstream>", "contentMaxAge": 1440, "metadataMaxAge": 1440},
"httpClient": {"blocked": false, "autoBlock": true},
"yum": {"repodataDepth": 0}
}'
2. Créer un dépôt hosted (interne, upload manuel) : même endpoint, .../yum/hosted (ou
raw/hosted, docker/hosted), avec "storage": {"writePolicy": "ALLOW", ...}.
3. Uploader un paquet dans un dépôt hosted :
curl -sk -u admin:'<mot de passe>' -X POST \
"https://10.100.2.178:8081/service/rest/v1/components?repository=indio-custom" \
-F "yum.asset=@mon-paquet.rpm" -F "yum.directory=/"
Piège : régénération asynchrone des métadonnées
Après un upload dans un dépôt yum hosted, la régénération de repodata/repomd.xml est
asynchrone — un dnf install lancé immédiatement après peut encore résoudre l'ancienne
version. Aucune tâche Nexus dédiée ne permet de forcer la régénération via l'API v1 publique
(POST /service/rest/v1/tasks refuse, HTTP 405). Attendre quelques secondes avant de tester,
ou vérifier directement le contenu de primary.xml.gz plutôt que le simple code HTTP 200 de
repomd.xml.
4. Créer un registre Docker à port dédié (nécessaire pour containerd/kubelet) : créer le dépôt
docker/proxy ou docker/hosted avec "docker": {"httpPort": <port>, "v1Enabled": false,
"forceBasicAuth": <bool>}, puis exposer ce port côté HAProxy avec un
health-check GET /v2/ + expect status 401 — jamais le health-check générique
(option httpchk seul), qui reçoit un 400 "Not a docker request" de Nexus sur ce type de
connecteur et fait passer le backend DOWN à tort.
5. Purger d'anciennes versions d'un composant interne :
curl -sk -u admin:'<mot de passe>' -X DELETE \
"https://10.100.2.178:8081/service/rest/v1/components/<id-composant>"
Procédure de déploiement¶
Non applicable au sens "redéployer depuis zéro" — pas de code. La procédure manuelle ci-dessus
couvre l'intégralité du cycle de vie opérationnel (créer un dépôt, y pousser un paquet, l'exposer).
Côté consommation, un client type suit ce chemin : dépôt Nexus créé → exposé via
repo.infra.infra (règle DFW X-to-repo déjà existante à vérifier avant d'en créer une nouvelle,
voir nsx.md) → fichier .repo/config containerd déployé côté client avec
proxy=_none_ si le proxy Squid global est actif (voir baseline-config.md).
Contrôle de santé / Vérification¶
# Liste complète des dépôts (lecture seule)
curl -sk -u admin:'<mot de passe>' http://10.100.2.178:8081/service/rest/v1/repositories
# Test d'un dépôt yum (doit répondre 200)
curl -sk -o /dev/null -w '%{http_code}\n' \
https://repo.infra.infra/repository/<nom>/repodata/repomd.xml
# Test d'un registre Docker à port dédié (401 attendu = fonctionnel, PAS 400)
curl -sk -o /dev/null -w '%{http_code}\n' https://repo.infra.infra:8083/v2/
Vérifié en direct le 2026-07-19 : 25 dépôts listés via l'API, tous online. Comptage par type —
14 yum (9 proxy + 5 hosted dont indio-custom/strangebee-rpm/grafana-rpm), 5 raw hosted, 6
docker (1 path-based + 4 proxy à port dédié + 1 hosted), 1 pypi, 1 npm — total 25 confirmé par
comptage direct (len(repositories)).
Points d'attention¶
- Aucune sauvegarde/versionnage de la configuration Nexus elle-même (dette technique) : toute la configuration (25 dépôts, réglages proxy, credentials upstream) n'existe que dans la base Nexus, sans export/IaC — une perte de cette VM impliquerait une reconstruction manuelle complète. Cette page sert de meilleur inventaire de référence disponible à ce jour, mais n'est pas un substitut à un vrai mécanisme de sauvegarde.
- Single point of failure : un seul serveur Nexus, pas de cluster/HA. Toute panne bloque simultanément les mises à jour système de toute la flotte, les images Docker du cluster Kubernetes, et l'installation de tout logiciel tiers.
- Rate-limit anonyme Docker Hub sur
docker-proxy/dockerhub: Nexus a un circuit-breaker (httpClient.autoBlock) qui réagit aux rejets 429 de Docker Hub (limite anonyme ~100 pulls/6h, partagée par toute l'IP de sortie du lab). Un compte Docker Hub authentifié a été configuré surdocker-proxyaprès avoir rencontré ce problème en conditions réelles — les registresk8s-proxy/ghcr-proxy/quay-proxyn'ont pas de limite anonyme équivalente connue. - Health-check HAProxy générique incompatible avec les connecteurs Docker :
option httpchkseul (sansGET /v2/+expect status 401) fait passer un backend Docker Nexus fonctionnel pourDOWN— déjà documenté et corrigé pour les 5 registres Docker, à reproduire pour tout futur dépôt Docker exposé de la même façon. proxy=_none_obligatoire sur chaque section.repocliente dès qu'un proxy Squid global est actif (RPMindio-baseline-config) — sinon le proxy intercepte aussi le trafic vers Nexus et le casse (domaine non whitelisté et/ou zone non routable depuis DMZ). Voir baseline-config.md.- Piège de diagnostic : forcer un en-tête
Hostsans port dans un testcurlcontre un registre Docker à port dédié fait répondre à Nexus unWWW-Authenticate realm=sans port, laissant croire à tort à un bug d'authentification — toujours laisser le client déduire leHostcomplet (avec port) comme le ferait un vrai client Docker/containerd. - Les identifiants admin Nexus (
admin/mot de passe fleet-wide) restent la seule voie d'administration — pas de compte de service dédié en lecture seule pour l'automatisation (contrairement à d'autres briques comme Grafana/Zabbix qui ont des tokens scoping).