У Apple для пользователя предусмотрен только один активный личный ключ App Store Connect API Key — это прямо указано в официальной документации по созданию ключей. Поэтому для одиночного разработчика ошибка «сразу создадим несколько личных ключей на разные пайплайны» не работает: начните с личного ключа, если вам достаточно доступа к назначенным приложениям, а для unattended-процесса с Provisioning или недоступными личному ключу операциями переходите к отдельному командному ключу с минимальной ролью.

Симптом: автоматическая публикация требует больше прав, чем вы ожидали, а общий ключ уже используется несколькими людьми. Быстрое решение: разделите App Store Connect API Key, сертификаты, приватные ключи подписи и доступ к удалённому Mac; личный ключ оставьте для ограниченного пользовательского сценария, командный создавайте только под конкретную автоматизацию.

Кому нужен этот разбор

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

Она также пригодится владельцу небольшой команды с несколькими приложениями и разными ответственными за релиз, а также поддерживающему CI-инженеру, который запускает fastlane, Transporter или собственный скрипт на постоянно доступном удалённом Mac.

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

Шаг 1. Зафиксируйте границы четырёх разных доступов

До создания ключа выпишите, что именно должен делать процесс. В App Store Connect часто смешивают несколько независимых полномочий:

  • доступ к данным и операциям App Store Connect API;
  • права в разделе Certificates, Identifiers & Profiles;
  • доступ к исходному коду и конфигурации сборки;
  • вход на удалённый Mac через SSH, VNC или веб-консоль;
  • сертификат подписи и связанный с ним закрытый ключ.
API Key не заменяет сертификат распространения, профиль Provisioning Profile или приватный ключ подписи. Он подтверждает право обращаться к API, но не превращает произвольную машину в доверенную среду для кодовой подписи.

Для каждого workflow составьте короткую матрицу: «действие — конечная точка — требуемая роль — приложение — владелец секрета». Например, загрузка архива, изменение метаданных TestFlight и управление тестировщиками могут относиться к разным операциям и не обязаны получать одинаковый уровень доступа.

Apple описывает роли и соответствующие операции в справочнике разрешений App Store Connect. Используйте его как источник для проверки роли, а не название должности в команде: «релиз-менеджер» не является техническим разрешением API.

Шаг 2. Сопоставьте личный и командный ключ

Вам нужно оценивать не удобство создания, а четыре последствия: откуда наследуются права, какие приложения видит автоматизация, как передаётся доступ и что будет отозвано при инциденте.

<
КритерийЛичный API KeyКомандный API Key
Источник правПрава конкретного пользователяВыбранная роль команды
Область приложенийПриложения, доступные этому пользователюНе ограничивается одним приложением на уровне ключа
Основной сценарийЛичная автоматизация и ограниченный проектОбщая или безымянная автоматизация
Удобство отзываОтзывается доступ конкретного пользователяМожет остановить несколько процессов, использующих ключ
Дополнительные API-возможностиНе все операции доступныПодходит для операций, которым требуется командный ключ
Риск при совместном использованииНиже при отдельном пользователеВысокий, если ключ передают нескольким людям
Личный ключ наследует разрешения пользователя и область приложений, к которым тот пользователь допущен. Это делает его естественным выбором для независимого разработчика: новый участник не получает автоматически доступ ко всей команде, а отзыв пользовательского доступа не требует менять общий секрет в каждом проекте.

Командный ключ создаётся для команды и получает роль, которую выбирает администратор. Но это не механизм изоляции приложения: одну командную API Key нельзя штатно привязать только к одному Bundle ID. Создание нескольких командных ключей с той же ролью не исправляет проблему, если каждый из них всё равно видит командную область, предусмотренную этой ролью.

Именно здесь часто возникает ложное ощущение безопасности: ключей стало больше, но полномочия не стали уже. Для App Store Connect API Key оценивайте не количество файлов, а реальную область действия роли.

Шаг 3. Выберите личный ключ для одиночного разработчика

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

  • запуска загрузки сборки в TestFlight от имени вашего пользователя;
  • автоматизации ограниченного набора метаданных;
  • чтения статуса обработки сборки;
  • ручного запуска fastlane из вашей рабочей среды;
  • сценария, где увольнение или смена подрядчика не должны затронуть других пользователей команды.
