En este artÃculo aprenderemos como montar un sistema de emails completo, podremos enviar/recibir emails, acceder mediante interfaz web, filtrar spam, definir filtros Sieve y firmar los emails salientes mediante DKIM.
Las tecnologÃas utilizadas para ello son:
- SendMail : EnvÃa y recibe emails
- Dovecot : Permite el acceso a los emails almacenados desde la interfaz web
- SpamAssassin : Filtra los emails entrantes en base a unas reglas determinadas
- RainLoop : Interfaz web de acceso a los emails
- Sieve : Sistema de filtrado/categorización de emails
Este artÃculo es bastante extenso asà que lo he dividido en varias secciones:
- SendMail
- Dovecot
- RainLoop
- SpamAssassin
- SPF
- DKIM
- DMARC
- SSL RainLoop
- SSL SendMail
- Reverse DNS
- Mantenimiento cuentas
- Debug
SendMail
El primer paso será asegurarnos de que nuestro servidor conozca su propio hostname:
127.0.0.1 HellStorm HellStorm.alfaexploit.com mail.alfaexploit.com localhost localhost.my.domain
SendMail viene instalado por defecto en FreeBSD, arrancamos el servicio:
sysrc sendmail_msp_queue_enable=yes
service sendmail start
Comprobamos que podamos acceder por red:
220 HellStorm.alfaexploit.com ESMTP Sendmail 8.16.1/8.16.1; Sun, 1 Jan 2023 17:39:07 +0100 (CET)
En el magnÃfico handbook de FreeBSD podemos encontrar una guÃa muy útil de la cual he extraÃdo la siguiente información.
Los permisos de acceso se filtran por origen en el fichero: /etc/mail/access
OK: Permitiremos la entrada del mail siempre y cuando el destino sea un dominio local(/etc/mail/local-host-names)
RELAY: Permitiremos el envÃo de mails a dominios de terceros a través de nuestro server
ERROR: Denegaremos el envÃo de mails con el mensaje indicado
SKIP: Denegaremos el envÃo de mails sin avisar al cliente que el email ha sido destruido
QUARANTINE: El mail se guardará en el servidor local pero no se enviará a su destino, el cliente recibirá una explicación de porque su email ha sido retenido
#From:cyberspammer.com ERROR:"550 We don't accept mail from spammers"
#From:okay.cyberspammer.com OK
#Connect:sendmail.org RELAY
#To:sendmail.org RELAY
#Connect:128.32 RELAY
#Connect:128.32.2 SKIP
#Connect:IPv6:1:2:3:4:5:6:7 RELAY
#Connect:suspicious.example.com QUARANTINE:Mail from suspicious host
#Connect:[127.0.0.3] OK
#Connect:[IPv6:1:2:3:4:5:6:7:8] OK
Pode defecto se aplica la polÃtica de OK desde cualquier ip siempre y cuando el destino sea local o esté listado en /etc/mail/local-host-names
Por ejemplo si intentamos enviar un email a
kr0m@alfaexploit.com
nos denegará el acceso pero para kr0m@localhost lo permitirá:
Trying 192.168.69.17...
Connected to 192.168.69.17.
Escape character is '^]'.
220 HellStorm.alfaexploit.com ESMTP Sendmail 8.16.1/8.16.1; Sun, 1 Jan 2023 17:39:07 +0100 (CET)
ehlo 192.168.69.17
250-HellStorm.alfaexploit.com Hello [192.168.69.17], pleased to meet you
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-8BITMIME
250-SIZE
250-DSN
250-ETRN
250-STARTTLS
250-DELIVERBY
250 HELP
mail from: test@kr0m.com
250 2.1.0 test@kr0m.com... Sender ok
rcpt to: kr0m@alfaexploit.com
250 2.1.5 kr0m@alfaexploit.com... Recipient ok
data
354 Enter mail, end with "." on a line by itself
prueba00
.
250 2.0.0 301Gd7Qr028498 Message accepted for delivery
quit
221 2.0.0 HellStorm.alfaexploit.com closing connection
Connection closed by foreign host.
En la sesión telnet no dará error pero en los logs podemos ver que no se ha enviado el mail:
Jan 1 17:41:05 HellStorm sm-mta[32142]: 301Gd7Qr028498: to=kr0m@alfaexploit.com, delay=00:00:12, xdelay=00:00:00, mailer=esmtp, pri=30009, relay=alfaexploit.com., dsn=5.3.5, stat=Local configuration error
En cambio si el destinatario es kr0m@localhost veremos la entrega:
Jan 1 17:45:31 HellStorm sm-mta[35699]: 301Gj39N027210: to=kr0m@localhost, delay=00:00:18, xdelay=00:00:00, mailer=local, pri=30396, relay=local, dsn=2.0.0, stat=Sent
El usuario kr0m puede leer este último email:
Mail version 8.1 6/6/93. Type ? for help.
"/var/mail/kr0m": 1 message 1 new
>N 1 test@kr0m.com Sun Jan 1 17:45 13/455
& 1
Message 1:
From test@kr0m.com Sun Jan 1 17:45:31 2023
Date: Sun, 1 Jan 2023 17:45:03 +0100 (CET)
From: test@kr0m.com
To: undisclosed-recipients:;
prueba01
&
Añadimos alfaexploit.com al grupo de dominios locales:
alfaexploit.com
Reiniciamos SendMail:
Volvemos a realizar la prueba:
Trying 192.168.69.17...
Connected to 192.168.69.17.
Escape character is '^]'.
220 HellStorm.alfaexploit.com ESMTP Sendmail 8.16.1/8.16.1; Sun, 1 Jan 2023 17:48:01 +0100 (CET)
ehlo 192.168.69.17
250-HellStorm.alfaexploit.com Hello [192.168.69.17], pleased to meet you
250-ENHANCEDSTATUSCODES
250-PIPELINING
250-8BITMIME
250-SIZE
250-DSN
250-ETRN
250-STARTTLS
250-DELIVERBY
250 HELP
mail from: test@kr0m.com
250 2.1.0 test@kr0m.com... Sender ok
rcpt to: kr0m@alfaexploit.com
250 2.1.5 kr0m@alfaexploit.com... Recipient ok
data
354 Enter mail, end with "." on a line by itself
prueba02
.
250 2.0.0 301Gm17S090416 Message accepted for delivery
quit
221 2.0.0 HellStorm.alfaexploit.com closing connection
Connection closed by foreign host.
Podemos ver que ahora la entrega se ha realizado correctamente:
Jan 1 17:48:28 HellStorm sm-mta[13349]: 301Gm17S090416: to=kr0m@alfaexploit.com, delay=00:00:12, xdelay=00:00:00, mailer=local, pri=30402, relay=local, dsn=2.0.0, stat=Sent
Leemos el mail:
Mail version 8.1 6/6/93. Type ? for help.
"/var/mail/kr0m": 1 message 1 new
>N 1 test@kr0m.com Sun Jan 1 17:48 13/461
& 1
Message 1:
From test@kr0m.com Sun Jan 1 17:48:28 2023
Date: Sun, 1 Jan 2023 17:48:01 +0100 (CET)
From: test@kr0m.com
To: undisclosed-recipients:;
prueba02
&
NOTA: Al meter el dominio alfaexploit.com en local-host-names es equivalente a haber creado las cuentas de email a partir de todos los usuarios que hay en el sistema operativo, para entornos mas grandes deberiamos buscar algún tipo de integración de usuarios virtuales a partir de una base de datos MySQL, LDAP o similar.
El fichero aliases define direcciones de correo que expanden a otros usuarios, direcciones externas, ficheros, programas u otros alias, en mi caso lo dejamos como viene por defecto pero root será un alias a
kr0m@alfaexploit.com
:
MAILER-DAEMON: postmaster
postmaster: root
_dhcp: root
_pflogd: root
auditdistd: root
bin: root
bind: root
daemon: root
games: root
hast: root
kmem: root
mailnull: postmaster
man: root
news: root
nobody: root
operator: root
pop: root
proxy: root
smmsp: postmaster
sshd: root
system: root
toor: root
tty: root
usenet: news
uucp: root
abuse: root
security: root
ftp: root
ftp-bugs: ftp
root: kr0m@alfaexploit.com
Regeneramos el hash del fichero:
Para realizar la conversión de una dirección de email a un mailbox utilizaremos el fichero virtusertable, el destino pueden ser mailboxes locales, mailboxes remotos, alias definidos en /etc/mail/aliases o ficheros. En mi caso no es necesario generar el fichero ya que no hago uso de dichas funcionalidades.
root@example.com root
postmaster@example.com postmaster@noc.example.net
@example.com joe
NOTA: Las entradas se comprueban en el orden en el que se encuentran en el fichero de configuración, en este ejemplo se ha configurado una entrada genérica para el dominio example.com, si el destinatario es cualquier distinto de root o postmaster se enviará a joe.
Si hemos generado el fichero /etc/mail/virtusertable, refrescamos el hash del fichero y reiniciamos SendMail:
service sendmail restart
Si necesitamos que algún equipo externo pueda utilizar nuestro servidor para enviar emails debemos indicar las ips/dns en el fichero relay-domains, en mi caso no es necesario:
service sendmail restart
Podemos encontrar la documentación sobre la configuración de SendMail en el fichero: /usr/share/sendmail/cf/README
Dovecot
Con SendMail podemos enviar y recibir emails pero no leer los recibidos, para ello necesitamos un servidor IMAP:
Copiamos los ficheros de configuración de ejemplo:
Eliminamos la configuración SSL ya que accederemos a los emails por interfaz web, el único punto de contacto con el exterior será a través de SendMail para enviar/recibir emails.
ssl = no
#ssl_cert = </etc/ssl/certs/dovecot.pem
#ssl_key = </etc/ssl/private/dovecot.pem
La interfaz web solo accede a Dovecot vÃa IMAP:
protocols = imap lmtp
Bindeamos Dovecot a la ip del servidor:
listen = 192.168.69.17
Le indicamos a Dovecot donde debe organizar los emails Sent/Drafts/Spam/Trash/Archive y donde buscar los emails recibidos:
mail_location = mbox:~/mboxDir:INBOX=/var/mail/%u
mail_privileged_group = mail
Ajustamos los permisos del directorio /var/mail:
Creamos los directorios de email en el home del usuario:
su -l kr0m
mkdir mboxDir
chmod 700 mboxDir
touch mboxDir/Sent
touch mboxDir/Drafts
touch mboxDir/Spam
touch mboxDir/Trash
touch mboxDir/Archive
chmod 600 mboxDir/Sent
chmod 600 mboxDir/Drafts
chmod 600 mboxDir/Spam
chmod 600 mboxDir/Trash
chmod 600 mboxDir/Archive
exit
Arrancamos el servicio:
service dovecot start
RainLoop
Instalamos RainLoop, una interfaz web que nos permitirá leer y enviar emails:
pkg install -y unzip curl wget socat
pkg install -y php82 php82-mbstring php82-tokenizer php82-pdo php82-pdo_mysql php82-phar php82-filter php82-zlib php82-dom php82-xml php82-xmlwriter php82-xmlreader php82-pecl-imagick php82-curl php82-session php82-ctype php82-iconv php82-gd php82-simplexml php82-zip php82-filter php82-tokenizer php82-calendar php82-fileinfo php82-intl php82-phar php82-soap php82-opcache php82-mysqli php82-bcmath php82-gmp
cp /usr/local/etc/php.ini-production /usr/local/etc/php.ini
Ajustamos algunos parámetros en el php.ini para poder enviar adjuntos de mayor tamaño:
date.timezone = Europe/Madrid
upload_max_filesize = 25M
post_max_size = 25M
Arrancamos el servicio php-fpm:
service php-fpm start
Instalamos un MySQL para poder almacenar los contactos del usuario:
Arrancamos el servicio:
service mysql-server start
Configuramos la base de datos:
Creamos el fichero de configuración para poder entrar sin necesidad de escribir el password cada vez:
[client]
user = root
password = XXXXXXXXX
Aseguramos el acceso:
Creamos la base de datos y un usuario con acceso a esta:
CREATE DATABASE rainloop;
CREATE USER rainloop@'192.168.69.17' IDENTIFIED WITH mysql_native_password BY 'XXXXXXXXX';
GRANT ALL PRIVILEGES ON rainloop.* TO rainloop@'192.168.69.17';
FLUSH PRIVILEGES;
exit;
Instalamos Nginx:
Arrancamos el servicio:
service nginx start
Creamos un vhost para RainLoop:
server {
listen 80;
server_name mail.alfaexploit.com;
root /usr/local/www/rainloop;
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ^~ /data {
deny all;
}
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_keep_conn on;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}
}
Incluimos el vhost en la configuración general de Nginx, añadimos el include y aumentamos el máximo del tamaño del body en la sección http{}:
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 25M;
include rainloop.conf;
Reiniciamos el servicio:
Nos bajamos e instalamos RainLoop:
cd /usr/local/www/rainloop
wget http://www.rainloop.net/repository/webmail/rainloop-latest.zip
unzip rainloop-latest.zip -d /usr/local/www/rainloop
rm rainloop-latest.zip
chown -R www:www /usr/local/www/rainloop
Accedemos al panel de administración:
http://mail.alfaexploit.com/?admin
admin
12345
Lo primero será cambiar el password
Configuramos el acceso a la base de datos donde se guardarán los contactos del usuario:
Contacts -> MySQL:
mysql:host=192.168.69.17;port=3306;dbname=rainloop
rainloop
XXXXXXXXX
Le damos al botón Test para comprobar el correcto acceso.
Le indicamos al login que añada de forma automática el @alfaexploit.com al usuario:
Añadimos el dominio:
Domains -> alfaexploit.com
IMAP
X.X.X.X
143
SMTP
X.X.X.X
25
Use short login
NOTA: Use short login -> Ya que el @alfaexploit.com nos lo pondrá RainLoop de forma automática tal y como le hemos indicado en el paso anterior.
Accedemos a la cuenta de email como usuario:
http://mail.alfaexploit.com
kr0m
PASSWORD-SO
Configuramos los directorios donde guardar los emails:
Ruedecita dentada de abajo -> Folders
System Folders
Asignamos el directorio a cada Folder, Deleted items y Junk emails podemos darle al ojo para que no salgan.
Realizamos pruebas de envÃo y recepción de emails y consultamos los logs asociados:
Sieve es un servicio de clasificación de emails, mediante filtros definidos por el sysadmin/usuario se pueden catalogar los emails entrantes, por ejemplo en base a ciertas cabeceras, origenes, esto resulta muy útil si combinamos SpamAssassin con Sieve ya que SpamAssassin nos marcará los emails con sus cabeceras y mediante Sieve filtraremos en base a estas.
SpamAssassin
Instalamos el milter de Spamassassin:
Habilitamos el servicio spamd indicándole desde que ips aceptará conexiones, en este caso la propia ip del servidor:
sysrc spamd_flags="-u spamd -H /var/spool/spamd -A 192.168.69.17"
Habilitamos el milter indicándole donde debe generar el socket Unix:
sysrc spamass_milter_socket="/var/run/spamass-milter.sock"
El resto de opciones que podrÃamos configurar los podemos ver en:
: ${spamass_milter_enable="NO"}
: ${spamass_milter_socket="/var/run/spamass-milter.sock"}
: ${spamass_milter_flags="-f -p ${spamass_milter_socket} ${spamass_milter_localflags}"}
: ${spamass_milter_socket_owner="root"}
: ${spamass_milter_socket_group="wheel"}
: ${spamass_milter_socket_mode="644"}
Actualizamos las reglas de SpamAssassin y las compilamos:
sa-compile
Arrancamos SpamAssassin y SpamAssassin-milter:
service spamass-milter start
Generamos un fichero base de configuración para SendMail:
make
El comando anterior habrá generado un fichero con el nombre del host, definimos el milter al final del fichero:
MAIL_FILTER(`spamassassin', `S=local:/var/run/spamass-milter.sock, F=, T=C:15m;S:4m;R:4m;E:10m')
define(`confINPUT_MAIL_FILTERS', `spamassassin')
Compilamos a M4 la nueva configuración:
Actualizamos la configuración de SendMail con la nuestra:
cp HellStorm.cf sendmail.cf
Reiniciamos el servicio:
Ponemos un tail para ver los logs, aparecerán algunos errores ya que SpamAssassin todavÃa no está configurado del todo:
Nos enviamos un email y podemos ver en los logs como spamd intercepta el email entrante y le asigna una puntuación:
Jan 1 19:42:55 HellStorm spamd[41333]: spamd: result: . -5 - DKIM_SIGNED,DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,HTML_MESSAGE,RCVD_IN_DNSWL_HI,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS,TVD_SPACE_RATIO scantime=0.3,size=2793,user=root,uid=58,required_score=5.0,rhost=192.168.69.17,raddr=192.168.69.17,rport=50983,mid=<CA+SWLLzfBAmUfQQTmiFejuKVkaRpkZKdd7ECv3qXLrPaW-QJAQ@mail.gmail.com>,autolearn=unavailable autolearn_force=no
Si miramos el email en raw veremos las cabeceras añadidas por SpamAssassin:
X-Spam-Status: No, score=-5.2 required=5.0 tests=DKIM_SIGNED,DKIM_VALID,
DKIM_VALID_AU,DKIM_VALID_EF,FREEMAIL_FROM,HTML_MESSAGE,
RCVD_IN_DNSWL_HI,RCVD_IN_MSPIKE_H2,SPF_HELO_NONE,SPF_PASS,
TVD_SPACE_RATIO autolearn=unavailable autolearn_force=no version=3.4.6
X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on
HellStorm.alfaexploit.com
NOTA: Si queremos marcar como Spam un dominio o dirección en concreto añadimos al final del fichero local.cf de SpamAssassin:
blacklist_from *@126.com
blacklist_from hacker@xxxxxxx.com
Reiniciamos el servicio:
Si vemos en los logs errores de este estilo:
Oct 21 22:21:19 DrWho spamd[3572]: plugin: eval failed: bayes: (in learn) locker: safe_lock: cannot create tmp lockfile /root/.spamassassin/bayes.lock.DrWho.alfaexploit.com.3572 for /root/.spamassassin/bayes.lock: Permission denied
Debemos definir la
ubicación de la base de datos
de SpamAssasin a una localización donde el usuario spamd tenga acceso:
bayes_path /var/spamassassin/bayes_db/bayes
bayes_file_mode 0775
Creamos el directorio:
Le asignamos los permisos comentados en la
documentación:
chown root:spamd /var/spamassassin/bayes_db/
Reiniciamos el servicio:
Ahora que ya marcamos nuestros emails habrá que catalogarlos según la reputación asignada por SpamAssassin, para ello emplearemos Sieve que nos permitirá configurar filtros en base a multitud de aspectos sobre el email, pero para poder utilizarlo debemos hacer que SendMail entregue los mails locales vÃa  LMTP a Dovecot:
Sustitumos:
FEATURE(local_lmtp)
Por:
FEATURE(local_lmtp,`[IPC]',`FILE /var/run/dovecot/lmtp')dnl
Recompilamos la configuración:
Sustituimos la configuración actual por la nuestra:
Reiniciamos el servicio:
Instalamos el paquete necesario para que Dovecot soporte Sieve:
Lo habilitamos como protocolo:
protocols = imap lmtp sieve
Y como pulgin LMTP:
protocol lmtp {
mail_plugins = $mail_plugins sieve
}
En la configuración de plugins configuramos donde se almacenarán los filtros Sieve de los usuarios y que filtro se debe aplicar, además indicamos un filtro que siempre se ejecutará antes que los definidos por el usuario, ideal para que los sysadmins puedan realizar sus filtrados pre-user:
plugin {
sieve = file:~/.sieve;active=~/.sieve/dovecot.sieve
sieve_before = file:/var/lib/dovecot/default.sieve
}
Cuando se programan scripts en Sieve se debe indicar arriba del todo las librerias que requiere y luego ya hacer uso de ellas en el resto de script:
require ["fileinto"];
if header :contains "X-Spam-Flag" "YES" {
fileinto "Spam";
stop;
}
Compilamos el script:
Creamos el directorio de los scripts Sieve en el home del usuario:
mkdir /home/kr0m/.sieve
exit
Comprobamos que el módulo LMTP de Dovecot haya cargado el plugin Sieve:
mail_plugins = sieve
Reiniciamos Dovecot:
Nos conectamos manualmente al Sieve-manager:
Trying 127.0.0.1...
Connected to HellStorm.
Escape character is '^]'.
"IMPLEMENTATION" "Dovecot Pigeonhole"
"SIEVE" "fileinto reject envelope encoded-character vacation subaddress comparator-i;ascii-numeric relational regex imap4flags copy include variables body enotify environment mailbox date index ihave duplicate mime foreverypart extracttext"
"NOTIFY" "mailto"
"SASL" "PLAIN"
"VERSION" "1.0"
OK "Dovecot ready."
Ya deberÃa de ejecutarse el script sieve_before(/var/lib/dovecot/default.sieve) que mueve los emails marcados como spam por SpamAssassin al directorio Spam.
Accedemos al panel de administración de RainLoop y habilitamos el Sieve para nuestro dominio:
http://mail.alfaexploit.com/?admin
admin
XXXXXXX
Domains -> alfaexploit.com
Sieve configuration:
Allow sieve scripts
Allow custom user script
Server: 192.168.69.17 Port: 4190
Secure: None
Clickamos sobre el botón Update.
Accedemos con la cuenta de usuario normal y configuramos un filtro:
Configuración -> Filters
NOTA: Debemos filtrar el tráfico de red mediante firewall para evitar accesos no autorizados a los servicios de Sieve(4190)/SpamAssassin(783).
SPF
Mediante SPF indicaremos que servidores están autorizados a enviar emails de nuestro dominio, esto no es mas que unas entradas DNS indicando las ips, en mi caso quedarÃa del siguiente modo.
A 92.176.161.228 mail.alfaexploit.com
MX mail.alfaexploit.com
TXT spf2.0/mfrom,pra a mx -all
TXT v=spf1 mx -all
NOTA: La primera entrada es una entrada simple A resolviendo mail.alfaexploit.com a la ip, la segunda indica que servidor(MX) es el encargado de recibir emails, la tercera indica que la ip del servidor indicado en el registro MX del dominio está autorizado a enviar emails y la cuarte hace lo mismo que la tercera pero se trata de spf1.
SPF soporta dos tipos de fails:
- hardfails: Cualquier email que provenga de una ip no permitida se eliminará, este comportamiento se indica al meter -all en la entrada DNS.
- softfail: Cualquier email que provenga de una ip no permitida se marcará como Spam, este comportamiento se indica al meter ~all en la entrada DNS.
Comprobamos que todas las entradas resuelvan como es debido:
0 mail.alfaexploit.com.
79.116.145.12
"spf2.0/mfrom,pra a mx ~all"
"v=spf1 mx ~all"
DKIM
Mediante DKIM seremos capaces de firmar los emails salientes con una clave privada de este modo el receptor podrá verificar que el email fué enviado desde nuestro servidor y no desde otro haciéndose pasar por nosotros, la clave de este proceso consiste en publicar la pubkey en una entrada DNS para que el receptor pueda obtenerla y comprobar asà la autenticidad del email.
Instalamos el milter OpenDkim:
Habilitamos el servicio:
Generamos la pareja private-key/pub-key:
cd /var/db/dkim
opendkim-genkey -s smtp -d alfaexploit.com
NOTA: El parámetro -s smtp no es mas que el selector, se trata del un string con el que se tendrá que realizar la query DNS para obtener el valor de la pubkey.
La configuración de OpenDkim quedarÃa del siguiente modo:
Domain alfaexploit.com
KeyFile /var/db/dkim/smtp.private
InternalHosts /var/db/dkim/internal_hosts
Selector smtp
Socket local:/var/run/milteropendkim/milter-opendkim.sock
Syslog Yes
Ajustamos los permisos para que OpenDkim pueda generar el fichero de socket unix:
Definimos que ips podrán conectar al milter, en mi caso la ip del propio servidor:
192.168.69.17
Arrancamos el servicio:
Configuramos SendMail para que haga uso del milter nuevo:
MAIL_FILTER(`spamassassin', `S=local:/var/run/spamass-milter.sock, F=, T=C:15m;S:4m;R:4m;E:10m')
MAIL_FILTER(`dkim-filter', `S=/var/run/milteropendkim/milter-opendkim.sock, F=T, T=R:2m')
define(`confINPUT_MAIL_FILTERS', `spamassassin, dkim-filter')
Compilamos  y actualizamos la configuración:
cp HellStorm.cf sendmail.cf
Reiniciamos el servicio:
Si enviamos un email veremos que salen firmados.
DKIM-Signature: v=1; a=rsa-sha256; c=simple/simple; d=alfaexploit.com; s=smtp; t=1672616405; bh=G1jVXBHX92kUkSHkyQ6MzL5LYYAW8yEUeAJmHQPkbeQ=; h=Date:From:Subject:To; b=u49nGRAoDNY0tWxab5lbE7GSDvJ796snMaJbMDKA9+Iz5NSxeqGbfCCswsDo+yuZ8
96TXfWXO0tzo0kjSqCcrm5s2gdhNPeG9Q2+eTjs71pXJS7sonqw4AncamPQl2kOx3i
ZFCq67c/BK+gup601dSdrurFVLt57jpgUcOwD9Kc=
Pero debemos publicar nuestra pubkey en Internet para que los servidores que reciban los emails nuestros puedan comprobar que la firma del email se generó con la private key correspondiente a la pubkey publicada:
smtp._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+6l6oAODR0hUzsHqb2StBHVKlXdemKhbRNaCNDdoqMH9yi7TOfYeO4Ko5Wnp4Gq449ur8h14Afvgji24DC6GCBNbHCcDh67M9HZW28BJPRoaaIInQHzt5+9oVa9BREliNa50gfbwmNS/WnrZ6o3X94xCCbb6xcdQJC6FCrGoyMQIDAQAB" ) ; ----- DKIM key smtp for alfaexploit.com
El registro TXT tendrá el siguiente contenido:
v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+6l6oAODR0hUzsHqb2StBHVKlXdemKhbRNaCNDdoqMH9yi7TOfYeO4Ko5Wnp4Gq449ur8h14Afvgji24DC6GCBNbHCcDh67M9HZW28BJPRoaaIInQHzt5+9oVa9BREliNa50gfbwmNS/WnrZ6o3X94xCCbb6xcdQJC6FCrGoyMQIDAQAB
La entrada DNS final debe contener el selector(smtp):
TXT smtp._domainkey v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+6l6oAODR0hUzsHqb2StBHVKlXdemKhbRNaCNDdoqMH9yi7TOfYeO4Ko5Wnp4Gq449ur8h14Afvgji24DC6GCBNbHCcDh67M9HZW28BJPRoaaIInQHzt5+9oVa9BREliNa50gfbwmNS/WnrZ6o3X94xCCbb6xcdQJC6FCrGoyMQIDAQAB
Si consultamos la entrada nos devuelve la pubkey configurada:
"v=DKIM1;k=rsa;p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+6l6oAODR0hUzsHqb2StBHVKlXdemKhbRNaCNDdoqMH9yi7TOfYeO4Ko5Wnp4Gq449ur8h14Afvgji24DC6GCBNbHCcDh67M9HZW28BJPRoaaIInQHzt5+9oVa9BREliNa50gfbwmNS/WnrZ6o3X94xCCbb6xcdQJC6FCrGoyMQIDAQAB;t=s;"
DMARC
DMARC son unos registros DNS donde indicamos a los servidores que reciben emails que deben hacer con este cuando el email entrante no supere el SPF/DKIM. Por supuesto cada ISP luego puede respetar lo indicado en el DMARC o no.
TXT _dmarc.alfaexploit.com
v=DMARC1\; p=reject\; rua=mailto:kr0m@alfaexploit.com\; ruf=mailto:kr0m@alfaexploit.com\; pct=100
Diseccionemos cada uno de los campos:
- v: Versión del protocolo
- p: Indica la polÃtica DMARC a seguir
- rua: Donde se enviarán las notificaciones cuando se reciba un email que no supere el SPF/DKIM
- ruf: Donde se enviará una copias de los emails-spam cuando se reciba un email que no supere el SPF/DKIM
- pct: A que % de los emails recibidos se le debe aplicar el filtro DMARC
Las posibles polÃticas son:
- none: Tratar el email sin aplicar DMARC
- quarantine: Aceptar el email pero tratarlo como Spam
- reject: Rechazar el email
Comprobamos que el registro DNS responda con la información correcta:
"v=DMARC1; p=reject; rua=mailto:kr0m@alfaexploit.com; ruf=mailto:kr0m@alfaexploit.com; pct=100"
Podemos ver como Google da por buenas las tres comprobaciones:
SSL RainLoop
Si vamos a acceder a la interfaz del RainLoop desde Internet mas vale hacerlo por SSL.
Para ello reconfiguraremos Nginx quitando el include del fichero rainloop.conf temporalmente:
...
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 25M;
#include rainloop.conf;
...
Reiniciamos el servicio:
Instalamos ACME:
Emitimos la petición de certificado:
[Mon Jan 2 08:02:05 CET 2023] Your cert is in: /root/.acme.sh/mail.alfaexploit.com/mail.alfaexploit.com.cer
[Mon Jan 2 08:02:05 CET 2023] Your cert key is in: /root/.acme.sh/mail.alfaexploit.com/mail.alfaexploit.com.key
[Mon Jan 2 08:02:05 CET 2023] The intermediate CA cert is in: /root/.acme.sh/mail.alfaexploit.com/ca.cer
[Mon Jan 2 08:02:05 CET 2023] And the full chain certs is there: /root/.acme.sh/mail.alfaexploit.com/fullchain.cer
Volvemos a incluir rainloop.conf
...
http {
include mime.types;
default_type application/octet-stream;
client_max_body_size 25M;
#include rainloop.conf;
...
Pero esta vez solo escuchará en el puerto 443, además mi tráfico pasa por un balanceadro de tráfico, asà que utilizo
proxy_protocol
:
server {
listen 443 ssl proxy_protocol;
server_name mail.alfaexploit.com;
set_real_ip_from 192.168.69.11;
real_ip_header proxy_protocol;
root /usr/local/www/rainloop;
ssl_certificate "/root/.acme.sh/mail.alfaexploit.com/fullchain.cer";
ssl_certificate_key "/root/.acme.sh/mail.alfaexploit.com/mail.alfaexploit.com.key";
index index.php;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ^~ /data {
deny all;
}
location ~ \.php$ {
try_files $uri =404;
include fastcgi_params;
fastcgi_index index.php;
fastcgi_split_path_info ^(.+\.php)(.*)$;
fastcgi_keep_conn on;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_pass 127.0.0.1:9000;
}
}
Reiniciamos el servicio:
SSL Sendmail
Por defecto SendMail viene con SSL habilitado con certificados autofirmados:
dnl Enable STARTTLS for receiving email.
define(`CERT_DIR', `/etc/mail/certs')dnl
define(`confSERVER_CERT', `CERT_DIR/host.cert')dnl
define(`confSERVER_KEY', `CERT_DIR/host.key')dnl
define(`confCLIENT_CERT', `CERT_DIR/host.cert')dnl
define(`confCLIENT_KEY', `CERT_DIR/host.key')dnl
define(`confCACERT', `CERT_DIR/cacert.pem')dnl
define(`confCACERT_PATH', `CERT_DIR')dnl
define(`confDH_PARAMETERS', `CERT_DIR/dh.param')dnl
Para configurar los nuestros primero haremos una copia de los originales:
Los certificados se obtienen en el servidor web, asà que los sincronizamos mediante el siguiente script:
!/usr/local/bin/bash
function sendTelegram {
message=${@:1}
#curl -s -X POST https://api.telegram.org/botAPI_KEY/sendMessage -d chat_id=CHAT_ID -d text="$message"
curl -s -X POST https://api.telegram.org/bot535179217:AAGXRe1df_1WNgqxOCfC8VrCNKGqouhslLw/sendMessage -d chat_id=30418601 -d text="$message"
}
if [ -f "/etc/mail/certs/alfaexploit.com/fullchain.cer" ] && [ -f "/etc/mail/certs/alfaexploit.com/alfaexploit.com.key" ] && [ -f "/etc/mail/certs/alfaexploit.com/ca.cer" ]; then
mv /etc/mail/certs/alfaexploit.com/fullchain.cer /etc/mail/certs/alfaexploit.com/fullchain.cer.ori
mv /etc/mail/certs/alfaexploit.com/alfaexploit.com.key /etc/mail/certs/alfaexploit.com/alfaexploit.com.key.ori
mv /etc/mail/certs/alfaexploit.com/ca.cer /etc/mail/certs/alfaexploit.com/ca.cer.ori
PREV_CERTS_FOUND=1
else
PREV_CERTS_FOUND=0
fi
if [ ! -d /etc/mail/certs/alfaexploit.com/ ]; then
mkdir /etc/mail/certs/alfaexploit.com/
fi
fetch http://admin:XXXX@192.168.69.19:8080/fullchain.cer -o /etc/mail/certs/alfaexploit.com/fullchain.cer
if [ $? -ne 0 ]; then
sendTelegram "$HOSTNAME-SMTP/SSL: Cant download http://admin:XXXX@192.168.69.19:8080/fullchain.cer"
fi
fetch http://admin:XXXX@192.168.69.19:8080/alfaexploit.com.key -o /etc/mail/certs/alfaexploit.com/alfaexploit.com.key
if [ $? -ne 0 ]; then
sendTelegram "$HOSTNAME-SMTP/SSL: Cant download http://admin:XXXX@192.168.69.19:8080/alfaexploit.com.key"
fi
fetch http://admin:XXXX@192.168.69.19:8080/ca.cer -o /etc/mail/certs/alfaexploit.com/ca.cer
if [ $? -ne 0 ]; then
sendTelegram "$HOSTNAME-SMTP/SSL: Cant download http://admin:XXXX@192.168.69.19:8080/ca.cer"
fi
chmod 600 /etc/mail/certs/alfaexploit.com/*
if [ $PREV_CERTS_FOUND -eq 1 ]; then
for file in /etc/mail/certs/alfaexploit.com/*.ori; do
md5_ori=$(md5 $file|awk '{print$4}')
file=${file::-4}
md5=$(md5 $file|awk '{print$4}')
#echo "md5_ori: $md5_ori -- md5: $md5"
if [ "$md5_ori" != "$md5" ]; then
echo ">> New certs detected -> Restarting SendMail"
service sendmail restart
sendTelegram "$HOSTNAME:25-SSL alfaexploit certificates updated"
break
fi
done
fi
check=$(echo Q|openssl s_client -connect 192.168.69.17:25 -starttls smtp 2>/dev/null | grep '0 s:' | grep 'CN =' | awk -F "CN = " '{print$2}' | awk -F "," '{print$1}')
#echo "check: $check"
if [ "$check" != "alfaexploit.com" ]; then
sendTelegram "$HOSTNAME-SMTP/SSL Incorrect CommonName: $check"
fi
Asginamos los permisos necesarios:
Lo crontabeamos:
*/30 * * * * /root/.scripts/get_alfaexploit_certs.sh >/dev/null 2>&1
Una vez copiados realizamos la siguiente configuración:
dnl Enable STARTTLS for receiving email.
define(`CERT_DIR', `/etc/mail/certs/alfaexploit.com')dnl
define(`confSERVER_CERT', `CERT_DIR/fullchain.cer')dnl
define(`confSERVER_KEY', `CERT_DIR/alfaexploit.com.key')dnl
define(`confCLIENT_CERT', `CERT_DIR/fullchain.cer')dnl
define(`confCLIENT_KEY', `CERT_DIR/alfaexploit.com.key')dnl
define(`confCACERT', `CERT_DIR/ca.cer')dnl
define(`confCACERT_PATH', `CERT_DIR')dnl
Compilamos la configuración:
cp HellStorm.cf sendmail.cf
Reiniciamos el servicio:
Dejamos un tail en el log para asegurarnos de que todo sigue funcionando:
Realizamos una prueba manual para comprobar que nos sirve el certificado correcto:
Verification: OK
Una forma de comprobar el SSL tanto la entrada como la salida es mediante esta web:
https://ssl-tools.net/mailservers
https://ssl-tools.net/mails
Reverse DNS
En cuanto a reverses DNS, los servidores de GMail/HotMail/OVH solo exigen que la ip de origen del mail tenga una reverse, da igual que reverse sea, NO exigen que corresponda con el dominio origen del email.
Mantenimiento cuentas
Con el tiempo los buzones pueden llenarse de emails por dejadez por parte de los usuarios provocando de este modo un consumo excesivo del espacio en disco, una denegación de servicio SMTP o incluso llegando a causar problemas al sistema operativo entero en caso de no haber particionado correctamente el disco o no haber aplicado cotas de disco.
Dovecot proporciona una herramienta de gestión del sistema de email llamada doveadm que además proporciona comandos útiles para la administración de cuentas de usuario.
Podemos ver donde almacena el usuario sus emails con el siguiente comando:
field value
uid 1001
gid 1001
home /home/kr0m
mail mbox:~/mboxDir:INBOX=/var/mail/kr0m
system_groups_user kr0m
Podemos consultar las carpetas de email:
Drafts
Trash
Archive
Sent
Spam
INBOX
Podemos ver de forma general el estado de cada carpeta:
Spam messages=4 recent=0 uidnext=2343 uidvalidity=1584649428 unseen=4 highestmodseq=4277 vsize=31212 guid=1e54ce3009d6735e13310000d09efc50 firstsaved=1661967302
También podemos obtener una lista de los email de dicha carpeta:
uid date.saved
2339 2022-08-31 19:35:02
2340 2022-09-01 07:14:41
2341 2022-09-01 09:25:54
2342 2022-09-01 22:00:09
Algo muy útil antes de borrar emails es hacer un búsqueda previa para asegurarnos de que vamos a borrar el contenido correcto:
1e54ce3009d6735e13310000d09efc50 2339
1e54ce3009d6735e13310000d09efc50 2340
Borramos los email:
Si consultamos ahora, veremos que solo quedan dos emails:
uid date.saved
2341 2022-09-01 09:25:54
2342 2022-09-01 22:00:09
Spam messages=2 recent=0 uidnext=2343 uidvalidity=1584649428 unseen=2 highestmodseq=4278 vsize=15667 guid=1e54ce3009d6735e13310000d09efc50 firstsaved=1662017154
Es buena idea crontabear un script que limpie cada cierto tiempo las cuentas:
#!/usr/local/bin/bash
/usr/local/bin/doveadm expunge -u kr0m mailbox Spam savedbefore 1d
/usr/local/bin/doveadm expunge -u kr0m mailbox Trash savedbefore 7d
Asignamos los permisos necesarios al script:
Crontabeamos el script:
00 00 * * * /root/.scripts/clearMail >/dev/null 2>&1
El comando expugne permite ejecutar los comandos en todos los usuarios existentes pero NO cuando se están utilizando usuarios del sistema:
If the -A option is present, the command will be performed for all users.
Using this option in combination with system users from userdb { driver = passwd } is not recommended, because it contains also users with a lower UID than the one configured with the first_valid_uid setting.
When the SQL userdb module is used make sure that the iterate_query setting in /etc/dovecot/dovecot-sql.conf.ext matches your database layout. When using the LDAP userdb module, make sure that the iterate_attrs and iterate_filter settings in /etc/dovecot/dovecot-ldap.conf.ext match your LDAP schema. Otherwise doveadm(1) will be unable to iterate over all users.
Debug
Podemos habilitar el debug de Dovecot y Sieve configurando ciertos parámetros:
log_path = syslog
syslog_facility = mail
mail_debug = yes
Consultamos los logs:
Podemos dumpear toda la running-config de Dovecot con:
Los plugins cargados en el servicio LMTP de Dovecot:
Algunos enlaces interesantes:
https://wiki.dovecot.org/Pigeonhole/Sieve/Troubleshooting
https://wiki1.dovecot.org/Logging
Una web muy interesante cuando experimentamos problemas de entregabilidad es:
https://mxtoolbox.com/