Files
knock-gui/desktop/LOCAL_KNOCKING.md

14 KiB
Raw Blame History

Локальное простукивание портов (Local Port Knocking)

Обзор

Функционал локального простукивания позволяет выполнять knock операции напрямую через Node.js API без использования внешнего HTTP API сервера. Это обеспечивает независимость от внешних сервисов и возможность работы в автономном режиме.

Условия активации

Локальное простукивание активируется автоматически когда:

  1. API URL пуст - поле apiBase не заполнено или содержит пустую строку
  2. API URL = "internal" - значение apiBase установлено в "internal"
  3. API URL не задан - значение apiBase равно null или undefined

Архитектура

Файлы реализации

1. src/main/main.js - Основная логика

Строки 210-367: Реализация локального простукивания

Ключевые функции:

  • parseTarget(targetStr) - парсинг строки цели в объект
  • parseDelay(delayStr) - конвертация задержки в миллисекунды
  • knockTcp(host, port, timeout) - TCP простукивание
  • knockUdp(host, port, timeout) - UDP простукивание
  • performLocalKnock(targets, delay, verbose) - основная функция простукивания
  • ipcMain.handle('knock:local', ...) - IPC обработчик

Поддерживаемые протоколы:

  • TCP - создает соединение и немедленно закрывает
  • UDP - отправляет пакет данных (fire-and-forget)

Формат целей:

protocol:host:port[:gateway]

Примеры:

  • tcp:127.0.0.1:22
  • udp:192.168.1.1:53
  • tcp:example.com:80:gateway.com

Поддержка Gateway:

Gateway можно указать двумя способами:

  1. В строке цели: tcp:host:port:gateway_ip
  2. Глобально через поле Gateway: используется для всех целей, если не указан в самой цели

Приоритет gateway:

  • Gateway из строки цели имеет приоритет над глобальным
  • Если gateway не указан, используется системный маршрут по умолчанию

Обход VPN/туннелей:

Gateway использует localAddress для принудительного направления трафика через указанный локальный IP-адрес. Это позволяет:

  • Обходить VPN соединения (WireGuard, OpenVPN и др.)
  • Использовать конкретный сетевой интерфейс
  • Направлять трафик через локальный шлюз

Пример обхода WireGuard:

{
  "gateway": "192.168.89.1"
}

Трафик будет направлен через интерфейс с IP 192.168.89.1, минуя WireGuard туннель.

Хелпер для gateway (Rust приоритетно, Go как fallback)

Когда задан gateway (IP или имя интерфейса), десктоп-приложение запускает встроенный бинарь из desktop/bin/:

  • knock-local-rust — приоритетный Rust-хелпер (если присутствует)
  • knock-local — Go-хелпер как запасной вариант

Оба на Linux используют SO_BINDTODEVICE для привязки к интерфейсу и надежного обхода VPN/туннелей (WireGuard и пр.).

Сборка при разработке:

  • npm run rust:build — соберёт Rust-хелпер
  • npm run go:build — соберёт Go-хелпер

В прод-сборках оба бинаря автоматически включаются в образ приложения.

Важно для TCP: привязка интерфейса устанавливается до connect(). Это гарантирует, что исходящее соединение пойдёт через нужный интерфейс, а не в туннель.

Формат задержки:

  • 1s - 1 секунда
  • 500ms - 500 миллисекунд (не поддерживается, используйте 0.5s)
  • 2m - 2 минуты
  • 1h - 1 час

2. src/preload/preload.js - IPC мост

Строка 13: Добавлен метод localKnock

localKnock: async (payload) => ipcRenderer.invoke('knock:local', payload)

3. src/renderer/renderer.js - UI логика

Строки 317-376: Логика выбора между локальным и API простукиванием

Ключевые изменения:

  • Проверка условия useLocalKnock = !apiBase || apiBase.trim() === '' || apiBase === 'internal'
  • Извлечение targets из всех режимов (inline, form, yaml)
  • Вызов window.api.localKnock() вместо HTTP запросов

Режимы работы

1. Inline режим

// Извлекает targets из поля #targets
targets = qsi("#targets").value.split(';').filter(t => t.trim());

2. Form режим

// Сериализует формы в строку targets
targets = [serializeFormTargetsToInline()];

3. YAML режим

// Парсит YAML и извлекает targets
const config = yaml.load(yamlContent);
targets = config.targets.map(t => {
  const protocol = t.protocol || 'tcp';
  const host = t.host || '127.0.0.1';
  const ports = t.ports || [t.port] || [22];
  return ports.map(port => `${protocol}:${host}:${port}`);
}).flat();

API локального простукивания

Входные параметры

{
  targets: string[],     // Массив целей в формате "protocol:host:port[:gateway]"
  delay: string,         // Задержка между целями (например "1s")
  verbose: boolean,      // Подробный вывод в консоль
  gateway: string        // Глобальный gateway для всех целей (опционально)
}

Выходные данные

{
  success: boolean,      // Успешность операции
  results: [            // Детальные результаты по каждой цели
    {
      target: string,    // Исходная строка цели
      success: boolean,  // Успешность простукивания
      message: string    // Сообщение о результате
    }
  ],
  summary: {            // Общая статистика
    total: number,      // Общее количество целей
    successful: number, // Количество успешных
    failed: number      // Количество неудачных
  }
}

