Перейти к содержанию

Установка

Alatyr состоит из двух частей, и ставятся они независимо:

  • Сервер — принимает заявки, проверяет аттестацию устройства, выпускает сертификаты через PKI, ведёт журнал аудита и отдаёт веб-интерфейс (дальше — админка). Разворачивается один раз на вашей инфраструктуре.
  • Агент — программа на машине сотрудника. Заводит ключ в чипе устройства, подаёт заявку и ставит выданный сертификат. Ставится на каждое устройство.

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

Прежде чем разворачивать сервер в работу, прочитайте Отказоустойчивость (HA) и Расчёт ресурсов: ни один из составов ниже не разворачивает базу или Vault в отказоустойчивой конфигурации сам по себе.

Что нужно перед началом

Машина под сервер

Параметр Минимум Рекомендуется Откуда число
CPU 2 ядра 4 ядра в покое весь состав потребляет менее 0,3 ядра; запас нужен на выпуск и на раскатку парка
Память 2 ГБ 4 ГБ замер в покое — около 120 МиБ на все компоненты; остальное уходит буферам PostgreSQL и файловому кэшу
Диск 20 ГБ 50 ГБ образы занимают 1,3–1,9 ГБ; данные растут десятками мегабайт в год даже на парке в 600 машин, см. Расчёт ресурсов
ОС Linux с systemd проверяется на Debian 12 и Ubuntu 22.04
Docker 24.0+ нужен docker compose как подкоманда, а не отдельный docker-compose

Для развёртывания в Kubernetes вместо Docker потребуется кластер 1.27+ и kubectl с правами на создание пространства имён.

Условия, без которых не заработает

Условие Зачем Чем проверить
Синхронизированное время Сертификаты и токены проверяются по времени; расхождение в минуты даёт отказы, которые выглядят как ошибки прав timedatectl status
Разрешение имени сервера в DNS Агенты обращаются к серверу по имени; адрес в конфигурации переживает переезд хуже getent hosts <имя> с машины агента
Доступ агентов к API сервера Без него агент зарегистрируется, но сертификат не получит curl https://<имя>:8090/health
Исходящий доступ к реестру образов Иначе образы придётся переносить вручную docker pull s3m4rgl/alatyr-server:<тег>

Что понадобится рядом

Компонент Обязательность Назначение
PostgreSQL обязательно основная база сервера
Vault или внешний SCEP CA обязательно выпуск сертификатов. Если УЦ ещё нет — встроенный УЦ поднимает его внутри состава
Keycloak опционально вход в админку через SSO. Альтернатива — встроенный вход по email и паролю

На стороне устройства требование задаёт способ хранения ключа: TPM 2.0 на Windows и Linux, Secure Enclave на macOS либо поддерживаемая смарт-карта. Устройство без такого хранилища получит отказ при выпуске, если включена политика «только аппаратное хранилище», — см. Известные ограничения.

Порты

Порт Что на нём
8090 API сервера (docker-compose.yml, меняется через ALATYR_API_PORT)
3000 админка (docker-compose.yml, меняется через ALATYR_UI_PORT)
13000 / 18090 демо-состав (docker-compose.demo.yml): админка / API
8200 Vault
8080 Keycloak (состав для ознакомления docker-compose.dev.yml)

Версия поставки: тег обязателен

Сервер и админка поставляются готовыми образами s3m4rgl/alatyr-server и s3m4rgl/alatyr-frontend. Собирать ничего не нужно — в этом репозитории лежит только развёртывание.

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

Тег выглядит как 1.4.2843 — мажор и минор продукта плюс номер сборки. Это не номер вида v1.4.5: теги репозитория и теги образов — разные нумерации. Действующий список — на странице образа, раздел Tags. Версия сервера и версия админки выпускаются парой и совпадают; берите один и тот же тег для обоих.

Манифесты из этого репозитория рассчитаны на образы той же поставки. Разойтись они могут молча и в обе стороны, поэтому правило простое: берите ALATYR_IMAGE_TAG из тех же релизных заметок, что и манифест.

Проверить, что поднялось именно то, что вы взяли:

curl -s http://localhost:8090/api/v1/version

Ответ содержит версию, коммит и время сборки.

Сервер

Пилот: набор развёртывания из выпуска

