Sommaire
Des images sur le site
LinuxFr.org vous permet dâutiliser des images externes dans les contenus et commentaires du site. Ces images sont incluses en syntaxe markdown avec 
(soit en saisissant directement du Markdown, soit en cliquant sur lâicĂŽne dâajout dâimage dans lâĂ©diteur). Profitons-en pour rappeler que pour utiliser une image sur LinuxFr.org, vous devez vous assurer de respecter sa licence.
Nous vous encourageons donc Ă utiliser des images sous licence libre et Ă citer les auteurs (câest mĂȘme obligatoire pour les licences CC-by et CC-by-sa). Cette citation est tirĂ©e de la dĂ©pĂȘche dâannonce Un nouveau reverse-proxy cache pour les images externes sur LinuxFr.org de 2012.
Il est aussi recommandĂ© de mettre une vraie description textuelle, qui finira dans lâattribut alt de la balise img utilisĂ©e pour lâaccessibilitĂ© ou si lâimage ne peut ĂȘtre chargĂ©e. Il peut ĂȘtre utile de lui donner un titre qui apparaĂźtra lâautre du survol de lâimage Ă la souris par exemple.
Exemple :
, sur un site francophone contributif gĂ©rĂ© par une Ă©quipe bĂ©nĂ©vole par et pour des libristes enthousiastes.")

Buts du cache dâimages
Les raisons évoquées à la mise en place de img (sans ordre particulier) :
- la sĂ©curitĂ© : si une image externe nâest servie quâen HTTP (en clair donc) et est appelĂ©e au milieu dâune page LinuxFr.org elle-mĂȘme servie en HTTPS, alors le navigateur va rĂąler sur le mĂ©lange des genres. img permet de servir toutes les images identiquement (par exemple en HTTPS, et avec le certificat de LinuxFr.org, via le serveur frontal devant img). Ă noter que ces images ne sont pas servies directement depuis le domaine principal linuxfr.org mais depuis un sous-domaine img.linuxfr.org pour Ă©viter que le JavaScript embarquĂ© dans les images en SVG puisse servir de vecteur dâattaque contre le site.
- la protection de la vie privĂ©e des personnes visitant LinuxFr.org : seul LinuxFr.org voit les informations en provenance de leur navigateur (dont lâadresse IP). Les Ă©quipes dâadministration des diffĂ©rents sites ne les voient plus (elles voient lâadresse IP du serveur LinuxFr.org).
- une meilleure gestion du trafic : au lieu dâenvoyer tout notre public chercher individuellement chaque image, LinuxFr.org la rĂ©cupĂšre une fois et la rend disponible. Si le site externe fournissant lâimage est un serveur Ă faibles ressources (liaison ADSL avec faible dĂ©bit montant par exemple), la mise en cache permet de garantir quâil ne recevra quâun faible volume de requĂȘtes (la rĂ©cupĂ©ration se faisant initialement toutes les 10 min tant que des demandes arrivent, le cache expirant aprĂšs 10 min).
- la conservation des images : les images incluses depuis des sites externes peuvent ne plus ĂȘtre disponibles (lâentitĂ© a disparu, le serveur a Ă©tĂ© arrĂȘtĂ©, le domaine a Ă©tĂ© perdu, lâadresse a changĂ©, etc.). Nous avons donc un mĂ©canisme de cache pour que nous puissions continuer Ă servir une image mĂȘme si elle devient indisponible.
Parmi les conséquences de cette implémentation initiale, on peut citer :
- si le fichier est changĂ© sur le serveur distant (modifiĂ©, converti dans un autre format), lâancien fichier est servi jusquâĂ la prochaine rĂ©cupĂ©ration et le nouveau fichier ne sera servi quâĂ la prochaine rĂ©cupĂ©ration ;
- si le fichier est supprimĂ© sur le serveur distant, lâimage ne sera plus servie aprĂšs la prochaine rĂ©cupĂ©ration (car le serveur a rĂ©pondu que lâimage nâexiste plus) ;
- il est possible de modifier lâimage au passage : les images dâavatar sont retaillĂ©es pour une hauteur de 64 pixels par exemple ;
- il est possible de bloquer des images : les images problĂ©matiques (pub/spam, contenus pour adultes, images injurieuses, etc.) peuvent ĂȘtre bloquĂ©es et ne plus ĂȘtre servies ;
- par ailleurs img nâaccepte de servir que les images connues de LinuxFr.org dont le poids fait moins de 5 MiB.
Ă lâutilisation
Lors de lâĂ©criture dâun commentaire ou dâun contenu sur LinuxFr.org, une personne va ajouter une image externe via la syntaxe Markdown, par exemple 
Ce qui donne Ă lâaffichage :

