Aller au contenu
English
Compte : non connecté, synchronisation possible Mon apprentissage
Selectionner le thème
English
Read in English

Varnish Artifact Firewall : contrôler l'entrée des dépendances

Par mis à jour le

Améliorer cette page

Un firewall d'artefacts est un proxy qui prend une décision de politique (autoriser, masquer, refuser) devant chaque npm install ou pip install, avant que le paquet n'entre dans votre usine logicielle. Un cache comme Nexus sert tout ce qu'on lui demande ; le firewall décide ce qui a le droit d'entrer. Cette leçon s'adresse aux équipes sécurité et plateforme qui gèrent déjà un gestionnaire d'artefacts et veulent passer du cache au contrôle.

L'exemple est Varnish Artifact Firewall, la brique Premium de Varnish Orca, devant Nexus. Tout est vérifié le 1er octobre 2026 sur Orca 0.18.1 (firewall 0.6.0) : la configuration de la leçon passe le validateur, mais l'image publique ne porte aucun addon de licence (addons=[]), si bien que le firewall n'applique rien sans licence d'essai. La leçon le dit à chaque endroit où cela change ce que vous pouvez observer.

  • Distinguer cache, firewall d'artefacts, analyse SCA et contrôle d'admission.
  • Rédiger des règles allow, hide et deny, et régler la quarantaine des publications récentes.
  • Monter un lab Orca et Nexus, et savoir ce qu'il montre sans licence.
  • Bloquer une version vulnérable et un nom interne usurpé.
  • Fermer les contournements, sur les runners CI comme sur les postes.
  • Produire la preuve de SOCLE-INT-GEN-3, une consommation OSS via un proxy interne contrôlé, sans tirage direct non filtré.

Vos développeurs lancent chaque jour des dizaines de npm install et de pip install. Chacune télécharge du code tiers, souvent des centaines de paquets transitifs, et l'exécute sur les postes et les runners CI. En septembre 2025, le ver Shai-Hulud a compromis plus de 500 paquets npm selon l'alerte de la CISA du 23 septembre : son script postinstall collectait les jetons et identifiants présents dans l'environnement, puis se republiait avec les jetons npm trouvés.

Un gestionnaire d'artefacts comme Nexus ou un cache comme Varnish Orca résout deux problèmes réels : la vitesse et la résilience face à une panne du registre public. Mais il sert tout ce qu'on lui demande. Si un développeur installe une version vulnérable, un paquet au nom usurpé par dependency confusion, ou une version publiée il y a deux heures par un compte compromis, le cache la télécharge, la stocke et la rediffuse à toute l'équipe.

Un firewall d'artefacts s'intercale entre vos clients (npm, pip, runners CI) et vos dépôts, et évalue chaque résolution contre un jeu de règles. Chez Varnish, une règle aboutit à l'une de trois actions, et une quarantaine globale s'y ajoute, d'après la documentation de configuration du firewall :

DécisionEffetCas d'usage
allowSert l'artefactException explicite, paquet de confiance
hideRetire la version de la résolution « dernière version »Évincer une version sans casser un lockfile existant
denyRefuse avec une réponse 403Paquet interdit, version vulnérable
quarantaine (default_quarantine_days)Bloque toute version publiée depuis moins de N jours, quelles que soient les règlesFenêtre de sécurité sur les publications fraîches

La quarantaine n'est donc pas une action de règle : c'est un réglage global, qui répond directement à l'exigence de délai avant adoption (cooldown) du référentiel, SOCLE-INT-OSS-1. Une version piégée publiée ce matin, comme celles de Shai-Hulud, est retenue le temps que la communauté la repère.

L'intérêt par rapport à un scanner est le moment de la décision : avant l'ingestion. Une dépendance refusée n'entre jamais dans le cache ni dans le build. Là où un SBOM scanné vous dit après coup « vous avez installé un composant vulnérable », le firewall empêche le composant d'arriver.

Le lab enchaîne trois composants. Le firewall est transparent : les clients visent le registre virtuel d'Orca, et enable_firewall: true fait passer ce registre par le firewall en interne. Le schéma suit une résolution :

