26
août 2013

Gestion des comptes pour vsftp avec python

Pour les besoins d'un projet j'ai mis en place un serveur ftp, j'ai retenue vsftp pour différente raison que je ne détaillerais pas ici car ce n'est pas l'objet de ce billet. Les utilisateurs de ce serveur sont des clients qui n'ont pas a avoir d'autres accès sur le réseau ou sur le serveur donc je ne devais pas les ajouter en tant que compte système ou même dans l'annuaire ldap. J'ai utilisé une base local Db Berkley dont on retrouve la configuration dans quasiment tout les tuto sur vsftp.

Le principe est de créer un fichier texte contenant le nom d'utilisateur et le mot de passe, ensuite lancer une commande à base de db_load  [...]. C'est très facile à mettre en place mais j'ai été confronté à devoir supprimer des comptes utilisateurs de cette base. Après pas mal de recherche rien dans les outils fournis dans db4-utils. Par chance j'avais décidé d'écrire un petit script pour gérer la création de compte ftp en python et magie il y a tout ce qui faut dans le module dsddb. Du coups dans mon script je gère l'ajout, la suppression et le listage des comptes. Soyez pas trop dur c'est mon premier programme en python donc il y a surement mieux, plus propre mais c'est fonctionnel comme ça.

Attention : ce script a été testé avec python 2.4.3

En ajout le programme :

  • Créé le répertoire de l'utilisateur (et des sous-répertoires que j'avais besoin avec un chmod différent suivant le cas);
  • Créé le fichier de config pour vsftpd;
  • Ajoute le compte à la base de données;
  • Monte des répertoires public à la racine du /home de l'utilisateur.


En suppression :

  • Il fait l'opération inverse de la création.

Mise à jour :

26/08/2013

  • Ajout mountUser pour monter tout les répertoires utilisateurs;
  • Ajout umountUser pour démonter tout les répertoires utilisateurs.

 

#!/usr/bin/python
# -*- coding: utf-8 -*-
# __author__ Philippe MALADJIAN (http://blogoflip.fr)
# __version__ 1.0

import os
import re
import hashlib
import time
import shutil
import stat
from bsddb import db

from optparse import OptionParser

# --- Variable global -------------
rootFtpDir ="/var/ftp"
rootFtpConf ="/etc/vsftpd"
userFtp = ""
# ---------------------------------

def createDir(dir):
    print "Création du répertoire " + dir
    try:
        os.makedirs(rootFtpDir + "/" + dir)
    except OSError:
        print "***[Warning] Erreur à la création du répertoire " + rootFtpDir + "/" + dir

