Configurar Port Knocking para securizar el servicio SSH
Port Knocking (traducido al español como «golpeo de puertos») es una de las técnicas que se puede utilizar para securizar un servidor Linux. Su funcionamiento es muy sencillo y permitiría que solo personas autorizadas accedan a un determinado puerto.
Junto al uso de un servicio de cortafuegos por software, su principal función es evitar que los puertos queden expuestos, tanto a personas como a bots o escáneres automatizados de puertos.
El usuario llama a una serie de puertos en un orden, por ejemplo al 2023, 2000, 9999 y el servicio de knockd habilitaría una regla en el firewall del sistema para permitir a la dirección IP solicitante el acceso al puerto o los puertos que hayamos configurado.
Si te interesa esta funcionalidad, sigue leyendo hasta el final porque puede ser una de las guías más completas de Port Knocking con knockd.
Índice de contenido
Instalar y configurar knock en Linux
En primer lugar, instalamos el demonio knockd en nuestro servidor. En este caso, la instalación se está realizando sobre Ubuntu 20.04 y la versión del paquete es la 0.8.
Para instalarlo, ejecutamos: sudo apt install knockd
Knock tiene dos ficheros de configuración: /etc/default/knockd y /etc/knockd.conf, los explico a continuación.
Fichero /etc/default/knockd
El primero a editar será /etc/default/knockd donde estableceremos el parámetro START_KNOCKD con valor a 1 para habilitar el servicio y cambiaremos el nombre de la interfaz en el parámetro KNOCKD_OPTS por ens3, quedando de la siguiente manera:
1 2 3 4 5 6 7 8 |
# control if we start knockd at init or not # 1 = start # anything else = don't start # PLEASE EDIT /etc/knockd.conf BEFORE ENABLING START_KNOCKD=1 # command line options KNOCKD_OPTS="-i ens3" |
El nombre de la interfaz de escucha podemos extraerlo con ifconfig
, pudiendo así especificar qué tarjeta de red funcionará bajo esta técnica.
Fichero /etc/knockd.conf
Es el fichero donde se definen todas las reglas o secuencias de golpeo de puertos. Por defecto, vienen configuradas las dos secuencias que estamos interesados en configurar: apertura del puerto SSH y cierre del puerto SSH.
En primer lugar, en el apartado de [options] cambiaremos UseSyslog por logfile = /var/log/knockd.log para que sea más fácil depurar y auditar el funcionamiento de knock.
Luego, tanto en el apartado [openSSH] como en el [closeSSH] especificaremos en sequence los puertos por orden. Es recomendable que los puertos no sean consecutivos, ya que normalmente los escaneos de puertos realizados por bots se realizan de manera incremental.
El parámetro seq_timeout define el tiempo de espera máximo (expresado en segundos) para que se complete la secuencia. También se recomienda que este tiempo no sea muy alto, para evitar que ante un escaneo de puerto masivos se complete la secuencia.
Tras la personalización, el fichero queda configurado de la siguiente manera:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[options] logfile = /var/log/knockd.log [openSSH] sequence = 2023,2000,9999 seq_timeout = 5 command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = syn [closeSSH] sequence = 9999,2000,2023 seq_timeout = 5 command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 22 -j ACCEPT tcpflags = syn |
Recuerda reiniciar el servicio de knockd tras los cambios en los ficheros: /etc/init.d/knockd restart
Configurar knockd con UFW (especialmente Ubuntu)
En servidores Ubuntu, por defecto, el firewall activo es UFW (Uncomplicated Firewall). Este gestiona las reglas de iptables a través de una sintaxis más simple.
Si queremos que las reglas creadas por knock sean gestionadas con UFW, basta con cambiar el parámetro command. Esto daría como resultado el siguiente fichero:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[options] logfile = /var/log/knockd.log [openSSH] sequence = 2023,2000,9999 seq_timeout = 5 command = ufw allow from %IP% to any port 22 comment "Added by knockd" tcpflags = syn [closeSSH] sequence = 9999,2000,2023 seq_timeout = 5 command = ufw delete allow from %IP% to any port 22 tcpflags = syn |
Conexión desde un cliente y depuración
Aunque podemos realizar el Port Knocking mediante telnet, lo recomendable es utilizar el cliente knock (apt install knock
) por su sencillez. En nuestro ejemplo de uso, el comando para abrir el puerto 22 sería:
knock -d 300 servidor.evaristogz.com 2023 2000 9999
Como peculiaridad, el delay (parámetro -d) se expresa en milisegundos, mientras que en el fichero de configuración lo expresamos en segundos. Puedes usar -v para ver el modo verbose.
Crear una función bash para facilitarnos el acceso
En total debemos ejecutar tres comandos: el knock que crea la regla de acceso al puerto, el comando de conexión mediante SSH a ese servidor y el knock que ejecuta la secuencia closeSSH y elimina la regla de acceso.
Por ello, una buena opción para facilitarnos el acceso es crear una función bash y añadirla en nuestro .bashrc del equipo cliente:
function portk { knock -d 300 $2 2023 2000 9999; ssh -o TCPKeepAlive=yes -o ServerAliveInterval=15 $1@"$2"; knock -d 300 $2 9999 2000 2023; }
Posterior a añadir esta función, cargamos los cambios del fichero con source .bashrc
.
Su uso sería el siguiente: portk NombreUsuarioSSH servidor.con.port.knocking.para.SSH
Depuración del funcionamiento de knock
En caso de que nos encontremos con problemas, podemos consultar el log /var/log/knockd.log.
En él ha de aparecer nuestra dirección IP, la secuencia que se está intentando llevar a cabo y un stage por cada puerto que tenemos configurado en sequence. Si el resultado es correcto, veremos «OPEN SESAME» y el comando ejecutado. Un ejemplo de caso de éxito sería el siguiente:
También podemos revisar el syslog para ver si UFW o iptables está realizando algún bloqueo: tail -f /var/log/syslog | grep NuestraDirecciónIP
Ejemplos de otros casos de uso de knockd
En linux.die.net encontraremos otros tres ejemplos para aplicar Port Knocking en nuestros servidores. Sin embargo, extrapolando un poco más el uso de Port Knocking, se me ocurren otros usos «locos».
Estos pueden ser el abrir un puerto de administración o inseguro durante un determinado tiempo, reiniciar un servicio o ejecutar un script.
Está claro que no es para lo que está pensado, pero puede ser una ñapa solución para entornos de desarrollo, técnicos de nivel 1 sin acceso a servidores, redes internas… De la misma manera, es una forma de mostrar otras funcionalidades o configuraciones que permite knockd.
Una de las limitaciones que tiene knockd es que no permite diferenciar secuencias según la interfaz de red, por lo que no podríamos habilitar una secuencia para, por ejemplo, permitir abrir el puerto SSH únicamente por la interfaz de red interna.
Apertura temporal del puerto 80
1 2 3 4 5 6 7 8 9 10 |
[options] logfile = /var/log/knockd.log [open80] sequence = 0001,0022,0333 seq_timeout = 5 start_command = /sbin/iptables -A INPUT -s %IP% -p tcp --dport 80 -j ACCEPT cmd_timeout = 900 stop_command = /sbin/iptables -D INPUT -s %IP% -p tcp --dport 80 -j ACCEPT tcpflags = syn |
start_command se usa para para ejecutar un comando tras la secuencia, que sería abrir el puerto 80 mediante iptables. cmd_timeout es el tiempo definido (expresado en segundos y que en este caso corresponde a 15 minutos) tras el que se ejecutará stop_command, que elimina la regla de iptables y nos garantiza que el puerto no se queda expuesto.
Reinicio de un servicio o ejecución de un script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
[options] logfile = /var/log/knockd.log [restartApache] sequence = 1111,6666,0000 seq_timeout = 5 command = /etc/init.d/apache2 restart tcpflags = syn [runScript] one_time_sequences = /etc/knockd/runScript_sequences seq_timeout = 5 command = /var/scripts/deploy_and_destroy.sh tcpflags = syn |
En este ejemplo se muestra la combinación de dos secuencias distintas: una para reiniciar el servicio apache2 y otra para ejecutar un script bash con las instrucciones que deseemos. En el caso de la secuencia runScript, esta solo se podrá ejecutar una vez debido al parámetro one_time_sequences.
Referencias
Hola, he estado repasando este software y veo que esta totalmente desfasado con bugs y problemas de seguridad segun los issues de github. Estoy buscando otra alternativa pero no veo nada. La estoy buscando porque no me funciona, se me queda en stage1 he probado con curl, nc, etc pero nada
Hola J.D! Gracias por comentar.
Es cierto, acabo de ver las issues del repositorio. Imagino que has probado incluso copiando las configuraciones de este post, ¿no?
Si quieres, escríbeme un correo o por Telegram con más info e intento ayudarte.
Saludos!