S’étirer quelques muscles techniques.
Les moments de temps libre où j’essaie de faire quelque chose lié à la technologie se font de plus en plus rares avec le temps. Avant, je le regrettais en me disant que je stagnais en termes de compétences. Il m’a fallu pas mal de temps et d’échanges avec des gens intéressants pour réaliser que ce qui s’était réellement passé, c’est que j’avais pris une direction professionnelle qui me permet désormais de faire ce que j’aime dans mon travail, en laissant un peu de place à la vie personnelle, ce qui est très important et de plus en plus ignoré chez les gens de la tech. Néanmoins, de temps à autre, je m’adonne encore au bricolage technique en guise de hobby, et ceci en fut l’un de ces cas.
Un peu de contexte
Je vis en Argentine, nous avons actuellement une plateforme d’e-commerce dominante qui tente d’occuper l’espace que remplissent e-bay et amazon ailleurs ; elle s’appelle Mercado Libre. Parmi ses services, elle propose une publication gratuite d’articles à vendre, sélectivement reléguée par rapport à d’autres articles en vente qui ont payé pour une publication plus premium (ce qui est, bien sûr, tout à fait juste), mais il y a une condition assez récente du service que je n’aime pas : elle oblige à utiliser son processeur de paiement, appelé Mercado Pago. J’ai eu des expériences passées plutôt désagréables avec lui (il m’a fait perdre beaucoup d’argent il y a quelques années) et les avis sur sa médiation de résolution des conflits ne se sont pas améliorés au fil des ans. Avec ce problème en tête, j’ai décidé que je préférais publier l’article sur mon propre blog, qui est un site statique fait avec hugo. Je voulais que les gens puissent me contacter pour des questions et peut-être m’envoyer des offres, mais je n’avais absolument pas envie de les faire se connecter à disqus pour ça, alors voici ma solution.
Tirer parti de l’infrastructure existante.
Ma règle pour ceci est : ça doit être court. Je l’ai fait un après-midi après le travail. Ça ne doit nécessiter aucun nouveau service sur mon serveur, je ne voulais pas ajouter de travail au moi du futur. Ça ne doit pas ajouter de nouveaux vecteurs d’attaque à mon serveur, la dernière chose dont j’ai besoin, c’est d’un souci de plus.
Il s’avère que j’ai déjà une ligne de communication avec les visiteurs de mon site, ça s’appelle l’access.log (oui, tu vois où je veux en venir). Mon serveur web actuel (nginx) prend en charge un access.log par route et un formatage personnalisé pour chacune.
Récupérer les « messages des utilisateurs »
La première chose dont j’avais besoin était un moyen simple de parcourir mes logs pour un chemin particulier ; disons que j’ai choisi /getmessages.
Un formatage rationnel pour les logs
J’allais avoir besoin que mes logs soient structurés pour ça, je n’ai pas envie d’écrire des invocations à cthulu en regexp juste pour obtenir le message, alors j’ai pris un exemple sur ce blog et choisi un format qui serait utile pour logstash (ce qui est bon pour eux est bon pour moi)
log_format logstash_json '{ "@timestamp": "$time_iso8601", '
'"@fields": { '
'"remote_addr": "$remote_addr", '
'"remote_user": "$remote_user", '
'"body_bytes_sent": "$body_bytes_sent", '
'"request_time": "$request_time", '
'"status": "$status", '
'"request": "$request", '
'"request_method": "$request_method", '
'"http_referrer": "$http_referer", '
'"http_user_agent": "$http_user_agent" } }';
La sortie de tout ceci ressemble plus ou moins à :
{
"@timestamp": "2018-02-01T18:23:48+00:00",
"@fields": {
"remote_addr": "xxx.xxx.xxx.xxx",
"remote_user": "-",
"body_bytes_sent": "220",
"request_time": "0.000",
"status": "404",
"request": "GET /getmessages/hello HTTP/2.0",
"request_method": "GET",
"http_referrer": "-",
"http_user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:58.0) Gecko/20100101 Firefox/58.0"
}
}
Ça a l’air assez confortable, je pourrais me passer des particularités du format mais, comme je l’ai dit, moins je travaille sur les parties ennuyeuses de tout ça, mieux c’est, puisque c’était une idée lancée à la va-vite.
Maintenant il ne reste qu’à configurer quelques éléments personnalisés dans nginx, comme le journal d’accès pour cette route particulière et un 404 personnalisé qui affichera un remerciement pour le message afin d’éviter les confusions (tu pourrais te montrer plus créatif avec un peu de javascript et peut-être des règles de redirection, je suppose, mais là c’était juste pour jouer).
AVERTISSEMENT la page 404 personnalisée empêche l’enregistrement des logs, alors j’essaie de déterminer ce qui se passe.
[...]
location /getmessage {
access_log /var/log/nginx/your_access.json logstash_json;
error_page 404 /messages404.html;
}
[...]
Décider des conventions.
Même si tout ce qui passerait par là serait soit un message soit du spam, je devais décider comment savoir quel message concernait quoi.
Je me suis fixé sur /getmessage/<topic_code>?<messagefields>, ainsi l’url contiendrait un code de sujet, une chaîne arbitraire au demeurant qui m’indiquerait de quel post/sujet il s’agissait, et les champs des messages seraient de simples formulaires encodés en GET ; cela offre de la flexibilité quant aux données envoyées et de l’uniformité pour pouvoir filtrer les sujets qui ne m’intéressent pas.
Analyser et visualiser les logs.
Pour ça, j’ai créé un petit binaire en go que je lance avec un cron chaque nuit ; on peut le trouver en bas.
Dans les coulisses
J’ai quelques protections supplémentaires en place pour éviter le spam et autres intentions malveillantes.
Lecture.
C’est une boîte aux lettres, j’utilise simplement mutt.
Laisse un commentaire :p
<form action="/messaging/blogpost-greetings" method="GET">
<div>
<label for="name">Name:</label>
<input type="text" name="name" id="name"> </input>
</div>
<div>
<label for="email">Email:</label>
<input type="email" name="email" id="email"> </input>
</div>
<div>
<label for="message">Message:</label>
<textarea type="textarea" name="message" id="message"> </textarea>
</div>
<div class="button">
<button type="submit">Talk to the nginx</button>
</div>
</form>