Evaristo R

Técnico Administrador de Sistemas

Piloto de RPAS certificado por AESA/EASA - A1/A3 - A2 - STS

Profesor e-Learning

Evaristo R

Técnico Administrador de Sistemas

Piloto de RPAS certificado por AESA/EASA - A1/A3 - A2 - STS

Profesor e-Learning

Blog Post

Seguridad básica en tu VPS

16 mayo, 2020 Hetzner, Seguridad, VPS
Seguridad básica en tu VPS

Cuando en AWS arrancamos una máquina, por ej. desde una AMI de «Amazon Linux 2» contamos con ciertos elementos de seguridad, como el uso por defecto de SSH keys en lugar de usar una contraseña root. Además de estar detrás de un Firewall.

Hace tiempo que tenía ganas de probar un proveedor Cloud, llamado Hetzner el cual ofrece VPS a precios bajísimos con buenos recursos. El único problema que le veo es que aquí no contamos con los «Grupos de seguridad» y «ACLs» de AWS.

Podemos arrancar una máquina desde un «snapshot» personal, pero la primera vez no tenemos ninguno, por lo que nuestras opciones son elegir una de las siguientes imágenes por defecto.

Sí que es cierto que una vez tenemos el servidor en marcha, podemos montarle una ISO de alguna distro para realizar una instalación desde cero.

En nuestro caso seleccionamos Ubuntu 20.04 con el tamaño de instancia CX11 (2GB RAM, 1vCPU, 20GB SSD, 20TB transferencia por 3,01€)

Durante el aprovisionamiento observamos una advertencia, y es que aunque recomiendan usar una SSH key, nos da la posibilidad de no usarla, enviándonos por e-mail la contraseña root; lo que sería empezar con muy mal pie.

¡No passwords!

Generamos nuestro par de clave pública/privada (Hay varias formas, según el SO en el que estés…openssl, Putty, MobaXterm, etc) 

En el caso de MobaXterm, nos facilita la clave pública con formato OpenSSH. Esta clave la pegaríamos en el apartado «Add an SSH key» de Hetzner. Eso hará que automáticamente nuestra máquina tenga la clave en /root/.ssh/authorized_keys lo que permitirá la conexión.

No te olvides de descargar la clave privada.

En unos segundos se aprovisionará el servidor. Y podremos acceder vía ssh.

ssh -i private_key.ppk root@laipquenosasignaron

Securizar sshd

Lo primero que vamos a hacer son unos ajustes del sshd para mejorar la seguridad de este.

Crear nuevo usuario y desactivar acceso root.

Vamos a crear un usuario con permisos y desactivar el acceso root en cuanto este esté operativo.

Añadimos un usuario

sudo adduser usuario

Crearemos la contraseña por el camino; de no ser así, ejecutamos:

sudo passwd usuario

Añadimos el usuario al sudoers:

sudo visudo
# User privilege specification
root    ALL=(ALL:ALL) ALL 
usuario ALL=(ALL:ALL) ALL

Aprovechando que el usuario root ya tiene la clave pública de mi certificado, y que vamos a deshabilitar el acceso root, voy a migrar esta al nuevo usuario.

mkdir /home/usuario/.ssh
chmod 700/home/usuario/.ssh
cp /root/.ssh/authorized_keys /home/usuario/.ssh/authorized_keys
chmod 600 /home/usuario/.ssh/authorized_keys
chown -R  usuario. /home/usuario/.ssh/

Con esto hemos migrado la clave pública al nuevo usuario, y le hemos dado los permisos correctos. 

¡Importante! Acceder con este nuevo usuario y probar que tenemos efectivamente acceso root, pues en el siguiente paso, desactivaremos el inicio de sesión root.

Desactivamos el inicio de sesión para root

Editamos /etc/ssh/sshd_config

PermitRootLogin no

Configurar tiempo para una desconexión de la sesión SSH

ClientAliveInterval 300
ClientAliveCountMax 1

Habilitar SSH solo para nuestro usuario

AllowUsers usuario

Deshabilitar la autenticación mediante contraseñas

¡Suponiendo que estés usando certificados!

PasswordAuthentication no
PubkeyAuthentication yes

Cambiar puerto de escucha SSH por defecto

El puerto por defecto es el 22, por lo que será el que más sondeen los chinos/rusos bots. 

Aunque si tienes o vas a activar el firewall (como veremos más adelante) quizás te de igual dejar el por defecto. A mi me gusta cambiarlo. [Si lo cambias revisa antes que no tengas el firewall activado, no vaya a ser que te quedes fuera del coche con la llave dentro 🙁 ]

Port 22

Número de intentos fallidos para que el servidor SSH te desconecte.

MaxAuthTries 2

Desactivar funciones no utilizadas

Si sabemos que no vamos a utilizar ciertas funciones, podemos desactivarlas parar restarle oportunidades a los malignos.

