Малена подключает голографического чат-бота к серверу через отдельный модуль с оранжевым кабелем

Статья

Раздельная маршрутизация на Ubuntu Server

Как я с помощью нейросети разбирался с подключением Telegram-бота и APT и настроил выборочное использование VPN на Ubuntu Server.

1

материал

Статья

формат

Опубликован

статус курса

Эта статья выросла из возни с моим VPS. Малена, мой Telegram-бот на Python, перестала нормально связываться с серверами Telegram. Заодно пришлось разбираться с DNS и загрузкой пакетов через APT. На VPS работали и другие службы, а доступ по SSH терять совсем не хотелось.

С Маленой можно пообщаться в Telegram. Кстати, на обложке тоже она =) А если захочешь сделать своего Telegram-бота, скоро в Академии появится мини-курс по их вайбкодингу.

В моём случае помогло выборочное использование VPN: запросы бота и APT пошли через него, а SSH и остальные службы сохранили обычный маршрут. Я настроил на VPS локальный прокси Xray, который принимал запросы нужных приложений и передавал их удалённому узлу из VPN-профиля.

Я описываю свой опыт раздельной маршрутизации трафика приложений на сервере. Материал не предназначен для рекламы средств обхода блокировок в Российской Федерации или призывов использовать их для этой цели.

Как мне помогла нейросеть

Я разбирался с сервером вместе с нейросетью: описывал проблему, получал команду, выполнял её в консоли и возвращал вывод в чат. По нему мы выбирали следующую проверку или правку. Так и дошли до рабочего результата.

Описать проблему → получить команду и понять её → выполнить → показать результат → выбрать следующий шаг.

Если у тебя есть доступ к нейросети, возможно, проще сразу дать ей эту статью и описать свои симптомы. Например: «Помоги выяснить, похожа ли моя проблема на описанную. Давай по одной команде, объясняй, что она проверяет или меняет, и жди моего вывода». Так у вас уже будет пример, с которым можно сравнить ситуацию на твоём сервере.

Перед запуском уточняй смысл непонятных команд, особенно если они меняют сеть или удаляют данные. А из вывода для чата убирай пароли, токены и ссылки подписок.

Перед началом

У меня стояла Ubuntu Server 24.04. Чтобы проверять команды на своём сервере, нужно уметь подключаться по SSH, редактировать файлы и выполнять команды с sudo. Команды для сервера написаны для Bash, а пример переноса файла с Windows — для PowerShell.

Перед изменениями открой вторую SSH-сессию и проверь доступ к консоли VPS через панель хостера. Она пригодится, если обычное подключение пропадёт.

Для подключения нужен рабочий профиль своего или разрешённого для использования удалённого сервера. Если у тебя купленная подписка, ключ или ссылка для импорта, сначала посмотри как получить конфигурацию из подписки. Сам удалённый сервер здесь не разворачиваем — подключаемся к готовому.

Для первых запросов используем example.com. Когда схема заработает, проверим нужный тебе сервис: у разных адресов доступность может отличаться.

Разбор разделён на восемь шагов: от диагностики до перезагрузки. Дополнительные проверки собраны в справочнике в конце, а ссылки на них стоят рядом с соответствующими шагами. Проходить весь справочник подряд не нужно.

Как разделяется трафик

Путь запроса состоит из двух участков. Сначала приложение обращается к Xray на том же VPS. Затем Xray передаёт запрос через подключение из VPN-профиля. То есть локальный прокси принимает запрос, а VPN-подключение определяет, куда он пойдёт дальше.

Это выборочная маршрутизация через VPN на уровне приложений. По смыслу — раздельное туннелирование: мы выбираем, какой трафик отправлять через удалённый узел. Для этого указываем адрес прокси только нужным приложениям. Общий VPN-интерфейс не создаём, маршрут Linux не меняем — используем входы Xray и настройки своего профиля.

Кто отправляет запросКуда подключаетсяДальнейший путь
Приложение с SOCKS5127.0.0.1:10808Xray → узел из VPN-профиля → сервис
APT или HTTP-клиент127.0.0.1:10809Xray → тот же узел из VPN-профиля → сервис
SSH и приложения без проксиОбычный сетевой интерфейсПрежний маршрут VPS

127.0.0.1 означает «этот же сервер». Числа 10808 и 10809 — порты, на которых Xray принимает подключения. SOCKS5 и HTTP — два способа общения с прокси: приложению подойдёт тот, который поддерживает его библиотека, а для APT используем HTTP.

Пример рассчитан на приложения, запущенные прямо на VPS и умеющие работать с прокси. Для приложений в контейнерах есть отдельная оговорка.

1. Находим место сбоя

Проверяем ресурсы сервера

Перед изменением сети смотрим, не закончились ли ресурсы:

uptime
free -h
df -h
df -i
ps aux --sort=-%cpu | head -15
ps aux --sort=-%mem | head -15

Смотрим, хватает ли памяти и места, не забрал ли один процесс все ресурсы. У df -i отдельная задача: проверить запас inode — записей, необходимых для создания файлов. Они могут закончиться, даже когда свободное место ещё есть. Нагрузку из uptime оценивай с учётом числа процессоров: она может расти и из-за ожидания диска.

