Changelog
[0.8.0] - 2026-06-13
Added
- Multi-server pool / failover —
poolblock inserver.json; nodes share the same X25519 keypair; in-protocol UDP sync over the VPN port (ControlPayload::PoolSync0x12) — sync traffic is indistinguishable from client traffic, no extra port or firewall rule required; all nodes derive identicalSessionKeysfrom a sharedsync_keyvia blake3 KDF;aivpn-server enroll <peer>command for one-shot peer enrollment (aivpn-server/src/pool_sync.rs) - Client server pool — failover, round-robin, weighted, and latency-based selection; optional
poolJSON array inaivpn://connection key (backward-compatible — old clients ignore unknown fields) (aivpn-client/src/server_pool.rs) - OpenWRT native package + LuCI plugin —
aivpn-openwrt/package/aivpn/with procd init script, UCI config template, WAN hotplug restart;luci-app-aivpnweb UI with Status and Configuration tabs; OpenWRT setup guide ataivpn-openwrt/docs/openwrt-setup.md - Per-client QoS / bandwidth limiting — eBPF TC egress hook (
ebpf/tc_qos_prog.c) with LRU_HASHqos_rulesmap, token-bucket rate limiting and DSCP marking per client VPN IP; transparent userspace fallback when BPF absent;--set-client-qosCLI flag (aivpn-server/src/qos.rs) - Backup / migration tools —
--export <path.tar.gz>and--import <path.tar.gz>withmanifest.json; covers clients DB, mask files, and server config (aivpn-server/src/backup.rs) - eBPF observability stub — XDP/TC ring-buffer stats observer; attaches when
/sys/fs/bpf/aivpn_eventsis present; graceful no-op otherwise (aivpn-server/src/ebpf_observer.rs) - Structured event logging —
AivpnEventenum covering connect/disconnect, key rotation, XDP drops, peer sync, kill-switch;EventBuswith JSONL stdout sink and optional webhook (aivpn-common/src/event_log.rs) - Adaptive mode — 20-entry sliding window tracks per-connection packet loss; auto-adjusts
mtu_delta(−50 per step, floor 576) and keepalive multiplier;--adaptiveCLI flag; toggle in all UI clients: Windows egui panel, macOS menu popover, iOS settings row, Android options menu (aivpn-client/src/adaptive.rs) - Admin audit log — append-only JSONL at
/var/log/aivpn/audit.log(configurable via--audit-log); records actor, action, target, result, and ISO-8601 timestamp for every management operation (aivpn-server/src/audit_log.rs) - Benchmarking / Diagnostics — UDP RTT probes, P50/P95/P99 latency percentiles, throughput up/down, packet loss %, 0–100 quality score;
aivpn-client benchsubcommand; Diagnostics panel in Windows GUI, macOS popover, iOS sheet, Android options-menu dialog (aivpn-client/src/bench.rs) - eBPF XDP drop statistics —
xdp_prog.cnow maintains aBPF_MAP_TYPE_ARRAYmap (drop_stats, 4 slots:TOO_SHORT,TAG_EXPIRED,RESERVED,TOTAL) and a 256 KBBPF_MAP_TYPE_RINGBUF(events). All XDP_DROP paths call an inlinerecord_drop(reason)helper that atomically increments the counter and emits a ring-buffer event.ebpf_observer.rsopens the pinned map via rawBPF_OBJ_GET/BPF_MAP_LOOKUP_ELEMsyscalls (no new crate dependency) and emits deltaXdpDropevents on theEventBus(aivpn-server/src/ebpf_observer.rs,aivpn-linux-kernel/src/xdp_prog.c) - DNS-over-HTTPS proxy — optional in-server DoH forwarder (
feature = "dns"); binds UDP :53 on the VPN interface and tunnels queries via RFC 8484 POST to a configurable upstream (default Cloudflare); optional secondary fallback resolver;block_plain_dnsmode adds an nftables rule dropping UDP/53 to non-VPN interfaces so clients cannot bypass the proxy; config block"dns"inserver.json(aivpn-server/src/dns_proxy.rs) - Site-to-site VPN — two or more AIVPN server nodes can connect their local subnets without any VPN client software; peers advertise routes via
ControlPayload::RouteSync(0x13) using the same blake3 KDF as pool sync; outbound advertisements are sent every 30 s; incomingRouteSyncis authenticated against the configured peer list (exactIP:portmatch), each received subnet is validated against the peer'sremote_subnetsallowlist, dangerous prefixes (default route, loopback, link-local) are rejected, payload is bounded at 4 KiB / 64 subnets; config block"site_to_site"inserver.json(aivpn-server/src/site_sync.rs) - Multi-hop chain forwarding — transparent double-hop routing; the entry node decrypts client IP payloads and re-wraps them in
ControlPayload::ChainForward(0x14) encrypted with the pool shared key, relaying them to a configured exit node; the exit node injects the inner payload directly into its TUN device and routes to the internet; the client is never aware of the hop; config:pool.exit_nodeon the entry node,pool.exit_node_enabled: trueon the exit node (defaults tofalseto prevent open relay);pool.sync_keymust be a valid non-zero 32-byte key or the chain forwarder refuses to start (aivpn-server/src/chain_forwarder.rs) - mTLS-lite client certificate layer — optional ed25519-signed client certificates layered over the existing X25519 + PSK handshake; certificate is a compact 104-byte token (
client_pub_key[32] || expiry_ts_le[8] || ca_signature[64]) sent viaControlPayload::ClientCert(0x15);required: false(default) accepts PSK-only clients and verifies the cert when present;required: trueblocks all Data packets from a session until a valid cert is received; no new crate dependency (uses existinged25519-dalek); config block"mtls"inserver.json(aivpn-server/src/mtls.rs) - Protocol: three new control subtypes —
RouteSync = 0x13,ChainForward = 0x14,ClientCert = 0x15added toControlSubtypeenum and fully encoded/decoded inControlPayloadwith 4-byte LE length-prefix framing (aivpn-common/src/protocol.rs)
Security
- mTLS enforcement —
Session.mtls_okfield tracks per-session cert state; set tofalseat session creation whenmtls.required = true; flipped totrueonly on a validClientCertmessage; Data packets are silently dropped until the gate opens - Exit-node relay gating —
ChainForwardmessages are rejected unlessGatewayConfig.exit_node_enabledistrue(pool.exit_node_enabledin config); prevents any server from inadvertently acting as an open relay - RouteSync peer authentication — inbound
RouteSyncis matched against configured peer endpoints (IP:port); packets from unknown senders are dropped; each advertised subnet is checked against the peer's declaredremote_subnetsallowlist before anyip route addsubprocess is spawned; default routes, loopback, and link-local prefixes are unconditionally rejected; deserialization is bounded (4 KiB JSON / 64 subnets) - Zero sync_key guard — chain forwarder startup aborts with an error log if
pool.sync_keyis absent, malformed, or decodes to all-zero bytes
Changed
- Version bumped 0.7.0 → 0.8.0 across workspace
Cargo.toml, all crateCargo.tomlfiles, macOSInfo.plist, iOSApp/Info.plistandTunnel/Info.plist, macOS/iOS version strings, Androidversion_footer GatewayConfiggainsevent_bus: EventBusandqos_enforcer: Arc<QosEnforcer>(backward-compatibleDefaultimpl); also gainschain_forwarder: Option<Arc<ChainForwarder>>,mtls: Option<MtlsConfig>,exit_node_enabled: boolClientConfiggainsqos: Option<ClientQos>with#[serde(default)]— existingclients.jsonfiles are unaffectedPoolSyncConfiggainsexit_node: Option<String>andexit_node_enabled: Option<bool>ServerFileConfiggainssite_to_site: Option<SiteToSiteConfig>,mtls: Option<MtlsConfig>,dns: Option<DnsProxyConfig>(all#[serde(default)])- Server
--audit-logdefaults to/var/log/aivpn/audit.log aivpn-server/Cargo.tomladdsflate2 = "1"andtar = "0.4"for backup functionality; addsdns = ["reqwest"]feature
[0.8.0] — 2026-06-13
Добавлено
- Пул серверов / отказоустойчивость — блок
poolвserver.json; узлы используют общую пару ключей X25519; синхронизация встроена в основной VPN-протокол (ControlPayload::PoolSync0x12) через UDP-порт VPN — трафик синхронизации неотличим от клиентского, не требует отдельного порта или правила firewall; все узлы выводят одинаковыеSessionKeysиз общегоsync_keyчерез blake3 KDF; командаaivpn-server enroll <peer>для регистрации пира в один шаг (aivpn-server/src/pool_sync.rs) - Пул серверов на клиенте — режимы failover, round-robin, weighted и latency-based; опциональный массив
poolв JSON-ключеaivpn://(обратная совместимость — старые клиенты игнорируют неизвестные поля) (aivpn-client/src/server_pool.rs) - Нативный пакет OpenWRT + плагин LuCI —
aivpn-openwrt/package/aivpn/с init-скриптом procd, шаблоном UCI-конфига, hotplug-перезапуском при поднятии WAN; веб-интерфейсluci-app-aivpnс вкладками Status и Configuration; руководство по установкеaivpn-openwrt/docs/openwrt-setup.md - QoS / ограничение полосы пропускания на клиента — eBPF TC egress-хук (
ebpf/tc_qos_prog.c) с картойLRU_HASH qos_rules, token-bucket ограничением скорости и DSCP-маркировкой по VPN IP клиента; прозрачный userspace-fallback при отсутствии BPF; флаг CLI--set-client-qos(aivpn-server/src/qos.rs) - Инструменты резервного копирования и миграции —
--export <path.tar.gz>и--import <path.tar.gz>сmanifest.json; охватывают БД клиентов, файлы масок и конфигурацию сервера (aivpn-server/src/backup.rs) - Заглушка наблюдаемости eBPF — наблюдатель статистики через кольцевой буфер XDP/TC; подключается при наличии
/sys/fs/bpf/aivpn_events; graceful no-op при отсутствии (aivpn-server/src/ebpf_observer.rs) - Структурированное логирование событий — перечисление
AivpnEvent: подключение/отключение, ротация ключей, XDP-дропы, синхронизация пиров, kill-switch;EventBusс JSONL-выводом в stdout и опциональным вебхуком (aivpn-common/src/event_log.rs) - Адаптивный режим — скользящее окно из 20 пакетов отслеживает потери; автоматически корректирует
mtu_delta(−50 за шаг, минимум 576) и множитель keepalive; флаг CLI--adaptive; переключатель во всех UI-клиентах: Windows (egui панель), macOS (popover меню), iOS (строка настроек), Android (меню опций) (aivpn-client/src/adaptive.rs) - Аудит-лог администратора — append-only JSONL по пути
/var/log/aivpn/audit.log(настраивается через--audit-log); фиксирует субъект, действие, цель, результат и метку времени ISO-8601 для каждой операции управления (aivpn-server/src/audit_log.rs) - Бенчмарк / Диагностика — UDP RTT-зондирование, перцентили задержки P50/P95/P99, пропускная способность вверх/вниз, процент потерь, оценка качества 0–100; подкоманда
aivpn-client bench; панель Диагностика в Windows GUI, macOS popover, iOS sheet, Android диалог из меню опций (aivpn-client/src/bench.rs) - Статистика дропов eBPF XDP —
xdp_prog.cтеперь ведёт картуBPF_MAP_TYPE_ARRAY(drop_stats, 4 слота:TOO_SHORT,TAG_EXPIRED,RESERVED,TOTAL) и кольцевой буферBPF_MAP_TYPE_RINGBUFобъёмом 256 КБ (events). Все пути XDP_DROP вызывают инлайн-хелперrecord_drop(reason), атомарно увеличивающий счётчик и отправляющий событие в кольцевой буфер.ebpf_observer.rsоткрывает закреплённую карту через сырые syscallBPF_OBJ_GET/BPF_MAP_LOOKUP_ELEM(без новых зависимостей) и публикует дельта-событияXdpDropвEventBus(aivpn-server/src/ebpf_observer.rs,aivpn-linux-kernel/src/xdp_prog.c) - DNS-over-HTTPS прокси — опциональный встроенный DoH-форвардер (
feature = "dns"); слушает UDP :53 на VPN-интерфейсе и пробрасывает запросы через RFC 8484 POST к настраиваемому апстриму (по умолчанию Cloudflare); поддерживается опциональный запасной резолвер; режимblock_plain_dnsдобавляет правило nftables, блокирующее UDP/53 на не-VPN интерфейсах, чтобы клиенты не могли обойти прокси; блок конфигурации"dns"вserver.json(aivpn-server/src/dns_proxy.rs) - Сеть сайт-сайт (site-to-site VPN) — два или более узла AIVPN могут соединить свои локальные подсети без клиентского ПО; пиры обмениваются маршрутами через
ControlPayload::RouteSync(0x13), используя тот же blake3 KDF, что и пул-синхронизация; исходящие объявления отправляются каждые 30 с; входящийRouteSyncаутентифицируется по списку настроенных пиров (точное совпадениеIP:port), каждая полученная подсеть проверяется по allowlistremote_subnetsпира, опасные префиксы (маршрут по умолчанию, loopback, link-local) отклоняются, полезная нагрузка ограничена 4 КиБ / 64 подсети; блок конфигурации"site_to_site"вserver.json(aivpn-server/src/site_sync.rs) - Многоскачковая цепочка (multi-hop) — прозрачная маршрутизация через двойной скачок; входной узел расшифровывает IP-нагрузку клиента и переупаковывает её в
ControlPayload::ChainForward(0x14), зашифрованный общим ключом пула, и пересылает на выходной узел; выходной узел вводит внутреннюю нагрузку прямо в TUN-устройство и маршрутизирует в интернет; клиент не знает о промежуточном скачке; конфигурация:pool.exit_nodeна входном узле,pool.exit_node_enabled: trueна выходном (по умолчаниюfalse, чтобы не превратиться в открытый прокси);pool.sync_keyдолжен быть корректным ненулевым 32-байтным ключом, иначе chain forwarder не запустится (aivpn-server/src/chain_forwarder.rs) - Лёгкий mTLS (mTLS-lite) — опциональные ed25519-подписанные клиентские сертификаты поверх существующего X25519 + PSK-рукопожатия; сертификат — компактный токен в 104 байта (
client_pub_key[32] || expiry_ts_le[8] || ca_signature[64]), передаётся черезControlPayload::ClientCert(0x15);required: false(по умолчанию) принимает клиентов без сертификата и проверяет его при наличии;required: trueблокирует все Data-пакеты сессии до получения корректного сертификата; без новых зависимостей (используется существующийed25519-dalek); блок конфигурации"mtls"вserver.json(aivpn-server/src/mtls.rs) - Протокол: три новых управляющих подтипа —
RouteSync = 0x13,ChainForward = 0x14,ClientCert = 0x15добавлены в перечислениеControlSubtypeи полностью реализованы вControlPayloadс 4-байтовым LE-префиксом длины (aivpn-common/src/protocol.rs)
Безопасность
- Принудительный mTLS — поле
Session.mtls_okотслеживает состояние сертификата в рамках сессии; устанавливается вfalseпри создании сессии, еслиmtls.required = true; переключается вtrueтолько при получении корректного сообщенияClientCert; Data-пакеты сбрасываются до открытия ворот - Ограничение ретрансляции exit-узла — сообщения
ChainForwardотклоняются, еслиGatewayConfig.exit_node_enabledне равноtrue(pool.exit_node_enabledв конфиге); исключает случайное превращение сервера в открытый прокси - Аутентификация пиров RouteSync — входящий
RouteSyncсопоставляется с адресами настроенных пиров (IP:port); пакеты от неизвестных отправителей сбрасываются; каждая рекламируемая подсеть проверяется по allowlistremote_subnetsпира перед любым вызовомip route add; маршруты по умолчанию, loopback и link-local префиксы безусловно отклоняются; десериализация ограничена (4 КиБ JSON / 64 подсети) - Защита от нулевого sync_key — запуск chain forwarder прерывается с записью в лог об ошибке, если
pool.sync_keyотсутствует, некорректен или декодируется в последовательность нулевых байт
Изменено
- Версия поднята с 0.7.0 до 0.8.0 во всём workspace
Cargo.toml, всехCargo.tomlкрейтов, macOSInfo.plist, iOSApp/Info.plistиTunnel/Info.plist, строках версии macOS/iOS, Androidversion_footer GatewayConfigполучает поляevent_bus: EventBusиqos_enforcer: Arc<QosEnforcer>(обратносовместимая реализацияDefault); также получаетchain_forwarder: Option<Arc<ChainForwarder>>,mtls: Option<MtlsConfig>,exit_node_enabled: boolClientConfigполучаетqos: Option<ClientQos>с#[serde(default)]— существующиеclients.jsonне затронутыPoolSyncConfigполучаетexit_node: Option<String>иexit_node_enabled: Option<bool>ServerFileConfigполучаетsite_to_site: Option<SiteToSiteConfig>,mtls: Option<MtlsConfig>,dns: Option<DnsProxyConfig>(все с#[serde(default)])--audit-logпо умолчанию равен/var/log/aivpn/audit.logaivpn-server/Cargo.tomlдобавляетflate2 = "1"иtar = "0.4"для функционала резервного копирования; добавляет фичуdns = ["reqwest"]