def createUser(userFtp):
    if not userFtp:
        userFtp = raw_input('Taper le numéro de compte et le numéro d\'adresse de livraison (ex: 020005c01) : ')

    if re.compile('[^a-zA-Z0-9]').search(userFtp):
        print "Nom de compte invalide"
    else:
        if os.path.isdir(rootFtpDir + "/" + userFtp):
            print "Attention l'utilisateur existe déjà"
            os._exit(1)

        userFtp = userFtp.lower()

        createDir(userFtp + "/commandes")
        os.chmod(rootFtpDir + "/" + userFtp + "/commandes", 0775)
        os.chown(rootFtpDir + "/" + userFtp + "/commandes", 0, 50)
        createDir(userFtp + "/commandes/archives")
        os.chmod(rootFtpDir + "/" + userFtp + "/commandes/archives", 0775)
        os.chown(rootFtpDir + "/" + userFtp + "/commandes/archives", 0, 50)
        createDir(userFtp + "/tracking")
        os.chmod(rootFtpDir + "/" + userFtp + "/tracking", 0775)
        os.chown(rootFtpDir + "/" + userFtp + "/tracking", 0, 50)
        createDir(userFtp + "/bon_liv")
        os.chmod(rootFtpDir + "/" + userFtp + "/bon_liv", 0775)
        os.chown(rootFtpDir + "/" + userFtp + "/bon_liv", 0, 50)
        createDir(userFtp + "/maj_catalogue")
        createDir(userFtp + "/maj_stock")

        print "Création du fichier de configuration ftp"
        fichier = open(rootFtpConf + "/vsftpd_user_conf/" + userFtp, "w")
        fichier.write("local_root=" + userFtp + "\n")
        fichier.write("write_enable=yes" + "\n")
        fichier.write("anon_upload_enable=yes" + "\n")
        fichier.write("anon_mkdir_write_enable=no" + "\n")
        fichier.write("anon_other_write_enable=yes" + "\n")
        fichier.write("local_umask=022" + "\n")
        fichier.write("anon_umask=022" + "\n")
        fichier.write("virtual_use_local_privs=no" + "\n")
        fichier.close()

        print "Ajout du compte utilisateur au ftp"
        password = hashlib.md5(time.strftime('%j%H%M%S')).hexdigest()
        password = password[0:10]

        print "Génération du fichier db"
        logindb = db.DB()
        try:
            logindb.open(rootFtpConf + "/login.db", None, db.DB_HASH, db.DB_CREATE)
        except Exception, err:
            print "Erreur avec la connexion à la base !"
            print str(err)
            os._exit(1)

        try:
            logindb.put(userFtp, password)
        except Exception, err:
            print "Erreur pendant l'ajout du compte " + userFtp
            print str(err)
            os._exit(1)

        logindb.close()

        print "Montage du répertoire maj_catalogue"
        os.system("mount -r --bind /home/public/export/maj_catalogue " + rootFtpDir + "/" + userFtp + "/maj_catalogue")

        print "Montage du répertoire maj_stock"
        os.system("mount -r --bind /home/public/export/maj_stock " + rootFtpDir + "/" + userFtp + "/maj_stock")
        
        print "+------------------------------+"
        print "| Compte créé avec succès      |"
        print "+------------------------------+"
        print "login : " + userFtp
        print "password : " + password           

# +--------------------------------+
# | Supprime un compte utilisateur |
# | @userFtp : nom d'utilisateur   |
# +--------------------------------+
def deleteUser(userFtp):
    if os.path.isdir(rootFtpDir + "/" + userFtp):
        print "Démontage des répertoires"
        try:
            os.system("umount " + rootFtpDir + "/" + userFtp + "/maj_catalogue")
            os.system("umount " + rootFtpDir + "/" + userFtp + "/maj_stock")
        except Exception, err:
            print "Erreur pendant le démontage du répertoire"
            print str(err)
            os._exit(1)

        print "Suppression du répertoire " + rootFtpDir 
        try:
            shutil.rmtree(rootFtpDir + "/" + userFtp)
        except Exception, err:
            print "Erreur pendant la suppression du répertoire " + userFtp
            print str(err)
            os._exit(1)

        print "Suppression du fichier de configuration ftp"
        try:
            os.remove(rootFtpConf + "/vsftpd_user_conf/" + userFtp)
        except Exception, err:
            print "Erreur pendant la suppression du fichier de configuration " + userFtp
            print str(err)
            os._exit(1)

        print "Suppression du compte ftp"
        logindb = db.DB()
        try:
            logindb.open(rootFtpConf + "/login.db", None, db.DB_HASH, db.DB_CREATE)
        except Exception, err:
            print "Erreur avec la connexion à la base !"
            print str(err)
            os._exit(1)

        try:
            logindb.delete(userFtp)
        except Exception, err:
            print "Erreur pendant la suppression du compte " + userFtp + "de la base"
            print str(err)
            os._exit(1)

        try:
            logindb.close()
        except Exception, err:
            print "Erreur de connexion à la fermeture de la base : "
            print str(err)
            os._exit(1)
    else:
        print "L'utilisateur " + userFtp + " n'existe pas"

