1. 程式人生 > >Android container in Chrome OS

Android container in Chrome OS

翻譯來自:
Android container in Chrome OS
https://chromium.googlesource.com/chromiumos/platform2/+/master/arc/container-bundle/README.md

本文件概述了Android在Chrome OS中的Linux容器中執行的過程。

除非另有說明,否則本文件將說明Android主機容器的工作原理。 N的容器可以以稍微不同的方式工作。

config.json

run_oci 使用 config.json 來描述容器的設定方式。此檔案描述了要建立的安裝結構,名稱空間,裝置節點,cgroups配置以及繼承的功能。

名稱空間

Android正在使用所有可用的Linux namespaces(7) 執行,以增加與系統其餘部分的隔離:

在名稱空間中執行Android的所有使用者空間也會增加相容性,因為我們可以為它提供一個更接近正常情況下預期的環境。

run_oci在init名稱空間(與大多數Chrome OS共享)中啟動,作為具有所有功能的真正根目錄執行。與之關聯的mount名稱空間稱為 init mount namespace

。在init mount名稱空間中執行的任何掛載都將跨越使用者會話,並在run_oci啟動之前執行,因此它們不會出現在config.json中。

首先,run_oci建立一個mount名稱空間(同時仍與init的使用者名稱空間相關聯),稱為 中間掛載名稱空間
由於當它在這個名稱空間中執行時它仍然具有init名稱空間中的所有root功能,它可以執行特權操作,例如執行重新安裝(例如,使用MS_REMOUNT呼叫mount(2)
沒有MS_BIND),並要求將tmpfs(5) 安裝到Android的/devdevexec標誌。此中間裝入名稱空間還用於避免將裝入洩漏到init裝入名稱空間中,並在名稱空間中的最後一個程序退出時自動清除。這個過程通常是Android的init,但是如果容器無法啟動,它也可以是run_oci

本身。

