Microdroid

Microdroid는 pVM에서 실행되는 소형 Android OS입니다. 다른 OS를 사용하는 VM을 시작할 수 있다면 Microdroid를 사용하지 않아도 됩니다. 하지만, pVM의 주요 사용 사례는 독립형 OS를 실행하는 것이 아니라 앱의 일부를 실행하기 위해 격리된 실행 환경을 제공하는 것으로 Android에서 제공하는 것보다 강력한 기밀성과 무결성을 보장합니다.

기존 운영체제는 Android 아키텍처를 온전히 활용하는 데 맞지 않으므로 기존 운영체제를 사용하려면 강력한 기밀성과 무결성을 제공하기 위해 상당한 양의 작업(중복되기도 함)이 요구됩니다. 예를 들어, 표준 Android 아키텍처에서는 개발자가 pVM에서 앱의 일부를 안전하게 로드하고 실행하는 수단을 구현해야 하며 glibc를 기반으로 페이로드가 빌드됩니다. Android 앱은 Bionic을 사용하고 통신은 vsock을 통한 맞춤 프로토콜이 필요합니다. 또한, adb를 사용한 디버깅은 어렵습니다.

Microdroid는 앱의 일부를 pVM으로 오프로드하는 데 필요한 개발자의 노력을 최소화하기 위해 설계된 기성 OS 이미지를 제공하여 이러한 간극을 보완합니다. 네이티브 코드는 Bionic으로 빌드되며 통신은 바인더를 통해 이루어집니다. 또한, 이를 통해 Android에서 APEX를 가져올 수 있고 Android API의 하위 집합(예: 하드웨어 지원 키를 사용하는 암호화 작업용 키 저장소)을 노출합니다. 전반적으로 Microdroid는 전체 Android OS에서 사용한 익숙한 도구가 포함된 개발자에게 친숙한 환경을 제공합니다.

기능

Microdroid는 pVM에 관한 몇 가지 추가 구성요소가 있는 간단한 Android 버전입니다. Microdroid는 다음을 지원합니다.

  • NDK API의 하위 집합(libc 및 Bionic의 Android 구현을 위한 모든 API 제공)
  • 디버깅 기능(예: adb, logcat, tombstone, gdb)
  • 자체 검사 부팅 및 SELinux 사용 설정됨
  • 공유 라이브러리와 함께 APK에 삽입된 바이너리 로드 및 실행
  • vsock을 통한 바인더 RPC와 파일 교환에 암시적 무결성 검사 사용
  • APEX 로드

Microdroid는 다음을 지원하지 않습니다.

  • android.\* 패키지의 Android Java API

  • SystemServer 및 Zygote

  • 그래픽/UI

  • HAL

Microdroid 아키텍처

Microdroid는 표준 Android와 유사한 아키텍처를 가지고 있다는 점에서 Cuttlefish와 유사합니다. Microdroid는 복합 디스크 이미지에 함께 그룹화된 다음 파티션 이미지로 구성됩니다.

  • bootloader - 커널을 확인하고 시작합니다.
  • boot.img - 커널과 init 램디스크를 포함합니다.
  • vendor_boot.img - VM별 커널 모듈(예: virtio)을 포함합니다.
  • super.img - 시스템 논리 파티션과 공급업체 논리 파티션으로 구성됩니다.
  • vbmeta.img - 자체 검사 부팅 메타데이터를 포함합니다.

파티션 이미지는 가상화 APEX에 포함되며 VirtualizationService에 의해 복합 디스크 이미지로 패키징됩니다. 기본 OS 복합 디스크 이미지 외에도 VirtualizationService는 다음과 같은 다른 파티션 생성을 담당합니다.

  • payload - Android의 APEX 및 APK에서 지원하는 파티션 세트
  • instance - 인스턴스별 솔트, 신뢰할 수 있는 APEX 공개 키, 롤백 카운터와 같이 인스턴스별 자체 검사 부팅 데이터를 유지하기 위해 암호화된 파티션

부팅 시퀀스

Microdroid 부팅 시퀀스는 기기 부팅 후에 실행됩니다. 기기 부팅은 아키텍처 문서에 설명되어 있습니다. 그림 1은 Microdroid 부팅 시퀀스 중에 발생하는 단계를 보여줍니다.

microdroid 인스턴스의 안전한 부팅 흐름

그림 1. microdroid 인스턴스의 안전한 부팅 흐름

