MyWiki

wiki для личного использования

Инструменты пользователя

Инструменты сайта


github:rbch

RKN Block Checker

Небольшой CLI-инструмент, определяющий, находится ли ваше подключение в зоне блокировки РКН/ТСПУ, и, что полезнее, какой именно тип блокировки применяется (отравление DNS, TCP RST, DPI TLS по SNI или подстановочная страница оператора).

Смысл не в том, чтобы узнать, что «сайт X не открывается», — браузер и так это скажет. Смысл в том, чтобы независимо проверить каждый уровень сетевого стека и сообщить, где именно произошёл сбой. Это даёт гораздо больше информации о вашей ситуации, чем стандартная страница «сайт недоступен».

Быстрый старт

pip install rkn-block-checker
rkn-check

Всё. Инструмент зондирует встроенный список сайтов, классифицирует каждый сбой по уровню и выдаёт заключение. Никакой настройки не требуется.

Пример вывода

======================================================================
  RKN Block Checker
======================================================================
  IP:       95.165.xxx.xxx
  ISP:      AS12389 Ростелеком
  Location: Москва, Москва, RU
----------------------------------------------------------------------

Белый список (должны всегда работать)
  название         вердикт                   TCP     TLS     PLT  статус
  --------------------------------------------------------------------
  gosuslugi        ✓ OK                      18ms    42ms   380ms  200
  yandex           ✓ OK                       8ms    25ms    95ms  200
  sberbank         ✓ OK                      12ms    38ms   250ms  200
  vk               ✓ OK                       9ms    28ms   180ms  200
  ...

Чёрный список (ограниченные РКН)
  название         вердикт                   TCP     TLS     PLT  статус
  --------------------------------------------------------------------
  instagram        ~ ВЕРОЯТНО TLS DPI        22ms       -       -  -
    └ Сброс TLS сразу после ClientHello — соответствует DPI по SNI
  twitter/x        ~ ВЕРОЯТНО TLS DPI        24ms       -       -  -
    └ TLS handshake молча оборван — соответствует DPI-фильтрации
  rutracker        ✗ HTTP STUB               18ms    45ms   120ms  200
    └ Тело ответа совпадает с известной подстановочной страницей оператора
  protonvpn        ✗ DNS                        -       -       -  -
    └ Системный DNS не резолвит, DoH резолвит — соответствует отравлению DNS

======================================================================
  Сводка
----------------------------------------------------------------------
  Белый список: 21/21 работают
  Чёрный список: 3/15 открыты, 12/15 заблокированы

  → Вероятно, нахождение в зоне блокировки РКН (средняя уверенность).
    Большинство сбоев в чёрном списке соответствуют шаблонам цензуры (TLS DPI, TCP RST),
    но эти сигналы также могут быть вызваны проблемами на стороне сервера.
    Для подтверждения нужна контрольная точка.

  Типы блокировок в чёрном списке:
    ~ ВЕРОЯТНО TLS DPI: 8
    ✗ DNS: 2
    ✗ HTTP STUB: 2
======================================================================

Метки вердиктов откалиброваны по степени уверенности:

  • — высокая уверенность (например, отравление DNS, подтверждённое DoH, HTTP 451, известная подстановочная страница).
  • ~ ВЕРОЯТНО — совпадение с известным шаблоном цензуры, но единичный сигнал не исключает проблем на стороне сервера.
  • ? — симптом неоднозначен.

Строка сводки прямо сообщает: «высокая уверенность», «средняя уверенность» или «неопределённо» — и никогда не утверждает больше, чем позволяют сигналы.

Примечание об использовании примеров: Фактические данные (IP, ISP, списки сайтов, вердикты) в выводе — это примеры. При реальном запуске инструмента значения будут соответствовать вашему подключению и текущей ситуации в сети.

Зачем это нужно

Если сайт не открывается, браузер сообщит об этом. Но чтобы предпринять какие-либо действия (выбрать правильный инструмент обхода, составить полезный отчёт об ошибке или просто понять, что происходит с вашим трафиком), нужно знать, на каком именно уровне сетевого стека происходит вмешательство.

