Instalación y configuración de un servidor de correo (Postfix + Dovecot + PostfixAdmin + Roundcube) en Debian 13

servidor-correo

¿Qué vamos a montar?

En este tutorial vamos a levantar un servidor de correo completo en Debian 13 con dominios y buzones virtuales almacenados en una base de datos MariaDB. Cada pieza cumple un papel:

  • Postfix: el servidor SMTP, se encarga de enviar y recibir el correo.
  • Dovecot: el servidor IMAP/POP3, permite a los clientes leer los buzones; además valida las contraseñas (SASL) para Postfix y entrega el correo a los buzones vía LMTP.
  • PostfixAdmin: un panel web para administrar dominios, buzones y alias sin tocar la base de datos a mano.
  • Roundcube: el webmail, para leer y enviar correo desde el navegador.

Trabajaremos con usuarios virtuales: no son cuentas del sistema, sino filas en la base de datos, lo que facilita gestionar muchos buzones. En Debian 13 los paquetes son Postfix 3.10, Dovecot 2.4 (ojo, con una sintaxis de configuración distinta a la 2.3), MariaDB 11.8, PHP 8.4, PostfixAdmin 3.3 y Roundcube 1.6.

Como datos de ejemplo usaremos el dominio de correo midominio.example, el servidor mail.ejemplo.lan (IP 10.10.10.12) y el buzón juan@midominio.example.

Requisitos

  • Debian 13 instalado y actualizado, con nombre de host e IP fija.
  • Registros DNS MX y A apuntando al servidor (en un entorno de pruebas basta con /etc/hosts).
  • Los puertos 25, 587, 465 (SMTP), 143/993 (IMAP) y 80/443 (web) accesibles.
  • Privilegios de root.

Instalación de los paquetes

Instalamos toda la pila. Durante la instalación de Postfix elegimos «Sitio de Internet» y como nombre de correo mail.ejemplo.lan:

root@mail:~# apt update
root@mail:~# apt install -y postfix postfix-mysql \
    dovecot-core dovecot-imapd dovecot-lmtpd dovecot-mysql \
    mariadb-server \
    apache2 libapache2-mod-php php php-mysql php-mbstring php-intl php-gd php-curl php-zip php-xml \
    postfixadmin roundcube roundcube-core roundcube-mysql

Dos detalles propios de Debian 13: el módulo LMTP de Dovecot ahora se llama dovecot-lmtpd (no dovecot-lmtp), y la extensión php-imap ya no existe en PHP 8.4 (se eliminó del núcleo); no la instalamos porque Roundcube trae su propia implementación de IMAP.

Durante la instalación de postfixadmin y roundcube, el asistente de dbconfig-common nos ofrecerá crear automáticamente su base de datos; aceptamos y definimos las contraseñas cuando lo pida.

Base de datos y usuario virtual del sistema

PostfixAdmin crea su propia base de datos (la llamaremos postfixadmin) con las tablas domain, mailbox y alias. Creamos un usuario de solo lectura para que Postfix y Dovecot consulten esas tablas:

root@mail:~# mysql
CREATE USER 'mailuser'@'127.0.0.1' IDENTIFIED BY 'ClaveFicticiaDB';
GRANT SELECT ON postfixadmin.* TO 'mailuser'@'127.0.0.1';
FLUSH PRIVILEGES;
EXIT;

Creamos también el usuario del sistema vmail (UID/GID 5000), propietario de todos los buzones, y el directorio donde se guardará el correo:

root@mail:~# groupadd -g 5000 vmail
root@mail:~# useradd -g vmail -u 5000 vmail -d /var/mail/vmail -m
root@mail:~# chown -R vmail:vmail /var/mail/vmail

Configuración de PostfixAdmin

Editamos /etc/postfixadmin/config.local.php para fijar el esquema de cifrado de contraseñas (usaremos el de Dovecot, BLF-CRYPT, muy recomendable) y activar la generación de buzones:

<?php
$CONF['configured'] = true;
$CONF['encrypt'] = 'dovecot:BLF-CRYPT';
$CONF['dovecotpw'] = "/usr/bin/doveadm pw";
$CONF['create_mailbox_subdirs_prefix'] = '';
// En un laboratorio con dominios que no resuelven en DNS (como midominio.example)
// desactivamos la comprobación DNS; en producción con un dominio real déjalo en 'YES'.
$CONF['emailcheck_resolve_domain'] = 'NO';
$CONF['default_aliases'] = array (
    'abuse'      => 'abuse@midominio.example',
    'hostmaster' => 'hostmaster@midominio.example',
    'postmaster' => 'postmaster@midominio.example',
    'webmaster'  => 'webmaster@midominio.example',
);

Para que doveadm pw sea accesible por el usuario del servidor web, le damos permiso de lectura al fichero de configuración de Dovecot más adelante. Ahora abrimos en el navegador la página de setup:

http://10.10.10.12/postfixadmin/setup.php

