Une vulnérabilité de sécurité dans Gitea permet l’accès non authentifié à des images de conteneurs censées être privées lorsque le registre de conteneurs intégré est activé. Le problème, relayé publiquement après une alerte reprise notamment par The Hacker News et documenté par l’éditeur dans son advisory officiel, touche directement les déploiements self-hosted utilisés comme forge logicielle, registre OCI et point d’intégration CI/CD. Pour les équipes DevOps, la gravité dépasse la simple fuite d’artefacts : une image privée peut contenir des dépendances internes, des binaires non publiés, des secrets embarqués, des identifiants techniques, des URL d’API internes, voire des couches révélant la structure d’un pipeline de build. Dans un contexte de supply chain, Gitea peut alors devenir un point de fuite transversal. Le score CVSS exact dépend de l’advisory et de l’environnement exposé, mais l’impact opérationnel est élevé dès lors que le registre est accessible depuis Internet ou un réseau tiers.

Le scénario type est simple : une organisation héberge Gitea sur une instance VPS, un cluster Kubernetes ou une VM chez OVH, Scaleway, o2switch ou un autre hébergeur, active le registre de conteneurs pour publier des images destinées à des environnements internes, puis suppose que le marquage “privé” suffit à empêcher toute consultation externe. La faille invalide cette hypothèse. Un attaquant capable d’interroger le registre peut récupérer des manifests, des couches d’images et des métadonnées associées sans disposer d’un compte légitime, selon les conditions décrites par l’éditeur. L’exposition est particulièrement critique pour les équipes qui stockent dans leurs images des fichiers de configuration, des certificats de test, des scripts d’initialisation ou des outils internes non durcis.

La source originale à privilégier reste l’avis de sécurité officiel de Gitea, qui précise le périmètre exact, les branches corrigées et les versions de remédiation. Les équipes françaises gagneront aussi à surveiller les publications du CERT-FR si l’incident fait l’objet d’une reprise ou d’une synthèse sectorielle, notamment pour les organismes publics et opérateurs d’importance. En pratique, tout déploiement Gitea avec registre activé doit être considéré comme potentiellement exposé jusqu’à vérification de la version effective et application du correctif.

Versions affectées

Le point essentiel est que la vulnérabilité ne concerne pas l’ensemble des usages de Gitea de manière uniforme, mais les instances sur lesquelles la fonctionnalité de registre de conteneurs est activée. Le périmètre exact doit être confirmé à partir de l’advisory officiel de Gitea correspondant à cette faille, avec le ou les identifiants CVE associés. Lorsque l’éditeur publie plusieurs branches de maintenance, il faut vérifier la version majeure et mineure réellement en production, y compris sur les nœuds secondaires, les images de conteneur dérivées et les déploiements Helm.

De manière opérationnelle, les équipes doivent inventorier :

  • la version de Gitea effectivement déployée ;
  • l’activation du registre via la configuration [packages] ou les options liées au container registry ;
  • l’exposition réseau du service, directe ou via reverse proxy ;
  • la présence d’images marquées privées dans les namespaces utilisateurs ou organisations ;
  • les forks, appliances ou images Docker internes embarquant une version vulnérable.

Dans l’attente de la nomenclature exacte issue de l’advisory, la règle de sécurité est la suivante : toute version de Gitea antérieure à la version corrigée publiée par l’éditeur et exécutant le registre de conteneurs doit être traitée comme vulnérable. Les branches corrigées sont celles explicitement listées par Gitea dans l’avis officiel. Il ne faut pas supposer qu’une branche mineure est sûre au seul motif qu’elle est récente : plusieurs organisations conservent des paquets reconstruits ou des images OCI mises en cache qui restent sur une révision vulnérable.

Pour identifier précisément la version locale :

gitea --version

Dans un déploiement conteneurisé :

docker inspect --format='{{.Config.Image}}' gitea
docker exec -it gitea /usr/local/bin/gitea --version

Avec docker compose :

docker compose ps
docker compose exec gitea /usr/local/bin/gitea --version

Sur Kubernetes :

kubectl get pods -n gitea
kubectl exec -n gitea deploy/gitea -- /usr/local/bin/gitea --version
kubectl get deploy -n gitea gitea -o jsonpath='{.spec.template.spec.containers[0].image}'