Начиная с v1.5.5 каждый выпуск несёт архив alatyr-deploy-<версия>.tar.gz: Docker Compose со встроенным корневым и промежуточными УЦ, сценарий first-up.sh, который придумывает все секреты и печатает их одним блоком (адрес админки, логин и пароль администратора, доступ к базе, токен и ключ распечатывания УЦ), и .env.example с уже вписанным тегом образов — набор и образы приходят парой.

tar -xzf alatyr-deploy-<версия>.tar.gz
cd alatyr-deploy-<версия>
./first-up.sh

Это рекомендуемый путь для пилота. Подробности — README.md внутри набора.

Остальные пути, и выбирать между ними нужно один раз:

Что вам нужно Берите
Пилот: всё поднимается одной командой, УЦ с промежуточными внутри набор развёртывания из выпуска (выше)
Рабочая установка, Vault и Keycloak у вас свои Docker Compose
Посмотреть продукт за одну команду, ничего не настраивая демо-состав
Рабочая установка в Kubernetes Helm-чарт
УЦ ещё нет и заводить его отдельно не хочется набор развёртывания из выпуска (выше); прежний встроенный УЦ устарел — без промежуточных УЦ и без печати учётных данных

Вариант 1 — Docker Compose

Шаг 1. Возьмите репозиторий и заполните .env

git clone https://github.com/s3m4rgl/alatyr-docs.git
cd alatyr-docs
cp .env.example .env
$EDITOR .env

Минимум, который нужно заполнить:

Переменная Что это
ALATYR_IMAGE_TAG версия поставки из релизных заметок
DB_PASSWORD пароль пользователя PostgreSQL; годится любая строка из openssl rand -base64 24
VAULT_ADDR плюс VAULT_ROLE_ID и VAULT_SECRET_ID (либо VAULT_TOKEN) доступ к вашему Vault. AppRole предпочтительнее статического токена и имеет над ним приоритет
ALATYR_LOCAL_AUTH_ENABLED, ALATYR_LOCAL_ADMIN_EMAIL, ALATYR_LOCAL_ADMIN_PASSWORD первый администратор, если вы не используете Keycloak
KEYCLOAK_URL, KEYCLOAK_REALM, KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET если используете Keycloak SSO

Полный список переменных с умолчаниями — Конфигурация.

ALATYR_DB_URL в этом составе не действует

Строку подключения docker-compose.yml собирает сам из DB_PASSWORD. Значение, заполненное в .env, молча игнорируется. Задавайте ALATYR_DB_URL только тогда, когда запускаете сервер не этим составом.

Секреты лучше передавать файлом

Любое имя из .env.example принимает форму <ИМЯ>_FILE — значение будет прочитано из указанного файла. Переменная окружения видна в docker inspect и в /proc/<pid>/environ, файл — нет.

Результат. docker compose config печатает состав целиком и не жалуется ни на одну незаданную переменную.

Шаг 2. Поднимите состав

docker compose up -d

Результат. Все три контейнера в состоянии running, а сервер отвечает своей версией:

docker compose ps
curl -s http://localhost:8090/api/v1/version

Если версия не та, что в релизных заметках, дальше разбираться бессмысленно — вернитесь к ALATYR_IMAGE_TAG.

Шаг 3. Войдите в админку

Откройте http://localhost:3000 (или ваш адрес — внешние порты задаются через ALATYR_API_PORT и ALATYR_UI_PORT). Войдите учётной записью первого администратора.

Первый вошедший получает полные права

Роль cert-admin автоматически достаётся первому, кто войдёт, — это работает и для Keycloak, и для локального входа. В установке только с Keycloak это буквально первый обладатель действующего токена настроенного клиента, а не обязательно вы. Войдите первым до того, как откроете API наружу. Подробности и другие способы закрыть это — Управление и роли.

Результат. Вы в админке, в правом верхнем углу ваша учётная запись с ролью «Администратор», и в левом меню видны разделы Пользователи, Настройки, Сервисные аккаунты.

Шаг 4. Направьте цели в их издатели

Чарт и состав задают только запасной путь к PKI. Какая цель куда ходит — запись в базе, и без неё заявки уедут в состояние vault_failed.

Откройте Настройки → Удостоверяющие центры и задайте каждой цели её точку монтирования и роль. Конкретные значения для встроенного УЦ — в соответствующем разделе; для вашего Vault — те, что вы завели у себя.

