Microdroide

Microdroid es un SO mini-Android que se ejecuta en una pVM. No es necesario que uses Microdroid, puedes iniciar una VM con cualquier SO. Sin embargo, los casos de uso principales para las PVM no ejecutan un SO independiente, sino que ofrecen un entorno de ejecución aislado para ejecutar una parte de una app con garantías de confidencialidad e integridad más sólidas que las que puede proporcionar Android.

Con los sistemas operativos tradicionales, proporcionar una confidencialidad y una integridad sólidas requiere una gran cantidad de trabajo (a menudo duplicado) porque los sistemas operativos tradicionales no se ajustan a la arquitectura general de Android. Por ejemplo, con la arquitectura estándar de Android, los desarrolladores deben implementar un medio para cargar y ejecutar de forma segura parte de su app en la pVM, y la carga útil se compila con glibc. La app para Android usa Bionic, la comunicación requiere un protocolo personalizado a través de vsock y la depuración con adb es un desafío.

Microdroid cubre estas brechas proporcionando una imagen de SO lista para usar diseñada para requerir la menor cantidad de esfuerzo de los desarrolladores para transferir una parte de su app a una pVM. El código nativo se compila con Bionic, la comunicación se realiza a través de Binder y permite importar APEX desde el host de Android y exponer un subconjunto de la API de Android, como el almacén de claves para operaciones de criptografía con claves respaldadas por hardware. En general, los desarrolladores deberían encontrar un entorno familiar en Microdroid con las herramientas que ya conocen del SO Android completo.

Funciones

Microdroid es una versión reducida de Android con algunos componentes adicionales específicos de las pVM. Microdroid admite lo siguiente:

  • Un subconjunto de las APIs del NDK (se proporcionan todas las APIs para la implementación de libc y Biionic en Android)
  • Funciones de depuración, como adb, logcat, tombstone y gdb
  • Inicio verificado y SELinux
  • Carga y ejecución de un ejecutable, junto con bibliotecas compartidas, incorporado en un APK
  • RPC de Binder a través de vsock y el intercambio de archivos con verificaciones de integridad implícitas
  • Carga de APEX

Microdroid no admite los siguientes elementos:

  • APIs de Java para Android en los paquetes android.\*

  • SystemServer y Zygote

  • IU y gráficos

  • HAL

Arquitectura de Microdroid

Microdroid es similar a Cuttlefish, ya que ambos tienen una arquitectura similar a la de Android estándar. Microdroid consta de las siguientes imágenes de partición agrupadas en una imagen de disco compuesta:

  • bootloader: Verifica y, luego, inicia el kernel.
  • boot.img: Contiene el kernel y el ramdisk de init.
  • vendor_boot.img: Contiene módulos de kernel específicos de la VM, como virtio.
  • super.img: Consiste en particiones lógicas del sistema y del proveedor.
  • vbmeta.img: Contiene metadatos de inicio verificado.

Las imágenes de partición se envían en el APEX de virtualización y se empaquetan en una imagen de disco compuesto por VirtualizationService. Además de la imagen de disco compuesto principal del SO, VirtualizationService es responsable de crear estas otras particiones:

  • payload: Es un conjunto de particiones respaldadas por los APEX y los APK de Android.
  • instance: Es una partición encriptada para conservar datos de inicio verificados por instancia, como la sal por instancia, las claves públicas de APEX de confianza y los contadores de reversión.

Secuencia de inicio

La secuencia de inicio de Microdroid se produce después del inicio del dispositivo. El inicio del dispositivo se analiza en la sección Firmware de pVM del documento Arquitectura. En la Figura 1, se muestran los pasos que se realizan durante la secuencia de inicio de Microdroid:

Flujo de inicio seguro de la instancia de microdroid

Figura 1: Flujo de inicio seguro de la instancia de microdroid

