Pull to refresh

Процедура нотаризации Electron приложения для macOS 10.14.5

JavaScriptDevelopment for MacOS
Sandbox

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



Введение


Спустя 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



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



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


  • 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 необходимо просто поставить галочку о нотаризации



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


$ 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.



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


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



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


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


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


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



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


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




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


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.inherit.plist


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <dict>
    <key>com.apple.security.cs.allow-jit</key>
    <true/>
    <key>com.apple.security.cs.allow-unsigned-executable-memory</key>
    <true/>
    <key>com.apple.security.cs.allow-dyld-environment-variables</key>
    <true/>
    <key>com.apple.security.cs.disable-library-validation</key>
    <true/>
    <key>com.apple.security.cs.disable-executable-page-protection</key>
    <true/>
    <key>com.apple.security.cs.debugger</key>
    <true/>
    <key>com.apple.security.automation.apple-events</key>
    <true/>

  </dict>
</plist>

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


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


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



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

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



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

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


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


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


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



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


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


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


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


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



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


Tags:javascriptmacOSelectron js
Hubs: JavaScript Development for MacOS
Total votes 19: ↑18 and ↓1 +17
Views8.7K

Comments 33

Only those users with full accounts are able to leave comments. Log in, please.

Popular right now

Node.js: серверный JavaScript
April 26, 202127,000 ₽Loftschool
Интенсив «Full stack разработчик. JavaScript»
April 26, 2021245,000 ₽Elbrus Coding Bootcamp
Интенсив «Full stack разработчик. JavaScript»
April 26, 2021190,000 ₽Elbrus Coding Bootcamp
Специалист JavaScript
May 24, 202122,690 ₽Специалист.ру