Files
BaC/content/2020-06-25_Journaliser-les-conversations-qui-passent-par-ses-serveurs-SMTP.md
2025-02-27 12:52:48 +01:00

151 lines
9.9 KiB
Markdown
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

+++
title = "Journaliser les conversations qui passent par ses serveurs SMTP"
date = 2020-06-25
aliases = [ "post/2020/06/25/Journaliser-les-conversations-qui-passent-par-ses-serveurs-SMTP"]
[taxonomies]
tags = [ "mail", "système" ]
+++
Parce que ça peut toujours servir
<!-- more -->
Récemment, je me suis un peu fritté avec Postfix et ElasticSearch et jai fini par me dire que ça pourrait en intéresser certains. Le challenge est assez simple en réalité : être capable de repérer très rapidement qui a parlé à qui et à quel moment.
Et en fait, cest pas aussi simple que ça.
## Au début, il y avait **syslog**
La base du problème nest finalement pas si compliqué que ça : quand on a un ou plusieurs serveurs SMTP, on peut commencer par balancer lensemble des logs dans **syslog**. Ça permet de les centraliser et ça permet également de faire des recherches croiser quand on en a plusieurs.
Jusque là, rien de bien sorcier. Je vous passe également le fait de balancer ces logs dans **Logstash**. Je ne dis pas que cest à la portée du premier con venu, mais ça dépasse quelque peu le cadre de cet article (je te rassure on va quand même en faire un peu, mais je ne détaillerai pas plus que ça).
Bref, je pars du principe que vous avez un **syslog** qui concentre tout et qui repasse le bébé à un **Logstash**.
## Ensuite, il fallu faire un peu le tri
alheureusement, comme souvent avec ELK, il va être nécessaire de faire le tri en amont pour ne pas se retrouver avec des grosses conneries un peu partout. Dans **Logstash**, il va falloir faire un **pipeline** et y appliquer des filtres. Donc, on va insérer ça dans le fichier `/etc/logstash/pipelines.yml` :
~~~
- pipeline.id: syslog
path.config: "/etc/logstash/syslog/conf.d/*.conf"
~~~
Ça permet de créer une configuration complètement à part pour **syslog** avec des entrées, des sorties et tout ce qui va bien, tout en faisant un truc à peu près propre. À partir de ce moment-là, on peut créer, dans `/etc/logstash/syslog/conf.d/`, deux fichiers `10_input.conf` et `90_output.conf` :
`10_input.conf` :
~~~
input {
udp {
port => 1234
type => "syslog"
}
}
~~~
`90_output.conf` :
~~~
output {
elasticsearch {
index => "syslog-%{+YYYY.MM.dd}"
template_name => "syslog-*"
}
}
~~~
**Si tu te poses la question, il faut que les fichiers soient lus dans le bon ordre pour faire les choses correctement, donc oui, je mets des chiffres en début de fichiers, cest moche mais au moins, je suis certain que la conf est lue dans le bon sens.**
Ça permet de créer une entrée (type **syslog**) et une sortie dans un **template** Elasticsearch. Jusque là, on na pas vraiment réinventé la mayonnaise, on prend juste des entrées et on les balance dans des sorties (et figurez-vous que ça fonctionne, cest un truc de gue-din). Bon, maintenant, on a certes des données, mais elles ne sont absolument pas triées.
En gros, on a une date et un message, mais cest à peu près tout. Et ça ne nous avance pas beaucoup. Donc, on va faire un premier filtre qui va permettre de récupérer les données **syslog** et de commencer à faire un premier tri. Tu peux faire un fichier intermédiaire (quon appellera `20_filter_10_begin.conf`) :
~~~
filter {
if [type] == "syslog" {
grok {
match => { "message" => "%{SYSLOGTIMESTAMP:syslog_timestamp} %{SYSLOGHOST:syslog_hostname} %{PROG:syslog_program}(?:\[%{POSINT:syslog_pid}\\])?: %{GREEDYDATA:syslog_message}" }\
add_field => [ "received_at", "%{@timestamp}" ]
add_field => [ "received_from", "%{host}" ]
}
date {
match => [ "syslog_timestamp", "MMM d HH:mm:ss", "MMM dd HH:mm:ss" ]
}
}
}
~~~
Voilà, maintenant, tu relances ton **Logstash** et tu devrais commencer à récupérer quelques données. Elles sont relativement brutes : tu as le serveur, la date et lheure, le nom du programme et cest à peu près tout. Ça peut déjà pas mal servir, mais on va quand même essayer daffiner un peu…
## Et après, on laisse affiner pendant quelques mois, comme pour le Beaufort
aintenant, tu vas [aller télécharger ces quelques fichiers](https://github.com/whyscream/postfix-grok-patterns) et les coller dans les bons répertoires (`/etc/logstash/syslog/patterns/` pour le fichier `.grok` et dans `conf.d` pour le reste, en faisant bien attention à le renommer pour quil soit lu après le reste, mais avant le **output**).
Une fois que tu as fait ça, tu peux de nouveau relancer **Logstash**. Ces modèles sont très très bien faits et permettent de vraiment trier complètement les différentes étapes de traitement de chaque message : mise en queue, traitement, livraison, nettoyage, etc…
Tu peux déjà tamuser dans linterface de Kibana pour essayer de retrouver certains messages, des destinataires, des expéditeurs et ainsi de suite :
![img](/postfix_premier_tri.png)
Et rien quavec ça, tu peux déjà faire pas mal de merdes ! Tu peux faire des rapports permettant de voir le nombre de messages échangés (suffit de compter les **queueid** uniques), tu peux voir le volume moyen des messages échangés, etc…
Tout ça, cest très bien, mais à un moment, quelquun viendra forcément te poser la question : et comment je fais pour savoir qui a écrit à qui ?
## Ça a lair simple comme ça, mais en fait non…
Dit comme ça, ça a effectivement lair tout con et dans labsolu, tu te dis probablement que tu as la donnée, donc que ça ne devrait pas être si compliqué que ça à extraire. En fait, si. Le souci, cest que tu as effectivement la donnée : si tu tries en fonction du **queueid** de Postfix, tu peux effectivement savoir qui a écrit à qui. *Mais dun point de vue données, tu nas pas tout sur une seule ligne !*
Et Elasticsearch a ce côté un peu chiant : si tu nas pas toutes les infos dont tu as besoin sur la même ligne, dans le même document, dans le même enregistrement, alors faire des recoupements peut devenir extrêmement pénible. Alors évidemment, pour de la recherche manuelle, ce nest pas bien compliqué : tu cherches le destinataire, tu trouves le **queueid**, tu retries par **queueid** et tu finiras bien par tomber dessus.
ais à partir du moment où tu dois le confier à quelquun dautre ou simplement si tu veux faire des stats ou des rapports un peu plus poussés, bah ça va être très compliqué, voire impossible.
## Bah alors, on fait quoi du coup ? À part avoir lair con…
Jarrive mes petits bisous, jarrive. En fait, jai tourné en rond sur ce problème pendant un bon moment. Jai dabord cherché à voir si je pouvais faire des corrélations un peu complexes dans ELK lui-même. Ça na pas vraiment donné les résultats que jattendais (probablement pas impossible à faire, mais bien trop compliqué à mon goût). Il paraît que la version payant dELK sait faire pas mal de choses de ce point de vue là, mais je suis trop pauvre pour ça (pour un besoin pro, ça pourrait se justifier, pour un besoin perso, cest quand même plus compliqué).
Jai ensuite cherché comment faire du multiligne. Cest possible, et ça existe, il y a même des [plugins spéciaux](https://www.elastic.co/guide/en/logstash/current/plugins-codecs-multiline.html) pour ça. Il y a même [des gens qui lont déjà fait](https://github.com/whyscream/postfix-grok-patterns).
Le souci, cest que tout cela date un peu (beaucoup de fonctions ont changé) et que je ne suis jamais parvenu à le faire fonctionner correctement. Avec un peu de temps et defforts, cest peut-être possible de le faire fonctionner, mais sincèrement bon courage !
Bref, après avoir bien galéré sur le sujet pendant un moment, je me suis dit quil était peut-être possible denrichir les logs de Postfix avec dautres données. Si on narrive pas à traiter en aval, il vaut mieux revenir sur les données en amont pour essayer de pousser plus de choses ou de rendre les choses un peu différemment.
Première chose à faire donc, dire à Postfix de journaliser dautres informations sur les messages reçus. Par exemple, leur sujet : tous les messages ont un sujet, cela doit donc être possible de le journaliser. Et bien, oui et cest même relativement simple. Dans la configuration de Postfix (`main.cf`), il suffit dajouter cette ligne :
~~~
header_checks = regexp:/etc/postfix/header_checks
~~~
Puis de créer le fichier de **regexp** correspondant :
~~~
/^Subject:/ WARN
~~~
Là, on dit à Postfix de journaliser tout message contenant lentête **Suject** (donc tous les messages en gros) et de lui attribuer le niveau **WARNING**. On relance Postfix et là, voilà ce quon obtient :
~~~
Jun 25 16:32:57 smtp-1 postfix/cleanup[32674]: C9D27601B2: warning: header Subject: RE: I iz a subject from mail1.example.com[203.0.113.1]; from=<bidule@example.org> to=<truc@example.net> proto=ESMTP helo=<mail1.example.com>
~~~
\o/\
Non seulement Postfix a journalisé le sujet du message (ce qui est déjà très bien, ça nous permet de visualiser cela en une seule ligne), mais, coup de bol, il récupère aussi les autres informations du message en bonus. Et figure-toi que les filtres que tu as utilisé avant permettent de récupérer toutes ces informations directement dans ELK.
Dans Kibana, il suffit alors de mettre en place un petit filtre :
* `syslog_program: postfix`
* `postfix_message_level: warning`
* `postfix_to exists`
* `postfix_from exists`
Et tu vas récupérer exactement la liste de lensemble des messages qui sont passés par ton SMTP, avec le destinataire, lexpéditeur et le sujet du message :
![img](/postfix_second_tri.png)
## Conclusage
Voilà, maintenant, tu as tout le nécessaire pour aller récupérer des données hyper précises sur tes SMTP et ça ne va pas être très compliqué de récupérer tous les messages dont machin est le destinataire ou tous les messages volumineux à destination de truc ou même faire des gros graphiques de ouf.