Il faut également vérifier que le registre est bien activé. Selon la méthode d’installation, cela peut apparaître dans le fichier app.ini, dans des variables d’environnement ou dans les valeurs Helm :

grep -n "packages" /etc/gitea/app.ini
grep -n "container" /etc/gitea/app.ini

Exemple de configuration à auditer :

[packages]
ENABLED = true

[service]
DISABLE_REGISTRATION = true

Le fait de désactiver l’inscription publique n’a aucun effet protecteur contre cette vulnérabilité si le registre lui-même expose des ressources sans authentification. C’est un point important : beaucoup d’équipes confondent contrôle d’accès au portail Git et contrôle d’accès aux endpoints OCI du registre.

Si l’éditeur a publié un identifiant CVE, il doit être référencé dans le suivi interne, les tickets de remédiation et les outils d’inventaire de vulnérabilités. De même pour le CVSS officiel lorsqu’il est disponible. Dans les environnements régulés, l’alignement entre l’identifiant CVE, la version installée et la date de patch est indispensable pour la traçabilité SSI.

Vecteur d’attaque

Le vecteur d’attaque est lié à la manière dont le registre de conteneurs de Gitea traite certaines requêtes OCI ou Docker Registry API pour des images privées. En temps normal, l’accès à une image privée doit nécessiter une authentification valide, souvent via un flux de type Bearer ou via des identifiants applicatifs. La faille introduit une rupture dans ce modèle : un client non authentifié peut obtenir tout ou partie des éléments nécessaires pour télécharger l’image ou ses couches, alors même que le dépôt est supposé privé.

Concrètement, un attaquant commence souvent par identifier le point d’entrée du registre. Celui-ci est fréquemment exposé derrière le même domaine que Gitea ou un sous-domaine dédié, avec des chemins de type :

  • /v2/
  • /v2/<namespace>/<image>/manifests/<tag>
  • /v2/<namespace>/<image>/blobs/<digest>

Dans un fonctionnement conforme, une requête non authentifiée sur une ressource privée doit renvoyer un refus, typiquement un 401 Unauthorized avec un en-tête WWW-Authenticate approprié, ou un 403 Forbidden selon l’implémentation. Dans le scénario vulnérable, la réponse peut au contraire permettre la récupération du manifeste ou des blobs. Cette différence est immédiatement exploitable par des outils standards de l’écosystème conteneur.

Exemple de vérification manuelle à effectuer depuis un poste de test, sans authentification :

curl -i https://gitea.exemple.fr/v2/
curl -i https://gitea.exemple.fr/v2/acme/api/manifests/latest
curl -i https://gitea.exemple.fr/v2/acme/api/tags/list

Si une ressource privée retourne un manifeste ou une liste de tags sans challenge d’authentification légitime, l’exposition est avérée. Les manifests OCI contiennent des informations structurantes sur l’image : digests, taille des couches, architecture, historique et parfois annotations. Même si seule une partie de ces données est exposée, elles peuvent suffire à confirmer l’existence d’un projet confidentiel ou à préparer une récupération complète.

Le risque réel apparaît lorsque l’attaquant télécharge les couches de l’image. Une couche OCI est souvent un artefact riche, contenant le système de fichiers résultant du build. Dans des chaînes CI/CD imparfaites, on y retrouve :

  • des fichiers .env oubliés ;
  • des variables injectées au build puis non nettoyées ;
  • des clés API de test ou des jetons de registry ;
  • des binaires internes ou des paquets propriétaires ;
  • des scripts de déploiement révélant la topologie cible ;
  • des certificats autosignés ou jeux de données de préproduction ;
  • des chemins internes, noms d’hôtes, endpoints et conventions d’exploitation.

Exemple de récupération avec un client OCI si l’accès n’est pas correctement protégé :

skopeo inspect docker://gitea.exemple.fr/acme/api:latest
skopeo copy docker://gitea.exemple.fr/acme/api:latest dir:/tmp/api-image

Ou via l’outil Docker :

docker pull gitea.exemple.fr/acme/api:latest

Si cette commande aboutit sans authentification pour une image censée être privée, l’incident doit être traité comme une fuite de données potentielle. Il ne faut pas se limiter à corriger l’accès ; il faut aussi supposer que les artefacts ont pu être exfiltrés antérieurement.

