Filtrado de correos por IMAP

Desde hace tiempo necesitaba filtrar correos en una cuenta que no contaba con filtros de correo server-side y la mejor opción que encontre es imapfilter, una herramienta que realiza el filtrado de correos por medio de IMAP.

Imapfilter es muy fácil de configurar y es muy flexible, permite utilizar varias cuentas e incluso mover mensajes entre ellas.

Los requerimientos son muy básicos (Lua, PCRE y OpenSSL) y la instalación es sencilla.

La configuración se realiza por medio de un archivo similar a este:

---------------
--  Options  --
---------------

options.timeout = 120
options.subscribe = true

----------------
--  Accounts  --
----------------

-- Connects to "imap1.mail.server", as user "user1" with "secret1" as
-- password.
account1 = IMAP {
    server = 'imap1.mail.server',
    username = 'user1',
    password = 'secret1',
}

-- Another account which connects to the mail server using the SSLv3
-- protocol.
account2 = IMAP {
    server = 'imap2.mail.server',
    username = 'user2',
    password = 'secret2',
    ssl = 'ssl3',
}

-- Get a list of the available mailboxes and folders
mailboxes, folders = account1:list_all()

-- Get a list of the subscribed mailboxes and folders
mailboxes, folders = account1:list_subscribed()

-- Create a mailbox
account1:create_mailbox('Friends')

-- Subscribe a mailbox
account1:subscribe_mailbox('Friends')

-----------------
--  Mailboxes  --
-----------------

-- Get the status of a mailbox
account1.INBOX:check_status()

-- Get all the messages in the mailbox.
results = account1.INBOX:select_all()

-- Get newly arrived, unread messages
results = account1.INBOX:is_new()

-- Get unseen messages with the specified "From" header.
results = account1.INBOX:is_unseen() *
          account1.INBOX:contain_from('weekly-news@news.letter')

-- Copy messages between mailboxes at the same account.
results:copy_messages(account1.news)

-- Get messages with the specified "From" header but without the
-- specified "Subject" header.
results = account1.INBOX:contain_from('announce@my.unix.os') -
          account1.INBOX:contain_subject('security advisory')

-- Copy messages between mailboxes at a different account.
results:copy_messages(account2.security)

-- Get messages with any of the specified headers.
results = account1.INBOX:contain_from('marketing@company.junk') +
          account1.INBOX:contain_from('advertising@annoying.promotion') +
          account1.INBOX:contain_subject('new great products')

-- Delete messages.
results:delete_messages()

-- Get messages with the specified "Sender" header, which are older than
-- 30 days.
results = account1.INBOX:contain_field('sender', 'owner@announce-list') *
          account1.INBOX:is_older(30)

-- Move messages to the "announce" mailbox inside the "lists" folder.
results:move_messages(account1['lists/announce'])

-- Get messages, in the "devel" mailbox inside the "lists" folder, with the
-- specified "Subject" header and a size less than 50000 octets (bytes).
results = account1['lists/devel']:contain_subject('[patch]') *
          account1['lists/devel']:is_smaller(50000)

-- Move messages to the "patch" mailbox.
results:move_messages(account2.patch)

-- Get recent, unseen messages, that have either one of the specified
-- "From" headers, but do not have the specified pattern in the body of
-- the message.
results = ( account1.INBOX:is_recent() *
            account1.INBOX:is_unseen() *
            ( account1.INBOX:contain_from('tux@penguin.land') +
              account1.INBOX:contain_from('beastie@daemon.land') ) ) -
          account1.INBOX:match_body('.*all.work.and.no.play.*')

-- Mark messages as important.
results:mark_flagged()

-- Get all messages in two mailboxes residing in the same server.
results = account1.news:select_all() +
          account1.security:select_all()

-- Mark messages as seen.
results:mark_seen()

-- Get recent messages in two mailboxes residing in different servers.
results = account1.INBOX:is_recent() +
          account2.INBOX:is_recent()

-- Flag messages as seen and important.
results:add_flags({ '\\Seen', '\\Flagged' })

-- Get unseen messages.
results = account1.INBOX:is_unseen()

-- From the messages that were unseen, match only those with the specified
-- regular expression in the header.
newresults = results:match_header('^.+MailScanner.*Check: [Ss]pam$')

-- Delete those messages.
newresults:delete_messages()

Si el servidor IMAP permite mantener conexiones en reposo puedes dejar el script corriendo todo el tiempo y en el momento que entre un correo lo filtrará.

Por el momento lo instalé en mi servidor web y estoy filtrando dos cuentas de correo pero se me ocurren muchos usos para esta herramienta.

 

Configurar llaves para SSH

En el post anterior hablaba sobre criptografía de llave pública, ahora les explicaré como implementarla en un servidor para proteger el acceso a SSH.

El par de llaves puede ser generado en windows o en sistemas basados en UNIX con los siguientes pasos:

MacBook-Pro-de-Arturo-Leon:/ arturo$ cd ~/.ssh

