sábado, 24 de octubre de 2020

Servidor de correo: Correo entrante

La máquina no va a existir porque sí. Vamos a ver que todo tiene un porqué. En este caso, va a ser un servidor de correo.

En principio basta con instalar Postfix. Lo configuramos como Sitio de Internet y configuramos correctamente el nombre de la máquina. Lo interesante vendrá después.

Ya que tenemos un cortafuegos, vamos a tener que autorizar las conexiones que correspondan al correo electrónico:

iptables -A INPUT -p tcp -m tcp --dport smtp -m state --state NEW,ESTABLISHED -j ACCEPT
iptables -A OUTPUT -p tcp -m tcp --dport smtp -j ACCEPT
ip6tables -A INPUT -p tcp -m tcp --dport smtp -m state --state NEW,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p tcp -m tcp --dport smtp -j ACCEPT

Y activar la regla de Postfix correspondiente en fail2ban: en la sección [postfix] del fichero /etc/fail2ban/jail.local añado la línea

enabled = true

De momento no voy a poner modos más agresivos ni jaulas extra. En vez de cargar la jaula a mano, como en la entrada anterior, también basta con

/etc/init.d/fail2ban reload

Bendito sysvinit.

El Postfix en realidad se configura solo, con lo anterior ya recibiría correo. Pero no lo queremos "simplemente" para eso. En mi caso, este servidor va a encargarse de todo el correo de un dominio, y evidentemente no voy a crear todos los usuarios que tiene ese dominio como usuarios UNIX (sería de locos). Así que tienen que ser usuarios virtuales. Lo más cómodo es que el propio dominio sea un dominio virtual.

Para ello, en /etc/postfix/main.cf añadimos estas líneas (donde, evidentemente, he cambiado el dominio) al final:

#Dominios virtuales
virtual_mailbox_domains = example.com
virtual_mailbox_base = /var/mail/v
virtual_mailbox_maps = hash:/etc/postfix/vmailbox
virtual_minimum_uid = 1000
virtual_uid_maps = static:5000
virtual_gid_maps = static:5000
virtual_alias_maps = hash:/etc/postfix/virtual

Los usuarios que tienen que tener cuenta se definen en el fichero /etc/postfix/vmailbox que tenemos que crear y que queda así:

# /etc/postfix/vmailbox:
# Recuerda ejecutar postmap /etc/postfix/vmailbox
# despues de modificar este fichero
compras@example.com	    example.com/compras/
ventas@example.com	    example.com/ventas/
legal@example.com	    example.com/legal/
# Descomenta la línea de demajo para tener un buzon catch-all.
# @example.com	        example.com/catchall/
#...Se pueden añadir otros dominios...

Los otros usuarios a tener en cuenta son esos usuarios que todo dominio debe tener, que redirigimos en el fichero /etc/postfix/virtual que también tenemos que crear:

#/etc/postfix/virtual
# Recuerda ejecutar postmap /etc/postfix/virtual
# despues de modificar este fichero
postmaster@example.com          postmaster
root@example.com                root

A Postfix no se le da nada bien crear los directorios Maildir que hemos indicado, y además es quisquilloso con los permisos. Hay que crear el directorio padre (que al crearse en /var/mail aparecerá como root:mail con permisos 2750) y para cada usuario crear el suyo con unos permisos específicos:

mkdir /var/mail/v
chmod g+w /var/mail/v
mkdir /var/mail/v/example.com
chmod g+w /var/mail/v/example.com
mkdir /var/mail/v/example.com/compras
chown 5000:5000 /var/mail/v/example.com/compras
mkdir /var/mail/v/example.com/ventas
chown 5000:5000 /var/mail/v/example.com/ventas
mkdir /var/mail/v/example.com/legal
chown 5000:5000 /var/mail/v/example.com/legal

...y así sucesivamente. Ya solo necesitamos activarlo todo, primero los mapas y después el propio Postfix:

postmap /etc/postfix/vmailbox
postmap /etc/postfix/virtual
/etc/init.d/postfix/restart

Y ya podemos empezar a recibir correo, para los usuarios definidos, en el nuevo servidor.

miércoles, 21 de octubre de 2020

Aumentando la seguridad

En la entrada anterior apliqué unas reglas de cortafuegos muy monas, y activé fail2ban.

Pero dichas reglas son solamente para IPv4, y hay que repetirlas en IPv6. Pero ojo, que en IPv6 el ICMP no es simplemente un protocolo de apoyo, es parte fundamental de la infraestructura, y si los paqutes no pasan, no hay comunicación (véase RCF 4890):