Un scénario d’attaque crédible dans une PME ou une ETI française est le suivant. Une équipe plateforme héberge Gitea sur une VM chez OVHcloud. Le registre sert à stocker des images de microservices non encore publiés, construites par un runner CI interne. L’une des images contient un binaire maison et un fichier de configuration de staging avec un jeton JWT de test, plus une URL d’API interne. Un attaquant externe découvre l’instance par scan passif, teste /v2/, liste les tags, extrait l’image, récupère le jeton, puis pivote vers une API exposée sur un VPN mal segmenté ou une interface de préproduction accidentellement accessible. La faille initiale n’est “que” une lecture non authentifiée du registre, mais elle devient un facilitateur de mouvement latéral et d’attaque supply chain.

Autre scénario : une organisation utilise Gitea comme hub de build pour des images de base internes. Ces images embarquent des dépendances non publiques, des versions précises de bibliothèques et des scripts d’initialisation. Un concurrent, un cybercriminel ou un chercheur malveillant peut analyser ces couches pour cartographier la stack technique, identifier des composants obsolètes et préparer des attaques ciblées sur les versions réellement déployées. L’exposition du registre devient alors une source de renseignement technique de haute valeur.

Les environnements Kubernetes sont particulièrement sensibles. Beaucoup d’équipes publient des images dans Gitea puis les déploient via des secrets de type imagePullSecrets. Elles supposent que l’existence de ces secrets prouve que l’image n’est pas lisible publiquement. Ce n’est pas le cas si le registre renvoie les ressources sans contrôle effectif. De plus, certaines images de jobs CI contiennent encore des tokens de dépôt, des clés de service ou des artefacts de build intermédiaires. Une seule image exposée peut suffire à compromettre une chaîne entière.

Sur le plan technique, cette vulnérabilité rappelle plusieurs classes de défauts déjà observées dans les registres et proxys de paquets : confusion entre visibilité du projet et visibilité de l’artefact, validation incomplète des permissions sur des endpoints annexes, ou traitement spécial des manifests et blobs qui contourne le contrôle d’accès principal. On a déjà vu des problèmes analogues dans d’autres écosystèmes de supply chain, qu’il s’agisse de registres de paquets, de proxies Maven, de dépôts npm privés ou de services OCI auto-hébergés. La leçon récurrente est qu’un artefact privé ne l’est réellement que si chaque endpoint de lecture applique la même politique d’autorisation, sans exception.

Impact

L’impact immédiat est la fuite d’images de conteneurs privées. Mais pour les équipes DevOps et sécurité, la portée concrète est plus large.

Fuite d’artefacts et de propriété intellectuelle

Une image privée contient souvent plus qu’une simple application compilée. Elle peut embarquer des scripts d’administration, des composants propriétaires, des agents de supervision, des modules de licence, des fichiers de migration, des modèles ML, des paquets internes ou des outils de diagnostic. Leur exposition peut porter atteinte à la propriété intellectuelle et faciliter la reproduction de l’environnement applicatif.

Exposition de secrets embarqués

Même dans des organisations matures, les secrets “temporaires” finissent parfois dans les couches d’image : variable d’environnement injectée au build, fichier de configuration copié puis supprimé dans une couche ultérieure, token de package manager, certificat de test, clé SSH d’automatisation, mot de passe d’intégration legacy. Une image extraite par un tiers peut révéler des secrets encore valides. Le problème est aggravé par la nature append-only des couches : supprimer un fichier dans une couche récente ne l’efface pas des couches précédentes.

Renseignement sur la supply chain

Les manifests et métadonnées permettent d’identifier les versions de base, le système d’exploitation, l’architecture, les dépendances et parfois l’outillage de build. Pour un attaquant, c’est une mine d’informations sur la chaîne de fabrication logicielle. Il peut en déduire l’usage d’une image de base vulnérable, d’un composant obsolète ou d’un framework exposé à distance.

Préparation d’attaques ultérieures

Une image privée peut contenir des endpoints internes, des noms DNS, des chemins réseau, des configurations de queue, des noms de buckets, des identifiants de clusters ou des conventions de déploiement. Ces éléments réduisent fortement le coût de préparation d’une intrusion. Ils permettent aussi de cibler des campagnes de phishing technique plus crédibles contre les équipes d’exploitation.

