Автоматизация Remnawave
Ноды, домены, сертификаты, файрвол, БС-релеи — из чата. Бот показывает план, делает по шагам и проверяет результат. Дальше по пунктам меню: как включить и обо что легко споткнуться.
Что делает
- Ставит ноду с нуля — от чистого VPS до работающей remnanode:
docker,
/opt/remnanode/, регистрация в панели и проверка, что контейнер жив, а ключ на месте. - Ведёт домены и DNS — A-записи в Cloudflare, свободный номер подбирает сам, умеет переселить базовый домен всего флота на новую зону.
- Держит два типа конфига — Reality или TLS-сертификат Let's Encrypt, выбор на уровне локации.
- Готовит сервер — apt, отключение IPv6, ufw, accelerator, traffic-guard: по одной ноде или сразу по всему флоту.
- Поднимает БС-серверы — HAProxy-релеи белого списка, со своим планом развёртывания и повтором с того же места.
- Управляет через API панели — список нод, вкл/выкл, рестарт xray.
- Знает структуру флота — локации, шаблоны имён и подписей правятся прямо из бота, без правки файлов на сервере.
Из чего состоит
- Бот — Python и aiogram 3, long polling. Входящие порты не нужны, поэтому ботов на одном сервере может жить сколько угодно.
- Слой операций — план, шаги и проверка результата: каждый шаг сначала смотрит, не сделан ли он уже.
- SSH — один вход на все команды, успех судится по коду возврата, а не по факту «команда доехала до конца».
- HTTP-клиенты — панель Remnawave и Cloudflare.
- Каталог
data/— настройки, структура флота и SSH-ключи. Лежит на хосте, переживает пересборку, в git не едет.
Что нужно
С чего начать
Запуск
git clone <репозиторий> && cd RWT-Automation/bot
./install.sh # спросит BOT_TOKEN и ADMIN_ID, поднимет контейнер
Репозиторий монтируется внутрь контейнера, поэтому кнопка «Обновить бота»
делает git pull прямо в этот каталог, а рестарт подхватывает
новый код.
Несколько ботов на одном сервере: каждому нужен свой клон, свой
data/, свой .env и свой
COMPOSE_PROJECT_NAME — иначе docker сочтёт их одним проектом.
Бот отвечает только администратору (ADMIN_ID плюс добавленные
в настройках). Остальным молчит — других уровней доступа нет.
Настройка
Для новой установки — четыре шага:
- Remnawave и Cloudflare — URL, токены, Zone ID. Кнопки «Проверить» делают живой запрос.
- Локации — достаточно кода (
NL): имя, подпись с флагом и префикс поддомена подставятся сами. - Схемы имён — если у вас другие: имя ноды, подпись host'а, поддомен, тег инбаунда.
- Настройки БС — профиль белого списка и шаблон имени хостов.
{num} — номер, который бот подбирает сам (первый свободный по
A-записям, хостам и нодам). Поэтому он обязателен во всех шаблонах имён.
Бот обновляется через git, а data/ не в репозитории. Пока конфиг
флота лежал в файле репозитория, обновление его затирало.
Доступ к серверам
Бот заходит по ключу (по умолчанию) или по паролю. Режим — свойство ноды: одни серверы могут быть на ключе, другие на пароле.
Ключи по умолчанию
Делать ничего не нужно: у каждого сервера свой ключ, его выдаёт bootstrap при добавлении. Утечка одного не открывает остальные.
Общий ключ — наследие старых версий: бот его больше не создаёт, только использует, пока файл есть. Экран 🔑 Ключи доступа показывает, кто ещё на нём, и раздаёт оставшимся свои.
Вход по паролю новое
Для готовых серверов, где ключа бота нет, а парольный вход работает. Бот запоминает пароль и ходит по нему: ключ не ставит, парольный вход не трогает.
| Когда | Где включить |
|---|---|
| При заведении | ➕ Добавить ноду → 🔐 По паролю |
| У существующей | Карточка → 🔐 Доступ и ключи → 🔐 Вход по паролю |
| Вернуть ключ | Там же → 🔑 Вернуть вход по ключу |
Пароль проверяется реальным заходом до сохранения: не пускает — режим не меняется, причина показывается. В карточке появляется строка «Вход: 🔒 по паролю».
Ключ или порт меняются только после того, как новый доказанно работает. Кнопка удаления общего ключа появляется, лишь когда он никому не нужен.
Ноды и установка
Список тянется из панели со статусом: 🟢 подключена · 🔴 нет · ⚪ выключена. Тап открывает карточку.
Меню карточки
| Группа | Что внутри |
|---|---|
| 🔐 Доступ и ключи | показать/скачать ключ, смена ключа и SSH-порта, вход по паролю |
| 🌍 Домен / Reality | смена домена, замена privateKey, переименование инбаунда |
| 🧰 Обслуживание | установка ноды, евро-нода, обновление remnanode |
| 🛠 Утилиты | apt, autoremove, IPv6 off, accelerator, traffic-guard |
Что делает установка
Выбираешь локацию, бот подбирает домен и показывает план — ничего не начнётся, пока не подтвердишь. Дальше по порядку:
- A-запись в Cloudflare (рано — чтобы DNS успел разойтись)
- подготовка сервера — только отмеченные галочками утилиты
- сертификат — только для cert-локаций, до remnanode
- remnanode: docker → compose up → проверка, что контейнер жив
- профиль ноды в панели и привязка к сквадам
- selfsteal — для Reality-локаций
- регистрация ноды и host в панели, затем перезагрузка
Галочки подготовки: 📦 apt · 🚫 IPv6 off · 🛡 файрвол · 🚀 accelerator · 🧿 traffic-guard. По умолчанию отмечено всё. Порядок задан в коде: traffic-guard идёт после ufw, поэтому его отметка сама включает файрвол.
Установка идемпотентна: каждый шаг сначала смотрит, не сделан ли он уже. Упало посередине — запускай заново.
Идёт в две фазы: сервер слушает старый и новый порт сразу, бот проверяет вход по новому — и только потом закрывает старый. Не сработало — откатывается сам.
Тип конфига локации новое
Тип решает, какой профиль бот сгенерирует ноде и что подготовит на сервере. Переключается в карточке локации.
| Тип | Конфиг | Установка |
|---|---|---|
| 🕵️ Reality дефолт | VLESS Reality, домен ноды в serverNames | ставит сайт-прикрытие (caddy/nginx) |
| 🔐 TLS-сертификат | VLESS + настоящий TLS, Let's Encrypt | выпускает сертификат, монтирует его в контейнер, selfsteal не ставит |
Reality — значение по умолчанию: у существующих локаций поля нет, поэтому они работают как раньше.
Что делает cert-локация
Сертификат выпускается до remnanode — xray читает его на старте:
certbot certonly --standalone --keep-until-expiring -d <домен>
Затем профиль генерится с путями /etc/letsencrypt/live/<домен>/,
а контейнер поднимается с volume /etc/letsencrypt:ro. Флаг
--keep-until-expiring делает шаг идемпотентным: живой сертификат не
перевыпускается. Продление — таймером certbot.
Порт 80 свободен и открыт (правило ufw бот добавит сам), A-запись — DNS-only, без оранжевого облака: через прокси ACME не достучится. Порт 443 занимает xray, поэтому selfsteal здесь несовместим.
Почта для certbot — ⚙️ Настройки → Ноды → Email для Let's Encrypt (пусто — регистрация без почты).
Xray-шаблоны
Каждая нода ставится на свой профиль — он создаётся при установке из шаблона. Шаблоны правятся в 🧬 Xray → Шаблоны: пришли JSON файлом или текстом, бот проверит рендеринг и сохранит. Пусто — встроенный дефолт.
Обычный конфиг — Reality-локации
{{domain}}{{tag}}{{privateKey}}
{{publicKey}}{{shortId}}{{fingerprint}}
{{dest}}{{xver}}{{port}}
TLS-сертификат — cert-локации новое
{{domain}}{{tag}}{{port}}
{{fallbackPort}}{{certDir}}{{keyFile}}
{{certFile}}{{fingerprint}}
Мост — цепочка из двух нод
{{entry_domain}}{{exit_address}}
{{client_id}}{{outbound_tag}}
{{flow}}
Строковые значения берутся в кавычки в самом шаблоне, числовые — без:
"serverName": "{{domain}}",
"port": {{port}}
Встроенный cert-дефолт даёт один VLESS-TCP-TLS инбаунд. Свой шаблон нужен, если инбаундов должно быть несколько (например ещё grpc).
Генератор шаблона прямо здесь
Переменные в шаблон бот сам не подставит: он делает текстовую замену
{{domain}} на значение, а догадаться, что
yt1.example.com в вашем конфиге — это «домен ноды», не может.
Загрузите конфиг как есть — домен останется константой, и все ноды получат
один и тот же.
Инструмент ниже делает это за вас: вставьте рабочий конфиг, он найдёт места, которые меняются от ноды к ноде, и заменит их плейсхолдерами. Всё считается прямо в браузере, никуда не отправляется.
Профиль создаётся на каждую ноду свой, и домен у ноды один. Если в
конфиге инбаунды на yt1, yt2, yt3 —
подставлять {{domain}} во все три нельзя: получится три
одинаковых инбаунда на одном порту. Оставьте в шаблоне один инбаунд
(или несколько, но на разных портах и с разными тегами).
Файрвол и массовые действия
Файрвол — единый поток и на одной ноде, и массово: цель (веб · SSH · порт панели · свой порт) → протокол (TCP / UDP / оба). ufw ставится автоматически, если его нет.
Включение по политике «минимум»: всё входящее закрыто, кроме текущего SSH-порта. Порт панели по возможности открывается только для её IP.
Массовые действия — те же операции по всем нодам с отчётом по строке на ноду. Разрушающие — с подтверждением. Вкл/выкл ноды только индивидуально.
Отдельно — перевод на ключ: бот идёт по серверам, заходит по паролю,
ставит каждому свой ключ и отключает парольный вход. Для каждого можно ввести
пароль, повторить прошлый (.) или пропустить (-).
БС-серверы
БС — не нода Remnawave, а прозрачный TCP-релей: HAProxy слушает порты инбаундов и по SNI пробрасывает трафик на евро-ноды, где терминируется Reality.
клиент → [ БС: HAProxy, SNI ] → [ евро-нода: Reality ] → интернет
Поэтому у БС нет xray и «включить в панели», а DNS для него не создаётся — клиенты ходят на IP. Конфиг не хардкодится: бот читает профиль из панели и строит из него frontend'ы, список SNI и backend.
Добавили евро-ноду в профиль — 🔁 Пересобрать балансир подхватит её. Сама евро-нода заводится кнопкой 🛡 Евро-нода: без DNS и host, чтобы не светить её в обход белого списка.
Обновление бота
Бот обновляет себя сам: git pull --ff-only и перезапуск. Источник —
ветка или latest (последний релизный тег), задаётся в
🤖 Бот → Репозиторий вместе с URL и токеном.
Раз в 3 минуты бот проверяет новую версию: если автообновление выключено — пришлёт уведомление с кнопкой и списком изменений, если включено — обновится сам и напишет об этом.
Если репозиторий принадлежит другому пользователю, а вы соавтор, нужен classic PAT: fine-grained его не видит, и git падает с 403.
Данные и секреты
| Путь | Что внутри | В git |
|---|---|---|
bot/.env | токен бота, ID админа | нет |
data/settings.json | токены панели и Cloudflare, схемы имён | нет |
data/locations.json | структура флота | нет |
data/nodes.json | ноды и БС, пути к ключам, пароли | нет |
data/keys/ | приватные SSH-ключи серверов | нет |
node/docker-compose.yml | шаблон, который льётся на ноду | да |
Весь data/ и .env — секреты: токены,
SECRET_KEY нод, приватные ключи, пароли серверов. Они gitignored,
но проверяйте индекс.
Каталоги bot/ и node/ нельзя двигать друг относительно
друга: шаблон compose читается по относительному пути.
Проблемы
apt вернул код 100
На свежем VPS unattended-upgrades держит dpkg-лок первые минуты.
Бот кладёт на сервер конфиг с таймаутом, и apt начинает ждать лок вместо
падения. Если всё равно упало — просто повторите шаг.
«Нет своего ключа, а общего больше нет»
Доступа к серверу у бота нет вообще. Варианты: завести ключ по паролю, включить вход по паролю или указать свой ключ в настройках.
Certbot не выпустил сертификат
Проверьте: A-запись указывает на этот сервер, она DNS-only, порт 80 свободен и доступен снаружи.
git pull не удался: diverged
Локальная история разошлась с удалённой. Сначала бэкап, потом сброс:
git branch backup/local
git reset --hard origin/main
Панель не видит ноду
Смотрите порт: он задаётся в настройках и подставляется и в compose ноды, и в правило ufw. Если порт открыт только для IP панели — проверьте, что IP актуален.
Внутри
Точка входа — bot.py. Операции (nodeops.py,
bsops.py) стоят поверх двух обёрток: SSH-слоя и HTTP-клиентов
панели и Cloudflare. Мимо них ходить нельзя.
Принципы
- Проверяем результат, а не факт выполнения — перечитываем файл, а не верим коду возврата.
- Идемпотентность — каждый шаг сначала смотрит, не сделан ли он уже.
- Опасное — после доказательства — старый ключ снимается, когда новый уже работает.
- Одно место правды — меню строятся из реестра действий, порт живёт в настройках, выбор «ключ или пароль» решается в одной функции.