ip6tables -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p tcp -m tcp --dport ssh -m state --state NEW,ESTABLISHED -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type echo-request -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type echo-replay -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type ttl-exceeded -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type router-solicitation -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type router-advertisement -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbour-solicitation -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbour-advertisement -j ACCEPT
ip6tables -J INPUT DROP
ip6tables -A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -p tcp -m tcp --dport http -j ACCEPT
ip6tables -A OUTPUT -p tcp -m tcp --dport https -j ACCEPT
ip6tables -A OUTPUT -p tcp -m tcp --dport domain -j ACCEPT
ip6tables -A OUTPUT -p udp -m udp --dport domain -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type echo-reply -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type echo-request -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type ttl-exceeded -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type parameter-problem -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type router-solicitation -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type router-advertisement -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbour-solicitation -j ACCEPT
ip6tables -A OUTPUT -p ipv6-icmp -m icmp6 --icmpv6-type neighbour-advertisement -j ACCEPT
ip6tables -J OUTPUT DROP
ip6tables -J FORWARD DROP

Para conseguir que las reglas se mantengan al reiniciar, basta con instalar el paquete iptables-persistent.

Por fail2ban para IPv6 no tenemos que preocuparnos, ya que funciona automáticamente.

La otra cosa pendiente es que no vale de mucho parar a un atacante por unos minutos si después vuelve a las andadas. La configuración por defecto de fail2ban prohíbe la entrada de paquetes durante 10 minutos. Esto para los ataques casuales, pero a los atacantes serios no. Pero subir el tiempo puede dejarle fuera a uno mismo si tiene problemas con la configuración del teclado, por ejemplo.

La solución a este tipo de problemas es prohibir el acceso de nuevo, por más tiempo, a los reincidentes. Siguiendo (un poco) los pasos de The art of web, creamos dos jaulas nuevas, cada una de ellas más dura que la anterior.

En /etc/fail2ban/jail.local añadimos estas líneas:

[sshd-fail2ban-L1]
# 3 veces en 6 horas, 24 horas
enabled = true
port = ssh
filter = sshd-fail2ban-L1
logpath = /var/log/fail2ban.log
maxretry = 3
findtime = 21600
bantime = 86400

[sshd-fail2ban-L2]
# 3 veces en 7 días, 30 días
enabled = true
port = ssh
filter = sshd-fail2ban-L2
logpath = /var/log/fail2ban.log
maxretry = 3
findtime = 604800
bantime = 2592000

A continuación, creamos el fichero /etc/fail2ban/filter.d/sshd-fail2ban-L1.conf con este contenido (ojo al espacio final en ignoreregex) como filtro:

[DEFAULT]
[Definition]
failregex = \[sshd\] Ban <HOST>
ignoreregex =

Y este otro contenido (la misma advertencia) como el fichero /etc/fail2ban/filter.d/sshd-fail2ban-L2.conf como el otro filtro:

[DEFAULT]
[Definition]
failregex = \[sshd-fail2ban-L1\] Ban <HOST>
ignoreregex =

Ahora basta con un par de órdenes y ya tenemos un sistema un poco más robusto:

fail2ban-client add sshd-fail2ban-L1
fail2ban-client start sshd-fail2ban-L1
fail2ban-client add sshd-fail2ban-L2
fail2ban-client start sshd-fail2ban-L2

lunes, 12 de octubre de 2020

Asegurando el servidor

El servidor que migré ayer a Devuan va a estar abierto a Internet, ese lugar tan grande. Así que no puede ser fácilmente accesible.

Lo primero será que no se pueda entrar como root mediante ssh (el acceso por consola debe seguir siendo posible, para un posible rescate). Así que eliminamos esa opción:

PasswordAuthentication no
PermitRootLogin without-password

Claro que antes nos hemos asegurado el acceso mediante clave pública ssh, metiéndola en /root/.ssh/authorized_keys.

El siguiente paso es poner un sistema que nos asegure que los niñatillos que se dedican a probar accesos se queden fuera. Para esto, yo suelo elegir fail2ban. Basta con instalarlo y, muy importante, copiar el fichero /etc/fail2ban/jail.conf como /etc/fail2ban/jail.local.

Inicialmente activaré solo la regla relativa a SSH, ya que es el único servicio que estará activo en la máquina. Para ello, en /etc/fail2ban/jail.local añadiremos

enabled=true

en la sección [sshd] de/etc/fail2ban/jail.local.

Tras ello, las reglas de cortafuegos.

Realmente, ¿qué tráfico necesita tu máquina? Es algo que tienes que pensar dos veces, y luego prohibe todo lo demás.

Los paquetes que correspondan a conexiones de salida ya establecidas, conexiones abiertas por nosotros, hay que permitirlos, o no habrá comunicación:

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