Если проблема видна здесь, сначала разбираемся с ней. Если ресурсов хватает, переходим к соединению.

Смотрим, на чём останавливается запрос

Теперь посмотрим, что происходит с обычным HTTPS-запросом. Сначала сервер узнаёт IP по имени через DNS, затем устанавливает TCP-соединение, договаривается о шифровании через TLS и получает HTTP-ответ. Вывод curl помогает увидеть, на каком этапе всё остановилось:

curl -4 -v --noproxy '*' \
  --connect-timeout 5 --max-time 15 \
  https://example.com

-v показывает ход запроса. --noproxy '*' заставляет curl идти напрямую, даже если в окружении задан прокси. Ограничения в 5 и 15 секунд не дадут проверке зависнуть надолго. С -4 проверяем только IPv4; IPv6 этим запросом не проверяется.

Что видно в выводеЧто проверять дальше
Could not resolve hostРазрешение имени
Resolving timed outDNS-сервер и локальный резолвер
Connection timed outДоступность адреса и порта, маршрут, firewall и удалённый сервер
SSL connection timeoutУстановление TLS, состояние сети и удалённой стороны
HTTP 200, 302, 401, 403Ответ уже дошёл до HTTP; дальше важен его смысл
SOCKS5 authentication failedРеквизиты и метод авторизации прокси

Сам по себе тайм-аут не объясняет причину сбоя: мы лишь знаем, что не дождались нужного этапа. А 401 или 403 означают, что HTTP-ответ уже получен — возможно, сервис требует авторизацию. 302 сообщает о перенаправлении. Во всех этих случаях работу самого API ещё нужно проверить.

Если ошибка связана с именем хоста или ты не уверен в своём DNS, начни с проверки и замены адресов DNS. Отдельно разобраны запрос с заданным вручную IP и проверка готового SOCKS5-прокси. Если адрес разрешается, а проблема дальше по соединению, продолжай основной путь.

Для диагностики используй запрос без токена. Прежде чем передать кому-либо вывод curl, проверь, нет ли в нём секретов, в том числе в заголовках.

2. Готовим профиль и устанавливаем Xray

Сначала нужен рабочий профиль

Xray нужны адрес удалённого узла, порт, протокол, реквизиты и настройки соединения. Всё это хранится в JSON-конфиге. В зависимости от профиля там могут встретиться VLESS, TLS, REALITY или WebSocket. Здесь берём уже рабочие значения из своего профиля.

Ключ, подписка или ссылка для импорта не обязательно содержат готовый JSON для Xray. Попробуй экспортировать конфиг из клиента или запросить его у администратора сервиса. Если нужно разобраться с форматом, загрузкой JSON или выбором профиля, это описано в разделе о подписках.

Начинать проще с одного удалённого выхода. Цепочки, FakeDNS и балансировщики добавляют зависимости, которые тоже придётся проверить.

Храни конфиг и ссылку подписки как пароли: в них могут быть ключи, UUID и другие реквизиты. Не отправляй их в публичный репозиторий, чат или онлайн-валидатор.

Скачиваем и распаковываем Xray

Сначала пробуем скачать Xray с GitHub прямо на VPS. У меня загрузка с сервера не проходила, поэтому пришлось скачать архив на свой компьютер и передать его по SSH через настроенный VS Code. Это запасной путь: если VPS скачивает файл сам, перенос с компьютера не нужен.

В примерах пользователь сервера — absolute, а папка Xray — /home/absolute/xray. Замени их на свои во всех командах, в том числе в файле службы ниже. Пользователь должен уже существовать. Службу назовём app-xray.

Проверяем архитектуру сервера:

uname -m

Для x86_64 нужен Xray-linux-64.zip. Для ARM выбери подходящий архив в официальных релизах Xray-core и замени имя файла в командах.

Дальше показана установка из архива. Скачаем ли мы его прямо на VPS или перенесём с компьютера, папка и последующие команды будут одинаковыми. Здесь же есть готовый официальный установщик, но он использует свои пути и службу.

Подготовим папку на VPS под пользователем absolute:

umask 077
mkdir -p /home/absolute/xray
cd /home/absolute/xray

Загрузка прямо на VPS

Попробуем скачать архив релиза с отметкой Latest на GitHub. Для конкретной версии подставь ссылку на её ZIP из официальных релизов. Так мы проверим, доступен ли сам файл: открывающейся главной страницы GitHub для этого недостаточно.

curl -fL --connect-timeout 10 --max-time 120 \
  https://github.com/XTLS/Xray-core/releases/latest/download/Xray-linux-64.zip \
  -o /home/absolute/xray/Xray-linux-64.zip

Если команда завершилась без ошибки и архив скачан, переходи к проверке и распаковке. Если нет, прочитай сообщение curl. Например, 404 может означать ошибку в ссылке. Если проблема именно в доступе с VPS, попробуй перенос с компьютера, как в моём случае.

