Aller au contenu principal

Proxy de dépendances

Le proxy de dépendances est un cache pull-through pour les registres publics, exploité par organisation. Vous dirigez docker, npm, pip, uv, dnf, Maven, NuGet, go, helm ou cargo vers le chemin du proxy de votre organisation sur frem.sh, et fremforge récupère les contenus depuis le registre public en votre nom :

  • Le premier pull d’une couche d’image ou d’un fichier de paquet est récupéré depuis la source en amont et vérifié par rapport au hash publié par celle-ci. Si votre organisation active l’analyse antivirus, il est aussi analysé par ClamAV pendant qu’il vous est transmis. Un fichier qui échoue à une vérification n’est jamais servi ni stocké.
  • Les pulls suivants sont servis depuis le stockage de fremforge dans l’UE, pour chaque membre de votre organisation.
  • Si la source en amont est en panne, la dernière copie valide est servie. Les tags, les packuments npm, les index PyPI, les repomd.xml RPM, les maven-metadata.xml Maven, les listes de versions et enregistrements NuGet, les listes de versions Go et @latest, les fichiers index.yaml Helm et les fichiers d’index Cargo reçoivent une réponse tirée de la dernière copie valide, avec l’en-tête de réponse X-Fremforge-Proxy: stale. Voir Pannes en amont.
  • La politique de paquets de votre organisation et les vérifications des paquets malveillants sont évaluées pour les paquets et images passant par le proxy, et chaque pull est enregistré dans le journal d’accès au registre. Depuis le 2026-10-11, un paquet ou une image connu comme malveillant est refusé pour toutes les organisations, et une politique en mode Appliquer refuse ce qui l’enfreint ; voir Ce qui est appliqué aujourd’hui.

Le proxy est en lecture seule. Vous ne pouvez pas y pousser de contenu.

Disponibilité et activation

Le proxy est disponible dans toutes les formules et activé par défaut pour chaque organisation. Un propriétaire de l’organisation peut le désactiver ou modifier ses paramètres sous Build et déploiement › Proxy de dépendances ou via l’API (un jeton avec la portée policy:write) :

curl -X PUT -H "Authorization: Bearer $FREMFORGE_TOKEN" -H 'Content-Type: application/json' \
  -d '{"enabled": true, "malware_scan": true}' \
  https://frem.sh/_app/api/v1/orgs/<org>/dependency-proxy

Une organisation qui n’a jamais enregistré ces paramètres a la configuration par défaut : le proxy activé, et les runners hébergés qui passent par lui (voir Runners hébergés). Dès qu’un propriétaire les enregistre, l’organisation conserve exactement ce qui a été enregistré.

L’analyse antivirus (malware_scan) est désactivée par défaut. Lorsqu’elle est activée, chaque couche d’image, fichier de paquet et chart que votre organisation récupère via le proxy est analysé par ClamAV, y compris lorsqu’une autre organisation l’a mis en cache en premier sans l’analyser : cette copie est analysée une fois, la première fois qu’une organisation qui analyse l’utilise, et son verdict vaut ensuite pour tout le monde. Les fichiers de paquets et les charts sont analysés avant d’être servis ; pour les couches d’images, voir Images de conteneurs. Les paquets connus comme malveillants (avis OSV MAL-*) sont détectés pour toutes les organisations, quelle que soit la valeur de ce paramètre. La même page et le même endpoint définissent block_until_scanned pour les images de conteneurs, qui nécessite l’analyse antivirus (voir Images de conteneurs), et retention_days (de 7 à 365, 90 par défaut ; voir Rétention). Un GET sur l’endpoint indique si le proxy est activé, votre utilisation et votre plafond.

Authentification

Utilisez les mêmes identifiants que pour le registre de paquets :

  • Personnes : un jeton d’accès personnel avec read:package (ou le jeton de courte durée que fremforge auth fournit à vos outils). Vous devez être membre de l’organisation.
  • CI : le jeton de job Forgejo Actions. Un jeton de job ne récupère que via l’organisation de son propre dépôt ; le proxy de toute autre organisation le refuse. Les jobs issus de pull requests de forks peuvent récupérer, mais jamais via une source en amont qui utilise les identifiants propres à votre organisation.
  • CI externe, hôtes de build et pull secrets : un robot de registre avec un accès en lecture. Son jeton est un identifiant read:package comme un autre.
  • Analyse des dépendances de fremforge : l’analyse d’atteignabilité lit les tarballs npm, les wheels PyPI et les JAR Maven via le proxy de votre organisation, par une route interne que seul le service d’analyse atteint. La vérification des licences, la vérification de l’âge de publication et le graphe de modules Go lisent de la même façon les métadonnées des registres (npm, PyPI, Go, Maven Central, RubyGems et Packagist). Cet accès est en lecture seule, ne peut pas être utilisé depuis l’extérieur de fremforge, et est soumis à votre politique de paquets, à votre cache et à votre plafond de stockage comme tout autre pull.

Runners hébergés

Par défaut, les jobs sur les runners hébergés par fremforge installent via le proxy de votre organisation (runner_pull_through, activé par défaut). Au démarrage de chaque job, le runner dirige npm, pip et uv vers le proxy, ainsi que Go, Maven, NuGet et la base de vulnérabilités de Trivy pour les écosystèmes listés dans runner_pull_through_ecosystems (les quatre par défaut). Le job s’authentifie avec son propre jeton de job, de sorte que chaque installation passe par votre politique de paquets et vos vérifications antivirus et figure dans votre journal d’accès avec le dépôt du job. Si le proxy est injoignable au démarrage du job, le job installe depuis les registres publics via le proxy de sortie de fremforge, et ce repli est journalisé. Définissez runner_pull_through à false pour désactiver ce comportement. Voir aussi Caches de paquets.

Images de conteneurs. Pour ces mêmes jobs, le démon Docker du job récupère aussi les images de conteneurs via le proxy de votre organisation : docker pull, docker run, le FROM d’un Dockerfile, ainsi que les conteneurs du job et de services: que le runner démarre. Rien ne change dans le workflow ; les noms d’images restent tels quels (ghcr.io/..., quay.io/...) et chaque pull est mis en cache pour votre organisation et enregistré dans votre journal d’accès.

  • Pour toutes les organisations : ghcr.io, quay.io, mcr.microsoft.com, registry.k8s.io, public.ecr.aws et registry.access.redhat.com.
  • Uniquement avec l’identifiant propre à votre organisation : docker.io, registry.redhat.io et registry.connect.redhat.com ne passent par le proxy qu’une fois qu’un propriétaire a ajouté l’identifiant de votre organisation pour ce registre sous Identifiants des registres en amont. Les jobs issus de pull requests de forks ne l’utilisent jamais, si bien que leurs pulls depuis ces registres vont directement au registre. Sans identifiant, les pulls Docker Hub sont anonymes et soumis aux limites de débit de Docker Hub.
  • Repli : le proxy est vérifié au début de chaque job. S’il est injoignable, le job récupère chaque image directement depuis le registre, comme avant, et le repli est journalisé. Un pull isolé que le proxy ne peut pas servir bascule également vers le registre.
  • Un cache, pas un contrôle : pour les images sur ce chemin, un pull que votre politique de paquets ou la vérification antivirus refuse bascule lui aussi vers le registre. Lorsqu’un refus doit arrêter le pull, récupérez explicitement depuis frem.sh/<org>/dependency-proxy/<registry>/<image> (ci-dessous).
  • Non couvert : un builder docker buildx qui utilise le driver docker-container exécute son propre BuildKit et récupère directement. Le driver par défaut docker passe par le proxy.

Images de conteneurs

docker login frem.sh            # a token with read:package, as for the registry
docker pull frem.sh/<org>/dependency-proxy/<upstream>/<image>:<tag>

Par exemple :

