瞭解uid和gid如何在Docker容器中工做

瞭解用戶名,組名,用戶ID(uid)和組ID(gid)如何在容器內運行的進程與主機系統之間進行映射對於構建安全系統很是重要。若是沒有提供任何其餘選項,容器中的進程將以root身份執行(除非在Dockerfile中提供了不一樣的uid)。本文將解釋這是如何工做的,如何正確授予權

逐步分析uid/god安全性

首先,讓咱們回顧一下如何實現 uid 和 gids.linux 內核負責管理 uid 和 gid 空間,它的內核級系統調用用於肯定是否應該授予所請求的權限。例如,當進程嘗試寫入文件時,內核會檢查建立該進程的 uid 和 gid,以肯定它是否具備足夠的權限來修改該文件。這裏不使用用戶名,使用 uid.
在服務器上運行 Docker 容器時,仍然只有一個內核。容器化帶來的巨大價值在於全部這些獨立的進程能夠繼續共享單個內核。這意味着即便在運行 Docker 容器的服務器上,整個 uids 和 gids 也由單個內核控制。
因此你不能讓不一樣的用戶在不一樣的容器中使用相同的 uid.這是由於在常見的 linux工具中顯示的用戶名(和組名)不是內核的一部分,而是由外部工具(/ etc / passwd,LDAP,Kerberos等)管理。所以,您可能會看到不一樣的用戶名,但即便在不一樣的容器內,您也沒法爲同一個 uid / gid 擁有不一樣的權限。這一開始看起來很混亂,因此咱們舉幾個例子說明一下:

簡單的Docker運行

我將首先以 docker 組中的普通用戶(marc)身份登陸服務器。這容許我在不使用 sudo 命令的狀況下啓動 docker 容器。而後,從容器外部,讓咱們看看這個過程是如何出現的。linux

marc@server:~$ docker run -d ubuntu:latest sleep infinity92c57a8a4eda60678f049b906f99053cbe3bf68a7261f118e411dee173484d10marc@server:~$ ps aux | grep sleeproot 15638 0.1 0.0 4380 808 ? Ss 19:49 0:00 sleep infinity複製代碼

有趣。即便我從未輸入 sudo 而且我不是 root 用戶,我執行的 sleep 命令也以root 用戶身份啓動並具備 root 權限。我怎麼知道它有 root 權限?容器內的 root = =容器外的 root 嗎?是的,由於正如我所提到的,有一個內核和一個共享的 uids 和 gids 池。由於用戶名在容器外部顯示爲「root」,因此我能夠確定地知道容器內的進程是由具備uid = 0的用戶啓動的。docker

具備已定義用戶的Dockerfile

當我在 Dockerfile 中建立不一樣的用戶並以該用戶身份啓動命令時會發生什麼?爲了簡化這個例子,我沒有在這裏指定一個gid,但一樣的概念適用於組ID.ubuntu

首先,我將這些命令做爲用戶「marc」運行,其 uid 爲1001。安全

marc@server:~$ echo $UID1001複製代碼

和 Dockerfile:bash

FROM ubuntu:latestRUN useradd -r -u 1001 -g appuser appuserUSER appuserENTRYPOINT [「sleep」, 「infinity」]複製代碼

讓咱們構建並運行它:服務器

marc@server:~$ docker build -t test .Sending build context to Docker daemon 14.34 kBStep 1/4 : FROM ubuntu:latest — -> f49eec89601eStep 2/4 : RUN useradd -r -u 1001 appuser — -> Running in 8c4c0a442ace — -> 6a81547f335eRemoving intermediate container 8c4c0a442aceStep 3/4 : USER appuser — -> Running in acd9e30b4aba — -> fc1b765e227fRemoving intermediate container acd9e30b4abaStep 4/4 : ENTRYPOINT sleep infinity — -> Running in a5710a32a8ed — -> fd1e2ab0fb75Removing intermediate container a5710a32a8edSuccessfully built fd1e2ab0fb75marc@server:~$ docker run -d test8ad0cd43592e6c4314775392fb3149015adc25deb22e5e5ea07203ff53038073marc@server:~$ ps aux | grep sleepmarc 16507 0.3 0.0 4380 668 ? Ss 20:02 0:00 sleep infinitymarc@server:~$ docker exec -it 8ad0 /bin/bashappuser@8ad0cd43592e:/$ ps aux | grep sleepappuser 1 0.0 0.0 4380 668 ? Ss 20:02 0:00 sleep infinity複製代碼
到底發生了什麼,這代表了什麼?我構建了一個 Docker 鏡像,其用戶名爲「appuser」,該用戶的定義 uid 爲1001。在個人測試服務器上,我正在使用的賬戶名爲「marc」,它的 uid 爲1001。我啓動容器,sleep 命令做爲 appuser 執行,由於 Dockerfile 包含「USER appuser」行。但這實際上並無使它做爲 appuser 運行,它使它做爲用戶的 uid 運行,Docker 鏡像知道appuser.
當我檢查在容器外部運行的進程時,我看到它被映射到用戶「marc」,可是在容器內部它被映射到用戶「appuser」。這兩個用戶名只顯示其執行上下文知道的用戶名映射到1001。
這不是很是重要的。但重要的是要知道在容器內部,用戶「appuser」從容器外獲取用戶「marc」的權限和特權。在linux主機上向用戶 marc 或uid 1001授予權限也將授予容器內 appuser 的這些權限。