Разные механизмы цензуры оставляют разные «отпечатки»:

  • Отравление DNS (DNS poisoning) — самый дешёвый и старый метод. DNS-резолвер провайдера врёт о домене.
  • TCP RST — чёрное дырообразование на IP-уровне. На практике встречается редко — большинство провайдеров не заморачиваются.
  • TLS DPI на SNI — современная «подпись» ТСПУ/РКН. Промежуточный узел (middlebox) отслеживает расширение SNI в TLS ClientHello и обрывает соединение, как только видит заблокированное имя хоста.
  • HTTP-подстановочные страницы — «вежливый» тип: подконтрольная провайдеру страница с телом «заблокировано РКН», часто со статусом 200 или редким, но явным 451.

`rkn-check` последовательно проходит DNS → TCP → TLS → HTTP для каждой цели и останавливается на первом же сбое. Уровень, на котором произошёл сбой, становится вердиктом.

Типовые сценарии

Просто диагностировать текущее подключение

rkn-check

Зондирует встроенные списки (~21 контрольный сайт, ~15 ограниченных РКН), выдаёт отчёт по каждому сайту и сводное заключение.

Проверить один URL

rkn-check --url https://example.com
rkn-check --url example.com --url google.com    # повторить для нескольких

Полностью пропускает встроенные списки и выполняет ad-hoc проверку только переданных URL. Сводного заключения нет — не с чем сравнивать. Используйте, когда нужно узнать, «открылся ли этот конкретный сайт?», не тратя время на полное сканирование.

Передать вывод в `jq`

# имена всех заблокированных сайтов
rkn-check --json | jq -r '.blacklist[] | select(.verdict != "OK") | .name'
 
# подсчёт по типу блокировки
rkn-check --json | jq '.blacklist | group_by(.verdict) | map({verdict: .[0].verdict, count: length})'
 
# только DPI-блокировки (TCP работает, TLS умирает)
rkn-check --json | jq '.blacklist[] | select(.verdict == "TLS_BLOCK" and .tcp_ok)'

Использовать собственные списки целей

rkn-check --black-file my-list.txt
rkn-check --white-file my-control.json --black-file my-targets.json

См. раздел Пользовательские списки целей.

Запускать из cron и сохранять JSON с течением времени

rkn-check --json --no-self-info > "snapshots/$(date -I).json"

`–no-self-info` пропускает определение публичного IP, чтобы инструмент не обращался к `ipinfo.io` при каждом запуске cron (и чтобы результирующий JSON не содержал ваш IP).

Использование

rkn-check [-h] [--json] [--white] [--black]
          [--white-file PATH] [--black-file PATH] [--url URL]
          [--timeout TIMEOUT] [--workers WORKERS] [-v]
          [--no-self-info] [--identify]
Флаг Что делает
`–json` Выдать machine-readable JSON вместо цветного отчёта.
`–white` Проверить только контрольные цели (белый список).
`–black` Проверить только цели из чёрного списка.
`–white-file PATH` Заменить встроенный белый список файлом `.txt` или `.json`.
`–black-file PATH` Заменить встроенный чёрный список файлом `.txt` или `.json`.
`–url URL` Зондировать один URL или имя хоста; повторите для нескольких. Пропускает встроенные списки.
`–timeout T` Таймаут на одно зондирование в секундах (по умолчанию 5.0).
`–workers N` Размер пула потоков для параллельных проверок (по умолчанию 10).
`–no-self-info` Пропустить определение публичного IP в начале отчёта.
`–identify` Отправлять самоидентифицирующий User-Agent вместо обычного Chrome. См. Конфиденциальность и модель угроз.
`-v` / `-vv` Логирование на уровне INFO / DEBUG.

`–white` и `–black` взаимно исключают друг друга. `–url` нельзя комбинировать с `–white`/`–black`/`–white-file`/`–black-file` — в ad-hoc режиме проверяются только переданные URL.

Как это работает

Для каждой цели инструмент проходит DNS → TCP → TLS → HTTP и останавливается на первом сбое. Уровень сбоя становится вердиктом.

