Машины и баны
Забанить аккаунт — значит попросить нарушителя завести новый. Laminara умеет узнавать компьютер, с которого заходят, и банить его: аккаунт, машину или группу связанных машин.
Включение
{ "hwid": { "mode": "observe", "saltPath": "/var/lib/laminara/hwid.salt", "ticketSecretPath": "/var/lib/laminara/hwid-ticket.key", "store": { "backend": "sql", "config": { "driver": "sqlite", "dsn": "/var/lib/laminara/machines.db" } } }}Пересоберите лаунчер: client-config положит в него соль, и без неё лаунчер вообще
ничего не измеряет.
mode |
Что делает |
|---|---|
off |
Ничего. Лаунчер не собирает данные. |
observe |
Записывает машины, никого не блокирует. По умолчанию. |
enforce |
Блокирует по банам. |
Как узнаётся машина
Лаунчер измеряет 8–14 свойств: UUID материнской платы, серийники дисков и платы, machine-id системы, MAC постоянных сетевых карт, модель видеокарты и процессора, объём памяти, идентификатор установки ОС. Каждое хешируется солью прямо на компьютере игрока — на сервер уходят только 16-байтовые дайджесты, сырых значений в протоколе нет вообще (в контракте просто нет такого поля).
Плюс лаунчер держит ключ машины. Он бывает двух видов, и разница честно сообщается
серверу флагом PLATFORM_KEY_FALLBACK:
- Аппаратный (TPM). Ключ живёт в чипе, приватная часть наружу не выходит никогда, чип отказывается его дублировать. На Linux это primary key, который TPM заново выводит из своего собственного сида: нигде ничего не хранится, удалять нечего, переустановка системы его не теряет, скопировать на другой компьютер невозможно. Вес — 40 (якорь).
- Программный. Пара Ed25519 сразу в двух местах: хранилище паролей ОС и скрытый файл в домашней папке, восстанавливается из любого уцелевшего. Побеждает «удалить папку лаунчера» — то, что пробуют первым, — но копируется и не переживает форматирование. Вес — 15.
Лаунчер сам берёт лучшее из доступного, настраивать ничего не нужно.
Где аппаратный ключ работает
| ОС | Состояние |
|---|---|
| Linux | Работает, проверено на живом TPM 2.0. Но /dev/tpmrm0 имеет права root:tss 0660 — игроку нужно быть в группе tss (sudo usermod -aG tss $USER один раз), иначе тихо откатываемся на программный ключ. |
| Windows | Через Microsoft Platform Crypto Provider, прав администратора не нужно. Написано, но на живом железе не проверялось. |
| macOS | Пока нет. Создание ключа в Secure Enclave не имеет консольного пути и требует биндингов Security.framework, которые здесь не собрать и не запустить. Остаётся программный ключ — на macOS Keychain и так надёжен. |
Совпадение считается по очкам, а не по равенству — реальная машина меняется:
| Вес | Сигналы |
|---|---|
| 40 | UUID платы, UUID платформы, аппаратный ключ машины (в виртуалке — вдвое меньше) |
| 15 | серийник платы, серийник диска (до двух), machine-id, программный ключ машины |
| 5 | MAC, том, видеокарта, процессор, память, имя хоста |
та же машина ⟸ очки ≥ 45 и совпало ≥ 2 разных видаочки ≥ 25 ⟸ новая машина, но в том же кластере (связанный компьютер)меньше ⟸ новая машина, новый кластерОдного якоря (40) намеренно не хватает: партии материнок уезжают с завода с одинаковым UUID. Видеокарта + процессор + MAC не решают никогда — тысячи людей делят модель.
Автодемоция. Дайджест, встреченный больше чем на 25 машинах, перестаёт считаться
уликой. Это само убирает нули в прошивке, серийники 0000_0000 у USB-переходников и
клонированные образы виртуалок — без ручных чёрных списков.
Баны
ban <ник> <причина...> [--account|--machine <id>|--cluster <id>] [--days N|--permanent] [--yes]bans список активныхbans LM-7F3A карточка одного банаbans lift LM-7F3A снятьmachines <ник> машины игрокаmachines show <id> машина, её аккаунты и кластерmachines trust <id> пометить машину доверенной (семейный ПК, интернет-кафе)machines prune вычистить давно неактивныеБез флага области ban выбирает её сам и печатает выбор: кластер, если машины игрока
лежат в одном небольшом кластере без доверенных машин, иначе аккаунт.
У каждого бана есть референс-код вида LM-7F3A. Игрок называет его в тикете, вы
делаете bans LM-7F3A и видите кто, когда, за что и в какой области. Это разница между
отвечаемым и неотвечаемым обращением.
Аппаратные баны истекают (по умолчанию 90 дней), аккаунтные — постоянные. Компьютеры продают и передают; вечный бан по железу копит ложные срабатывания бесконечно.
Если бан заденет больше трёх разных аккаунтов, команда откажется его применять и покажет
список — придётся подтвердить --yes. Семейный ПК, интернет-кафе и школьный класс из
одного образа реально выглядят одной машиной; machines trust помечает такие, и они
больше не попадают под автоматический выбор кластера.
Где проверяется
- Вход в лаунчер — при бане сессия отзывается сразу же.
- Перед запуском игры и при восстановлении сессии — так бан, выданный посреди установки, всё же останавливает игрока.
/yggdrasil/authserver/authenticate— вход в игру./sessionserver/.../join— коннект к серверу, ловит бан посреди сессии.
Пункт 3 — несущий. Сторонний лаунчер с authlib-injector ходит туда напрямую, минуя весь
API Laminara. Включите "requireLauncher": true, и вход в игру потребует короткоживущий
тикет, который выдаётся только через нормальный вход в лаунчер.
Настройки
modestring
off · observe (по умолчанию) · enforce.saltPathstring
ticketSecretPathstring
storeobject
memory (по умолчанию, не переживает перезапуск) или sql с driver = sqlite/postgres/mysql.requireReportboolean
false.requireChallengeboolean
enforce.requireLauncherboolean
requireHardwareKeyboolean
minScore · minKinds · clusterScorenumber
45, 2, 25.fanOutLimitnumber
25.maxClusterSizenumber
ban готов выбрать область «кластер» сам. По умолчанию 8.vmPolicystring
allow · flag (по умолчанию) · deny.hardwareBanTTLstring
2160h (90 дней).retention · ipRetentionstring
4320h) и IP (720h).Приватность
Сырых значений в протоколе нет — это ограничение контракта, а не обещание. Соль запечена в лаунчер, значит она есть и у игрока: высокоэнтропийные входы (UUID прошивки, серийник диска) необратимы по-настоящему, низкоэнтропийные (модель процессора, объём памяти) переберёт любой, у кого есть соль. Соль даёт ровно две вещи: Laminara никогда не хранит и не передаёт сырой серийник, и украденная база бесполезна для сопоставления с базой другого сервера.
Из соображений давности IP хранится 30 дней и только последний, машины без активности
180 дней вычищаются целиком (machines prune).
Экран входа прямо говорит игроку, что отпечаток собирается. Прятать это бессмысленно — лаунчер открытый, — и именно скрытность злит людей.
Что она не ловит
- Пропатченный лаунчер. Сбор идёт на машине игрока; заголовок версии клиент заявляет сам. Что реально даёт порог версии — при каждом релизе патч приходится накладывать заново. Это не проверка целостности.
- Коммерческие спуферы уровня ядра (~$20–50/мес на рынке читов) подменяют SMBIOS, дисковые запросы и MAC разом. Из пользовательского пространства с этим ничего не сделать.
- Программный ключ можно скопировать. Он переживает переустановку лаунчера, но переносится на чужой компьютер и не переживает форматирование. Аппаратный (TPM) этих слабостей не имеет — но он есть не у всех, см. таблицу выше.
- Свежая виртуалка стоит 10 минут. Определяется и помечается, блокируется только при
vmPolicy: deny, что заодно отсекает честных игроков на Parallels и QEMU. - Linux слабее остальных.
product_uuidиboard_serialтам читает только root, у лаунчера прав нет — отчёт помечаетсяSMBIOS_UNREADABLEи держится на machine-id, дисках и ключе установки. - Аппаратный ключ Windows не проверялся на живом железе. Код ядра собирается и его тесты прогоняются на Windows и macOS в CI, но TPM на сборочных машинах нет, поэтому эта ветка остаётся непроверенной. Linux-путь — и сбор, и TPM — проверен на живой системе.
- Вторая физическая машина непобедима, и это правильное конечное состояние: цель — чтобы обход стоил дороже, чем уйти в другой проект.
Честный пол: система надёжно останавливает гриферa, который заводит новый аккаунт; человека, удалившего ключ реестра по совету с форума; того, кто запустил взломанный сторонний лаунчер. Она не останавливает человека с деньгами и терпением — и ни одна клиентская система этого не делает.