Risque réglementaire et contractuel

Si des images privées contiennent des données personnelles, des secrets clients, des certificats ou des composants contractuellement sensibles, l’incident peut avoir une portée juridique. Les organisations soumises à des exigences de sécurité sectorielles ou à des clauses de confidentialité doivent documenter l’exposition, évaluer la fuite effective et notifier en interne selon leur procédure de gestion d’incident.

Comment patcher

La remédiation prioritaire consiste à mettre à jour Gitea vers la version corrigée indiquée dans l’advisory officiel de l’éditeur pour la branche concernée. Il faut viser explicitement la version de sécurité publiée, et non “la dernière version disponible” sans validation, afin de maîtriser la compatibilité applicative et les éventuels changements de schéma ou de configuration.

Avant mise à jour :

  • sauvegarder la base de données Gitea ;
  • sauvegarder le fichier app.ini ;
  • sauvegarder les volumes de données, dépôts Git et packages ;
  • préparer une fenêtre de maintenance si l’instance est critique ;
  • noter la version actuelle et l’image déployée.

Mise à jour via paquet système

Sur une distribution Debian ou Ubuntu si vous utilisez un paquet maintenu en interne ou un dépôt tiers proposant la version corrigée :

sudo apt update
sudo apt install --only-upgrade gitea
gitea --version

Sur RHEL, AlmaLinux, Rocky Linux ou Fedora :

sudo dnf upgrade gitea
gitea --version

Il faut vérifier que le paquet distribué par votre dépôt embarque bien la version de sécurité attendue. Dans certains environnements, le nom du paquet peut varier ou la version être retardée par rapport à l’upstream.

Mise à jour par binaire

Pour une installation à partir du binaire officiel, remplacer le binaire par la version corrigée depuis la source officielle de Gitea, puis redémarrer le service :

sudo systemctl stop gitea
sudo cp /usr/local/bin/gitea /usr/local/bin/gitea.bak
sudo install -m 0755 gitea /usr/local/bin/gitea
sudo systemctl start gitea
gitea --version

Après redémarrage, vérifier les logs :

sudo journalctl -u gitea -n 100 --no-pager

Mise à jour en conteneur Docker

Si Gitea est déployé via Docker ou Compose, remplacer l’image par la version corrigée publiée officiellement :

docker pull gitea/gitea:<version-corrigee>
docker compose down
docker compose up -d
docker compose exec gitea /usr/local/bin/gitea --version

Exemple de service Compose à ajuster :

services:
  gitea:
    image: gitea/gitea:<version-corrigee>
    restart: always
    volumes:
      - ./gitea:/data
    ports:
      - "3000:3000"
      - "2222:22"

Veiller à ne pas utiliser une balise flottante de type latest en production. Une version explicitement figée facilite l’audit et la reproductibilité.

Mise à jour sur Kubernetes

Pour un déploiement Kubernetes :

kubectl set image deployment/gitea gitea=gitea/gitea:<version-corrigee> -n gitea
kubectl rollout status deployment/gitea -n gitea
kubectl exec -n gitea deploy/gitea -- /usr/local/bin/gitea --version

Si vous utilisez Helm, mettre à jour les valeurs d’image puis déployer :

helm upgrade gitea gitea-charts/gitea \
  --namespace gitea \
  --set image.tag=<version-corrigee>

Après patch, tester explicitement qu’une image privée n’est plus téléchargeable sans authentification :

curl -i https://gitea.exemple.fr/v2/acme/api/manifests/latest
docker logout gitea.exemple.fr
docker pull gitea.exemple.fr/acme/api:latest

Le comportement attendu est un refus d’accès cohérent, avec challenge d’authentification ou interdiction explicite.

Une fois la mise à jour appliquée, il est recommandé de :

  • faire tourner un inventaire des images privées hébergées ;
  • rechercher les secrets potentiellement présents dans les couches ;
  • faire pivoter les tokens, clés et mots de passe retrouvés ;
  • réévaluer l’exposition du registre sur Internet ;
  • journaliser la date de remédiation et les preuves de test.

Détection

La détection doit répondre à deux questions : l’instance était-elle vulnérable, et des accès non autorisés ont-ils eu lieu avant le patch ? Les journaux de Gitea, du reverse proxy et des outils réseau sont essentiels. Si l’instance est derrière Nginx, Traefik, HAProxy ou un WAF, il faut corréler les requêtes vers les endpoints /v2/.