Уровень Зонд Что означает сбой
DNS Системный резолвер против Cloudflare DoH, сравнение полных наборов адресов Наборы совпадают, но системный резолвер не работает один → отравление DNS. Непересекающиеся наборы → прозрачная подмена (rewriting).
TCP Обычное TCP-рукопожатие на `:443` Получен `RST` → чёрное дырообразование на IP-уровне. Редко — большинство провайдеров не заморачиваются.
TLS TLS-рукопожатие с SNI = целевой хост Сброс/таймаут именно здесь при работающем TCP — классическая «подпись» ТСПУ/DPI: промежуточный узел видит SNI и обрывает соединение.
HTTP `GET` после завершения рукопожатия 451 или подстановочная страница оператора, возвращающая 200 с телом «заблокировано Роскомнадзором».

Стоит выделить два зонда:

  • Системный DNS против DoH, на основе наборов адресов. Самый дешёвый способ заблокировать сайт — заставить DNS провайдера врать. Каждый хост резолвится дважды: через `getaddrinfo` (использует настройки ОС, обычно DNS провайдера) и через DoH Cloudflare, который провайдер не может перехватить. Два набора возвращённых IP сравниваются: несовпадение засчитывается, только когда наборы полностью не пересекаются. Любой общий адрес трактуется как балансировка нагрузки, а не отравление — крупные сайты часто меняют порядок нескольких A-записей при каждом запросе, и сравнение только первых IP давало бы ложные срабатывания.
  • TLS-рукопожатие с SNI. Современное оборудование ТСПУ не обрывает TCP-соединение — оно позволяет подключиться, читает расширение SNI из ClientHello и затем отправляет RST или просто перестаёт отвечать. Поэтому мы должны реально начать TLS-рукопожатие, чтобы это увидеть. `TLS_BLOCK` после чистого `TCP_OK` — однозначный отпечаток DPI-блокировки.

Вердикты и уверенность

Каждый результат содержит как вердикт, так и уровень уверенности. Вердикт говорит, какой именно сбой произошёл; уверенность — насколько можно доверять диагнозу.

Вердикт Значение
`OK` Сайт загрузился нормально.
`DNS_BLOCK` Системный DNS не резолвит, а DoH резолвит — соответствует отравлению DNS.
`TCP_RESET` На TCP-рукопожатие получен RST.
`TLS_BLOCK` TCP успешен, но TLS-рукопожатие сброшено, оборвано или иным образом убито (типичный DPI по SNI).
`HTTP_STUB` Ответом была известная подстановочная страница оператора или HTTP 451.
`TIMEOUT` Произошёл таймаут, недостаточно данных для дальнейшей классификации.
`DOWN` И резолвинг, и связность не работают, но не в форме, похожей на цензуру.
`UNKNOWN` Неожиданная ошибка, см. примечания.

Уровни уверенности:

  • HIGH (высокая) — два независимых сигнала согласуются (например, отравление DNS, подтверждённое DoH, явный HTTP 451, известная подстановочная страница в теле ответа).
  • MEDIUM (средняя) — совпадение с известным шаблоном цензуры, но сигнал сам по себе не исключает проблемы на стороне сервера или нестабильного канала (сброс TLS сразу после ClientHello, TCP RST в середине потока).
  • LOW (низкая) — симптом неоднозначен (обычный таймаут, неклассифицированный сбой).

Итоговая строка внизу отражает это. Если большинство сбоев в чёрном списке соответствует шаблонам с высокой уверенностью, будет сказано «Вероятно, нахождение в зоне блокировки РКН (высокая уверенность)». Если большинство сигналов средней уверенности — формулировка становится осторожнее. А когда сам белый список в основном не работает, инструмент не делает никаких выводов — без работающего базового уровня нельзя отличить цензуру от проблем с каналом, поэтому сводка становится «Неопределённо».

Конфиденциальность и модель угроз

