Когда сервер с конфигом заблокировали — клиент отвалился. Разбираем способ доставки который сломать сложнее чем сам интернет
У любого прокси-сервиса есть слабое место которое не связано с протоколом. Сервер переехал, IP сменился, конфиг устарел — и пользователь сидит без связи пока не получит обновление вручную. Чем больше пользователей, тем острее проблема.
Стандартное решение — раздавать конфиги через HTTPS. Удобно, пока URL не попал в реестр. После этого тысяча человек одновременно пишет в поддержку.
DNS TXT-записи решают эту проблему не через обход блокировок, а через выбор канала который блокировать политически сложно.
Что такое TXT-запись и зачем она вообще существует
TXT-запись — это произвольная строка текста привязанная к домену. Придумали её для SPF (верификация почтовых серверов) и DKIM, потом стали использовать для подтверждения владения доменом в Google Search Console и Let's Encrypt. Технически это просто поле «здесь может лежать любой текст до 255 байт».
Несколько TXT-записей на один домен — нормальная практика. Суммарно можно передать несколько килобайт. VLESS URI с UUID, адресом сервера и параметрами Reality влезает в одну запись с запасом.
Запросить TXT-запись можно так:
dig TXT config.yourdomain.com +short
Или через любой DoH-резолвер:
curl "https://1.1.1.1/dns-query?name=config.yourdomain.com&type=TXT" \ -H "accept: application/dns-json"
Почему этот канал сложно заблокировать
DNS как мы знаем инфраструктура интернета, и это создаёт определённую защиту. Полная блокировка порта 53 действительно ломает браузер, почту и всё остальное. Но провайдер может действовать тоньше: DNS-спуфинг на уровне провайдера позволяет заблокировать конкретный домен не трогая остальные запросы. Именно так работает большинство DNS-блокировок в РФ. Защита здесь не абсолютная, а в стоимости атаки: заблокировать один домен легко, но если у клиента зашит список из десяти резервных доменов на разных регистраторах — провайдеру нужно блокировать все десять, и делать это оперативно при каждом обновлении списка. DoH дополнительно усложняет задачу: DNS over HTTPS к 1.1.1.1 выглядит как обычный HTTPS-трафик, перехватить его без MITM не получится.
Как это выглядит на практике
Схема минимальной реализации:
1. Создаём TXT-запись с конфигом
Через панель DNS-провайдера добавляем запись:
Тип: TXT Имя: config.yourdomain.com TTL: 300 Значение: vless://uuid@server:443?type=tcp&security=reality&...
TTL в 300 секунд означает что при смене конфига клиенты подхватят обновление максимум через 5 минут.
2. Клиент запрашивает конфиг при старте
Сначала устанавливаем библиотеку — dns.resolver не входит в стандартную библиотеку Python:
pip install dnspython pip install httpx
import dns.resolver def get_config(domain): answers = dns.resolver.resolve(domain, 'TXT') for rdata in answers: for txt_string in rdata.strings: return txt_string.decode()
В случае если нужен DoH вместо системного резолвера:
import httpx def get_config_doh(domain): r = httpx.get( "https://1.1.1.1/dns-query", params={"name": domain, "type": "TXT"}, headers={"accept": "application/dns-json"} ) answers = r.json().get("Answer", []) for answer in answers: if answer["type"] == 16: return answer["data"].strip('"')
Три строки логики. Клиент при запуске делает один DNS-запрос, получает актуальный URI, подключается.
3. При смене сервера
Меняем только TXT-запись. Никаких обновлений приложения, никаких рассылок пользователям. Через 5 минут все клиенты работают с новым сервером.
Защита конфига от чужих глаз
TXT-запись публична — любой может сделать dig TXT и прочитать конфиг. Для личного использования это некритично: один сервер с одним пользователем не привлекает внимания.
Если сервером пользуются несколько человек и конфиг светить не хочется — два варианта.
Шифрование. Конфиг шифруется симметричным ключом, который передаётся пользователю один раз при онбординге. В TXT лежит зашифрованная строка, клиент расшифровывает локально.
from cryptography.fernet import Fernet # При создании конфига key = b'ваш_секретный_ключ_зашитый_в_клиент' f = Fernet(key) encrypted = f.encrypt(b'vless://uuid@server:443?...') # encrypted кладём в TXT # На клиенте config = f.decrypt(txt_value).decode()
Двухшаговая доставка. В TXT лежит не сам конфиг, а URL следующего шага — например, ссылка на Pastebin или Github Gist с актуальным конфигом. Первый шаг через DNS, второй через HTTPS. Блокировка одного не убивает канал полностью.
Ограничения о которых нужно знать
Кеширование. DNS кеширует ответы на TTL. Если TTL поставить 3600 — обновление конфига дойдёт до клиента через час. Для оперативных изменений ставить TTL не больше 300, а лучше 60 секунд — если DNS-провайдер позволяет.
Размер. По RFC 1035 (ietf.org/rfc/rfc1035.txt) одна строка внутри TXT-записи ограничена 255 байтами. Но сама запись может содержать несколько строк — приложение собирает их в одну. Теоретический потолок 65 535 байт, практический упирается в UDP MTU: без EDNS0 это 512 байт на весь DNS-пакет, с EDNS0 до 4 КБ. Большинство современных резолверов поддерживают EDNS0, так что в реальности доступно несколько килобайт. Для VLESS URI хватает с запасом, для полного JSON-конфига sing-box с несколькими inbound уже нет.
Домен должен резолвиться. Если сам домен попал в реестр РКН канал собственно мёртв. Решается через несколько резервных доменов зашитых в клиент. Клиент перебирает список по порядку, использует первый который ответил.
FALLBACK_DOMAINS = [ "config.primary-domain.com", "cfg.backup-domain.net", "c.reserve-domain.org", ] def get_config_with_fallback(): for domain in FALLBACK_DOMAINS: try: return get_config(domain) except Exception: continue return None
DoH нужно настраивать отдельно. Стандартный dns.resolver в Python ходит через системный резолвер на порту 53. Если нужен DoH — придётся использовать httpx или requests напрямую к API Cloudflare или Google.
Кто уже использует этот подход
Psiphon активно работает с DNS-инфраструктурой — в открытом репозитории psiphon-tunnel-core есть отдельный пакет для DNS и утилиты резолвинга. Конкретный механизм доставки конфигов через TXT-записи публично не задокументирован, поэтому утверждать что они используют именно этот подход не буду. Из открытых реализаций ближе всего к описанной схеме динамические ключи Outline через URL и ряд публичных прокси-сервисов которые хранят актуальный URI в Telegram-боте. DNS TXT в этом контексте — менее популярная но более независимая альтернатива: не требует Telegram, не зависит от мессенджера который сам может быть заблокирован.
Когда это имеет смысл, а когда нет
Канал решает конкретную проблему — доставку и обновление конфига при заблокированном основном канале. Сам по себе он не делает соединение более стойким к детекции и не меняет протокол туннеля.
Если проблема в том что конфиги устаревают и пользователи отваливаются — DNS TXT решает её элегантно и дёшево. Если проблема в том что сам трафик детектируется — нужно смотреть в сторону протокола, а не канала доставки.
Если есть идеи для разбора, нашёл ошибку в конфиге
или хочешь предложить тему — пиши на
aleksandr@murzin.digital Отвечаю.