Ya en la carpeta generamos nuestro par de llaves, al no pasar parámetros nos generará por defecto una llave RSA de 2048 bits

MacBook-Pro-de-Arturo-Leon:.ssh arturo$ ssh-keygen

Mostrará la siguiente salida, cuando te pide el archivo donde se guardará puedes presionar enter para dejarlo en default (id_rsa) o escribir uno y opcionalmente puedes usar una contraseña que agrega una capa extra de seguridad ya que para acceder al servidor se necesitará la llave y la contraseña, al no configurar una con la llave será suficiente para acceder.

Generating public/private rsa key pair.
Enter file in which to save the key (/Users/arturo/.ssh/id_rsa):
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /Users/arturo/.ssh/id_rsa.
Your public key has been saved in /Users/arturo/.ssh/id_rsa.pub.
The key fingerprint is:
75:fc:34:ab:80:21:7b:43:a8:ea:19:7b:f2:09:94:eb arturo@MacBook-Pro-de-Arturo-Leon.local
The key's randomart image is:
+--[ RSA 2048]----+
|                 |
|       .   .     |
|      o o . o o  |
|   . . + + . o o |
|  o . . S .   o  |
| . o   . . . .   |
|  =         .    |
| o.=..           |
|  E+o            |
+-----------------+

Ya está generado el par de llaves, lo podemos comprobar listando el contenido del directorio

MacBook-Pro-de-Arturo-Leon:.ssh arturo$ ls
id_rsa		id_rsa.pub	known_hosts

id_rsa será nuestra llave privada mientras que id_rsa.pub será la llave pública. Ahora es necesario copiar el contenido de id_rsa.pub dentro de el archivo ~/.ssh/authorized_keys en el servidor remoto, puedes tener cuantas llaves quieras (una por línea).

En mi caso voy a deshabilitar el acceso a SSH utilizando contraseña editando el archivo /etc/ssh/sshd_config en el servidor remoto dejan el parámetro de la siguiente forma

PasswordAuthentication no

Y asegúrate que el acceso con llave esté habilitado

PubkeyAuthentication yes

Nos queda finalmente recargar la configuración de SSH con el siguiente comando

service sshd reload

Es recomendable primero activar el acceso con llave y luego deshabilitar el acceso con password para evitar quedar “fuera” de el servidor y no poder entrar para hacer correcciones.

Otras opciones para asegurar el servidor es cambiando el puerto por defecto que utiliza SSH, deshabilitar el acceso root, siempre contar con un firewall y tener el software actualizado.

Criptografía de llave pública

La criptografía de llave pública se refiere a un sistema criptográfico asimétrico que requiere dos llaves separadas, una para cifrar la información y otra para descifrarla, ninguna de las dos llaves puede realizar ambas funciones.

Bajo este esquema, cualquier persona puede cifrar mensajes usando la llave pública, pero solo quién tenga la llave privada puede descifrar el mensaje, la seguridad depende de la secrecía de dicha llave.

La criptografía asimétrica esta basada en relaciones matemáticas que no tienen una solución eficiente, el uso de estos algoritmos también permite verificar la autenticidad de el mensaje creando una firma digital usando la llave privada que puede ser verificado usando la llave pública.

Algunos ejemplos de protocolos utilizan la criptografía de llave pública son SSH, SSL y PGP.

Respaldar todas las bases de datos MySQL con SSH

Para hacer un respaldo de todas las bases de datos MySQL de nuestro usuario con SSH podemos usar mysqldump de la siguiente manera:

mysqldump -u <usuario> -p<password> –all-databases –lock-all-tables > <destino>.sql

<usuario> debe ser reemplazado por tu usuario de mysql
<password> debe ser reemplazado por tu contraseña de mysql
<destino> es la ruta y nombre del archivo donde queremos almacenar el respaldo
–all-databases indica que queremos reemplazar todas las bases de datos
–lock-all-tables bloquea las tablas una por una mientras está haciendo el respaldo para que sean consistentes (aunque no serán consistentes entre ellas ya que el respaldo se hace en estados distintos)

Podemos comprimir el archivo de respaldo en GZIP modificando un poco el comando de la siguiente manera:

mysqldump -u <usuario> -p<password> –all-databases –lock-all-tables | gzip > <destino>.sql.gz

Al igual que el anterior este nos creará un archivo en <destino> solo que esta vez con terminación .sql.gz

Respaldar cuentas de cPanel manualmente via SSH

cPanel tiene un script para generar respaldos completos de cuenta, via SSH lo podemos utilizar para generar estos archivos ya sea para tener una copia de seguridad o moverlo a otro servidor.

La forma de generar el respaldo es utilizando el siguiente comando:

/scripts/pkgacct <usuario> <destino> backup

<usuario> debe ser sustituido por el nombre de usuario a respaldar
<destino> debe ser reemplazado por la ubicación donde queremos almacenar el respaldo

Esto nos va a generar un archivo con el formato <usuario>.tar.gz en <destino>