`rkn-check` — это диагностический инструмент, а не инструмент обхода. Но люди, которые его запускают, как правило, уже находятся под сетевым наблюдением, поэтому настройки по умолчанию выбраны так, чтобы минимизировать след, оставляемый одним запуском.

  • User-Agent. По умолчанию используется обычная строка Chrome-on-Windows с полным набором браузерных заголовков (`Accept`, `Accept-Language`, `Sec-Fetch-*` и т.д.). Более ранний вариант по умолчанию `Mozilla/5.0 (RKN-Checker)` был достаточно уникальным, чтобы идентифицировать запуск инструмента в любых логах на пути — включая, в некоторых юрисдикциях, логи VPN-провайдеров, которые передаются регуляторам по запросу. Обычный User-Agent смешивает запрос с обычным трафиком. Если вы хотите быть идентифицированным как диагностический инструмент (например, при зондировании вашей собственной инфраструктуры), передайте `–identify` для переключения на самоидентифицирующий UA (`rkn-block-checker/<ver>`).
  • Определение публичного IP. По умолчанию инструмент получает ваш IP/ISP/местоположение с `ipinfo.io` и печатает их в начале отчёта. Это исключительно для человека, читающего отчёт — сам диагноз от этого не зависит. Передайте `–no-self-info`, чтобы полностью пропустить этот запрос; это также правильный выбор для скриптов cron и CI.
  • Нет телеметрии. Инструмент никуда не обращается сам. Единственные исходящие соединения — это запрошенные вами зонды целей, DoH-запрос к `cloudflare-dns.com` (всегда включён — это контрольная сторона DNS-сравнения) и опциональный запрос к `ipinfo.io`, если вы его не отключили.
  • Нет эксфильтрации результатов. Результаты выводятся в stdout. Они никуда больше не попадают.

JSON-вывод

`–json` выдаёт один объект, содержащий `self_info` (блок IP/ISP из заголовка или `null`, если установлен `–no-self-info`) и списки результатов. Каждый результат содержит полную трассировку зонда для каждой цели — какие DNS-резолверы что вернули, успешность TCP и TLS с таймингами, HTTP-статус, вердикт, уровень уверенности и человекочитаемые примечания.

(Сокращённый пример, полная версия в исходном репозитории)

{
  "self_info": {
    "ip": "95.165.xxx.xxx",
    "city": "Moscow",
    "country": "RU",
    "org": "AS12389 Rostelecom"
  },
  "whitelist": [
    {
      "name": "gosuslugi",
      "url": "https://www.gosuslugi.ru/",
      "verdict": "OK",
      "confidence": "HIGH",
      "notes": [],
      "sys_ip": "95.181.182.36",
      "doh_ip": "95.181.182.36",
      "sys_ips": ["95.181.182.36"],
      "doh_ips": ["95.181.182.36"],
      "dns_mismatch": false,
      "tcp_ok": true,  "tcp_time_ms": 18.4,
      "tls_ok": true,  "tls_time_ms": 42.1,
      "tls_cert_cn": "*.gosuslugi.ru",
      "status_code": 200, "plt_ms": 380.7
    }
  ],
  "blacklist": [
    {
      "name": "instagram",
      "url": "https://www.instagram.com/",
      "verdict": "TLS_BLOCK",
      "confidence": "MEDIUM",
      "notes": ["TLS reset right after ClientHello - consistent with SNI-based DPI filtering"],
      "tcp_ok": true,  "tcp_time_ms": 22.4,
      "tls_ok": false, "tls_error": "connection reset during TLS"
    }
  ]
}

`sys_ip` / `doh_ip` содержат наименьший отсортированный адрес из каждого набора для обратной совместимости; `sys_ips` / `doh_ips` содержат полные отсортированные списки. Поля трассировки зонда всегда присутствуют, чтобы можно было понять, почему был вынесен тот или иной вердикт — `TLS_BLOCK` с `tcp_ok: true` — это признак DPI по SNI; если `tcp_ok: false`, значит, что-то другое сломалось раньше.

Пользовательские списки целей

`–white-file` и `–black-file` принимают либо JSON, либо обычный текст. Формат определяется расширением файла (`.json` → JSON, всё остальное → текст).