Indicateurs de compromission et d’exposition

  • requêtes anonymes réussies sur /v2/ ou sur des chemins /manifests/, /blobs/, /tags/list ;
  • codes HTTP 200 ou 307 sur des images privées sans session authentifiée ;
  • hausse inhabituelle du trafic sortant lié au téléchargement de blobs ;
  • user-agents d’outils comme docker, containerd, skopeo, crane, oras, curl depuis des IP inconnues ;
  • accès à des namespaces ou tags rarement utilisés ;
  • résolutions répétées de manifests multi-architecture ;
  • pics de bande passante sur le stockage objet ou le backend packages.

Exemple de recherche dans des logs Nginx :

grep ' /v2/' /var/log/nginx/access.log | grep ' 200 '
grep -E '/v2/.*/(manifests|blobs|tags/list)' /var/log/nginx/access.log
grep -E 'docker|containerd|skopeo|oras|crane|curl' /var/log/nginx/access.log

Exemple de filtre avec jq si les logs sont structurés en JSON :

jq 'select(.request_uri|test("^/v2/")) | {time,remote_addr,status,request_uri,user_agent}' /var/log/gitea-registry.json

Si vous centralisez les événements dans un SIEM, une règle simple consiste à alerter sur toute requête non authentifiée menant à un 200 sur un chemin du registre associé à un namespace privé. Il est utile de comparer la liste des images privées connues avec les journaux de téléchargement sur une période couvrant au minimum la date de publication de la faille et, idéalement, plusieurs semaines antérieures.

Vérification active

Une détection active peut être menée en auditant un échantillon d’images privées depuis un poste sans session ni identifiants. Les tests doivent être réalisés de manière contrôlée, documentée et limitée aux artefacts de l’organisation.

curl -I https://gitea.exemple.fr/v2/acme/interne/manifests/prod
curl -I https://gitea.exemple.fr/v2/acme/interne/tags/list

Résultat attendu après correctif : refus d’accès. Résultat problématique : réponse positive ou redirection menant au contenu.

Analyse post-incident

Si l’instance était vulnérable et exposée sur Internet, la posture prudente consiste à traiter l’événement comme une fuite potentielle. Les actions minimales sont :

  • inventorier les images privées présentes pendant la période d’exposition ;
  • extraire et scanner ces images pour identifier les secrets ;
  • faire pivoter les secrets retrouvés, même sans preuve de téléchargement ;
  • vérifier les journaux de CI/CD pour détecter des usages anormaux de tokens ;
  • contrôler les accès aux environnements de staging et de production ;
  • rechercher des connexions ultérieures depuis des IP ayant interrogé le registre.

Exemple de scan de secrets dans une image récupérée à des fins d’audit :

trivy image --scanners secret gitea.exemple.fr/acme/api:latest
docker save gitea.exemple.fr/acme/api:latest -o /tmp/api.tar

On peut compléter avec des outils comme gitleaks ou des scanners de configuration pour inspecter les couches exportées. L’objectif n’est pas seulement de confirmer la fuite, mais d’évaluer ce qu’un tiers aurait pu apprendre ou réutiliser.

Mitigation

Quand le patch ne peut pas être appliqué immédiatement, il faut réduire l’exposition du registre. Aucune mitigation ne remplace la mise à jour, mais plusieurs mesures peuvent limiter le risque à court terme.

Désactiver temporairement le registre de conteneurs

Si le service n’est pas indispensable à court terme, la mesure la plus sûre est la désactivation temporaire de la fonctionnalité concernée dans la configuration Gitea, suivie d’un redémarrage contrôlé. Vérifier ensuite qu’aucun endpoint /v2/ n’est encore servi.

Exemple de paramètre à revoir dans app.ini :

[packages]
ENABLED = false

Puis :

sudo systemctl restart gitea

Restreindre l’accès réseau

Limiter le registre à un VPN, à une liste d’IP de confiance ou à un segment interne reste une mesure efficace, surtout pour les plateformes hébergées chez un fournisseur cloud ou un hébergeur français. Si l’usage du registre est strictement interne, il ne devrait pas être exposé à Internet.