Примеры использования

Настройка для локального режима

Вариант 1: Пустой API URL

{
  "apiBase": ""
}

Вариант 2: Специальное значение

{
  "apiBase": "internal"
}

Пример конфигурации

{
  "apiBase": "internal",
  "gateway": "192.168.1.1",
  "inlineTargets": "tcp:127.0.0.1:22;tcp:192.168.1.100:80",
  "delay": "2s"
}

Пример YAML конфигурации

targets:
  - protocol: tcp
    host: 127.0.0.1
    ports: [22, 80]
  - protocol: udp
    host: 192.168.1.1
    ports: [53]
delay: 1s

Логирование и отладка

Консольный вывод

При verbose: true в консоли main процесса появляются сообщения:

Knocking TCP 127.0.0.1:22
Knocking UDP 192.168.1.1:53 via 192.168.1.1
Knocking TCP example.com:80 via 10.0.0.1

Результаты в DevTools

Детальные результаты логируются в консоль renderer процесса:

console.log('Local knock results:', result.results);

Статус в UI

В интерфейсе отображается краткий статус:

"Локальное простукивание завершено: 2/3 успешно"

Ограничения

Поддерживаемые протоколы

  • TCP - полная поддержка
  • UDP - отправка пакетов
  • ICMP - не поддерживается
  • Другие протоколы - не поддерживаются

Таймауты

  • TCP: 5 секунд по умолчанию
  • UDP: 5 секунд по умолчанию
  • Настраивается в коде функций knockTcp и knockUdp

Сетевая безопасность

  • Локальное простукивание использует системные сокеты
  • Подчиняется правилам файрвола операционной системы
  • Не требует дополнительных разрешений в Electron

Совместимость

Операционные системы

  • Windows - полная поддержка
  • macOS - полная поддержка
  • Linux - полная поддержка

Electron версии

  • v28+ - протестировано
  • ⚠️ v27 и ниже - может потребовать адаптации

Переключение между режимами

API → Локальный

  1. Открыть настройки (Ctrl/Cmd+,)
  2. Установить apiBase в "internal"
  3. Сохранить настройки
  4. Перезапустить приложение

Локальный → API

  1. Открыть настройки
  2. Установить корректный apiBase URL
  3. Сохранить настройки
  4. Перезапустить приложение

Устранение неполадок

Проблема: "No targets provided"

Причина: Не удалось извлечь цели из конфигурации Решение: Проверить корректность заполнения полей targets

Проблема: "Unsupported protocol"

Причина: Использован неподдерживаемый протокол Решение: Использовать только tcp или udp

Проблема: "Connection timeout"

Причина: Цель недоступна или заблокирована файрволом Решение: Проверить доступность цели и настройки файрвола

Проблема: "Invalid target format"

Причина: Неверный формат строки цели Решение: Использовать формат protocol:host:port

Проблема: "Uncaught Exception"

Причина: Необработанные ошибки в асинхронных операциях Решение: ИСПРАВЛЕНО - Добавлены глобальные обработчики ошибок и защита от двойного resolve

Исправления в версии 1.1:

  • Добавлен флаг resolved в TCP/UDP функциях для предотвращения двойного вызова resolve
  • Глобальные обработчики uncaughtException и unhandledRejection в main процессе
  • Глобальные обработчики ошибок в renderer процессе
  • Улучшенная валидация входных данных в IPC обработчике
  • Try-catch блоки вокруг всех критических операций

Безопасность

Ограничения доступа

  • Локальное простукивание выполняется с правами пользователя приложения
  • Не требует root/administrator прав
  • Подчиняется системным ограничениям сетевого доступа

Логирование

  • Результаты простукивания логируются в консоль
  • Не сохраняются в файлы по умолчанию
  • Можно отключить через параметр verbose: false

Разработка и расширение

Добавление новых протоколов

  1. Создать функцию knockProtocol() в src/main/main.js
  2. Добавить обработку в performLocalKnock()
  3. Обновить документацию

Настройка таймаутов

// В src/main/main.js
function knockTcp(host, port, timeout = 10000) { // 10 секунд
  // ...
}

Добавление дополнительных опций

// Расширить payload в IPC
{
  targets: string[],
  delay: string,
  verbose: boolean,
  timeout: number,    // новый параметр
  retries: number     // новый параметр
}

Пример обхода WireGuard

Проблема

WireGuard активен, весь трафик идет через туннель, но нужно простучать порт через локальный шлюз.

Решение

{
  "apiBase": "internal",
  "gateway": "192.168.89.1",
  "inlineTargets": "tcp:external-server.com:22",
  "delay": "1s"
}

Логи

Using localAddress 192.168.89.1 to bypass VPN/tunnel
Knocking TCP external-server.com:22 via 192.168.89.1
TCP connection to external-server.com:22 via 192.168.89.1 successful

Версия документации: 1.2
Дата создания: 2024
Дата обновления: 2024 (поддержка обхода VPN)
Совместимость: Electron Desktop App v1.0+