Microdroid es un SO Android en miniatura 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 de las pVM no son ejecutar un SO independiente, sino ofrecer un entorno de ejecución aislado para ejecutar una parte de una app con garantías de confidencialidad e integridad más sólidas de las que puede proporcionar Android.
Con los sistemas operativos tradicionales, proporcionar una gran confidencialidad e integridad requiere una cantidad considerable 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 en glibc. La app para Android usa Bionic, la comunicación requiere un protocolo personalizado sobre vsock y la depuración con adb es difícil.
Microdroid llena estos vacíos proporcionando una imagen del SO lista para usar diseñada para requerir la menor cantidad de esfuerzo de los desarrolladores para descargar una parte de su app en una pVM. El código nativo se compila en Bionic, la comunicación se realiza a través de Binder y permite importar APEX desde el host de Android y expone un subconjunto de la API de Android, como el almacén de claves para operaciones criptográficas con claves respaldadas por hardware. En general, los desarrolladores deberían encontrar en Microdroid un entorno familiar con las herramientas a las que se acostumbraron en el SO Android completo.
Funciones
Microdroid es una versión reducida de Android con algunos componentes adicionales específicos para las PVMS. Microdroid admite lo siguiente:
- Un subconjunto de las APIs del NDK (se proporcionan todas las APIs para la implementación de libc y Bionic de Android)
- Funciones de depuración, como adb, logcat, tombstone y gdb
- Inicio verificado y SELinux
- Carga y ejecución de un archivo binario, 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 lo siguiente:
APIs de Java de Android en los paquetes
android.\*
SystemServer y Zygote
IU y gráficos
HALs
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 e inicia el kernel.boot.img
: Contiene el kernel y el ramdisk init.vendor_boot.img
: Contiene módulos de kernel específicos de la VM, como virtio.super.img
: Consta de particiones lógicas del sistema y del proveedor.vbmeta.img
: Contiene metadatos de inicio verificado.
Las imágenes de partición se incluyen en el APEX de virtualización y VirtualizationService
las empaqueta en una imagen de disco compuesta. Además de la imagen de disco compuesta del SO principal, 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 los datos de inicio verificado 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 arranque del dispositivo se analiza en la sección Firmware de la pVM del documento Arquitectura. En la figura 1, se muestran los pasos que se llevan a cabo durante la secuencia de inicio de Microdroid:
A continuación, se explica cada paso:
crosvm carga el cargador de arranque en la memoria y pvmfw comienza a ejecutarse. Antes de pasar al cargador de arranque, pvmfw realiza dos tareas:
- Verifica el cargador de arranque para comprobar si proviene de una fuente de confianza (Google o un OEM).
- Garantiza que se use el mismo cargador de arranque de manera coherente en varios arranques de la misma PVm a través del 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 cargador de arranque en la imagen de instancia y la encripta. Por lo tanto, la próxima vez que se inicie la pVM con la misma imagen de instancia, pvmfw descifrará la identidad guardada de la imagen de instancia y verificará que sea la misma que se guardó anteriormente. Si las identidades difieren, pvmfw se niega a arrancar.
Luego, el bootloader inicia Microdroid.
El cargador de arranque accede al disco de la instancia. Al igual que pvmfw, el cargador de arranque 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.
El cargador de arranque verifica vbmeta y las particiones encadenadas, como
boot
ysuper
, y, si se realiza correctamente, deriva los secretos de la pVM de la siguiente etapa. Luego, Microdroid le entrega el control al kernel.Dado que el cargador de arranque ya verificó la superpartición (paso 3), el kernel la activa de forma incondicional. Al igual que con el sistema Android completo, la superpartición consta de varias particiones lógicas montadas sobre dm-verity. Luego, el control se pasa al proceso de
init
, que inicia varios servicios nativos. La secuencia de comandosinit.rc
es similar a la de Android completo, pero se adapta a las necesidades de Microdroid.El proceso
init
inicia el administrador de Microdroid, que accede a la imagen de la instancia. El servicio del administrador de Microdroid descifra la imagen con la clave que se pasó desde la etapa anterior y lee las claves públicas y los contadores de reversión de los APK y APEX del cliente en los que confía esta pVM.zipfuse
yapexd
usan esta información más adelante cuando montan el APK del cliente y los APEX solicitados, respectivamente.El servicio del administrador de Microdroid inicia
apexd
.apexd
activa los APEX en los directorios/apex/<name>
. La única diferencia entre la forma en que Android y Microdroid montan los APEX es que, en Microdroid, los archivos APEX provienen de dispositivos de bloques virtuales (/dev/vdc1
, …), no de archivos normales (/system/apex/*.apex
).zipfuse
es el sistema de archivos FUSE de Microdroid.zipfuse
activa el APK del cliente, que es básicamente un archivo ZIP como sistema de archivos. Debajo, el pVM pasa el archivo APK como un dispositivo de bloqueo virtual con dm-verity, 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 activa APEX.El flujo de arranque regresa al servicio del administrador de Microdroid. Luego, el servicio del administrador se comunica con
VirtualizationService
de Android a través de RPC de Binder para poder informar eventos importantes, como fallas o apagados, y aceptar solicitudes, como la finalización de 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 que 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 no confían entre sí a través de los límites de la pVM.
Básicamente, AuthFS es un sistema de archivos remoto con verificaciones de integridad transparentes en operaciones de acceso individuales, similar 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, el backend (fd\_server
) se inicia con una configuración por archivo que especifica si está destinado a la entrada (solo lectura) o la salida (lectura y escritura). Para la entrada, el frontend exige que el contenido coincida con un hash conocido, además de un árbol de Merkle para la verificación de acceso. Para la salida, AuthFS mantiene internamente un árbol hash del contenido tal como se observa en 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, pero esto podría cambiar 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 se pueden verificar que fueron producidas por la pVM.
RPC de Binder
La mayoría de las interfaces de Android se expresan en AIDL, que se basa en el controlador del kernel de Linux de Binder. Para admitir interfaces entre pVM, se reescribió 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 las interfaces AIDL existentes de Android se usen 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 nuevas conexiones. Los clientes pueden conectarse a este servidor con un objeto RpcSession
, obtener el objeto Binder
y usarlo exactamente como se usa un objeto Binder
con el controlador Binder del kernel.