Cloudflare WARP на вашем VPS без официального клиента.
Нативный WireGuard + умный watchdog с авто-ротацией endpoint. Сделан для работы с Xray-core, Remnawave, 3X-UI и Marzban.
Хостинги блокируют, Cloudflare шейпит трафик из датацентров — WARP перестаёт работать. Стандартный warp-cli жирный и капризный.
VPS-WARP поднимает туннель через wg-quick и ставит systemd-watchdog, который каждые 3 минуты пингует 1.1.1.1, 8.8.8.8 и 9.9.9.9 через туннель. Если пинг не проходит или хэндшейк не обновлялся больше 3 минут — скрипт меняет endpoint на случайный из пула Cloudflare и перезапускает туннель. Тихо, без вашего участия.
- Нативный WireGuard — никаких проприетарных демонов Cloudflare
- Авто-ротация endpoint — при падении связи выбирает случайный адрес из
188.114.{96,97}.xна портах2408 / 500 / 4500 / 1701 - Изолированная маршрутизация —
Table = 51820+ правилоip rule fwmark 255. Основной трафик сервера не затрагивается: в туннель уходит только то, что помечено меткой 255 (см. Интеграция с Xray) - TCP MSS Clamping — правило в
mangle POSTROUTINGна исходящих черезwarp, MSS 1240 при MTU 1280. Предотвращает зависания на тяжёлых сайтах - Backoff при ротации — если Cloudflare заблокирован целиком, watchdog не дёргает туннель каждые 3 минуты, а наращивает паузу до 30 минут
- rp_filter — при строгом режиме (
=1, дефолт RHEL/Rocky/Alma) обратный трафик туннеля дропается ядром. Скрипт понижает до loose (=2) и закрепляет в/etc/sysctl.d/99-vps-warp.conf. На Ubuntu, где уже=2, ничего не трогает - Только IPv4 — IPv6-адреса и маршруты вырезаются из конфига, нет проблем с blackhole-роутингом у провайдеров
- Поддержка WARP+ — опционально, ввод лицензионного ключа при установке
- CLI-утилита
vps-warp— статус, трафик, ротация, обновление, удаление
bash <(curl -fsSL https://raw.githubusercontent.com/tagashi666/vps-warp/main/warp_install.sh)Запускать от root. Скрипт спросит язык и опционально ключ WARP+.
Поддерживаются apt / dnf / yum / zypper / pacman / apk / emerge. Нужен systemd — на OpenRC-системах туннель придётся поднимать вручную, watchdog работать не будет.
| Команда | Описание |
|---|---|
vps-warp |
Статус: IP туннеля, endpoint, handshake, состояние маршрутизации, трафик |
vps-warp start |
Запустить интерфейс |
vps-warp stop |
Остановить интерфейс |
vps-warp restart |
Перезапустить (endpoint сохраняется) |
vps-warp rotate |
Сменить endpoint на случайный и перезапустить |
vps-warp log |
Логи watchdog (история ротаций) |
vps-warp update |
Обновить до последней версии из репозитория |
vps-warp uninstall |
Полное удаление с подтверждением |
Строка Routing: в статусе показывает, на месте ли правило fwmark 255 → table 51820. Если там MISSING — трафик Xray уходит мимо туннеля, с реального IP сервера. Лечится через vps-warp restart.
Строка rp_filter: показывает эффективное значение — ядро берёт max(conf.all, conf.warp), поэтому смотреть на один только интерфейс бесполезно. STRICT (=1) означает, что обратный трафик дропается.
После установки скрипт выведет локальный IP туннеля (вида 172.16.0.x) и номер метки. Есть три способа направить трафик через WARP.
Работает вместе с политикой маршрутизации, которую ставит скрипт: пакеты с меткой 255 уходят в таблицу 51820.
{
"tag": "warp-out",
"protocol": "freedom",
"settings": {
"domainStrategy": "ForceIPv4"
},
"streamSettings": {
"sockopt": {
"mark": 255
}
}
}{
"tag": "warp-out",
"protocol": "freedom",
"settings": {
"domainStrategy": "ForceIPv4"
},
"streamSettings": {
"sockopt": {
"interface": "warp",
"tcpFastOpen": true
}
}
}Если sockopt не поддерживается вашей версией Xray:
{
"tag": "warp-out",
"protocol": "freedom",
"sendThrough": "<LOCAL_WARP_IP>",
"settings": {
"domainStrategy": "ForceIPv4"
}
}<LOCAL_WARP_IP> — IP, который вывел скрипт после установки.
{
"type": "field",
"domain": [
"geosite:openai",
"domain:instagram.com",
"domain:google.com"
],
"outboundTag": "warp-out"
}Если 255 конфликтует с чем-то на сервере — поменяйте WARP_FWMARK в шапке warp_install.sh перед установкой. Значение попадёт в конфиг WireGuard, в watchdog и в вывод статуса автоматически.
Xray/Sing-box
│
│ (трафик для заблокированных доменов, помечен fwmark 255)
▼
ip rule fwmark 255 → table 51820
│
▼
warp (wg-quick интерфейс, MTU 1280)
│
│ WireGuard UDP
▼
188.114.9{6,7}.x:{2408,500,4500,1701} ←── watchdog меняет endpoint при блокировке
│
▼
Cloudflare WARP → целевой ресурс
Watchdog (warp-watchdog.timer) срабатывает каждые 3 минуты:
- Проверяет, активен ли
wg-quick@warp. Если вы остановили туннель сами — молча выходит - Смотрит возраст последнего handshake (порог — 180 секунд)
- Пингует три DNS-резолвера с меткой 255, то есть ровно тем маршрутом, которым ходит Xray
- Если что-то не так — меняет
Endpointв/etc/wireguard/warp.confи делаетsystemctl restart wg-quick@warp - Считает подряд идущие неудачи. Пауза перед следующей ротацией растёт
3 → 6 → 12 → 24 → 30минут и сбрасывается, как только связь восстановилась
Смысл пятого шага: если Cloudflare недоступен целиком, ротация всё равно не поможет, а перезапуск WireGuard 480 раз в сутки — сам по себе заметный на потоке признак.
Туннель асимметричен by design: помеченные пакеты Xray уходят через warp (таблица 51820), а дефолтный маршрут сервера остаётся на основном интерфейсе. Расшифрованные ответы возвращаются на warp без метки — правило fwmark 255 не срабатывает, лукап уходит в main table, упирается в дефолт через основной NIC, входящий интерфейс не совпадает. Строгий rp_filter такой пакет отбрасывает.
Симптом коварный: wg show показывает свежий handshake, vps-warp пишет ACTIVE, а трафика через туннель нет.
wg-quick эту ситуацию не закрывает. Его sysctl net.ipv4.conf.all.src_valid_mark=1 живёт внутри add_default(), а add_route() попадает туда только при Table = auto или пустом Table. С явным Table = 51820 берётся другая ветка, и src_valid_mark не выставляется никогда.
Xray умеет отключать rp_filter сам, но делает это только в proxy/wireguard/tun_linux.go — на kernel-TUN пути встроенного WireGuard-протокола. То есть срабатывает лишь при outbound/inbound с "protocol": "wireguard", и внутри контейнера с read-only /proc/sys падает (failed to disable ipv4 rp_filter for all: read-only file system).
При нашей схеме Xray этот код не выполняет вообще: туннель ядерный, Xray ходит в него обычным freedom + sockopt.mark. Отсюда два следствия:
privileged: trueдля этой схемы не нужен. ДостаточноNET_ADMINв docker-compose ноды —SO_MARKтребуетCAP_NET_ADMIN, не более- rp_filter обязан настроить кто-то на хосте, потому что Xray про ядерный туннель ничего не знает. Этим и занимается скрипт
Если на ноде всё же используется встроенный WG-outbound Xray и он не стартует из-за rp_filter — помимо privileged: true есть вариант мягче: "noKernelTun": true в settings, откат на userspace-стек gVisor, которому привилегии не нужны. Ценой производительности.
Выбран loose (=2), а не 0: он по-прежнему отбрасывает мартиан и немаршрутизируемые источники, разрешая только асимметричный путь. Скрипт трогает только значение 1 — если вы осознанно поставили 0, оно останется.
| Путь | Что это |
|---|---|
/etc/wireguard/warp.conf |
Конфиг туннеля (0600, содержит приватный ключ) |
/etc/vps-warp/wgcf/ |
Рабочая директория wgcf: wgcf-account.toml |
/etc/vps-warp/version |
Установленная версия, для vps-warp update |
/etc/vps-warp/watchdog.state |
Счётчик неудач и время последней ротации |
/opt/vps-warp/watchdog.sh |
Скрипт watchdog |
/usr/local/bin/vps-warp |
CLI |
/usr/local/bin/wgcf |
Бинарь wgcf |
/etc/sysctl.d/99-vps-warp.conf |
Закреплённый rp_filter (создаётся только если он был строгим) |
vps-warp uninstallСнимет сервисы, удалит ip rule и правило MSS, погасит интерфейс, уберёт sysctl-файл и почистит все данные. Пакет wireguard остаётся установленным — при желании снесите руками.
MIT — LICENSE