Если архив не скачивается с сервера

Скачай тот же архив на свой компьютер с официальной страницы релизов. Передать его на VPS можно удобным тебе способом:

  • Через приложение. Я использовал VS Code с подключением по SSH. В своём клиенте с поддержкой загрузки файлов или SFTP открой удалённую папку /home/absolute/xray и загрузи туда ZIP. Если доступно перетаскивание, перенеси файл из локального проводника в эту папку. Точные действия зависят от клиента; про подключение по SSH можно прочитать в документации VS Code.
  • Через консоль. На Windows можно открыть PowerShell в папке со скачанным архивом и выполнить:
scp .\Xray-linux-64.zip absolute@SERVER_IP:/home/absolute/xray/

Замени SERVER_IP на адрес VPS и проверь имя пользователя. Команда передаст архив в подготовленную папку по защищённому соединению. После передачи вернись в терминал сервера.

Проверка и распаковка архива

Сверь SHA-256 архива с контрольной суммой, опубликованной для того же релиза. На сервере хеш показывает sha256sum /home/absolute/xray/Xray-linux-64.zip, на Windows — Get-FileHash .\Xray-linux-64.zip -Algorithm SHA256. Важно именно сравнить значения, а не просто вычислить хеш.

Если обновляешь существующую установку, перед распаковкой сохрани рабочую версию и конфиг.

Независимо от способа загрузки архив теперь лежит в /home/absolute/xray. Распаковываем и смотрим версию:

cd /home/absolute/xray
python3 -m zipfile -e Xray-linux-64.zip .
chmod +x xray
./xray version

Python позволяет распаковать ZIP, даже если unzip пока не установлен. На этом установка из архива закончена: переходи к настройке входов.

Сохрани вывод xray version: при ошибке он поможет проверить совместимость профиля с версией Xray. Версия Ubuntu сама по себе не определяет версии Xray и Python-библиотек.

Готовый установщик Xray

Если Xray уже распакован по шагам выше, этот подраздел пропусти. Готовый скрипт Xray-install можно выбрать вместо ручной установки, если загрузка с GitHub работает:

curl -fL --connect-timeout 10 --max-time 60 \
  https://raw.githubusercontent.com/XTLS/Xray-install/main/install-release.sh \
  -o /tmp/install-xray.sh
less /tmp/install-xray.sh

После проверки скрипта:

sudo bash /tmp/install-xray.sh install

Установщик обычно помещает программу в /usr/local/bin/xray, конфигурацию — в /usr/local/etc/xray/ и создаёт службу xray.service. Проверь её настройки через systemctl cat xray, включая права пользователя на чтение конфига.

Если выбрал этот способ, подставляй его пути в следующие команды и используй уже созданную службу. Второй экземпляр Xray на тех же портах запускать не нужно.

3. Настраиваем входы для приложения и APT

В конфиге Xray используются три названия: inbound — вход, outbound — выход к удалённому узлу, routing — правила между ними. Добавим входы для приложений и укажем, в какой выход направлять их запросы.

Сохрани рабочий профиль как /home/absolute/xray/app-xray.json. До изменения сделай защищённую копию с новым именем, не перезаписывая прежнюю:

cd /home/absolute/xray
chmod 600 app-xray.json
cp -p -n app-xray.json app-xray.before-proxy.json
nano app-xray.json

Файл откроется в редакторе nano. После правок нажми Ctrl+O → Enter → Ctrl+X: сохранить, подтвердить имя файла, выйти. Ctrl+C для этого не подходит. Так же сохраняются остальные файлы ниже. Краткая памятка — в документации nano.

Добавим два входа: SOCKS5 на порту 10808 и HTTP на 10809. Ниже только фрагмент для массива inbounds; рабочий профиль целиком он не заменяет:

{
  "inbounds": [
    {
      "tag": "socks",
      "listen": "127.0.0.1",
      "port": 10808,
      "protocol": "socks",
      "settings": {
        "auth": "noauth",
        "udp": false
      }
    },
    {
      "tag": "apt-http",
      "listen": "127.0.0.1",
      "port": 10809,
      "protocol": "http",
      "settings": {}
    }
  ]
}

Если inbounds уже есть, измени его содержимое — второй ключ с тем же именем добавлять не нужно. Теги и порты не должны повторяться. Остальную часть рабочего профиля сохрани: outbounds, streamSettings, routing, DNS и связанные файлы.

Оба входа слушают 127.0.0.1. Не меняй его на 0.0.0.0: без аутентификации прокси может стать доступен посторонним. При этом другие процессы на самом сервере смогут к нему обратиться. Если на VPS есть пользователи, которым ты не доверяешь, понадобятся дополнительные ограничения. Подробности настроек — в документации SOCKS и HTTP.

Связываем входы с нужным выходом

Допустим, у рабочего удалённого outbound уже есть "tag": "proxy". Тогда в routing.rules можно поставить первым такое правило:

{
  "type": "field",
  "inboundTag": ["socks", "apt-http"],
  "outboundTag": "proxy"
}

