Activar Dovecot API y comandos en Bash y Python (parte 17)

Esta es una entrada dentro de la serie para la instalación de un servidor de correo completo. Índice completo de contenidos pincha aquí.


Dovecot ofrece la posibilidad de activar una funcionalidad experimental, a partir de su versión v2.2.22, que permite interactuar con comandos doveadm utilizando una serie de APIs vía HTTP. Esto nos permite ejecutar sus comandos mediante el uso de la herramienta CURL.

Esta nueva característica nos brinda la oportunidad de realizar nuestros propios programas, scripts, paneles de adminsitración de buzones, etc sin necesidad de utilizar un usuario del sistema con privilegios. Símplemente debemos de tener o las credenciales del usuario de doveadm o el Token que nos permita lanzar peticiones HTTP desde el exterior o desde la misma máquina local.

Son muchos los comandos que podremos ejecutar, cubriendo cualquier necesidad que podamos tener en la administración de este Backend para buzones de correo electrónico.

Esta entrada se suma a nuestro manual para la configuración de un compelto servidor de correo electróncio, indicado al comienzo, pero es extensible a cualquier instalación de Dovecot que cumpla con la versión mínima requerida o superior.

En el momento en el que se redactó esta entrada del blog disponíamos de la versión Dovecot v2.22.27.

Vamos al lío!!!


Consideraciones

La configuración de esta funcionalidad nunca debe ser accesible desde cualquier origen. Debemos ser muy cuidadosos con su uso y los permisos de acceso, ya que de lo contrario pondremos en riesgo todo nuestro sistema de buzones, exponiendo su información, configuración y contenido.

Es recomendable activarla para su uso interno, bien para el desarrollo de algún proceso, automatismo, panel de administración Web, … De tener que publicarlo se debería de filtrar por origen al que se precisa conceder acceso, pero nunca publicarlo sin restricción alguna.


Configuración del servicio

Para realizar la configuración solo debemos de agregar unas líneas a nuestro archivo de configuración global para Dovecot, salvo que queramos activar el servicio SSL lo cual nos requiere tener previamente configurado este servicio en nuestra instalación.

Sin SSL

Si vamos a utilizar las APIs para consultas internas desde el mismo sistema podemos prescindir del servicio SSL, ya que la información no saldrá al exterior.

Solo será necesario configurar las siguientes líneas del listener en el archivo dovecot.conf, que en mi caso se encuentra en la ruta /etc/dovecot:

doveadm_password = nuestra_pwd
doveadm_api_key = nuestra_pwd

service doveadm {
  inet_listener http {
    port = 8080
    #ssl = yes
  }
}

Reiniciamos el servicio y estaría listo:

sudo service dovecot restart

El puerto configurado quedaría levantado:

netstat -an | grep 8080
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN     
tcp6       0      0 :::8080                 :::*                    LISTEN     

Con SSL

En mi caso prefiero configurar el servicio con SSL para cifrar cualquier tipo de información que circule por la red, lo que requiere tener el servicio configurado en nuestra instalación global. En la entrada Servidor de correo – Configuración Dovecot, SIEVE, ManageSIEVE (parte 7) se explica como hacerlo.

Una vez lo tengamos configuardo, debemos de habilitar el servicio SSL en nuestro listener para la API de Dovecot:

doveadm_password = nuestra_pwd
doveadm_api_key = nuestra_pwd

service doveadm {
  inet_listener http {
    port = 8080
    ssl = yes
  }
}

Reiniciamos el servicio y estaría listo:

sudo service dovecot restart

El puerto configurado quedaría levantado:

netstat -an | grep 8080
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN     
tcp6       0      0 :::8080                 :::*                    LISTEN     

Hay que tener en cuenta que para cualquier consulta que lancemos tendremos que utilizar el protocolo HTTPS y el nombre de host configurado en el certificado, en mi caso correu2.yeloquehay.com, como veremos más adelante.


Comandos de la API

Para poder enviar comandos de la API podemos usar tanto el Token como las credenciales user:pwd, pero en cualquiera de los casos debemos de obtener su valor en Base64.

Credenciales en Base64

Para obtener el valor de las credenciales encriptado en Base64 podemos hacer uso de un servicio online, o usar el siguiente código en Python3 y la librería base64:

>>> import base64
>>> 
>>> token = 'nuestra_pwd'
>>> credentials = 'doveadm:nuestra_pwd'
>>>
>>>  # TOKEN
>>> message_bytes = token.encode('ascii')
>>> base64_bytes = base64.b64encode(message_bytes)
>>> base64_message = base64_bytes.decode('ascii')
>>> print(base64_message)
bnVlc3RyYV9wd2Q=
>>> 
>>> # USER:PWD 
>>> message_bytes = credentials.encode('ascii')
>>> base64_bytes = base64.b64encode(message_bytes)
>>> base64_message = base64_bytes.decode('ascii')
>>> print(base64_message)
ZG92ZWFkbTpudWVzdHJhX3B3ZA==
>>> 

El código resultante es el que usaremos para lanzar los comandos vía CURL.

Listado de comandos

Podemos obtener todos los comandos disponibles de la siguiente forma, usando HTTP o HTTPS dependiendo de la configuración que hubieramos realizado en el paso anterior:

  • Con usuario:contraseña:
curl -H "Authorization: Basic ZG92ZWFkbTpudWVzdHJhX3B3ZA=="  https://correu2.yeloquehay.com:8080/doveadm/v1
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0[
    {"command":"serviceStop", "parameters":[
        {"name":"service","type":"array"}
    ]},
    {"command":"serviceStatus", "parameters":[
        {"name":"service","type":"array"}
    ]},
    {"command":"processStatus", "parameters":[
        {"name":"service","type":"array"}
    ]},
    {"command":"stop", "parameters":[]},
    {"command":"reload", "parameters":[]},
    {"command":"statsDump", "parameters":[
        {"name":"socketPath","type":"string"},
        {"name":"type","type":"string"},
        {"name":"filter","type":"string"}
    ]},
...
  • Con Token:
curl -H "Authorization: X-Dovecot-API ZG92ZWFkbTpudWVzdHJhX3B3ZA=="  https://correu2.yeloquehay.com:8080/doveadm/v1
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0[
    {"command":"serviceStop", "parameters":[
        {"name":"service","type":"array"}
    ]},
    {"command":"serviceStatus", "parameters":[
        {"name":"service","type":"array"}
    ]},
    {"command":"processStatus", "parameters":[
        {"name":"service","type":"array"}
    ]},
    {"command":"stop", "parameters":[]},
    {"command":"reload", "parameters":[]},
    {"command":"statsDump", "parameters":[
        {"name":"socketPath","type":"string"},
        {"name":"type","type":"string"},
        {"name":"filter","type":"string"}
    ]},
...

La lista es muy extensa, por lo que es recomendable dedicarle un tiempo para buscar el que más se ajuste a nuestras necesidades y probarlo.

Ejemplo desde BASH

Un ejemplo de comando sería obtener la quota utilizada y disponible de un buzón, el cual podemos obtener con el siguiente comando desde BASH. Los valores vienen dados en KB:

curl -H "Authorization: X-Dovecot-API ZG92ZWFkbTpudWVzdHJhX3B3ZA==" -H "Content-Type: application/json" -d '[["quotaGet", { "user": "email@email.com"}, "c01"]]' https://correu2.yeloquehay.com:8080/doveadm/v1

[["doveadmResponse",[{"root":"User quota","type":"STORAGE","value":"497713","limit":"10485760","percent":"4"},{"root":"User quota","type":"MESSAGE","value":"11116","limit":"-","percent":"0"}],"c01"]]

En la respuesta doveadmResponse podemos ver la capacidad utilizada, el límite máximo y el porcentaje utilizado, tanto para el tamaño STORAGE como de número de mensajes MESSAGE.

Si recibiéramos un error sería similar al siguiente resultado:

[["error",{"type":"exitCode", "exitCode":67},"c01"]]

Ejemplo desde Python

Desde Python podemos ejecutar el comando anterior con la librería requests, la cual no viene por defecto y hay que instalarla:

python3 -m pip install requests
>>> import requests
>>> url = 'https://correu2.yeloquehay.com:8080/doveadm/v1'
>>> headers = {'Authorization':'X-Dovecot-API ZG92ZWFkbTpudWVzdHJhX3B3ZA==', 'Content-Type':'application/json'}
>>> payload = '[["quotaGet", { "user": "email@email.com"}, "c01"]]'
>>> r = requests.post(url, data=payload, headers=headers)

El resultado lo podríamos ver de la siguiente forma:

>>> r.content
b'[["doveadmResponse",[{"root":"User quota","type":"STORAGE","value":"491169","limit":"10485760","percent":"4"},{"root":"User quota","type":"MESSAGE","value":"10960","limit":"-","percent":"0"}],"c01"]]'

>>> response = r.json()
>>> print(response[0])
['doveadmResponse', [{'value': '491169', 'limit': '10485760', 'percent': '4', 'type': 'STORAGE', 'root': 'User quota'}, {'value': '10960', 'limit': '-', 'percent': '0', 'type': 'MESSAGE', 'root': 'User quota'}], 'c01']
>>> print(response[0][1])
[{'value': '491169', 'limit': '10485760', 'percent': '4', 'type': 'STORAGE', 'root': 'User quota'}, {'value': '10960', 'limit': '-', 'percent': '0', 'type': 'MESSAGE', 'root': 'User quota'}]
>>> print(response[0][1][1])
{'value': '10960', 'limit': '-', 'percent': '0', 'type': 'MESSAGE', 'root': 'User quota'}

Una forma más visual de mostrar los datos:

>>> storage = response[0][1][0]
>>> for key in storage.keys():
...  print('%s - %s' % (str(key),str(storage[key])))
...
value - 491169
limit - 10485760
percent - 4
type - STORAGE
root - User quota

Y con la librería de Python humanfriendly podemos ver el resultado en formato más amigable, pero debemos de instalarla. Los valores vienen dados en KB, por lo que tendremos que multiplicar x1000 el resultado obtenido para ver el dato correcto:

python3 -m pip install humanfriendly
>>> import humanfriendly
>>>
>>> used = humanfriendly.format_size(storage['value'] * 1000)
>>> max_limit = humanfriendly.format_size(storage['limit'] * 1000)
>>>
>>> print('value - %s\nlimit - %s\npercent - %s\%\ntype - %s\nroot - %s' % (str(used), str(max_limit), str(storage['percent']), str(storage['type']), str(storage['root'])))
value - 491.2 MB
limit - 10.05 GB
percent - 4%
type - STORAGE
root - User quota

Conclusiones

Con esta nueva funcionalidad de Dovecot podemos interactuar con los comandos Doveadm sin necesidad de usuario de sistema, ni de estar dentro de la consola de nuestro servidor, pero mucho cuidado con la seguridad ya que estamos exponiendo un acceso directo a la administración de nuestros buzones y su contenido.


Enlaces de interés

Oficial Dovecot API

Oficial configuración Dovecot API

Servidor de correo – Configuración Dovecot, SIEVE, ManageSIEVE (parte 7)