У wifi и user_mtls издатель обязан быть разным

Оба сертификата несут одинаковое имя, и приёмник mTLS различает их только по издателю. Пока их выписывает один издатель, машинный сертификат — который подписывается молча, без PIN и биометрии — годится как замена пользовательскому. Разбор и замеры — mTLS пользователя → Настройка.

Результат. На вкладке у каждой цели указана своя точка монтирования, кнопка проверки связи отвечает успехом, а в журнале сервера нет строки Vault auth is not configured:

docker compose logs alatyr-server | grep -i vault

Шаг 5. Смените пароль первого администратора

После смены уберите ALATYR_LOCAL_ADMIN_PASSWORD из .env: он нужен только на первый запуск, пока таблица пользователей пуста.

Результат. Вход под прежним паролем не проходит, под новым — проходит.

Демо-состав

Одна команда, ничего настраивать не нужно. Поднимает PostgreSQL, Vault с уже настроенным PKI, сервер, админку и наполняет базу демонстрационными данными:

ALATYR_IMAGE_TAG=<версия из релизных заметок> \
  docker compose -f docker-compose.demo.yml up -d

Результат. Открывается http://localhost:13000, вход — admin@wifi.local / Admin1234!. API — http://localhost:18090.

Это демонстрация, а не установка

Пароль администратора, пароль базы, токен Vault и ключи шифрования заданы прямо в файле, чтобы состав поднимался одной командой. Значит, они известны каждому, кто этот файл открыл. Никогда не переносите их в рабочую установку и не выставляйте этот состав в сеть.

Состав для ознакомления с чужим Keycloak или Vault

docker-compose.dev.yml поднимает только инфраструктуру — PostgreSQL (порт 5433), Vault в режиме разработки (8200) и Keycloak (8080), — чтобы подключить к ней ваш realm или ваши настройки Vault и посмотреть, как продукт ведёт себя с ними. Сервер и админку он поднимает по запросу:

docker compose -f docker-compose.dev.yml --profile app up -d

Vault здесь тоже в режиме разработки: данные в памяти, перезапуск стирает выпущенный УЦ вместе со всеми подписанными сертификатами.

Вариант 2 — Kubernetes (Helm)

В репозитории лежит рабочий чарт charts/alatyr. Он разворачивает сервер, админку и (по желанию) PostgreSQL. Vault он внутри себя не поднимает — это сознательное решение: Vault в кластере это отдельная установка со своим хранилищем, распечатыванием и резервными копиями, и делать её попутно значит спрятать её от того, кто за неё отвечает. Если УЦ ещё нет — начните со встроенного УЦ.

Шаг 1. Заведите Secret

Значений секретов в values.yaml нет и не будет: values уезжают в git и в helm get values любому, у кого есть доступ к релизу. Чарт ждёт ссылку на существующий Secret со следующими ключами:

Ключ Когда нужен
local-jwt-secret HS256, не короче 32 байт (openssl rand -hex 32)
local-admin-email, local-admin-password первый администратор; убрать после первого входа
database-url при postgres.mode: external
postgres-password при postgres.mode: embedded
vault-role-id, vault-secret-id при vault.auth: approle
vault-token при vault.auth: token
webhook-enc-key если включаете вебхуки
keycloak-client-secret если используете Keycloak
kubectl create namespace alatyr
kubectl -n alatyr create secret generic alatyr-secrets \
  --from-literal=local-jwt-secret="$(openssl rand -hex 32)" \
  --from-literal=local-admin-email=admin@corp.example \
  --from-literal=local-admin-password='<сильный пароль>' \
  --from-literal=vault-role-id='<role_id>' \
  --from-literal=vault-secret-id='<secret_id>' \
  --from-literal=database-url='postgres://…'

Результат. kubectl -n alatyr get secret alatyr-secrets показывает нужное число ключей.

Шаг 2. Установите релиз

helm upgrade --install alatyr charts/alatyr \
  --namespace alatyr --create-namespace \
  --set image.tag=<версия поставки> \
  --set secrets.existingSecret=alatyr-secrets \
  --set vault.addr=https://vault.corp.example:8200 \
  --set pki.publicAddr=https://vault.corp.example:8200 \
  --set ingress.enabled=true \
  --set ingress.host=alatyr.corp.example

