Port knocking para añadir una segunda capa de protección a SSH

Una vez configurado el servicio SSH de nuestra Raspberry Pi con dos factores de autenticación, vamos a añadir una segunda capa de protección: port knocking o golpeo de puertos, que no es más que un mecanismo para abrir puertos externamente si se recibe cierta conexión o conexiones a puertos cerrados. Digamos que es cómo un santo y seña para mostrar una "puerta oculta"...

Tradicionalmente se suele hacer port knocking con knockd o incluso sólo con iptables mediante la recepción de una secuencia preestablecida de intentos de conexión a distintos puertos cerrados. Sin embargo, a veces hay errores en la recepción de la secuencia de puertos y sobretodo el sistema es vulnerable a ataques MiTM que pueden poner en evidencia las carencias de esta técnica de seguridad por oscuridad, por lo que siempre ha de utilizarse esta técnica con una medida adicional de seguridad, nunca cómo la única.

También he de decir que las últimas herramientas de port knocking como fwknop ya implementan el uso de un Paquete Único de Autorización cifrado (incluso con clave asimétrica) que mejoran la seguridad en este aspecto, si bien necesitarás normalmente instalar y configurar el cliente de una forma menos convencional.
Por esa razón para nuestra pequeña Raspberry nos conformaremos simplemente con instalar knockd y usar una simple secuencia de tres puertos que, si lo piensas bien, tampoco es poca cosa: si por ejemplo utilizamos los puertos 1000, 2000, 3000, un atacante tendría que probar cada combinación de tres puertos en un rango de 65535 posibles, por lo que necesitaría  una media de 9 trillones de paquetes para poder obtener un único puerto abierto. Así de sencillo pero efectivo ;)

Lo primero que haremos será cambiar el puerto por defecto del servicio SSH:
[root@pirobot1 ~]# vi /etc/ssh/sshd_config
#Port 22
Port 2224
[root@pirobot1 ~]# systemctl restart sshd

Posteriormente configuraremos el firewall con algunas reglas básicas. En mi caso crearé la regla TRAFFIC y permitiré el acceso desde la LAN así como ICMP y HTTP/HTTPS:
[root@pirobot1 ~]# vi /etc/iptables/iptables.rules
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
:TRAFFIC - [0:0]

-A INPUT -j TRAFFIC
-A FORWARD -j TRAFFIC

# Accepted connections by default
-A TRAFFIC -i lo -j ACCEPT
-A TRAFFIC -s 192.168.1.0/24 -j ACCEPT
-A TRAFFIC -p icmp --icmp-type any -j ACCEPT
-A TRAFFIC -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
-A TRAFFIC -m conntrack --ctstate NEW -m tcp -p tcp --dport 80 -j ACCEPT
-A TRAFFIC -m conntrack --ctstate NEW -m tcp -p tcp --dport 443 -j ACCEPT

# Any other traffic is dropped
-A TRAFFIC -j DROP
COMMIT

[root@pirobot1 ~]# systemctl start iptables.service
[root@pirobot1 ~]# systemctl status iptables.service
iptables.service - Packet Filtering Framework
          Loaded: loaded (/usr/lib/systemd/system/iptables.service; disabled)
          Active: active (exited) since Mon 2013-02-25 23:42:42 UTC; 1s ago
         Process: 269 ExecStart=/usr/sbin/iptables-restore /etc/iptables/iptables.rules (code=exited, status=0/SUCCESS)

Feb 25 23:42:42 pirobot1 systemd[1]: Starting Packet Filtering Framework...
Feb 25 23:42:42 pirobot1 systemd[1]: Started Packet Filtering Framework.
[root@pirobot1 ~]# systemctl enable iptables.service
ln -s '/usr/lib/systemd/system/iptables.service' '/etc/systemd/system/multi-user.target.wants/iptables.service'
[root@pirobot1 ~]#

Después instalaremos el paquete knockd:
[root@pirobot1 ~]# pacman -S knockd
resolving dependencies...
looking for inter-conflicts...

Targets (1): knockd-0.5-10

Total Download Size:    0.02 MiB
Total Installed Size:   0.11 MiB

