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)
執行,以增加與系統其餘部分的隔離:
cgroup_namespaces(7)
- IPC(用於System V IPC)
mount_namespaces(7)
network_namespaces(7)
pid_namespaces(7)
user_namespaces(7)
- UTS(主機名和域名)
在名稱空間中執行Android的所有使用者空間也會增加相容性,因為我們可以為它提供一個更接近正常情況下預期的環境。
run_oci
在init名稱空間(與大多數Chrome OS共享)中啟動,作為具有所有功能的真正根目錄執行。與之關聯的mount名稱空間稱為 init mount namespace
run_oci
啟動之前執行,因此它們不會出現在config.json
中。
首先,run_oci
建立一個mount名稱空間(同時仍與init的使用者名稱空間相關聯),稱為 中間掛載名稱空間。
由於當它在這個名稱空間中執行時它仍然具有init名稱空間中的所有root功能,它可以執行特權操作,例如執行重新安裝(例如,使用MS_REMOUNT
呼叫mount(2)
和
沒有MS_BIND
),並要求將tmpfs(5)
安裝到Android的/dev
中dev
和exec
標誌。此中間裝入名稱空間還用於避免將裝入洩漏到init裝入名稱空間中,並在名稱空間中的最後一個程序退出時自動清除。這個過程通常是Android的init,但是如果容器無法啟動,它也可以是run_oci
仍在中間掛載名稱空間內,通過呼叫clone(2
系統呼叫來建立容器程序。 CLONE_NEWPID
和CLONE_NEWUSER
標誌。鑑於mount名稱空間具有所有者使用者名稱空間,我們可以轉換為兩者的唯一方法是同時執行兩者。從Linux 3.9開始,CLONE_NEWUSER
意味著CLONE_FS
,所以這也有使這個新程序不再共享其根目錄的副作用(chroot(2)
)與任何其他過程。
進入容器使用者名稱空間後,容器程序將使用輸入名稱空間的其餘部分
unshare(2)
系統呼叫,每個名稱空間都有相應的標誌。在使用CLONE_NEWNS
標誌執行此操作後,它將進入mount名稱空間,該名稱空間稱為容器安裝名稱空間。這是絕大多數坐騎發生的地方。由於這與容器使用者名稱空間相關聯,並且此處的程序不再在init使用者名稱空間中以root身份執行,因此核心不再允許某些操作,即使可能設定了這些功能。一些例子是修改exec
,suid
,dev
標誌的重新安裝。
一旦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.img
由arc-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/cache
:config.json
將主機的只讀目錄之一繫結到/data
。這個只讀和接近空的/data
僅用於登入螢幕的“迷你”容器,並且在使用者登入Chrome OS之前一直使用。一旦使用者登入,arc_setup.cc
的OnBootContinue()
函式解除安裝只讀/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.json
的linux
>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
的第二階段。