Чарт роняет установку, а не предупреждает, если чего-то не хватает: пустой image.tag, отсутствующий secrets.existingSecret, пустой vault.addr, postgres.mode: embedded без включённой резервной копии, больше одной реплики сервера без явного признания ограничений. Ошибка на helm install дешевле, чем ImagePullBackOff или сервер, который стартует и отказывает на первой заявке.

Что стоит задать осознанно:

Значение Зачем
postgres.mode external (рекомендуется) — ваша база, строка подключения в Secret; embedded — один под в этом релизе, пригодно для пилота, не отказоустойчиво
pki.publicAddr адрес Vault, по которому его видят клиенты, а не поды. Попадает внутрь выданного сертификата как точка распространения списка отзыва: имя службы кластера снаружи не разрешается, и проверить отозванность будет нечем. Смена значения не чинит уже выданные сертификаты
server.trustedProxies CIDR ingress-прокси. Без него у всех вызывающих один и тот же адрес, и любое ограничение по адресу становится общим разрешением — см. Конфигурация
server.replicas больше одной реплики требует server.acknowledgeMultiReplicaLimits: true: опрос SCEP рассчитан на одну реплику, ограничение частоты между репликами не общее
namespace пространство имён, которое чарт проставляет самим объектам. По умолчанию alatyr. Если вы ставите релиз в другое, задайте и --namespace, и --set namespace=…: иначе объекты уедут не туда, куда смотрит helm

helm install --wait может зависнуть не из-за ошибки

Если класс хранения работает в режиме WaitForFirstConsumer (так устроен local-path в k3s), PVC под резервные копии остаётся в Pending до первого запуска задачи, и --wait ждёт его до истечения времени, хотя релиз развёрнут правильно. Либо не задавайте --wait, либо запустите копию сразу:

kubectl -n alatyr create job первая-копия \
  --from=cronjob/alatyr-postgres-backup

Результат. Поды в состоянии Running, а сервер отвечает своей версией:

kubectl -n alatyr get pods
kubectl -n alatyr port-forward svc/alatyr-server 8090:8090 &
curl -s http://localhost:8090/api/v1/version

Шаг 3. Те же три шага, что и в Docker

Дальше — вход в админку, направить цели в их издатели и смена пароля первого администратора. Проверить, что сервер дошёл до Vault:

kubectl -n alatyr logs deploy/alatyr-server | grep -i vault

Строка Vault auth is not configured означает, что ключи в Secret названы не так, как ждёт чарт.

Свой сертификат для админки

Речь здесь про сертификат, которым сама админка отвечает по HTTPS, — не про сертификаты сотрудников (user_mtls, mTLS пользователя). Оба выпускает один и тот же корень Alatyr Root CA, отсюда и типичная путаница: доверие этому корню на Mac сотрудника нужно для входа в браузере по user_mtls, а не для самой админки — подробнее в Пилоте, шаг 5.

Раздел ниже относится к набору развёртывания (alatyr-deploy-<версия>.tar.gz, врезка выше): только в нём есть служба, которая сама выпускает и продлевает сертификат админки. В варианте Docker Compose и в Helm-чарте TLS перед админкой всегда ставит заказчик сам — этот раздел к ним не относится.

Свой сертификат подложить в набор нельзя

Настройки «загрузить свой сертификат» в наборе нет. Сертификат выпускает служба tls-cert сама, из встроенного удостоверяющего центра, на имена и адреса из ALATYR_TLS_NAMES, и перевыпускает его при подходящем сроке, смене этих имён или смене корня PKI. Что за сертификат лежит в томе tls_data, служба не проверяет. Поэтому подложенный туда вручную сертификат может какое-то время работать, а затем будет молча заменён её собственным: при плановом продлении, при смене ALATYR_TLS_NAMES или корня, а если вместе с ним затронуты служебные файлы тома — при ближайшей проверке (раз в 12 часов). Предупреждения при этом не будет: однажды браузеры сотрудников просто перестанут доверять адресу админки. Так делать нельзя.

Как правильно: свой обратный прокси