Exemple Nginx avec filtrage IP :

location /v2/ {
    allow 10.0.0.0/8;
    allow 192.168.0.0/16;
    deny all;
    proxy_pass http://127.0.0.1:3000;
}

Exemple avec un pare-feu Linux :

sudo ufw allow from 10.0.0.0/8 to any port 443
sudo ufw deny 443/tcp

La règle doit être adaptée au reverse proxy et à l’architecture réelle. Un filtrage mal placé peut laisser le service accessible par un autre point d’entrée.

Forcer une authentification au niveau du reverse proxy

Dans certains environnements, on peut imposer une authentification supplémentaire sur le chemin /v2/ via le reverse proxy ou un middleware d’accès. Cela ne corrige pas la faille applicative, mais ajoute une barrière.

Exemple Nginx avec authentification basique temporaire :

location /v2/ {
    auth_basic "Restricted Registry";
    auth_basic_user_file /etc/nginx/.htpasswd;
    proxy_pass http://127.0.0.1:3000;
}

Cette mesure peut perturber certains clients OCI selon le mode d’authentification attendu. Elle doit être testée avant déploiement large.

Désindexer et cloisonner

Si l’instance est publique pour la forge Git mais pas pour le registre, utiliser un sous-domaine distinct pour le registre facilite le cloisonnement réseau, la journalisation et les politiques WAF. Il est aussi utile d’empêcher l’énumération indirecte via des pages projet, badges ou documentation publique mentionnant des noms d’images internes.

Rotation préventive des secrets

Si des images privées ont pu être exposées, faire pivoter de manière préventive :

  • tokens d’accès au registre ;
  • tokens CI/CD ;
  • clés API de staging et de test ;
  • secrets injectés au build ;
  • certificats techniques non publics ;
  • mots de passe de services embarqués par erreur.

La rotation doit être priorisée selon la criticité et la présence effective dans les couches d’image.

Perspective écosystème et comparaison avec des failles antérieures

Cette vulnérabilité s’inscrit dans une tendance plus large : les plateformes de développement se transforment en hubs de supply chain complets, combinant Git, CI/CD, packages, images OCI, artefacts de release et authentification fédérée. Chaque brique supplémentaire augmente la surface d’attaque. Une forge comme Gitea n’est plus seulement un serveur Git ; c’est un composant de production qui manipule des secrets, des binaires et des dépendances critiques.

L’écosystème a déjà connu plusieurs incidents où un registre ou un dépôt de packages exposait involontairement des artefacts privés. Les causes récurrentes sont connues :

  • contrôle d’accès appliqué au projet mais pas à tous les endpoints d’artefacts ;
  • erreurs de logique sur les redirections ou les manifests ;
  • mauvaise séparation entre ressources publiques et privées ;
  • supposition erronée que l’authentification côté UI couvre aussi les API machine-to-machine ;
  • tests de sécurité insuffisants sur les flux OCI spécifiques.

Pour les RSSI et responsables plateforme, la leçon stratégique est claire : les registres d’artefacts doivent être traités comme des coffres techniques, avec le même niveau d’exigence qu’un gestionnaire de secrets ou qu’un dépôt de code sensible. Cela implique :

  • inventaire précis des fonctionnalités activées ;
  • durcissement des reverse proxies ;
  • tests d’accès anonymes réguliers ;
  • scans de secrets dans les images avant publication ;
  • segmentation réseau ;
  • surveillance des journaux d’accès aux artefacts ;
  • politique de mise à jour rapide des composants de forge.

Dans les environnements self-hosted, cette discipline est souvent plus importante encore que sur les plateformes SaaS, car l’équipe interne cumule les rôles d’éditeur, d’exploitant et de responsable sécurité. Une faille de registre mal gérée peut transformer une simple plateforme de développement en source de fuite massive pour l’ensemble de la chaîne logicielle.

Le correctif de Gitea doit donc être appliqué rapidement, puis complété par une revue de configuration et une vérification des images privées déjà publiées. Les équipes qui veulent renforcer durablement leur hygiène de plateforme peuvent compléter cette remédiation par des mesures de durcissement et de contrôle décrites dans la catégorie /categorie/pratiques, notamment sur la gestion des secrets, l’exposition réseau des services internes et la sécurité de la supply chain applicative.

Retour aux actualités