Et cÎté code HTML :
<img src="https://linuxfr.org/images/logos/linuxfr2_classic_back.png" alt="Logo LinuxFr.org">
OK, mauvais exemple ce nâest pas une image externe, puisquâelle est hĂ©bergĂ©e sur LinuxFr.org justement. Prenons un autre exemple 
.
Ce qui donne Ă lâaffichage :

Et cÎté code :
<img src="//img.linuxfr.org/img/68747470733a2f2f617072696c2e6f72672f63616d7061676e652d323032342f72656c6169732f62616e6e6965726543616d7061676e65417072696c2e737667/banniereCampagneApril.svg" alt="April - Campagne dâadhĂ©sion" title="Source : https://april.org/campagne-2024/relais/banniereCampagneApril.svg">
Donc on sert lâimage via le sous-domaine img.linuxfr.org. On peut aussi noter le titre rempli automatiquement avec la source. Expliquons la nouvelle adresse :
- // on sert en https si la page est en https et en http si la page est en http (câest plutĂŽt un oubli quâautre chose, vu que le site est uniquement en https)
- img.linuxfr.org on sert depuis un sous-domaine du site
- 68747470733a2f2f617072696c2e6f72672f63616d7061676e652d323032342f72656c6169732f62616e6e6965726543616d7061676e65417072696c2e737667 est la version en texte-vers-hexadĂ©cimal de lâadresse dâorigine (68 pour h, 74 pour t (deux fois), 70 pour p, etc.). Il existe des sites et des outils en local pour faire cette conversion, mais cela ne concerne pas la simple utilisation du site.
- banniereCampagneApril.svg on met Ă la fin le nom du fichier pour ĂȘtre sympa si vous voulez sauver lâimage en local avec un nom plus explicite
Ceci Ă©tait le cas oĂč tout se passe bien, comme prĂ©vu, comme le voulait la personne qui voulait utiliser une image externe.
Voyons maintenant ce qui se passe dans le cas pas si rare oĂč la personne a donnĂ© une adresse dâimage invalide, une adresse ne pointant pas vers une image vers autre chose (cas extrĂȘmement frĂ©quent), une image trop grosse (plus de 5 MiB), etc. Il se passe la mĂȘme chose cĂŽtĂ© code, mais cĂŽtĂ© affichage, pas dâimage, et on voit seulement le texte alternatif dans son navigateur. Dans les coulisses, img a rĂ©pondu 404, cette adresse nâest pas disponible.
On note donc quâune mĂȘme image servie en http:// ou en https:// aura une adresse convertie en hexadĂ©cimal diffĂ©rente, donc sera vue comme une autre image par img. MĂȘme chose si le serveur externe accepte des adresses sans tenir compte de la casse, ou si on rajoute des paramĂštres dans lâadresse comme « ?mot_magique=merci ».
CÎté code Ruby on Rails
Un contenu ou commentaire est en cours de crĂ©ation et une image externe a Ă©tĂ© mentionnĂ©e. Le code de gestion des images va vĂ©rifier que lâimage est dĂ©clarĂ©e dans redis (crĂ©er lâentrĂ©e img/<adresse>
avec adresse lâadresse de lâimage en clair, ajouter un champ created_at
avec lâhorodatage, ajouter lâadresse dans la liste des derniĂšres images img/latest
) et renvoyer lâadresse via img.
Le code peut aussi modifier le champ status
dâune image dans redis pour mettre ou enlever un blocage (valeur Blocked) par lâĂ©quipe du site, et lâajouter/enlever de la liste des images bloquĂ©es img/blocked
.
CÎté img
Les schémas dans la documentation du service img explicitent les possibilités et les comportements.
Il est possible de faire un GET /status et on obtient une rĂ©ponse HTTP 200 avec un contenu OK. Câest utile pour tester que le service est lancĂ© (depuis lâintĂ©rieur de la plateforme).
Sinon, on peut envoyer des requĂȘtes GET /img/<adresse_en_hexa>
or GET /img/<adresse_en_hexa>/<nom_de_fichier>
pour les images, et GET /avatars/<adresse_en_hexa>
ou GET /avatars/<adresse_en_hexa>/<nom_de_fichier>
pour les avatars.
En se limitant aux requĂȘtes lĂ©gitimes, le comportement de img est le suivant :
- lâadresse demandĂ©e a Ă©tĂ© prĂ©cĂ©demment dĂ©clarĂ©e (dans redis par la partie code Ruby On Rails) sinon il rĂ©pond 404 ;
- lâadresse demandĂ©e nâest pas bloquĂ©e par lâĂ©quipe du site sinon il rĂ©pond 404 ;
- lâadresse est dĂ©jĂ dans le cache disque, alors il renvoie lâimage ;
- lâadresse nâest pas dans le cache disque et la rĂ©cupĂ©ration Ă©choue, il renvoie 404 (et va noter temporairement lâĂ©chec dans
img/err/<uri>
) ;
- lâadresse nâest pas dans le cache disque et la rĂ©cupĂ©ration a lieu (notĂ© temporairement dans
img/update/<uri>
): si le serveur rĂ©pond positivement Ă la demande, avec une image comme attendue, pas trop volumineuse, alors on la met en cache disque. Si câest un avatar, on peut retailler lâimage. On aura des champs supplĂ©mentaires stockĂ©s type
avec la nature de lâimage (en-tĂȘte Content-Type), checksum
avec un hachage SHA1 et etag
avec la valeur ETag (entĂȘte ETag).
Le cache est rafraßchi réguliÚrement.
img est un binaire statique en Go. Il offre des options pour dĂ©finir le couple adresse:port dâĂ©coute, pour dĂ©finir oĂč envoyer les logs, pour se connecter Ă une base redis, pour dĂ©finir le rĂ©pertoire du cache disque, pour choisir le User-Agent qui sera utilisĂ© pour les requĂȘtes externes, pour dĂ©finir lâavatar qui sera renvoyĂ© par dĂ©faut, et la possibilitĂ© de le lancer uniquement en mode audit interne pour vĂ©rifier la cohĂ©rence et lâĂ©tat des donnĂ©es et des fichiers.
Dans les logs on va trouver des infos comme :
2024/10/20 20:39:24 Status code of http://example.invalid/exemple1.png is: 404
2024/10/20 20:39:24 Fail to fetch http://example.invalid/exemple1.png (serve from disk cache anyway)
2024/10/20 20:44:12 Fetch http://example.invalid/exemple2.png (image/png) (ETag: "be5e-4dba836030980")
2024/10/20 20:44:12 http://example.invalid/exemple3.png has an invalid content-type: text/html;charset=UTF-8
2024/10/20 20:44:12 Fail to fetch http://example.invalid/exemple3.png (serve from disk cache anyway)
Ici lâexemple 1 est dĂ©jĂ en cache et peut ĂȘtre servi mĂȘme si on Ă©choue Ă le rĂ©cupĂ©rer Ă ce moment-lĂ . Lâexemple 2 vient dâĂȘtre rĂ©cupĂ©rĂ©. Lâexemple 3 a dĂ©sormais une adresse invalide (qui renvoie une page HTML au lieu dâune image) mais il existe en cache une image prĂ©cĂ©demment rĂ©cupĂ©rĂ©e.
Historique
img a Ă©tĂ© crĂ©Ă© par Bruno Michel en 2012. Adrien Kunysz amĂšne 5 commits en novembre 2013, mais globalement Bruno est le seul Ă travailler dessus (43 commits) jusquâen 2018. img fait le job et il nâest pas besoin dây retoucher trop souvent.
En 2022, Bruno quitte lâĂ©quipe du site, et par ailleurs il y a des montĂ©es de versions et des migrations Ă faire sur les serveurs de LinuxFr.org, et img fait partie des services Ă reprendre en main. Ce qui veut dire le comprendre, le documenter et au besoin lâamĂ©liorer.
Bref je dĂ©cide de me plonger dans img (2022-2024), car a priori ce nâest pas le composant le plus compliquĂ© du site (il vit dans son coin, il offre une interface, câest du Go, donc on a un binaire seulement Ă gĂ©rer).
Ătape 1 : je vais commencer par ajouter un Dockerfile permettant de recompiler img dans un conteneur, en contrĂŽlant la version de Go utilisĂ©e, en effectuant une dĂ©tection dâĂ©ventuelles vulnĂ©rabilitĂ©s au passage avec govulncheck. Cela me permet de valider que lâon sait produire le binaire dâune part, et que lâon offre Ă tout le monde la possibilitĂ© de contribuer facilement sur ce composant.
Ătape 2 : je vais tester le composant pour vĂ©rifier quâil fonctionne comme je le pense et quâil fait ce quâon attend de lui. Je vais ajouter une suite des tests qui couvrent les diffĂ©rentes fonctionnalitĂ©s et les vĂ©rifient en IPv4 et en IPv6, en HTTP 1.1 et en HTTP 2.0. Les tests utilisent Hurl et docker-compose (avec des images redis et nginx), et encore une fois lâidĂ©e de donner la possibilitĂ© de contribuer facilement. Ils comprennent des tests de types de contenus non pris en charge, le test de la limite Ă 5 MiB, diffĂ©rents types dâimages, le test de vie, des appels erronĂ©s (mauvais chemin, mauvaise mĂ©thode, etc). Le choix des cas de tests est basĂ© sur le trafic rĂ©ellement constatĂ© sur le serveur de production, sur les diffĂ©rents cas dans le code et un peu sur lâexpĂ©rience du testeur.
Ătape 2,5 : lâavatar par dĂ©faut renvoie sur le site de production, y compris sur les tests en dĂ©veloppement en local et sur le serveur de test du site. Jâen profite pour ajouter un paramĂštre pour cela (et cela permettra de passer du PNG au SVG par dĂ©faut).
Ătape 3 : encore une fois essayons de simplifier la vie dâhypothĂ©tiques personnes contributrices. Une petite modification pour que hurl et redis soient fournis via docker-compose et ne soient plus nĂ©cessaires sur le poste de dĂ©veloppement.
Ătape 4 : il est temps de documenter plus le fonctionnement. Jâavais dĂ©jĂ dĂ©crit les infos stockĂ©es dans redis, mais pour comprendre le systĂšme de cache, autant fournir des diagrammes pour illustrer ce qui se passe lors dâune requĂȘte et comment on passe dâun Ă©tat Ă un autre. Câest aussi lâoccasion de complĂ©ter la suite de tests en ajoutant des tests avant et aprĂšs expiration du cache, histoire de pouvoir documenter ces cas prĂ©cis.
Ătape 5 : en cas dâĂ©chec de rĂ©cupĂ©ration, une image Ă©tait indisponible jusquâĂ la prochaine rĂ©cupĂ©ration (donc potentiellement pendant 10 min). Autant servir lâancienne version en cache lorsque cela se produit : je modifie le code et les tests en consĂ©quence.
Ătape 6 : je sais que certaines images ont Ă©tĂ© perdues, que des adresses dâimages ont toujours Ă©tĂ© erronĂ©es, que des contenus et commentaires ont Ă©tĂ© supprimĂ©s et quâil nây a donc plus lieu de garder les images associĂ©es. Je dĂ©cide dâimplĂ©menter dans img un audit interne qui indiquera si des anomalies sont prĂ©sentes dans redis, si des images sont indisponibles ou si des entrĂ©es dans le cache disque ne correspondent plus Ă aucune image. Et jâajoute cet audit dans la suite de tests.
Ătape 7 : jâĂ©cris une dĂ©pĂȘche pour parler de tout cela.
Ăvolutions rĂ©centes
Dockerfile
Le fichier Dockerfile du projet permet :
- de partir dâune image officielle Go dâune version donnĂ©e, basĂ©e sur une distribution minimale Alpine
- de lâutiliser pendant la construction en prenant la liste des dĂ©pendances, en les tĂ©lĂ©chargeant, en prenant lâunique fichier source img.go et en le compilant statiquement avec lâoption pour retirer les chemins de compilation
- de rechercher les éventuelles vulnérabilités avec govulncheck
- dâajouter le paquet tzdata pour avoir les dĂ©finitions fuseaux horaires (nĂ©cessaire pour les conversions de/vers GMT pour les entĂȘtes type Last-Modified).
- de repartir dâune base Alpine en y mettant les dĂ©finitions de fuseaux horaires et le binaire issus de la partie construction, de dĂ©clarer le port dâĂ©coute et de lancer le binaire avec des variables disposant de valeurs par dĂ©faut.
La suite de tests
Pour lâutiliser, câest assez simple, il faut aller dans le rĂ©pertoire tests et lancer un docker-compose up --build, qui va produire le conteneur contenant img, et dĂ©marrer le redis et le nginx prĂ©configurĂ©s pour les tests. Si tout va bien, on attend, et au bout dâun moment il sâaffiche :
linuxfr.org-img-test_1 | All tests look good!
tests_linuxfr.org-img-test_1 exited with code 0
Rentrons un peu dans les détails.
Dâabord un fichier docker-compose.yaml qui dĂ©crit le rĂ©seau IPv4/IPv6 utilisĂ© pour les tests, lâimage redis qui sera utilisĂ©e (stockage gĂ©rĂ© par docker), lâimage nginx qui sera utilisĂ©e avec sa configuration et ses fichiers Ă servir pour les tests, lâimage img et son paramĂ©trage (dont lâaccĂšs au redis et au nginx) ainsi que le rĂ©pertoire du cache et enfin lâimage de la suite de tests qui est construit avec son Dockerfile, prĂ©vue pour faire du Docker-in-Docker et avoir accĂšs au cache img et aux fichiers nginx.
Le Dockerfile de tests est basĂ© sur une image Hurl (un outil pour faire des tests HTTP). On ajoute les fichiers de tests en .hurl, le script shell qui pilote le tout, on prĂ©voit dâavoir les paquets dont on aura besoin : bash (pas par dĂ©faut dans les Alpine), coreutils, docker et xxd (pour les conversions texte vers hexadĂ©cimal). Et on lance les tests par dĂ©faut.
La configuration nginx de test Ă©coute en HTTP sur le port 80 en IPV4 et IPv6 et permet de dĂ©finir des chemins avec des rĂ©ponses en HTTP 301, 302, 308, 400, 401, 403, etc. jusquâĂ 530 et mĂȘme 666 pour les codes invalides, ainsi quâune redirection infinie.
Dans les donnĂ©es de tests servies par nginx, on trouve des contenus du mauvais type, une image destinĂ©e Ă ĂȘtre bloquĂ©e, des images dans divers formats, une image trĂšs grande en pixels mais pas trop en octets, une image trop grande en octets, et un avatar Ă servir par dĂ©faut.
Sont aussi présents cinq fichiers de tests avec une extension en .hurl :
- le test de vie et les chemins hors img/ et avatars/
- les tests sur les avatars : adresse valide ou invalide, image inexistante, bon et mauvais types, comportements sur les différents codes HTTP et sur une boucle de redirection infinie
- les tests sur les images (dĂ©coupĂ©s en trois parties, la partie initiale, la partie entre la rĂ©cupĂ©ration initiale et lâexpiration du cache et enfin la partie aprĂšs la rĂ©cupĂ©ration et lâexpiration du cache.
Vient enfin le script shell qui pilote le tout :
- on dĂ©finit les variables pour les cibles IPv4/IPv6 et les binaires redis et img que lâon veut utiliser dans les autres conteneurs Docker
- on liste les images dans différentes catégories :
- celles qui vont Ă©chouer et ne comporteront donc quâune entrĂ©e dans redis sans rien dans le cache disque (avec sous-catĂ©gories possibles bloquĂ©es/non-bloquĂ©es)
- les images devant ĂȘtre en erreur
- les images qui iront normalement dans le cache
- on prépare des images qui seront altérées plus tard
- on purge le cache sur disque, on nettoie redis et on déclare toutes nos images comme le faire le code Ruby on Rails. Certaines sont déclarées bloquées pour les tests.
- on lance les premiers tests (en IPv4 et IPv6, en HTTP 1.1 et en HTTP 2.0)
- on modifie certaines images pour simuler un changement sur le serveur externe, une suppression sur le serveur externe ou un blocage par lâĂ©quipe de site
- on lance les tests post-rĂ©cupĂ©ration initiale mais avant lâexpiration du cache (toujours avec toutes les variantes)
- on force lâexpiration du cache
- on lance les tests post-expiration du cache (toujours avec toutes les variantes)
- si on est arrivĂ© jusquâici, câest quâon a passĂ© tous les tests Hurl, alors maintenant on recompte ce que lâon a dans redis et sur disque et on vĂ©rifie si ça correspond Ă nos attentes
- on nettoie les images mises volontairement en Ă©chec
- on lance le test dâaudit interne qui doit nous dire que tout va bien
- si on est arrivé jusque-là on écrit que tout va bien et on déclenche un sourire de satisfaction.
Lâaudit interne
Lâobjectif est de vĂ©rifier la cohĂ©rence des donnĂ©es dans redis, si des images sont indisponibles ou si des entrĂ©es dans le cache disque ne correspondent plus Ă aucune image.
Le binaire dâimg peut donc ĂȘtre appelĂ© en mode audit et lancer des contrĂŽles internes.
Dâabord il collecte la liste des fichiers dans le cache disque.
Ensuite il vérifie que toutes les images listées dans les derniÚres images (img/latest) existent comme entrées individuelles.
Puis il vĂ©rifie sâil existe des images bloquĂ©es (il rĂąlera sâil y en a) et si chacune existe comme entrĂ©e individuelle le cas Ă©chĂ©ant.
Ensuite on parcourt tous les entrĂ©es individuelles dâimages :
- on rĂąle si on tombe sur une entrĂ©e img/updated/ ou img/err/ sans date dâexpiration
- on rĂąle si on tombe sur une entrĂ©e img/ sans champ created_at, sans type ou dâun type inconnu, sans checksum, avec un statut inconnu, une image bloquĂ©e non prĂ©sente dans les images bloquĂ©es, un champ inconnu, une prĂ©sence inattendue dans le cache disque, etc. Et on marque les images que lâon a vu passer comme attendu dans le cache.
- on rĂąle sur tous les fichiers du cache restants (ne correspondant Ă aucune image)
- si on a rùlé, on renvoie 1, sinon 0
Le grand nettoyage
img a fonctionnĂ© pendant 12 ans en production : il a rencontrĂ© des bugs, des comportements inattendus, des contenus et commentaires ont Ă©tĂ© supprimĂ©s ou rĂ©Ă©ditĂ©s, etc. Il est donc probable quâil y ait besoin dâaller dĂ©poussiĂ©rer un peu tout cela et de retirer ce qui est inutile.
Les traces du grand nettoyage sont dâabord visibles dans la rĂ©trospective de la premiĂšre quinzaine de septembre 2024 :
- une « image » sur sept prĂ©sente un souci (nâest pas une image, adresse invalide, trop grosse, etc.) et nâest donc pas dans le cache sur disque (ce qui a conduit Ă pas mal de taf sur la partie gestion des images)
- les types de contenu (Content-Type) en provenance de sites variĂ©s et divers, câest quelque chose⊠entre les «âŻimage/JPEGâŻÂ» ou «âŻimage/PNGâŻÂ» en majuscules parce que, les charset=utf-8 ou UTF-8 ou⊠sur du binaire, les name= qui ne sont pas dans la norme⊠Wikimedia renvoie aussi du profile="https://www.mediawiki.org/wiki/Specs/SVG/1.0.0" (pareil ça semble en dehors de tout standard).
Dâabord jâattaque le sujet la fleur au fusil en me disant que ça va passer crĂšme, je fais un joli tableau qui rĂ©sume lâĂ©tat initial :
img/<uri> img/updated/<uri> img/err/<uri> blocked
total 25565 -21 634 160 5
no created_at 23 -23 0 0 0
created_at 2857 -3 0 5 1
created_at+type 222 0 0 0
total not in cache 3104 -26 0 0 0
created_at+type+checksum(+etag) 22463 +5 634 155 4
files in cache 22778 +5
Donc on a officiellement 25âŻ565 images, mais 23 sont mal crĂ©Ă©es (Ă©tat thĂ©oriquement impossible hors race condition), 222 sont incomplĂštes (Ă©tat thĂ©oriquement impossible race condition), 22âŻ463 sont attendues en cache et on a 22âŻ778 fichiers dans le cache. Ăa part mal. Je nettoie en premier le plus facile (on voit le delta +/- de mes corrections). Et on arrive Ă une situation oĂč une image sur sept prĂ©sente alors un souci et il faut gĂ©rer un grand volume de corrections Ă faire.
Parmi les soucis on trouve des types de contenus inattendus (image/PNG ou image/JPEG avec majuscules, image, des images binaires annoncĂ©es avec un charset, des types invalides comme image/jpg au lieu de image/jpeg, etc), des erreurs de notre lectorat (mauvais lien, mauvais copier-coller, lien vers une page web au lieu dâune image), mais aussi des espaces insĂ©cables et autres blancs inopportuns, des guillemets convertis, des doubles scheme (http://https://
ou http://file://
).
AprĂšs cela se cache une autre catĂ©gorie encore plus pĂ©nible : les images que lâon a en cache, mais qui ne sont plus utiles au site : par exemple celles qui Ă©taient dans des contenus ou commentaires supprimĂ©s (notamment le spam), celles qui Ă©taient dans des commentaires ou contenus rĂ©Ă©ditĂ©s depuis, etc.
Un problĂšme connu est devenu vite pĂ©nible : on nâa pas dâassociation entre les images externes et les contenus/commentaires concernĂ©s. Donc il faut dâabord extraire la liste de toutes les dĂ©clarations dâimages externes des 12 tables SQL oĂč lâon peut trouver des images et des avatars, sous forme HTML ou Markdown.
Ensuite il faut sortir toutes les entrĂ©es dans redis et regarder si on les retrouve en clair ou converties en hexadĂ©cimal dans lâextraction SQL.
Et par sécurité on fera une double vérification pour celles détectées en erreur, en relançant une recherche en base (attention à la casse dans la recherche texte).
Au final, on peut supprimer des milliers dâentrĂ©es redis et de fichiers dans le cache.
Et un jour lâaudit dit :
Connection 127.0.0.1:6379 0
2024/10/19 12:11:21 Sanity check mode only
2024/10/19 12:11:37 Files in cache: 17926
2024/10/19 12:11:39 Total img keys in redis: 18374
OK
Ăa aura pris un mois et demi (lâaudit a Ă©tĂ© fusionnĂ© le 8 septembre 2024), certes pas en continu, mais ça a Ă©tĂ© long et guĂšre palpitant de faire ce grand mĂ©nage. Et jâai refait une seconde passe du traitement complet la semaine dâaprĂšs pour vĂ©rifier que tout se passait correctement et que les soucis rĂ©siduels aprĂšs tout ça Ă©taient minimes ou nuls.
Parmi les anecdotes, Web Archive / archive.org a eu sa fuite de comptes utilisateurs et a Ă©tĂ© indisponible sur la fin (ce qui rendait compliquĂ© la rĂ©cupĂ©ration dâimages perdues ou leur remplacement par un lien valide par exemple). Et, mentionnĂ© dans la rĂ©trospective de la seconde quinzaine de septembre 2024, un compte de spammeur de 2015 supprimé⊠mieux vaut tard que jamais : dĂ©tectĂ© parce que comme beaucoup de visiteurs, le spammeur ne fait pas la diffĂ©rence entre un lien vers un document et lâajout dâune image.
Les problématiques restantes
Il y a la question habituelle de la montĂ©e de versions des dĂ©pendances (pour nous actuellement contraintes celles du code Ruby on Rails) et du remplacement des composants devenus non-libres (migrer vers valkey plutĂŽt que redis ? Questions Ă se poser sur lâavenir de nginx ?).
On pourrait aussi ajouter la prise en charge du TLS et dâun certificat X.509 directement dans img plutĂŽt que dans un frontal. Mais ce nâest utile que si on les sĂ©pare sur deux serveurs distants, ce qui nâest pas le cas actuellement. Donc mĂȘme si ça ne paraĂźt pas compliquĂ© Ă faire, ce nâest pas urgent.
Ensuite une entrĂ©e de suivi existe pour sĂ©parer le cache des avatars du cache des autres images : les contraintes pour le cache des avatars Ă©tant diffĂ©rentes de celui des autres images, le stockage en cache devrait ĂȘtre diffĂ©rent. Cela reste un problĂšme mineur. Le changement doit dâabord ĂȘtre fait cĂŽtĂ© Ruby on Rails pour dĂ©finir les avatars avec des clĂ©s redis diffĂ©rentes (genre avatars/ au lieu de img/). Ensuite on peut modifier img pour sĂ©parer le traitement des requĂȘtes HTTP /img/<adresse_hexa>
vers les clés redis img/<adresse>
et le cache disque des images par rapport aux requĂȘtes /avatars/<adresse_hexa>
vers les clés avatars/<adresse>
et le cache des avatars. Il faudra aussi dĂ©placer les avatars stockĂ©s dans lâactuel cache des images dans leur propre cache. Et lĂ on devrait pouvoir avoir la mĂȘme adresse dans les deux caches mais avec un rendu Ă©ventuellement diffĂ©rent.
Un autre problĂšme concerne la non-association des contenus ou commentaires avec les images externes quâils contiennent, ce qui rend lâadministration des anciennes images un peu pĂ©nible. Le fait que les contenus et commentaires peuvent ĂȘtre rĂ©Ă©ditĂ©s ou simplement prĂ©visualisĂ©s (donc que des images peuvent ĂȘtre supprimĂ©es et dâautres ajoutĂ©es) vient compliquer un peu la tĂąche. Actuellement un ensemble de scripts permettent dâobtenir ces infos et fournissent un contournement, mais ça reste un peu laborieux.
Un cache rafraĂźchi pĂ©riodiquement conserve les images pour Ă©viter de surcharger le site dâorigine, pas si le site a changĂ©, dĂ©placĂ© ou perdu lâimage. La modification pour servir depuis le cache disque en cas dâĂ©chec de rĂ©cupĂ©ration couvre le cas de la disparition dâune image avec une erreur sur lâadresse, pas celui oĂč le serveur rĂ©pond une mauvaise image. Il y a donc une autre entrĂ©e de suivi images et disparition du web Ă©voquant lâaugmentation des soucis sur les images externes avec un cache rafraĂźchi, en raison des domaines rĂ©cupĂ©rĂ©s par des spammeurs et autres pĂ©nibles, ou perdus ou utilisĂ©s pour du phishing (imageshack.us, aprĂšs framapic, pix.toilelibre, etc.). Diverses problĂ©matiques sont mentionnĂ©es comme la perte dâinformation et donc la diminution de lâintĂ©rĂȘt des contenus anciens, la prime aux pĂ©nibles du rĂ©fĂ©rencement SEO qui pourrissent le net en rĂ©cupĂ©rant les vieux domaines, la modification possible des images publiĂ©es. Pour rĂ©soudre cela techniquement, ça nĂ©cessite de suivre les images et les domaines perdus, et dâintervenir de façon rĂ©guliĂšre. Ou bien de ne plus rafraĂźchir le cache (que cela soit jamais, aprĂšs la publication ou au bout dâun certain temps aprĂšs la publication). Pour juste Ă©viter la perte dâinfo, il est possible de remplacer par une image locale rĂ©cupĂ©rĂ©e dâune archive du net type archive.org, avec le cĂŽtĂ© « pĂ©nible Ă faire » et sans garantie que ça soit toujours possible (merci waybackpy).
Enfin une troisiĂšme entrĂ©e de suivi suggĂšre l'hĂ©bergement des images des dĂ©pĂȘches (et Ă©ventuellement des journaux), idĂ©alement en permettant dâavoir une version modifiĂ©e dâune image en changeant sa taille. On peut citer en vrac comme problĂ©matiques la responsabilitĂ© lĂ©gale, lâĂ©ventuelle volumĂ©trie, lâimpossibilitĂ© de corriger une image publiĂ©e facilement par la personne qui lâa soumise, la centralisation et la perte de rĂ©fĂ©rencement pour des tiers, lâĂ©ventuelle rĂ©troactivitĂ© et le traitement de lâhistorique, le fait quâil faut traiter tous les autres contenus/commentaires pouvant accueillir des images, etc. Autre question, faut-il diffĂ©rencier les images passĂ©es en modĂ©ration a priori de celles en modĂ©ration a posteriori ?
Conclusion ?
Bref sans surprise, il reste des problĂ©matiques et du code Ă faire pour les gĂ©rer (câest rare un composant sans demandes dâĂ©volution ou de correction). Yapuka (mais probablement plus tard, il faut aussi partager le temps avec les autres composants, ou avoir plus de contributions).
img apporte les fonctionnalitĂ©s que lâon attendait de lui mĂȘme si on pourrait faire mieux. Plonger dans ce composant sâest avĂ©rĂ© assez intĂ©ressant et formateur (et nĂ©cessaire) : techniquement cela a Ă©tĂ© lâoccasion de faire du Go, du docker et du docker-compose, du redis et du nginx, du hurl et de lâHTTP. Et de comprendre ce que faisait un code Ă©crit par une autre personne, de se poser des questions pour choisir les tests et le contenu de la documentation, de se demander pour quelles raisons tel ou tel choix a Ă©tĂ© fait, de rendre ce composant plus « contribuable », et de complĂ©ter le tout de façon dĂ©taillĂ©e avec une dĂ©pĂȘche. Reste Ă savoir si jâai rĂ©pondu Ă lâattente dâun article technique sur le fonctionnement de ce cache, les choix techniques qui ont Ă©tĂ© faits, les erreurs commises donc Ă Ă©viter⊠et la rĂ©ponse est Ă trouver dans les commentaires.