Servidor de correo en VPS


En la actualidad, existen muchas soluciones en el mercado que te permiten enviar correos desde tu aplicación (Mailgun, Postmark, etc.), sin embargo, todos son productos “freemium” y tienen una cantidad limitada de correos mensuales, que en la mayoría de los casos es suficiente para un proyecto pequeño. Pero, debido a algunos problemas técnicos con las plataformas 😅, tuve que optar por instalar mi propio servicio de correo electrónico.

Objetivo

Instalar un servicio de correo electrónico en un VPS con Ubuntu 22.04 que me permita enviar correos desde una aplicación alojada en el mismo servidor, en un subdominio diferente.

Proceso

El proceso es relativamente sencillo:

  • Tener un dominio vigente.
  • Configurar un registro tipo MX en el DNS de tu proveedor de dominios.
  • Instalar y habilitar el servicio de postfix en el servidor.

Sin embargo, la teoría no siempre es perfecta. Me encontré con diversos factores que hicieron larga la instalación y configuración del servicio.

Reto 1: Seguridad del servicio Postfix

Por fortuna de todos, los frameworks de desarrollo tienen restricciones que te impiden usar servicios con seguridad baja o nula. Tal es el caso de mi aplicación, la cual no me permite usar el servicio de SMTP si no tiene una encriptación TLS y, por ende, requería de un certificado SSL.

Para ello, me apoyé del tutorial de upcloud (que por cierto, es extremadamente bueno 🌟), pero un par de inconvenientes causaron un retraso de un par de horas.

Tan solo agregaría dos puntos importantes:

Punto de refuerzo 1 - Habilitar submission

Submission es uno procesos que vienen en el paquete de instalación de postfix, pero, viene deshabilitado por defecto. Su función es escuchar peticiones de aplicaciones de terceros para enviar un correo electrónico cuándo estas lo soliciten.

Para habilitarlo:

  1. Debemos editar el archivo /etc/postfix/master.cf
  2. Quitar el comentario de la entrada del proceso submission (o agregar la línea completa en caso de que no exista), quedando de la siguiente manera:
submission inet n       -       y       -       -       smtpd

Punto de refuerzo 2 - Habilitar los puertos de postfix en el firewall

En caso de tener algún firewall activo, es importante que permitas el acceso a los puertos de email. En mi caso, usando ufw, logré esto con los siguientes comandos:

$ ufw allow Postfix
$ ufw allow "Postfix SMTP"
$ ufw allow "Postfix Submission"

Reto 2: [Configuración de Laravel] - host

Para poder enviar notificaciones por correo electrónico que traía mi aplicación por defecto, se requiere especificar los siguientes datos:

Driver=smtp
Host=
Port=
User=
Password=
Encryption=

El servidor, en teoría, podría ser suplido por los valores 127.0.0.1 o localhost. Sin embargo, esto causó bastantes problemas porque el servicio de portfix está configurado para rechazar peticiones cuyo host difiera del dominio principal. Para solucionarlo:

  1. Debemos modificar el archivo /etc/postfix/main.cf
  2. En la entrada mydestination, debemos agregar el dominio esperado, ejemplo:
mydestination = example.com, mail.example.com, $mydomain, $myhostname, localhost.$myhostname, localhost.com, , localhost

Agregamos el texto: mail.example.com, $mydomain, $myhostname, localhost.$myhostname

Reto 3: [Configuración de Laravel] - Usuario y contraseña

En el post compartido por upcloud, se asume que conocemos el funcionamiento del servicio postfix, ya que su intención es la instalación y configuración. Sin embargo, para poder utilizarlo en mi aplicación, es necesario proporcionar un usuario y contraseña.

Como dato curioso: cada usuario creado en linux genera su propio usuario de correo electrónico, compuesto por {USER_NAME}@{POSTFIX_DOMAIN}, por ejemplo, si creamos un usuario anne_droid y asumiendo que el dominio configurado en postfix es example.com, su correo por defecto sería anne_droid@example.com.

Para solucionar este punto, bastó con ejecutar las siguientes instrucciones en la terminal:

  1. Crear un usuario que no requirere login:
$ adduser -s /sbin/nologin {USERNAME};
  1. Establecer una contraseña de usuario
$ passwd {USERNAME}

Una vez creado, tanto el usuario como la contraseña, la aplicación se configuró con estos valores.

Reto 3: Los correos no llegaban 😢

Finalmente, el servicio de correo electrónico parecía configurado y no arrojaba ningún error al enviar correos, pero estos jamás llegaban.

Investigando un poco, los registros de correo electrónico suelen encontrarse en /var/log/mail.log y gracias a ellos descubrí que el servicio estaba siendo rechazado por Google con el error:

550-5.7.26 This mail is unauthenticated, which poses a security risk to the sender and Gmail users, and has been blocked. The sender must authenticate with at least one of SPF or DKIM. 