Единственный поддерживаемый способ — выключить встроенный TLS и поставить перед сервером свой обратный прокси с сертификатом от корпоративного УЦ:

  1. В .env набора задайте ALATYR_TLS=off — встроенный УЦ сертификат больше не выпускает, и админка отдаётся по http на ALATYR_UI_PORT.
  2. Поставьте перед сервером свой обратный прокси (nginx, HAProxy и подобные) с сертификатом от вашего УЦ, проксирующий запросы на ALATYR_UI_PORT.
  3. Впишите адрес, по которому сотрудники будут открывать админку через прокси, в ALATYR_EXTRA_ORIGINS (через запятую, если адресов несколько). Без этого вход не сработает: браузер шлёт заголовок Origin, сервер отвечает 403 на адрес не из списка разрешённых, а админка показывает это как неверный пароль.
  4. Повторите ./first-up.sh (или docker compose up -d для ручной установки), чтобы переменные применились.

Агентов сотрудников через этот прокси пускать необязательно: по умолчанию они обращаются к серверу напрямую, на ALATYR_API_PORT (8090), в обход админки и её TLS. Решите пустить и их через свой прокси — укажите его https-адрес как адрес сервера при установке агента (параметр WIFI_CERT_SERVER, Установка → Агент) и настройте на прокси проксирование на тот же ALATYR_API_PORT.

Проверить. Откройте админку по адресу прокси — браузер не предупреждает о сертификате, а в его свойствах издатель — ваш корпоративный УЦ, не Alatyr Root CA. Вход под верным паролем администратора проходит; отвечает «неверный пароль» при заведомо верном — адрес прокси не попал в ALATYR_EXTRA_ORIGINS (README.md набора, раздел «Частые проблемы и их решение»).

Агент

Агент — один исполняемый файл, общий для macOS, Windows и Linux. Он заводит ключ в аппаратном хранилище устройства, подаёт заявку на сервер, ждёт одобрения администратором и ставит выданный сертификат и профиль сети.

Отдельной учётной записи на сервере агенту не требуется: он предъявляет enrollment_token устройства, полученный при первой регистрации.

Что задаётся при установке — одно и то же на всех трёх системах:

Параметр Обязателен Что это
адрес сервера да URL сервера Alatyr, тот же, что в адресной строке админки
почта владельца да, либо вместо неё домен На кого выпускается сертификат
корпоративный домен да, если почта не задана Почта тогда выводится как <имя машины>@<домен>

Без владельца агент уходит в цикл перезапуска

Если не задать ни почту, ни домен, агент не может определить владельца машины и выходит с ошибкой на каждом запуске. Снаружи это выглядит как успешная установка: пакет встал, «Готово» напечатано, а счётчик перезапусков службы растёт. На Linux install.sh поэтому отказывается ставить агента без одного из двух параметров; у пакетов deb/rpm задать их нужно самому — см. ниже.

Где взять пакеты. Готовые пакеты для Windows, macOS и Linux лежат в разделе Releases этого репозитория — возьмите файл своей платформы из последнего выпуска. Пакет macOS начиная с v1.5.5 подписан и нотаризован Apple; адрес сервера в него не вшит и задаётся при установке, см. раздел про macOS.

Linux

Пакет ставит исполняемый файл /usr/local/bin/alatyr-agent, конфигурацию /etc/alatyr-agent/config.env, системные службы alatyr-agent.service и alatyr-agent-secretd.service, пользовательскую службу alatyr-agent-user.service и значок в системной панели.

Поддерживаются amd64 и arm64.

Шаг 1. Поставьте пакет вместе с параметрами

Пакеты deb и rpm принимают параметры установки переменными окружения: имена те же, что в config.env.

# Debian, Ubuntu
sudo WIFI_CERT_SERVER="https://alatyr.your-domain.example" \
     CORP_DOMAIN="your-domain.example" \
     apt install ./alatyr-agent_<версия>_amd64.deb

# Fedora, RHEL, Alma, Rocky
sudo WIFI_CERT_SERVER="https://alatyr.your-domain.example" \
     CORP_DOMAIN="your-domain.example" \
     dnf install ./alatyr-agent-<версия>-1.x86_64.rpm

Если config.env уже настроен (есть хоть одно незакомментированное присваивание), параметры установки не применяются и пакет говорит об этом в выводе: обновление парка не должно молча переписывать то, что человек правил руками.

Для дистрибутивов без apt и dnf есть архив tar.gz со своим установщиком — он сам доставит зависимости TPM и PKCS#11:

