Microdroid ist ein Mini-Android-Betriebssystem, das in einer privaten virtuellen Maschine ausgeführt wird. Sie müssen Microdroid nicht verwenden, sondern können eine VM mit einem beliebigen Betriebssystem starten. Die primären Anwendungsfälle für PVMs sind jedoch nicht die Ausführung eines eigenständigen Betriebssystems, sondern die Bereitstellung einer isolierten Ausführungsumgebung für die Ausführung eines Teils einer App mit stärkeren Vertraulichkeits- und Integritätsgarantien als Android.
Bei herkömmlichen Betriebssystemen erfordert die Bereitstellung von hoher Vertraulichkeit und Integrität einen erheblichen Aufwand (oftmals doppelt), da herkömmliche Betriebssysteme nicht zur übergeordneten Android-Architektur passen. In der Standard-Android-Architektur müssen Entwickler beispielsweise eine Möglichkeit zum sicheren Laden und Ausführen eines Teils ihrer App in der pVM implementieren. Die Nutzlast wird für glibc erstellt. Die Android-App verwendet Bionic, die Kommunikation erfordert ein benutzerdefiniertes Protokoll über vsock und das Debugging mit adb ist schwierig.
Microdroid schließt diese Lücken, indem es ein sofort einsatzbereites Betriebssystem-Image bereitstellt, das Entwicklern den geringsten Aufwand abverlangt, um einen Teil ihrer App in eine pVM auszulagern. Nativer Code wird für Bionic erstellt, die Kommunikation erfolgt über Binder und es können APEX-Dateien vom Host-Android importiert werden. Außerdem wird eine Teilmenge der Android API bereitgestellt, z. B. der Keystore für kryptografische Vorgänge mit hardwaregestützten Schlüsseln. Insgesamt sollten Entwickler Microdroid als vertraute Umgebung mit den Tools empfinden, die sie im vollständigen Android-Betriebssystem gewohnt sind.
Funktionen
Microdroid ist eine abgespeckte Version von Android mit einigen zusätzlichen Komponenten, die speziell für pVMs entwickelt wurden. Microdroid unterstützt:
- Eine Teilmenge der NDK-APIs (alle APIs für die Android-Implementierung von libc und Bionic sind verfügbar)
- Debugging-Funktionen wie adb, logcat, tombstone und gdb
- Verifizierter Bootmodus und SELinux
- Laden und Ausführen einer Binärdatei zusammen mit gemeinsam genutzten Bibliotheken, die in ein APK eingebettet sind
- Binder-RPC über vsock und Austausch von Dateien mit impliziten Integritätsprüfungen
- APEXes werden geladen
Microdroid unterstützt Folgendes nicht:
Android-Java-APIs in den
android.\*
-PaketenSystemServer und Zygote
Grafiken/Benutzeroberfläche
HALs
Microdroid-Architektur
Microdroid ähnelt Cuttlefish, da beide eine Architektur haben, die dem Standard-Android ähnelt. Microdroid besteht aus den folgenden Partition-Images, die in einem zusammengesetzten Datenträger-Image gruppiert sind:
bootloader
: Überprüft und startet den Kernel.boot.img
: Enthält den Kernel und die Init-Ramdisk.vendor_boot.img
: Enthält VM-spezifische Kernel-Module wie virtio.super.img
– Besteht aus logischen System- und Anbieterpartitionen.vbmeta.img
: Enthält Metadaten zum verifizierten Bootmodus.
Die Partition-Images werden im Virtualization APEX ausgeliefert und von VirtualizationService
in einem zusammengesetzten Laufwerk-Image verpackt. Zusätzlich zum zusammengesetzten Hauptbetriebssystem-Laufwerk-Image ist VirtualizationService
für das Erstellen der folgenden Partitionen verantwortlich:
payload
– Eine Reihe von Partitionen, die von APEX- und APK-Dateien von Android unterstützt werdeninstance
: Eine verschlüsselte Partition zum Speichern von pro Instanz verifizierten Daten für den sicheren Start, z. B. pro Instanz verwendeter Salt, öffentliche Schlüssel von vertrauenswürdigen APEX-Modulen und Rollback-Zähler.
Boot-Sequenz
Die Microdroid-Bootsequenz erfolgt nach dem Geräte-Bootvorgang. Das Booten des Geräts wird im Abschnitt „pVM-Firmware“ des Dokuments Architektur beschrieben. Abbildung 1 zeigt die Schritte, die während der Microdroid-Bootsequenz ausgeführt werden:
Hier eine Erklärung der einzelnen Schritte:
Der Bootloader wird von crosvm in den Arbeitsspeicher geladen und die Ausführung von pvmfw beginnt. Bevor pvmfw zum Bootloader wechselt, werden zwei Aufgaben ausgeführt:
- Prüft den Bootloader, um festzustellen, ob er von einer vertrauenswürdigen Quelle (Google oder einem OEM) stammt.
- Sorgt dafür, dass derselbe Bootloader bei mehreren Starts derselben pVM durch die Verwendung des Instanz-Images konsistent verwendet wird. Die PVM wird zunächst mit einem leeren Instanz-Image gebootet. pvmfw speichert die Identität des Bootloaders im Instanz-Image und verschlüsselt sie. Wenn die pVM das nächste Mal mit demselben Instanz-Image gebootet wird, entschlüsselt pvmfw die gespeicherte Identität aus dem Instanz-Image und prüft, ob es sich um dieselbe handelt, die zuvor gespeichert wurde. Wenn die Identitäten unterschiedlich sind, wird der Start von pvmfw verweigert.
Der Bootloader startet dann Microdroid.
Der Bootloader greift auf das Instanzlaufwerk zu. Ähnlich wie bei pvmfw hat der Bootloader ein Instanzlaufwerk mit Informationen zu Partition-Images, die in dieser Instanz bei früheren Starts verwendet wurden, einschließlich des öffentlichen Schlüssels.
Der Bootloader prüft „vbmeta“ und die verketteten Partitionen wie
boot
undsuper
und leitet bei Erfolg die Geheimnisse der nächsten Phase der pVM ab. Anschließend übergibt Microdroid die Steuerung an den Kernel.Da die Superpartition bereits vom Bootloader überprüft wurde (Schritt 3), wird sie vom Kernel bedingungslos eingebunden. Wie beim vollständigen Android besteht die Superpartition aus mehreren logischen Partitionen, die über dm-verity gemountet werden. Die Steuerung wird dann an den
init
-Prozess übergeben, der verschiedene native Dienste startet. Dasinit.rc
-Skript ähnelt dem von Android, ist aber auf die Anforderungen von Microdroid zugeschnitten.Beim
init
-Prozess wird der Microdroid-Manager gestartet, der auf das Instanz-Image zugreift. Der Microdroid-Manager-Dienst entschlüsselt das Image mit dem Schlüssel, der aus der vorherigen Phase übergeben wurde, und liest die öffentlichen Schlüssel und Rollback-Zähler der Client-APKs und APEXs, denen diese pVM vertraut. Diese Informationen werden später vonzipfuse
undapexd
verwendet, wenn sie das Client-APK bzw. die angeforderten APEX-Dateien einbinden.Der Microdroid-Manager-Dienst wird gestartet
apexd
.apexd
stellt die APEXs in/apex/<name>
-Verzeichnissen bereit. Der einzige Unterschied zwischen der Art und Weise, wie Android und Microdroid APEXes einbinden, besteht darin, dass die APEX-Dateien in Microdroid von virtuellen Blockgeräten (/dev/vdc1
, …) und nicht von regulären Dateien (/system/apex/*.apex
) stammen.zipfuse
ist das FUSE-Dateisystem von Microdroid.zipfuse
stellt die Client-APK bereit, die im Wesentlichen eine ZIP-Datei als Dateisystem ist. Die APK-Datei wird von der pVM mit dm-verity als virtuelles Blockgerät übergeben, genau wie APEX. Das APK enthält eine Konfigurationsdatei mit einer Liste von APEX-Dateien, die der App-Entwickler für diese pVM-Instanz angefordert hat. Die Liste wird vonapexd
beim Aktivieren von APEX-Modulen verwendet.Der Bootvorgang kehrt zum Microdroid-Manager-Dienst zurück. Der Manager-Dienst kommuniziert dann über Binder-RPC mit
VirtualizationService
von Android, um wichtige Ereignisse wie Abstürze oder das Herunterfahren zu melden und Anfragen wie das Beenden der pVM entgegenzunehmen. Der Manager-Dienst liest den Speicherort des Hauptbinärprogramms aus der Konfigurationsdatei des APK und führt es aus.
Dateiaustausch (AuthFS)
Android-Komponenten verwenden häufig Dateien für Eingabe, Ausgabe und Status und übergeben diese als Dateideskriptoren (ParcelFileDescriptor
-Typ in AIDL), wobei der Zugriff vom Android-Kernel gesteuert wird. AuthFS bietet ähnliche Funktionen für den Austausch von Dateien zwischen Endpunkten, die sich gegenseitig nicht vertrauen, über pVM-Grenzen hinweg.
AuthFS ist im Grunde ein Remote-Dateisystem mit transparenten Integritätsprüfungen für einzelne Zugriffsoperationen, ähnlich wie fs-verity
. Die Prüfungen ermöglichen es dem Frontend, z. B. einem in einer pVM ausgeführten Programm zum Lesen von Dateien, zu erkennen, ob das nicht vertrauenswürdige Backend, in der Regel Android, Dateiinhalte manipuliert hat.
Für den Austausch von Dateien wird das Backend (fd\_server
) mit einer Konfiguration pro Datei gestartet, die angibt, ob es für die Eingabe (schreibgeschützt) oder Ausgabe (Lese-/Schreibzugriff) vorgesehen ist. Bei der Eingabe wird im Frontend zusätzlich zu einem Merkle-Baum für die On-Access-Überprüfung erzwungen, dass der Inhalt mit einem bekannten Hash übereinstimmt. Für die Ausgabe verwaltet AuthFS intern einen Hash-Baum der Inhalte, wie sie bei Schreibvorgängen beobachtet werden, und kann die Integrität erzwingen, wenn die Daten wieder gelesen werden.
Der zugrunde liegende Transport basiert derzeit auf Binder RPC, dies kann sich jedoch in Zukunft ändern, um die Leistung zu optimieren.
Schlüsselverwaltung
pVMs werden mit einem stabilen Sealing-Schlüssel bereitgestellt, der sich zum Schutz persistenter Daten eignet, und einem Attestierungsschlüssel, der sich zum Erstellen von Signaturen eignet, die nachweislich von der pVM erstellt wurden.
Binder-RPC
Die meisten Android-Schnittstellen werden in AIDL ausgedrückt, das auf dem Binder-Linux-Kernel-Treiber basiert. Zur Unterstützung von Schnittstellen zwischen pVMs wurde das Binder-Protokoll so umgeschrieben, dass es über Sockets funktioniert, im Fall von pVMs über vsock. Durch die Verwendung von Sockets können die vorhandenen AIDL-Schnittstellen von Android in dieser neuen Umgebung verwendet werden.
Um die Verbindung einzurichten, erstellt ein Endpunkt, z. B. die pVM-Nutzlast, ein RpcServer
-Objekt, registriert ein Stammobjekt und beginnt, auf neue Verbindungen zu warten. Clients können über ein RpcSession
-Objekt eine Verbindung zu diesem Server herstellen, das Binder
-Objekt abrufen und es genau wie ein Binder
-Objekt mit dem Binder-Treiber des Kernels verwenden.