Закрепление идентификации сервера
Каждый сервер rVPN идентифицируется долговременным Ed25519 ключом идентификации. Клиент закрепляет этот ключ при первом подключении к профилю и отказывается соединяться, если закреплённый ключ когда-либо изменится без валидного доказательства ротации. Это та же модель доверия, что и в SSH, дополненная церемонией ротации на цепочке подписей — так оператор может менять ключи, не ломая уже настроенных клиентов.
Формат отпечатка
Заголовок раздела «Формат отпечатка»Все отпечатки записаны в единой канонической кодировке:
ik:1:<52 символа base32>ik:— пространство имён, зарезервировано под будущие типы ключей.1— версия, чтобы легитимная ротация могла явно перейти кik:2:….- Тело —
sha256(identity_key)в base32 по RFC 4648, строчные буквы, без заполнителей. 52 символа, ASCII-безопасно, подходит для QR-кодов.
Пример:
ik:1:d4rgmp5b7ta6qmxi2mccwkjq4qxopfxzr7qivbfgu4wjycmuxnlaРаньше десктопный клиент хранил hex-отпечаток из 32 символов. Он всё ещё
читает эти старые значения при первом подключении, а затем переписывает
их в форме ik:1: — изменения конфигурации не требуются.
Как закрепление работает на мобильных устройствах
Заголовок раздела «Как закрепление работает на мобильных устройствах»На iOS, macOS и Android отпечаток хранится в самом профиле, в поле
Server Fingerprint. Отдельного known_hosts.json больше нет.
- Первое подключение при пустом
Server Fingerprintи включённом Trust on First Use: приложение фиксирует отпечаток сервера и сохраняет его в профиле. - Последующие подключения: туннель открывается только если отпечаток сервера совпадает с сохранённым значением.
- Несовпадение: приложение показывает диалог Server identity changed с закреплённым и полученным отпечатками рядом и тремя вариантами — Cancel, Delete Profile, Trust New Identity.
Чтобы закрепить идентификацию явно до первого подключения, вставьте
отпечаток (полученный от оператора в форме ik:1:) в поле
Server Fingerprint редактора профиля.
Как закрепление работает в десктопном CLI
Заголовок раздела «Как закрепление работает в десктопном CLI»Десктопный клиент rvpn хранит отпечатки в known_hosts.json
(путь настраивается) и накладывает поверх этого отпечаток из конфига.
Полное описание полей см. в разделе [server_identity] на странице
Конфигурация клиента;
кратко:
fingerprint— задайте значениеik:1:…, чтобы принудительно использовать конкретный ключ, независимо от содержимогоknown_hosts.json.trust_on_first_use— по умолчаниюtrue. Захватывать при первом подключении.strict_mode— по умолчаниюfalse. Установитеtrue, чтобы полностью отказывать неизвестным серверам; в этом случаеfingerprintнужно задать до первого подключения.strict— по умолчаниюtrue. Приfalseнесовпадение приводит только к предупреждению в логах, а соединение не разрывается.
При сохранении known_hosts.json клиент также мигрирует любой найденный
устаревший hex-отпечаток в канонический ik:1:, сохраняя исходную
метку first_seen.
Церемония ротации ключа
Заголовок раздела «Церемония ротации ключа»Когда оператор меняет ключ идентификации сервера, prekey bundle получает два новых поля, по которым клиент проверяет непрерывность цепочки:
identity_key_version— монотонное; новая идентификация ⇒+1.rotation_signature— Ed25519-подпись предыдущим ключом идентификации над байтами нового публичного ключа плюс номером новой версии.
Клиент, закрепивший версию N, сделает следующее:
- Увидит bundle, чей ключ не совпадает с сохранённым отпечатком,
- Проверит, что
identity_key_version == N + 1, - Проверит подпись ротации, используя закреплённую идентификацию как ключ верификации,
- Если обе проверки пройдены — молча обновит сохранённый отпечаток на новое значение, без диалога, соединение просто работает.
Если клиент видит неподписанную ротацию или пропуск версии, он падает обратно к диалогу несовпадения, описанному выше.
Церемония на стороне оператора
Заголовок раздела «Церемония на стороне оператора»Свежие серверы публикуют v1 bundle без подписи ротации:
rvpn-server keygenrvpn-server prekey-bundleДля ротации сгенерируйте новый ключ идентификации и подпишите новый bundle старым ключом:
# Сохраняем старый ключ, чтобы подписать ротациюmv server_identity.key old_identity.key
# Генерируем новыйrvpn-server keygen --output server_identity.key
# Публикуем v2 bundle, подписанный старым ключомrvpn-server prekey-bundle \ --identity server_identity.key \ --output prekey-bundle.json \ --rotate-from old_identity.key \ --from-version 1--rotate-from и --from-version должны использоваться вместе.
Сервер отказывается публиковать неподписанную ротацию. Выложите новый
bundle; уже закреплённые клиенты обновятся молча, новые — закрепят новый
ключ при первом подключении.
Заархивируйте old_identity.key — он нужен ровно до момента публикации
новой ротации, но, уничтожив его, вы навсегда лишаетесь возможности
подписать следующую ротацию с этой версии.
Экспорт и импорт профилей
Заголовок раздела «Экспорт и импорт профилей»И мобильный, и десктопный экспорт профиля переносят поле
serverFingerprint. Когда вы импортируете .rvpn профиль с уже
установленным отпечатком, отправитель поручился за идентификацию сервера
внесистемно — сравните опубликованный им отпечаток с тем, что показан на
экране импорта, прежде чем сохранять.
Если у импортируемого профиля отпечатка нет, туннель захватит его при первом успешном подключении — так же, как если бы вы создали профиль локально.
Модель угроз
Заголовок раздела «Модель угроз»Защищает от
- Пассивного MITM после первого успешного подключения.
- Ситуации, когда враждебный оператор перехватывает домен или IP вашего сервера и направляет их на другой бэкенд.
- Повтора украденного prekey bundle с другого сервера.
Не защищает от
- Активного MITM при самом первом подключении. Классическое ограничение TOFU. Смягчается импортом профиля с уже вложенным отпечатком или его вставкой из внесистемного канала до подключения.
- Модификации локального хранилища профиля на устройствах с root или jailbreak. Штатная iOS изолирует профили в песочнице; штатный Android держит их как приватные для приложения.
Если вам нужно намеренно заменить идентификацию сервера — например, после компрометации — правильный путь именно через диалог несовпадения. Опубликуйте новый отпечаток внесистемно, попросите пользователей принять изменение, и они вернутся к новому ключу со свежим отпечатком.