Escribimos una contraseña en el formulario Generate setup_password y pulsamos el botón: la web nos devuelve una línea $CONF['setup_password'] = '$2y$...'; que copiamos y pegamos en config.local.php. Después recargamos la página y ya podremos autenticarnos con esa contraseña para crear el primer administrador. El aviso rojo “connection not secure” es normal al acceder por HTTP en un entorno de pruebas; en producción usaríamos HTTPS.

postfixadmin-setup

Iniciamos sesión en http://10.10.10.12/postfixadmin/ con la cuenta de administrador recién creada.

postfixadmin-login

Una vez dentro, PostfixAdmin nos muestra el menú principal con todas las acciones de administración.

postfixadmin-menu

Desde Lista de dominios → Nuevo dominio damos de alta nuestro dominio de correo midominio.example.

postfixadmin-dominio

Y en Buzones de correo → Añadir buzón creamos la cuenta juan@midominio.example con su contraseña.

postfixadmin-buzon

Configuración de Postfix

Creamos los ficheros de consulta MySQL que Postfix usará para saber qué dominios y buzones son válidos.

/etc/postfix/mysql-virtual-mailbox-domains.cf:

user = mailuser
password = ClaveFicticiaDB
hosts = 127.0.0.1
dbname = postfixadmin
query = SELECT domain FROM domain WHERE domain='%s' AND active = '1'

/etc/postfix/mysql-virtual-mailbox-maps.cf:

user = mailuser
password = ClaveFicticiaDB
hosts = 127.0.0.1
dbname = postfixadmin
query = SELECT maildir FROM mailbox WHERE username='%s' AND active = '1'

/etc/postfix/mysql-virtual-alias-maps.cf:

user = mailuser
password = ClaveFicticiaDB
hosts = 127.0.0.1
dbname = postfixadmin
query = SELECT goto FROM alias WHERE address='%s' AND active = '1'

Protegemos estos ficheros porque contienen la contraseña:

root@mail:~# chmod 640 /etc/postfix/mysql-virtual-*.cf
root@mail:~# chgrp postfix /etc/postfix/mysql-virtual-*.cf

Editamos /etc/postfix/main.cf y añadimos (o ajustamos) los parámetros de dominios virtuales, entrega vía LMTP a Dovecot, SASL y TLS:

myhostname = mail.ejemplo.lan
mydestination = localhost
mynetworks = 127.0.0.0/8, 10.10.10.0/24
message_size_limit = 52428800

# Dominios y buzones virtuales (MySQL / PostfixAdmin)
virtual_mailbox_domains = mysql:/etc/postfix/mysql-virtual-mailbox-domains.cf
virtual_mailbox_maps    = mysql:/etc/postfix/mysql-virtual-mailbox-maps.cf
virtual_alias_maps      = mysql:/etc/postfix/mysql-virtual-alias-maps.cf

# Entrega al buzón mediante Dovecot LMTP
virtual_transport = lmtp:unix:private/dovecot-lmtp

# Autenticación SASL contra Dovecot
smtpd_sasl_type = dovecot
smtpd_sasl_path = private/auth
smtpd_sasl_auth_enable = yes
smtpd_sasl_security_options = noanonymous

# TLS (certificado autofirmado para el laboratorio)
smtpd_tls_cert_file = /etc/ssl/certs/mail.ejemplo.lan.crt
smtpd_tls_key_file  = /etc/ssl/private/mail.ejemplo.lan.key
smtpd_tls_security_level = may
smtp_tls_security_level  = may

Generamos el certificado autofirmado del ejemplo (en producción usaríamos Let’s Encrypt):

root@mail:~# openssl req -new -x509 -days 3650 -nodes \
    -subj "/CN=mail.ejemplo.lan" \
    -out /etc/ssl/certs/mail.ejemplo.lan.crt \
    -keyout /etc/ssl/private/mail.ejemplo.lan.key

Habilitamos los servicios de envío autenticado submission (587) y submissions (465) en /etc/postfix/master.cf descomentando y dejando estos bloques:

submission inet n       -       y       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_sasl_type=dovecot
  -o smtpd_sasl_path=private/auth
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject

smtps     inet  n       -       y       -       -       smtpd
  -o syslog_name=postfix/smtps
  -o smtpd_tls_wrappermode=yes
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_sasl_type=dovecot
  -o smtpd_sasl_path=private/auth
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject

Configuración de Dovecot 2.4

Aquí está el mayor cambio respecto a versiones anteriores: Dovecot 2.4 usa una sintaxis nueva para el correo y para la conexión SQL.

En /etc/dovecot/conf.d/10-mail.conf definimos el almacenamiento Maildir y el usuario del sistema:

mail_driver = maildir
mail_path = ~/Maildir
mail_home = /var/mail/vmail/%{user | domain}/%{user | username}
mail_uid = vmail
mail_gid = vmail
first_valid_uid = 5000
last_valid_uid = 5000

namespace inbox {
  inbox = yes
}

En /etc/dovecot/conf.d/10-auth.conf habilitamos los mecanismos y el backend SQL:

auth_mechanisms = plain login
auth_allow_cleartext = yes
!include auth-sql.conf.ext

Y reescribimos /etc/dovecot/conf.d/auth-sql.conf.ext con la nueva sintaxis 2.4 (bloque de conexión mysql { }, passdb sql y userdb static):

sql_driver = mysql

mysql 127.0.0.1 {
  user = mailuser
  password = ClaveFicticiaDB
  dbname = postfixadmin
}

passdb sql {
  query = SELECT username AS user, password FROM mailbox \
          WHERE username = '%{user}' AND active = '1'
}

userdb static {
  fields {
    uid = vmail
    gid = vmail
    home = /var/mail/vmail/%{user | domain}/%{user | username}
  }
}

En /etc/dovecot/conf.d/10-master.conf publicamos los sockets que Postfix necesita: el de autenticación SASL y el de entrega LMTP, ambos dentro de /var/spool/postfix/private/:

service auth {
  unix_listener /var/spool/postfix/private/auth {
    mode = 0660
    user = postfix
    group = postfix
  }
}

service lmtp {
  unix_listener /var/spool/postfix/private/dovecot-lmtp {
    mode = 0600
    user = postfix
    group = postfix
  }
}

Y en /etc/dovecot/conf.d/20-lmtp.conf normalizamos el nombre de usuario a minúsculas. Importante: como nuestros usuarios son la dirección completa (usuario@dominio), usamos %{user | lower} para conservar el dominio; si usáramos %{user | username | lower} se quedaría solo con la parte local y la entrega fallaría:

protocol lmtp {
  auth_username_format = %{user | lower}
}

Por último, en /etc/dovecot/conf.d/10-ssl.conf indicamos el mismo certificado que en Postfix. Ojo con Dovecot 2.4: las directivas del certificado se renombraron respecto a la 2.3 (ssl_cert/ssl_key pasan a ser ssl_server_cert_file/ssl_server_key_file, ahora sin el prefijo <):

ssl = yes
ssl_server_cert_file = /etc/ssl/certs/mail.ejemplo.lan.crt
ssl_server_key_file = /etc/ssl/private/mail.ejemplo.lan.key
ssl_min_protocol = TLSv1.2

Reiniciamos ambos servicios y comprobamos la configuración de Dovecot:

root@mail:~# doveconf -n | head
root@mail:~# systemctl restart dovecot postfix

Configuración de Roundcube (webmail)

Roundcube ya quedó instalado y con su base de datos creada por dbconfig. Solo tenemos que apuntarlo a nuestro servidor IMAP/SMTP local editando /etc/roundcube/config.inc.php:

$config['imap_host'] = 'localhost:143';
$config['smtp_host'] = 'tls://localhost:587';
$config['smtp_user'] = '%u';
$config['smtp_pass'] = '%p';
$config['product_name'] = 'Correo midominio.example';
// Como el certificado es autofirmado, no verificamos el par en las conexiones locales
$config['imap_conn_options'] = ['ssl' => ['verify_peer' => false, 'verify_peer_name' => false]];
$config['smtp_conn_options'] = ['ssl' => ['verify_peer' => false, 'verify_peer_name' => false]];

Importante con el envío: el prefijo tls:// en smtp_host obliga a Roundcube a hacer STARTTLS. Nuestro servicio submission (587) está configurado con smtpd_tls_security_level = encrypt, es decir, no ofrece autenticación hasta que la conexión esté cifrada. Si dejamos localhost:587 sin tls://, Roundcube no negocia TLS, Postfix no anuncia AUTH y al enviar obtendríamos «Error SMTP: Ha fallado la autenticación».

En Debian el paquete deja el Alias /roundcube comentado en /etc/apache2/conf-available/roundcube.conf; lo descomentamos, activamos la configuración y recargamos Apache:

root@mail:~# sed -i 's|#\s*Alias /roundcube|Alias /roundcube|' /etc/apache2/conf-available/roundcube.conf
root@mail:~# a2enconf roundcube
root@mail:~# systemctl reload apache2

Abrimos http://10.10.10.12/roundcube/ e iniciamos sesión con el buzón que creamos, juan@midominio.example.

roundcube-login

Ya dentro tenemos la bandeja de entrada del webmail.

roundcube-bandeja

Prueba de envío y recepción

Para verificar el circuito completo, redactamos un correo desde Roundcube dirigido a otro buzón del dominio (por ejemplo maria@midominio.example) y lo enviamos.

roundcube-envio

Si todo está bien conectado, el mensaje sale por el servicio submission de Postfix, se autentica contra Dovecot y se entrega por LMTP en el buzón de destino. Lo confirmamos en la carpeta Enviados.

roundcube-enviado

También podemos seguir el recorrido en los registros:

root@mail:~# journalctl -u postfix -u dovecot -f

Y con esto ya hemos terminado la instalación y configuración de nuestro servidor de correo en Debian 13, con dominios y buzones virtuales administrados cómodamente desde PostfixAdmin y accesibles por webmail con Roundcube.