tar xzf alatyr-agent-linux-<версия>.tar.gz -C alatyr-agent
cd alatyr-agent
sha256sum -c SHA256SUMS.txt
sudo ./install.sh \
    --server "https://alatyr.your-domain.example" \
    --corp-domain "your-domain.example"

--skip-tpm-deps пропускает установку зависимостей, если вы ставите их сами.

Результат. Служба работает, а не перезапускается по кругу:

systemctl status alatyr-agent.service

В строке состояния должно быть active (running), а не activating (auto-restart). В журнале не должно быть строки CORP_EMAIL not set and CORP_DOMAIN not set.

Шаг 2. Если параметры нужно задать или поправить позже

sudo $EDITOR /etc/alatyr-agent/config.env
sudo systemctl restart alatyr-agent.service

Не ставьте этому файлу права 0600

Пакет кладёт config.env с правами 0640, владелец root, группа alatyr-tpm, и это сделано намеренно: тот же файл читает непривилегированная пользовательская половина агента, входящая в эту группу. Ужесточение до 0600 оставляет её без адреса сервера и почты владельца — процесс тогда бесконечно висит в ожидании личности, не объясняя причины.

Если права уже изменены, верните их:

sudo chown root:alatyr-tpm /etc/alatyr-agent/config.env
sudo chmod 0640 /etc/alatyr-agent/config.env

Результат. ls -l /etc/alatyr-agent/config.env показывает -rw-r----- root alatyr-tpm, а alatyr-agent status печатает состояние, а не ошибку.

Шаг 3. Проверьте зависимости TPM

Пакеты deb и rpm тянут обязательное сами: модуль tpm2-pkcs11 (в Debian и Ubuntu это libtpm2-pkcs11-1) и p11-kit. Без модуля агент падает на каждом цикле при выпуске user_mtls и ssh.

Ещё два пакета идут как рекомендуемые, и их легко потерять при установке с --no-install-recommends:

Пакет Что сломается без него
libengine-pkcs11-openssl реальное подключение по Wi-Fi: регистрация и выпуск пройдут нормально, а EAP-TLS упадёт позже
tpm2-tools ручная диагностика TPM; на работу агента не влияет
# Debian, Ubuntu
dpkg -l libtpm2-pkcs11-1 p11-kit libengine-pkcs11-openssl

Без TPM агент работает, но иначе

Если модуля tpm2-pkcs11 нет вовсе, агент использует программный ключ на диске (/var/lib/alatyr-agent/, права 0600). Это годится для знакомства и не годится для парка: ключ становится файлом, который можно скопировать, — то есть ровно то, от чего продукт защищает.

Результат. Все три пакета установлены, и alatyr-agent status показывает аппаратное хранилище, а не программный ключ.

Диагностика и удаление

systemctl status alatyr-agent.service alatyr-agent-secretd.service
journalctl -u alatyr-agent.service -f
sudo /usr/local/bin/alatyr-agent status

Удаление: sudo apt remove alatyr-agent (данные и сертификаты остаются для аудита) либо sudo apt purge alatyr-agent — с полной очисткой. Для архивной установки — sudo ./uninstall.sh и sudo ./uninstall.sh --purge.

macOS

Агент на macOS работает у каждого пользователя отдельно (LaunchAgent), а не на уровне машины. Причина в Secure Enclave: ключ подписи создаётся в связке ключей вошедшего пользователя, которой под системной учётной записью не существует. Каждый пользователь одной машины регистрируется отдельно и получает свой сертификат.

Машина без графической сессии сертификат не получит

Пакет ставится нормально и без активного входа, но сертификат не выпустится, пока кто-то не войдёт в графическую сессию: ключ Secure Enclave держит системный процесс secd, а он есть только в такой сессии. Это свойство платформы, а не недоработка.

Шаг 1. Возьмите пакет из выпуска

alatyr-agent-<версия>.pkg из выпуска подписан Developer ID и нотаризован Apple. Проверить это до установки:

spctl -a -vvv -t install alatyr-agent-<версия>.pkg
# alatyr-agent-<версия>.pkg: accepted
# source=Notarized Developer ID

Пакет требует macOS 12 или новее. Адрес сервера в пакет не вшит: без него агент встаёт ненастроенным и к серверу не обращается, о чём пишет в свой журнал.