JSON-формат — плоский объект, отображающий имя в URL:

{
  "google":   "https://google.com",
  "github":   "https://github.com",
  "rutracker": "https://rutracker.org"
}

Текстовый формат — одна запись на строку. Принимаются три формы:

# простой URL - имя автоматически извлекается из имени хоста
https://example.com

# имя<пробел>URL
github https://github.com

# имя=URL
custom=https://example.org

# пустые строки и строки, начинающиеся с #, пропускаются

К URL без схемы добавляется https://. Повторяющиеся имена перезаписываются (с логированием предупреждения); используйте уникальные имена, если нужно зондировать оба.

Важно: Убедитесь, что ваши пользовательские файлы следуют одному из указанных форматов. Инструмент не проверяет синтаксис каждого URL, но ошибки в файле могут привести к пропуску целей или неожиданному поведению.

Установка

Требуется Python 3.10+.

Из PyPI:

pip install rkn-block-checker

Из исходников:

git clone https://github.com/MayersScott/rkn-block-checker.git
cd rkn-block-checker
pip install -e .

Для разработки (добавляет pytest и другое):

pip install -e ".[dev]"

Структура проекта

rkn_checker/
  __main__.py     # python -m rkn_checker
  cli.py          # argparse + точка входа
  core.py         # оркестрация DNS -> TCP -> TLS -> HTTP
  dns.py          # системный резолвер + Cloudflare DoH (полные наборы адресов)
  network.py      # сырые TCP и TLS зонды
  http.py         # HTTP GET, набор заголовков, обнаружение подстановочных страниц
  output.py       # цветной CLI-отчёт
  lists.py        # парсер пользовательских файлов целей
  targets.py      # встроенные белый список, чёрный список, маркеры подстановок
  models.py       # CheckResult, Verdict, Confidence
tests/            # pytest, все сетевые вызовы замоканы

Тесты

pip install -e ".[dev]"
pytest

В тестовом наборе нет сетевых вызовов — каждый зонд замокан, поэтому тесты работают одинаково в CI, в самолёте или за корпоративным прокси.

Релизы

Релизы автоматически публикуются в PyPI через workflow `release.yml` при пуше тега `v*`. Workflow использует PyPI Trusted Publishing — ключ API не хранится в секретах репозитория.

Чтобы выпустить новую версию:

# сначала обновить версию в pyproject.toml, закоммитить
git tag v0.3.4
git push origin v0.3.4

Workflow проверяет, что тег соответствует версии в `pyproject.toml`, собирает sdist + wheel, запускает `twine check –strict`, публикует в PyPI и прикрепляет артефакты к GitHub Release с автоматически сгенерированными примечаниями.

Ограничения

  • Только IPv4. Некоторые российские провайдеры по-разному обрабатывают IPv6 (часто с меньшей фильтрацией), но путь по IPv4 — это то, что пользователи реально испытывают на практике.
  • Встроенные списки целей невелики (~20 сайтов в каждой категории). Этого достаточно для вердикта, но не позволит обнаружить блокировку, затрагивающую только один конкретный ресурс. Используйте `–url` для ad-hoc проверок или `–white-file` / `–black-file` для собственных списков.
  • Одномоментный снимок (one-shot), без повторных попыток и отслеживания изменений во времени. Если нужно мониторить соединение постоянно, запускайте `rkn-check –json` из cron и сохраняйте снимки.
  • Маркеры подстановок (stub markers) в основном представляют собой русскоязычные фразы, суженные настолько, чтобы избежать ложных срабатываний на несвязанных статьях, где упоминается Роскомнадзор. Новые паттерны добавляются по мере поступления сообщений.

Благодарности

Проект был значительно улучшен людьми, которые критически посмотрели на код и сообщили о проблемах с конкретными воспроизведениями. (Список участников с краткими описаниями их вклада сохранён в соответствии с оригиналом, но имена и ссылки на pull requests/issues были оставлены как есть, так как это часть истории проекта.)

Лицензия

MIT


Antistatus 12.05.2026 12:39

github/rbch.txt · Последнее изменение: admin

Инструменты страницы