docker pull frem.sh/acme/dependency-proxy/quay.io/prometheus/busybox:latest

En CI :

echo "$GITHUB_TOKEN" | docker login frem.sh -u x --password-stdin

Les sources en amont depuis lesquelles vous pouvez récupérer :

Source en amontIdentifiantsStatut
docker.ioL’identifiant Docker Hub propre à votre organisation, obligatoire (ci-dessous)Vérifié de bout en bout
registry.k8s.io, mcr.microsoft.com, public.ecr.aws, registry.access.redhat.comAucunVérifié de bout en bout
ghcr.io, quay.ioAucun pour les images publiques. L’identifiant propre à votre organisation, facultatif, pour les images privées (ci-dessous)Images publiques : vérifié de bout en bout. Images privées avec votre identifiant : pris en charge, pas encore vérifié
registry.redhat.io, registry.connect.redhat.comLe compte de service de registre Red Hat de votre organisation, obligatoirePris en charge, pas encore vérifié : cela nécessite un compte Red Hat, dont fremforge ne dispose pas pour ses tests

Tout autre hôte est refusé : le proxy ne récupère jamais une URL libre.

Le proxy ne liste pas les tags d’une image. Renovate hébergé propose malgré tout des mises à jour pour une image que vous référencez via ce chemin, comme le FROM frem.sh/<org>/dependency-proxy/docker.io/library/python:3.14-slim d’un Dockerfile. Il recherche les nouveaux tags et digests auprès du registre en amont et les écrit dans votre référence, qui conserve le chemin du proxy. Vos builds continuent de récupérer via le proxy, de sorte que les contrôles de pull décrits ci-dessous s’appliquent toujours.

Les charts Helm stockés comme artefacts OCI sur ces sources en amont passent par le même chemin (Helm 3.8 ou ultérieur) :

echo "$FREMFORGE_TOKEN" | helm registry login frem.sh -u <user or robot name> --password-stdin
helm pull oci://frem.sh/<org>/dependency-proxy/ghcr.io/prometheus-community/charts/prometheus --version 29.36.1

Pour les charts d’un dépôt Helm classique (index.yaml), voir Charts Helm.

Un tag est revérifié auprès de la source en amont au plus une fois par minute, par une requête HEAD, que Docker Hub ne décompte pas de sa limite de pulls. Les couches d’images passent par les mêmes contrôles de pull que les images poussées dans votre registre : avec l’analyse antivirus activée, les couches infectées ou impossibles à analyser sont refusées ; il en va de même des images au-delà de votre seuil de CVE si vous avez défini block_pull. Les paquets malveillants à l’intérieur d’une image sont traités ci-dessous. Par défaut, une image est servie pendant que ses couches sont analysées. Avec block_until_scanned, le premier pull d’une image pas encore analysée, ou d’une couche par digest, renvoie 503 avec Retry-After jusqu’à la fin de l’analyse, en général en quelques secondes. Une couche qu’une analyse a trouvée infectée est refusée à toutes les organisations.

Pendant qu’une requête récupère et analyse une couche, les autres pulls de la même couche l’attendent. Si cela prend plus d’environ quatre minutes, ils reçoivent 503 avec Retry-After: 30 (this layer is still being fetched and malware-scanned by another request), et une nouvelle tentative est servie une fois la couche stockée. Une analyse qui manque de temps n’est pas un verdict : la couche est retenue, comme une couche impossible à analyser, mais aucun résultat n’est enregistré. Elle est mise en file d’attente pour l’analyseur de couches en arrière-plan de fremforge, qui dispose d’un budget de temps plus long, et un pull ultérieur est servi une fois que cette analyse est réussie.

Un fichier de paquet dont l’analyse dépasse son délai (npm, PyPI, RPM, Maven, NuGet, Go, Cargo ou un chart Helm) est traité de la même façon : cette requête est refusée, aucun résultat n’est enregistré, et le fichier est mis en file pour le même analyseur en arrière-plan. Une requête ultérieure est servie une fois cette analyse réussie, ou refusée si elle trouve le fichier infecté. Un RPM reste propre à votre organisation pendant cette attente : il est stocké et analysé uniquement dans l’espace de votre organisation, et une autre organisation qui récupère le même RPM récupère et analyse sa propre copie.

Limites de taille. Le proxy ne sert aucune couche ni aucun fichier de plus de 10 Gio (403). ClamAV lit au plus 4 Gio d’un même fichier ; avec l’analyse antivirus activée, une couche ou un fichier plus volumineux ne peut donc pas être entièrement analysé et est retenu, sauf une couche de modèle safetensors ou GGUF qui passe sa vérification de format (voir Analyse des modèles).

Paquets malveillants dans les images en cache

Les paquets de langage contenus dans chaque image que votre organisation met en cache (paquets npm, PyPI, Maven, Go, crates, RubyGems, NuGet, Packagist, Pub et Hex) sont comparés aux avis OSV sur les paquets malveillants (MAL-*), la même vérification que celle qui s’exécute sur les images que vous poussez :

  • après la première mise en cache de l’image, en général dans le cycle d’analyse suivant (environ 15 minutes), et
  • à nouveau chaque jour, de sorte qu’un avis publié après que vous avez récupéré l’image est quand même détecté.

Une correspondance est un résultat critique dans Sécurité › Résultats › Dépendances › Logiciels malveillants, listé comme une image nommée <org>/dependency-proxy/<upstream>/<repository> avec le digest qui la contient, et elle est envoyée aux personnes qui reçoivent vos alertes de sécurité. L’ignorer fonctionne comme pour tout résultat de logiciel malveillant, avec un motif et une date de fin facultative. Le résultat se ferme de lui-même une fois que votre organisation ne met plus cette image en cache, par exemple après sa suppression par la rétention.

Le pull d’une image comportant un résultat ouvert et non ignoré est refusé avec 403 DENIED, comme pour les images poussées, et journalisé dans votre journal d’accès comme blocked avec le motif image_malware. Refusé depuis le 2026-10-11 ; auparavant, un tel pull était servi et enregistré comme would_block.

Identifiants des registres en amont

Docker Hub (docker.io) et les registres Red Hat (registry.redhat.io et registry.connect.redhat.com, les images certifiées des partenaires de Red Hat) ne sont récupérés via le proxy de dépendances qu’avec le compte propre à votre organisation. Pour GitHub (ghcr.io) et Quay (quay.io), un identifiant est facultatif : sans identifiant, ils sont récupérés anonymement, ce qui sert les images publiques ; ajoutez-en un pour récupérer vos images privées. Un propriétaire de l’organisation ajoute l’identifiant sous Build et déploiement › Proxy de dépendances › Identifiants des registres en amont, ou via l’API à /_app/api/v1/orgs/<org>/dependency-proxy/upstreams. Formes acceptées :

  • Docker Hub : votre nom d’utilisateur plus un jeton d’accès personnel, ou un jeton d’accès d’organisation avec le nom de l’organisation Docker comme nom d’utilisateur.
  • Red Hat : un compte de service de registre (un nom d’utilisateur tel que 1234567|ci, plus son jeton), ou son pull secret OpenShift. Ajoutez-le une fois par registre Red Hat depuis lequel vous récupérez ; chacun ne conserve que sa propre entrée du pull secret.
  • GitHub (ghcr.io) : votre nom d’utilisateur GitHub plus un jeton d’accès personnel (classique) avec read:packages.
  • Quay (quay.io) : un compte robot (organisation+robot) plus son jeton, ou l’entrée quay.io d’un pull secret Red Hat.

Les registres Red Hat avec votre identifiant, ainsi que les images privées ghcr.io et quay.io, sont pris en charge mais pas encore vérifiés de bout en bout : fremforge les a testés avec le flux de jetons des registres, mais ne dispose d’aucun compte pour récupérer des images.

