Port knocking with iptables

Last update: 2010-03-05

For anyone new to firewalls, a firewall creates a barrier for incoming or outgoing network traffic on a computer. More simply, it prevents other people from accessing your computer over the internet and messing with your files.

This article covers Linux and iptables. Other tools and methods are available for port knocking and firewall configuration. These are covered because they are common. iptables may already be installed on most Linux distributions, such as Ubuntu. This article does not cover installation on other distributions; in some cases, a command such as apk add iptables on Alpine Linux is enough.

If you do not know what a computer-network port is, start here, then return to this article. The list of ports can help identify the ports used by services.

Understanding port knocking ​

Firewalls usually define which ports are open or closed, but iptables can do much more, from port forwarding to limiting connections per second.

Because port numbers follow conventions (such as port 22 for SSH), people sometimes move a service to an unrelated port. A simple port scan can still find it, so that does not solve much. Another option is a firewall rule allowing a port only from one IP address, but that is inconvenient with ADSL or 3G connections that constantly change IP addresses.

Port knocking addresses this problem by requiring a connection to one or more ports in a specific sequence before allowing a connection to the port where the service is listening.

Basic iptables configuration ​

If you already have iptables firewall rules, continue to the next section. Otherwise, create a firewall.sh file and make it executable with chmod a+x firewall.sh. Put the rules below in the file; run it whenever you need to reload the firewall. You need root privileges.

Before changing firewall rules, remember that blocking ports may lock you out of the machine (including SSH). Always have a backup plan while testing.

sh
# Limpando todas as regras existentes, isso também irá liberar todas as portas
# Caso faça alguma bobagem e queira remover todas as regras, execute as próximas linhas
iptables -F
iptables -X
iptables -Z
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
# Fim da limpeza

# Por padrão, um firewall deve bloquear tudo, então utilizamos
iptables -P INPUT DROP
iptables -P FORWARD DROP
# Descomente abaixo se deseja bloquear as conexões de saída também
#iptables -P OUTPUT DROP

# Estas regras liberam a conexão local (localhost/127.0.0.1)
iptables -A INPUT -i lo -s 0/0 -d 0/0 -j ACCEPT
iptables -A OUTPUT -o lo -s 0/0 -d 0/0 -j ACCEPT

# Se você quiser liberar todas as conexões para um IP específico descomente
# a linha abaixo. Note que TODAS as portas serão liberadas para o IP!
#iptables -A INPUT -s 000.000.000.000 -j ACCEPT

# Caso tenha alguns serviços que quer liberar para qualquer IP
# Libere as portas 53 UDP/TCP (DNS), 80 TCP (HTTP) e 443 TCP (HTTPS)
iptables -A INPUT -p tcp -m multiport --destination-ports 53,80,443 -j ACCEPT
iptables -A INPUT -p udp -m multiport --destination-ports 53 -j ACCEPT

# Vale também criar uma regra para que não "derrube" conexões já estabelecidas
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Port-knocking rules ​

After running firewall.sh with the rules above, you will find that new SSH connections on port 22 time out, and pings are ignored. The rule that preserves established connections should let your existing SSH session continue.

For this example, port 22 is opened for a few seconds after a TCP connection sequence to ports 1000, 3000, and 2000. Avoid sequential port numbers: a port scan usually follows an order and could accidentally make the connections that open port 22.

Add the following code to the end of your firewall configuration file:

sh
# Obs.: Uma Chain é uma lista de regras de filtragem dentro do iptables
# São 3 passos de conexão, portanto criamos 2 chains
iptables -N AGUARDANDO-PASSO2
iptables -N AGUARDANDO-PASSO3

# De acordo com a chain acessada, define uma flag para demonstrar o passo atingido
iptables -A AGUARDANDO-PASSO2 -m recent --name PASSO2 --set
iptables -A AGUARDANDO-PASSO3 -m recent --name PASSO3 --set

# Para cada porta na ordem 1000 3000 2000 encaminha o pacote para a chain respectiva
iptables -A INPUT -p tcp --syn --dport 1000 -m recent --set --name PASSO1
iptables -A INPUT -p tcp --syn --dport 3000 -m recent --rcheck --seconds 3 \
     --name PASSO1 -j AGUARDANDO-PASSO2
iptables -A INPUT -p tcp --syn --dport 2000 -m recent --rcheck --seconds 3 \
     --name PASSO2 -j AGUARDANDO-PASSO3

# Esta é a regra final, que habilita para todoos os ips (-s 0/0) a conexão na porta
# 22 por 3 segundos
iptables -A INPUT -p tcp -s 0/0 --dport 22 -m recent --rcheck --seconds 3 \
     --name PASSO3 -j ACCEPT

Each flag lasts three seconds, so do not wait too long between connection attempts.

Sending TCP requests to open the port ​

You can use telnet to open the port, but you must make all connections within three seconds of one another. On Linux, nc can help. You can also create a script with the sequence:

sh
nc -w 1 <hostdestino> 1000 # Conecta no host destino e aguarda 1 segundo para desconectar
nc -w 1 <hostdestino> 3000
nc -w 1 <hostdestino> 2000
ssh <usuario>@<hostdestino>