Publi

Consejos para endurecer un servidor SSH y hacerlo más seguro

openssh
Hoy toca hablar de OpenSSH, y es que la semana pasada se descubrió una vulnerabilidad grave que podría comprometer las claves privadas de los usuarios que se conectan a un servidor. Y como son claves privadas, nadie debería conocerlas, sólo nosotros. Así que, hoy es un buen día para compartir algunas medidas que podemos tomar para endurecer nuestros servidores y hacer nuestro SSH más seguro.

Actualizado el 22/11/2016 con técnicas avanzadas para detectar y mitigar vulnerabilidades en algoritmos de intercambio de claves, autentificación y cifrado.

En el post, muchas veces hablaré de incluir cierta línea en los archivos de configuración, aunque normalmente, muchas distribuciones traen un archivo de configuración por defecto, en ese caso, debemos asegurarnos de que la línea está y que el valor es correcto (a veces tendremos que cambiar un yes por un no, o un número, etc).

No utilices passwords clásicos

Evita hacer la conexión a partir de contraseñas de toda la vida, esas cadenas de texto que tienen que aprenderse los usuarios para poder entrar en el servidor. Es muy recomendable utilizar pares de claves pública y privada (el servidor debe conocer las claves públicas de los usuarios que tienen acceso).

Como usuarios, en el ordenador cliente, debemos ejecutar:

$ ssh-keygen -b 4096

Esto nos creará dos archivos ~/.ssh/id_rsa y ~/.ssh/id_rsa.pub (podemos cambiar su nombre, el comando anterior nos preguntará si queremos almacenar las claves en otro archivo). El primer archivo será la clave privada y el segundo la clave pública. Podemos abrir los archivos con cualquier editor de texto sin problema, pero no modificarlos.

Seguidamente, dentro del servidor, debemos añadir el contenido del archivo id_rsa.pub al final del archivo ~/.ssh/authorized_keys
A partir de ahora, debemos tener el archivo id_rsa en nuestro ordenador siempre que queramos acceder a dicho servidor, este archivo será nuestra contraseña.
Puedes ver más información aquí.

Configuración del archivo de claves

Nuestro archivo de claves públicas (puede haber varias claves públicas para un mismo usuario, ya que puede que vayamos a acceder a él desde varios sistemas), por defecto está en ~/.ssh/authorized_keys
pero tal vez queramos especificar otro archivo, y puede que no esté en una ruta cómoda para el usuario. Podemos meterlo en /etc/ si lo deseamos, por ejemplo, o dentro de /var/. Podemos utilizar como palabras clave (%h que nos devuelve el $HOME del usuario y %u que nos devuelve el nombre de usuario) para formar la ruta de acceso al archivo de claves públicas autorizadas.
Para cambiarlo, debemos añadir a /etc/ssh/sshd_config lo siguiente:

AuthorizedKeysFile /var/claves/%u/authorized_keys

Si aún así prefieres seguir con passwords planos…

Vamos a configurar un poco mejor /etc/ssh/sshd_config y añadir unas cuantas cosas interesantes para passwords planos.
Es muy recomendable no permitir contraseñas vacías, para ello:

PermitEmptyPasswords no

Limitar el tiempo que le damos al usuario para enviarnos la contraseña (con un par de minutos sobra, aunque podemos limitarlo más):

LoginGraceTime 120

Limitar el número de contraseñas fallidas, así ralentizamos los ataques de fuerza bruta:

MaxAuthTries 3

Denegar acceso al usuario root

Es muy importante denegar el acceso al usuario root si nuestro servidor tiene conexión a Internet. Si es un ordenador de casa no es plan de ponernos paranoicos, siempre pueden entrar en nuestra wifi y ponerse a lanzar ataques a los equipos, pero no es tan probable.
Ya contaré la anécdota completa un día, pero un usuario malintencionado puede entrar, y de hecho entra, y se pone a probar contraseñas típicas.
Para ello, debemos entrar en /etc/ssh/sshd_config e incluir la línea:

PermitRootLogin no

Denegar autentificación por Password

Al principio hablamos de utilizar pares de claves pública y privada como contraseña, está bien, pero si el servidor sigue aceptando autentificación con password plano, podemos ser víctimas de un ataque y podrán probar contraseñas para intentar entrar.
Para ello en /etc/ssh/sshd_config debemos añadir:

PasswordAuthentication no