Правило читается так: «Всё, что пришло через socks или apt-http, отправить в выход proxy». Добавляем его как элемент массива routing.rules. Имя proxy должно совпадать с тегом рабочего удалённого выхода — переименование прямого выхода freedom в proxy не превратит его в удалённый.

Xray читает правила сверху вниз и выбирает первое подходящее. Если совпадений нет, используется первый outbound. Поэтому после настройки обязательно проверим реальный запрос. Подробнее — в документации маршрутизации.

Это правило подходит, когда весь трафик двух входов нужно отправлять в один выход. Если в профиле есть исключения для DNS или внутренних адресов, балансировщик, FakeDNS либо свой скрипт маршрутизации, сначала проверь их логику и порядок. Иначе новое правило может сработать раньше нужного исключения.

4. Проверяем, что запрос проходит через прокси

Сначала попросим Xray проверить конфиг, не запуская службу:

cd /home/absolute/xray
./xray run -test -config ./app-xray.json

Если всё в порядке, команда завершится успешно; обычно появится Configuration OK.. Если нет — читаем ошибку и проверяем JSON, теги, версию Xray и связанные файлы. Соединение с удалённым узлом эта команда ещё не проверяет.

Запускаем вручную:

./xray run -config ./app-xray.json

Оставляем Xray работающим в первом терминале. Во втором смотрим, открылись ли нужные порты:

sudo ss -ltnp '( sport = :10808 or sport = :10809 )'

В списке должны появиться 127.0.0.1:10808 и 127.0.0.1:10809, открытые нашим процессом. Если порт уже занят, сначала выясни, какой программой. Не останавливай неизвестную службу наугад.

Теперь сравним внешний адрес прямого и проксированного запроса:

curl -4 --noproxy '*' --max-time 10 https://api.ipify.org
curl --noproxy '' --proxy socks5h://127.0.0.1:10808 \
  --max-time 20 https://api.ipify.org

Первая команда показывает внешний IP прямого запроса, вторая — запроса через прокси. Разные адреса помогают подтвердить, что пути отличаются. Если адрес совпал, проверь правило и выбранный outbound. Совпадение не обязательно означает ошибку: оба подключения могут выходить в интернет через общий NAT-шлюз.

Если прямой запрос не проходит, адрес из панели VPS даст ориентир. Но адрес входящих подключений иногда отличается от исходящего, поэтому такое сравнение не всегда точно. Сервис проверки IP видит адрес и время запроса; при необходимости используй свой диагностический адрес.

Теперь проверим HTTPS-запрос и выведем только код ответа и время:

curl --noproxy '' --proxy socks5h://127.0.0.1:10808 \
  --max-time 20 -sS -o /dev/null \
  -w 'HTTP: %{http_code}, Time: %{time_total}s\n' \
  https://example.com

После example.com подставь адрес своего API без секретов в URL, затем проверь обычное действие приложения. Именно оно покажет, решена ли исходная проблема. Если curl вывел 000, HTTP-ответ получить не удалось.

5. Настраиваем автозапуск

Ручной запуск помог проверить соединение. Теперь поручим systemd запускать Xray вместе с сервером и перезапускать при сбое. Для нашей установки из архива создаём файл службы:

sudo nano /etc/systemd/system/app-xray.service
[Unit]
Description=Application Xray Proxy
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
User=absolute
WorkingDirectory=/home/absolute/xray
ExecStart=/home/absolute/xray/xray run -config /home/absolute/xray/app-xray.json
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
UMask=0077

[Install]
WantedBy=multi-user.target

После вставки сохрани файл: Ctrl+O → Enter, затем выйди из nano через Ctrl+X.

У пользователя из User должны быть права на чтение конфига и запуск программы. Для прослушивания портов выше 1024 права root не нужны. А network-online.target определяет порядок запуска: доступность внешнего API он не гарантирует.

Останавливаем ручной Xray через Ctrl+C, затем запускаем службу:

sudo systemctl daemon-reload
sudo systemctl enable --now app-xray
systemctl status app-xray --no-pager
sudo journalctl -u app-xray -n 50 --no-pager

Повтори запрос через SOCKS5: статус active означает, что служба работает, но не подтверждает соединение с нужным сервисом. Если процесс падает, смотри журнал. Частые причины — неверный путь, недостаточные права, занятый порт или ошибка в конфиге. Перед публикацией журнала проверь его на секреты.

6. Подключаем приложение

Осталось указать прокси самому приложению. Обычно это делается в настройках HTTP-библиотеки. Адрес SOCKS5-входа — socks5://127.0.0.1:10808.

Проверь документацию своей библиотеки: поддерживает ли она SOCKS, нужны ли дополнительные пакеты и кто разрешает DNS-имена. Написание socks5h из curl подходит не всем клиентам.

Для небольшой проверки на Python можно обойтись без дополнительных пакетов. Используем второй вход — HTTP — и встроенный urllib.request:

from urllib.request import ProxyHandler, build_opener

proxy = "http://127.0.0.1:10809"
opener = build_opener(ProxyHandler({"http": proxy, "https": proxy}))

