Защита слепого ретранслятора от злоупотреблений
Ограничения, которые узел AeroNyx применяет перед пересылкой зашифрованного трафика ретрансляции: ограничение частоты от предыдущего перехода и карантин, подавление повторов маршрутов с сохранением при перезапуске, ограниченные повторные попытки и допуск по CPU — только на основе подписанных метаданных и агрегированных счётчиков.
Защита слепого ретранслятора от злоупотреблений (Blind Relay Abuse Guard) — это набор ограничений, которые узел AeroNyx применяет, прежде чем пересылать зашифрованный трафик ретрансляции для другого узла. Она сдерживает злонамеренные или нестабильные пиры, используя только подписанные метаданные маршрутизации и локальные счётчики. Она никогда не разбирает шифротекст, не хранит историю маршрутов и не раскрывает, кто с кем общается.
Область действия
| Механизм | Состояние |
|---|---|
| Пересылка непрозрачной полезной нагрузки | Реализовано |
| Подписанные проверки свежести и подавление повторов маршрутов | Реализовано. Состояние защиты от повторов сохраняется при перезапуске, когда включено хранилище ретранслятора чата узла |
| Ограничение частоты запросов от предыдущего перехода и карантин | Реализовано |
| Общепроцессные ограничения допуска запросов и криптоопераций | Реализовано |
| Агрегированные данные о работе в состоянии узла и в Nodeboard | Реализовано |
| Проверка открытого текста или полезной нагрузки | Не выполняется, по замыслу |
| Аналитика по пользователям, маршрутам или социальному графу | Не выполняется, по замыслу |
Правило слепого узла
Ретранслятор может аутентифицировать метаданные маршрутизации, применять ограниченную политику, пересылать непрозрачный конверт и возвращать подписанную квитанцию. Он никогда не должен проверять полезную нагрузку или раскрывать метаданные, по которым можно восстановить маршрут, идентичность пользователя или связь между пользователями. В частности, ретрансляторы не записывают и не раскрывают:
- открытый текст сообщений, пакетов, медиаданных или памяти
- DNS-запросы, места назначения, домены, URL или историю просмотра
- идентификаторы маршрутов, полные пути, URL эндпоинтов или IP-адреса клиентов
- полные открытые ключи, идентичности получателей, идентификаторы сообщений или рёбра социального графа
- закрытые ключи, секреты ваучеров, трафик на уровне кошельков или материалы для расшифровки
Конвейер допуска
Каждый запрос проходит следующие проверки, прежде чем сможет использовать ресурсы пересылки. Шаги 1–6 выполняются в указанном порядке. На шаге 7 запрос, адресованный этому узлу, переходит к его собственной конечной или луковой обработке, а запрос для другого узла резервируется после определения его следующего перехода.
1. request gate: process-wide admission window and in-flight limit (64 requests)
2. crypto admission: a free slot for signature and onion work, or Backpressure
3. authenticate the signed previous hop and envelope
4. previous-hop rate limit and quarantine check
5. envelope metadata: size, age and future-skew limits
6. self-loop check
7. route loop, TTL and next-hop descriptor checks, plus the route replay reservation
8. forward the opaque ciphertext, or peel the terminal layer and deliver
Каждое решение принимается на основе подписанных метаданных маршрутизации и локального агрегированного состояния, а не расшифрованного содержимого.
Ограничения во время работы
Ниже приведены текущие значения по умолчанию в исходном коде узла. Это рабочие значения, а не постоянные обещания протокола.
| Константа | По умолчанию |
|---|---|
BLIND_RELAY_ROUTE_REPLAY_CAPACITY | 8192 активных маршрута |
BLIND_RELAY_ROUTE_REPLAY_TTL_SECS | 600 секунд |
PREVIOUS_HOP_RATE_LIMIT на PREVIOUS_HOP_RATE_WINDOW_SECS | 120 запросов за 60 секунд |
PREVIOUS_HOP_FAILURE_THRESHOLD на PREVIOUS_HOP_FAILURE_WINDOW_SECS | 12 учитываемых сбоев за 300 секунд |
PREVIOUS_HOP_QUARANTINE_SECS | 300 секунд |
MAX_PREVIOUS_HOP_BUCKETS | 4096 корзин |
BLIND_RELAY_MAX_ENVELOPE_AGE_SECS | 600 секунд |
BLIND_RELAY_MAX_FUTURE_SKEW_SECS | 120 секунд |
BLIND_RELAY_DELIVERY_RECEIPT_MAX_AGE_SECS | 120 секунд |
DEFAULT_MAX_ATTEMPTS (повторы пересылки) | 3 попытки |
MAX_IN_FLIGHT_BLIND_RELAY_REQUESTS | 64 запроса |
Ограничение частоты от предыдущего перехода и карантин
Ограничения частоты привязаны к подписанной идентичности предыдущего перехода. Более 120 запросов от одного предыдущего перехода в течение 60 секунд запускают для этого перехода пятиминутный локальный карантин. Отдельная оценка сбоев запускает такой же карантин после 12 учитываемых сбоев в течение пяти минут. В оценку засчитываются только следующие причины:
invalid_previous_hop | invalid_signature | self_loop | route_loop | ttl_exhausted
Тайм-ауты транспорта, потерянные подтверждения, дублирующие повторы и конфликты повторов не увеличивают оценку, поэтому исправный пир не попадает в карантин из-за проблем с сетью. Хранилище корзин ограничено, а неактивные корзины вытесняются, поэтому защита не может разрастись в долгосрочный граф коммуникаций.
Повторы маршрутов и повторные попытки
Каждый запрос ретрансляции несёт идентификатор маршрута. Когда хранилище ретранслятора чата узла включено ([memchain.chat_relay] enabled = true), резервирования маршрутов и завершённые ответы хранятся в его базе данных SQLite, поэтому защита от повторов сохраняется при перезапуске. В противном случае узел использует реестр в памяти с той же ёмкостью и окном. Если все 8192 слота заняты активными маршрутами, новые маршруты отклоняются с replay_capacity, пока слоты не истекут.
Повторный идентификатор маршрута приводит к одному из четырёх исходов:
| Ситуация | Ответ | Влияние на предыдущий переход |
|---|---|---|
| Тот же запрос; первая попытка завершена | Повторно воспроизводится сохранённый ответ, включая квитанцию, подписанную конечным узлом, если она есть. Ничего не доставляется и не пересылается повторно (duplicate_route) | Засчитывается как успех |
| Тот же запрос; первая попытка ещё выполняется | HTTP 503 route_in_flight. Отправитель может повторить попытку | Нет |
| Другой запрос с тем же идентификатором маршрута | HTTP 400 replay_conflict | Записывается в состояние пира. Не увеличивает оценку для карантина |
| Тот же запрос, но сохранённому ответу больше 120 секунд | HTTP 409 replay_response_expired. Маршрут остаётся зарезервированным до конца своего 600-секундного окна, поэтому не может быть выполнен снова | Нет |
Последний исход относится к постоянному хранилищу. Подписанные квитанции действительны только как свежее доказательство, поэтому узел не воспроизводит квитанцию повторно по прошествии 120 секунд.
Когда пересылка на следующий переход завершается тайм-аутом, ошибкой соединения или ошибкой запроса, узел повторяет попытку, всего не более трёх попыток. Короткая задержка перед каждой повторной попыткой вычисляется из идентификатора маршрута, следующего перехода и номера попытки. Сбои, которые нижестоящий узел сообщает как окончательные отказы, не повторяются.
Допуск криптоопераций (примечание о ёмкости для операторов)
Проверки подписей, снятие луковых слоёв и подписание квитанций нагружают CPU, поэтому каждый процесс узла их ограничивает:
- Всего слотов: половина потоков CPU, которые сообщает хост, с округлением вверх, не менее 4 и не более 8, поэтому даже хост с одним или двумя потоками получает 4 слота.
- Входящие запросы: на один слот меньше общего числа, чтобы создание исходящих маршрутов и завершение квитанций всегда могли продвигаться. Таким образом, даже на самых маленьких хостах входящие запросы получают 3 слота.
- Когда все слоты заняты: запрос немедленно отклоняется с HTTP 429 и причиной
backpressure. Он не ждёт в очереди.
Поэтому на небольшом хосте ретранслятор рано сбрасывает нагрузку при всплесках. Растущий счётчик backpressure_dropped означает, что узлу нужно больше CPU или меньше трафика ретрансляции.
Агрегированные счётчики во время работы
Узлы передают в своём heartbeat грубые накопительные счётчики и временные метки свежести в разделах:
system_stats.discovery_status.peer_store.runtime.blind_relay
system_stats.discovery_status.peer_store.blind_relay_quality
system_stats.discovery_status.peer_store.peer_health_summary
| Группа счётчиков | Поля |
|---|---|
| Приём и распределение | received, terminal, forwarded, rejected |
| Проверка и защита | invalid_signature, envelope_too_large, ttl_exhausted, no_route, invalid_endpoint, loop_detected, replay_dropped, timestamp_rejected, rate_limited, quarantined, quarantine_started |
| Транспорт и повторы | backpressure_dropped, forward_failed, retry_attempted, retry_succeeded, retry_exhausted |
| Синтетические зонды | probe_attempted, probe_succeeded, probe_failed, two_hop_probe_attempted, two_hop_probe_succeeded, two_hop_probe_failed |
| Реальная доставка и свежесть | verified_client_onion_deliveries, last_verified_client_onion_delivery_at, last_accepted_at, last_event_at |
Эти счётчики описывают работу узла. Они не являются журналами сообщений или данными для выставления счетов и не доказывают, что какой-либо конкретный разговор имел место.
Качество доказательств
Сводка качества разделяет четыре вида доказательств: принятую работу по непрозрачной ретрансляции, синтетические зонды достижимости, синтетические двухшаговые зонды и квитанции, подписанные конечным узлом, для доставок, инициированных клиентом. Для real_relay_ready требуется свежая аутентифицированная квитанция о клиентской доставке. Синтетические доказательства никогда не выдаются за пользовательский трафик.
status | Значение |
|---|---|
idle | Активности ретрансляции или зондов пока не было |
observing | Некоторые доказательства есть, но готовность не установлена |
stale | Прежние доказательства успеха больше не свежие |
ready | Свежая принятая работа или квалифицирующее доказательство при отсутствии активных проблем с транспортом |
protecting | Сработали механизмы защиты от злоупотреблений, а ретранслятор продолжает работать |
degraded | Сбои пересылки или зондов требуют расследования |
attention | Backpressure или исчерпанные повторные попытки требуют оперативного внимания оператора |
proof_scope указывает самое сильное из полученных доказательств: client_message_delivery, relay_acceptance, message_delivery, control_plane, single_hop_control_plane, attempted или none.
Состояние пиров без связей по трафику
peer_health_summary позволяет операторам найти сбойный пир или пир в карантине, не видя, кто с кем общается.
Включено: сокращённый префикс идентификатора узла, грубое состояние работоспособности и дескриптора, свежесть gossip-обмена и успешных маршрутов, агрегированные счётчики успешных и неудачных маршрутов, состояние карантина, оставшееся время и грубые группы причин.
Никогда не включается: полные открытые ключи узлов, идентификаторы маршрутов, списки эндпоинтов, зашифрованные блоки данных или хеши полезной нагрузки, идентификаторы сообщений, идентичности получателей, IP-адреса клиентов, места назначения, данные DNS или рёбра социального графа.
Карта исходного кода
| Уровень | Путь в репозитории узла | Роль |
|---|---|---|
| Оркестрация ретрансляции | crates/aeronyx-server/src/api/chat_peer/blind_relay.rs | Шлюз запросов, допуск криптоопераций, аутентификация, проверки петель и TTL, исходы повторов, пересылка и конечная обработка |
| HTTP-интерфейс и ограничения | crates/aeronyx-server/src/api/chat_peer.rs | Маршруты, ограничения тела запроса и одновременных запросов, коды ошибок и группы причин |
| Политика против злоупотреблений | crates/aeronyx-server/src/api/chat_peer_abuse_guard.rs | Ограничения частоты от предыдущего перехода, оценка сбоев и карантин |
| Реестр повторов | crates/aeronyx-server/src/api/chat_peer_replay.rs | Реестр в памяти и формат сохранённых ответов |
| Постоянное хранилище повторов | crates/aeronyx-server/src/services/chat_relay_blind_route_store.rs | Резервирования маршрутов и ответы в SQLite |
| Политика повторных попыток | crates/aeronyx-server/src/api/chat_peer_retry.rs | Повторяемые и окончательные сбои и тайминг повторных попыток |
| Счётчики и качество | crates/aeronyx-server/src/services/peer_store.rs и services/peer_store/status.rs | Агрегированные счётчики, готовность и классификация доказательств |
| Отчёты о состоянии | crates/aeronyx-server/src/api/vpn_health.rs и management/reporter.rs | Локальный JSON состояния и полезная нагрузка heartbeat |
ПО узлов имеет открытый исходный код под лицензией AGPL-3.0 на github.com/AeroNyxNetwork/AeroNyx. О том, как строятся маршруты, см. Обнаружение узлов и зашифрованная многошаговая ретрансляция.
<!-- faq:start -->Часто задаваемые вопросы
Может ли узел-ретранслятор читать или записывать в журнал пересылаемый трафик?
Нет. Ретранслятор проверяет подписанные метаданные маршрутизации и пересылает непрозрачный конверт. Он хранит только агрегированные счётчики и никогда — полезную нагрузку, историю маршрутов или идентичности получателей.
Что происходит, когда узел повторяет тот же маршрут?
Если первая попытка завершена, узел воспроизводит сохранённый ответ и ничего не доставляет повторно. Если первая попытка ещё выполняется, узел возвращает допускающий повтор route_in_flight. Другой запрос с тем же идентификатором маршрута отклоняется как replay_conflict.
Сохраняется ли защита от повторов при перезапуске узла?
Да, когда включено хранилище ретранслятора чата узла. В этом случае резервирования маршрутов и завершённые ответы хранятся в SQLite. Без этого хранилища узел использует реестр в памяти, который очищается при перезапуске.
Почему пир попадает в карантин?
Пир попадает в карантин на пять минут после отправки более 120 запросов за 60 секунд или после 12 учитываемых сбоев проверки за пять минут. Тайм-ауты, потерянные подтверждения и дублирующие повторы не считаются сбоями.
Почему мой узел возвращает backpressure под нагрузкой?
Все его слоты для криптоопераций ретрансляции были заняты. Узел выделяет для работы с подписями и луковыми слоями примерно половину потоков CPU (не менее 4 и не более 8) и немедленно отклоняет избыточные запросы, а не ставит их в очередь.
<!-- faq:end -->