AllowTcpForwarding no                   # Disables port forwarding.
X11Forwarding no                        # Disables remote GUI view.
AllowAgentForwarding no                 # Disables the forwarding of the SSH login.
AuthorizedKeysFile .ssh/authorized_keys # The ".ssh/authorized_keys2" file should be removed.

Aplicar los cambios

Verificamos la configuración ssh

sshd -t

Si no se encontraron errores entonces, reiniciamos el servicio sshd

systemctl restart sshd

Si cambiamos el puerto de escucha, acuérdate de esto cuando vuelvas a conectarte.

ssh -p puerto -i private_key.ppk usuario@laipquenosasignaron

Protección contra Ataques de Fuerza Bruta (Fail2Ban)

Fail2ban es una aplicación escrita en Python que ayuda a proteger un servidor de la fuerza bruta y los ataques de denegación de servicios (DDoS).

Analiza los logs y cuando detecta varios intentos fallidos de conexión, penaliza o bloquea a la IP maligna.

Hetzner dispone de un sistema que detecta y bloquea ataques DDOS, pero no viene mal un extra de seguridad.

Instalación de Fail2ban

apt install fail2ban
systemctl enable fail2ban

Configuración de Fail2ban

Fail2Ban viene configurado por defecto para proteger sshd, pero podemos ampliar su utilidad creando ficheros de configuración en este directorio:

/etc/fail2ban/jail.d/

Por ejemplo:

[sshd]
enabled = true
port = 3333
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
[apache]
 enabled = true
 port = http,https
 filter = apache-auth
 logpath = /var/log/apache2/*error.log
 maxretry = 3
 bantime = 600

[apache-overflows]
 enabled = true
 port = http,https
 filter = apache-overflows
 logpath = /var/log/apache2/*error.log
 maxretry = 3
 bantime = 600

[apache-noscript]
 enabled = true
 port = http,https
 filter = apache-noscript
 logpath = /var/log/apache2/*error.log
 maxretry = 3
 bantime = 600

[apache-badbots]
 enabled = true
 port = http,https
 filter = apache-badbots
 logpath = /var/log/apache2/*error.log
 maxretry = 3
 bantime = 600

[http-get-dos]
 enabled = true
 port = http,https
 filter = http-get-dos
 logpath = /var/log/apache2/*access.log
 maxretry = 400
 findtime = 400
 bantime = 200
 action = iptables[name=HTTP, port=http, protocol=tcp]
  • [apache] Bloquea los host remotos con mas de 3 intentos fallidos de autenticación.
  • [apache-overflows] Bloquea host remotos que realicen peticiones de URLs sospechosas.
  • [apache-noscript] Bloquea host remotos que busquen scripts para ejecutar en el sitio web.
  • [apache-badbots] Bloquea host remotos que traten de solicitar bot maliciosos
  • [http-get-dos] Detiene ataques DDOS

Tenemos que crear también la expresión regular en el fichero http-get-dos.conf

nano /etc/fail2ban/filter.d/http-get-dos.conf
[Definition]
# Option: failregex
#  Note: This regex will match any GET entry in your logs, so basically all valid and not valid entries are a match.
#  You should set up in the jail.conf file, the maxretry and findtime carefully in order to avoid false positives.
failregex = ^<HOST> -.*"(GET|POST).*
 
#  Option: ignoreregex
#  Notes.: regex to ignore. If this regex matches, the line is ignored.
#  Values: TEXT

ignoreregex =

Para aplicar los cambios:

systemctl restart fail2ban

Podemos ver el estado de nuestras jaulas con:

fail2ban-client status

Firewall (ufw/iptables)

Aquí no contamos con los “Grupos de seguridad” de AWS, por lo que creo, que es muy necesario configurar un firewall. Yo he utilizado ufw (Uncomplicated Firewall) para editar de manera muy rápida las reglas iptables. A los pocos minutos de tenerlo activado podremos apreciar en /var/log/syslog como bloquea conexiones no deseadas.

Comprobamos su estado actual

ufw status

En mi caso estaba desactivado. Por lo que procedo a configurarlo, y luego activarlo. Queremos permitir el tráfico http y https, el de ssh y el del agente de checkmk, con el que monitorizamos la máquina. Deberías ajustar esto a tus necesidades claro.

ufw allow http
ufw allow https
ufw allow 6556/tcp
ufw allow 22/tcp (o el puerto en el que esté escuchando sshd)

Una vez configurado lo habilitamos y comprobamos de nuevo el estado.

Nos avisará de que podríamos ser desconectados de la sesión, pero esto no pasará si configuramos la regla apropiada para el puerto de escucha ssh.

ufw enable
ufw status

Permitir SSH solo a la IP de mi casa (IP dinámica)

Las reglas para checkmk (6556/tcp) y para la conexión ssh me gustaría que no estuvieran abiertas al mundo, que únicamente estuvieran permitidas a mi IP. Para esto:

  1. Nos agendamos un proveedor de DNS dinámico (DDNS)  gratuito. Yo utilizo https://www.duckdns.org/ De esta manera tendremos una dirección dns que apuntará todo el tiempo a la IP de casa/oficina. En mi caso tengo configurado un cron en la raspberry que se encarga de mantener actualizado el registro de duckdns.
  2. Configuramos otro cron en nuestro VPS que lance un script encargado de verificar que IP responde a la dirección DDNS; para seguidamente modificar las reglas del firewall.

En este hilo encontré el script, que modifique a mi gusto. Concretamente la versión que se encuentra en uno de los últimos comentarios:

/etc/iptables_update.sh

#!/bin/bash
HOSTNAME=YOUR.DNS.NAME.HERE

if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root"
   exit 1
fi

new_ip=$(host $HOSTNAME | head -n1 | cut -f4 -d ' ')
old_ip=$(/usr/sbin/ufw status | grep $HOSTNAME | head -n1 | tr -s ' ' | cut -f3 -d ' ')

if [ "$new_ip" = "$old_ip" ] ; then
    echo IP address has not changed
else
    if [ -n "$old_ip" ] ; then
        /usr/sbin/ufw delete allow from $old_ip to any
    fi
    /usr/sbin/ufw allow from $new_ip to any comment $HOSTNAME
    echo iptables have been updated
fi

Pero modificando la última parte para permitir los puertos que me interesaban, y súper importante, realizando una comprobación previa, para ver si el valor que devuelve «host» es una IP válida.

Esta comprobación la añadí a posteriori, al darme cuenta de que en ocasiones los servidores DDNS de Duckdns fallan y tardan un rato en responder, por lo que no responden, y el resultado de:

host $HOSTNAME | head -n1 | cut -f4 -d ' '

sería «found» o simplemente nada, por lo que, el script original, aunque no podría añadir una nueva regla a iptables con ese valor, pero sí que eliminaría la antigua regla, dejándonos sin acceso hasta que los servidores DDNS de Duckdns volvieran a funcionar.

Con esta comprobación, problema solucionado.

#!/bin/bash
function valid_ip()
{
    local  ip=$1
    local  stat=1

    if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
        OIFS=$IFS
        IFS='.'
        ip=($ip)
        IFS=$OIFS
        [[ ${ip[0]} -le 255 && ${ip[1]} -le 255 \
            && ${ip[2]} -le 255 && ${ip[3]} -le 255 ]]
        stat=$?
    fi
    return $stat
}

HOSTNAME=YOUR.DNS.NAME.HERE
if [[ $EUID -ne 0 ]]; then
   echo "This script must be run as root"
   exit 1
fi

new_ip=$(host $HOSTNAME | head -n1 | cut -f4 -d ' ')
old_ip=$(/usr/sbin/ufw status | grep $HOSTNAME | head -n1 | tr -s ' ' | cut -f3 -d ' ')

if ! valid_ip $new_ip;
then
   exit 1
fi


if [ "$new_ip" = "$old_ip" ] ; then
    exit 0
else
    if [ -n "$old_ip" ] ; then
        /usr/sbin/ufw delete allow from $old_ip to any port 22,6556 proto tcp
    fi
    /usr/sbin/ufw allow from $new_ip to any port 22,6556 proto tcp comment $HOSTNAME
    echo "Se ha modificado iptables por cambio de ip $new_ip"
    exit 0
fi




En el cron de root configuro:

*/5 * * * * /etc/iptables_update.sh

