[Из песочницы] Процедура нотаризации Electron приложения для macOS 10.14.5

С выходом macOS 10.14.5, Apple добавила обязательную процедуру нотаризации (Notarization process) приложений перед их распространением. Что это и какие сложности возникли с этим обновлением при разработке на Electron.js я и хотел бы рассказать.

7cvoqzkecwn_jtlk82d33eghvda.png


Введение

Спустя 2 года после бума Electron.js, все горячие холивары о том насколько он плох и зачем нужен — затихли. Давайте не будем разжигать их заново в комментариях. Спасибо.

На нашем проекте используется electron-builder для сборки приложения, но и для electron-packager эта процедура будет примерно аналогична.

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

Подписание приложения c electron-builder не выглядит сложным, но для полноты картины, я в кратце расскажу и об этой процедуре. Если у вас нет проблем с подписанием приложения, можете пропустить эту главу.


Подписание приложения

Для подписания приложения нам необходимо экспортировать сертификаты из личного кабинета разработчика Apple. Нам потребуются:


  • Developer ID Application
  • Developer ID Installer
  • *3rd Party Mac Developer Application и 3rd Party Mac Developer Installer (Если планируется публиковать приложение в AppStore)

Сертификат Developer ID Installer выпускается под определенное приложение, для этого необходим bundleID. У electron-builder он задается параметром «appId» в package.json

pzxcutr2ad_xxln6bqd5dlegi1q.png

Сертификаты необходимо собрать в единый файл. Для этого добавляем их в keychain (2 клика по сертификату).
Затем переходим в keychain, выбираем нужные сертификаты и в контекстном меню нажимаем «export items». После экcпорта мы получаем один файл с расширением .p12.

uqsinodlzfxhbrk0y3eqcyw2igi.png

После полученного файла сертификата, добавляем следующие записи в переменные окружения


  • CSC_LINK (путь до файла сертификата .p12)
  • CSC_KEY_PASSWORD (пароль доступа к сертификату)

Если не добавлять эти переменные, то сборщик будет автоматически искать подходящие ключи в хранилище keychain. Добавление этих записей, позволяет точно определить сертификаты, которые вы хотите использовать для подписи.

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

Гладко это работало до выхода macOS 10.14.5…


Что изменилось с выходом macOS 10.14.5

Небольшое отступление. Выполняя ночью последние работы над новым патчем, решил оставить сборку продакшн версии на утро. Заметив, что пришло обновление на macOS запустил его и пошел спать.

На следующее утро с удивлением увидел, что сборка падает от незнакомой ошибки в момент подписания приложения — «Unnotarized Developer ID».


Не откладывай на завтра то, что можно сделать сегодня. Бенджамин Франклин


Суть проблемы

Начиная с macOS 10.14.5, Apple ввела обязательную процедуру нотаризации. Первая статья Apple об этом была в 2018 году, но именно с этим обновлением эта процедура стала обязательной. Как она выглядит.

Вы собираете приложение → отправляете его на сервер Apple → Apple заверяет его → Возвращает статус успешного заверения → Выполняется команда установки штампа заверения.

Для разработчиков на Xcode необходимо просто поставить галочку о нотаризации

-xsat4hgljlntg9x36agif_k13c.png

Так же процесс нотаризации собранного приложения можно выполнить командой в терминале.

$ xcrun altool --notarize-app --primary-bundle-id "com.example.ote.zip" --username "AC_USERNAME" --password "@keychain:AC_PASSWORD" --file OvernightTextEditor_11.6.8.zip


  • primary-bundle-id — bundleID приложения
  • username — логин пользователя на developer.apple.com
  • password — «app-specific password». Его можно создать в личном кабинете appleid.apple.com под учеткой разработчика.

Если не выполнить процедуру нотаризации, то при попытке пользователя установить приложение, вылетает окошко с ошибкой. За проверку на безопасность приложения отвечает gatekeeper. Именно он и ломал сборку приложения на electron-builder.

v6gm-azhc2cgsxtvrqylh4hz_l0.jpeg


Как выглядил процесс сборки electron-builder

После сборки приложения в .app файл, с помощью утилиты electron-osx-sign происходило подписание приложения. После подписания сертификатом запускался процесс проверки приложения gatekepper’ом. Но с выходом обновления gatekeeper стал проверять и корректную нотаризацию приложения, это и не давало успешно завершить процедуру подписания приложения.