仍在中間掛載名稱空間內,通過呼叫clone(2 系統呼叫來建立容器程序。 CLONE_NEWPIDCLONE_NEWUSER標誌。鑑於mount名稱空間具有所有者使用者名稱空間,我們可以轉換為兩者的唯一方法是同時執行兩者。從Linux 3.9開始,CLONE_NEWUSER意味著CLONE_FS,所以這也有使這個新程序不再共享其根目錄的副作用(chroot(2))與任何其他過程。

進入容器使用者名稱空間後,容器程序將使用輸入名稱空間的其餘部分
unshare(2)系統呼叫,每個名稱空間都有相應的標誌。在使用CLONE_NEWNS標誌執行此操作後,它將進入mount名稱空間,該名稱空間稱為容器安裝名稱空間。這是絕大多數坐騎發生的地方。由於這與容器使用者名稱空間相關聯,並且此處的程序不再在init使用者名稱空間中以root身份執行,因此核心不再允許某些操作,即使可能設定了這些功能。一些例子是修改execsuiddev標誌的重新安裝。

一旦run_oci完成設定容器程序並呼叫exit(2)來守護容器程序樹,系統中不再有任何直接引用中間裝入名稱空間的程序,因此無法再從任何位置訪問它。這意味著無法獲得可以按順序傳遞給setns(2)的檔案描述符輸入它。名稱空間本身仍然存在,因為它是容器裝入名稱空間的父級。

使用者名稱稱空間

通過以下方式為使用者名稱空間分配2,000,000個uid:

init namespace uid range container namespace uid range
655360 - 660359 0 - 4999
600 - 649 5000 - 5049
660410 - 2655360 5050 - 2000000

第二個範圍將Chrome OS守護程式uids(600-649)對映到Android的 OEM特定AID 之一範圍。

類似地,gid的分配方式與uids賦值相同,除了為容器gid 1065分配特殊gid 20119,這是Android的保留gid。這個例外是因為ext4 resgid 只接受16位gid,因此最初對映的gid 1065 + 655360不適合ext4回覆了。

根據上述規則,特殊的GID 5005(內部容器,也稱為vendor_arc_debugfs)對映到GID 605(外部容器,也稱為debugfs-access)。此GID作為init程序的補充組新增,以允許對/sys/kernel/debug/tracing/(在開發模式下)中的某些跟蹤檔案進行寫訪問。

網路名稱空間

Mounts

有幾種方法可以在容器中安裝資源:

*迴圈安裝:用於將檔案系統映像安裝到檔案系統中。 Android使用其中兩個:一個用於system.raw.img,另一個用於vendor.raw.img
*繫結掛載:這些使得檔案或目錄可以從另一個子目錄中看到,並且可以跨越chroot(2) pivot_root(2)
*共享掛載:這些掛載在init mount名稱空間中使用mount(2)MS_SHARED標誌,容器裝入名稱空間中的“MS_SLAVE”,它會導致該裝載點下的任何裝載更改傳播到其他共享子樹。

在Android上,它的init的第一個階段設定了幾個掛載點,如/dev/mnt,但ARC根本不使用init的功能。相反,在ARC上,config.json設定了這些標準的Android掛載點以及
幾個ARC特定的。

所有安裝都在/opt/google/container/android/rootfs/root子樹中執行。鑑於run_oci不修改init mount名稱空間,任何跨越使用者會話的安裝(例如system.raw.img迴圈安裝)應該在run_oci啟動之前執行。這通常由arc-setup 處理。

mounts部分的標誌是mount(8)理解的標誌。請注意,一個掛載條目可能會成為多次呼叫mount(2),因為核心會忽略某些標誌組合(例如,掛載傳播標誌的更改會忽略所有其他標誌)。

容器裝入名稱空間中可見的裝入列表

  • /:這是/opt/google/containers/android/system.raw.imgarc-setup迴圈安裝(從/etc/init/arc-system-mount.conf呼叫)在init名稱空間中。這跨越了容器呼叫,因為它是無狀態的。 exec/suid標誌被新增到中間裝入名稱空間中,並遞迴地將其傳播標誌改為MS_SLAVE
  • /config/sdcardfs:由esdfs建立的普通configfs/sys/kernel/config/sdcardfs子目錄的繫結。
    */dev:這是一個tmpfs安裝在中間掛載名稱空間中,android-root作為所有者。這是獲取dev/exec掛載標誌所必需的。
  • /dev/pts:Pseudo TTS使用名稱空間支援來保護檔案系統,使其與父名稱空間位於不同的名稱空間,即使裝置節點ID看起來相同。仿生CTS測試需要。該裝置安裝了nosuid和noexec掛載選項,以提高安全性,儘管Android不使用它們。
  • /dev/ptmx:devpts的核心文件表明有兩種方法可以支援/dev/ptmx :建立指向/dev/pts/ptmx或繫結掛載/dev/pts/ptmx的符號連結。選擇bind-mount來標記它:u:object_r:ptmx_device:s0`。
  • /dev/kmsg:這是主機的/run/arc/android.kmsg.fifo的繫結掛載,它只是一個FIFO檔案。寫入假裝置的日誌由名為arc-kmsg-logger的作業讀取,並存儲在主機的/var/log/android.kmsg中。
  • /dev/socket:這是一個普通的tmpfs,由Android的init用來儲存套接字檔案。
  • /dev/usb-ffs/adb:這是主機的/run/arc/adbd的繫結掛載,是一個從掛載,它包含一個FIFO,作為通過ConfigFS/FunctionFS配置的ADB小工具。此檔案僅出現在開發者模式中。寫入/dev/usb-ffs/adb/ep0檔案後,批量輸入和批量輸出端點將被繫結掛載到同一目錄中。
  • /data/data/cacheconfig.json將主機的只讀目錄之一繫結到/data。這個只讀和接近空的/data僅用於登入螢幕的“迷你”容器,並且在使用者登入Chrome OS之前一直使用。一旦使用者登入,arc_setup.ccOnBootContinue()函式解除安裝只讀/data,然後bind-mounts/home/root/$ {HASH}/android-data/{資料,快取}分別為/data/data/cache`。這些源目錄是可寫的,並且位於由加密家管理的Chrome OS使用者加密目錄中。
  • /var/run/arc:一個tmpfs,它包含來自其他容器的幾個掛載點,用於Chrome <=> Android檔案系統通訊,例如dlfs,OBB和外部儲存。
  • /var/run/arc/sdcard:在容器外部執行的sdcard守護程式提供的FUSE檔案系統。
  • /var/run/chrome:儲存ARC橋和Wayland UNIX域套接字。
  • /var/run/cras:儲存CRAS UNIX域套接字。
  • /var/run/inputbridge:儲存FIFO以在容器內執行IPC。 surfaceflinger使用FIFO來支援從主機到容器的輸入事件。
  • /sys:一個普通的sysfs
  • /sys/fs/selinux:這是從容器外的/sys/fs/selinux繫結安裝的。
  • /sys/kernel/debug:由於這個目錄由具有非常嚴格許可權的真實root擁有(因此容器無法訪問該目錄中的任何資源),因此在其位置安裝了tmpfs
  • /sys/kernel/debug/sync:放寬主機中該目錄的許可權,以便android-root可以訪問它,並繫結掛載在容器中。
  • /sys/kernel/debug/tracing:這是從主機的/run/arc/debugfs/tracing繫結掛載的,僅在開發模式下。請注意,組ID對映到容器中以允許DAC從內部進行訪問。
  • /proc:正常的proc fs。它安裝在容器裝入名稱空間中,該名稱空間與容器使用者+ pid名稱空間相關聯,以顯示正確的PID對映。
  • /proc/cmdline:帶有執行時生成的核心命令列的常規檔案是繫結安裝的,而不是Chrome OS核心命令列。
  • /proc/sys/vm/mmap_rnd_compat_bits/proc/sys/vm/mmap_rnd_bits:兩個常規檔案是繫結掛載的,因為原始檔案由真正的root擁有,具有非常嚴格的許可權。 Android的init修改了這些檔案的內容以增加mmap(2)熵,並且如果不允許此操作。掛載這兩個檔案會將mod的數量減少到init
  • /proc/sys/kernel/kptr_restrict:與/proc/sys/vm/mmap_rnd_bits相同。
  • /oem/etc:這是從主機的/run/arc/oem/etc繫結掛起並儲存platform.xml檔案。
  • /var/run/arc/bugreport:這是從主機的/run/arc/bugreport繫結安裝的。容器在目錄中建立一個管道檔案,以允許主機的debugd讀取它。當它被讀取時,Android的bugreport輸出被髮送到主機端。
  • /var/run/arc/apkcache:這是從主機的`/mnt/stateful_partition/unencrypted/apkcache繫結掛載的。主機目錄用於儲存由裝置策略指定並在主機端下載的APK檔案。
  • /var/run/arc/dalvik-cache:這是從主機的/mnt/stateful_partition/unencrypted/art-data/dalvik-cache進行繫結安裝的。主機目錄用於儲存在主機端編譯的boot * .art檔案。這允許容器立即載入檔案而不構建它們。
  • /var/run/camera:儲存arc-camera UNIX域套接字。
  • /var/run/arc/obb:這是從主機的/run/arc/obb繫結掛載的。在容器外部執行的名為/usr/bin/arc-obb-mounter的守護程式在請求時將OBB映像檔案作為FUSE檔案系統掛載到目錄。
  • /var/run/arc/media:這是從主機的/run/arc/media繫結安裝的。在容器外部執行的名為/usr/bin/mount-passthrough的守護程式在需要時將外部儲存作為FUSE檔案系統安裝到目錄中。
  • /vendor:這是從主機的/opt/google/containers/android/vendor.raw.img迴圈安裝的。該目錄可能包含圖形驅動程式,Houdini,特定於板的APK等。
  • /mnt:這是一個tmpfs掛載點。請注意,我們不應該在config.json中的/mnt中掛載任何其他檔案系統,即使它是一個空的tmpfs。這樣做會使我們的容器與Android的相容性降低,甚至可能破壞CTS。

能力

Android在使用者名稱空間中執行,名稱空間中的root使用者具有該名稱空間中的所有可能功能。儘管如此,核心中仍有一些操作可以在init名稱空間中對使用者執行功能檢查。以這種方式完成所有檢查的所有功能(例如CAP_SYS_MODULE)都將被刪除,因為容器中的任何使用者都無法使用它。

此外,刪除了以下功能(通過從允許的,可繼承的,有效的和環境的功能集列表中刪除它們)來通知容器它無法執行某些操作:

*CAP_SYS_BOOT:這表示Android的init程序不應該使用reboot(2),而是呼叫exit(2)。它還用於決定是否阻止SIGTERM訊號,該訊號可用於請求容器從外部終止自身。
*CAP_SYSLOG:這表示Android無法訪問/proc/kallsyms中的核心指標。

Cgroups

預設情況下,不允許在容器內執行的程序訪問任何裝置檔案。他們只能訪問config.jsonlinux>resources>devices部分中明確允許的那些。

啟動過程

在這裡插入圖片描述

Hooks

run_oci使用的鉤子遵循POSIX-platform Hooks的Open Container Initiative規範,使用Chrome OS特定的擴充套件,允許在處理完所有掛載之後安裝掛鉤,但在呼叫chroot(2)之前/chroot.2.html)。

所有的鉤子都是通過呼叫fork(2)+ execve(2)來執行的,來自run_oci程序(它是容器程序的父程序),並位於中間裝入名稱空間內。

為了避免為建立多個程序和在名稱空間之間來回切換(在天真地完成時為引導時間增加幾毫秒)付出代價,我們將所有掛鉤執行合併到兩個掛鉤:pre-create和pre- chroot環境。

預建立鉤子使用--mode = setup標誌呼叫arc-setup並建立主機將通過config.json繫結到容器的檔案和目錄。

pre-chroot hook使用--mode = pre-chroot標誌呼叫arc-setup執行幾個操作:

  • 設定binfmt_misc以在英特爾裝置上執行ARM二進位制轉換。
  • 恢復由run_oci建立的幾個檔案和目錄的SELinux上下文,因為這些檔案和目錄不是由構建系統處理,也不是在呼叫run_oci之前首次呼叫arc-setup
  • 觸控/dev/.coldboot_done,它被Android用作在啟動序列期間已達到某一點的訊號。這通常是由Android的init在第一階段完成的,但是我們不使用它並直接啟動Android進入init的第二階段。

參考文獻