Antivirus — détails techniques
Ce que le dispositif contrôle, les réglages de plateforme et leurs effets : Antivirus des pièces jointes.
Où l'analyse s'insère
Tous les modules déposent leurs pièces jointes par le même chemin,
Storage#writeUploadFile d'entcore. L'analyse est branchée là, et une seule fois : elle
couvre donc d'un coup l'espace documentaire, la messagerie, le casier, le formulaire, le
cahier de textes, exercizer, workflowhub, le chat, la vidéo, les présences, les compétences…
L'ordre est ce qui compte : l'analyse a lieu avant l'acquittement de l'upload.
POST /workspace/document
└─ FileStorage.writeUploadFile
├─ écriture du fichier sur le stockage
├─ appel du service antivirus (clamd)
│ ├─ sain / ignoré / désactivé → 200 {_id, status: ok}
│ └─ infecté + mode bloquant → suppression du fichier
│ 400 {status: error, message: file.infected}
└─ suite du traitement du module
C'est ce qui distingue ce dispositif de l'antivirus historique d'ODE (cf. Infra), qui analysait après coup et se contentait de remplacer le contenu du fichier une fois celui-ci déjà accepté et référencé.
L'utilisateur voit le message file.infected (ou file.not.scanned si l'analyse n'a pas pu
aboutir et que la plateforme est réglée pour refuser dans ce cas), traduit en français et en
anglais.
Raccordement (ent-core.yaml)
Bloc file-system.antivirus du module infra, recopié dans la configuration partagée et donc
lu par tous les modules. Seul le raccordement est ici — la politique est dans le service.
file-system:
path: ${BASE_PATH}/storage/
antivirus:
url: ${ANTIVIRUS_URL}
mode: ${ANTIVIRUS_MODE} # stream | path
failOnError: false
timeout: 35000
Modes stream et path
stream(défaut) : le contenu est poussé au service. Ne suppose aucun volume partagé, donc le seul mode viable en développement local et sur un stockage S3. Au-delà de 32 Mo le contenu est relayé sans être gardé en mémoire ; la quarantaine, qui a besoin du contenu, est alors sautée.path: le service lit le fichier sur le volume de stockage, monté chez lui en lecture seule au même chemin. Rien ne transite sur le réseau, y compris pour une vidéo de 200 Mo, et la quarantaine fonctionne quelle que soit la taille. Exigeantivirus.sharedStorage: truedans le chart ; le volume de stockage étant enReadWriteOnce, le chart impose alors la co-localisation des deux pods.
failOnError
Service injoignable ou analyse en échec : par défaut l'upload passe — une panne du scanner
ne doit pas mettre l'ENT à genoux — et l'incident est journalisé et compté. failOnError: true
inverse le choix : refuser plutôt qu'accepter un fichier non analysé.
Déploiement
Le conteneur est déployé par le chart monolithe
(helm/openent-monolithe/templates/antivirus.yaml) : un Deployment, un Service interne et
un volume persistant. Ce volume compte : sans lui, chaque redémarrage retélécharge ~250 Mo de
signatures et perd les compteurs.
clamd charge toute la base en mémoire — en dessous de ~2 Gi il est tué au chargement, d'où les ressources par défaut du chart (2 Gi demandés, 3 Gi en limite).
Diagnostic
L'image n'embarque pas curl : on interroge l'API avec bun, qui est présent.
AV=$(kubectl -n ent get pod -l app.kubernetes.io/component=antivirus -o name | head -1)
# état complet : moteur, base virale, politique, compteurs, dernières détections
kubectl -n ent exec $AV -- bun -e \
'fetch("http://127.0.0.1:3550/status").then(r=>r.text()).then(console.log)'
# le moteur détecte-t-il vraiment ? (fichier de test EICAR)
kubectl -n ent exec $AV -- bun -e \
'fetch("http://127.0.0.1:3550/selftest",{method:"POST"}).then(r=>r.text()).then(console.log)'
Le service n'est pas exposé publiquement : c'est un ClusterIP, joignable seulement depuis
l'intérieur du cluster.
La signature EICAR ne se déclenche que sur le fichier de test exact (68 octets) ou sur ce
fichier dans une archive. Concaténer EICAR à un gros fichier ne produit aucune détection :
c'est le comportement de ClamAV, pas un défaut du dispositif. Pour un test réaliste sur un gros
fichier, mettre EICAR dans un .zip.
Couverture de tests
Le client d'analyse est couvert par 6 tests unitaires
(common/src/test/java/org/entcore/common/storage/OpenEntAntivirusClientTest.java d'entcore) :
fichier sain non bloqué, fichier infecté bloqué, infecté non bloqué en détection seule,
erreur du service non bloquante par défaut, service injoignable bloquant sous failOnError.
L'image du conteneur est validée à chaque publication par la chaîne d'intégration, qui exige que le fichier de test EICAR soit détecté dans l'image publiée — une image qui démarre sans rien analyser ne passe pas.
L'écran d'administration n'a pas encore de test e2e.
Conformité
| Maillon de la chaîne qualité | Référence |
|---|---|
| 🎯 Fonctionnalités attendues | fiche fonctionnelle |
| 🧪 Tests réalisés | client d'analyse : 6 tests unitaires ; image : test EICAR en intégration continue ; écran d'administration : aucun test e2e à ce jour |
| ✅ Tests de conformité | tableau de conformité |
Voir aussi
- Analyse de contenu — détails techniques — l'autre contrôle branché sur les contenus déposés, côté texte et non côté fichier.
- Infra — l'antivirus historique d'ODE, qui analysait après coup.