다음은 단계에 관한 설명입니다.

  1. 부트로더는 crosvm에 의해 메모리에 로드되고 pvmfw가 실행을 시작합니다. 부트로더로 이동하기 전에 pvmfw가 다음 두 가지 작업을 실행합니다.

    • 부트로더가 신뢰할 수 있는 소스(Google 또는 OEM)의 것인지 확인합니다.
    • 인스턴스 이미지를 사용하여 동일한 pVM의 여러 부팅에 동일한 부트로더가 일관되게 사용되도록 합니다. 특히 pVM은 처음에 빈 인스턴스 이미지로 부팅됩니다. pvmfw는 부트로더 ID를 인스턴스 이미지에 저장하고 암호화합니다. 따라서 다음에 pVM이 동일한 인스턴스 이미지로 부팅될 때 pvmfw는 인스턴스 이미지에서 저장된 ID를 복호화하고 이전에 저장된 것과 동일한지 확인합니다. ID가 다른 경우 pvmfw는 부팅을 거부합니다.

    그런 다음, 부트로더는 Microdroid를 부팅합니다.

  2. 부트로더가 인스턴스 디스크에 액세스합니다. pvmfw와 마찬가지로, 부트로더에는 공개 키를 포함하여 이전 부팅 중 이 인스턴스에서 사용된 파티션 이미지 관련 정보가 포함된 인스턴스 디스크 드라이브가 있습니다.

  3. 부트로더는 vbmeta 및 연결된 파티션(예: boot, super)을 확인하고, 확인에 성공하면 다음 단계의 pVM 보안 비밀을 파생합니다. 그런 다음 Microdroid가 커널에 컨트롤을 전달합니다.

  4. super 파티션은 부트로더에서 이미 확인했기 때문에(3단계) 커널은 super 파티션을 무조건 마운트합니다. 전체 Android를 사용하는 것과 마찬가지로 super 파티션은 dm-verity를 통해 마운트된 여러 논리 파티션으로 구성됩니다. 그런 다음 컨트롤이 init 프로세스로 전달되어 다양한 네이티브 서비스를 시작합니다. init.rc 스크립트는 전체 Android의 스크립트와 비슷하지만 Microdroid의 요구사항에 따라 맞춤설정되었습니다.

  5. init 프로세스는 인스턴스 이미지에 액세스하는 Microdroid 관리자를 시작합니다. Microdroid 관리자 서비스는 이전 단계에서 전달된 키를 사용하여 이미지를 복호화하고 이 pVM이 신뢰하는 클라이언트 APK와 APEX의 공개 키 및 롤백 카운터를 읽습니다. 이 정보는 추후 zipfuseapexd가 각각 클라이언트 APK와 요청된 APEX를 마운트할 때 사용됩니다.

  6. Microdroid 관리자 서비스가 apexd를 시작합니다.

  7. apexd/apex/<name> 디렉터리에 APEX를 마운트합니다. Android가 APEX를 마운트하는 것과 Microdroid가 APEX를 마운트하는 것의 유일한 차이는 Microdroid의 경우 APEX 파일이 일반 파일(/system/apex/*.apex)이 아닌 가상 블록 기기(/dev/vdc1 등)에서 제공된다는 점입니다.

  8. zipfuse는 Microdroid의 FUSE 파일 시스템입니다. zipfuse는 클라이언트 APK(기본적으로 파일 시스템인 ZIP 파일)를 마운트합니다. 그 아래, APK 파일은 APEX와 마찬가지로 dm-verity를 사용하여 pVM에 의해 가상 블록 기기로 전달됩니다. APK에는 앱 개발자가 이 pVM 인스턴스에 요청한 APEX 목록이 있는 구성 파일이 포함됩니다. 이 목록은 apexd가 APEX를 활성화할 때 사용합니다.

  9. 부팅 흐름이 Microdroid 관리자 서비스로 돌아갑니다. 그런 다음, 관리자 서비스는 장애 또는 종료와 같은 중요한 이벤트를 보고하고 pVM 종료와 같은 요청을 수락할 수 있도록 바인더 RPC를 사용하여 Android의 VirtualizationService와 통신합니다. 관리자 서비스가 APK의 구성 파일에서 기본 바이너리의 위치를 읽고 실행합니다.

파일 교환(AuthFS)

일반적으로 Android 구성요소는 입출력과 상태에 파일을 사용하고 이러한 항목을 Android 커널에서 제어하는 액세스를 통해 파일 설명자(AIDL의 ParcelFileDescriptor 유형)로 전달합니다. AuthFS는 pVM 경계에서 상호 신뢰하지 않는 엔드포인트 간에 파일을 교환하는 데 유사한 기능을 활용합니다.

기본적으로 AuthFS는 fs-verity와 마찬가지로 개별 액세스 작업에 투명한 무결성 검사를 사용하는 원격 파일 시스템입니다. 이 검사를 사용하면 신뢰할 수 없는 백엔드(일반적으로 Android)가 파일 콘텐츠를 조작하는지 프런트엔드(예: pVM에서 실행되는 파일 읽기 프로그램)에서 감지할 수 있습니다.

파일 교환을 위해 백엔드(fd\_server)는 입력(읽기 전용) 또는 출력(읽기-쓰기) 중 무엇을 의미하는지 지정하는 파일별 구성으로 시작됩니다. 입력의 경우, 프런트엔드는 콘텐츠가 액세스 시 인증을 위해 Merkle 트리 상의 알려진 해시와 일치하도록 강제합니다. 출력의 경우, AuthFS는 쓰기 작업에서 관찰된 대로 콘텐츠의 해시 트리를 내부적으로 유지하며 데이터를 다시 읽을 때 무결성을 적용할 수 있습니다.

현재 기본 전송은 바인더 RPC를 기반으로 하지만 향후 성능 최적화를 위해 변경될 수 있습니다.

키 관리

pVM에는 안정적인 실링 키증명 키가 제공됩니다. 실링 키는 보호되는 영구 데이터에 적합하며 증명 키는 pVM에서 생성되는 인증 가능한 서명을 만드는 데 적합합니다.

바인더 RPC

Android 인터페이스의 대부분은 AIDL로 표현되며, 이는 바인더 Linux 커널 드라이버를 기반으로 빌드됩니다. pVM 간의 인터페이스를 지원하기 위해 바인더 프로토콜은 소켓(pVM의 경우 vsock)을 통해 작동하도록 재작성됩니다. 소켓을 통해 작동하면 이 새로운 환경에서 Android의 기존 AIDL 인터페이스를 사용할 수 있습니다.

연결을 설정하기 위해 pVM 페이로드와 같은 하나의 엔드포인트는 RpcServer 객체를 만들고 루트 객체를 등록하며 새 연결 리슨을 시작합니다. 클라이언트는 RpcSession 객체를 사용하여 이 서버에 연결하고 Binder 객체를 가져오며 Binder 객체가 커널 바인더 드라이버와 함께 사용되는 것처럼 정확하게 사용할 수 있습니다.