port-knock.py
Клиент для port knocking — отправляет последовательность TCP SYN-пакетов на закрытые порты сервера, после чего сервер (через knockd или аналог) открывает доступ к SSH. Удобно для защиты SSH на VPS.
Зависимости
- Python 3.9+
- На сервере:
knockdили аналогичный демон
Использование
# Стук в порты 7000, 8000, 9000 python3 port-knock.py example.com 7000 8000 9000 # С указанием задержки между стуками (мс) python3 port-knock.py example.com 7000 8000 9000 --delay 200 # С автоматическим подключением по SSH после python3 port-knock.py example.com 7000 8000 9000 --ssh user
Код
#!/usr/bin/env python3
"""port-knock.py — клиент для port knocking."""
import socket
import argparse
import time
import os
import sys
def knock(host: str, port: int, timeout: float = 0.5) -> None:
"""Отправляет TCP SYN на указанный порт."""
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(timeout)
s.connect_ex((host, port))
s.close()
except (socket.timeout, OSError):
pass # Ожидаемо — порт закрыт
def main():
parser = argparse.ArgumentParser(description="Port knocking клиент")
parser.add_argument("host", help="Адрес сервера")
parser.add_argument("ports", nargs="+", type=int, help="Последовательность портов")
parser.add_argument("--delay", type=int, default=300,
help="Задержка между стуками в мс (по умолчанию 300)")
parser.add_argument("--ssh", metavar="USER",
help="Подключиться по SSH после knock")
args = parser.parse_args()
print(f"Knock: {args.host} -> {', '.join(map(str, args.ports))}")
for i, port in enumerate(args.ports):
knock(args.host, port)
print(f" → :{port} ✓")
if i < len(args.ports) - 1:
time.sleep(args.delay / 1000)
print("Последовательность отправлена.")
if args.ssh:
time.sleep(0.5)
print(f"Подключаюсь: ssh {args.ssh}@{args.host}")
os.execlp("ssh", "ssh", f"{args.ssh}@{args.host}")
if __name__ == "__main__":
main()