GitHub mirror: mrvasil/ctf-ad-mirror-tool-rust
Go to file
2026-10-23 01:10:09 +03:00
deploy Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
docs Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
examples Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
scripts Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
src Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
tests Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
web Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
.gitignore Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
agent.md Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
AGENTS.md Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
Cargo.lock Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
Cargo.toml Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
findings.md Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
mirror.example.toml Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
progress.md Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
README.md Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00
task_plan.md Rewrite mirror manager in Rust with async forwarding and ForcAD routing 2026-10-23 01:10:09 +03:00

Mirror Tool

Асинхронный TCP mirror и панель управления маршрутами для A/D CTF. Один Rust-бинарник включает сетевое ядро, веб-интерфейс и клиент ForcAD. Python, Node и внешние CDN для работы не нужны.

Клиент общается с одним основным upstream. Входящий поток можно одновременно отправлять выбранным репликам; их ответы вычитываются и игнорируются. Смена маршрута применяется к новым соединениям. Открытая сессия сохраняет свой upstream до завершения.

Запуск

Нужен актуальный stable Rust (проверенные версии записаны в docs/verification.md).

cargo build --release --locked
cp mirror.example.toml mirror.toml
# Отредактируйте команды, адреса и порты. Пример использует тестовую сеть RFC 5737.
read -s MIRROR_PASSWORD
export MIRROR_PASSWORD
./target/release/mirror-tool --config mirror.toml check
./target/release/mirror-tool --config mirror.toml

Пароль должен содержать минимум 12 символов. Панель по умолчанию доступна на http://127.0.0.1:6700. Для удалённого доступа используйте VPN/SSH-туннель либо HTTPS reverse proxy; при HTTPS установите secure_cookie = true. TLS для управляющей панели внутри бинарника не реализован. Переменные окружения с паролем и конфигурацию игры не коммитьте.

В панели: выберите основной маршрут, включите нужные реплики и запустите сервис. Радиокнопка меняет primary, флажок выбирает реплику. Настройки сохраняются после успешного ответа сервера; они переживают рестарт. Сервис можно добавить с произвольными портом слушателя и upstream-портом, команду — с IPv4/IPv6-адресом. Авто режим выбирает только из назначенных реплик.

Доставка и перегрузка

Режим При медленной реплике При недоступной/сломавшейся реплике
isolate (по умолчанию) Ограниченная очередь; при заполнении реплика отключается для этой сессии. Primary продолжает работать. Ошибка и отключение учитываются в статистике; primary независим.
strict Чтение клиента замедляется до скорости всех реплик (backpressure). Сессия завершается с ошибкой; продолжать без обязательной реплики нельзя.

Произвольные фрагменты потока не выбрасываются с продолжением передачи, как в старой версии. Но TCP mirror не является репликацией БД: серверы могут генерировать разные идентификаторы, challenge/nonce, ответы и побочные эффекты. TLS-поток нельзя содержательно зеркалировать в независимые TLS-сессии. Проверяйте совместимость конкретного протокола и checker перед игрой.

Абсолютные «0 потерь / 0 накладных расходов» недостижимы при сбоях сети и медленных репликах. Strict сохраняет порядок и полную передачу при здоровых участниках ценой скорости/доступности; isolate может доставить отключённой реплике только префикс. Счётчики байтов показывают успешные локальные socket writes, а не фиксацию данных приложением или БД. Незавершённый запрос никогда не переигрывается на другом upstream.

ForcAD и автоматический выбор

[scoreboard]
kind = "forcad"
url = "http://scoreboard.example:8080"
poll_interval_ms = 3000
stale_after_ms = 15000

ForcAD: постоянная сессия Engine.IO v4 / Socket.IO polling, namespace /game_events, ping/pong и переподключение с ограниченным backoff. init_scoreboard задаёт метаданные и состояние; update_scoreboard обновляет состояние без потери метаданных. Поддерживается формат upstream pomo-mondreganto/ForcAD, проверенный на 6ce3407. События сопоставляются по точному имени сервиса и имени/IP команды. Настройте имена как на scoreboard. Произвольные несовместимые форки автоматически не угадываются.

Для adplatf укажите kind = "adplatf": используется HTTP /state. URL не должен содержать пароль, query или fragment. Доступный endpoint должен отдавать публичную таблицу без отдельной аутентификации.

