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К показов