Une fois qu’un identifiant ghcr.io ou quay.io est stocké, chaque pull depuis ce registre l’utilise, images publiques comprises, et est mis en cache pour votre organisation uniquement. Les jobs issus de pull requests de forks ne l’utilisent jamais : ils récupèrent ces deux registres anonymement et n’obtiennent donc que les images publiques.

Le secret est stocké chiffré et utilisé uniquement par le proxy de dépendances, pour les pulls de votre propre organisation. Il n’est plus jamais affiché ; vous voyez une empreinte à la place. Tester la connexion le vérifie et, pour Docker Hub, indique combien de pulls il vous reste dans votre fenêtre de limite actuelle.

Les images récupérées avec votre identifiant sont mises en cache pour votre organisation uniquement, et jamais servies à une autre organisation. Les jobs issus de pull requests de forks ne peuvent pas récupérer via un registre qui exige votre identifiant. Si l’identifiant expire, les images déjà en cache continuent d’être servies et le statut affiche Refusé par le registre. Sans identifiant, un pull Docker Hub est refusé avec un message qui l’indique.

Kubernetes et OpenShift

Faites pointer un miroir vers le proxy. Pour containerd, dans /etc/containerd/certs.d/quay.io/hosts.toml :

server = "https://quay.io"

[host."https://frem.sh/v2/<org>/dependency-proxy/quay.io"]
  capabilities = ["pull", "resolve"]
  override_path = true

Pour OpenShift, avec un ImageTagMirrorSet (et un ImageDigestMirrorSet pour les digests) :

apiVersion: config.openshift.io/v1
kind: ImageTagMirrorSet
metadata:
  name: frem-sh-dependency-proxy
spec:
  imageTagMirrors:
    - source: quay.io
      mirrors:
        - frem.sh/<org>/dependency-proxy/quay.io

Le pull secret du cluster nécessite un jeton frem.sh avec read:package.

npm

.npmrc dans votre projet ou votre répertoire personnel :

registry=https://frem.sh/_app/proxy/<org>/npm/
//frem.sh/_app/proxy/<org>/npm/:_authToken=${FREMFORGE_TOKEN}

Utilisez ensuite npm, pnpm ou yarn comme d’habitude. Les paquets proviennent de registry.npmjs.org, paquets à portée (scoped) compris. Chaque tarball doit correspondre à l’integrity (sha512) que le registre npm publie pour lui avant d’être servi.

npm audit et pnpm audit fonctionnent avec le proxy. Ils reçoivent leur réponse de la propre copie de la base d’avis OSV de fremforge, de sorte que votre liste de dépendances n’est envoyée à aucun service d’avis tiers. Les clients pris en charge et le format de réponse sont décrits sous npm audit et pnpm audit via le proxy de dépendances.

Pour combiner le proxy avec les paquets que votre organisation publie dans son propre registre, dirigez votre portée vers le registre et tout le reste vers le proxy :

registry=https://frem.sh/_app/proxy/<org>/npm/
@<your-scope>:registry=https://frem.sh/api/packages/<org>/npm/
//frem.sh/_app/proxy/<org>/npm/:_authToken=${FREMFORGE_TOKEN}
//frem.sh/api/packages/<org>/npm/:_authToken=${FREMFORGE_TOKEN}

PyPI

pip install --index-url "https://<user>:${FREMFORGE_TOKEN}@frem.sh/_app/proxy/<org>/pypi/simple/" requests
uv pip install --index-url "https://<user>:${FREMFORGE_TOKEN}@frem.sh/_app/proxy/<org>/pypi/simple/" requests

Ou dans pip.conf / uv.toml, avec l’identifiant dans un fichier netrc ou un trousseau plutôt que dans l’URL. L’index est servi dans les deux formats qu’utilisent les installeurs (HTML PEP 503 et JSON PEP 691), avec le sha256, le requires-python, le statut de retrait (yank) et les métadonnées PEP 658 de chaque fichier. Chaque fichier doit correspondre au sha256 que PyPI liste pour lui avant d’être servi.

Utilisez le proxy comme index unique. Ne l’ajoutez pas avec --extra-index-url (ou PIP_EXTRA_INDEX_URL, UV_EXTRA_INDEX_URL) à côté d’un autre index, ni un autre index à côté de lui : mélanger les index est ce qui rend possibles les attaques par confusion de dépendances, car l’installeur prend un nom dans n’importe quel index qui le propose. Pour installer les paquets que votre organisation publie dans son propre registre, lancez une installation séparée avec ce registre comme index unique et --no-deps, ou, avec uv, épinglez chacun d’eux à ce registre par son nom (explicit = true et [tool.uv.sources]).

RPM (dnf et yum)

Chaque dépôt est accessible à l’adresse

https://frem.sh/_app/proxy/<org>/rpm/<upstream>/<repository path>

Aujourd’hui, la source en amont est cdn-ubi.redhat.com, les dépôts Universal Base Image de Red Hat (UBI 8, 9 et 10 : BaseOS, AppStream et CodeReady Builder, pour chaque architecture que Red Hat publie), sous content/public/ubi/. Ils ne nécessitent aucun compte Red Hat. Un fichier .repo pour UBI 9 sur x86_64 :

[ubi-9-baseos-fremforge]
name=UBI 9 BaseOS through fremforge
baseurl=https://frem.sh/_app/proxy/<org>/rpm/cdn-ubi.redhat.com/content/public/ubi/dist/ubi9/9/x86_64/baseos/os
username=<user or robot name>
password=<token with read:package>
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

[ubi-9-appstream-fremforge]
name=UBI 9 AppStream through fremforge
baseurl=https://frem.sh/_app/proxy/<org>/rpm/cdn-ubi.redhat.com/content/public/ubi/dist/ubi9/9/x86_64/appstream/os
username=<user or robot name>
password=<token with read:package>
enabled=1
gpgcheck=1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release

dnf envoie username et password en authentification HTTP Basic. Utilisez un robot de registre pour les hôtes de build et les builds d’images, et rendez le fichier lisible uniquement par root (chmod 600), car il contient le jeton. Dans un build de conteneur, désactivez les dépôts propres à l’image pour l’installation (dnf --disablerepo='*' --enablerepo='*-fremforge' install …), afin que rien ne soit récupéré en contournant le proxy.

Ce que fait le proxy :

  • repodata/repomd.xml est revérifié auprès de la source en amont au plus toutes les 5 minutes et servi exactement tel que la source l’a publié. Un nouveau repomd.xml n’est accepté qu’une fois que le primary.xml qu’il désigne a été téléchargé et correspond au sha256 et à la taille que repomd.xml indique pour lui. D’ici là, et tant que la source en amont est en panne, le précédent est servi.
  • Les fichiers de métadonnées du dépôt (primary, filelists, other, updateinfo, modules, comps et leurs variantes sqlite) doivent correspondre au sha256 et à la taille indiqués dans repomd.xml.
  • Les paquets doivent correspondre au sha256 et à la taille indiqués dans primary.xml. Avec l’analyse antivirus activée, chaque paquet est aussi analysé à l’intérieur de sa charge utile compressée, et pas seulement comme fichier, et ses scripts d’installation sont analysés eux aussi. Un paquet que l’analyseur ne peut pas ouvrir est alors refusé, et non servi sans analyse.
  • Les signatures des paquets sont vérifiées par dnf lui-même (gpgcheck=1), avec la clé de Red Hat, exactement comme sans le proxy. Le proxy ne modifie jamais un paquet ni les métadonnées du dépôt.
  • Le contenu RPM est mis en cache pour votre organisation uniquement. Une autre organisation qui récupère le même paquet récupère et stocke sa propre copie. Ainsi, le même chemin de code pourra plus tard servir du contenu Red Hat récupéré avec votre propre abonnement Red Hat, qui ne doit jamais parvenir à un autre client.

