Utiliser Varnish pour injecter du contenu sans toucher au backend
Varnish, en voilà une techno qu’on ne voit pas tous les jours sur les plateformes modernes d’hébergement, et pourtant, ça se déploie même dans Kubernetes Et si je vous dis que j’ai eu à gérer un cas de fourniture de contenu dans un contexte plus que particulier, avec du Varnish en frontal, et que je ne pouvais pas passer par le serveur web ? Allez, je vous explique.
Varnish, un logiciel méconnu, mais très puissant
Varnish est un serveur « cache », qui fonctionne comme un « proxy » entre votre site web et ses visiteurs. Son rôle est de garder en mémoire temporairement le résultat de la construction des pages de votre site. En effet, quand vous visitez un site comme le mien par exemple, le moteur WordPress, qui doit être chargé en premier lieu, doit construire l’intégralité du code HTML de la page, avec les références aux feuilles de style, aux scripts JavaScript des différents plugins, et le contenu de la page en question, ce qui implique des appels à la base de données. Bref, un paquet d’activités à rejouer pour chaque visiteur, chaque page visitée. Si le contenu d’une page n’a pas à bouger pendant un certain temps, on peut se dire que générer une fois la page, et servir le résultat X fois pendant ledit certain temps, est un gain de ressources et donc de performances non négligeable. C’est un des points forts des CDN, allié à l’aspect géographique de ce type de service.
Varnish est reconnu pour son empreinte minimaliste, et ses performances redoutables. Il est aussi connu pour la mentalité des développeurs du projet concernant la sécurité (ils se sont réjoui le jour où ils ont ouvert une section « security advisory », après 10 ans d’existence). Il est aussi devenu un peu pénible à utiliser depuis que le HTTPS s’est généralisé, parce que les développeurs ont pris le parti de ne pas l’implémenter, ce qui a réduit un peu son intérêt. L’usage des CDNs étant une autre raison du désamour croissant.
Le contexte particulier qui nous intéresse
Particulier, c’est bien le mot : le site sur lequel je dois intervenir est déployé avec Docker, un container avec Varnish en frontal, un container avec Apache/PHP derrière. Mais j’ai plusieurs problèmes avec cette plateforme. Les containers sont déployés avec docker-compose, mais je n’ai pas accès au fichier, tout est construit et déployé avec un pipeline Jenkins, jusque là rien que de très commun me direz-vous. Sauf que le serveur Jenkins n’existe plus non plus, je n’ai accès à aucune source ni aucun support de l’agence web d’origine (plus de contrat). Le bonheur de tout sysadmin.
On me demande de mettre à jour le « merchant ID » d’Apple pour le site web (un identifiant qui sert pour Apple Pay), et ça passe par un fichier spécial qui doit être joignable avec une URI de la forme /.well-known/apple-developer-merchantid-domain-association
. Pas de bol, le container avec le site web est en lecture seule (comprendre, l’utilisateur du container n’est pas le propriétaire du « DocumentRoot », le dossier qui contient le site web). Mais, le Varnish 5 présent en front, lui, il l’est, d’autant plus qu’on a accès à son compte root !
On est donc tout équipé pour tenter une greffe à chaud sans anesthésie.
Opération à cœur ouvert
J’identifie le fichier « VCL » qui contient la configuration relative au site, et j’attaque la première tentative. On rajoute un test dans la section vcl_recv{}
:
sub vcl_recv { if (req.url ~ "^/.well-known/apple-developer-merchantid-domain-association") { return (synth(200,"7C227073704964223B2236304337424...")); }
return (synth(()
est la méthode à utiliser si on veut renvoyer du contenu qui ne vient pas du backend. Il faut par contre définir quelques propriétés dans la section vcl_synth{}
pour qu’il ne renvoie pas du HTML par défaut mais du texte brut :
sub vcl_synth { set resp.http.Content-Type = "text/plain; charset=utf-8"; set resp.body = resp.reason; return(deliver); }
Vient ensuite l’injection à chaud de la configuration. C’est un truc que j’aime bien avec Varnish, on peut lui demander de charger plusieurs configurations en parallèle et basculer entres celles-ci en fonction des besoins. En gros, voilà ce que je fais :
bash-5.0# varnishadm 200 ----------------------------- Varnish Cache CLI 1.0 ----------------------------- Linux,3.10.0-1160.83.1.el7.x86_64,x86_64,-junix,-smalloc,-sdefault,-hcritbit varnish-6.2.1 revision 9f8588e4ab785244e06c3446fe09bf9db5dd8753 Type 'help' for command list. Type 'quit' to close CLI session. varnish> vcl.list 200 active warm warm 2 boot varnish> vcl.load apple /var/varnish-docker/dockerized.apple.vcl 200 VCL compiled. varnish> vcl.list 200 active warm warm 2 boot available auto warm 0 apple varnish> vcl.use apple 200 VCL 'apple' now active varnish> quit 500 Closing CLI connection bash-5.0# curl -s http://127.0.0.1:6081/.well-known/apple-developer-merchantid-domain-association 7C227073704964223B2236304337424(...)
Le détail du fonctionnement des commandes Varnish se trouve dans la documentation.
Hourra ! Enfin presque, parce qu’après avoir informé le client, ce dernier me dit que c’est toujours pas bon. Et là, le gaillard qui travaille sur l’intégration Apple Pay m’indique qu’il y a un souci avec le checksum (première fois que c’est mentionné dans nos échanges). La documentation que l’on ne m’avait évidemment pas fourni indiquait bien de livrer le fichier et de ne pas copier son contenu. Je vous le donne en mille, il manque un retour à la ligne à la fin.
J’ai donc bossé sur une alternative. La doc de Varnish me renvoie vers un std.fileread()
. Je copie donc le fichier dans le même répertoire que le VCL, et j’en profite pour faire une syntaxe un poil plus propre au niveau de ce dernier, la voici.
sub vcl_recv { if (req.url ~ "^/.well-known/apple-developer-merchantid-domain-association") { return (synth(200,"apple")); } (...) } sub vcl_synth { (...) if (resp.reason == "apple") { set resp.http.Content-Type = "text/plain; charset=utf-8"; set resp.body = std.fileread("/var/varnish-docker/apple-developer-merchantid-domain-association"); return(deliver); } }
Et là, j’ai bien le bon checksum (franchement, pour un retour à la ligne…). À noter qu’il faut bien faire attention aux permissions du fichier qu’il doit lire – même si dans le cas présent on est root-, et que le contenu n’est pas lu dynamiquement, mais mis en cache au chargement à l’instar du reste de la configuration, il faut donc jouer avec use/discard/load/use de varnishadm pour jouer avec les configs (ou charger avec un nom différent à chaque fois, mais c’est un peu plus dégueu).
Dans mon cas, vu le peu de persistance nécessaire je me suis arrêté là. Le site était en cours de refonte et c’est d’ailleurs ce qui a motivé le choix de l’ajout du fichier en premier lieu sur cette plateforme, vu qu’elle répondait déjà au domaine. Je n’avais pas le temps de tenter un reverse des images pour la jouer au niveau du serveur web, et c’était cool au final de bosser avec Varnish, que je vous recommande de découvrir rien que pour comprendre comment fonctionnent certains CDNs.
Amusez-vous bien