Résolution d'une dépendance : le client npm ou pip interroge le registre virtuel Orca, le firewall décide allow, hide ou deny ; un refus renvoie une erreur 403 au client, sinon la requête rejoint le groupe Nexus, qui sert depuis son dépôt hosted interne ou son proxy vers npmjs.org et pypi.org.Client npm ou pipRegistre virtuel OrcaFirewallallow, hide, denyRéponse 403Groupe NexusDépôt hostedpaquets internesProxy publicnpmjs, PyPI
Source · SVG
Échap pour fermer, + et − pour zoomer, défilement pour se déplacer. 100 %

Le rôle de Nexus est de centraliser : un dépôt proxy vers le registre public, un dépôt hosted pour vos paquets internes, et un groupe qui expose une URL unique. L'ordre des membres du groupe (hosted avant proxy) constitue déjà une première barrière contre la dependency confusion ; le firewall ajoute la décision par règle au-dessus.

Le firewall n'est pas lié à Nexus. La documentation Varnish décrit Orca comme un gestionnaire de registres virtuels agnostique : chaque registre virtuel a un backend remotes.url, et Orca transmet le chemin client tel quel vers ce backend. Rien n'empêche donc de pointer ce backend vers un autre gestionnaire, comme Pulp, le gestionnaire d'artefacts open source.

Ce montage avec Pulp a été vérifié dans un lab antérieur à cette leçon : le registre virtuel se charge, l'index PyPI et le téléchargement des artefacts passent à travers Orca. Deux points de vigilance en sont ressortis :

  • Le montage le plus direct vise le registre de conteneurs : Pulp expose son API OCI sur /v2/ à la racine, comme le registre public qu'Orca sait déjà mettre en cache.
  • Pour PyPI, Pulp émet des liens de téléchargement absolus via son paramètre CONTENT_ORIGIN ; Orca les ré-enveloppe dans son endpoint /redirectz, et il faut aligner CONTENT_ORIGIN sur l'URL exposée par Orca.

Placer Orca devant Pulp est intéressant parce que Pulp n'a pas de quarantaine native : le firewall apporte la couche de décision qui manque, sur un stockage entièrement open source.