Apple отдельно описывает создание личного и командного ключа, а также правила их скачивания и отзыва в [инструкции по API Key](https://developer.apple.com/documentation/appstoreconnectapi/creating-api-keys-for-app-store-connect-api). Перед настройкой проверьте, что конкретный инструмент действительно принимает личный ключ и поддерживает нужные вам конечные точки. Нельзя выводить поддержку из одного лишь факта, что инструмент умеет работать с JWT.

Ограничение с одной активной личной API Key означает, что переключение между двумя независимыми окружениями нужно проектировать иначе. Разделяйте права на уровне пользователей и секрет-хранилищ, а не пытайтесь получить второй личный ключ для каждой ветки или каждого Mac.

Если workflow должен создавать или обновлять сертификаты, идентификаторы и профили, личного ключа может оказаться недостаточно. В таком случае сначала определите требуемую конечную точку, затем проверьте роль и тип ключа в официальной документации. Не выдавайте командную роль только потому, что «так обычно работает fastlane».

Шаг 4. Настройте права небольшой команды по ролям

В небольшой команде разделите как минимум три функции:

  1. владелец или администратор аккаунта управляет пользователями, ролями, ключами и отзывом;
  2. разработчик выполняет сборку и проверяет результат;
  3. автоматизация загружает архив и обрабатывает публикационный workflow.
Эти функции могут принадлежать одному человеку, но их не следует объединять в один общий секрет. Для каждого сотрудника создайте отдельную учётную запись. Если процесс касается конкретного приложения, ограниченный пользователь с личным ключом часто даёт более узкую область, чем командный ключ.

Не назначайте Admin по умолчанию. Сначала определите, требуется ли только публикация сборки, работа с TestFlight или ещё и управление идентификаторами и профилями. Затем сопоставьте задачу с актуальной таблицей ролей Apple.

Есть важное исключение: если CI работает без человека и нужная операция недоступна личному ключу, создайте отдельный командный ключ с минимальной ролью. Название ключа должно объяснять его назначение, например «ci-release-project-a», но само имя не ограничивает доступ. Не используйте его как универсальный пропуск для нескольких проектов.

Для публикации в TestFlight отдельно проверьте, что команда понимает этапы обработки сборки: успешная передача файла ещё не означает, что сборка появилась среди доступных тестировщикам. Общая последовательность описана в официальном обзоре TestFlight.

Шаг 5. Закройте доступ внешнему разработчику

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

После этого разделите три уровня доступа:

  • пользователь App Store Connect;
  • вход на удалённый Mac;
  • материалы кодовой подписи.
Если подрядчик подключается к удалённому Mac, его учётная запись на хосте не должна автоматически давать доступ к приватному ключу подписи или файлу App Store Connect API Key. Используйте отдельного системного пользователя, ограничьте права на каталоги и не оставляйте секреты в домашней папке после завершения задачи.

При завершении договора выполните отзыв в правильном порядке: удалите пользователя из команды, отзовите его API Key, закройте SSH/VNC-доступ, очистите секреты на Mac и проверьте, что процесс CI не обращается к старому файлу. Отзыв API Key не отзывает автоматически пароль хоста, сертификат подписи или доступ к репозиторию.

Важно: Key ID и Issuer ID не равны приватному ключу по чувствительности, но публикация первых двух в логе всё равно облегчает анализ конфигурации. Приватный ключ нельзя показывать в выводе команд, артефактах и отчётах CI.

Шаг 6. Спроектируйте хранение секретов на удалённом Mac

Для fastlane, Transporter или собственного скрипта используйте не один «секретный файл со всем», а раздельную модель:

  • Key ID хранится как идентификатор конфигурации;
  • Issuer ID передаётся отдельно от приватного ключа;
  • содержимое приватного ключа инжектируется только на время задания;
  • сертификат подписи и профиль Provisioning Profile управляются отдельным контуром;
  • SSH-ключи и пароль удалённого Mac не смешиваются с API-секретами.
Для JWT задайте только необходимые параметры: Issuer ID, Key ID, приватный ключ и срок действия токена в соответствии с документацией. Алгоритм и формат не угадывайте по старому конфигурационному файлу: сверяйте реализацию с [официальной инструкцией Apple по генерации JWT](https://developer.apple.com/documentation/appstoreconnectapi/generating-tokens-for-api-requests).

В примерах используйте только заполнители:

ISSUER_ID=<ISSUER_ID>
KEY_ID=<KEY_ID>
PRIVATE_KEY_PATH=/secure/path/AuthKey_<KEY_ID>.p8
TEAM_ID=<TEAM_ID>
BUNDLE_ID=<BUNDLE_ID>

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

На удалённом Mac также проверьте, не остаётся ли приватный ключ после сбоя. Временный каталог должен очищаться и при успешном, и при аварийном завершении задания. Если среда восстанавливается из снимка или пересоздаётся, документируйте, какие секреты возвращаются, а какие должны быть инжектированы заново.

Если вы выбираете постоянный хост для CI, сначала изучите руководство по удалённому Mac для сборки, а затем отдельно оцените условия аренды Mac и доступные периоды использования. Эти материалы относятся к инфраструктуре, а не к правам Apple: они не заменяют проверку ролей и конечных точек.

Шаг 7. Проведите контролируемую проверку перед ротацией

Новая API Key считается готовой не после скачивания файла, а после прохождения нескольких независимых проверок. Выполните их на тестовом приложении или на контролируемой ветке:

  1. Проверьте, что JWT создаётся с правильными заполнителями, а приватный ключ читается только процессом сборки.
  2. Запустите авторизацию через выбранный инструмент и убедитесь, что в логах нет тела ключа.
  3. Создайте Archive в Xcode и отдельно проверьте кодовую подпись; API Key не должен считаться заменой signing identity.
  4. Передайте сборку способом, который поддерживает ваш workflow, сверяясь с документацией Apple по загрузке сборок.
  5. Убедитесь, что сборка прошла обработку и стала видимой в TestFlight, а не только получила успешный ответ от команды загрузки.
  6. Проверьте операцию, ради которой выдавалась роль: чтение метаданных, управление тестированием или вызов Provisioning-эндпоинта.
  7. Отзовите старый ключ и повторите запуск: корректный процесс должен завершиться контролируемой ошибкой, а не незаметно перейти на случайный резервный секрет.
При ротации сохраняйте журнал: владелец, назначение, роль, приложение или группа приложений, дата выдачи, дата проверки и условие отзыва. Сам приватный ключ в журнал не копируйте. Для отзыва используйте [официальную процедуру Apple](https://developer.apple.com/documentation/appstoreconnectapi/revoking-api-keys), а не удаление файла на Mac: удаление локального файла не отключает ключ на стороне App Store Connect.

Условия выбора: если выполняется X, выбирайте A

Используйте следующие ветки вместо универсального правила:

  • Если публикацию выполняете вы один, нужен доступ к назначенным приложениям, а операции с Provisioning не требуются, то выбирайте личный API Key.
  • Если ключ должен работать без пользователя, а конкретная конечная точка недоступна личному ключу, то создавайте отдельный командный API Key с минимальной ролью.
  • Если несколько приложений должны иметь разные границы доступа, то сначала используйте отдельных пользователей с личными ключами; несколько командных ключей не создадут App-уровневую изоляцию.
  • Если внешний разработчик загружает TestFlight-сборку только одного проекта, то выдайте отдельного ограниченного пользователя, а не общий командный секрет.
  • Если процесс запускается на удалённом Mac, то разделяйте права хоста, API Key и материалы подписи; при невозможности раздельного хранения возвращайтесь к более безопасной схеме до выдачи ключа.
  • Если после отзыва старого ключа публикация продолжает работать, то ищите копию секрета в переменных среды, кэше, резервном каталоге или другом Mac — это не доказательство корректной ротации.

Какой вариант получает более высокую оценку

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

Для CI, которому нужна дополнительная API-возможность без интерактивного входа, отдельный командный ключ получает 8 из 10: он практичнее для безлюдного процесса, но требует более строгого контроля и не ограничивается одним приложением.

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

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

FAQ: частные случаи перед настройкой

Можно ли личным ключом закрыть весь контур подписи?

Нет. Доступ к App Store Connect API, право работать с Certificates, Identifiers & Profiles и наличие приватного ключа сертификата — разные элементы. Даже если загрузка архива через API прошла, подпись должна быть корректно настроена на машине сборки. Проверяйте нужную конечную точку и не расширяйте роль без подтверждённой необходимости.

Что выбрать для fastlane с автоматической отправкой?

Выбирайте личную API Key, если fastlane запускается от вашего пользователя и работает с доступными ему приложениями. Если процесс автономный и требует операции, которую личный ключ не поддерживает, используйте отдельный командный ключ. Сначала проверьте документацию вашей версии fastlane и реальный тестовый запуск, потому что название инструмента само по себе не определяет права Apple.

Как изолировать несколько приложений в одной команде?

Не рассчитывайте на несколько командных ключей с одинаковой ролью: командный ключ не становится App-ограниченным только из-за другого имени. Создайте отдельных пользователей, назначьте им нужные приложения и применяйте личные ключи, если это покрывает workflow. Для исключения, когда нужна командная возможность, выделяйте отдельный ключ и принимайте его более широкую область действия.

Текущий подход или постоянный удалённый Mac

Если сейчас вы публикуете с личного ноутбука, у такого подхода есть реальные ограничения: он может быть выключен во время ночного релиза, его окружение меняется вместе с повседневной работой, а секреты и сертификаты часто оказываются рядом с личными файлами. Временный облачный запуск, напротив, может усложнять доступ к macOS-инструментам, повторяемость окружения и диагностику подписи, если вы не контролируете хост целиком.

Когда командный ключ должен работать в длительном безнадзорном процессе, постоянный удалённый Mac с независимым доступом администратора, безопасной инъекцией секретов и возможностью восстановления обычно лучше случайного рабочего компьютера. MACGPU имеет смысл рассматривать именно после проверки этих условий и только если вам нужна временная или постоянно доступная среда для сборки, TestFlight и публикационных задач. Для долгой стабильной нагрузки сравните аренду с покупкой собственного Mac: физическая машина может быть выгоднее, если она будет занята постоянно и вам нужны прямые аппаратные интерфейсы.

Перед выбором аренды проверьте, как выдаются SSH/VNC-права, как удаляется окружение после завершения периода и можно ли восстановить сборочную среду без возврата старых секретов. В инфраструктуре публикации это столь же важно, как сама цена доступа: неверная изоляция Mac не исправляется более строгой ролью API Key.