如何控制容器的訪問權限

另外一種選擇是運行 docker 容器並指定用戶名或 uid,以及運行時的組名或gid。
再次使用上面的初始示例。
marc@server:~$ docker run -d --user 1001 ubuntu:latest sleep infinity84f436065c90ac5f59a2256e8a27237cf8d7849d18e39e5370c36f9554254e2bmarc@server$ ps aux | grep sleepmarc     17058 0.1 0.0 4380 664 ? Ss 21:23 0:00 sleep infinity複製代碼
我在這作什麼 我建立了容器以做爲1001用戶啓動。所以,當我執行諸如 ps 或top(或大多數監視工具)之類的命令時,該過程將映射到「marc」用戶。
有趣的是,當我執行到該容器時,您能夠看到1001用戶在/ etc / passwd 文件中沒有條目,並在容器的 bash 提示符中顯示爲「我沒有名字!」。
marc@server:~$ docker exec -it 84f43 /bin/bashI have no name!@84f436065c90:/$複製代碼

須要注意的是,在建立容器時指定用戶標誌也會覆蓋 Dockerfile 中的該值。還記得第二個例子,我使用的 Dockerfile 有一個映射到本地主機上不一樣用戶名的uid嗎?當咱們在命令行上使用用戶標誌運行它以啓動執行「睡眠無限」過程的容器時會發生什麼?app

marc@server:$ docker run -d test489a236261a0620e287e434ed1b15503c844648544694e538264e69d534d0d65marc@server:~$ ps aux | grep sleepmarc     17689 0.2 0.0 4380 680 ? Ss 21:28 0:00 sleep infinitymarc@server:~$ docker run --user 0 -d testac27849fcbce066bad37190b5bf6a46cf526f56d889af61e7a02c3726438fa7amarc@server:~$ ps aux | grep sleepmarc     17689 0.0 0.0 4380 680 ? Ss 21:28 0:00 sleep infinityroot     17783 0.3 0.0 4380 668 ? Ss 21:28 0:00 sleep infinity複製代碼

在上面的最後一個例子中,你能夠看到我最終有2個容器運行睡眠過程,一個用做「marc」,另外一個用做「root」。這是由於第二個命令經過--user 在命令行上傳遞標誌來更改uid.
工具

這意味着什麼?

如今咱們已經探討了這一點,有意義的是,運行具備有限權限的容器的方法都利用來自主機的用戶系統:

一、若是已知的 uid 表示容器內的進程正在執行,則可能就像限制對主機系統的訪問同樣簡單,以便容器中的 uid 有有限的訪問權限。測試

二、更好的解決方案是使用已知的 uid 啓動容器--user(您也可使用用戶名,但請記住,它只是從主機的用戶名系統提供uid的更友好的方式),而後限制對主機上的 uid 的訪問你決定容器將運行爲。ui

三、因爲 uid 和用戶名(以及gid和組名稱)如何從容器映射到主機,所以指定容器化進程運行的用戶可使進程看起來由內部和容器外部的不一樣用戶擁有。

注意這些測試

我在 Ubuntu 16.04服務器上運行這些測試。雖然能夠在 Docker for OSX 上執行相同的命令和測試,但您會看到不一樣的結果,由於 Docker for OSX 其實是在基於 Alpine Linux 的虛擬機上執行 docker-engine,而且您執行的命令正在OSX 上執行。

- end -

做者:Marc Campbell

本文轉載於外網,轉載請標明出處


相關文章
相關標籤/搜索