Шаг 2. Поставьте пакет вместе с параметрами

Штатный installer в macOS не умеет передавать параметры пакету, поэтому они кладутся заранее в файл предварительных значений /Library/Application Support/AlatyrAgent/config.env.preseed. Сценарий установки пакета читает его (только если владелец файла — root), применяет и удаляет. Скопируйте блок целиком, подставив свои значения:

PKG=./alatyr-agent-1.5.6.pkg
SERVER=https://alatyr.your-domain.example
CORP_DOMAIN=your-domain.example
CORP_EMAIL=user@your-domain.example   # можно оставить пустым — тогда <логин>@CORP_DOMAIN

sudo mkdir -p "/Library/Application Support/AlatyrAgent"
printf 'WIFI_CERT_SERVER="%s"\nCORP_DOMAIN="%s"\nCORP_EMAIL="%s"\n' \
    "$SERVER" "$CORP_DOMAIN" "$CORP_EMAIL" |
  sudo tee "/Library/Application Support/AlatyrAgent/config.env.preseed" >/dev/null
sudo chmod 600 "/Library/Application Support/AlatyrAgent/config.env.preseed"
sudo installer -pkg "$PKG" -target /

Имя ключа WIFI_CERT_SERVER — историческое, это адрес сервера Alatyr. Через MDM (например, FleetDM: Software → Add Software) делается то же самое: сценарий до установки кладёт этот файл, затем ставится пакет.

Почту нельзя передать переменной окружения установщика

installer в macOS не пробрасывает переменные окружения в postinstall-скрипт: sudo CORP_EMAIL=x installer -pkg … не работает и молча откатывается на автоопределение (<короткое имя>@<корпоративный домен> либо атрибут из каталога, если Mac включён в домен). Задавайте почту только файлом предварительных значений.

Результат. launchctl list | grep semargl показывает загруженный LaunchAgent, а команда

/Applications/alatyr-agent.app/Contents/MacOS/alatyr-agent status

печатает состояние. Журнал агента — ~/Library/Logs/AlatyrAgent/agent.log.

Для разработчиков: сборка пакета своей подписью

Нужна, только если организация хочет подписать агента своим Developer ID вместо поставляемого пакета.

  1. Разово создайте в учётной записи Apple Developer вашей организации сертификаты Developer ID Application (подпись приложения) и Developer ID Installer (подпись пакета) и пароль приложения для notarytool.
  2. Соберите выпуск скриптом sign-agent-release.sh, задав адрес сервера и корпоративный домен. Скрипт компилирует приложение, подписывает, нотаризует и упаковывает в .pkg. Для локальной проверки без отправки на серверы Apple есть SKIP_NOTARIZE=1.

Результат. В каталоге dist/ лежит .pkg, и spctl -a -vvv -t install на нём отвечает accepted. Ставится он так же, как в шаге 2.

Windows

Агент ставится пакетом MSI: alatyr-agent-<версия>-x64.msi. Он кладёт службу, окно агента, минидрайвер смарт-карты и драйвер считывателя. Пакет рассчитан на раскатку через групповую политику, FleetDM, Intune и подобные средства.

Ключ подписи создаётся в TPM 2.0 и хранится на уровне машины, в отличие от macOS.

Только x64

Пакета для ARM64 нет: драйвер считывателя собирается под x64, и пакет для ARM64 приехал бы без поддержки карты.

Шаг 1. Установите доверие к издателю пакета

Пакет подписан нашим сертификатом, а не сертификатом общедоступного УЦ, и корня Semargl в списке доверенных у Windows по умолчанию нет. Пока корень не установлен, Windows считает издателя неизвестным: установка проходит, но с предупреждением, а политики, требующие подписанного кода (AppLocker, WDAC, Smart App Control), пакет отвергнут. Неподписанный MSI, кроме того, не раскатывается через групповую политику.

На одной машине:

certutil -addstore -f Root "C:\Program Files\AlatyrAgent\driver\alatyr-driver-ca.cer"
certutil -addstore -f TrustedPublisher "C:\Program Files\AlatyrAgent\driver\alatyr-driver-signer.cer"

В домене то же делается один раз на парк: Computer Configuration → Policies → Windows Settings → Security Settings → Public Key Policies → Trusted Root Certification Authorities.

