Aprovisionamiento de cuentas en línea de comando

Script creación de cuentas con redirección

Script de creación de cuentas con recuperación de otra cuenta en POP

El script a continuación fue inicialmente creado para la migración del correo del CUP y luego del CURE, en el que fue necesario recuperar en POP el contenido de las cuentas en el servidor anteriormente utilizado (en SeCIU).

Realiza lo siguiente:
  • creación de una cuenta con nombre, apllido, usuario y dominio,
  • configuración de una cuenta externa accedida en POP desde el servidor. En términos zimbra: creación de una fuente de datos que accede una cuenta POP3
  • envío de un correo electrónico a la misma cuenta (no aún migrada) para informar de la creación de la cuenta y entregar la contraseña temporaria.

(NB: En una versión intermedia del script, creabamos una carpeta IMAP en la casilla del servidor para la conexión externa. No pudimos configurar un acceso IMAP2, pero sí hubieron conexiones IMAPs concluyentes (CSIC, por ejemplo)

Para crear las cuentas en masa, necesitamos una tabla con todos los datos necesarios, conformes a este formato: Formato de Migración de datos. Los recopilamos en una planilla, y lo exportamos en texto plano, en formato CSV.
Las columnas son las siguintes: nombres,apellidos, dirección de correo,identificador de usuario,dominio,contraseña temporaria de la cuenta creada,contraseña de acceso POP/IMAP a la vieja cuenta.

En python, podemos usar el siguiente script para generar un script que crea todas las cuentas de una vez:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# CrearCuentasZimbra.py: Crea cuentas zimbra para un dominio con conexión POP a cuentas en SeCIU, a partir de un CSV

import sys
import os
import pwd
import argparse
import csv
import subprocess
import codecs

# Import smtplib for the actual sending function
import smtplib

# Import the email modules we'll need
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email import Charset

reload(sys) 
sys.setdefaultencoding('utf8')

# Gestión (muy básica) de un argumento: la lista de correos por crear, en CSV, bien formateada. 
parser = argparse.ArgumentParser(description='Genera un script de creación de cuentas en zimbra.')
parser.add_argument('listaCSV', nargs='?', type=argparse.FileType('rb'), default=sys.stdin,
                   help='una lista de nombres y correso en archivo CSV con los datos siguientes: nombre,apellido,usuario,dominio,tmpPasswd,POPpasswd')

args = parser.parse_args()

# hay que ejecutar este script con el usuario zimbra, para que ejecute correctamente los comandos de zimbra
# OjO: entrar a zimbra con un 'su zimbra', no 'su - zimbra' de manera a tener correctamente la local en UTF-8
if pwd.getpwuid(os.getuid())[0] != 'zimbra':
        print("Hay que correr este script con el usuario zimbra, para los comandos zmprov")
        sys.exit(1)

# el mismo hecho de declarar el argumento como FileType parece abrirlo: args.listCSV ya contiene el archivo
with args.listaCSV as correos:
    tabla = csv.reader(correos, delimiter=',', quotechar='"')
    # la primera línea son los títulos de los campos. 
    # el orden de los campos es: nombre,apellido,usuario,dominio,tmpPasswd,POPpasswd
    tabla.next()

        #Luego tomamos el archivo linea por linea
        #OjO: no hay verificación ni saneamiento de datos,. hay que dar un archivo sano. 
    for linea in tabla:
                print linea
                nombre = linea[0]
                apellido = linea[1]
                usuario = linea[2]
                dominio = linea[3]
                tmpPasswd = linea[4]
                POPpasswd = linea[5]
                correo = '{0}@{1}'.format(usuario,dominio)
                if nombre == '':
                        NombreCompleto = apellido
                else: 
                        NombreCompleto = '{0} {1}'.format(nombre,apellido)
                # {0} a {6} son los campos de la línea, o sea en orden: nombre,apellido,usuario,dominio,tmpPasswd,POPpasswd
                # estas lineas estan solo para el print que sigue. lo podriamos sacar. 
                zCrearCuenta = 'zmprov ca {2}@{3} {4} givenName "{0}" sn "{1}" displayName "{0} {1}" \
                zimbraPasswordMustChange TRUE'.format(*linea)
                print(zCrearCuenta)

                #Aca creamos realmente la cuenta, llamando a zmprov
                zCrearCuentaArgs = ('zmprov','ca',correo, tmpPasswd,'givenName', nombre,'sn',apellido,'displayName',NombreCompleto,'zimbraPasswordMustChange','TRUE')
                subprocess.check_call(zCrearCuentaArgs)

                #Aca creamos, con zmmailbox, una carpeta IMAP en la cuenta, donde se recibiran los correos de SeCIU
                #OjO: el .rstrip() esta porque el resultado termina con un: \n
                #IdCarpeta = subprocess.check_output(('zmmailbox','-z','-m',correo,'createFolder','-F','u','/{0}-SeCIU'.format(correo))).rstrip()
                # print(IdCarpeta)
                subprocess.check_call(('zmprov','cds',correo,'pop3','{0}-SeCIU'.format(correo),'zimbraDataSourceEmailAddress',correo,'zimbraDataSourceEnabled', 'TRUE',\
                                                                'zimbraDataSourceHost', 'mail.seciu.edu.uy', 'zimbraDataSourceUsername',correo,'zimbraDataSourcePassword',POPpasswd,'zimbraPrefFromAddress',correo, \
                                                                'zimbraDataSourceImportOnly','FALSE', 'zimbraDataSourceLeaveOnServer','TRUE',\
                                'zimbraPrefFromDisplay',NombreCompleto,'zimbraDataSourceConnectionType','cleartext','zimbraDataSourcePort','110','zimbraDataSourceFolderId','2','zimbraDataSourcePollingInterval','5m'))
                                #'zimbraPrefFromDisplay',NombreCompleto,'zimbraDataSourceConnectionType','cleartext','zimbraDataSourcePort','110','zimbraDataSourceFolderId',IdCarpeta,'zimbraDataSourcePollingInterval','5m'))

                DatosCorreo = {'nombre' : nombre,'apellido' : apellido,'usuario' : usuario,'dominio' : dominio,'tmpPasswd' : tmpPasswd}
                FileCorreo = codecs.open('correo.txt', 'rb', 'utf-8')
                #print(FileCorreo.read())

                TextoCorreo = FileCorreo.read().format(**DatosCorreo)
                FileCorreo.close()
                FileCorreo_html = codecs.open('correo.html', 'rb', 'utf-8')
                #print(FileCorreo_html.read())
                HtmlCorreo = FileCorreo_html.read().format(**DatosCorreo)
                FileCorreo_html.close()
                Mensaje = MIMEMultipart('alternative')
                Mensaje.set_charset("utf-8")
                parte_txt = MIMEText (TextoCorreo.decode('string_escape').encode('utf-8'), 'plain','UTF-8')
                parte_html = MIMEText (HtmlCorreo.decode('string_escape').encode('utf-8'), 'html','UTF-8')
                Mensaje.attach(parte_txt)
                Mensaje.attach(parte_html)

                #MIMEText(FileCorreo.read())

                Mensaje['Subject'] = 'Nueva casilla de correo'
                Mensaje['From'] = 'informatica@{0}'.format(dominio)
                Mensaje['To'] = correo
                Mensaje['Cc'] = 'informatica@{0}'.format(dominio)
                #Mensaje[''] =

                s = smtplib.SMTP('distri.rau.edu.uy')
                s.sendmail('informatica@{0}'.format(dominio), [correo], Mensaje.as_string())
                s.quit()        


Guardado en un archivo ejecutable CrearCuentasZimbra.py, se ejecuta desde el usuario zimbra. Conectado en root en el servidor Zimbra:

su zimbra
./CrearCuentasZimbra.py ListaUsuariosCorreosMigracion.csv

OjO: hacer un "su zimbra" y no un "su - zimbra", porque sino se pierde la locale en UTF-8 y hay errores con los caracteres no ASCII en la generación de cuentas-

Documentación y explicación de comandos

A continuación documentamos algunos comandos que nos fueron útiles para ir construyendo ese script.

Comandos zimbra

Para crear en Zimbra las cuentas de correo a partir de listados, y no una por una a través de la consola web, vamos a utilizar sus potentes herramientas de línes de comando, en particular la de aprovisionamiento zmprov.

Para identificar el nombre exacto de los parámetros, utilizaremos los comandos de lectura ("_get_"), eventualmente modificándo en la consola web los parámetros que queremos definir. Por ejemplo, si creamos una cuenta , indicando que se tiene que cambiar la contraseña, luego con el comando zmprov -v ga, entre los múltiples parámetros identificaremos:

zmprov -v ga prueba@cure.edu.uy
# name prueba@cure.edu.uy
cn: Prueba
displayName: Prueba
mail: prueba@cure.edu.uy
mail: prueba@correo.cure.edu.uy
objectClass: inetOrgPerson
objectClass: zimbraAccount
objectClass: amavisAccount
sn: Prueba
uid: prueba
userPassword: VALUE-BLOCKED

(...)

zimbraPasswordMustChange: TRUE

Para crear una cuenta de correo , con los correspondientes Nombres y Apellidos, con una contraseña aleatoria temporaria (obligación de cambiarla) ejecutamos:

zmprov ca napellido@cure.edu.uy <contraseña_aleatoria>  \
                                givenName "Nombres" sn "Apellidos" displayName "Nombres Apellidos" \
                                zimbraPasswordMustChange TRUE

Si además quisiéramos configurar una redirección a una cuenta , sin copia local, ejecutaríamos:

zmprov ca napellido@cure.edu.uy <contraseña_aleatoria>  \
                                givenName "Nombres" sn "Apellidos" displayName "Nombres Apellidos" \
                                zimbraPasswordMustChange TRUE zimbraPrefMailForwardingAddress napellido@mail.seciu.edu.uy \
                                zimbraPrefMailLocalDeliveryDisabled TRUE

Configuraciones adicionales a las cuentas

Para agregar el alias de correo que puede ser necesario a migración:

zmprov aaa napellido@cure.edu.uy  napellido@correo.cure.edu.uy

O, para agregar un alias con nombre y apellido:

zmprov aaa napellido@cure.edu.uy  nombre.apellido@cure.edu.uy

Para configurar, en esa cuenta, un acceso IMAP sobre SSL a una cuenta (Ver esta entrada de foro):
OjO: lo que sigue no anda, no alcanza,hay también que crear un folder en la cuenta con el comando zmmailbox. Ver el último post de ese mismo hilo de foro

zmprov cds napellido@cure.edu.uy imap "Nombre Apellido - SeCIU" \
                                 zimbraDataSourceEmailAddress napellido@cure.edu.uy zimbraDataSourceEnabled TRUE zimbraDataSourceHost mail.seciu.edu.uy \
                                 zimbraDataSourceUsername napellido zimbraDataSourcePassword <contraseña> zimbraPrefFromAddress napellido@cure.edu.uy \ 
                                 zimbraPrefFromDisplay 'Nombres Apellidos' zimbraDataSourceConnectionType ssl zimbraDataSourcePort 993 zimbraDataSourceFolderId `uuidgen`

NB: resulta indispensable darle un valor al parámetro zimbraDataSourceFolderId. o hacemos ejecutando uuidgen, que dará un valor suficientemente complejo.

todos estos comandos se pueden generar fácilmente, a partir de tablas de datos, con un script o incluso directo en una hoja de cálculo·

Acceso directo ldap

Para acceder directamente a todo el contenido del directorio LDAP, ejecutamos:

ldapsearch -H ldap://godel.csic.edu.uy:389 -D uid=zimbra,cn=admins,cn=zimbra -w <contraseña> -Zv | less

(Este comando sólo es posible en el mismo servidor donde está el zimbra, no a distancia)

con la contraseña que encontramos acá en el archivo /opt/zimbra/conf/localconfig.xml

  ... 
  <key name="ldap_root_password">
    <value><contraseña></value>
  </key>
  ...