Separación de privilegios

Suele venir activada por defecto en instalaciones modernas. Se trata de que el proceso del OpenSSH crea una conexión con un proceso que no tiene permiso para hacer demasiado, sólo atender al tráfico entrante y abrir conexiones, pero cuando un usuario se identifica con éxito, se cambiará al usuario que acaba de entrar y funcionará de forma normal. Así es mucho más difícil que un ataque a OpenSSH durante el proceso de identificación pueda ser dañino si tiene éxito.
Para ello añadir en /etc/ssh/sshd_config:

UsePrivilegeSeparation yes

Denegamos conexiones concurrentes sin autentificación

Puede que un atacante intente establecer varias conexiones a la vez en nuestro servidor para probar autentificaciones distintas, aunque podemos pararle los pies añadiendo lo siguiente a /etc/ssh/sshd_config:

MaxStartups 2

Así sólo podrá hacer dos conexiones a la vez. Podemos limitarlo más, pero a mí me gusta establecer varias conexiones a la vez para algunas tareas.

Cambiar el puerto

El puerto estándar del servicio SSH es el 22. Y por ello, es un puerto que se va a probar ante cualquier escaneo sencillo. Es común que algún usuario intente ver qué puertos tenemos abiertos, y muchas veces no se prueban todos los puertos posibles, sino los más típicos. Entonces, ¿por qué no dificultamos un poco encontrar este servicio? Podemos poner un puerto como el 222, o el 1234 o 5432. Para ello en /etc/ssh/sshd_config debemos añadir:

Port 28

Aunque este tema tiene mucha controversia. Si lo hacemos en un puerto no privilegiado (<1024) y descubren la manera de tirar el servidor, se podría iniciar otro servidor desde un usuario cualquiera lo que podría hacer que se interceptaran todas las conexiones y alguien interceptara información de los usuarios, (podría haber contraseñas ahí), sería complicado pero posible. Y no tiene que estar alguien dentro para poder hacerlo, puede haber un sistema automatizado que se encargue de tirar el SSH actual, lanzar su SSH pirata y luego enviar la información a su dueño aprovechando para autodestruirse.

Mantén tu servidor actualizado

Es muy recomendable hacerlo, aunque a veces no es posible. Puede que si actualizamos algo en nuestro servidor se rompa otra cosa, aunque debemos procurar que sea posible actualizarlo. Esto es importante porque cada versión de openSSH utiliza nuevos algoritmos más seguros para la identificación e intercambio de mensajes. En este caso tanto el cliente como el servidor deben estar actualizados ya que siempre que un cliente se conecta se establece una negociación con los algoritmos que tanto uno como otro saben utilizar.
Sería malo que una de las dos partes no estuviera actualizada. Por tanto, actualiza tu cliente y tu servidor.

Limitar los dispositivos de escucha

En el caso en el que nuestro servidor tenga varios dispositivos de red (cable, inalámbrico, acceso telefónico, VPN, etc), podemos hacer que sólo sea posible la escucha desde uno o varios dispositivos. Por ejemplo, podemos hacer que sólo esté abierto el servicio SSH por VPN (suele ser el dispositivo tun0
), o por eth0 (muchos VPS suelen tener varios dispositivos de red). Para ello podemos añadir en /etc/ssh/sshd_config lo siguiente:

ListenAddress 192.168.1.6

Debemos incluir la dirección IP de nuestro servidor.

Expulsar a usuarios inactivos

No sabemos si los usuarios que se conectan al servidor están en su casa, o en un sitio público (o con acceso físico de otros usuarios), por otro lado, mantener usuarios inactivos es un gasto innecesario de recursos de nuestro servidor. Por lo que podemos expulsarlos cuando lleven 10 minutos (600 segundos) sin hacer nada añadiendo en /etc/ssh/sshd_config lo siguiente:

ClientAliveInterval 600
ClientAliveCountMax 0

Los servidores SSH enviarán señales para mantener vivos a los clientes (es decir, que no se desconecten), en este caso se enviará una señal cada ClientAliveInterval, y le estamos diciendo que el número de señales que enviaremos por cliente será 0, por lo que se desconectarán automáticamente.

Advertencia disuasoria