with opener.open("https://example.com", timeout=20) as response:
    print("HTTP:", response.status)

Через прокси пойдут запросы, сделанные этим opener. Другие процессы продолжат работать как раньше. Исключение — домены из no_proxy: urllib может обратиться к ним напрямую. Это поведение описано в документации Python 3.12.

В своём приложении задай прокси для HTTP-сессии, которая отправляет запросы. При остановке приложения сессию нужно закрывать. Токен API оставь в прежнем хранилище секретов. Проверь обычный запрос, затем перезапусти службу, заменив my-app на её настоящее имя:

sudo systemctl restart my-app
sudo journalctl -u my-app -n 50 --no-pager

Проверь, возобновляет ли приложение запросы после временного сбоя сети. Если задавал переменные в SSH-терминале, учти: systemd-служба не получает их автоматически.

7. Восстанавливаем загрузку пакетов через APT

У меня перестала нормально работать и загрузка пакетов через APT — менеджер пакетов Ubuntu. Ему тоже понадобился отдельный сетевой маршрут. Если на твоём сервере APT скачивает обновления без ошибок, этот шаг можно пропустить.

Если ошибка связана с разрешением имени, сначала проверь DNS. Если с доступом к зеркалу — попробуй загрузку через уже настроенное VPN-подключение. APT умеет работать через HTTP-прокси, поэтому подойдёт второй вход Xray. Начнём с файла со сведениями о пакетах Ubuntu 24.04:

curl --noproxy '' --proxy http://127.0.0.1:10809 \
  --max-time 20 -sS -o /dev/null \
  -w 'HTTP: %{http_code}, Time: %{time_total}s\n' \
  https://archive.ubuntu.com/ubuntu/dists/noble/InRelease

Для этого файла ожидаем 200. Если используется другое зеркало, проверяй его адрес из настроек APT. Успех на example.com ничего не говорит о доступности конкретного зеркала.

Если файла с таким именем ещё нет, создаём отдельный конфиг:

sudo nano /etc/apt/apt.conf.d/90-xray-proxy
Acquire::http::Proxy "http://127.0.0.1:10809/";
Acquire::https::Proxy "http://127.0.0.1:10809/";

В обоих значениях указан http:// — так APT подключается к локальному прокси. Когда нужен HTTPS-сайт, прокси создаёт туннель CONNECT, а клиент продолжает проверять сертификат конечного сервера. Отключать проверку TLS или подписи пакетов не нужно. Подробнее — в документации APT для HTTP и HTTPS.

Смотрим, какие настройки применились, и пробуем загрузить списки пакетов:

apt-config dump | grep -i proxy
sudo apt-get update

Посмотри, нет ли в конце предупреждений о недоступных индексах. Команда может завершиться, хотя часть данных скачать не удалось. Если запросы идут не туда, проверь другие конфиги APT и настройки для отдельных хостов. Когда загрузка заработает, можно установить недостающие инструменты — например, sudo apt-get install unzip.

APT теперь зависит от Xray. Для возврата к прямой загрузке используй команды отключения прокси APT.

8. Проверяем работу после перезагрузки

Перед перезагрузкой проверь, включён ли автозапуск Xray, приложения и остальных нужных служб — ботов, обработчиков задач, CI runners. Используй их имена на своём сервере; my-app ниже — пример.

systemctl is-enabled app-xray my-app
systemctl is-active app-xray my-app

Выбери время, когда сервер можно перезагрузить. Убедись, что нет важных выполняющихся заданий и доступна аварийная консоль. Затем:

sudo reboot

Повторно подключаемся и проверяем:

uname -r
systemctl is-active app-xray my-app
resolvectl status
getent ahostsv4 archive.ubuntu.com
sudo ss -ltnp '( sport = :10808 or sport = :10809 )'
curl --noproxy '' --proxy socks5h://127.0.0.1:10808 \
  --max-time 15 -sS -o /dev/null \
  -w 'HTTP: %{http_code}, Time: %{time_total}s\n' \
  https://example.com
sudo apt-get update
sudo dpkg --audit

Пустой вывод dpkg --audit означает, что команда не нашла тех проблем с состоянием пакетов, которые умеет проверять. Номер ядра и время запроса запиши для своей машины — совпадать с результатом чужого VPS они не обязаны.

Практика с контролируемым отказом

На тестовом сервере или в согласованное окно останови Xray:

sudo systemctl stop app-xray

Повтори запрос через локальный прокси и попробуй открыть новую SSH-сессию. Запрос через прокси должен перестать работать, а SSH — остаться доступным. Посмотри, как ведёт себя приложение: сообщает об ошибке, повторяет запрос или переключается на прямой доступ. Последний вариант может не подходить для твоей задачи — это тоже нужно проверить.

Верни службу и повтори штатную операцию приложения:

sudo systemctl start app-xray

После запуска запросы должны снова проходить. Если этого не произошло, проверь журнал и то, умеет ли приложение повторно подключаться после обрыва. В итоге у тебя должны работать три вещи: запрос приложения через прокси, загрузка APT и отдельный доступ по SSH.

