Mikrodroid

Microdroid ist ein Mini-Android-Betriebssystem, das in einer pVM ausgeführt wird. Sie müssen kein Microdroid verwenden, sondern können eine VM mit jedem Betriebssystem starten. Die primären Anwendungsfälle für pVMs bestehen jedoch nicht darin, ein eigenständiges Betriebssystem auszuführen, sondern vielmehr darin, eine isolierte Ausführungsumgebung für die Ausführung eines Teils einer App mit stärkeren Vertraulichkeits- und Integritätsgarantien anzubieten, als Android bieten kann.

Bei herkömmlichen Betriebssystemen erfordert die Bereitstellung starker Vertraulichkeit und Integrität einen erheblichen Arbeitsaufwand (oft doppelt), da herkömmliche Betriebssysteme nicht zur übergreifenden Android-Architektur passen. Bei der Standard-Android-Architektur müssen Entwickler beispielsweise eine Möglichkeit implementieren, einen Teil ihrer App sicher in die pVM zu laden und auszuführen, und die Nutzlast wird gegen glibc erstellt. Die Android-App verwendet Bionic, die Kommunikation erfordert ein benutzerdefiniertes Protokoll über vsock und das Debuggen mit adb ist eine Herausforderung.

Microdroid füllt diese Lücken, indem es ein handelsübliches Betriebssystem-Image bereitstellt, das den Entwicklern den geringsten Aufwand zum Auslagern eines Teils ihrer App in eine pVM ermöglicht. Nativer Code basiert auf Bionic, die Kommunikation erfolgt über Binder, ermöglicht den Import von APEXes aus Android und stellt eine Teilmenge der Android-API bereit, beispielsweise einen Keystore für kryptografische Vorgänge mit hardwaregestützten Schlüsseln. Insgesamt dürften Entwickler mit Microdroid eine vertraute Umgebung mit den Tools vorfinden, mit denen sie sich an das vollständige Android-Betriebssystem gewöhnt haben.

Merkmale

Microdroid ist eine abgespeckte Version von Android mit einigen zusätzlichen pVM-spezifischen Komponenten. Microdroid unterstützt:

  • Eine Teilmenge der NDK-APIs (alle APIs für die Android-Implementierung von libc und Bionic werden bereitgestellt)
  • Debugging-Funktionen wie ADB, Logcat, Tombstone und GDB
  • Verifizierter Start und SELinux aktiviert
  • Laden und Ausführen einer Binärdatei zusammen mit gemeinsam genutzten Bibliotheken, eingebettet in eine APK
  • Binder RPC über vsock und Austausch von Dateien mit impliziten Integritätsprüfungen
  • Laden von APEXes

Microdroid unterstützt nicht:

  • Android-Java-APIs in den android.\* Paketen

  • SystemServer und Zygote

  • Grafik/Benutzeroberfläche

  • HALs

Mikrodroid-Architektur

Microdroid ähnelt Cuttlefish insofern, als beide eine Architektur haben, die dem Standard-Android ähnelt. Microdroid besteht aus den folgenden Partitionsbildern, die in einem zusammengesetzten Festplattenbild zusammengefasst sind:

  • bootloader – Verifiziert und startet den Kernel.
  • boot.img – Enthält den Kernel und die Init-Ramdisk.
  • vendor_boot.img – Enthält VM-spezifische Kernelmodule, wie z. B. virtio.
  • super.img – Besteht aus logischen System- und Herstellerpartitionen.
  • vbmeta.img – Enthält verifizierte Boot-Metadaten.

Die Partitionsabbilder werden im Virtualization APEX ausgeliefert und von VirtualizationService in ein zusammengesetztes Festplattenabbild gepackt. Zusätzlich zum Haupt-OS-Composite-Disk-Image ist VirtualizationService für die Erstellung dieser anderen Partitionen verantwortlich:

  • payload – Eine Reihe von Partitionen, die von Androids APEXes und APKs unterstützt werden
  • instance – Eine verschlüsselte Partition für die Beibehaltung verifizierter Startdaten pro Instanz, z. B. Salt pro Instanz, vertrauenswürdige öffentliche APEX-Schlüssel und Rollback-Zähler

Startvorgang

Die Microdroid-Startsequenz erfolgt nach dem Gerätestart . Der Gerätestart wird im Architekturdokument erläutert. Abbildung 1 zeigt die Schritte, die während der Microdroid-Startsequenz stattfinden:

Sicherer Bootflow der Microdroid-Instanz

Abbildung 1. Sicherer Bootflow der Microdroid-Instanz