No he redirigido la salida estándar, porque tengo configurado “ssmtp” para que el servidor me avise de ciertos eventos. En este caso me avisaría cada vez que la IP ha cambiado. (Sí…me produce satisfacción conocer cuando me cambia la IP; qué le vamos a hacer) Y menos mal, por que gracias a esto pude darme cuenta rápidamente de los fallos en la resolución del host como comentaba antes.

Habilitar Backups

Muy recomendable activar el servicio de Backups de tu proveedor VPS. En este caso Hetzner nos ofrece copias diarias hasta un máximo de 7, por un 20% de lo que cuesta el plan al mes.

De esta manera si algo saliera mal, o tuviéramos una intrusión en el sistema sería fácil volver atrás en el tiempo y recuperar la máquina.

Monitorizar

Y lo más importante, tanto para poder tomar decisiones como para vigilar posibles amenazas, monitorizar el sistema. En este caso utilizo Checkmk desde la Raspberry.

Conclusión

Con unos sencillos pasos hemos reforzado considerablemente la seguridad de nuestro VPS. En estos momentos podríamos crear un «snapshot» para que las futuras máquinas que lancemos puedan estar basadas en esta.

Fuentes:

Taggs:
1 Comment
  • Ana Rosiris Castro 9:53 pm 16 mayo, 2020 Responder

    El post es súper útil y completo!!! Guaoooooo.

Write a comment

Este sitio web utiliza cookies para que usted tenga la mejor experiencia de usuario. Si continúa navegando está dando su consentimiento para la aceptación de las mencionadas cookies y la aceptación de nuestra política de cookies, pinche el enlace para mayor información. Si está interesado en leer el aviso de privacidad pinche aquí.

ACEPTAR
Aviso de cookies