Podemos poner un mensaje antes de que un usuario se conecte, algo así como que los accesos no autorizados serán denunciados, que se está monitorizando la IP de los accesos o algo así. Intenta que el mensaje sea creíble y duro a la vez. Ese mensaje lo grabas en un archivo de texto, por ejemplo /etc/disuasorio y lo añadimos a /etc/ssh/sshd_config:

Banner /etc/disuasorio

Eso sí, no incluyas la versión de OpenSSH, ni el sistema operativo que usas, ni tu uptime, ni ninguna de esas cosas chulas que a los frikis nos hacen ilusión porque esa información puede ser utilizada en nuestra contra.

Autentificación basada en host

¿A que suena feo? Que alguien se pueda autentificar en un sistema en función del host en el que esté. Como si alguien no pudiera suplantar a alguien físicamente, o incluso dentro de una red. Sólo debe estar activo a no ser que realmente sepamos lo que estamos haciendo o, por supuesto, para hacer experimentos. Por lo tanto, desactivemos la lectura de los ficheros de usuario (suelen venir desactivados ya por defecto, pero no está de más comprobarlo por si alguien ha metido mano) ~/.rhosts y ~/.shosts (contienen listados de usuarios y hosts):

IgnoreRhosts yes
HostBasedAuthentication no

Ocultar versión del sistema operativo (para Debian)

La versión de openSSH no se puede ocultar. Es por temas de compatibilidad, y es que los clientes utilizan este dato para saber cómo tienen que dirigirse a los servidores en materia de seguridad. Es triste y seguro que nos dicen alguna vez que intentemos ocultarla, pero no es barato. Para ocultar la versión de OpenSSH del servidor tendremos que editar el código fuente y cambiar la firma del servidor. Es más, para ver la versión no tenemos que estar identificados ni nada, con un simple:

ncat servidor.com 22
SSH-2.0-OpenSSH_6.0p1 Debian-4+deb7u2