# +--------------------------------+
# | Liste des comptes utilisateurs |
# | présent dans rootFtpDir        |
# +--------------------------------+
def listUser():
    print "+--------------------------------+"
    print "| Comptes clients                |"
    print "+--------------------------------+"

    logindb = db.DB()
    try:
        logindb.open(rootFtpConf + "/login.db", None, db.DB_HASH, db.DB_DIRTY_READ)
    except Exception, err:
        print "Erreur de connexion avec la base : "
        print str(err)
        os._exit(1)

    # -------------------------
    # Utilisé pour le debug
    #cursor = logindb.cursor()
    #rec = cursor.first()

    #while rec:
    #    print rec
    #    rec = cursor.next()
    # -------------------------

    for user in logindb.keys():
        print "- " + user

    try:
        logindb.close()
    except Exception, err:
        print "Erreur de connexion à la fermeture de la base : "
        print str(err)
        os._exit(1)

# +-----------------------------+
# | Monter les répertoires des  |
# | clients                     |
# +-----------------------------+
def mountUser():
        logindb = db.DB()
        try:
                logindb.open(rootFtpConf + "/login.db", None, db.DB_HASH, db.DB_DIRTY_READ)
        except Exception, err:
                print "Erreur de connexion avec la base : "
                print str(err)
                os._exit(1)

        for user in logindb.keys():
                os.system("mount -r --bind /home/public/export/maj_catalogue " + rootFtpDir + "/" + user + "/maj_catalogue")
                os.system("mount -r --bind /home/public/export/maj_stock " + rootFtpDir + "/" + user + "/maj_stock")
        try:
                logindb.close()
        except Exception, err:
                print "Erreur de connexion avec la base : "
                print str(err)
                os._exit(1)

# +-----------------------------+
# | Démonter les répertoires    |
# | des clients                 |
# +-----------------------------+
def umountUser():
        logindb = db.DB()
        try:
               logindb.open(rootFtpConf + "/login.db", None, db.DB_HASH, db.DB_DIRTY_READ)
        except Exception, err:
                print "Erreur de connexion avec la base : "
                print str(err)
                os._exit(1)

        for user in logindb.keys():
                os.system("umount " + rootFtpDir + "/" + user + "/maj_catalogue")
                os.system("umount " + rootFtpDir + "/" + user + "/maj_stock")
        try:
                logindb.close()
        except Exception, err:
                print "Erreur de connexion avec la base : "
                print str(err)
                os._exit(1)

def main():
    try:
        parser = OptionParser()
        parser.add_option("-a", "--add", dest="createAccount", help="Ajouter un compte client", type="string", default=False)
        parser.add_option("-d", "--del", dest="deleteAccount", help="Supprimer un compte client", type="string", default=False)
        parser.add_option("-l", "--list", action="store_true", dest="listAccount", help="Liste les comptes clients", default=False)
        parser.add_option("-m", "--mount", action="store_true", dest="mountUser", help="Monte les répertoires des utilisateurs", default=False)
        parser.add_option("-u", "--umount", action="store_true", dest="umountUser", help="Démonte les répertoires des utilisateurs", default=False)

        (options, args) = parser.parse_args()

        if options.createAccount:
            createUser(options.createAccount)

        if options.deleteAccount:
            deleteUser(options.deleteAccount)

        if options.listAccount:
            listUser()

        if options.mountUser:
             mountUser()

        if options.umountUser:
             umountUser()

    except KeyboardInterrupt:
        print "\n"
        os._exit(0)

if __name__ == '__main__':
    main()

Administrateur système de métier mais surtout curieux de découvrir de nouvelles technos très orientées DIY. A mes heures perdues je fais de la photo avec toujours une petite envie d'intégrer des DIY sous forme de timelaps à base de raspberry.

Écrire un commentaire

Quelle est la deuxième lettre du mot tytzcd ? :

Gestion des comptes utilisateurs pour vsftp avec python - Philippe Maladjian - Péripéties bucoliques d'un administrateur systèmes au royaume de la virtualisation, du stockage et accessoirement photographe à ses heures perdues