Port knocking
Реализация технологии port knicking с помощью одного iptables с модулем recent
iptables -N reset_knock # Цепочка для сброса процесса стука iptables -A reset_knock -m recent --name PHASE1 --remove iptables -A reset_knock -m recent --name PHASE2 --remove iptables -A reset_knock -m recent --name PHASE3 --remove iptables -A reset_knock -m recent --name PHASE4 --remove iptables -N in_phase_2 # Создаем цепочку для фазы 2 iptables -A in_phase_2 -m recent --name PHASE1 --remove # Удаляем запись из списка первой фазы iptables -A in_phase_2 -m recent --name PHASE2 --set # Добавляем ее в список второй фазы iptables -N in_phase_3 # Создаем цепочку для фазы 3 iptables -A in_phase_3 -m recent --name PHASE2 --remove # Удаляем запись из списка второй фазы iptables -A in_phase_3 -m recent --name PHASE3 --set # Добавляем ее в список третьей фазы iptables -N in_phase_4 # Создаем цепочку для фазы 4 iptables -A in_phase_4 -m recent --name PHASE3 --remove # Удаляем запись из списка третьей фазы iptables -A in_phase_4 -m recent --name PHASE4 --set # Добавляем ее в список четвертой фазы iptables -N checked # Для записей, прошедших проверку iptables -A checked -j reset_knock # Очищаем списки iptables -A checked -j ACCEPT # Разрешаем пакет iptables -F INPUT # Очищаем цепочку INPUT iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT # Разрешаем пакеты по установленным соединениям # Первая фаза iptables -A INPUT -p tcp --dport 21210 -m recent --name PHASE1 --set -j RETURN # Для тех, кто присутствует в списке первой фазы — переход во вторую iptables -A INPUT -p tcp --dport 11992 -m recent --rcheck --name PHASE1 --seconds 5 -g in_phase_2 # И т.д. iptables -A INPUT -p tcp --dport 16043 -m recent --rcheck --name PHASE2 --seconds 5 -g in_phase_3 iptables -A INPUT -p tcp --dport 23050 -m recent --rcheck --name PHASE3 --seconds 5 -g in_phase_4 # Если стучатся не в том порядке — сброс iptables -A INPUT -p tcp -m multiport --dport 21210,11992,16043,23050 -j reset_knock # Для тех, кто прошел все четыре фазы — разрешаем доступ iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m recent --rcheck --name PHASE4 --seconds 5 -j checked iptables -P INPUT DROP # Дефолтное правило цепочки INPUT
Принцип прост — при стуке в порт, соответствующий очередной фазе, проверяется наличие записи в предыдущей фазе. Теперь порт 22 откроется на 5 секунд после стука в порты 21210, 11992, 16043, 23050 со строгим соблюдением порядка перечисления и интервалами не более 5 секунд.
Обратите внимание на технику реализации процесса — цепочки фаз вызываются не через -j, а через -g, поэтому после прохождения этих цепочек к пакетам сразу применяется правило по умолчанию цепочки INPUT, то есть DROP. В противном случае пакеты доходили бы до правила сброса (которое с multiport), и процесс стука все время сбрасывался бы.
Последовательный стук в порты можно организовать, например, утилитой netcat
for it in {21210,11992,16043,23050}; do echo " " | nc -w 1 host $it done ssh user@host
Или на powershell
# Remove old readonly constants from session Remove-Variable -Name KNOCK_DESTINATION -Force -ErrorAction SilentlyContinue Remove-Variable -Name KNOCK_VALID_TYPES -Force -ErrorAction SilentlyContinue Remove-Variable -Name KNOCK_PORTS -Force -ErrorAction SilentlyContinue Remove-Variable -Name KNOCK_EXE_TARGET -Force -ErrorAction SilentlyContinue # === SCRIPT CONFIGURATION === Set-Variable KNOCK_DESTINATION -Option ReadOnly -Value "1.1.1.1" Set-Variable KNOCK_VALID_TYPES -Option ReadOnly -Value ("TCP", "UDP") Set-Variable KNOCK_PORTS -Option ReadOnly -Value ((1, "TCP"), (2, "TCP"), (3, "TCP"), (4, "UDP"), (5, "UDP")) Set-Variable KNOCK_EXE_TARGET -Option ReadOnly -Value "mstsc /v:$KNOCK_DESTINATION /prompt" # === END OF SCRIPT CONFIGURATION === # Knock all configured ports in the correct order $KNOCK_PORTS | foreach { $knockPort = $_[0] $knockType = $_[1] # Make sure that no invalid knock type was specified if ( -Not $KNOCK_VALID_TYPES.Contains($knockType) ) { Write-Error "Invalid knock type specified: $knockType" Exit(1) } else { Write-Host "Knocking $knockType port $knockPort..." # Execute the port knock, either TCP or UDP switch($knockType) { "TCP" { $tcpClient = New-Object System.Net.Sockets.TcpClient $tcpClient.BeginConnect($KNOCK_DESTINATION, $knockPort, $null, $null) | Out-Null $tcpClient.Close() | Out-Null } "UDP" { $udpClient = New-Object System.Net.Sockets.UdpClient $udpClient.Connect($KNOCK_DESTINATION, $knockPort) | Out-Null $udpClient.Send([byte[]](0), 1) | Out-Null $udpClient.Close() | Out-Null } } # Wait a second to make sure that our firewall gets the packets in the right order sleep 1 } } # Start the configured service Write-Host "Open sesame!" #Write-Host "Executing target command...: $KNOCK_EXE_TARGET" #Invoke-Expression -Command $KNOCK_EXE_TARGET</pre?