Если хочешь полностью отказаться от этой схемы, сначала верни прежние настройки приложения и APT. Перезапусти приложение, затем отключи app-xray командой sudo systemctl disable --now app-xray.

DNS возвращается отдельно, по сохранённой конфигурации и с проверкой доступа. Остановка Xray его настройки не восстанавливает. Что нужно сохранить заранее, перечислено в разделе «Что сохранить для восстановления».

Справочник

Дальше — отдельные случаи: сомнения в работе DNS, проверка готового прокси, получение профиля из подписки и возврат настроек. Возвращайся к ним по ссылкам из основных шагов.

Какие инструменты используются

В командах нужны curl, python3, ip, ss, systemctl и resolvectl. Для дополнительных проверок — dig из пакета dnsutils и nc из netcat-openbsd. Если APT пока не работает, начни с того, что уже установлено: например, getent проверяет системное разрешение имён и без dig.

Где эта схема работает

Мы явно подключаем приложение к прокси. Системный TUN-интерфейс при этом не создаётся, а произвольный трафик сервера не перехватывается. UDP в нашем SOCKS-входе отключён.

В контейнере адрес 127.0.0.1 обычно указывает на сам контейнер, а не на VPS. Поэтому приложение внутри него не увидит запущенный на хосте Xray по адресам из статьи. Для такого случая подключение нужно настроить отдельно.

Проверка и замена адресов DNS

Если DNS разрешает имена без ошибок, ничего менять не нужно. Этот раздел пригодится, когда curl не может определить адрес по имени или есть сомнения, что настроенный DNS отвечает. Сначала сравним ответы и только по результатам решим, нужна ли замена адресов.

cat /etc/resolv.conf
resolvectl status
getent ahostsv4 example.com
dig @1.1.1.1 example.com +time=2 +tries=1
dig @8.8.8.8 example.com +time=2 +tries=1

В этих командах dig обращается прямо к указанному DNS-серверу, а getent использует настройки системы. Если первый отвечает, а второй нет, проверяем /etc/resolv.conf, настройки интерфейса и systemd-resolved. Сравнение сужает поиск, но ещё не указывает точную причину.

Для временной проверки можно назначить доступные DNS-серверы интерфейсу. Сначала узнаём его имя:

ip -br link

В исходном случае интерфейс назывался enp3s0. Если у тебя другое имя, замени его:

sudo resolvectl dns enp3s0 1.1.1.1 1.0.0.1
sudo resolvectl flush-caches
getent ahostsv4 example.com

Это временное изменение для системы с systemd-resolved. Если оно помогло, отдельно разбери сохранение DNS после перезагрузки. Адреса публичных DNS здесь — примеры; используй резолверы, доступные и подходящие для твоей сети.

Чтобы снова использовать штатную конфигурацию интерфейса, выполни sudo resolvectl revert enp3s0. Команда сбросит и другие временные настройки resolved на этом интерфейсе. Если ты их задавал, сначала запиши значения.

Запрос без обычного DNS-разрешения

Возьми актуальный IPv4-адрес нужного домена из успешного DNS-ответа и подставь вместо ACTUAL_IPV4:

curl -4 -v --noproxy '*' \
  --resolve 'example.com:443:ACTUAL_IPV4' \
  --connect-timeout 5 --max-time 15 \
  https://example.com

Адрес подключения мы задаём вручную, а имя хоста остаётся прежним — оно нужно для HTTPS и проверки сертификата. Заменять URL на IP или отключать проверку через -k не требуется.

Если запрос с правильным IP тоже зависает, дело не только в обычном DNS-разрешении. Бери свежий адрес из DNS-ответа: старый IP из чужого лога уже мог измениться. Параметры команды описаны в документации curl.

Если уже есть внешний SOCKS5-прокси

Проверяем доступность его TCP-порта:

nc -vz -w 5 PROXY_IP PROXY_PORT

Открытый порт ещё не означает работающий прокси. Нужен запрос через него:

curl -v --noproxy '' --max-time 20 \
  --proxy socks5h://PROXY_IP:PROXY_PORT \
  --proxy-user 'LOGIN' \
  https://example.com

Замени адрес, порт и логин; при таком интерактивном запуске curl запросит пароль. Не записывай пароль прямо в команду и историю shell. Если прокси не требует авторизации, убери --proxy-user.

С socks5h имя назначения разрешает SOCKS-прокси. Какой DNS-маршрут он использует дальше, зависит от его настроек. Параметр --noproxy '' не даёт переменной NO_PROXY исключить проверяемый домен.

Если TCP-соединение устанавливается, а обмен с SOCKS-прокси зависает на рукопожатии, проверяй протокол, авторизацию, сам прокси и сеть между ним и сервером.

Как сохранить настройки DNS после перезагрузки

Этот раздел нужен, только если диагностика DNS показала проблему и новые адреса уже помогли при временной проверке. Изменение системного DNS затронет весь сервер, включая приложения без прокси.

На VPS сетевую конфигурацию часто создаёт cloud-init. Поэтому правки в его 50-cloud-init.yaml могут быть перезаписаны. Сохрани копии текущих настроек в защищённом каталоге и посмотри, как устроена сеть:

ip -br link
ip route
sudo netplan get
resolvectl status

Следующий пример подходит, если интерфейс уже получает адрес по DHCP (dhcp4: true), а сетью управляет networkd. При статическом адресе или другом renderer настройку нужно адаптировать. Имя интерфейса возьми из Netplan: при использовании match/set-name оно может отличаться от вывода ip.

Создай /etc/netplan/99-custom-dns.yaml, если файл ещё не существует:

network:
  version: 2
  ethernets:
    enp3s0:
      dhcp4-overrides:
        use-dns: false
      nameservers:
        addresses:
          - 1.1.1.1
          - 1.0.0.1

Адрес интерфейса и маршрут остаются в основной конфигурации — этот фрагмент их не создаёт. Посмотри итог через netplan get: списки из нескольких YAML-файлов могут складываться.

Если одновременно включены DHCPv4 и DHCPv6, в networkd нужно согласовать значения overrides. DNS из IPv6 RA приходит отдельно. Здесь важна установленная версия Netplan; подробности есть в справочнике YAML.

Применяем с подтверждением:

sudo chmod 600 /etc/netplan/99-custom-dns.yaml
sudo netplan generate
sudo netplan try --timeout 120

Пока первая сессия ждёт подтверждения, во второй проверь новый SSH-вход, resolvectl status и getent ahostsv4 archive.ubuntu.com. Подтверждай только рабочий результат.

Если не подтвердить изменение, netplan try должен его откатить. Но у этого механизма есть ограничения: после тайм-аута или отмены нужно проверить и сеть, и YAML на диске. Перед перезагрузкой убедись, что сохранена нужная конфигурация, и при необходимости восстанови её. На такой случай и нужен доступ к консоли провайдера. Подробности — в документации netplan try.

Если раньше вручную задавал FallbackDNS, проверь и эти адреса. Например, недоступный с твоего VPS 9.9.9.9 не поможет как резервный DNS. Добавляй туда серверы, доступность которых проверена.

Как отключить прокси для APT

APT теперь зависит от локального Xray. Для разовой попытки прямого обновления:

sudo apt-get \
  -o Acquire::http::Proxy=DIRECT \
  -o Acquire::https::Proxy=DIRECT \
  update

Если для отдельных хостов заданы свои прокси или включено автоопределение, проверь эти настройки тоже. Чтобы вернуться к прежней конфигурации, перенеси созданный нами файл за пределы apt.conf.d. Выбери имя, под которым ещё нет другого файла:

sudo mv -i /etc/apt/apt.conf.d/90-xray-proxy /root/90-xray-proxy.disabled

Вернуть настройку:

sudo mv -i /root/90-xray-proxy.disabled /etc/apt/apt.conf.d/90-xray-proxy

Так отключённый файл не участвует в чтении конфигурации. Не оставляй в apt.conf.d копию с другим допустимым именем: APT может прочитать и её. Порядок чтения описан в apt.conf.

Как получить и проверить JSON подписки

Здесь продолжаем подготовку рабочего профиля. Даже у купленной подписки нужно выяснить формат. Нам нужен совместимый с Xray конфиг — его можно получить через доступный экспорт или из документированного формата сервиса.

В моём случае подписка возвращала массив конфигураций Xray. Это не универсальный формат: другой сервер может вернуть текст, Base64, список URI или HTML со страницей ошибки.

Ссылки вида happ://crypt4/... и incy://crypt1/... предназначены для определённых клиентов. Xray сам по себе может их не понимать. Для защищённого формата понадобится совместимый экспорт или документированный инструмент его владельца. Не отправляй ссылку случайному онлайн-декодеру и не отключай антивирус ради неизвестного конвертера.

Если собираешься использовать Python-инструмент из исходников, сначала выясни, где его настоящий репозиторий, кто автор, какая лицензия и какие версии формата он поддерживает. Проект может существовать без пакета в PyPI, но похожее имя на GitHub ещё не доказывает, что ты нашёл нужный. Не устанавливай неизвестный пакет на рабочий сервер ради одной попытки.

Когда получишь HTTPS-адрес доверенным способом, сохрани его в subscription.txt. Для этого создадим защищённую папку вне Git; файлы будут принадлежать текущему пользователю:

umask 077
mkdir -p ~/private-xray
chmod 700 ~/private-xray
cd ~/private-xray
nano subscription.txt
chmod 600 subscription.txt

Следующий скрипт рассчитан на JSON-массив. Он скачивает ответ, проверяет его основную структуру и сохраняет файл, из которого можно выбрать профиль. User-Agent: Happ взят из моего случая; для другого сервера используй заголовок из его документации. Не запускай скрипт с чужой или недоверенной ссылкой.

import json
import os
import urllib.error
import urllib.request
from pathlib import Path
from urllib.parse import urlsplit

os.umask(0o077)
url = Path("subscription.txt").read_text(encoding="utf-8").strip()
parts = urlsplit(url)
if parts.scheme != "https" or not parts.hostname:
    raise SystemExit("Нужен HTTPS-адрес подписки")