Proceed with installation? [Y/n]
:: Retrieving packages from aur...
 knockd-0.5-10-armv6h                                                        16.7 KiB   115K/s 00:00 [############################################################] 100%
(1/1) checking package integrity                                                                     [############################################################] 100%
(1/1) loading package files                                                                          [############################################################] 100%
(1/1) checking for file conflicts                                                                    [############################################################] 100%
(1/1) checking available disk space                                                                  [############################################################] 100%
(1/1) installing knockd                                                                              [############################################################] 100%
[root@pirobot1 ~]#

Ahora configuraremos knockd con la secuencia deseada y el comando a ejecutar que, normalmente, es añadir o quitar una regla iptables para la IP que está llevando a cabo la secuencia de conexiones aunque también podríamos por ejemplo parar o iniciar el demonio sshd en caso de éxito.
[root@pirobot1 ~]# vi /etc/knockd.conf
[options]
        logfile = /var/log/knockd.log

[openSSH]
        sequence    = 7000,8000,9000
        seq_timeout = 10
        command     = /usr/sbin/iptables -I TRAFFIC -s %IP% -p tcp --dport 2224 -j ACCEPT
        tcpflags    = syn

[closeSSH]
        sequence    = 9000,8000,7000
        seq_timeout = 10
        command     = /usr/sbin/iptables -D TRAFFIC -s %IP% -p tcp --dport 2224 -j ACCEPT
        tcpflags    = syn
       
Ahora activamos y reinicamos knockd:
[root@pirobot1 ~]# systemctl start knockd
[root@pirobot1 ~]# systemctl enable knockd
ln -s '/usr/lib/systemd/system/knockd.service' '/etc/systemd/system/multi-user.target.wants/knockd.service'

Y comprobamos el port knocking, ¡ÁBRETE SÉSAMO!:
C:\Users\vmotos>nmap -sT -p7000,8000,9000 host.miraspberry.com

Starting Nmap 5.51 ( http://nmap.org ) at 2013-02-25 23:15 Hora estßndar romance

Nmap scan report for host.miraspberry.com
Host is up (0.33s latency).
PORT     STATE  SERVICE
7000/tcp closed afs3-fileserver
8000/tcp closed http-alt
9000/tcp closed cslistener
MAC Address: B8:27:EB:B0:D5:EB (Unknown)

Nmap done: 1 IP address (1 host up) scanned in 5.71 seconds

[root@pirobot1 ~]# tail -f /var/log/knockd.log

[2013-02-25 23:10] 192.168.1.34: closeSSH: Stage 1
[2013-02-25 23:10] 192.168.1.34: closeSSH: Stage 2
[2013-02-25 23:10] 192.168.1.34: closeSSH: Stage 1
[2013-02-25 23:10] 192.168.1.34: closeSSH: Stage 2
[2013-02-25 23:10] 192.168.1.34: closeSSH: Stage 3
[2013-02-25 23:10] 192.168.1.34: closeSSH: OPEN SESAME
[2013-02-25 23:10] closeSSH: running command: /usr/sbin/iptables -I TRAFFIC -s X.X.X.134 -p tcp --dport 2224 -j ACCEPT

************ Cambiar IP por ip pública
[root@pirobot1 ~]# iptables -L
Chain INPUT (policy DROP)
target     prot opt source               destination
TRAFFIC    all  --  anywhere             anywhere

Chain FORWARD (policy DROP)
target     prot opt source               destination
TRAFFIC    all  --  anywhere             anywhere

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain TRAFFIC (2 references)
target     prot opt source               destination
ACCEPT     tcp  --  X.X.X.134             anywhere             tcp dpt:efi-mg
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  192.168.1.0/24       anywhere
ACCEPT     icmp --  anywhere             anywhere             icmp any
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ES                                                                                        TABLISHED
ACCEPT     tcp  --  anywhere             anywhere             ctstate NEW tcp dp                                                                                        t:http
ACCEPT     tcp  --  anywhere             anywhere             ctstate NEW tcp dp                                                                                        t:https
DROP       all  --  anywhere             anywhere

Comentarios