Asylo от Google: как работать с новым фреймворком
Анклавы — интересная парадигма, позволяющая запускать код в изоляции даже от ядра системы. Но для работы с анклавами требуются определённые инструменты. Именно для этого и создан фреймворк Asylo, о котором сегодня пойдёт речь.
4К открытий4К показов
Как мы писали ранее, Google анонсировала новый фреймворк, который позволяет выносить часть функциональности приложения в защищённую среду выполнения, называемую анклав. В данной статье разберёмся, как использовать этот фреймворк.
Что такое анклав
В традиционных системах ядро ОС имеет неограниченный доступ к аппаратным ресурсам машины. Ядро обычно предоставляет большинство прав доступа root-пользователю без каких-либо ограничений. Кроме того, root-пользователь может расширять или изменять ядро в запущенной системе. В результате, если злоумышленник сможет выполнить код с root-привилегиями, он сможет скомпрометировать все секретные данные и обойти все политики безопасности на машине.
Анклавы — это новая парадигма, которая меняет положение дел. Анклав — это особый контекст выполнения, в котором код может работать будучи защищённым даже от ядра ОС. Это значит, что даже пользователь с root-правами не сможет извлечь секреты анклава или поставить под угрозу его целостность. Такая защита обеспечивается с помощью технологий аппаратной изоляции, таких как Intel SGX или ARM TrustZone, или даже с помощью дополнительных программных уровней, таких как гипервизор. Эти технологии позволяют создавать новые формы изоляции за пределами обычного разделения ядро-пользователь.
Новые функции безопасности привлекательны для разработчиков защищённых приложений, но на практике существует большая разница между наличием возможностей и разработкой приложений, которые используют эту возможность. Для создания полезных приложений в анклаве требуются инструменты для создания, загрузки и управления анклавами. Для полноценной работы в анклаве требуется поддержка языков программирования и доступ к библиотекам основных платформ.
Что такое Asylo
Asylo — это open-source фреймворк для разработки приложений в анклаве. Он определяет абстрактную модель анклава, которую можно привязать к различным анклавным технологиям (например, анклавный бэкенд). Asylo предоставляет платформу для разработки программного обеспечения, которая поддерживает всё более широкий спектр вариантов использования.
Далее мы шаг за шагом напишем простой анклав. В примере будет показана инициализация анклава, передача аргументов коду, работающему внутри анклава, и возвращение результатов. Несмотря на то, что это довольно простой пример, он демонстрирует основную функциональность Asylo и шаги, которым нужно следовать для использования этой функциональности.
Начало работы
Запустите следующие команды, чтобы загрузить наш Docker-контейнер и исходники, которые мы будем использовать Прочитайте README для дополнительных инструкций по использованию Docker.
На месте MY_PROJECT вы можете указать любую директорию. Эта переменная среды затем будет использоваться в инструкциях по созданию и запуску приложения в анклаве.
Примеры кода можно найти в репозитории Asylo SDK на GitHub.
Общий подход
В Asylo анклав работает в контексте пользовательского приложения. Тем не менее, по соображениям безопасности и компактности, Asylo не поддерживает прямое взаимодействие кода анклава и ОС. Вместо этого для всех таких взаимодействий требуется посредник в виде кода, который работает за пределами анклава. Такой код мы называем недоверенным приложением, в то время как код, запущенный в анклаве, называем доверенным приложением или просто анклавом.
В данном гайде мы фокусируемся на модели, где основная часть пользовательской логики находится внутри анклава. В этой модели разработчику возможно придётся написать определённое количество шаблонного кода (похожего на тот, что будет далее этой статье), однако большая часть кода, необходимого для создания, запуска и взаимодействия с анклавами предусмотрена фреймворком Asylo.
Asylo использует объектно-ориентированный подход к разработке приложений в анклаве. По сути анклав представляет собой набор частных данных и логики вместе с публичными методами для доступа к ним. С этой целью Asylo создаёт анклав, используя TrustedApplication, абстрактный класс, определяющий разные точки входа в анклав. Для реализации приложения в анклаве разработчику нужно создать подкласс TrustedApplication и реализовать соответствующие методы.
В этой статье мы будем ссылаться на экземпляр TrustedApplication, называя его как «доверенное приложение», так и «анклав».
Модель взаимодействия анклавов
В Asylo анклавы работают с protocol-buffer сообщениями; все входы и выходы анклава представляют собой protocol buffer.
Процесс перехода с ненадёжного приложения к анклаву мы назовём входом в анклав, а обратный процесс — выходом из анклава.
В Asylo все анклавные взаимодействия обрабатываются через абстрактный класс EnclaveClient. Asylo предоставляет конкретные реализации этого класса для всех поддерживаемых анклавных технологий. В классе EnclaveClient определено несколько методов для входа в анклав. С другой стороны, выход из анклава проходит неявно — это происходит автоматически, когда анклав завершает работу или когда анклав обращается к службам операционной системы.
Среди множества методов входа в анклав, определённых интерфейсом EnclaveClient, три представляют особый интерес для пользователей Asylo:
EnterAndInitialize: Этот метод принимает сообщениеEnclaveConfig, которое содержит основные настройки конфигурации анклава, и передаёт его в анклав. Это приватный метод и он неявно вызывается фреймворком Asylo при загрузке бинарного образа анклава.EnterAndRun: Этот метод принимает сообщениеEnclaveInputи передаёт его в анклав, который в результате может вернутьEnclaveOutput. СообщенияEnclaveInputиEnclaveOutputможно расширить с помощью protobuf-расширений для удовлетворения требований к обработке данных в приложении. Этот метод публичный и его можно вызывать произвольное количество раз с разными входными данными после инициализации анклава.EnterAndFinalize: Этот метод принимает сообщениеEnclaveFinal, в котором может содержаться информация, необходимая для финализации анклава, и передаёт это сообщение анклаву прямо перед его уничтожением. Это приватный метод классаEnclaveClient, который неявно вызывается фреймворком при уничтожении анклава.
Каждый EnclaveClient связан ровно с одним анклавом и Asylo передаёт вызовы к методам EnclaveClient соответствующим методам анклава в соответствующем экземпляре TrustedApplication, который может быть переопределён пользователем анклава.
В интерфейсе TrustedApplication объявлены методы, соответствующие трём входным методам, определённым абстрактным классом EnclaveClient:
Initialize: Этот метод принимает сообщениеEnclaveConfigиз EnclaveClient::EnterAndInitialize и инициализирует анклав, используя настройки конфигурации вEnclaveConfig.Run: Этот метод принимает сообщениеEnclaveInputизEnclaveClient::EnterAndRun, возвращает сообщениеEnclaveOutputи проводит доверенное выполнение.Finalize: Этот метод принимает сообщениеEnclaveFinalизEnclaveClient::EnterAndFinalizeи готовит анклав к уничтожению.
Жизненный цикл анклава
Вход в анклав аналогичен системному вызову. Точка входа в анклав представляет собой интерфейс для защищённого кода с доступом к ресурсам анклава. Аргументы копируются в защищённую память анклава при входе, а результаты копируются при выходе.
В коде выше показаны три точка входа в анклав. Давайте пройдёмся по каждой части кода.
Инициализация
Недоверенное приложение выполняет следующие шаги для инициализации доверенного:
- Настраивает экземпляр
EnclaveManagerс параметрами по умолчанию.EnclaveManagerобрабатывает все ресурсы анклава в недоверенном приложении. - Настраивает объект
SimLoaderдля получения бинарного образа анклава с диска. - Вызывает
EnclaveManager::LoadEnclave, чтобы привязать анклав к имени"demo enclave". Затем неявно вызывается метод анклаваInitialize.
Безопасное выполнение
Недоверенное приложение выполняет следующие шаги для безопасного выполнения рабочей нагрузки в доверенном приложении:
- Получает дескриптор анклава через
EnclaveManager::GetClient. - Передаёт произвольные входные данные в
EnclaveInput. В этом примере используется строковое protobuf-расширение для сообщенияEnclaveInput. Поле этого расширение используется для передачи данных анклаву для шифрования. - Вызывает анклав с помощью
EnclaveClient::EnterAndRun. Этот метод является основной точкой входа, используемой для отправки сообщений в анклав. Его можно вызывать произвольное число раз. - Получает результат из анклава в
EnclaveOutput. Разработчики могут добавить protobuf-расширения к сообщениюEnclaveOutputдля предоставления произвольных выходных значений из их анклава.
Финализация
Недоверенное приложение выполняет следующие шаги для финализации доверенного приложения:
- Передаёт произвольные данные финализации в анклав и уничтожает анклав с помощью
EnclaveManager::DestroyEnclave. - Запускает метод анклава
Finalize. Фреймворк Asylo неявно выполняет этот шаг во время уничтожения анклава.
Пишем приложение в анклаве
Мы уже знаем, как инициализировать, запускать и финализировать анклав с помощью Asylo. Эти вызовы происходили на недоверенной стороне анклава. Теперь давайте посмотрим на код на доверенной стороне.
В коде выше определяется класс EnclaveDemo, который наследуется от TrustedApplication, и реализуется логика безопасного выполнения анклава в методе Run. Этот метод шифрует входное сообщение и выводит зашифрованный текст.
Класс TrustedApplication предоставляет реализации методов Initialize, Run, и Finalize по умолчанию. Предполагается, что создатель анклава переопределит эти методы должным образом для реализации логики анклава. Как показано в этом примере, создатель анклава обычно переопределяет метод TrustedApplication::Run для обеспечения анклава основной логикой и использует этот метод для взаимодействия с анклавом. В качестве альтернативы создатель анклава может запустить RPC-сервер (например, gRPC-сервер) в методе TrustedApplication::Initialize и затем взаимодействовать с анклавом через сервер. В этом случае разработчик может не переопределять метод TrustedApplication::Run. Фреймворк Asylo гибкий и позволяет разработчикам использовать анклавы наиболее удобным для них способом.
Пишем и запускаем приложение в анклаве
Asylo реализует анклавный бэкенд для среды, основанной на симуляторах. Чтобы написать приложение в анклаве, мы должны объявить несколько объектов, которые будут использовать этот бэкенд.
Показанный выше файл Bazel BUILD определяет логику нашего анклава в sim_enclave с именем demo_enclave. Этот объект содержит нашу реализацию TrustedApplication и связан с временем выполнения Asylo. Мы используем правило sim_enclave для создания анклава, который можно запустить в режиме симуляции.
Недоверенным компонентом является demo_driver, который содержит код для обработки логики инициализации, запуска и финализации анклава, а также отправки и получения сообщений через интерфейс анклава. В приложении вне анклава quickstart был бы cc_binary объектом, однако правило debug_enclave_driver упрощает сочетание объектов драйвера и анклава. В частности, оно гарантирует, что demo_driver.cc компилируется с помощью crosstool хоста, что зависимости данных анклава компилируются с помощью бэкенд-специфичного crosstool анклава и что demo_driver вызывается с флагом --enclave_path, который указывает путь к бинарному образу анклава.
Теперь давайте запустим демо-анклав внутри образа Docker, который мы скачали ранее. Вы можете установить флаг --message, который передаётся объекту //quickstart, со строкой, которую вы хотите зашифровать.
Примечание Следующая команда запускает анклав в режиме симуляции.
Поздравляем, вы написали и запустили своё первое приложение в анклаве!
Что делать дальше
Теперь вы знаете достаточно о Asylo для того, чтобы попробовать изменить приложение в анклаве. Вот что можно попробовать сделать:
- Обратите внимание на то, что на данный момент мы никак не используем переменную
output, переданную вEnterAndRun. ИспользуйтеSetEnclaveOutputMessageвdemo_enclave.ccиGetEnclaveOutputMessage
вdemo_driver.cc, чтобы вернуть зашифрованное сообщение из анклава в драйвер и отобразить его. Вывод приложения не должен измениться. - Функцию
EnterAndRunможно вызывать много раз после инициализации анклава. Изменитеdemo_driver.cc, чтобы добавить ещё один вызовEnterAndRunс целью перезайти в анклав с другим сообщением для шифрования. - Используйте protobuf-расширения в сообщении
EnclaveInput, чтобы добавить возможность отправки зашифрованного сообщения в анклав для дешифровки с помощью функцииDecryptMessage.
Код доступен на GitHub.
4К открытий4К показов



