20 KiB
Rampart — Development TODO & Roadmap
Живой документ. Философия: KISS → DRY → SOLID → YAGNI.
0. Принципы разработки
KISS
- Не добавляй абстракцию до третьего повторения.
- Функция ≤ 60 строк, модуль ≤ 500 строк.
- Не используй generics где хватит
&strиVec<u8>.
DRY
- Повтор > 2 раз → выноси, но лучше копипаста чем неправильная абстракция.
SOLID (Rust)
- S: один файл = одна ответственность
- O: расширяй через трейты
- L:
dyn Filter— любая реализация без side effects - I: маленькие трейты вместо одного
ShieldTrait - D: core зависит от
trait StateStore, не от Redis
YAGNI
- Не пиши BGP до v0.6, K8s Operator до v0.5
- Не добавляй feature flag если фича не готова
Rust-специфичные
unwrap()— только в main() и тестахunsafe— только в xdp/, комментарий обязателенclone()осознанно, профилируй hot path- Блокирующие операции →
spawn_blocking - Логи:
tracing::info!/debug!/error! - Метрики: register один раз при старте, инкремент в hot path
1. Текущее состояние (v0.2+)
✅ Готово
| Компонент | Статус |
|---|---|
| rampart-core (Rust Edge) | ~85% — работает: TCP listener, handshake parse, HMAC sign, rate limit, death code (8 паттернов), blacklist (DashMap + TTL), Redis sync, Prometheus metrics, graceful shutdown |
| rampart-manager (Rust API) | ~80% — работает: JWT auth, CRUD blacklist, servers/nodes list, heartbeat мониторинг |
| rampart-cli (Rust CLI) | ~40% — 3/6 команд (status, doctor, blacklist list/add) |
| velocity-plugin (Java) | ~90% — domain whitelist, HMAC verify (constant-time), Redis server registry (delta-sync), TPS-aware load balancer (circuit breaker < 12 TPS) |
| paper-plugin (Java) | ~90% — Redis heartbeat (TPS/online/sec), auto-registration, graceful shutdown |
| dashboard (React/TS) | ~85% — login, Servers/Blacklist/Nodes таблицы, auto-refresh, dark theme |
| CI/CD | GitHub Actions (Rust check+test+clippy+deny + Java build + Dashboard build + Docker), Makefile, deny.toml |
| Docs | ~80% — architecture, anti-bot, ebpf, ddos, deployment, configuration |
| Ref analysis | Проанализированы Sonar, LimboFilter, AtomGuard, Infrarust, MC-XDP-eBPF, PowGo |
| ref/ в .gitignore | Добавлено |
❌ Не начато / частично
| Компонент | Статус |
|---|---|
| Аудит-фикс v0.3 | открыт — все P0/P1/P2 из code review 2026-08 (см. сек. 2) |
| GeoIP/ASN reputation | 0% — enum есть, реализации нет |
| Bloom filter blacklist | 0% |
⚠️ Ревизия статусов (после аудита 2026-08)
Прошлые строки «XDP 0%» / «PoW 0%» были устаревшими: код уже написан, но не докатан. Реальное состояние (подробности — в сек. 2):
| Компонент | Реальность |
|---|---|
| xdp_filter.c + loader | ~написан, но: не собирается в Docker/CI (feature xdp не в default), баги IPv6 (daddr вместо saddr), dead-код в DIRECT_READ_LOGIN |
| PoW (Layer 2) | ~написан, но ломает ванильных клиентов — по умолчанию никто не войдёт (P0) |
| Layer 6 (Traffic Intel) | написан, но не подключён ни в один hot path (мёртвый код) |
| ClickHouse + Grafana | врайтер написан, вызовов push() нет — мёртвый код |
| Velocity physics | написан, но фейк: не читает позиции, проверка по времени между событиями |
| CAPTCHA | написан, но challenge() нигде не вызывается — мёртвый код |
2. Аудит-фикс v0.3 (code review 2026-08) — закрыть до релиза
Полный список минусов из ревью. Философия: «мёртвый код = баг», «по умолчанию безопасно».
P0 — Showstopper (блокируют релиз)
- PoW совместимость с ванильными клиентами. Решение (a): PoW off по умолчанию (
config.rs), код PoW сохранён, включение только с клиентским модом или MC-совместимым PoW. README + docs +deploy/config/edge.tomlобновлены. Follow-up (клиентский мод / PoW поверх MC) — в backlog. API_PASSWORDбез дефолта. Fail-fast при старте (нет env илиchangeme→ ошибка), constant-time сравнение (subtle), rate-limit 5/60с на/api/v1/auth/login(429),CorsLayer::permissive()→CORS_ORIGINиз env. Неверный пароль → 401.- Чтение полного кадра.
read_full_frame()вtunnel.rs: накопление по varint-длине, лимит 8192, таймаут, EOF/ошибки → death-code path. - IPv6. Полная поддержка: rate-limit/blacklist/whitelist переведены на
IpAddr(DashMap),redis.rsпарсит черезIpAddr::parse, whitelist валидируется на старте. Попутно исправлен overflow-panic в redis.rs (octet > 255). XDP остаётся IPv4-only (задокументировано).
P1 — Безопасность
- HMAC: nonce/timestamp + TTL в подпись; реализовать ротацию ключей (dual-key) и задействовать
key_rotation_interval_secs(сейчас мёртвый конфиг). Детерминированная подпись = вечная утечка. - RateLimiter: TTL-эвикция idle bucket'ов (фоновый sweep) + cap размера карты — иначе ботнет съест память.
- Blacklist: вызывать
clear_expired()по таймеру (сейчас мёртвый код). - Redis IP-parse: валидировать октеты ≤ 255 (
redis.rs:95) — сейчас(ip_u32<<8)|octetс октетом >255 даёт panic в debug. - JWT: валидация ролей/audience, secret ≥ 32 байт, rate-limit на login.
P1 — Целостность слоёв
- XDP в Docker/CI: собирать
rampart-core --features xdp, clang+libbpf в builder-образ, smoke-attach в CI. - Синхронизация blacklist Rust ↔ XDP:
XdpFilter::ban_ipвызывать при death-code бане; TTL из конфига, не хардкод 300с. - Подключить Layer 6 (
AttackDetector/IpReputation/TrafficProfiler) в hot path: метрики, reputation-скоринг, auto-ban. - Подключить ClickHouse: реальные
push()из hot path + flush task + таблица (сейчас мёртвый код). - CAPTCHA: вызвать
challenge()на входе ИЛИ удалить (сейчас мёртвый код;markVerified/verifiedPlayersпишутся, но не читаются). routeServer(domain): реализовать доменную маршрутизацию поServerInfo(сейчас параметр игнорируется, только round-robin).
P2 — Баги и долг
- XDP IPv6:
src_ip = ip6->daddr(xdp_filter.c:141) → исправить наsaddr; иначе whitelist/blacklist/flow-ключи по чужому IP. - XDP seq-трекинг: пересмотреть
expected_seq; убрать dead-код вDIRECT_READ_LOGIN(login_consumed < (end-cursor)всегда false). - Порядок фильтров: rate limit ДО PoW (сейчас PoW-работа тратится на rate-limited IP); убрать двойной
check()на соединение (съедает 2 токена). std::sync::Mutexв async (DifficultyAdjusterвtunnel.rs) →tokio::sync::Mutex/атомика; whitelist-сравнение по строке → пре-парс IP/CIDR.replace_hostname: проверка длины подписанного hostname ≤ 255 (добавка сигнатуры выбивает длинные домены).- Physics: переделать на реальные данные позиций или удалить фейковый falling check; «re-verify» должен реально что-то проверять, а не дисконнектить.
- Redis:
KEYS→SCAN(manager +ServerRegistry), TTL на ключи серверов (иначе мусор копится), reconnect pubsub-подписчика (сейчас умирает навсегда).
P2 — Мёртвый код / конфиг
- Удалить или использовать:
max_connections_per_ip,rate_limit_status_pps,logging.level/format,ACTIVE_CONNECTIONS,BLACKLIST_SIZE,io-uring/tokio-splice,ClickHouseWriterбез вызовов. - Manager blacklist: хранить reason/created/expires, применять
duration_secs(сейчас фабрикуются фейковые поля). - CLI:
drain/emergencyиз заглушек → реальная логика или явныйunimplemented. - README: убрать неподтверждённые цифры (io_uring 110k, XDP 15–20M pps), привести в соответствие коду и TODO.
DoD этапа 0: все P0 закрыты, P1/P2 закрыты или явно задекларированы как «позже с issue», cargo test + cargo clippy -D warnings + Java build + Docker (с XDP) зелёные.
3. Anti-Regression — как не допускать
Каждая фича обязана пройти чеклист ниже. Мёртвый код, «бумажные слои» и дефолт-секреты = reject на ревью.
Правила
- No dead code: каждый
pubв prod-модуле имеет вызов вне#[cfg(test)]. Если компонент не вызывается — он не существует (CAPTCHA, ClickHouse, Layer 6). - Config field = потребитель: нет конфиг-поля без использования. Добавил поле — сразу потребитель (или не добавляй).
- Метрика регистрируется → обновляется: каждый Gauge/Counter имеет единственного «writer»; ревью проверяет, что
inc/setреально вызываются. - Feature flag = сборка в CI: любое
featureсобирается в CI (--all-featuresуже есть) и в Docker-образе. «Фича не в образе» = фичи нет. - По умолчанию безопасно: нет дефолтных секретов/паролей; отсутствие обязательного env = fail-fast, а не warn.
- Интеграционный тест на слой: PoW+handshake (симуляция ванильного клиента), XDP attach smoke, Redis sync, router по домену.
- Парсеры читают полный кадр: никогда «один read» для MC-пакета; неполный кадр = accumulate или отказ, но не молчаливый drop валидного клиента.
- Listener/Handler = вызывается: новый Java-listener или Rust-модуль подключается в
main/pluginonEnable, иначе reject. - CI guardrails (добавить в
.github/workflows/ci.yml):cargo clippy --all-targets --all-features -- -D warningscargo test(уже есть) + сборка XDP (clang -target bpf) + Docker build с--features xdp- grep-проверка отсутствия дефолт-секретов:
changeme,password = "в коде/конфигах - Java build (уже есть) +
./gradlew test
- README/TODO не врут: каждое заявленное число/слой имеет ссылку на код или тест. Нет — не пишем.
4. 6-слойная архитектура (план)
Layer 1: XDP/eBPF (C) TCP state machine, SYN throttle, blacklist, UDP drop
Layer 2: PoW Challenge (Rust) SHA256 hashcash, dynamic difficulty
Layer 3: Rust Core (Rust) MC handshake, HMAC sign, rate limit, death code
Layer 4: Velocity (Java) Domain whitelist, HMAC verify, physics, CAPTCHA
Layer 5: Paper Agent (Java) Redis heartbeat, auto-registration
Layer 6: Traffic Intel (Rust) EWMA thresholds, 168h profiling, reputation
5. Этапы разработки
Этап 4: XDP/eBPF — Layer 1 (сейчас)
Цель: Написать полноценный XDP фильтр с TCP state machine, исправив баги Minecraft-XDP-eBPF.
-
Изучен reference Minecraft-XDP-eBPF:
- Найден TCP handshake deadlock (pure ACK drop)
- Найден VarInt sign extension UB
- Найдена stale conntrack на RST/FIN
- Найден IPv6 bypass
- Найдена отсутствие LRU на player map
-
xdp/xdp_filter.c— TCP state machine (465 строк):- AWAIT_ACK → AWAIT_MC_HANDSHAKE → AWAIT_LOGIN → VERIFIED
- Исправление: pure ACK → PASS, не DROP
- Исправление: RST/FIN → удалять conntrack entry
-
xdp/maps.h— 6 BPF maps:conntrack_map(LRU_HASH, 16384)player_connection_map(LRU_HASH, 65535) — LRU, не plain HASHconnection_throttle(LRU_HASH, 65535) — SYN throttle per-IPblacklist_map(LPM_TRIE, 100000) — CIDR blacklistwhitelist_map(LPM_TRIE, 1000) — CIDR whiteliststats_map(PERCPU_ARRAY) — счетчики для Prometheus
-
xdp/protocol.h— парсеры Minecraft на C -
xdp/varint.h— VarInt (без sign extension UB) -
xdp/config.h— Runtime-конфигурация (volatile const) -
Rust loader (
crates/rampart-core/src/xdp/mod.rs):- Загрузка .o через libbpf-rs
- Attach XDP к интерфейсу через
bpf_xdp_attach ban_ip/unban_ip/get_statsметоды
-
build.rs— компиляция .c → .o (clang -target bpf) -
Пропатчить глобальные переменные из config.toml
-
Чтение ringbuf → blacklist events
-
BPF stats → Prometheus интеграция
-
Тесты:
hping3 -S --flood→ XDP дропает, CPU < 30%iperf3UDP flood → XDP дропает- TCP handshake проверка: Minecraft клиент коннектится без задержки
DoD: SYN flood 1M pps дропается в XDP, TCP handshake без deadlock, CPU < 30%, CI собирает xdp_filter.o
Этап 2b: PoW Challenge — Layer 2 (после XDP)
- Challenge generator: случайный token + timestamp + difficulty
- Dynamic difficulty: 4 (спокойно) → 12 (атака) по CPS
- Nonce verification: SHA256(challenge + nonce) prefix check
- Одноразовый challenge (token + timestamp, max 30 сек)
- Интеграция в rampart-core: PoW перед HMAC handshake
- Тесты: PoW solver timing, nonce replay защита, dynamic adjustment
DoD: Edge требует PoW перед handshake, бот не может флудить >50 handshake/сек
Этап 4b: Velocity Physics — Layer 4 (после PoW)
- Falling check (pre-computed cache:
(0.98^t-1)*3.92, 128 ticks)- Исправление: checkY() без fast-forward, сброс ignoredTicks
- Protocol check (Transaction, SetHeldItem, ArmAnimation)
- Vehicle check (Boat gravity + Minecart gravity)
- CAPTCHA (Map item или PoW как fallback)
- HMAC fingerprint (не hashCode!) для verified DB
- Idempotent finishVerification() (нет race condition)
Этап 6: Traffic Intelligence — Layer 6
- 168-hour traffic profiling (per-hour-slot baseline)
- EWMA adaptive thresholds (правильная variance формула)
- Z-Score anomaly detection (3 consecutive minutes)
- Attack detection (CPS/PPS thresholds)
- Reputation system (IP score -100..+100)
- Discord webhook на атаки
Этап 5: Observability
- ClickHouse writer (batch, раз в сек, буфер 1000)
- Grafana dashboard JSON
- Страница Attack Log в dashboard
Этап 6b: Scale + HA
- NATS JetStream (blacklist, drain, audit)
- mTLS между всеми компонентами (rustls)
- Auto-discovery edge нод
- rampart-cli:
drain,wg sync,add-node
Этап 7: Polish
- io_uring runtime (feature flag)
- Zero-copy splice после handshake
- SLSA Level 3: signed releases, reproducible builds
- secret rotation (dual-key HMAC)
6. Backlog
- Bedrock / RakNet (UDP модуль)
- Plugin API через WASM (как Infrarust)
- BGP Anycast (требует AS + /24)
- ML anomaly detection (Isolation Forest — многомерный, не univariate)
- Fuzzing для handshake parser (
cargo-fuzz) - Chaos engineering (random node kills)
7. Definition of Done
☐ cargo check / cargo test проходят
☐ cargo clippy -- -D warnings — 0 warnings
☐ cargo fmt --check проходит
☐ Unit тесты покрывают happy path + 2+ error cases
☐ Интеграционный тест проходит (PoW+handshake, XDP smoke, Redis sync)
☐ Нет мёртвого кода: каждый pub-модуль/конфиг-поле/метрика имеют потребителя
☐ Нет дефолтных секретов/паролей (grep-чек в CI)
☐ Docker-образ собирает те же features, что CI (включая XDP)
☐ README соответствует коду (нет «бумажных» цифр/слоёв)
☐ Документация обновлена
☐ CI зелёный
8. Anti-Patterns
❌ Тесты после кода. Пиши до (TDD) или вместе.
❌ Коммиты в main напрямую. Только PR.
❌ TODO в коде без issue. TODO = баг.
❌ Оптимизация без профиля.
❌ Зависимость ради 1 функции.
❌ async где хватит sync.
❌ Секреты в репозитории. Используй .env + SOPS.
❌ Игнор compiler warnings.
❌ Мёртвый код: pub без вызовов, конфиг-поле без потребителя, метрика без writer.
❌ «Бумажный слой»: фича в README/архитектуре, которой нет в коде или она не вызывается.
❌ Дефолтный секрет: `changeme`/`password="..."` в коде или конфиге.
❌ Парсер за «один read» — MC-пакет может прийти фрагментами.
❌ Feature flag, который не собирается в Docker/CI — фичи нет.
Статус секций 5–8: план на будущее. Актуальный приоритет — Аудит-фикс v0.3 (сек. 2): закрыть P0/P1/P2 до релиза.
Версия: 3.0 | Обновлён: август 2026 (аудит-фикс v0.3)