Результат. certutil -store Root содержит корень Semargl, а установка пакета не показывает диалог «неизвестный издатель».

Шаг 2. Поставьте пакет

msiexec /i alatyr-agent-<версия>-x64.msi /qn `
    SERVER=https://alatyr.your-domain.example `
    CORP_DOMAIN=your-domain.example `
    CORP_EMAIL=user@your-domain.example

Полезные ключи установки:

Ключ Умолчание Что делает
SERVER — Адрес сервера Alatyr
CORP_DOMAIN — Корпоративный домен
CORP_EMAIL — Почта владельца устройства
INSTALL_CARD 1 Заводить ли смарт-карту и считыватель
ENROLL_PURPOSE — Что создаёт машинная регистрация: пусто или wifi — устройство и машинный сертификат; none — только устройство и enrollment_token, без заявки. Нужно там, где хосту требуется только ssh или только карта
PURGE 0 При удалении снести данные агента
REMOVE_READER 1 При удалении убрать устройство считывателя
REMOVE_ANCHOR 1 При удалении убрать корневой сертификат УЦ

Журнал установки: добавьте /l*v C:\Windows\Temp\alatyr-msi.log.

Обновление — та же команда с более новым пакетом: он сам снимает предыдущую версию. Ключи очистки при обновлении не срабатывают, иначе перекат сносил бы считыватель и корень доверия между шагами.

Результат. Служба работает и задача пользовательской половины заведена:

sc.exe query AlatyrAgent
schtasks /Query /TN AlatyrAgentUser

Шаг 3. Проверьте карту и готовность ко входу

certutil -silent -scinfo
& "C:\Program Files\AlatyrAgent\alatyr-agent.exe" rdp --target <fqdn>

Последняя команда печатает список препятствий ко входу по карте через RDP: адрес, политика, карта, клиент, PIN, служба считывателей, защита LSA.

Результат. Карта видна, контейнер на месте, и все проверки готовности отвечают «да».

Раскатка на парк

FleetDM и Intune передают свойства штатно:

msiexec /i alatyr-agent-<версия>-x64.msi /qn SERVER=https://alatyr.corp `
    CORP_EMAIL=$FLEET_VAR_HOST_END_USER_EMAIL_IDP

Групповая политика свойств msiexec передавать не умеет — администратор назначает пакет компьютерам, и всё. Поэтому настройки задаются самой политикой, через реестр:

HKLM\SOFTWARE\Policies\Semargl\Alatyr
  WIFI_CERT_SERVER = https://alatyr.corp
  CORP_EMAIL       = user@corp
  CORP_DOMAIN      = corp

Имена значений те же, что у переменных окружения и ключей config.env. Ветка Policies выбрана намеренно: её содержимое перезаписывается при каждом применении политики, поэтому значение, убранное из политики, исчезает с машины само.

Разовая ручная установка перебивает доменную политику

Порядок источников, от старшего к младшему: флаг командной строки → config.env → свойства установки (HKLM\SOFTWARE\Semargl\Alatyr) → групповая политика. Значит, свойство, переданное разовой ручной установкой, переживёт последующие раскатки по политике и будет её перебивать. Агент пишет в журнал, откуда взял каждое значение — по этой строке такой случай виден сразу.

Что делает пакет, а что служба

Пакет умеет ровно то, что умеет Windows Installer: кладёт файлы, заводит службу AlatyrAgent, пишет свойства в реестр и убирает всё это при удалении.

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

Переход с прежней установки (.zip и install.ps1)

Ставьте пакет поверх — снимать старое руками не нужно. Машинную задачу планировщика AlatyrAgent, которую заводил прежний установщик, служба снимает сама при первом запуске. Это не косметика: пока задача жива, она гоняет цикл каждые десять минут, и служба делает ровно то же с тем же файлом состояния — две регистрации одного устройства идут параллельно и перетирают друг друга, а по журналу это выглядит как повторные заявки без причины.

Пользовательскую задачу снимать не нужно — пакет перезаписывает её своей.

Удаление

msiexec /x alatyr-agent-<версия>-x64.msi /qn            # данные сохраняются
msiexec /x alatyr-agent-<версия>-x64.msi /qn PURGE=1    # вместе с данными

Что дальше