La documentación de Google explica cómo resolver este problema desde Google Workspace, pero no tiene secciones para la configuración de terceros.

Después de buscar información en algunos sitios, todo se resume a lo siguiente:

Se deben implementar 3 estándares de seguridad para pasar los filtros de Google, cada uno equivale a una entrada TXT en el proveedor de dominios y DNS.

SPF (Sender Policy Framework)

Ayuda a prevenir la suplantación de identidad, permitiendo especificar qué servidores están autorizados para enviar correos en nombre de tu dominio.

Basta con agregar un registro en la tabla de DNS con los siguientes datos:

  • Tipo: TXT
  • Host: @
  • Valor: v=spf1 mx -all
  • TTL: Automatic | 60 Min

Es importante mencionar que esta entrada es genérica, pero se puede mejorar muchísimo creando un registro personalizado usando la palabra include que permite especificar que solo cierto dominio puede enviar correos, por ejemplo:

v=spf1 include:example.com ~all

Que cumpliría con el mismo proposito.

DKIM (DomainKeys Identified Mail)

Permite asociar un dominio con un mensaje de correo electrónico, lo que permite a una organización responsabilizarse del mensaje.

Probablemente el más complejo de todos, consta de 3 pasos:

  1. Instalar el servicio opendkim y opendkim-tools.
$ apt-get install opendkim opendkim-tools
  1. Crear un par de llaves usando:
$ mkdir -p /etc/opendkim/keys
$ cd /etc/opendkim/keys
$ opendkim-genkey -b 2048 -h rsa-sha256 -r -v -s default -d example.com

Esto generará 2 archivos: un default.private, que se usará de forma interna, y un default.txt, cuyo contenido se va a usar en el siguiente paso.

  1. Configurar el archivo /etc/opendkim.conf, en específico los siguientes valores:
Mode                    sv

Domain          example.com
Selector        default # Important for the DNS record
KeyFile         /etc/opendkim/keys/default.private #{KEY_PATH}
  1. Revisar el contenido de la llave default.txt que tiene un formato similar al siguiente:
default._domainkey	IN	TXT	( "v=DKIM1; h=rsa-sha256; k=rsa; s=email; "
	  "p=long_text_1"
	  "long_text_2" )  ; ----- DKIM key default for example.com
  1. Ir al proveedor de DNS y agregar un nuevo registro:
  • Tipo: TXT
  • Host: default._domainkey
  • Valor: v=DKIM1; h=rsa-sha256; k=rsa; s=email; p=long_text_1long_text_2
  • TTL: Automatic | 60 Min

Importante: En este paso, default._domainkey puede cambiar dependiendo del selector que se usó en la configuración del archivo /etc/opendkim.conf, por ejemplo, si escribímos 2020 como selector, el valor de Host deberá cambiar a 2020._domainkey.

Importante: Al igual que se modificó el contenido del archivo default.txt para agregarlo al registro DNS, hay que prestar atención al valor de p que concatenó ambas cadenas sin espacios (long_text_1 y long_text_2) y eliminó por completo las comillas y los saltos de línea del archivo original.

DMARC (Domain-based Message Authentication, Reporting, and Conformance)

Especifica cómo los receptores deben tratar los correos que no pasan las comprobaciones de SPF y DKIM.

Se corrige agregando un registro en la tabla de DNS con los siguientes datos:

  • Tipo: TXT
  • Host: @
  • Valor: v=DMARC1; p=none; rua=mailto:info@example.com
  • TTL: Automatic | 60 Min

En dónde:

  • p: Especifica la acción a realizar y puede tener los valores: none, quarantine o reject según tus necesidades.
  • rua: Es un correo al cuál se puede dirigir en caso de ser necesario.

Resultados y conclusión

Una vez realizada esta serie de pasos, el servicio de correo electrónico quedó totalmente configurado y listo para su uso con mi aplicación interna.

Lo que me gustaría destacar, extrayendo la información esencial de los recursos utilizados, son los pasos a seguir para configurar un servicio smtp de forma segura:

  1. Contar con un dominio y acceso al panel de configuración de DNS.
  2. Agregar registros MX para el correo electrónico.
  3. Instalar los servicios postfix y dovecot. Puedes usar la guia de upclod para facilitar la instalación.
  4. Instalar un certificado SSL. Te puedes apoyar de let’s encrypt y certbot para ello.
  5. Configurar los certificados SSL en postfix.
  6. Habilitar el proceso submission de postfix.
  7. Habilitar los puertos de postfix en tu firewall.
  8. Implementar el estándar SPF.
  9. Implementar el estándar DKIM.
  10. Implementar el estándar DMARC.
  11. Probar la configuración auxiliándose del de mail-utils y el archivo /var/log/mail.log.
  12. Crear un usuario exclusivo para correo (que puede ser usado por tu aplicación).

El proceso es mucho más complejo de lo esperado, pero espero que esta guía haga un poco más ameno este proceso para todos nosotros.