Le lab tient dans un docker-compose.yml (Nexus et Orca) et quelques fichiers de configuration. Les images sont figées par digest ; les versions sont celles du 1er octobre 2026.

  1. Déclarer Nexus et Orca

    docker-compose.yml
    services:
    nexus:
    image: sonatype/nexus3:3.96.4@sha256:5f48f9085096e96f4111ec07d79d57ca5a4a1da5a2003a3028a0b60c62bea7d1
    ports: ["8081:8081"]
    volumes: ["nexus-data:/nexus-data"]
    orca:
    image: varnish/orca:0.18.1@sha256:44df08bdb5843ef1173bda327adb487c2bd101681b24559482c10bf6d8f1429d
    platform: linux/amd64 # Orca est distribué pour linux/amd64
    depends_on: [nexus]
    ports:
    - "8080:80" # registres virtuels (endpoint client)
    - "9090:9090" # métriques du firewall (Premium)
    volumes:
    - ./orca/orca.yaml:/etc/varnish-supervisor/default.yaml:ro
    - ./orca/rulesets:/etc/orca/rulesets:ro
    volumes:
    nexus-data:
  2. Créer les dépôts Nexus (proxy, hosted, groupe) pour npm et PyPI via l'API REST. Le groupe npm-all agrège npm-private-hosted puis npm-public-proxy, et c'est lui que verra le firewall.

    Nexus Community Edition ne sert aucun artefact tant que son EULA n'est pas acceptée. Mesuré sur 3.96.4 : un téléchargement répond 403 You must accept the End User License Agreement (EULA), puis 200 après acceptation par l'API, qui attend le corps renvoyé par GET, avec "accepted": true.

    Fenêtre de terminal
    curl -s -u "admin:$NEXUS_PASSWORD" http://localhost:8081/service/rest/v1/system/eula \
    | jq '.accepted = true' \
    | curl -s -u "admin:$NEXUS_PASSWORD" -H 'Content-Type: application/json' \
    -X POST http://localhost:8081/service/rest/v1/system/eula -d @-
    # réponse 204, sans corps
  3. Configurer le firewall Orca

    orca/orca.yaml
    virtual_registry:
    registries:
    - name: npm-nexus
    default: true
    enable_firewall: true
    remotes:
    - url: http://nexus:8081/repository/npm-all/
    - name: pypi-nexus
    enable_firewall: true
    remotes:
    - url: http://nexus:8081/repository/pypi-all/
    firewall:
    default_action: allow # observe en POC ; deny en production
    default_quarantine_days: 2
    severity_deny_threshold: 9
    severity_allow_threshold: 4
    audit_log_output: stdout
    metrics_address: ":9090"
    rulesets:
    - path: /etc/orca/rulesets/npm-rules.yaml
    - path: /etc/orca/rulesets/pypi-rules.yaml

    Le binaire embarque un validateur. Mesuré sur Orca 0.18.1, il répond configuration is valid pour ce fichier, et refuse une clé inconnue (field champ_inconnu not found in type firewall.FirewallConfig) ou une entrée de rulesets écrite en simple chaîne au lieu d'un objet path:.

    Fenêtre de terminal
    docker run --rm --platform linux/amd64 \
    -v "$PWD/orca/orca.yaml":/c.yaml:ro -v "$PWD/orca/rulesets":/etc/orca/rulesets:ro \
    varnish/orca:0.18.1@sha256:44df08bdb5843ef1173bda327adb487c2bd101681b24559482c10bf6d8f1429d \
    varnish-supervisor -validate -config /c.yaml
    # configuration is valid
  4. Pointer les clients sur le firewall

    Orca transmet le chemin client tel quel : incluez le chemin du groupe Nexus.

    .npmrc
    registry=http://npm-nexus.localhost:8080/repository/npm-all/
    pip.conf
    [global]
    index-url = http://pypi-nexus.localhost:8080/repository/pypi-all/simple
    trusted-host = pypi-nexus.localhost

Une règle décrit quoi reconnaître (match) et quoi décider (action). Les règles sont regroupées dans un fichier identifié par un id, et triées par priority. Ce jeu de règles npm de démonstration couvre les trois actions.

orca/rulesets/npm-rules.yaml
id: npm-demo-rules
rules:
# Blocage dur d'un paquet précis.
- id: block-left-pad
priority: 100
action: deny
severity: 9.5
reason: "Package bloqué pour la démonstration."
match:
- type: npm
name: left-pad
# Anti dependency confusion : un scope interne ne vient jamais du public.
- id: block-internal-scope
priority: 90
action: deny
reason: "Le scope @company ne doit pas être résolu depuis le registre public."
match:
- type: npm
name: "@company/*"
# Masquer les vieilles versions sans casser un lockfile existant.
- id: hide-lodash-old
priority: 50
action: hide
reason: "Les versions de lodash < 4.17.21 ne sont pas proposées."
match:
- type: npm
name: lodash
version: "vers:npm/<4.17.21"

Côté Python, la même grammaire bloque une version vulnérable. La plage de versions utilise la notation VERS (vers:pypi/<2.32.0), un format normalisé de plages de versions par écosystème.

orca/rulesets/pypi-rules.yaml
id: pypi-demo-rules
rules:
- id: block-old-requests
priority: 100
action: deny
severity: 9.0
reason: "Les versions de requests < 2.32.0 sont interdites."
match:
- type: pypi
name: requests
version: "vers:pypi/<2.32.0"

Une règle peut porter un score severity, qui prend le pas sur son action. D'après la documentation de Varnish, une règle dont la sévérité est supérieure ou égale à severity_deny_threshold (9 par défaut) aboutit à deny, une règle inférieure ou égale à severity_allow_threshold (4 par défaut) aboutit à allow, et entre les deux, c'est l'action de la règle qui s'applique. Dans l'exemple npm, block-left-pad (9.5) serait refusé même si son action valait hide.