Hier ist eine Erklärung der Schritte:

  1. Der Bootloader wird von crosvm in den Speicher geladen und pvmfw beginnt mit der Ausführung. Bevor pvmfw zum Bootloader springt, führt er zwei Aufgaben aus:

    • Überprüft den Bootloader, um zu prüfen, ob er von einer vertrauenswürdigen Quelle (Google oder einem OEM) stammt.
    • Stellt durch die Verwendung des Instanz-Images sicher, dass derselbe Bootloader bei mehreren Starts derselben pVM konsistent verwendet wird. Konkret wird die pVM zunächst mit einem leeren Instanz-Image gebootet. pvmfw speichert die Identität des Bootloaders im Instanz-Image und verschlüsselt es. Wenn die pVM das nächste Mal mit demselben Instanz-Image gestartet wird, entschlüsselt pvmfw die gespeicherte Identität aus dem Instanz-Image und überprüft, ob es sich um dieselbe Identität handelt, die zuvor gespeichert wurde. Wenn die Identitäten unterschiedlich sind, verweigert pvmfw den Start.

    Der Bootloader startet dann Microdroid.

  2. Der Bootloader greift auf die Instanzfestplatte zu. Ähnlich wie pvmfw verfügt der Bootloader über ein Instanzlaufwerk mit Informationen zu Partitionsbildern, die in dieser Instanz bei früheren Starts verwendet wurden, einschließlich des öffentlichen Schlüssels.

  3. Der Bootloader überprüft vbmeta und die verketteten Partitionen wie boot und super und leitet bei Erfolg die pVM-Geheimnisse der nächsten Stufe ab. Anschließend übergibt Microdroid die Kontrolle an den Kernel.

  4. Da die Superpartition bereits vom Bootloader überprüft wurde (Schritt 3), mountet der Kernel die Superpartition bedingungslos. Wie beim vollständigen Android besteht die Superpartition aus mehreren logischen Partitionen, die über dm-verity gemountet sind. Die Steuerung wird dann an den init Prozess übergeben, der verschiedene native Dienste startet. Das init.rc Skript ähnelt dem von Android, ist jedoch auf die Bedürfnisse von Microdroid zugeschnitten.

  5. Der init Prozess startet den Microdroid-Manager, der auf das Instanz-Image zugreift. Der Microdroid-Manager-Dienst entschlüsselt das Image mithilfe des in der vorherigen Phase übergebenen Schlüssels und liest die öffentlichen Schlüssel und Rollback-Zähler der Client-APXs und APEXes, denen diese pVM vertraut. Diese Informationen werden später von zipfuse und apexd verwendet, wenn sie das Client-APK bzw. angeforderte APEXes bereitstellen.

  6. Der Microdroid-Manager-Dienst startet apexd .

  7. apexd mountet die APEXes in den Verzeichnissen /apex/<name> . Der einzige Unterschied zwischen der Art und Weise, wie Android und Microdroid APEXes bereitstellen, besteht darin, dass in Microdroid die APEX-Dateien von virtuellen Blockgeräten ( /dev/vdc1 , …) stammen, nicht von regulären Dateien ( /system/apex/*.apex ).

  8. zipfuse ist das FUSE-Dateisystem von Microdroid. zipfuse mountet das Client-APK, bei dem es sich im Wesentlichen um eine Zip-Datei als Dateisystem handelt. Darunter wird die APK-Datei von der pVM mit dm-verity als virtuelles Blockgerät übergeben, genau wie APEX. Das APK enthält eine Konfigurationsdatei mit einer Liste von APEXes, die der App-Entwickler für diese pVM-Instanz angefordert hat. Die Liste wird von apexd beim Aktivieren von APEXes verwendet.

  9. Der Startvorgang kehrt zum Microdroid-Manager-Dienst zurück. Der Manager-Dienst kommuniziert dann über Binder RPC mit VirtualizationService von Android, sodass er wichtige Ereignisse wie Absturz oder Herunterfahren melden und Anfragen wie das Beenden der pVM akzeptieren kann. Der Manager-Dienst liest den Speicherort der Hauptbinärdatei aus der Konfigurationsdatei des APK und führt sie aus.

Dateiaustausch (AuthFS)

Es ist üblich, dass Android-Komponenten Dateien für Eingabe, Ausgabe und Status verwenden und diese als Dateideskriptoren (Typ ParcelFileDescriptor in AIDL) weitergeben, wobei der Zugriff vom Android-Kernel gesteuert wird. AuthFS ermöglicht eine ähnliche Funktionalität für den Austausch von Dateien zwischen einander misstrauischen Endpunkten über pVM-Grenzen hinweg.

Grundsätzlich handelt es sich bei AuthFS um ein Remote-Dateisystem mit transparenten Integritätsprüfungen einzelner Zugriffsvorgänge, ähnlich wie fs-verity . Die Prüfungen ermöglichen es dem Frontend, beispielsweise einem Dateileseprogramm, das in einer pVM ausgeführt wird, zu erkennen, ob das nicht vertrauenswürdige Backend, typischerweise Android, Dateiinhalte manipuliert hat.

Um Dateien auszutauschen, wird das Backend ( fd\_server ) mit einer Konfiguration pro Datei gestartet, die angibt, ob sie für die Eingabe (schreibgeschützt) oder die Ausgabe (Lesen/Schreiben) gedacht ist. Für die Eingabe erzwingt das Frontend, dass der Inhalt mit einem bekannten Hash über einem Merkle-Baum zur Überprüfung bei Zugriff übereinstimmt. Für die Ausgabe verwaltet AuthFS intern einen Hash-Baum der bei Schreibvorgängen beobachteten Inhalte und kann die Integrität beim Zurücklesen der Daten erzwingen.

Der zugrunde liegende Transport basiert derzeit auf Binder RPC, dies könnte sich jedoch in Zukunft ändern, um die Leistung zu optimieren.

Schlüsselverwaltung

pVMs werden mit einem stabilen Siegelschlüssel ausgestattet, der für geschützte persistente Daten geeignet ist, und einem Attestierungsschlüssel , der für die Erstellung von Signaturen geeignet ist, die nachweislich von der pVM erstellt werden.

Binder RPC

Ein Großteil der Android-Schnittstellen wird in AIDL ausgedrückt, das auf dem Binder-Linux-Kerneltreiber aufbaut. Um Schnittstellen zwischen pVMs zu unterstützen, wurde das Binder-Protokoll so umgeschrieben, dass es über Sockets funktioniert, im Fall von pVMs über vsock . Durch den Betrieb über 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 Root-Objekt und beginnt mit der Überwachung auf neue Verbindungen. Clients können über ein RpcSession Objekt eine Verbindung zu diesem Server herstellen, das Binder Objekt abrufen und es genau so verwenden, wie ein Binder Objekt mit dem Kernel-Binder-Treiber verwendet wird.