A continuación, se explican los pasos a seguir:

  1. Crosvm carga el bootloader en la memoria y pvmfw comienza a ejecutarse. Antes de pasar al bootloader, pvmfw realiza dos tareas:

    • Verifica el bootloader para comprobar si proviene de una fuente de confianza (Google o un OEM).
    • Garantiza que se use el mismo bootloader de forma coherente en varios inicios de la misma pVM mediante el uso de la imagen de instancia. Específicamente, la pVM se inicia inicialmente con una imagen de instancia vacía. pvmfw almacena la identidad del bootloader en la imagen de la instancia y la encripta. Por lo tanto, la próxima vez que se inicie la pVM con la misma imagen de instancia, pvmfw desencriptará la identidad guardada de la imagen de instancia y verificará que sea la misma que se guardó anteriormente. Si las identidades son diferentes, pvmfw no se iniciará.

    Luego, el bootloader inicia Microdroid.

  2. El bootloader accede al disco de la instancia. Al igual que pvmfw, el bootloader tiene una unidad de disco de instancia con información sobre las imágenes de partición que se usaron en esta instancia durante los arranques anteriores, incluida la clave pública.

  3. El bootloader verifica vbmeta y las particiones en cadena, como boot y super, y, si se ejecuta correctamente, deriva los secretos de pVM de la siguiente etapa. Luego, Microdroid le entrega el control al kernel.

  4. Como el bootloader ya verificó la superpartición (paso 3), el kernel activa la superpartición de forma incondicional. Al igual que con Android completo, la superpartición consta de varias particiones lógicas activadas sobre dm-verity. Luego, el control se pasa al proceso init, que inicia varios servicios nativos. La secuencia de comandos init.rc es similar a la de Android completo, pero se adapta a las necesidades de Microdroid.

  5. El proceso init inicia el administrador de Microdroid, que accede a la imagen de la instancia. El servicio de administrador de Microdroid desencripta la imagen con la clave que se pasa desde la etapa anterior y lee las claves públicas y los contadores de reversión del APK y los APEX del cliente en los que confía esta pVM. zipfuse y apexd usan esta información más adelante cuando activan el APK del cliente y los APEX solicitados, respectivamente.

  6. El servicio de administrador de Microdroid inicia apexd.

  7. apexd activa los APEX en los directorios /apex/<name>. La única diferencia entre Android y Microdroid activan APEX es que, en Microdroid, los archivos APEX provienen de dispositivos de bloques virtuales (/dev/vdc1 y ...), no de archivos normales (/system/apex/*.apex).

  8. zipfuse es el sistema de archivos FUSE de Microdroid. zipfuse activa el APK del cliente, que es, en esencia, un archivo ZIP como un sistema de archivos. Debajo, la pVM pasa el archivo APK como un dispositivo de almacenamiento en bloques virtual con dm-verity, al igual que APEX. El APK contiene un archivo de configuración con una lista de APEX que el desarrollador de la app solicitó para esta instancia de pVM. apexd usa la lista cuando se activan APEX.

  9. El flujo de inicio regresa al servicio del administrador de Microdroid. Luego, el servicio de administrador se comunica con VirtualizationService de Android a través de RPC de Binder para informar eventos importantes, como fallas o cierres, y aceptar solicitudes, como finalizar la pVM. El servicio de administrador lee la ubicación del objeto binario principal del archivo de configuración del APK y lo ejecuta.

Intercambio de archivos (AuthFS)

Es común que los componentes de Android usen archivos para la entrada, la salida y el estado, y los pasen como descriptores de archivos (tipo ParcelFileDescriptor en AIDL) con acceso controlado por el kernel de Android. AuthFS facilita una funcionalidad similar para intercambiar archivos entre extremos que desconfían mutuamente entre los límites de la pVM.

En esencia, AuthFS es un sistema de archivos remoto con verificaciones de integridad transparentes en operaciones de acceso individuales, similares a fs-verity. Las verificaciones permiten que el frontend, como un programa de lectura de archivos que se ejecuta en una pVM, detecte si el backend no confiable, por lo general, Android, manipuló el contenido del archivo.

Para intercambiar archivos, se inicia el backend (fd\_server) con la configuración por archivo que especifica si está destinado a la entrada (solo lectura) o a la salida (lectura y escritura). Para la entrada, el frontend aplica la restricción de que el contenido coincida con un hash conocido, sobre un árbol de Merkle para la verificación de acceso. Para la salida, AuthFS mantiene internamente un árbol de hash del contenido tal como se observa desde las operaciones de escritura y puede aplicar la integridad cuando se vuelven a leer los datos.

Actualmente, el transporte subyacente se basa en RPC de Binder. Sin embargo, es posible que eso cambie en el futuro para optimizar el rendimiento.

Administración de claves

Las pVM se proporcionan con una clave de sellado estable que es adecuada para proteger los datos persistentes y una clave de certificación que es adecuada para producir firmas que la pVM puede verificar.

RPC de Binder

La mayoría de las interfaces de Android se expresan en AIDL, que se compila sobre el controlador de kernel de Binder Linux. Para admitir interfaces entre pVM, se volvió a escribir el protocolo Binder para que funcione a través de sockets, vsock en el caso de las pVM. El funcionamiento a través de sockets permite que se usen las interfaces AIDL existentes de Android en este nuevo entorno.

Para configurar la conexión, un extremo, como la carga útil de pVM, crea un objeto RpcServer, registra un objeto raíz y comienza a escuchar conexiones nuevas. Los clientes pueden conectarse a este servidor con un objeto RpcSession, obtener el objeto Binder y usarlo de la misma manera que se usa un objeto Binder con el controlador de Binder del kernel.