Una de las herramientas fundamentales a la hora de montar nuestro propio servidor de correo es la implementación de filtros (Mail Filters) para seleccionar de forma eficiente el correo que vamos a procesar. Entre otras cosas, podremos:
- Hacer más eficiente nuestro servicio ya que enviaremos solo los correos que cumplan unas determinadas reglas (eliminan SPAM, virus y otros tipos de cosas que no queremos recibir.
- Modificar ciertos mensajes para automatizar procesos en nuestra compañía, por ejemplo, añadir destinatarios en copia automáticamente, añadir una cabecera adicional al correo, incluir un sistema de seguimiento al mensaje…
- Monitorizar el número de correos recibidos y enviados por el servidor. Con fines estadísticos y, por supuesto para mejorar el servicio que estamos prestando.
- Restringir el envío de correo a determinados hosts/IPs/dominios/usuarios. Por ejemplo, teniendo un sistema de créditos, o impidiendo que se envíen de forma automatizada muchos mensajes a través de nuestro servidor como una protección extra contra spammers u ordenadores zombies (incluso podríamos avisar a alguien si esto pasa).
- Implementación de listas blancas, negras o grises.
- Espiar mensajes. Vale, como administrador de sistemas siempre me dicen que si espío los mensajes de la gente y siempre gasto la broma de que tengo un programa que me avisa cuando hablan de mí. Aunque no lo tengo, porque me da pereza, con este tipo de filtros podría hacerlo.
Por otro lado, cuando montamos un nuevo sistema debemos pensar en la futura escalabilidad del mismo. Es decir, vamos a pensar en cuando nuestro sistema sea grande. Los servicios tanto de entrega, recepción de correo, incluso filtros podrán estar en máquinas separadas. Así que la forma de atacar a estos sistemas será haciendo conexiones de red. Puede que para un sistema muy pequeño perdamos algo de rendimiento, ya que el establecimiento de la conexión y la negociación de la misma puede tardar un tiempo que no tardaría la ejecución de un programa local, pero que hacen posible la separación de los servicios, incluso podríamos colocar los filtros detrás de un balanceador de carga y destinar varias máquinas a éstos. De todas formas, para este ejemplo, vamos a utilizar sockets unix por lo que, en local, será muy rápido y no será complicado hacer que en lugar de un socket Unix estemos utilizando un host/puerto.
Tabla de contenidos
Configuración de Postfix
Postfix es capaz de ejecutar muchos milters cada vez que viene un mensaje. Estos milters, con respecto a cuándo se ejecutan antes de encolar los mensajes y pueden ser:
- tipo smtpd: Los que se ejecutan para los mensajes que vienen desde conexiones SMTP. Pueden ser de usuarios que se conectan a nuestro servidor para enviar mensajes o de otros servidores que se conectan a nosotros para entregar mensajes.
- tipo no-smtpd: Serán mayormente los que vengan de manera local. Mensajes generados en el propio servidor o alguna de las aplicaciones que hay en él, como sendmail.
Solo tenemos que ir al fichero /etc/postfix/main.cf y, por ejemplo al final, añadir:
smtpd_milters = milter1, milter2, {milter3, …},…
milter_default_action = accept
De esta forma, por defecto, si algún milter no se ejecuta, por defecto aceptamos el mensaje. Por ejemplo, si estamos utilizando un milter de monitorización, si el milter falla, que por lo menos se entregue el mensaje. Si es un milter de filtro de correo, debemos ver qué es más importante, que cuando el milter falle rechace todo (reject) o si deberíamos entregarlo. Otras acciones que pueden suceder cuando el milter falle son tempfail (fallo temporal) o quarantine (cuarentena).
Como hemos visto, podemos separar una serie de filtros por comas, incluso, a veces, ponemos configuración del milter entre llaves. Eso sí, si solo tenemos uno, no hacen falta comas, y si la configuración es sencilla, llaves tampoco. Vamos a ver un poco el por qué de todo esto.
En principio, con respecto a cómo se ejecutan, podemos tener otros dos tipos de milters:
- unix:/ruta/al/socket : serán los milters asociados a un socket Unix accesible a través de una ruta local.
- inet:host:port : serán los milters asociados a un host y un puerto. Es decir, los que podemos colocar en una máquina diferente.
También hemos visto que los milters podemos ponerlos entre llaves, esto es cuando su configuración es más compleja que solo decir dónde tiene que conectar, por ejemplo podemos controlar:
- connection_timeout : Cuánto tiempo va a esperar Postfix a la conexión con el milter. Ya que es un servicio que puede ser externo, la conexión puede no ser inmediata. Por defecto son 30s.
- command_timeout : Cuánto tiempo va a esperar Postfix a que el milter responda tras el envío de un comando. Nuestro filtro tiene tiempo para realizar operaciones, pero tampoco se puede eternizar porque constantemente están llegando mensajes y el proceso no puede parar. Son 30s por defecto.
- content_timeout : Cuánto tiempo va a esperar Postfix tras el envío de contenidos del mensaje para una respuesta por parte del milter. Por defecto son 300s.
- default_action : Podemos especificar la acción por defecto para cada milter. Esta puede ser accept, reject, tempfail, quarantine
- Podemos consultar una lista completa de configuraciones aquí.
Así, un ejemplo de configuración de milter puede ser:
smtpd_milters = unix:/var/run/milters/monitor, {inet:blacklist.mydomain.com:6234, connection_timeout=5x, default_action=tempfail, command_timeout=1s}
milter_default_action = accept
Milters en Python
Aunque podemos crear nuestros milters en C o C++, es más, podemos encontrar muchos ejemplos que utilizan libmilter y funcionan muy bien. Hoy le toca el turno a Python, de hecho la biblioteca de Python para trabajar con milters se apoya en la biblioteca de C. Aunque no tendrá tanto rendimiento como uno programado en C, en el hipotético caso en el que el filtro lo hagamos optimizado en cada lenguaje, Python nos hace mucho más fácil el mantenimiento y el desarrollo, pudiendo crecer muy rápidamente. Lo primero será preparar las dependencias. Podemos instalarlas con pip:
Aunque, dependiendo de nuestro filtro, podremos tener muchas más dependencias. Ya nuestra imaginación no tiene límites.
Creando el milter en Python
Vamos a crear un milter de ejemplo en Python. En este ejemplo, solo vamos a permitir mensajes que se envíen hacia las direcciones de correo que figurarán en un archivo llamado whitelist. Por lo que nuestro servidor no podrá mandar correo a cualquiera. El contenido de whitelist puede ser el siguiente:
mi@correo.com
yo@dominio.com
otromail@otrodominio.com
El milter está basado en este. Ahí va el código:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | # -*- coding: utf-8 -*- # Milter para filtrar los mails que vienen de direcciones entrantes import Milter import StringIO import time import email import sys from socket import AF_INET, AF_INET6 from Milter.utils import parse_addr if True: from multiprocessing import Process as Thread, Queue else: from threading import Thread from Queue import Queue logq = Queue(maxsize=4) class myMilter(Milter.Base): def __init__(self): # Se crea una instancia con cada conexión entrante self.id = Milter.uniqueID() @Milter.noreply def connect(self, IPname, family, hostaddr): self.IP = hostaddr[0] self.port = hostaddr[1] if family == AF_INET6: self.flow = hostaddr[2] self.scope = hostaddr[3] else: self.flow = None self.scope = None self.IPname = IPname self.H = None self.fp = None self.receiver = self.getsymval('j') self.log("connect from %s at %s" % (IPname, hostaddr) ) return Milter.CONTINUE def hello(self, heloname): # Cuando entra un nuevo mail vemos esto. self.log("Recibo HELO") self.H = heloname self.log("HELO %s" % heloname) return Milter.CONTINUE def envfrom(self, mailfrom, *str): # Cuando se indica un remitente entra por aquí. Podemos elegir si continuar o no self.F = mailfrom self.R = [] self.fromparms = Milter.dictfromlist(str) self.user = self.getsymval('{auth_authen}') self.log("mail from:", mailfrom, *str) self.fp = StringIO.StringIO() self.canon_from = '@'.join(parse_addr(mailfrom)) self.fp.write('From %s %s\n' % (self.canon_from,time.ctime())) return Milter.CONTINUE def envrcpt(self, to, *str): # Cuando recibimos un destinatario, entra por aquí filename = 'whitelist' # Obtenemos el listado de correos aceptados addresses = tuple(el.strip() for el in open(filename, 'r') if el.strip()) tomail = '@'.join(parse_addr(to)) if not any(tomail.lower() in l for l in addresses): return Milter.REJECT rcptinfo = to,Milter.dictfromlist(str) self.R.append(rcptinfo) return Milter.CONTINUE @Milter.noreply def header(self, name, hval): # Cuando recibimos un encabezado entramos por aquí self.fp.write("%s: %s\n" % (name,hval)) return Milter.CONTINUE @Milter.noreply def eoh(self): # Cuando terminamos los encabezados entramos a esta función self.fp.write("\n") return Milter.CONTINUE @Milter.noreply def body(self, chunk): # Cuando se recibe cada uno de los trozos del cuerpo del mensaje entramos a este punto self.fp.write(chunk) return Milter.CONTINUE def eom(self): # Cuando terminamos de recibir el mensaje entramos a esta función self.fp.seek(0) msg = email.message_from_file(self.fp) return Milter.ACCEPT def close(self): # Cierre de conexión y limpieza de recursos. Si se llama a abort() se llamará luego a close() # automáticamente return Milter.CONTINUE def abort(self): # El cliente se ha desconectado de forma prematura. ¿Un fallo en postfix o al enviar el mensaje? return Milter.CONTINUE def log(self,*msg): # Logea algo logq.put((msg,self.id,time.time())) def background(): while True: t = logq.get() if not t: break msg,id,ts = t print "%s [%d]" % (time.strftime('%Y%b%d %H:%M:%S',time.localtime(ts)),id), for i in msg: print i, ## === def main(): bt = Thread(target=background) bt.start() socketname = "/var/run/milters/whitelistmilter" timeout = 600 Milter.factory = myMilter flags = Milter.CHGBODY + Milter.CHGHDRS + Milter.ADDHDRS flags += Milter.ADDRCPT flags += Milter.DELRCPT # Le decimos al Milter los puntos que "interceptaremos" Milter.set_flags(flags) print "%s milter startup" % time.strftime('%Y%b%d %H:%M:%S') sys.stdout.flush() Milter.runmilter("pythonfilter",socketname,timeout) logq.put(None) bt.join() print "%s bms milter shutdown" % time.strftime('%Y%b%d %H:%M:%S') if __name__ == "__main__": main() |
Probando nuestro milter
Para probar esto, desde un terminal, podemos ejecutar nuestro script python de nuestro milter, reiniciar Postfix e intentar enviar un correo a través del servidor SMTP elegido. Si queremos hacerlo todo desde terminal, podemos probar utilizar este script para enviar correos desde terminal.
Aunque todo esto podemos hacerlo, si la configuración nos lo permite a pelo. Y puede que en el futuro haga un post dedicaco a esto. Pero por ahora nos apañaremos con este apartado. Nuestro servidor de correo requeire autentificación, de hecho para las pruebas he utilizado un Postfix configurado como relay, con toda la configuración del anterior post. incluyendo el nombre de usuario y contraseña para poder acceder al servidor de correo. Para ello, y como vamos a utilizar telnet, vamos a prepararnos en un terminal la siguiente información:
Como estamos utilizando en el servidor el modo AUTH LOGIN, tendremos que escribir el usuario y la contraseña codificados en base64. Los textos codificados, podemos dejarlos visibles para copiarlos y pegarlos cuando convenga. Ahora, accederemos al servidor de correo (el puerto será el 25, y si lo tenemos instalado en un VPS, tendremos que asegurarnos de que nuestro proveedor tiene abierto el puerto 25. A veces necesitamos enviar un mensaje al proveedor para que lo activen). ¡Fijaos, la IP es digna de CSI!
Como vemos, cuando introducimos una dirección de correo que no está en la lista, nos aparece el comando como rechazado (Command rejected), mientras que cuando la dirección está en la lista, nos devuelve Ok. Si queremos, podemos mirar el log en la ventana donde estamos ejecutando el script para ver el resultado y observar que efectivamente están llegando los comandos a nuestro milter.
Creación del servicio
Como queremos que nuestro milter siempre esté en ejecución, debemos crear un servicio. Por ejemplo, para systemd podemos crear el siguiente archivo: whitelistmilter.service
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | [Unit] Description=Milter startup After=networking.target [Service] WorkingDirectory=/home/user/milters/ Type=simple ExecStart=/usr/bin/python /home/user/milters/whitelistmilter.py KillSignal=SIGTERM User=postfix Group=postfix [Install] WantedBy=multi-user.target |
Luego podemos crear un enlace a /etc/systemd/system y arrancar el servicio:
En este punto ya podemos reiniciar Postfix para empezar a utilizar el filtro:
Problemas que pueden surgir
Postfix chroot y acceso a sockets Unix
Uno de los problemas más comunes que podemos tener es que Postfix se ejecuta en un chroot o jaula. Por lo que, cuando especificamos la ruta de un socket Unix, la ruta esté correcta y todo deba funcionar perfectamente. En los ficheros de log veremos que la ruta no se encuentra. Esto se debe a que el unix socket no está dentro del chroot de postfix y efectivamete no se verá.
Una posible solución a esto es meter nuestro sockets unix de los milters en el directorio /var/run/milters y luego hacer:
Con esto, creamos un directorio var/run dentro de la ruta de la jaula de postfix y luego montamos el directorio local /var/run/milters dentro de la jaula. De esta forma, postfix ya tendrá acceso a los milters bajo su ruta absoluta /var/run/milters (recordemos que para Postfix, el directorio raíz / es el directorio /var/spool/postfix para el resto de los mortales.
Foto principal: andrew welch
Gracias por este gran artículo!
Perfecto! aqui puedes encontrar como obtener seguidores en tiktok https://tysonkdmb84826.link4blogs.com/47967287/tiktok-es-lo-mejor
This is a great inspiring article. I am pretty much pleased with your good work. You put really very helpful information. Georgia bulldogs Leather Jacket
This is excellent article, thank you for the share! This is what I am looking for, hope in future you will continue sharing such an superb work.
T Birds Jacket Grease
This website’s content has really impressed me. It demonstrates your comprehension of the topic Retro bowl.
This is a really helpful post. Thank you very much for sharing it for me and everyone to know.
A very informative article on how to improve one’s expression, mood, and sentence. Please refer to it here.먹튀검증
It was very informative. Thank you for sharing.
Accountants
Pretty nice post. I just stumbled upon your weblog and wanted to say that I have really enjoyed browsing your blog posts. After all I’ll be subscribing to your feed and I hope you write again soon!
My website: sabong online game
It’s full of amazing data. I was impressed Your presentation has been well investigated and well written to convey your position on this to all readers.토토사이트추천
Thanks for making this content so informative!
Plastic Surgery Fresno
Actually, it’s pretty good to see! Tiler Adelaide
Thanks for sharing! Tiler Adelaide
Thanks for letting us know! Tiler Wollongong
Excellent post! Concreters in Wollongong
Thanks for sharing this to public! Adelaide Landscaping
I visited Your blog and got a massive number of informative articles. I read many articles carefully and got the information that I had been looking for for a long time. Hope you will write such a helpful article in future. Thanks for writing.Tilers in Hobart
Very useful and informative post! Tiling Townsville
Very informative post! tiler melbourne
To be honest, I generally don’t read. But, this article caught my attention.digital marketing adelaide
I am really impressed with your writing style. Keep it up! Landscapers Canberra
Many thanks for sharing this! Adelaide Coolroom Hire
Thanks for sharing! Sliding Doors Adelaide
It’s so kind of you! Solar Panels Adelaide
Many many thanks to you! Cleaning Services Adelaide
You presented your ideas and thoughts really well on the paper. adelaide electrician
What a great piece of article! seo adelaide
Very informative content. Thanks. tow truck wollongong
Please keep up the good work! drum lessons adelaide
Thanks for letting us know. Tiler Adelaide
I thik this is very helpfull post Canberra landscapers
Great Post! I learned a lot from this, Thank you! Canberra landscapers
Really nice article and helpful me Canberra landscapers
Nice article, waiting for your another Canberra landscapers
This is awesome! Nice share. tama superstar hyperdrive
Such a great post! Pro landscaping Adelaide
Thats what I was looking for! air conditioning service adelaide
Good to know about this! Tilers Wollongong Albion Park
This is really very nice blog and so informative Bathroom Tilers Sydney
Thanks for this post, I am a big fan of this web site would like to proceed updated. 먹튀검증
Pokemon fusion can be a fun and creative way for fans to express their love for the Pokemon franchise and explore new possibilities within the world of Pokemon.
color blind test – Use as an independent control tool to measure the duration of other color blindness test results.
Nice site and blog
Tree Service Fort Worth
Greetings from Jacksonville Drywall Contractors! Gracias por este gran artículo!
Great work to the publisher of this blog. Dover Handyman appreciates all your contributions here.
It’s actually a nice and helpful piece of info. I’m happy that you shared this helpful information with us. Please stay us up to date like this.Thank’s for sharing.
Here is My Homepage: OKBet online login
Maybe we could either implement content or we don’t need the whole mail body to process it, you could simply package includes a sample filter that replaces dangerous attachments with a warning message, discards mail, or re-injects the mail.
Local SEO Citations
Towing services are a necessity, and it’s comforting to know there are experts out there offering them at competitive rates. towing junk cars
This is excellent article, thank you for the share! This is what I am looking for, hope in future you will continue sharing such an superb work.
Thank you for sharing this with us.
Accountants Hamilton
สล็อต ufa789 เว็บพนันครบวงจร แทงบอล บาคาร่า สล็อต ครบจบที่เดียว
Great site. A lot of helpful info here. I¡¦m sending it to a few friends ans additionally sharing in delicious. And certainly, thank you for your sweat! ufabet
I appreciate you writing such a fantastic article. I’m eagerly anticipating your next posts. Visiting this blog is a pleasurable experience. I appreciate you telling me that. papa’s freezeria game
I’m glad to hear that you’re enjoying the blog! Saints often provide profound insights and inspiration through their thoughts, writings, and teachings. Their wisdom can offer guidance and comfort to people from all walks of life, regardless of their religious beliefs.
If you have any specific topics or saints you’re interested in learning more about, feel free to let me know, and I can provide more information or recommendations for further reading. Whether it’s exploring the lives of well-known saints or delving into lesser-known figures, there’s always something new to discover and learn from their teachings.
BigToken is legit or a scam will depend on various factors, including user experiences, transparency, and adherence to privacy and data security standards. It’s essential to approach such platforms with caution and conduct thorough research before providing any personal information or participating in their programs.https://www.wpgio.com/bigtoken-review/
Thank you very much for your kind words! I’m delighted that you found the post helpful and appreciate your recognition of my efforts. If you have any more questions or need further assistance, please don’t hesitate to ask. I’m here to help!
Vetiver essential oil is derived from the roots of the vetiver grass, renowned for its earthy, grounding aroma and numerous therapeutic properties. Its calming scent makes it a popular choice in aromatherapy for relaxation and stress relief. Additionally, vetiver oil is valued for its skincare benefits, including its ability to moisturize, soothe inflammation, and promote healing, making it a versatile addition to natural skincare routines.
Thank you very much for your kind words! I’m delighted that you found the post helpful and appreciate your recognition of my efforts. If you have any more questions or need further assistance, please don’t hesitate to ask. I’m here to help!
Vetiver essential oil is derived from the roots of the vetiver grass, renowned for its earthy, grounding aroma and numerous therapeutic properties. Its calming scent makes it a popular choice in aromatherapy for relaxation and stress relief. Additionally, vetiver oil is valued for its skincare benefits, including its ability to moisturize, soothe inflammation, and promote healing, making it a versatile addition to natural skincare routines.https://www.rockymountainoils.com/products/vetiver-essential-oil
Nice site and blog
Carpet Cleaning Regina
Thanks for sharing insights on implementing filters for various purposes, from spam elimination to message tracking.
We’ve been creating elegant digital printing for over 20 years. Right from the start of the digital printing revolution, in fact. In that time we have grown with you, our clients. And the thing is, like any important relationship check our website for more infobetflixvip เข้าสู่ระบบ
betflixvip เข้าสู่ระบบ
Hey there! You can also modify certain messages to automate processes in our company according to our Best website builder. for example, automatically add recipients in copy, add an additional header to the email, include a message tracking system…
It’s a game. Five dollars is free. Try it It’s not an easy game ->-> 카지노사이트.COM