Portknocking para asegurar el servicio SSH

Retomo este blog para publicar, en este caso, la configuración que he puesto en marcha de este sistema de protección de forma que me sea fácil volver a ella para cada servidor que administre. Está basado en la publicada en https://www.digitalocean.com/community/tutorials/how-to-use-port-knocking-to-hide-your-ssh-daemon-from-attackers-on-ubuntu y abundan las entradas por la web de configuraciones similares.
No voy a profundizar en la técnica del portknocking ni en la necesidad de securizar un servidor, de esto también abunda la información en la red. Lo básico que hay que saber es que el puerto del servicio SSH debe estar abierto y público para poder hacer la conexión remotamente y esto hace que sea susceptible a múltiples ataques, principalmente de fuerza bruta.
Existen muchas técnicas de protección y como propone la idea de las capas de la cebolla en esto de la seguridad, lo recomendable es que ésta sea una capa más y se complemente con otras como denyhost, fail2ban, etc...
Éstas técnicas utilizan en su mayoría el firewall integrado en las distintas distribuciones GNU/Linux y más concretamente en Debian que es mi caso por lo que primero vamos a tratar el tema del cortafuegos, pieza imprescindible en la seguridad de nuestros servidores.

 

IPTABLES

Lo ideal según la teoría de la seguridad es configurar el cortafuegos de forma que bloquee todas las conexiones a nuestro servidor por defecto y configurar que acepte aquellos puertos/tráfico que nos interese de forma concreta. En este caso (para este servidor en particular), voy a bloquear únicamente el tráfico SSH, con los comandos:

iptables -I INPUT 1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT iptables -I INPUT 2 -p tcp --dport 22 -j DROP

Explico:

Con el primero me aseguro que las conexiones ya establecidas (por ejemplo la actual si estamos accediendo al servidor por SSH) se mantienen abiertas y con el segundo bloqueo todo el tráfico SSH. En ambos casos no añado las líneas al firewall actual sino que las inserto (-I) en la posición 1 y 2 de la cadena INPUT para que prevalezcan a las reglas actuales del firewall. En mi caso mantengo a continuación las reglas que crea el fail2ban, por ejemplo, de forma que si alguna vez me quedase el puerto abierto aún tendría esta otra línea de defensa.


KNOCKD

Este es el servicio que nos va a permitir abrir el firewall para el SSH bajo petición, se basa en la idea de escuchar las conexiones entrantes y si se produce una secuencia concreta, envía el comando adecuado para abrir la puerta.
Instalamos el servicio (en debian y como root) con:

aptitude install knockd

o el sistema que más os guste para ello (apt-get, etc...) y editamos el fichero de configuración en /etc/knockd.conf que por defecto viene así:


[options] UseSyslog [openSSH] sequence = 7000,8000,9000 seq_timeout = 5 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = syn [closeSSH] sequence = 9000,8000,7000 seq_timeout = 5 command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = syn

Evidentemente por motivos de seguridad no podemos dejar esta configuración, en mi caso he optado por esta otra:

[options] # UseSyslog LogFile = /var/log/knockd.log [SSH] sequence = 666,9663:udp,2355 seq_timeout = 5 command = /sbin/iptables -I INPUT 1 -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = syn cmd_timeout = 30 stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT [OpenSSH] sequence = 789:udp,456:udp,852:udp,951:udp seq_timeout = 5 command = /opt/scripts/ssh_open.sh tcpflags = syn cmd_timeout = 60 stop_command = /opt/scripts/ssh_close.sh

Como es lógico, estos puertos no son los que uso realmente, explico los cambios:
En lugar de usar el fichero de log de sistema (UseSysLog) le especifico un fichero de log independiente. Al menos inicialmente viene bien para comprobar que está funcionando correctamente.
En la secuencia SSH le pongo tres puertos (en este caso dos TCP, la conexión por defecto y uno UDP, 5 segundos de espera máxima entre cada conexión y el comando de apertura, el que se ejecutará si la secuencia es correcta. En este caso, añado al iptables la IP desde la que hago la petición para que le abra el puerto (sólo a esta IP). A los 30 segundos se lanza el otro comando de iptables que borra la regla añadida anteriormente. Con esto no perdemos la conexión establecida gracias a la regla de iptables que habíamos añadido en la sección anterior, de forma que ya no quedará abierto el puerto para esta IP y en cuanto nos desconectemos todo volverá a estar cerradito.
Con esto bastaría pero yo he añadido otra secuencia más en OpenSSH para lanzar un script que abre el SSH del todo y a los 60 segundos lo vuelve a cerrar con otro script. Porqué? Pues en algunos entornos no he sido capaz de que me funcione la primera secuencia, seguramente por limitaciones propias de la red en la que envío la petición (red securizada que puede tener alguna protección para los envíos de paquetes) y lo que he tenido que hacer es recurrir a esta otra secuencia que me permite abrir el puerto 22 desde el móvil y me da 60 segundos para conectarme antes de volver a cerrarlo. Llamemoslo una apertura de emergencia.

Para activar el servicio es imprescindible editar el fichero /etc/default/knockd y modificar la linea:

START_KNOCKD=1

con un 1 en lugar del 0 que viene por defecto, con esto le indicamos al servicio que se inicie con el sistema.

En este fichero también aparece la línea:

#KNOCKD_OPTS="-i eth1"

que en algunos casos debemos descomentar e indicar en qué interfaz ha de escuchar las peticiones. Por ejemplo, en algunos VPS la interfaz es virtual y puede tener una nomenclatura diferente o usar una subinterfaz.

Una vez modificado estos ficheros debemos reiniciar el servicio, por ejemplo:

service knockd restart

y comprobar en el fichero de log que detecta las peticiones, por ejemplo con:

tail -f /var/log/knockd.log

La llamada

Para hacer "la llamada" y activar el puerto 22, solo tenemos que enviar desde la máquina cliente la secuencia configurada anteriormente. Hay diversas formas de hacerlo pero la más sencilla sin duda es usar el propio knockd (habrá que instalarlo también en esta máquina) que permite usar el comando "knock" para hacer las peticiones. En este caso concreto sería:

knock servidor 666 9663:udp 2355 && ssh usuario@servidor

como se puede ver, hay que especificar el nombre del servidor (o la IP) y luego la secuencia de puertos (por defecto TCP y se puede especificar si hay alguno UDP como en el segundo del ejemplo). En este caso para ahorrar tiempo le añado a continuación la conexión por ssh, si todo va bien, ya permitirá conectarse.

El resultado en el fichero de log del servidor sería algo parecido a esto:

[2016-03-18 12:55] 51.127.41.17: SSH: Stage 1 [2016-03-18 12:55] 51.127.41.17: SSH: Stage 2 [2016-03-18 12:55] 51.127.41.17: SSH: Stage 3 [2016-03-18 12:55] 51.127.41.17: SSH: OPEN SESAME [2016-03-18 12:55] SSH: running command: /sbin/iptables -I INPUT 1 -s 51.127.41.17 -p tcp --dport 22 -j ACCEPT [2016-03-18 12:56] 51.127.41.17: SSH: command timeout [2016-03-18 12:56] SSH: running command: /sbin/iptables -D INPUT -s 51.127.41.17 -p tcp --dport 22 -j ACCEPT

Hay multiples aplicaciones que permiten hacer el port-knocking, yo he probado varias en Android y funcionan bien, espero que os sirva de algo.

Comentarios