Podemos sacar la versión. Si queremos algo más de información podemos hacer un:
nmap otroservidor.com 298
Starting Nmap 7.01 ( https://nmap.org ) at 2016-11-21 20:55 CET
Nmap scan report for otroservidor.com (xx.xx.xxx.xxx)
Host is up (0.053s latency).
rDNS record for xx.xx.xxx.xxx: ip-xx-xx-xxx-xxx.otroservidor.com
PORT      STATE SERVICE VERSION
18765/tcp open  ssh     OpenSSH 6.7p1 Debian 5+deb8u3 (protocol 2.0)
| ssh-hostkey:
|   2048 90:e0:66:4f:f5:24:30:b4:72:bc:4b:67:90:88:da:43 (RSA)
|_  256 14:0a:5d:63:ee:52:89:3c:37:41:46:e1:04:c5:4f:f2 (ECDSA)
<ins class="adsbygoogle"
style="display:block"
data-ad-client="ca-pub-1713542931034615"
data-ad-slot="2358673881"
data-ad-format="auto">
(adsbygoogle = window.adsbygoogle || []).push({});
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 2.54 seconds

¡ Hasta cambiando la el puerto nos podemos dar cuenta !

Eso sí, lo que queda un poco mal es eso de Debian 5+deb8u3 porque estamos dando más información de la cuenta, información que no es necesaria en ningún caso.

Para eso, editamos /etc/ssh/sshd_config y añadimos:

DebianBanner no

Usuarios y grupos permitidos

Podemos especificar qué usuarios vamos a permitir entrar en nuestro servidor, ya que puede que haya usuarios que no nos interesa que entren libremente, o un antiguo empleado cuyo usuario sigue siendo útil, pero no queremos que pueda entrar ya. Para ello, podemos utilizar en /etc/ssh/sshd_config:

AllowUsers alberto bruno carlos ernesto

o si es más fácil decir el grupo:

AllowGroups admins developers

En este caso, incluso podemos incluir el host desde el que se debe identificar un usuario. Por ejemplo, si el usuario diego, sólo queremos que se conecte desde nuestra red local (10.1.1.0/24), podemos incluir:

AllowUsers alberto bruno carlos ernesto diego@10.1.1.0/24

O también poner la IP de su puesto de trabajo:

AllowUsers alberto bruno carlos ernesto diego@10.1.1.94

Envío de datos del servidor gráfico

Si nuestra máquina es un servidor, y no tiene instaladas herramientas gráficas o, si las tiene instaladas, tampoco nos interesa utilizar la versión gráfica de las herramientas por SSH ya que la velocidad de la conexión no será muy buena o porque muchas veces se instalan solas cuando metemos un paquete y no las vamos a utilizar. El caso es cerrar el servidor lo más posible para evitar fallos, y por eso, es buena idea introducir estas líneas en /etc/ssh/sshd_config:

X11Forwarding no

Esto es buena idea porque muchas veces ejecutamos comandos que disponen de versión gráfica, el programa detecta que tenemos una sesión X (la de nuestro ordenador local, porque tenemos activada la redirección), el programa tardará un poco más en arrancar, eso sí, tendremos la ventana gráfica en nuestro escritorio.

Limitar conexiones entrantes al puerto de SSH

Puede que un usuario malintencionado nos quiera aplicar la fuerza bruta para averiguar nuestras claves. Para ello, intentará establecer automáticamente múltiples conexiones a nuestro servidor probando una clave distinta cada vez hasta que acierte. Podemos, con un pequeño script de iptables limitar las conexiones por minuto que se establecen al puerto 22 (o 5432 si lo hemos cambiado):

1
2
3
4
5
6
#!/bin/bash
IPTABLES=`which iptables` # Podemos reemplazarlo por la ruta y nombre del ejecutable de iptables
iface=eth1
port=22
$IPTABLES -I INPUT -p tcp --dport ${port} -i ${iface} -m state --state NEW -m recent  --set
$IPTABLES -I INPUT -p tcp --dport ${port} -i ${iface} -m state --state NEW -m recent  --update --seconds 60 --hitcount 5 -j DROP

O si preferimos usar ufw, podemos limitar a 6 conexiones cada 30 segundos simplemente utilizando:

# ufw limit ssh

o

# ufw limit 5432

Descubrir y desactivar algoritmos vulnerables

Utilizar la herramienta ssh-audit para verificar las últimas recomendaciones sobre vulnerabilidades en los algoritmos utilizados por openSSH para el intercambio de claves y mensajes entre cliente y servidor. Esta aplicación leerá el banner de SSH para obtener la versión además de reunir información sobre la identificación y los algoritmos utilizados para autentificación, cifrado, intercambio de claves, etc.
Este programa, a su vez, genera un informe con los algoritmos que han sido declarados como débiles, información obtenida de fallos, vulnerabilidades y CVEs que nos ayudarán a hacer nuestro SSH más seguro. Sólo debemos hacer lo siguiente:

./ssh-audit.py [mi_servidor.com]

Y nos dará un informe parecido a este:
ssh_audit
Ahora viene algo de trabajo de campo. Tenemos que mirar los algoritmos en cada una de las categorías y dejar sólo los mejores (a veces es tarea difícil), pero, por ejemplo vemos que en la imagen son todos malos (rojos y naranjas), podríamos dejar sólo los que están en naranja, aunque mejor dejar los que están en verde. Para eso, según la categoría estableceremos una serie de algoritmos que nuestro servidor permitirá en la sesión SSH y puede que clientes muy antiguos no se puedan conectar.
Para establecer dicha restricción tenemos que editar /etc/ssh/sshd_conf y añadir una línea por cada categoría con algoritmos permitidos separados por comas, como por ejemplo:

KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com

Como vemos para los algoritmos de intercambio de claves (Key Exchange o kex, para los amigos), utilizaremos KexAlgorithms, para los MAC (Message Authentication Code), utilizaremos MACs y para los de cifrado, Ciphers.

Por otro lado, los Host Key Algorithms los podemos ir eliminando en las líneas que comienzan por HostKey, en las que podemos comentar las líneas que nos estorben.

La configuración que he puesto justo arriba es válida para servidores con versiones de openSSH 6.7+ aunque con ssh-audit podremos crear configuraciones para la versión que necesitemos.

No permitir reenvío de tráfico TCP

Es una recomendación de Akamai Threat Advisory. Aunque openSSH no declara que esto sea inseguro, puede ser utilizado en nuestra contra. Incluso un usuario con permisos en dicha máquina podría interceptar información de identificación en una máquina a la que conectemos a través de un túnel.
No es crítico, pero si no lo vamos a usar, debemos poner en /etc/ssh/sshd_config:

AllowTcpForwarding no

Nivel de información

Aunque no se recomienda un LogLevel DEBUG porque puede violar la privacidad de nuestros usuarios, podemos poner un LogLevel VERBOSE, para tener un informe más completo de todos los usuarios y sistemas que se identifican aquí. Todo esto va, como siempre en /etc/ssh/ssdh_config

Usar Latch en nuestro servidor

Latch es una aplicación que nos permite activar o desactivar nuestros credenciales en diferentes servicios. Funciona a través de una aplicación para móvil y podemos permitir o denegar nuestra autentificación en webs o servidores. Trae muchos plugins oficiales y posibilidad para crear plugins por parte de terceros.
En definitiva, es una forma de permitir que nos podamos identificar en nuestro servidor SSH o denegarlo. Podemos permitir la identificación cuando estemos delante de nuestro ordenador y denegarla cuando no, por ejemplo, o denegarla cuando sospechemos que alguien puede tener nuestra clave.
Para eso podemos encontrar guías aquí o aquí. Necesitamos tiempo paciencia y ganas.

Otras ideas interesantes

Hay muchas más cosas por hacer, por ejemplo, monitorizar el uso que se hace del servidor con auditd.

Port knocking

Otras técnicas interesantes son el port knocking, que es como llamar a una puerta con una cierta contraseña. Tú le dices a quien está dentro: «llamaré 3 veces rápidamente, luego una pausa y luego 2 veces lentamente», mientras no escuches eso, no abras a nadie. Lo mismo puedes hacer en un servidor de Internet, originalmente tienes los puertos cerrados en un firewall, pero tras una secuencia de intentos de conexión a ciertos puertos, el firewall te da acceso al puerto que te interesa, y lo podemos hacer para las conexiones SSH. Podemos hacerlo con reglas de iptables o con knockd (que también usará iptables al final, pero es mucho más amigable y tiene más opciones). Cómo configurar port knocking en nuestro servidor.

Otros

Incluso podemos hacer que un usuario determinado (a través de su clave pública) sólo sea capaz de ejecutar un comando. Normalmente se ejecuta el comando que le da acceso a shell, pero tal vez queremos restringir algo más el permiso de ejecución que le damos a un usuario. Para ello, en el archivo de authorized_keys podemos anteponer a cada clave ‘command=»comando»‘ y será el único comando que se ejecute en la sesión.

Variables de entorno

En versiones de openSSH anteriores a la 6.6, no se gestiona bien el envío de variables de entorno. En concreto, si miras /etc/ssh/sshd_config y ves una línea que dice:

AcceptEnv LANG LC_*

Esto quiere decir que el cliente te puede enviar las variables LANG y todas las que empiecen por «LC_«, aunque por cómo está implementada la gestión de ese asterisco, un cliente podría saltarse esa restricción y esto no sería bueno.
Lo primero sería actualizar nuestro openSSH, ya que las versiones nuevas no tienen ese problema, pero si nos vemos obligados a utilizar una versión antigua podríamos quitar esa línea y poner lo siguiente:

AcceptEnv LANG LC_TYPE LC_NUMBER LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION

Así quitamos el asterisco. También podríamos incluir alguna variable más que sea útil para nuestra organización

Extra: comprobar que hemos hecho las cosas bien

Es importante, cuando hemos terminado de configurar nuestro servidor, saber que hemos puesto bien toda la configuración. De hecho, cuando reiniciemos el demonio ssh, tendremos que pararlo y arrancarlo pero, ¿y si la configuración nueva tiene algún fallo? SSH no arrancará, ¿y si tenemos algún problema de red? Ya no podremos entrar nunca más. En VPS como DigitalOcean que te permite iniciar una sesión web en un terminal para recuperar el servicio, puede que tengamos que perder muchas horas de trabajo para recuperar el acceso. Por eso, más vale perder un par de segundos ahora que lamentarnos después.

sudo /usr/sbin/sshd -t
/etc/ssh/sshd_config: line 52: Bad configuration option: sPasswordAuthentication
/etc/ssh/sshd_config: terminating, 1 bad configuration options
# Corregimos el problema y…
sudo /usr/sbin/sshd -t
sudo service sshd restart

Actualización 14/07/2016 : UsePrivilegeSeparation, mejor a yes, X11Forwarding y sshd -t
Actualización 22/11/2016 : Detección de algoritmos vulnerables y desactivación de los mismos, nivel de información y desactivación del reenvío (TcpForwarding) y algunos consejos generales más.
Actualización 27/08/2017 : Enlace al post sobre port knocking

También podría interesarte....

Leave a Reply