← Блог

Безперервний pipeline: від .onion-форуму до класифікованої загрози за 5 секунд

500 Tor-нод, HAProxy load balancer, DuckDB, Claude Haiku — і безперервний цикл, який збирає заголовки, витягує тіла повідомлень та класифікує загрози без пауз. 17 000+ постів оброблені автоматично.

OSINT · 2026-04-12T12:00:00


Як ми побудували систему, яка без зупинок краулить даркнет-форуми через 500 Tor-нод, збирає повні тексти повідомлень і класифікує їх за допомогою LLM -- з нульовою затримкою між етапами.

Чому "кожну годину" -- недостатньо

У попередній статті ми розповідали, як побудували моніторинг заголовків даркнет-форумів. Тоді система працювала за розкладом: кожну годину краулер збирав нові заголовки, потім класифікатор обробляв backlog.

Проблеми стали очевидними:

  • Затримка: між появою поста і його класифікацією -- від 1 до 2 годин
  • Заголовки -- не все: тред із заголовком "Fresh batch -- 500k" може бути чим завгодно. Без тіла повідомлення класифікація неточна
  • Backlog: якщо класифікатор не встигав обробити всі нові пости, черга зростала
  • Ми перебудували pipeline з нуля: замість cron-подібних інтервалів -- безперервний цикл, який працює без пауз поки є робота.


    Архітектура: три стадії в одному потоці

    ~~~ CONTINUOUS PIPELINE THREAD

    ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ 1. Forum │───>│ 2. Body │───>│ 3. LLM │ │ Monitor │ │ Fetcher │ │ Classifier │ │ │ │ │ │ │ │ ~3 хв │ │ ~3 сек │ │ ~1.5 сек │ │ 500 Tor-нод │ │ 450 паралел. │ │ Claude Haiku │ └──────────────┘ └──────────────┘ └──────────────────┘

    <-- Цикл повторюється НЕГАЙНО якщо є робота --> <-- Пауза 10 сек тільки якщо нічого нового --> ~~~

    Ключове рішення: всі три стадії працюють послідовно в одному потоці. Не на таймерах, не на cron. Потік виконує цикл:

    1. Forum Monitor -- обходить всі .onion-форуми, збирає нові заголовки тредів 2. Body Fetcher -- для кожного нового треду завантажує повний текст повідомлення 3. Classifier -- класифікує нові пости через Claude Haiku (AWS Bedrock) 4. Якщо була робота -- негайно наступний цикл. Якщо ні -- пауза 10 секунд.

    ~~~python def _continuous_pipeline(): while True: did_work = False

    # 1. Краулимо всі форуми forum_monitor.run() did_work = True

    # 2. Забираємо тіла поки є unfetched while get_unfetched_thread_urls(limit=1): forum_body_fetcher.run() did_work = True

    # 3. Класифікуємо поки є backlog while get_forum_stats()["unclassified"] > 0: forum_classifier.run() did_work = True

    if not did_work: time.sleep(10) ~~~


    Стадія 1: Forum Monitor -- 500 Tor-нод через HAProxy

    Tor Fleet

    Ми запускаємо 500 окремих Tor-контейнерів. Кожен -- це ізольований Tor-процес зі своїм circuit, своїм exit node і своєю IP-адресою.

    Чому 500, а не 1?

  • Швидкість: один Tor-нод обробляє ~2-3 запити на секунду. 500 нод дають ~1000-1500 req/s
  • Reliability: якщо один circuit зламався -- 499 інших працюють
  • Anti-ban: кожен запит йде з різної IP-адреси. Форум бачить 500 різних "користувачів"
  • Rotation: кожні 10 хвилин всі 500 нод отримують команду NEWNYM і змінюють circuits
  • HAProxy Load Balancer

    Всі 500 Tor SOCKS5-проксі балансуються через HAProxy:

    ~~~ Frontend (10.77.0.1:9100) └── 500 Tor SOCKS5 backends ├── tor-1:9050 ├── tor-2:9050 ├── ... └── tor-500:9050 ~~~

    Для краулера це виглядає як один проксі -- socks5://10.77.0.1:9100. HAProxy розподіляє запити по 500 нодам з round-robin балансуванням.

    Health-check моніторить кожну ноду і автоматично виключає ті, що впали.

    Що краулить Monitor

    Forum Monitor завантажує список seed-URLs з чотирьох джерел:

    | Джерело | Опис | |---------|------| | forum_seeds.json | Ручний список відомих форумів (BFD, BreachForums, Exploit) | | resolved_forum_seeds.json | Актуальні .onion-адреси знайдені через Ahmia | | discovered_forums.json | Нові форуми виявлені snowball-методом | | ukraine_forum_seeds.json | UA-related форуми для моніторингу загроз Україні |

    Кожен форум краулиться з паралельністю 400 одночасних запитів через Tor fleet. За один прохід Monitor обробляє 80+ .onion-сайтів і збирає ~1300-1400 заголовків.

    DeDuplication

    Кожен заголовок хешується (SHA-256 від forum_name + subject). Якщо хеш вже є в DuckDB -- запис ігнорується. Це дозволяє краулити один і той самий форум кожні 3 хвилини без дублікатів.


    Стадія 2: Body Fetcher -- повний текст за 3 секунди

    Проблема заголовків

    Заголовок "Fresh batch UA 2026" може означати:

  • Продаж вкрадених облікових записів (critical)
  • Обговорення нового батчу стікерів (low)
  • Рекламу фішинг-кіту (high)
  • Без тіла повідомлення LLM змушений вгадувати. З тілом -- точність класифікації зростає з ~70% до ~95%.

    Як це працює

    Body Fetcher бере з DuckDB всі записи де body_fetched = false та thread_url IS NOT NULL:

    ~~~python BATCH_SIZE = 3000 # до 3000 тредів за раз CONCURRENCY = 450 # 450 паралельних запитів через Tor fleet

    async def crawl(self): pending = get_unfetched_thread_urls(limit=BATCH_SIZE) semaphore = asyncio.Semaphore(CONCURRENCY)

    async with httpx.AsyncClient(proxy=TOR_PROXY) as client: tasks = [_fetch_one(item, client) for item in pending] await asyncio.gather(*tasks) ~~~

    450 паралельних запитів через 500 Tor-нод -- HAProxy рівномірно розподіляє навантаження.

    Intelligent HTML Parsing

    Тіла повідомлень витягуються каскадним парсером -- спершу специфічні CSS-селектори для відомих форумних движків (XenForo, MyBB, phpBB), потім generic селектори, і як fallback -- найбільший текстовий блок на сторінці. Текст обрізається до 10 000 символів.

    Безперервний fetch

    Pipeline не чекає наступного інтервалу. Одразу після Monitor перевіряє unfetched -- якщо є нові треди з URL, Body Fetcher запуститься негайно. Тільки коли backlog = 0 -- переходимо до класифікації.


    Стадія 3: LLM Classifier -- Claude Haiku через AWS Bedrock

    Batch-класифікація

    Класифікатор бере до 25 некласифікованих постів за раз і відправляє один запит до Claude Haiku. Модель аналізує заголовок + тіло і повертає для кожного:

    | Поле | Приклад | |------|---------| | category | credentials, ransomware, exploit | | subcategory | stealer_logs, victim_announcement, zero_day | | entities | ["Monobank", "PrivatBank"] | | risk_level | critical / high / medium / low | | language | en, ru, uk, zh |

    Taxonomy: 12 категорій, 45+ підкатегорій

    ~~~ data_leak -> database_dump, pii_exposure, government_leak credentials -> stealer_logs, combo_list, api_keys access_sale -> rdp_access, corporate_access, webshell ransomware -> victim_announcement, affiliate_recruitment malware -> rat, botnet, stealer, loader, crypter exploit -> zero_day, cve_exploit, poc carding -> fullz, credit_cards, cashout fraud -> phishing_kit, fake_documents, bank_fraud services -> ddos_service, hacking_service, mixing_service drugs -> drugs, precursors weapons -> firearms, explosives discussion -> tutorial, news, opsec, offtopic ~~~


    Результати: 17 000+ постів у реальному часі

    | Метрика | Значення | |---------|----------| | Tor-нод | 500 (всі healthy) | | Форумів у seed-списку | 87 | | Зібрано forum subjects | 17 754 | | Класифіковано | 17 754 (100%) | | Ransomware victims | 16 216 | | GitHub leaks | 280 | | Час циклу pipeline | ~3 хвилини | | Затримка body fetch | < 5 секунд після monitor | | Затримка classify | < 2 секунди після body fetch |

    Розподіл по ризику

    ~~~ high 6 371 постів (36%) low 5 686 постів (32%) critical 3 089 постів (17%) medium 2 608 постів (15%) ~~~

    Більше половини постів класифіковані як high або critical -- це активні загрози: продаж доступів, нові жертви ransomware, свіжі credential dumps.


    Інфраструктура: WireGuard + home server + cloud

    ~~~ ┌──────────────────────────┐ WireGuard ┌─────────────────┐ │ Home Server │<═══ 10.77.0.0/24 ═══════>│ AWS EC2 │ │ 10.77.0.1 │ (encrypted tunnel) │ 10.77.0.3 │ │ │ │ │ │ ┌────────────────────┐ │ │ ┌───────────┐ │ │ │ 500x Tor containers│ │ │ │ FastAPI │ │ │ │ HAProxy LB (:9100) │ │<── crawler traffic ─────│ │ Backend │ │ │ │ Collector (:8100) │ │ │ │ nginx │ │ │ │ DuckDB │ │ │ │ (static) │ │ │ │ OpenSanctions │ │ │ └───────────┘ │ │ └────────────────────┘ │ └─────────────────┘ └──────────────────────────┘ | Cloudflare CDN | Clients ~~~

  • Tor fleet на home server -- 500 контейнерів коштують $200+/міс у хмарі, але безкоштовні на своєму сервері
  • Backend у хмарі -- low-latency API для клієнтів, Cloudflare CDN
  • WireGuard -- зашифрований тунель, backend бачить collector та yente як локальні сервіси

  • Storage: DuckDB -- не PostgreSQL

  • Single-file: одна база index.duckdb -- легко бекапити і переносити
  • Columnar storage: аналітичні запити по 17K+ записах працюють миттєво
  • Zero overhead: жодного окремого процесу, жодного connection pool
  • Компроміс: один writer одночасно. Для послідовного pipeline -- не проблема.


    Стек

    | Компонент | Технологія | |-----------|-----------| | Tor Fleet | 500x Docker containers + HAProxy LB | | Crawler | Python + httpx + BeautifulSoup + asyncio | | Database | DuckDB (single-file, columnar) | | LLM | Claude Haiku 4.5 via AWS Bedrock | | Backend API | FastAPI + uvicorn | | Tunnel | WireGuard (home -- cloud) | | CDN | Cloudflare (Full Strict SSL) | | Infra | Home server (Tor fleet) + AWS EC2 (API) |

    > 500 Tor-нод. 87 форумів. 17 000+ класифікованих постів. Нульова затримка між етапами. Безперервний моніторинг даркнету -- без жодного аналітика, який не спить о 3 ночі.


    Запустити OSINT-розвідку компанії →

    Теги: #Dark Web, #Tor, #Pipeline, #HAProxy, #DuckDB, #LLM, #AWS Bedrock, #Automation, #DevOps

    Схожі статті

    Повернутися до блогу | Panoptic