Ce mécanisme permet de brancher des règles communautaires, par exemple les jeux de règles dérivés des données OSV, et de piloter leur effet par deux seuils, sans réécrire chaque règle. Il porte aussi un piège : une règle deny de sévérité 3 est autorisée.

Charger les règles depuis des fichiers locaux (path:) impose de redémarrer Orca pour les prendre en compte. Le mode dynamique passe par une source git, que le firewall rafraîchit à intervalle régulier, sans redémarrage. L'extrait suivant passe le validateur de la 0.18.1.

orca/orca.yaml (extrait firewall)
firewall:
mode: report # décisions journalisées, rien n'est bloqué
rulesets:
- git:
name: npm-osv-rules
url: https://github.com/varnish/osv-rules.git
sub_path: rulesets/npm/all.yaml
ref: main # branche suivie : les règles changent sans revue locale
interval: 30m # fréquence de rafraîchissement
webhook:
url: http://collector.interne/reloaded # notifié après chaque rechargement

Le dépôt varnish/osv-rules existe et publie rulesets/npm/all.yaml. Suivre sa branche main est un choix assumé : les règles se mettent à jour seules, au prix d'une confiance dans l'éditeur. Pour vos propres règles, un dépôt interne versionné et relu par revue de code donne une vraie démarche GitOps. Le mode report est le bon point de départ : toutes les décisions sont ramenées à allow, mais restent écrites dans le journal d'audit, ce qui permet de mesurer l'effet d'une politique avant de l'activer.

Avec une licence d'essai montée, le firewall applique les décisions. Le scénario d'étude le plus parlant compare deux installations. Les étapes suivantes n'ont pas été rejouées pour cette leçon, faute de licence : elles décrivent le comportement documenté.

  1. Installer une dépendance autorisée

    Fenêtre de terminal
    npm install is-number # aucune règle ne correspond : allow
  2. Tenter une dépendance bloquée

    Fenêtre de terminal
    npm install left-pad # règle block-left-pad : deny, réponse 403
  3. Lire le journal d'audit

    Fenêtre de terminal
    docker compose logs orca | grep -i deny

    Le journal porte la décision, le paquet et la raison (reason) de la règle. C'est lui que vous routerez vers un SIEM en production.

  4. Modifier une règle et rejouer

    Retirez block-left-pad, redémarrez Orca, relancez l'installation : elle passe. La politique pilote la décision.

Un firewall d'artefacts ferme une porte : l'entrée non gouvernée des dépendances. Le présenter comme une protection globale serait une erreur, et trois limites le montrent.

  • Il ne lit pas le code d'un paquet autorisé : une dépendance saine aujourd'hui peut être compromise par une mise à jour, d'où la quarantaine.
  • Il ne protège ni le runner CI compromis, ni l'exfiltration de secrets, ni l'image déjà construite et non signée.
  • Il ne produit ni SBOM ni provenance : ce sont d'autres briques de ce module.

Surtout, la configuration du registre incite à passer par le firewall mais ne l'impose pas : un développeur ou un job peut toujours écrire npm config set registry https://registry.npmjs.org. Refermer ce contournement est un sujet à part entière.

Un firewall ne protège que ce qui passe par lui. Tant que les clients peuvent joindre registry.npmjs.org ou pypi.org en direct, n'importe qui peut le court-circuiter d'une seule commande. La règle tient en une phrase : la configuration incite, le réseau impose.

Côté CI, le contournement se ferme proprement parce que vous maîtrisez l'environnement. Placez les runners dans un réseau sans route sortante vers Internet : seul le firewall d'artefacts est joignable. Une tentative npm config set registry https://registry.npmjs.org échoue alors faute de route. Sous Kubernetes, une NetworkPolicy egress qui n'autorise que le service du firewall produit le même effet, et des runners éphémères empêchent toute configuration locale persistante.