Автопереключение:

  1. TCP-пробы выполняются параллельно с ограничением; отдельно считаются последовательные сбои и успешные проверки.
  2. Причина переключения — устойчивый TCP-сбой primary либо свежий checker CORRUPT/MUMBLE/DOWN. CHECK_FAILED и неизвестный статус не принимаются за неисправность сервиса.
  3. Кандидат должен быть назначенной репликой с недавней успешной TCP-проверкой и пройденным порогом восстановления. Свежий плохой checker исключает кандидата; UP получает приоритет. При равенстве используются stolen, затем ID.
  4. Работает cooldown, включая ручную смену primary. Самопроизвольного возврата на прежний сервер нет. Старый primary становится репликой.
  5. Устаревший или отключённый scoreboard не влияет на выбор; при отсутствии пригодного кандидата маршрут сохраняется. Активные сессии не мигрируют.

Встроенный proxy выбирает upstream; системные маршруты, nftables и iptables не меняются. Исходный IP клиента на upstream не сохраняется. UDP и PROXY protocol не реализованы.

Ресурсы и остановка

Tokio multiplexes соединения без subprocess/thread на сессию. Общий max_connections ограничивает активные сессии всех сервисов; лишние подключения закрываются и учитываются как rejected. Очереди реплик ограничены replica_queue_chunks, общий бюджет их разделяемых payload — max_buffered_bytes (256 MiB по умолчанию). В этот бюджет не входят рабочие буферы чтения и TCP-буферы ОС. Рабочие буферы ограничены числом сессий/реплик; не выставляйте лимиты выше доступной памяти и fd.

Путь без реплик переиспользует буфер; зеркальный путь передаёт разделяемые Bytes без отдельной копии на реплику. Счётчики используют атомарные операции. TCP_NODELAY и TCP keepalive включены; connect timeout не превращается в короткий idle timeout.

Stop отключает приём новых соединений, сохраняя открытые. Удаление остановленного сервиса ждёт drain_timeout_ms, затем отменяет остаток. SIGINT/SIGTERM закрывает слушатели, даёт всем сессиям общий срок дренирования, затем отменяет и дожидается принадлежащих им задач. Isolate также ограничивает время отправки остатка репликам после завершения основного обмена. Strict в нормальном режиме может ожидать медленную реплику неограниченно; общая остановка остаётся ограниченной.

Конфигурация и состояние

mirror.toml задаёт настройки процесса и начальную топологию. После первого изменения сохранённый state_file содержит авторитетный список команд/сервисов; TOML продолжает задавать сеть, мониторинг и scoreboard. Путь состояния отсчитывается от каталога TOML. Чтобы применить начальную топологию заново, остановите процесс и переместите state-файл в резервную копию. Файл не исполняется, версия и содержимое валидируются.

Запись состояния: временный файл на той же ФС → fsync → atomic rename; права 0600. Сбой сохранения вызывает попытку отката runtime. Публикация topology сериализована с ручными и автоматическими изменениями. Runtime и ФС не образуют распределённую транзакцию: клиент, принятый в коротком окне до неудачного сохранения, может сохранить временно применённый маршрут. Необходимость отката выводится как ошибка.

Старый config.py не выполняется и не импортируется. Перенесите TEAMS в [[teams]], TASKS в [[services]], ForcAD URL в [scoreboard]. PASSWORD заменён переменной окружения, SECRET_KEY не нужен: сервер хранит случайные сессии в памяти. Старые cookie после перезапуска недействительны. API описан в docs/api.md.

Для Linux есть systemd unit: создайте отдельного пользователя mirror-tool, разместите бинарник в /usr/local/bin/, конфиг и закрытый mirror.env в /etc/mirror-tool/; в TOML задайте state_file = "/var/lib/mirror-tool/state.json". Unit даёт только CAP_NET_BIND_SERVICE для портов ниже 1024; при ненужности удалите capability. При увеличении drain timeout увеличьте TimeoutStopSec. Unit поставляется как шаблон, автоматически не устанавливается.

Разработка и проверка

cargo fmt --check
cargo clippy --all-targets --locked -- -D warnings
cargo test --all-targets --locked
node --check web/app.js
cargo run --release --example benchmark -- 64

Benchmark сравнивает direct loopback с proxy в том же процессе, проверяет каждый байт, выполняет три чередующихся прогона и 500 latency-проб. Это воспроизводимый локальный baseline, не прогноз скорости реальной сети. Результаты и практические ограничения: docs/verification.md.

Для ручного теста UI на изолированном loopback: cargo build, затем python3 scripts/ui_fixture.py target/debug/mirror-tool. Это необязательная тестовая утилита; её публичный тестовый пароль указан в начале файла, конфиг/состояние временные. Не используйте fixture для реального доступа.

Структура: config — валидация, engine — TCP, scoreboard — протоколы таблиц, control — health/выбор/изменения, store — сохранение, auth/web — управление, web/ — встроенный интерфейс. agent.md и журналы фиксируют ход работы.