[smtplib] problème pour envoyer un mail

Résolu
artemis-037 Messages postés 57 Date d'inscription   Statut Membre Dernière intervention   -  
artemis-037 Messages postés 57 Date d'inscription   Statut Membre Dernière intervention   -

Bonjour,

J'ai écrit un programme pour récupérer les logs de connexion sur mon serveur par mail 

import smtplib, ssl
import os
import subprocess

os.system('sudo sh -c "> /var/log/apache2/access.log"')

def envoi_mail(contenu):
    smtp_address = 'smtp.gmail.com'
    smtp_port = 465
    email_address = '*******************'
    email_password = '********************'
    email_receiver = '********************'
    context = ssl.create_default_context()
    with smtplib.SMTP_SSL(
        smtp_address,
        smtp_port,
        context=context
    ) as server:
        server.login(email_address, email_password)
        server.sendmail(email_address, email_receiver, contenu)

envoi_mail('demarage')
run = True
while run:
    v = subprocess.check_output(
        ['sudo', 'cat', '/var/log/apache2/access.log']
    ).decode('utf-8', errors="backslashreplace").split('\n')
    if v != ['']:
        print(v)
        envoi_mail('nouvelle connection' + str(v))
        os.system('sudo sh -c "> /var/log/apache2/access.log"')

Mais le problème, c'est que ce code envoie bien le mail avec démarrage mais lorsque je me connecte je reçois un mail vide au lieu de "nouvelle connection...")

J'attend vos réponses avec impatience

A voir également:

2 réponses

avion-f16 Messages postés 19252 Date d'inscription   Statut Contributeur Dernière intervention   4 507
 

Bonjour,

Chaque page Web peut entraîner des dizaines de requêtes HTTP à cause des CSS, JS et autres ressources incluses, cela risque de générer un grand nombre de connexions vers le serveur SMTP de Gmail, avec à chaque fois l'établissement d'une nouvelle session TLS et une authentification par utilisateur / mot de passe, et donc des performances médiocres, en plus de risquer de se heurter à des limites de connexion. Je parle là des "connexions" (au niveau TCP/TLS), mais Gmail applique aussi des limites journalières sur les e-mails envoyés. Si tu souhaites recevoir des notifications pour chaque requête HTTP, même si c'est une idée un peu farfelue, je te conseille de voir du côté de Gotify ou Ntfy. Peux-tu nous dire l'objectif final de ton projet ? Il existe sûrement une solution plus adaptée et pratique.

La deuxième chose qui m'interpelle, c'est que tu utilises des commandes shell pour lire/écrire dans les fichiers : pourquoi ne pas utiliser les fonctions Python appropriées ?

La façon dont tu surveilles le registre est loin d'être optimale : avec une boucle infinie sans aucun délai, sans aucune pause, de quoi faire grimper le CPU inutilement.
Je pense que la meilleure solution serait de configurer Apache afin de rediriger les logs vers ton programme et non vers un fichier. De cette façon, plus besoin de surveiller le fichier, ton programme est appelé à chaque nouvelle entrée. https://httpd.apache.org/docs/2.4/logs.html#piped
Si tu veux continuer avec le fichier access_log habituel, il y a des façons plus intelligentes de surveiller les changements, par exemple avec https://pypi.org/project/watchfiles/ sinon pourquoi pas avec un délai de quelques secondes entre chaque vérification.

Pour le problème que tu rencontres, c'est surprenant que tu reçoives un mail vide, il devrait au minimum contenir "nouvelle connection". Quel est le résultat affiché par la ligne 26 ? Si dans la fonction "envoi_mail" tu affiches la variable "contenu" ?

2
artemis-037 Messages postés 57 Date d'inscription   Statut Membre Dernière intervention   2
 

Merci pour ta réponse. Si par contenu, tu parles de la variable v, il y est bien écrit les log, et non, il n'y a même pas "nouvelle connexion" dans le mail.

Je vais rajouter un délai de 5 sec entre chaque actualisation, même si c'est le seul programme, qui tourne sur mon serveur pour le moment.

0
mamiemando Messages postés 33769 Date d'inscription   Statut Modérateur Dernière intervention   7 878
 

Bonjour,

J'ignore volontairement toutes les considérations liées à l'envoi d'email puisque tu parviens à en envoyer, mais sur le plan pratique, je partage l'avis d'avion-f16 (voir #1), tu risques d'en recevoir beaucoup si tu en envoies au moindre changement apporté à ce fichier de log :-)

Je signale par ailleurs qu'apache offre de base des mécanismes pour envoyer des emails à son administrateur sous certaines conditions (voir par exemple cette discussion).

