Sélectionner une page

Il y a quelques jours, nous sommes tombés sur un cas d’école de pourquoi un hébergeur infogérant comme Octopuce fait le choix d’en faire « un petit peu plus » pour ses clients, et ce que cela implique en terme de sécurité, fiabilité, et gain final pour les clients.

Un de nos clients, qui dispose d’une boutique sous Prestashop, a quelques problèmes de cache. Nous avions signalé le problème au client, et l’agence qui développe le site s’apprête à faire des mises à jour et améliorations de ces aspects. Cependant, un problème régulier apparait : le site tombe, saturant la CPU, et cela apparaît à chaque fois dans les minutes après l’envoi de la newsletter du site à ses abonnés.

Nous constatons la saturation CPU un matin, et allons voir les logs de visite : près de 2000 visites en 10 minutes (entre 8h et 8h10 en gros). C’est exceptionnel pour ce site. C’est aussi très étonnant : ce n’est pas normal, en envoyant une newsletter à 8h00 que le trafic se concentre entre 8h et 8h10 : les gens n’ouvrent pas toutes et tous leur mail au moment pile de leur réception, et ne cliquent pas tant que cela sur les liens qu’ils contiennent. Mystère !

Une petit analyse plus tard, on tombe sur un bloc d’adresses IP qui a fait à lui seul 60% du traffic. Un autre 20% et un 3e, 10%… Le premier bloc est à OpenDNS by Cisco, le second des IP AWS (hébergement web par amazon, impossible de savoir qui est derrière) et le troisième est l’IP d’un service d’antispam allemand… C’est ce 3e qui nous a mis sur la piste.

L’Email, en 2023 ça reste compliqué

Lorsque vous envoyez une newsletter, celle-ci arrive sur les serveurs mail de vos destinataires, et passe le plus souvent à travers un antispam, un antivirus, donc divers outils de qualification, avant d’être distribué (ou pas). Certains antispam vont visiter les liens présents dans les mails, pour vérifier que ces derniers ne comprennent pas de code malveillant, ne sont pas des sites connus de phishing etc.

Ce que l’on a compris de cette situation c’est que lors de l’envoi de la newsletter de ce client, les mails arrivent sur des boites mails qui vérifient la non-nocivité des liens présents dans le mail, et pour ce faire, vont surfer sur le site de notre client… client qui, avec ses petits problèmes de cache, se retrouve avec la CPU saturée en cas de trop de visites simultanées.

Notre premier réflexe dans ce genre de situation serait de régler la limite du nombre de pages par IP à une valeur plus faible, pour éviter que ces robots ne saturent le serveur, et les bloquer au-delà de cette limite. Mais comme souvent, on décide de réfléchir avant de sortir l’artillerie lourde : si on bloque ces services antispam rapidement, le risque que les liens soient considérés comme non-sûr (à défaut d’être considérés comme dangereux) risquerait de provoquer leur non-distribution !

Nous devions donc trouver une solution pour empêcher le plantage du site, tout en permettant à OpenDNS (surtout lui, les autres sont mineurs) de nous demander près de 1200 pages en moins de 10 minutes …

La solution fut toute trouvée : nous avons dégainé le joker Varnish <3 !

Varnish, un cache programmable, puissant et libre

Varnish est un logiciel mandataire web (un « proxy ») permettant de mettre en cache des pages et de les servir depuis le cache (en ram ou sur disque) si celles-ci n’ont pas été modifiées. Sa puissance réside dans plusieurs très beaux morceaux de code : une capacité à gérer un nombre de connexions très grand, une optimisation de la mémoire et du disque utilisés pour le cache, et surtout un langage de configuration complet (le VCL) permettant de déteminer avec précision quelle page mettre en cache et quelle page ne pas cacher, de manière très adaptée au code.

Avec Varnish qui a l’immense bonne idée d’être un logiciel libre, si votre site est bien fait, les économies de puissance (CPU/RAM) sont énormes, et donc autant dire de suite : on ADORE Varnish chez Octopuce !

La solution

On a donc installé un Varnish sur le serveur de ce client, et l’avons intercalé entre le Nginx (qui sert les pages HTTPS) et le serveur web qui calcule les pages Prestashop. Nous avons demandé à Varnish de ne rien cacher par défaut (ou du moins de conserver son comportement par défaut, qui ne cache que s’il en a l’autorisation explicite du serveur web).

Ensuite, nous avons ajouté une règle à Varnish pour lui dire : si l’internaute vient d’un de ces blocs d’adresses IPs (on les appelera « les pénibles »), tu peux mettre en cache, et ce de manière totalement agressive : on ignore totalement les cookies et en-têtes nous empêchant de mettre en cache.

Ainsi, non seulement le site pourra continuer à fonctionner dans de bonnes conditions quand OpenDNS viendra saturer de requêtes le site, mais cela ne consommera quasiment aucune ressource, et permettra à 0penDNS de donner une bonne réputation au site donc à la newsletter.

Même pas mal !

Vous trouverez ci-dessous la configuration Varnish dans le langage VCL, pour vous aider si ce genre de problème vous arrive :

vcl 4.0;

import std;

# Default backend definition. Set this to point to your content server.
backend default {
    .host = "127.0.0.1";
    .port = "8000";
    .first_byte_timeout = 300s;
}

acl penibles {
    "172.0.0.0/24"; # VPN Octopuce pour tests
    "146.112.163.0/24"; # OpenDNS Cisco
    "54.236.1.0/24"; # AWS bot inconnu
    "54.71.187.0/24"; # AWS bot inconnu
    "94.100.132.0/24"; # Hornetsecurity GmbH
}

sub vcl_recv {
    # Happens before we check if we have this in cache already.
    # Typically you clean up the request here, removing cookies you don't need,
    # rewriting the request, etc.

    if (req.http.X-Real-IP) { 
       set req.http.X-Forwarded-For = req.http.X-Real-IP;
    }
       
    if (std.ip(req.http.x-real-ip, "0.0.0.0") ~ penibles) {
       return (hash);
    }
    return (pass);
}

sub vcl_backend_response {
    # Happens after we have read the response headers from the backend.
    # Here you clean the response headers, removing silly Set-Cookie headers
    # and other mistakes your backend does.
    if (std.ip(bereq.http.x-real-ip, "0.0.0.0") ~ penibles) {
      unset beresp.http.Expires;  
      unset beresp.http.Cache-Control;  
      unset beresp.http.Set-Cookie;  
      unset beresp.http.Pragma;  
      set beresp.http.Cache-Control = "public, max-age=60";  
      set beresp.grace = 4h;
      set beresp.ttl = 4h;
    }

}