request = urllib.request.Request(url, headers={"User-Agent": "Happ"})
try:
    with urllib.request.urlopen(request, timeout=20) as response:
        payload = response.read(5 * 1024 * 1024 + 1)
except (urllib.error.URLError, TimeoutError):
    raise SystemExit("Не удалось получить подписку; проверь соединение") from None

if len(payload) > 5 * 1024 * 1024:
    raise SystemExit("Ответ превышает лимит примера в 5 МиБ")
try:
    configs = json.loads(payload)
except (json.JSONDecodeError, UnicodeDecodeError):
    raise SystemExit("Ответ не является корректным JSON") from None

if not isinstance(configs, list) or not configs:
    raise SystemExit("Ожидался непустой массив конфигураций")
for config in configs:
    if not isinstance(config, dict):
        raise SystemExit("Элемент массива должен быть объектом")
    for key in ("inbounds", "outbounds"):
        items = config.get(key, [])
        if not isinstance(items, list) or not all(
            isinstance(item, dict) for item in items
        ):
            raise SystemExit("Некорректная структура входов или выходов")

with Path("configs.json").open("x", encoding="utf-8") as output:
    json.dump(configs, output, ensure_ascii=False, indent=2)

print("Количество конфигураций:", len(configs))
for index, config in enumerate(configs, 1):
    print("Профиль", index)
    for key in ("inbounds", "outbounds"):
        protocols = [item.get("protocol") for item in config.get(key, [])]
        print(key, protocols)

Режим open("x") защищает существующий файл от перезаписи. Перед повторной загрузкой сохрани прошлый результат под другим именем, сохранив защиту доступа. Скрипт не выводит URL, UUID и полный конфиг, но перед публикацией его вывода всё равно проверь содержимое.

Название профиля обычно находится в remarks. Открой файл локально, посмотри название и параметры, затем скопируй выбранный объект целиком в app-xray.json. Весь массив туда переносить не нужно. Проверяй профиль по содержимому: номер может измениться при обновлении подписки.

Если подписка недоступна с VPS, скачай её на доверенной машине и перенеси выбранный конфиг по SSH. Получение профиля не должно зависеть от прокси, который ещё предстоит настроить.

В примере HTTPS-запрос делает стандартный urllib.request. Скрипт умеет загружать конкретный формат; универсальным импортёром подписок он не является. С готовым профилем возвращайся к установке Xray или, если он уже установлен, к настройке локальных входов.

Что сохранить для восстановления

Для восстановления понадобятся рабочий JSON, unit-файл службы, конфиг APT, сетевые настройки до и после изменений, версия Xray и имя скачанного релизного архива. Сохрани их в защищённом месте вне публичного репозитория. Проверь, что копии будут доступны, даже если сам VPS перестанет отвечать.

Позже можно добавить systemd timer для регулярной проверки. Он будет делать запрос через локальный прокси с ограничением по времени и уведомлять администратора после нескольких сбоев подряд. Один неудачный запрос ещё не повод для тревоги. Желательно, чтобы уведомления шли по независимому каналу, иначе при сбое они тоже не дойдут. В настройке из этой статьи такого мониторинга пока нет.

Материалы для справки

Авторство и материалы

В основе статьи — моя инструкция по диагностике и настройке собственного VPS. Команды оформлены как учебные примеры. Там, где это не меняет принцип работы, адреса назначения и имена служб заменены. Параметры сверены с документацией сторонних проектов, ссылки на неё приведены в тексте. Бинарники, клиентские конфиги и чужие исходники в статью не включены.

Мой учебный код в статье доступен по 0BSD, в том числе для коммерческого использования без обязательного упоминания автора. Это разрешение не распространяется на текст статьи, обложку и сторонние компоненты. Xray-core распространяется по MPL-2.0, официальный установщик Xray-install — по GPL-3.0. Остальные программы сохраняют собственные лицензии.

Вопросы и помощь

Я не специалист по администрированию серверов. Здесь я рассказал, как решал свою задачу с помощью нейросети.

Если остались вопросы или у тебя другая ситуация, продолжить разбор можно тем же способом. Передай нейросети статью, опиши симптомы, версии программ и то, что уже проверил. Попроси объяснять команды и ждать твоего вывода перед следующим шагом — как в начале статьи.

Сверяй ответы с результатами команд и документацией. Пароли, токены, UUID, ключи и ссылки подписок в переписку не отправляй.

Материал пригодился?

Если материал помог, можно поблагодарить меня чашечкой кофе. Спасибо за поддержку!

Угостить кофе

DonationAlerts · откроется в новой вкладке

Бейдж Unreal Authorized Instructor используется по лицензии Epic Games. Absolute Academy — независимый авторский проект. Авторизация инструктора не означает, что Epic Games одобряет или спонсирует Академию и её учебные материалы, либо что Академия аффилирована с Epic Games.

Мой учебный код можно свободно использовать в своих проектах. Упоминание автора — по желанию. Условия и лицензия 0BSD