Si malgré toutes ces réserves, tu souhaites poursuivre, l'idéal serait que ton programme python utilise watchdog. En s'inspirant de cette réponse, tu pourrais écrire :

import sys
import time
from watchdog.observers import Observer
from watchdog.events import FileSystemEventHandler


class MyHandler(FileSystemEventHandler):
    def __init__(self, path):
        super().__init__()
        self.path = path
        self.last_content = None
        with open(path, "r") as f:
            self.last_content = f.read()

    def on_modified(self, event):
        if not event.is_directory:
            with open(event.src_path, "r") as f:
                content = f.read()
                if (
                    self.last_content is not None and
                    content != self.last_content
                ):
                    change = content[len(self.last_content):]
                    self.notify_change(change)
                self.last_content = content

    def notify_change(self, change):
        print(self.path, ":", change, end="")


if __name__ == "__main__":
    path = sys.argv[1] if len(sys.argv) > 1 else '.'
    event_handler = MyHandler(path)
    observer = Observer()
    observer.schedule(event_handler, path, recursive=False)
    observer.start()
    try:
        while True:
            time.sleep(1)
    except KeyboardInterrupt:
        observer.stop()
    observer.join()

Utilisation : en admettant que le script python s'appelle monitor.py :

python3 monitor.py /var/log/apache2.log

Il ne reste qu'à adapter la méthode notify_change pour y créer un email dont le corps de message contiendra le contenu de la variable change.

Bonne chance

1
artemis-037 Messages postés 57 Date d'inscription   Statut Membre Dernière intervention   2
 

Ok, mais je comprends pas tout : comment les programme fait pour envoyer un mail et s'il faut que je le rajoute où faut-t'il le faire ?

Merci

0
mamiemando Messages postés 33769 Date d'inscription   Statut Modérateur Dernière intervention   7 878 > artemis-037 Messages postés 57 Date d'inscription   Statut Membre Dernière intervention  
 

Bonjour,

Comme je l'ai expliqué dans mon message :

Il ne reste qu'à adapter la méthode notify_change pour y créer un email dont le corps de message contiendra le contenu de la variable change.

 Donc quelque chose du genre :

import smtplib
import ssl
# ...


class MyHandler(FileSystemEventHandler):
    # ...

    def notify_change(self, change):
        smtp_address = 'smtp.gmail.com'
        smtp_port = 465
        email_address = '*******************'
        email_password = '********************'
        email_receiver = '********************'
        context = ssl.create_default_context()
        with smtplib.SMTP_SSL(
            smtp_address,
            smtp_port,
            context=context
        ) as server:
            server.login(email_address, email_password)
            server.sendmail(
                email_address,
                email_receiver,
                change
            )

# ...

Ensuite il y a plusieurs manières d'améliorer ce code.

  • Concernant le design, on pourrait imaginer que l'envoi de l'email soit implémenté dans une classe dédiée, et que ton handler prenne une instance de cette classe en paramètre.
  • Concernant la sécurité, il faut éviter de stocker un mot de passe en clair dans du code. Idéalement, ton programme devrait le lire depuis un fichier avec des droits restreints.

Bonne chance

1
artemis-037 Messages postés 57 Date d'inscription   Statut Membre Dernière intervention   2 > mamiemando Messages postés 33769 Date d'inscription   Statut Modérateur Dernière intervention  
 

Ok merci, je teste ça ce week end. Pour le moment je ne suis pas chez moi et je n'ai plus les accès. J'ai ouvert le port 22 avec une authentification par mot de passe, et comme ça me dit accès refusé, il y a certainement quelqu'un qui a changé le mot de passe.

0
mamiemando Messages postés 33769 Date d'inscription   Statut Modérateur Dernière intervention   7 878 > artemis-037 Messages postés 57 Date d'inscription   Statut Membre Dernière intervention  
 

Pour tes problèmes de connexion ssh, je ne peux que t'encourager à mettre en place une clé ssh et désactiver l'authentification par mot de passe (voir ici). Je ne m'étends pas plus car ce n'est pas le sujet ici. Si tu rencontres des difficultés, je t'invite à ouvrir un nouveau fil de discussion sur le forum Linux. Merci également de soigner l'orthographe dans tes futurs messages.

1
artemis-037 Messages postés 57 Date d'inscription   Statut Membre Dernière intervention   2 > mamiemando Messages postés 33769 Date d'inscription   Statut Modérateur Dernière intervention  
 

Merci et désolé pour les fautes, je suis dyslexique et tout ce qui va avec, je vais essayer de faire attention.

0