Le poste du développeur est plus dur à contraindre : le développeur y a souvent les droits administrateur, donc aucune configuration applicative n'est infalsifiable. Pousser un .npmrc, un pip.conf ou un GOPROXY par MDM ou gestion de configuration réduit la friction, mais reste contournable d'un --registry. Le contrôle robuste est, là encore, réseau :

SolutionEffetRobustesse
Couper l'egress direct vers npmjs, pypi, Docker HubLe registre public n'a plus de routeÉlevée
DNS interne : registry.npmjs.org résolu vers le firewallMême une URL publique en dur atterrit sur le firewallÉlevée, avec l'egress fermé
Poste sans route Internet directeTout transite par le proxy interneMaximale
Environnement de dev distant (dev container, VDI)On contrôle le réseau de l'environnementÉlevée
Configuration gérée (.npmrc, pip.conf, GOPROXY)Oriente le défautFaible seule

Aucune ligne ne suffit isolément sur une machine où le développeur est administrateur. La combinaison qui tient : pas de route directe vers les registres publics, plus un DNS qui ramène les noms publics sur le firewall. La configuration gérée s'ajoute pour le confort, pas pour la sécurité.

Le réseau ferme la porte ; la détection vérifie qu'elle reste fermée. Ajoutez en CI un contrôle des lockfiles : une entrée resolved (npm) ou une URL d'index qui pointe ailleurs que vers le firewall fait échouer le build. OSV-Scanner repère de son côté les dépendances vérolées qui auraient contourné le firewall, et les journaux du proxy réseau signalent toute tentative d'accès direct aux registres publics. Ces contournements prolongent les techniques décrites dans Attaques via les gestionnaires de paquets.

Le firewall n'est pas un substitut aux autres contrôles du module : il occupe la position la plus en amont. Une chaîne mûre les combine, chacune répondant à une question différente.

ÉtapeBriqueRôle
Entrée des dépendancesVarnish Artifact FirewallDécide avant ingestion
CentralisationNexusCache et expose les artefacts
InventaireSBOMListe les composants livrés
ExploitabilitéVEXDocumente ce qui est réellement exploitable
SignatureCosignSigne et vérifie l'image
ProvenanceSLSAAtteste l'origine du build

Le firewall réduit le bruit en aval : moins de composants douteux entrent, moins de vulnérabilités à trier ensuite. Il ne remplace pas la défense en profondeur, il la commence.

  • Un cache accélère et résiste aux pannes ; un firewall d'artefacts décide ce qui a le droit d'entrer.
  • Trois actions de règle, allow, hide et deny, plus une quarantaine globale (default_quarantine_days) sur les publications récentes.
  • La sévérité prend le pas sur l'action : au-dessus du seuil de refus, deny ; sous le seuil d'autorisation, allow.
  • Le mode report journalise les décisions sans rien bloquer : le bon départ pour mesurer une politique.
  • -validate contrôle la structure de la configuration, pas le contenu des règles.
  • Sans règle d'egress réseau, la configuration du registre se contourne.
  • Chez Varnish, le firewall est Premium : l'image publique 0.18.1 démarre avec addons=[] et ne filtre rien.
  • La preuve attendue par SOCLE-INT-GEN-3 est la configuration du proxy interne et la preuve d'absence de tirage direct non filtré.

Vérifiez que l'essentiel de cette leçon est acquis. Les questions portent uniquement sur ce qui vient d'être expliqué ici.

Contrôle de connaissances

Validez vos connaissances avec ce quiz interactif

6 questions
6 min.
70% requis

Informations

  • Le chronomètre démarre au clic sur Démarrer
  • Questions à choix multiples, vrai/faux et réponses courtes
  • Vous pouvez naviguer entre les questions
  • Les résultats détaillés sont affichés à la fin

Lance le quiz et démarre le chronomètre

Ce projet vous est utile ?

Les guides, formations, quiz et labs restent gratuits. Si vous les utilisez régulièrement, vous pouvez contribuer à leur maintenance et à leur développement.

Devenir membre de soutien (nouvel onglet) Soutenir une fois (nouvel onglet) En savoir plus

Suivez aussi le projet sur LinkedIn