gs-vg0zydktmqzserrjzfqrjmi0.png


Патч для возможности нотаризации

Github пользователь Kallin довольно оперативно предложил коммит решения, с добавлением двух новых параметров в настройки. Первый это «gatekeeperAssess» — отключает валидацию сборки после подписания и второй «sign» — который отключает подписание сертификатом установочного файла (dmg). Данный коммит вошел в релиз electron-builder 20.43.0.

Для самого процесса нотаризации у electron-userland есть модуль electron-notarize, который выполняет эту задачу, нужно лишь написать небольшой скрипт и запустить его, используя хук afterSign.


Процесс подписания и нотаризации приложения

mcqgn5yehovsqcheiyq_5a1b-qo.png

Первоначально нужно проверить, что у вас установлен electron-builder версии >=20.43.0 и установить пакет electron-notarize.

В переменные окружения добавим 2 записи:

q0cobvddzumimcg6kzffx9hzjkg.png

Теперь создадим скрипт нотаризации, который будет выполняться после подписания приложения.

const notarize = require('electron-notarize').notarize;

module.exports = async (context) => {
    const { electronPlatformName } = context;
    if (electronPlatformName === 'darwin') {
        try {
            console.log('Try notarize app');
            await notarize({
              appBundleId: 'APP_BUNDLE_ID',
              appPath: './dist/mac/APP_NAME.app',
              appleId: process.env.appleId,
              appleIdPassword: process.env.appleASP,
            });
            console.log('Success notarize');
        } catch (err) {
            console.log(err);
        }
  }
};

Сохраняем его в удобном для вас месте.

Так же для корректной нотаризации нам потребуется определить права доступа
к ресурсам системы для нашего приложения. Для этого создадим файл build/entitlements.mac.plist




  
    com.apple.security.cs.allow-jit
    
    com.apple.security.cs.allow-unsigned-executable-memory
    
    com.apple.security.cs.allow-dyld-environment-variables
    
    com.apple.security.cs.disable-library-validation
    
    com.apple.security.cs.disable-executable-page-protection
    
    com.apple.security.cs.debugger
    
    com.apple.security.automation.apple-events
    

  

Содержимое файла в моем случае. Для вас может быть другая конфигурация. Описание всех полей.
Обязательным для Electron.js является — com.apple.security.cs.allow-unsigned-executable-memory.

Теперь обновим настройки в package.json

В разделе для macOS:

lg-2tnabm6hagn1s17mrcfgzhbw.png


  • gatekeeperAssess (отключает валидацию приложения на стороне electron-osx-sign)
  • hardenedRuntime (позволяет создать перечень разрешений безопасности и доступа к ресурсам системы)
  • entitlements (путь к файлу резрешений доступа для нашего приложения)

В общем разделе настроек electron-builder:

jb6eekoqrg9qvfm2wxhilunfdjk.png


  • afterSign (путь до скрипта нотаризации, который будет зупущен после подписания приложения)

Запускаем процесс сборки. Может показаться, что он немного подвис, но передача файла на сервер Apple и ожидание ответа занимает какое-то время (от 3 до 10 минут)

Состояние нотаризации можно посмотреть в терминале, выполнив команду:

$ xcrun altool --notarization-history 0 -u $appleId -p $appleASP

Ответ будет представлен таблицей. Поле статус может иметь значение 'process', 'approved', 'invalid'

lf-cgtkjf3jwkwses9dnbaztnig.png

При статусе 'invalid' по номеру запроса можно посмотреть, что именно пошло не так.

$ xcrun altool --notarization-info "RequestUUID" -u $appleId

Вот и весь процесс подписания и нотаризации. Надеюсь, моя статья окажется вам полезна. Спасибо.


Небольшое дополнение

При передаче приложения для тестирования, обнаружился интересный глюк. Приложение полученное через Телеграм, просто отказывалось запускаться. При просмотре логов обнаружилось, что приложение помещено в карантин Телеграмом. По какой причине и как это произошло, я не смог найти ответ. При отправке файла через Яндекс.Диск (или любой другой способ, чтобы скачать через браузер) такой проблемы не возникает.

ww3j4kakytwimj8uvz89d0jm2l0.png


Полезные ссылки


© Habrahabr.ru