Un dépôt qui utilise des sommes de contrôle autres que sha256 n’est pas servi. La mise en proxy de cdn.redhat.com avec votre certificat de droits Red Hat est prévue et pas encore disponible.

Maven

Maven Central est accessible à l’adresse

https://frem.sh/_app/proxy/<org>/maven/central/

Faites-en le miroir de tous les dépôts dans ~/.m2/settings.xml (ou le settings.xml qu’utilise votre build), avec l’identifiant dans un <server> de même id :

<settings>
  <servers>
    <server>
      <id>fremforge-proxy</id>
      <username><user or robot name></username>
      <password>${env.FREMFORGE_TOKEN}</password>
    </server>
  </servers>
  <mirrors>
    <mirror>
      <id>fremforge-proxy</id>
      <name>Maven Central through fremforge</name>
      <url>https://frem.sh/_app/proxy/<org>/maven/central/</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
</settings>

Maven envoie le nom d’utilisateur et le mot de passe en authentification HTTP Basic lorsque le proxy les demande. Pour utiliser le proxy avec les paquets que votre organisation publie dans son propre registre, écrivez <mirrorOf>*,!fremforge</mirrorOf> et déclarez votre registre (https://frem.sh/api/packages/<org>/maven) comme dépôt fremforge avec son propre <server>.

Gradle (settings.gradle.kts) :

dependencyResolutionManagement {
    repositories {
        maven {
            url = uri("https://frem.sh/_app/proxy/<org>/maven/central/")
            credentials {
                username = providers.environmentVariable("FREMFORGE_USER").get()
                password = providers.environmentVariable("FREMFORGE_TOKEN").get()
            }
        }
    }
}

Ce que fait le proxy :

  • maven-metadata.xml est revérifié auprès de Maven Central au plus toutes les 5 minutes. Une nouvelle copie n’est acceptée que si elle correspond au propre .sha1 de Maven Central. D’ici là, et tant que Maven Central est en panne, la copie précédente est servie. Ses .sha1, .md5, .sha256 et .sha512 sont calculés à partir de la copie qui vous est servie, de sorte qu’ils lui correspondent toujours.
  • Les artefacts (jars, POM, fichiers .module et tout autre fichier d’une version) doivent correspondre au .sha1 de Maven Central et, lorsque Maven Central en publie un, à son .sha256. Un fichier sans aucune des deux sommes de contrôle n’est pas récupéré. Avec l’analyse antivirus activée, chaque fichier est analysé pendant qu’il vous est transmis. Un jar est aussi ouvert : une entrée que l’analyseur ne peut pas décompresser lui-même (zstd) est décompressée pour lui, et un jar contenant une entrée qu’aucun des deux ne peut ouvrir (par exemple LZMA) est refusé plutôt que servi sans analyse.
  • Last-Modified correspond à l’heure à laquelle Maven Central a publié le fichier, y compris lorsqu’il provient du cache, de sorte que l’âge minimal de publication de Renovate et les autres outils qui le lisent voient la véritable date de publication. Un fichier que Maven Central a envoyé sans cet en-tête n’a aucun Last-Modified, jamais l’heure à laquelle fremforge l’a mis en cache. Un HEAD sur un fichier en cache répond 200 avec son Content-Length et ce Last-Modified.
  • Les fichiers de sommes de contrôle (.sha1, .sha256, …) et les signatures (.asc) des artefacts sont transmis sans modification. Vérifiez les signatures avec le plugin PGP de votre build, comme vous le feriez sans le proxy.
  • Les snapshots ne sont pas servis. Maven Central n’en publie pas, et un snapshot peut changer sous la même version. Une requête -SNAPSHOT reçoit la réponse 404, si bien que votre build passe au dépôt qui le possède.

Pour la politique de paquets, un paquet Maven est nommé groupId:artifactId. Sa date de publication est celle à laquelle Maven Central a publié le fichier, et sa licence est la <licenses> que déclare son POM. Une licence héritée d’un POM parent n’est pas recherchée ; elle compte donc comme non évaluée.

NuGet

La source de paquets est l’index de service du proxy :

https://frem.sh/_app/proxy/<org>/nuget/v3/index.json

nuget.config à côté de votre solution :

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <packageSources>
    <clear />
    <add key="fremforge" value="https://frem.sh/_app/proxy/<org>/nuget/v3/index.json" />
  </packageSources>
  <packageSourceCredentials>
    <fremforge>
      <add key="Username" value="<user or robot name>" />
      <add key="ClearTextPassword" value="%FREMFORGE_TOKEN%" />
    </fremforge>
  </packageSourceCredentials>
</configuration>

<clear /> fait du proxy la seule source, de sorte qu’aucun paquet n’est récupéré en le contournant. NuGet développe %FREMFORGE_TOKEN% à partir de l’environnement ; le jeton n’a donc pas besoin de figurer dans le fichier. Utilisez ensuite dotnet restore, dotnet add package <id> --version <version> ou nuget install comme d’habitude.

Ce que fait le proxy :

  • L’index de service ne liste que des ressources sur frem.sh : le contenu des paquets (flat container) et l’enregistrement des paquets. La recherche n’est pas fournie, car la restauration et l’installation n’en ont pas besoin. dotnet add package sans --version a besoin de la recherche ; indiquez donc la version.
  • Les listes de versions et l’enregistrement sont revérifiés auprès de nuget.org au plus toutes les 5 minutes, et la dernière copie valide est servie tant que nuget.org est en panne.
  • Les paquets (.nupkg) doivent correspondre au SHA-512 et à la taille que le catalogue de nuget.org enregistre pour la version avant d’être servis. Une version dont l’entrée de catalogue ne comporte aucun hash n’est pas récupérée. Avec l’analyse antivirus activée, chaque paquet est analysé pendant qu’il vous est transmis, à l’intérieur du paquet aussi bien qu’en tant que fichier.
  • Les signatures des paquets sont vérifiées par le client NuGet lui-même, exactement comme sans le proxy. Le proxy ne modifie jamais un paquet.

Pour la politique de paquets, un paquet NuGet est nommé par son id en minuscules. Sa date de publication et sa licence sont la date published de nuget.org et l’expression de licence déclarée par le paquet. Un paquet qui déclare sa licence sous forme de fichier ou d’URL compte comme non évalué.

Modules Go

export GOPROXY=https://frem.sh/_app/proxy/<org>/go
export GOSUMDB=sum.golang.org      # the default; keep it on

L’authentification provient de ~/.netrc (_netrc sous Windows), que la commande go lit pour frem.sh. Rendez le fichier lisible par vous seul (chmod 600 ~/.netrc) :

machine frem.sh
login <user or robot name>
password <token with read:package>

Utilisez GOPROXY=https://frem.sh/_app/proxy/<org>/go plutôt que d’ajouter ,direct : avec direct, un module que le proxy ne sert pas est récupéré depuis son dépôt source en contournant le proxy.

Ce que fait le proxy :

  • @v/list et @latest sont revérifiés auprès de proxy.golang.org au plus toutes les 5 minutes, et la dernière copie valide est servie tant qu’il est en panne.
  • .mod et .zip doivent correspondre aux hashes h1: que la base de sommes de contrôle Go (sum.golang.org) a signés pour cette version du module. Le proxy vérifie la signature de la base avec la même clé que celle intégrée à la commande go. Une archive zip de module est décompressée et hachée de la même manière que la commande go la hache avant d’être servie. Un module que la base de sommes de contrôle ne connaît pas n’est pas servi. Avec l’analyse antivirus activée, chaque zip est analysé pendant qu’il vous est transmis.
  • La base de sommes de contrôle est elle aussi atteinte via le proxy. La commande go demande au proxy s’il sert sum.golang.org, puis récupère chaque recherche et chaque tuile par son intermédiaire. Ainsi, GOSUMDB reste activé et effectue toujours sa propre vérification complète, y compris les preuves que le proxy lui-même ne refait pas, et votre machine n’a besoin d’aucun accès direct à sum.golang.org.
  • Les modules privés ne sont pas servis : proxy.golang.org ne peut pas les récupérer, et la base de sommes de contrôle ne les connaît pas. Excluez-les avec GOPRIVATE (ou GONOPROXY et GONOSUMDB), et la commande go les récupère depuis votre propre hébergement Git comme avant.
  • Les modules hébergés sur frem.sh ne sont jamais demandés à Google. Une requête pour un module sous frem.sh (ou un sous-domaine) est refusée avec 404 avant que le proxy ne contacte qui que ce soit, car la requête elle-même révélerait le chemin du module à proxy.golang.org et sum.golang.org. Ajoutez frem.sh à GOPRIVATE afin que la commande go récupère ces modules directement depuis frem.sh.

Ce qui parvient à Google pour les modules publics

Le proxy récupère les modules Go publics depuis le miroir de modules de Google (proxy.golang.org) et les vérifie par rapport à la base de sommes de contrôle de Google (sum.golang.org). Lorsqu’un module ou une version n’est pas encore en cache, Google reçoit donc :

  • le chemin et la version du module récupérés (par exemple github.com/gorilla/websocket@v1.5.0), ainsi que les recherches correspondantes dans la base de sommes de contrôle ;
  • des requêtes provenant des adresses de sortie de fremforge, et non de vos machines, sans le nom de votre organisation, ni utilisateur, jeton ou dépôt.

Google ne reçoit ni votre code, ni votre go.mod, ni les noms de vos dépôts, ni l’identité de l’organisation à l’origine de la demande. Un module déjà en cache est servi sans contacter Google. Il en va de même pour les analyseurs de sécurité : ils lisent les modules Go publics via ce proxy et ne demandent jamais un module qui est privé selon les règles ci-dessus.

Pour la politique de paquets, un module Go est nommé par son chemin de module. Sa date de publication est celle que proxy.golang.org enregistre pour la version. Les modules Go ne déclarent aucune licence que le proxy puisse lire ; les règles de licence ne s’y appliquent donc pas.

Charts Helm

Les dépôts Helm classiques (un index.yaml et des archives de charts) sont servis à partir d’une liste fixe :

Nom du dépôtSource en amont
prometheus-communityhttps://prometheus-community.github.io/helm-charts
grafanahttps://grafana.github.io/helm-charts
jetstackhttps://charts.jetstack.io (cert-manager)
ingress-nginxhttps://kubernetes.github.io/ingress-nginx

Ajoutez un dépôt avec l’URL du proxy et un identifiant disposant de read:package :

helm repo add prometheus-community https://frem.sh/_app/proxy/<org>/helm/prometheus-community \
  --username <user or robot name> --password-stdin <<< "$FREMFORGE_TOKEN"
helm search repo prometheus-community/prometheus
helm pull prometheus-community/prometheus --version 29.36.1
helm install my-prometheus prometheus-community/prometheus --version 29.36.1

Chaque URL de chart dans l’index renvoie vers frem.sh, de sorte que Helm envoie votre identifiant avec chaque téléchargement de chart et que --pass-credentials n’est pas nécessaire.

Ce que fait le proxy :

  • index.yaml est revérifié auprès du dépôt au plus toutes les 5 minutes, et la dernière copie valide est servie tant que le dépôt est en panne ou répond par autre chose qu’un index Helm. Le proxy le renvoie en JSON, que Helm lit comme du YAML.
  • Les charts doivent correspondre au digest (sha256) que l’index du dépôt enregistre pour cette version avant d’être servis. Une version dont l’entrée d’index n’a pas de digest, ou dont le chart ne se trouve pas sur les hôtes propres au dépôt (par exemple une URL oci://), est exclue de l’index que vous recevez. Avec l’analyse antivirus activée, chaque chart est analysé pendant qu’il vous est transmis.
  • Les fichiers de provenance (.prov) sont transmis sans modification, de sorte que helm pull --verify et helm install --verify fonctionnent avec la clé publique de l’éditeur du chart, par exemple pour cert-manager : helm pull jetstack/cert-manager --version v1.21.2 --verify --keyring cert-manager-keyring.gpg. Un fichier de provenance doit nommer le chart avec le digest de l’index, sinon il n’est pas servi. Parmi les quatre dépôts, jetstack et prometheus-community publient des fichiers de provenance ; pour un chart qui n’en a pas, --verify échoue avec failed to fetch provenance (404).
  • Les dépendances d’un chart sont résolues par Helm à partir des dépôts que nomme son Chart.yaml. Un helm dependency update atteint ces dépôts directement, sauf si vous les ajoutez eux aussi via le proxy.

Le dépôt classique de Bitnami ne figure pas dans la liste : ses versions de charts actuelles sont des artefacts OCI sur Docker Hub. Récupérez-les via le chemin des conteneurs, oci://frem.sh/<org>/dependency-proxy/docker.io/bitnamicharts/<chart>, qui utilise l’identifiant Docker Hub propre à votre organisation (ci-dessus).

Pour la politique de paquets, un chart est nommé par son nom de chart. Sa date de publication est la date created dans l’index, et sa licence l’annotation artifacthub.io/license, si le chart en a une.

Cargo (crates.io)

Le proxy est un registre sparse pour crates.io. Il nécessite Cargo 1.74 ou ultérieur. Remplacez crates-io par lui dans .cargo/config.toml, dans votre projet ou dans ~/.cargo/config.toml pour tous les projets :

[source.crates-io]
replace-with = "fremforge"

[registries.fremforge]
index = "sparse+https://frem.sh/_app/proxy/<org>/cargo/index/"
credential-provider = "cargo:token"

Cargo envoie le jeton comme en-tête Authorization complet ; donnez-lui donc le préfixe Bearer. Gardez-le hors du fichier, dans l’environnement :

export CARGO_REGISTRIES_FREMFORGE_TOKEN="Bearer $FREMFORGE_TOKEN"
cargo fetch
cargo build --locked

Avec le remplacement de source, chaque crate de crates.io passe par le proxy, y compris les crates qu’épingle un Cargo.lock existant, et aucune crate n’est récupérée en le contournant.

Ce que fait le proxy :

  • config.json est propre au proxy. Il renvoie les téléchargements de crates vers le chemin de votre organisation sur frem.sh et indique à Cargo que le registre exige une authentification, de sorte que Cargo envoie le jeton avec chaque requête d’index et de téléchargement.
  • Les fichiers d’index proviennent de index.crates.io. Ils sont revérifiés au plus toutes les 5 minutes, et la dernière copie valide est servie tant que crates.io est en panne. Chaque fichier est celui de crates.io, version pour version. Les versions retirées (yanked) y restent, marquées comme telles : Cargo utilise toujours une version que votre Cargo.lock épingle, et n’en choisit pas pour une nouvelle résolution.
  • Les crates (.crate) proviennent de static.crates.io et doivent correspondre au cksum (sha256) de leur entrée d’index avant d’être servies. Une version dont l’entrée d’index n’a pas de somme de contrôle est exclue. Avec l’analyse antivirus activée, chaque crate est analysée pendant qu’elle vous est transmise, y compris à l’intérieur de l’archive. Cargo vérifie de nouveau la même somme de contrôle de son côté.
  • La recherche, la publication, le retrait (yank) et la gestion des propriétaires ne sont pas fournis : cargo search répond 404 et cargo publish est refusé avec 405. Publiez vos propres crates dans le registre Cargo de votre organisation.

Pour la politique de paquets, une crate est nommée par son nom tel que crates.io le publie, et sa date de publication est le pubtime de son entrée d’index. L’index ne contient aucune licence ; les règles de licence ne s’appliquent donc pas aux crates. Les vulnérabilités et les crates malveillantes proviennent des données crates.io d’OSV : les avis RustSec et les enregistrements MAL-*.

Modules Terraform et OpenTofu

Le proxy sert les modules publics provenant des registres de modules et des dépôts git publics, de sorte que tofu init et terraform init les obtiennent depuis le cache de fremforge plutôt que depuis Internet. Il couvre :

  • Les modules de registre de registry.opentofu.org et registry.terraform.io, par exemple terraform-aws-modules/vpc/aws ou les Azure Verified Modules (Azure/avm-*).
  • Les sources git sur github.com et gitlab.com à un tag ou un commit, par exemple git::https://github.com/<owner>/<repo>.git?ref=v1.2.3 ou github.com/<owner>/<repo>?ref=v1.2.3.

Une branche est refusée : un module en cache doit rester identique ; indiquez donc un tag ou un commit. Les dépôts privés, les autres hôtes et les sources SSH ne sont pas servis.

La vérification IaC lit les modules via ce cache pour toutes les organisations, y compris celles dont le proxy de dépendances est désactivé. La clé de cache des modules, dont votre propre CI a besoin, ne peut pour l’instant être créée que dans les organisations internes de fremforge : pour toute autre organisation, la section de la console n’est pas affichée et l’API répond 404.

La clé de cache des modules

OpenTofu et Terraform atteignent le cache via la clé de cache des modules de votre organisation, qui figure dans l’URL. Un propriétaire de l’organisation la crée sous Build et déploiement › Proxy de dépendances › Modules Terraform et OpenTofu. La clé n’est affichée qu’une fois, sous forme de configuration prête à coller pour OpenTofu et d’une autre pour Terraform. Ensuite, la page n’affiche que ses premiers caractères, le jour de sa dernière utilisation et un bouton Renouveler la clé de cache des modules. Après un renouvellement, l’ancienne clé continue de fonctionner pendant 24 heures, afin que les pipelines en cours se terminent ; ensuite, chaque configuration CI a besoin de la nouvelle. La création et le renouvellement d’une clé sont consignés dans le journal d’audit et parviennent à votre SIEM comme les autres événements d’audit.

La clé peut aussi être créée et renouvelée via l’API, avec un jeton disposant de policy:write : GET /orgs/{org}/dependency-proxy/module-key affiche son préfixe et ses dates (jamais la clé), et POST /orgs/{org}/dependency-proxy/module-key avec {"expect": "<current prefix>"} (ou "" lorsqu’il n’y en a pas) renvoie la nouvelle clé une seule fois. Si une autre clé est devenue courante entre-temps, la réponse est 409 module-key-stale et rien ne change.

La clé permet de lire des modules publics au nom de votre organisation, et rien d’autre. Traitez-la comme un mot de passe : conservez-la dans vos secrets CI, pas dans un dépôt.

Modules de registre : aucune modification de vos sources

Faites pointer le registre d’OpenTofu vers le cache avec un fichier de configuration CLI (~/.tofurc, ou le fichier que désigne TF_CLI_CONFIG_FILE). Vos lignes source et version restent inchangées :

host "registry.opentofu.org" {
  services = {
    "modules.v1"   = "https://frem.sh/_app/proxy/<org>/terraform/k/<module cache key>/registry/registry.opentofu.org/v1/modules/"
    "providers.v1" = "https://registry.opentofu.org/v1/providers/"
  }
}
  • Conservez les deux lignes. Le bloc host remplace tout ce qu’OpenTofu sait du registre. Sans providers.v1, chaque installation de provider échoue avec « does not offer a OpenTofu provider registry ».
  • N’ajoutez pas de bloc credentials. OpenTofu et Terraform associent les identifiants au nom d’hôte du registre ; un jeton placé dans un tel bloc serait donc aussi envoyé au registre public : avec chaque requête de provider, et avec chaque requête sur une machine où le bloc host est absent. La clé de cache des modules n’a besoin d’aucun bloc d’identifiants.
  • Terraform : écrivez registry.terraform.io au lieu de registry.opentofu.org aux trois endroits. Nous avons vérifié cette configuration sur OpenTofu 1.13.1, avec une clé valide et avec une clé erronée (403 Forbidden). Terraform documente le même comportement pour l’hôte et les identifiants ; nous ne l’avons pas mesuré.

Pour vérifier que le cache est utilisé, exécutez TF_LOG=debug tofu init et recherchez les lignes GET https://frem.sh/_app/proxy/<org>/terraform/... pour vos modules.

Sources git

Indiquez comme source HTTPS l’archive du tag ou du commit fournie par le proxy. //* sélectionne la racine du module dans l’archive, suivie éventuellement d’un sous-répertoire :

module "network" {
  source = "https://frem.sh/_app/proxy/<org>/terraform/k/<module cache key>/git/github.com/<owner>/<repo>/v1.2.3.tar.gz//*/modules/network"
}

Aucun identifiant n’est nécessaire en dehors de la clé de cache des modules dans l’URL.

Ce que fait le proxy

  • Une version est épinglée lors de sa première récupération. Le proxy résout le tag en commit à partir de la liste des refs git du dépôt lui-même (ce que lit git ls-remote), récupère l’archive de ce commit et enregistre le commit et le SHA-256 de l’archive. Chaque requête ultérieure, de n’importe quelle organisation, obtient exactement ces octets. Une version de module de registre est épinglée au commit que désignait son tag.
  • Un tag déplacé ne modifie pas votre module. Le proxy revérifie les tags épinglés au plus toutes les 6 heures. Si un tag désigne désormais un autre commit, le proxy continue de servir celui qui est épinglé et enregistre que le tag en amont a été déplacé. Seul un opérateur fremforge peut réépingler un module.
  • Ce contre quoi cela protège, et ce contre quoi cela ne protège pas. Les registres et les hébergeurs git ne publient aucune somme de contrôle pour les archives de modules ; la première récupération est donc considérée comme fiable. Rien de ce qui change en amont ensuite ne vous parvient : un tag redirigé ou une archive régénérée n’est pas servi. Une version qui était malveillante lors de sa première publication est servie telle quelle.
  • Les archives sont vérifiées avant d’être servies. Une archive de plus de 50 Mio, qui se décompresse en plus de 512 Mio ou 20 000 entrées, ou qui contient un chemin absolu, un chemin .., un fichier de périphérique ou un lien pointant hors de l’archive, est refusée, et rien n’est stocké.
  • L’analyse antivirus s’applique aux archives de modules comme aux autres fichiers lorsque votre organisation l’a activée. OSV n’a pas d’écosystème Terraform ; aucune liste de paquets connus comme malveillants ne s’applique donc aux modules, et les règles de licence et d’âge de publication de la politique de paquets ne leur sont pas appliquées.
  • Partagé, journalisé par organisation. Un module public est stocké une seule fois pour tout le monde. Les récupérations de chaque organisation sont enregistrées dans son propre journal d’accès, avec l’acteur module-cache-key. La clé de cache des modules elle-même n’est jamais écrite dans un journal.

Politique de paquets et paquets malveillants

La politique de paquets de votre organisation est évaluée pour les paquets npm, PyPI, RPM, Maven, NuGet, Go et Cargo et les charts Helm récupérés via le proxy, avec les mêmes règles que pour les paquets de votre registre :

  • Listes d’autorisation, de refus et d’avertissement des licences, appliquées à la licence déclarée de la version.
  • Vulnérabilités égales ou supérieures à votre seuil de gravité ou de CVSS, issues de la base OSV.
  • Listes de refus et d’autorisation par nom de paquet et plage de versions.
  • Âge minimal de publication, à partir de la date de publication de la source en amont.
  • Les paquets malveillants (avis OSV MAL-*) sont toujours vérifiés, quoi que dise la politique.

Pour les paquets RPM, la version est [epoch:]version-release, la licence est le champ License du paquet, et l’âge de publication est le moment où le paquet est entré dans le dépôt. OSV ne dispose d’aucune donnée sur les paquets malveillants ni sur les vulnérabilités pour les paquets RPM ; ces deux vérifications ne s’appliquent donc pas aux RPM du proxy. Les vulnérabilités des paquets RPM sont détectées lorsque les images construites à partir d’eux sont analysées. Il en va de même pour les charts Helm : OSV n’a pas de données les concernant ; la licence, les listes de refus et l’âge de publication s’appliquent donc, et les images qu’un chart déploie sont analysées là où elles sont récupérées. Une fois le refus activé, une version de chart bloquée reste dans l’index Helm et son téléchargement est refusé avec 403.

Le refus est activé depuis le 2026-10-11. Une politique en mode enforce retire une version bloquée du packument npm ou de l’index PyPI, de sorte que votre résolveur choisit une version autorisée, et un téléchargement direct de cette version est refusé avec 403 en nommant la règle. Une politique en mode shadow, le mode par défaut, sert toujours le paquet et enregistre le pull dans le journal d’accès comme would_block avec la règle qui a correspondu. Les paquets malveillants sont refusés quel que soit le mode de votre politique, Observation et Désactivé compris, et exclus des métadonnées de paquets que sert le proxy.

Tant que le proxy ne parvient pas à obtenir de réponse de politique de fremforge (après quelques secondes de nouvelles tentatives, et sans réponse récente pour le même paquet sur laquelle se rabattre), il refuse le téléchargement avec 503 plutôt que de servir une version qu’il n’a pas pu vérifier contre les logiciels malveillants. Une nouvelle tentative ultérieure réussit dès que la réponse est de nouveau disponible.

Les données d’avis sont actualisées à partir du miroir OSV de fremforge plusieurs fois par jour, et une modification de politique s’applique au pull suivant. Rien n’a besoin d’être réanalysé.

Ce qui est appliqué aujourd’hui

VérificationS’applique àAujourd’hui
Somme de contrôle de la source en amont (digest d’image, integrity npm, sha256 PyPI, sha256 et taille de repomd.xml et primary.xml RPM, .sha1 et .sha256 Maven, SHA-512 et taille du catalogue NuGet, h1: Go signé par la base de sommes de contrôle, digest de l’index.yaml Helm, cksum de l’index Cargo)Chaque couche, paquet et fichier de métadonnées de dépôtRefusé en cas de non-correspondance ; rien n’est stocké
Antivirus (ClamAV) pendant la transmission du fichier, si votre organisation a activé l’analyse antivirus (malware_scan, désactivée par défaut)Chaque couche, fichier de paquet et chart ; charges utiles RPM décompressées ; entrées de jar, de nupkg et de zip de module que l’analyseur ne peut pas ouvrir lui-même décompressées, ou refuséesRefusé : un fichier infecté est interrompu avant son dernier octet et jamais stocké
Résultat d’analyse des couches, infectée ou impossible à analyser (avec l’analyse antivirus activée)Images de conteneursRefusé, comme pour les images que vous poussez
block_pull sur les vulnérabilités, si votre organisation l’a définiImages de conteneursRefusé avec 403 DENIED ; voir Analyse des images
Paquets malveillants (OSV MAL-*) dans l’imageImages de conteneursRefusé, comme pour les images que vous poussez ; un résultat dans l’onglet Logiciels malveillants
Paquets malveillants (OSV MAL-*)npm, PyPI, Maven, NuGet, Go, CargoRefusé avec 403, quel que soit le mode de votre politique
Politique de paquets : licence, vulnérabilités, liste de refus, âge de publicationnpm, PyPI, Maven, NuGet, Go et Cargo (pas de licence pour Go et Cargo)Refusé lorsque votre politique est en mode enforce ; enregistré (would_block) en mode shadow
Politique de paquets : licence, liste de refus, âge de publicationRPM, charts HelmRefusé lorsque votre politique est en mode enforce ; enregistré (would_block) en mode shadow
Vulnérabilité connue comme exploitée avec une version corrigée (ENISA EUVD, CISA KEV)npm, PyPI, Maven, NuGet, Go, CargoRefusé avec 403, quel que soit le mode de votre politique (voir ci-dessous)

Les images en cache sont aussi analysées à la recherche de vulnérabilités et de secrets divulgués, comme les images que vous poussez ; voir Analyse des images.

Vulnérabilités connues comme exploitées

Depuis le 2026-10-13, fremforge vérifie chaque version de paquet passant par le proxy par rapport aux vulnérabilités connues pour être exploitées dans la nature : la base européenne de vulnérabilités de l’ENISA (EUVD) en premier lieu, complétée par le catalogue Known Exploited Vulnerabilities de la CISA. Une version n’est comptée que si une telle vulnérabilité l’affecte et qu’une version corrigée existe, de sorte qu’il y a toujours une version vers laquelle migrer. C’est la règle que fremforge applique déjà à ses propres images de runners.

Depuis le 31/10/2026, une telle version est refusée. Un téléchargement répond 403 avec un message qui nomme les CVE et vous invite à mettre à niveau, et la réponse porte x-fremforge-proxy-policy: blocked;package_policy:known_exploited. La version est aussi retirée des métadonnées de paquet que le proxy sert, de sorte qu’une plage de versions choisit une autre version de la plage ; seul un fichier de verrouillage ou un fichier de build qui épingle la version concernée échoue. Le refus est enregistré dans votre journal d’accès comme tout autre refus de politique.

C’est une règle de la plateforme, comme pour les paquets malveillants : elle s’applique quel que soit le mode de votre politique, et les exceptions de votre politique ne la lèvent pas. Le bot de mise à jour des dépendances de votre organisation voit toujours ces versions dans les métadonnées, afin de pouvoir proposer la mise à niveau. Les scanners de pull requests de fremforge les lisent aussi toujours, de sorte qu’une dépendance connue comme exploitée apparaît toujours comme un résultat au lieu de disparaître de l’analyse.

Si un build échoue à cause d’elle, passez à la version corrigée : une pull request de correctif de sécurité est peut-être déjà ouverte. Mesuré du 13/10/2026 au 31/10/2026, avant le changement, aucune organisation cliente n’a téléchargé une telle version.

Le même flux permet déjà à une version qui corrige une vulnérabilité connue comme exploitée de contourner l’âge minimal de publication de votre politique.

Stockage, plafonds et facturation

Les fichiers en cache sont comptabilisés dans le stockage de paquets de votre organisation pour leur taille totale, comme s’ils se trouvaient dans votre propre registre. Une couche de conteneur ou un fichier npm ou PyPI que plusieurs organisations récupèrent est stocké une seule fois, mais l’utilisation de chaque organisation le comptabilise. Le contenu RPM est stocké par organisation (voir RPM).

  • Formules payantes : les fichiers en cache partagent un même pool de stockage de paquets avec vos propres paquets. Avec le dépassement désactivé, le proxy peut utiliser ce que vos propres paquets laissent du pool. Lorsqu’il ne reste plus rien, les nouvelles récupérations sont refusées avec 403 (dependency proxy storage cap reached), et ce qui est déjà en cache est toujours servi. Avec le dépassement activé, le proxy n’a pas de plafond, et le stockage au-delà du pool est facturé au tarif de dépassement des paquets.
  • Essais : 5 Go forfaitaires de fichiers en cache.

La page Proxy de dépendances affiche votre utilisation et votre plafond. Le chiffre de vos propres paquets à partir duquel le plafond est calculé est actualisé une fois par jour.

Rétention

Un fichier en cache que votre organisation n’a pas récupéré depuis retention_days (90 par défaut, de 7 à 365) est retiré de votre cache, avec les tags qui pointent vers lui, et cesse d’être comptabilisé dans votre stockage. La vérification s’exécute toutes les heures. Lorsqu’aucune organisation ne référence plus un fichier, il est supprimé du stockage de fremforge 24 heures plus tard. Le récupérer à nouveau le récupère depuis la source en amont.

Les versions de paquets encore utilisées sont conservées. Un fichier de paquet (npm, PyPI, Maven, NuGet, Go, Cargo, Helm ou RPM) n’est pas retiré, quelle que soit l’ancienneté de son dernier pull, tant que la même version du paquet est un composant actuel de l’inventaire des composants de votre organisation : dans le SBOM actuel de la branche par défaut d’un dépôt, sa dernière release, une image actuelle, un paquet de registre en ligne ou un SBOM téléversé. Une dépendance épinglée dans un fichier de verrouillage mais construite seulement deux fois par an reste donc en cache. Elle est toujours comptabilisée dans votre stockage. Dès que la version quitte tous les SBOM actuels, la règle normale s’applique de nouveau lors de la vérification horaire suivante.

Les images de base qu’utilisent vos builds actuels sont conservées. Une image de conteneur en cache n’est pas retirée, quelle que soit l’ancienneté de son dernier pull, tant que l’une des images actuelles de votre organisation est construite sur elle : chaque couche de l’image en cache est aussi une couche d’une image toujours publiée sous un tag dans le registre de votre organisation. Le manifeste de l’image, sa configuration et toutes ses couches sont conservés ensemble, de sorte qu’un pull de celle-ci continue de fonctionner, et il en va de même pour un index multiplateforme dont vous construisez sur l’image d’une plateforme. Une image qui partage seulement une couche avec vos builds (par exemple une autre image de la même distribution) n’est pas conservée par cette règle. Elle est toujours comptabilisée dans votre stockage. Dès qu’aucune image actuelle n’est construite sur elle, la règle normale s’applique de nouveau. Toutes les autres images en cache restent soumises à la règle du dernier pull. Deux cas ne sont pas reconnus et restent soumis à la règle du dernier pull : une image de base utilisée uniquement pour une plateforme autre que linux/amd64, et un build qui recompresse les couches de l’image de base au lieu de les réutiliser.

Pannes en amont

Lorsqu’un registre en amont est en panne, lent ou refuse les requêtes, les builds continuent de fonctionner pour tout ce que votre organisation a déjà récupéré :

  • Les fichiers déjà récupérés (couches d’images, .tgz, wheels, .jar, .nupkg, .zip et .mod Go, charts Helm, crates) sont servis depuis le stockage de fremforge. Leur service n’attend jamais la source en amont.
  • Les index et tags (tags d’images, packuments npm, index PyPI, repomd.xml RPM, maven-metadata.xml Maven, listes de versions et enregistrements NuGet, @v/list et @latest Go, index.yaml Helm, fichiers d’index Cargo) reçoivent une réponse tirée de la dernière copie valide. La réponse porte alors X-Fremforge-Proxy: stale, de sorte qu’une nouvelle version publiée pendant la panne n’est pas visible tant que la source en amont n’est pas rétablie.
  • Les tags d’images qu’une autre organisation a mis en cache sont eux aussi servis depuis la dernière copie valide, lorsque votre organisation n’a jamais récupéré ce tag : uniquement pour les images publiques récupérées sans identifiants, et uniquement depuis le stockage partagé de fremforge, jamais à partir d’un contenu qu’une autre organisation a récupéré avec ses propres identifiants de registre. Les images Docker Hub sont toujours récupérées avec l’identifiant propre à votre organisation et ne sont pas partagées de cette façon.
  • Tout ce qui n’a jamais été récupéré répond 502, car il n’y a rien à servir.

Si le service de connexion ou d’autorisation de fremforge lui-même est brièvement indisponible, les requêtes qui ont été autorisées au cours des 15 dernières minutes continuent d’être servies depuis le cache (lectures uniquement). Un jeton révoqué ou une adhésion supprimée prend néanmoins effet dès que fremforge peut le vérifier.

Ce qui survit à une panne est limité par la rétention. Un fichier que votre organisation n’a pas récupéré depuis retention_days (90 par défaut) est retiré. Il doit ensuite être récupéré à nouveau depuis la source en amont, ce qui est impossible tant que celle-ci est en panne. Les versions de paquets de votre inventaire des composants, et les images de base sur lesquelles vos images actuelles sont construites, sont conservées dans tous les cas (voir Rétention). Pour tout autre élément qui doit survivre à une longue panne, récupérez-le au moins une fois au cours de votre période de rétention, ou augmentez retention_days.

Journal d’accès

Chaque pull via le proxy apparaît dans le journal d’accès au registre de votre organisation avec l’origine proxy, à l’exception de deux types de trafic CI de routine : les lectures de métadonnées autorisées d’un job CI (index de paquets et listes de versions) ne sont plus stockées depuis le 2026-10-12, et les téléchargements d’un job CI que le proxy sert depuis son cache sont comptés dans le recensement des pulls par jour, version, dépôt et exécution depuis le 2026-10-13. Les téléchargements d’un job CI récupérés depuis le registre en amont, ses refus et ses erreurs restent des événements. Le journal enregistre le paquet, la version et le fichier, qui l’a récupéré (un utilisateur ou, pour la CI, le dépôt du job), le résultat et toute décision de politique. Un pull via frem.sh servi depuis le cache est servi par le proxy (200) ; celui d’un job CI est une redirection vers le stockage objet (307), sauf pour les fichiers Maven jusqu’à 1 Mio. Dans les deux cas, sa taille est enregistrée. Une requête HEAD via frem.sh est enregistrée comme GET, car le CDN transmet les requêtes HEAD à fremforge sous forme de GET. Les lectures propres à l’analyse des dépendances (voir plus haut) ne passent ni par le CDN ni par le proxy de téléversement ; elles ne figurent donc pas dans le journal d’accès, et fremforge les journalise sous forme de comptages par analyse.

Ce que voit la source en amont

fremforge contacte la source en amont que vous avez choisie, en votre nom. La source en amont reçoit l’adresse IP de fremforge et le nom de l’image ou du paquet. Pour docker.io, les registres Red Hat, et ghcr.io ou quay.io lorsque vous avez stocké un identifiant pour eux, elle reçoit aussi l’identifiant propre à votre organisation, de sorte que le pull est décompté sur votre compte chez ce fournisseur. Elle ne reçoit rien concernant vos utilisateurs. La plupart de ces registres sont exploités par des entreprises situées hors de l’UE. Après la première récupération, les pulls sont servis depuis le stockage européen de fremforge sans recontacter la source en amont, sauf pour revérifier les tags, les métadonnées npm et PyPI, les repomd.xml RPM, les maven-metadata.xml Maven et les listes de versions et l’enregistrement NuGet, les listes de versions Go, les fichiers index.yaml Helm et les fichiers d’index Cargo (au plus toutes les 5 minutes). La base de sommes de contrôle de Go est elle aussi atteinte via le proxy, pour le compte de la commande go.

Erreurs

StatutSignification
401Aucun identifiant, ou frem.sh ne l’a pas accepté.
403Vous n’êtes pas membre, le proxy est désactivé pour l’organisation, l’identifiant n’a pas read:package, un jeton de job appartient à une autre organisation, le plafond de stockage de votre organisation est atteint, ou une vérification a refusé la requête (le message la nomme).
404La source en amont ne l’a pas, ou l’hôte en amont n’est pas disponible via le proxy.
405Une écriture. Le proxy est en lecture seule.
429La limite de récupérations en amont de votre organisation a été atteinte. Réessayez après le nombre de secondes indiqué par Retry-After.
502La source en amont est indisponible et rien n’est en cache pour cette requête.
503Une dépendance (analyseur antivirus, vérification de politique) est indisponible ; le proxy est occupé ; ou une autre requête est encore en train de récupérer et d’analyser la même couche. Réessayez après Retry-After.