12 KiB
Gekata — это легковесный сервис на Node.js для извлечения «связных доменов» с веб-страниц, запускаемый в контейнере Debian с Chromium; он сначала делает предзапросы с ручным следованием редиректам, затем при необходимости поднимает безголовый браузер, ограничивает глубину редиректов и кэширует результаты в SQLite через better-sqlite3. Сервис предоставляет HTTP API /domains, возвращающее финальный URL, цепочку редиректов и список связанных доменов, а также /health для проверки готовности.
Назначение
Gekata сканирует заданный домен, разрешает маркетинговые и другие редиректы до целевой HTML-страницы, загружает её в безголовом Chromium и собирает множество доменных имён из всех сетевых запросов страницы, формируя список «связных доменов» для анализа интеграций, трекинга и CDN. Такой подход работает и для динамических сайтов с клиентским рендерингом.
Архитектура
- Веб-сервер на Express предоставляет REST‑маршруты, принимает домен, валидирует его и инициирует сканирование, обрабатывая таймауты и коды ошибок.
- Предпроверка делает GET с ручным управлением редиректами, классифицируя сценарии: форс‑редиректы «маркетинга», запреты 403, скачивания и не‑HTML контент, чтобы экономить запуск браузера.
- Эскалация в безголовый Chromium (через Playwright) выполняет навигацию, применяя ограничение глубины редиректов только для документных переходов и ожидая «тихое окно» сети для стабильного сбора доменов.
- Кэширование результатов в SQLite с TTL ускоряет повторные запросы; используется лучшее для продакшна подключение better-sqlite3 и WAL‑журналирование для устойчивости.
Потоки данных
- Вход: GET /domains?domain= — принимает хост или URL, нормализует до ASCII/Punycode и формирует стартовый https:// URL.
- Предобработка: ручной обход 3xx с ограничением шагов; детекция «похожих на файл» ссылок и контента non‑HTML; маркетинговый редирект помечается и может быть целевой.
- Сканирование браузером: навигация на целевой URL, слежение за запросами/ответами страницы, сбор доменов из всех сетевых событий, исключая шум (google/doubleclick по эвристике), построение цепочки редиректов для документной навигации.
- Выход: JSON с finalUrl, relatedDomains[], redirectChain[], статусами ok/skipped/blocked и служебными пометками (cached, ttl).
API
- GET /domains
Параметры: domain — доменное имя или URL.
Ответ 200 ok:
- domain: нормализованный запрошенный домен.
- finalUrl: конечный URL после редиректов/навигации.
- relatedDomains: уникальные домены, замеченные при загрузке страницы.
- redirectChain: массив { from, to, status } для документных 3xx.
- cached: true/false, cachedAt, ttlAt.
- status: ok | skipped | blocked; дополнительные note/reason при skip/blocked.
- GET /health — простой JSON { ok: true } для readiness/liveness.
Обработка редиректов
- На этапе предпроверки ограничение PRECHECK_MAX_REDIRECTS предотвращает бесконечные цепочки до запуска браузера; 403 заставляет эскалировать в браузер, non‑HTML/attachment возвращают немедленный ответ.
- В браузере включён маршрут‑ограничитель только для документной навигации: запросы навигации обрабатываются с maxRedirects, ассеты идут без ограничений, чтобы не ломать рендеринг.
- Если лимит превышен, навигация завершается контролируемо и возвращается ошибка «Too many redirects», переводимая в понятный статус ответа API.
Кэш и TTL
- SQLite таблица domain_cache хранит: домен, JSON списка доменов, финальный URL, цепочку редиректов, время обновления и ttl_at.
- Повторные обращения до истечения TTL возвращают сохранённый результат без запуска браузера, снижая задержки и нагрузку.
Контейнеризация
- Образ состоит из двух стадий: builder и runtime, обе на debian:bookworm-slim.
- Стадия builder устанавливает Node.js, компилятор и заголовки SQLite для сборки native‑модуля better‑sqlite3, затем выполняет npm ci с пропуском dev‑зависимостей и копирует исходники.
- Стадия runtime устанавливает tini как корректный PID 1, Node.js runtime, системный Chromium и минимальный набор X/GTK/NSS/GBM/шрифтов, необходимых для безголового режима; копируются node_modules и исходники из builder.
- Создаётся непривилегированный пользователь nodeuser; директория приложения принадлежит ему; сервис запускается не от root.
Переменные окружения
- PORT — порт HTTP сервера (по умолчанию 3000).
- CHROMIUM_PATH — путь к системному Chromium (/usr/bin/chromium в контейнере).
- CACHE_TTL_SECONDS — срок жизни кэша (по умолчанию 6 часов).
- HARD_TIMEOUT_MS — жёсткий таймаут обработки HTTP‑запроса (по умолчанию 70 секунд).
- MAX_REDIRECT_STEPS — максимальная глубина редиректов для документной навигации (по умолчанию 20).
- NAV_TIMEOUT_MS, QUIET_WINDOW_MS — таймауты навигации и «тихого окна» сети.
- DEBUG — включает подробные логи страницы/сетевых событий при значении 1.
Безопасность и устойчивость
- tini как init обрабатывает сигналы и «зомби» процессы; контейнер корректно завершает Chromium по SIGTERM/SIGINT, предотвращая утечки.
- Запуск под непривилегированным пользователем снижает риск компрометации; Chromium стартует с флагами no‑sandbox/disable‑setuid-sandbox, что совместимо с безпривилегированным окружением контейнеров.
- Ограничение редиректов для документных переходов устраняет зацикливание «маркетинговых» и неверных конфигураций, не влияя на загрузку ассетов.
Производительность
- npm ci в builder‑стадии плюс копирование package*.json до исходников задействуют кэш слоёв Docker, ускоряя сборки.
- better‑sqlite3 с синхронными подготовленными выражениями обеспечивает быстрый локальный кэш без отдельного сервиса БД.
- Предпроверка HTTP избавляет от лишних подъёмов браузера для не‑HTML или «прикреплённых» ответов.
Сборка и запуск
- Сборка образа:
- docker build -t gekata:latest .
- Запуск контейнера:
- docker run --rm -p 3000:3000 -e CACHE_TTL_SECONDS=21600 -e MAX_REDIRECT_STEPS=20 gekata:latest
- Примеры запросов:
Журналирование и диагностика
- Лог‑метки [BOOT], [HTTP], [SCAN], [BROWSER], [CACHE], [SIGNAL] позволяют быстро локализовать этап и тип события.
- При включённом DEBUG=1 логируются консоль страницы, ошибки, неудавшиеся запросы и сетевые эвенты, что помогает анализировать блокировки, CORS, антибот‑защиту и таймауты.
Ограничения
- Сайты с жёсткими антибот‑мерами (403/JS‑челленджи) могут быть помечены как blocked или потребовать дополнительной эмуляции (например, иные user‑agent/locale/timezone/proxy).
- Сбор связанных доменов базируется на фактически выполненных сетевых запросах и может меняться при A/B тестах, гео‑таргетинге или различиях по user‑agent.
Расширения и доработки
- Добавить белый/чёрный список доменов, тонкую фильтрацию трекеров и интеграций.
- Вынести кэш в внешний SQLite‑файл через volume для сохранения между рестартами, настроить резервное копирование.
- Параметризовать user‑agent/locale/timezone и добавить поддержку прокси для региональных сценариев.
- Экспортировать полный сетевой журнал и тайминги (HAR‑подобный формат) как опциональную выгрузку.
Файлы проекта
- server.js — основной сервис, логика API, предобработка, сканирование браузером, кэш, ограничения редиректов, завершение по сигналам.
- Dockerfile — двухстадийная сборка, системный Chromium в рантайме, tini, непривилегированный пользователь, переменные окружения и запуск службы.