El tráfico de entrada SSH evidentemente hay que permitirlo:

-A INPUT -p tcp --dport ssh -m state --state NEW,ESTABLISHED -j ACCEPT

Y queremos que funcione el ping:

-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT

¿Algo más? De momento no. todo lo demás, prohibido.

-P INPUT DROP

El tráfico de salida también hay que permitirlo, pero solo para lo que lo necesitemos. En primer lugar, el tráfico de salida correspondiente a conexiones de entrada ya establecidas:

-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

En nuestro caso, queremos las actualizaciones de paquetes y otras consultas de ese estilo a sitios web:

-A OUTPUT -p tcp --dport http -j ACCEPT
-A OUTPUT -p tcp --dport https -j ACCEPT

Evidentemente tiene que funcionar el DNS, o no hacemos nada:

-A OUTPUT -p tcp --dport domain -j ACCEPT
-A OUTPUT -p udp --dport domain -j ACCEPT

Y también queremos que funcione el ping:

-A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
-A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT

Todo lo demás, incluso de salida, prohibido.

-P OUTPUT DROP

La tabla FORWARD no la utilizaremos, ya que este sistema no es un enrutador:

-P FORWARD DROP

Con esto nos aseguramos un cortafuegos bastante restrictivo, y sin embargo utilizable.

domingo, 11 de octubre de 2020

Vamo' a migra' un server

Quiero poner un servicio de correo electrónico donde no está. Y eso es una migración. Y lo voy a hacer de on-premises a eso que llaman "la nube". Aunque lo local sea mi servidor casero y la nube sea un servidor contratado con un servicio de hosting.

Así que lo primero es pagar por, e instalar el servidor remoto. De entre las opciones disponible, he elegido Debian 9 (Stretch), porque me será sencillo migrarlo a Devuan.

Así que una vez creado el servidor, de nombre mercurio, el mensajero de los dioses, le instalo lo mínimo que requiero siempre (es decir, vim, htop y aptitude) y a actualizarlo a Devuan.

El primar paso desde Stretch, siguiendo las indicaciones oficiales de Devuan es instalar sysvinit-core y eliminar libpam-systemd, para sentar las bases del nuevo sistema de arranque. Con esto puede que el escritorio no arranque, pero en serio, ¿alguien tiene un escritorio en un servidor recién instalado? (Ejem, SuSE, ejem) En cambio, aseguramos que el sistema sí arranque. Particularmente, en este tipo de operaciones yo siempre las eliminaciones las hago con purge y no con delete.

El siguiente paso sería cambiar las fuentes, pero yo no lo hago directamente. En su lugar creo un fichero /etc/apt/sources.list.d/beowulf que contiene las líneas que hay que añadir. Así queda todo más claro.

Se actualizan las listas de paquetes, se instala el llavero y se actualizan otra vez. Aquí el documento peca de optimista: te la han podido colar con un MITM justo en ese momento. ¡Ah!

Vamos a evitar esto. Primero, vemos e importamos (ojo, si no la importamos será como si nada) la clave maestra de Devuan con

gpg --keyserver keys.gnupg.net --search-keys BB23C00C61FC752C

tal y como indica la página de la distribución. No me creas a mi, comprueba por ti mismo la huella que tienes que importar. Cuando ya la tienes, la puedes ver y jugar con ella, pero lo importante es que puedes usarla para autentificar paquetes:

gpg --export BB23C00C61FC752C | apt-key add -

En este momento ya se puedes realizar el aptitude update, instalar el paquete devuan-keyring, realizar el segundo aptitude update, y seguir. Para ello hay que instalar libtinfo6 y pasar a meternos en harina. Ahí viene el primer paso de la migración real: la paquetería.

aptitude upgrade

"Sí a todo".

Luego hacer el primer reinicio. Ojo, que puedes perder el acceso remoto al servidor en este paso, si no te has asegurado de que todas las tarjetas de red van a levantar, las IPs están bien asignadas, etc. El renombrado de ens* a eth* puede dar problemas.

Tras el reinicio instalamos eudev y hacemos un full-upgrade, para cambiar lo que queda de la paquetería:

aptitude full-upgrade

Con esto vemos desaparecer systemd, y dejar el sistema listo para los siguientes pasos. Finalmente, puedes sustituir libsystemd0 por libelogind0 y ya no quedará rastro.

Lo siguiente que hago es asegurarme de que todos los paquetes que hay instalados tienen una razón de ser: recorro todo aptitude marcando los paquetes como auto, y los que aparecen para borrar porque no son dependencias de otro decido si los quiero o no.