如何隔離docker容器中的用戶的方法
筆者在前文《理解 docker 容器中的 uid 和 gid》介紹了 docker 容器中的用戶與宿主機(jī)上用戶的關(guān)系,得出的結(jié)論是:docker 默認(rèn)沒(méi)有隔離宿主機(jī)用戶和容器中的用戶。如果你已經(jīng)了解了 Linux 的 user namespace 技術(shù)(參考《Linux Namespace : User》),那么自然會(huì)問(wèn):docker 為什么不利用 Linux user namespace 實(shí)現(xiàn)用戶的隔離呢?事實(shí)上,docker 已經(jīng)實(shí)現(xiàn)了相關(guān)的功能,只是默認(rèn)沒(méi)有啟用而已。筆者將在本文中介紹如何配置 docker 來(lái)隔離容器中的用戶。
說(shuō)明:本文的演示環(huán)境為 ubuntu 16.04。
了解 Linux user namespace
Linux user namespace 為正在運(yùn)行的進(jìn)程提供安全相關(guān)的隔離(其中包括 uid 和 gid),限制它們對(duì)系統(tǒng)資源的訪問(wèn),而這些進(jìn)程卻感覺(jué)不到這些限制的存在。關(guān)于 Linux User Namespace 的介紹請(qǐng)參考筆者的《Linux Namespace : User》一文。
對(duì)于容器而言,阻止權(quán)限提升攻擊(privilege-escalation attacks)的最好方法就是使用普通用戶權(quán)限運(yùn)行容器的應(yīng)用程序。
然而有些應(yīng)用必須在容器中以 root 用戶來(lái)運(yùn)行,這就是我們使用 user namespace 的最佳場(chǎng)景。我們通過(guò) user namespace 技術(shù),把宿主機(jī)中的一個(gè)普通用戶(只有普通權(quán)限的用戶)映射到容器中的 root 用戶。在容器中,該用戶在自己的 user namespace 中認(rèn)為自己就是 root,也具有 root 的各種權(quán)限,但是對(duì)于宿主機(jī)上的資源,它只有很有限的訪問(wèn)權(quán)限(普通用戶)。
User namespace 的用戶映射
在配置 docker daemon 啟用 user namespace 前,我需要先來(lái)了解一些關(guān)于從屬(subordinate)用戶/組和映射(remapping)的概念。從屬用戶和組的映射由兩個(gè)配置文件來(lái)控制,分別是 /etc/subuid 和 /etc/subgid。看下它們的默認(rèn)內(nèi)容:在配置 docker daemon 啟用 user namespace 前,我需要先來(lái)了解一些關(guān)于從屬(subordinate)用戶/組和映射(remapping)的概念:
對(duì)于 subuid,這一行記錄的含義為:
用戶 nick,在當(dāng)前的 user namespace 中具有 65536 個(gè)從屬用戶,用戶 ID 為 100000-165535,在一個(gè)子 user namespace 中,這些從屬用戶被映射成 ID 為 0-65535 的用戶。subgid 的含義和 subuid 相同。
比如說(shuō)用戶 nick 在宿主機(jī)上只是一個(gè)具有普通權(quán)限的用戶。我們可以把他的一個(gè)從屬 ID(比如 100000 )分配給容器所屬的 user namespace,并把 ID 100000 映射到該 user namespace 中的 uid 0。此時(shí)即便容器中的進(jìn)程具有 root 權(quán)限,但也僅僅是在容器所在的 user namespace 中,一旦到了宿主機(jī)中,你頂多也就有 nick 用戶的權(quán)限而已。
當(dāng)開(kāi)啟 docker 對(duì) user namespace 的支持時(shí)(docker 的 userns-remap 功能),我們可以指定不同的用戶映射到容器中。比如我們專門創(chuàng)建一個(gè)用戶 dockeruser,然后手動(dòng)設(shè)置其 subuid 和 subgid:
nick:100000:65536 dockeruser:165536:65536
并把它指定給 docker daemon:
{ "userns-remap": "dockeruser" }
請(qǐng)注意 subuid 的設(shè)置信息,我們?yōu)?dockeruser 設(shè)置的從屬 ID 和 nick 用戶是不重疊的,實(shí)際上任何用戶的從屬 ID 設(shè)置都是不能重疊的。
或者一切從簡(jiǎn),讓 docker 為我們包辦這些繁瑣的事情,直接把 docker daemon 的 userns-rempa 參數(shù)指定為 "default":
{ "userns-remap": "default" }
這時(shí),docker 會(huì)自動(dòng)完成其它的配置。
配置 docker daemon 啟用用戶隔離
這里筆者采取簡(jiǎn)單的方式,讓 docker 創(chuàng)建默認(rèn)的用戶用于 user namespace。我們需要先創(chuàng)建 /etc/docker/daemon.json 文件:
$ sudo touch /etc/docker/daemon.json
然后編輯其內(nèi)容如下(如果該文件已經(jīng)存在,僅添加下面的配置項(xiàng)即可),并重啟 docker 服務(wù):
{ "userns-remap": "default" }
$ sudo systemctl restart docker.service
下面我們來(lái)驗(yàn)證幾個(gè)關(guān)于用戶隔離的幾個(gè)點(diǎn)。
首先驗(yàn)證 docker 創(chuàng)建了一個(gè)名為 dockremap 的用戶:
然后查看 /etc/subuid 和 /etc/subgid 文件中是否添加了新用戶 dockremap 相關(guān)的項(xiàng):
接下來(lái)我們發(fā)現(xiàn)在 /var/lib/docker 目錄下新建了一個(gè)目錄: 165536.165536,查看該目錄的權(quán)限:
165536 是由用戶 dockremap 映射出來(lái)的一個(gè) uid。查看 165536.165536 目錄的內(nèi)容:
與 /var/lib/docker 目錄下的內(nèi)容基本一致,說(shuō)明啟用用戶隔離后文件相關(guān)的內(nèi)容都會(huì)放在新建的 165536.165536 目錄下。
通過(guò)上面的檢查,我們可以確認(rèn) docker daemon 已經(jīng)啟用了用戶隔離的功能。
宿主機(jī)中的 uid 與容器中 uid
在 docker daemon 啟用了用戶隔離的功能后,讓我們看看宿主機(jī)中的 uid 與容器中 uid 的變化。
$ docker run -d --name sleepme ubuntu sleep infinity
uid 165536 是用戶 dockremap 的一個(gè)從屬 ID,在宿主機(jī)中并沒(méi)有什么特殊權(quán)限。然而容器中的用戶卻是 root,這樣的結(jié)果看上去很完美:
新創(chuàng)建的容器會(huì)創(chuàng)建 user namespace
在 docker daemon 啟用用戶隔離的功能前,新創(chuàng)建的容器進(jìn)程和宿主機(jī)上的進(jìn)程在相同的 user namespace 中。也就是說(shuō) docker 并沒(méi)有為容器創(chuàng)建新的 user namespace:
上圖中的容器進(jìn)程 sleep 和宿主機(jī)上的進(jìn)程在相同的 user namespace 中(沒(méi)有開(kāi)啟用戶隔離功能的場(chǎng)景)。
在 docker daemon 啟用用戶隔離的功能后,讓我們查看容器中進(jìn)程的 user namespace:
上圖中的 4404 就是我們剛啟動(dòng)的容器中 sleep 進(jìn)程的 PID??梢钥闯觯琩ocker 為容器創(chuàng)建了新的 user namespace。在這個(gè) user namespace 中,容器中的用戶 root 就是天神,擁有至高無(wú)上的權(quán)力!
訪問(wèn)數(shù)據(jù)卷中的文件
我們可以通過(guò)訪問(wèn)數(shù)據(jù)卷中的文件來(lái)證明容器中 root 用戶究竟具有什么樣的權(quán)限?創(chuàng)建四個(gè)文件,分別屬于用戶 root 、165536 和 nick。rootfile 只有 root 用戶可以讀寫(xiě),用戶 nick 具有 nickfile 的讀寫(xiě)權(quán)限,uid 165536 具有文件 165536file 的讀寫(xiě)權(quán)限,任何用戶都可以讀寫(xiě) testfile 文件:
下面把這幾個(gè)文件以數(shù)據(jù)卷的方式掛載到容器中,并檢查從容器中訪問(wèn)它們的權(quán)限:
$ docker run -it --name test -w=/testv -v $(pwd)/testv:/testv ubuntu
容器中的 root 用戶只能訪問(wèn) 165536file 和 testfile,說(shuō)明這個(gè)用戶在宿主機(jī)中只有非常有限的權(quán)限。
在容器中禁用 user namespace
一旦為 docker daemon 設(shè)置了 "userns-remap" 參數(shù),所有的容器默認(rèn)都會(huì)啟用用戶隔離的功能(默認(rèn)創(chuàng)建一個(gè)新的 user namespace)。有些情況下我們可能需要回到?jīng)]有開(kāi)啟用戶隔離的場(chǎng)景,這時(shí)可以通過(guò) --userns=host 參數(shù)為單個(gè)的容器禁用用戶隔離功能。--userns=host 參數(shù)主要給下面三個(gè)命令使用:
docker container create docker container run docker container exec
比如執(zhí)行下的命令:
$ docker run -d --userns=host --name sleepme ubuntu sleep infinity
查看進(jìn)程信息:
進(jìn)程的有效用戶又成 root 了,并且也沒(méi)有為進(jìn)程創(chuàng)建新的 user namespace:
已知問(wèn)題
User namespace 屬于比較高級(jí)的功能,目前 docker 對(duì)它的支持還算不上完美,下面是已知的幾個(gè)和現(xiàn)有功能不兼容的問(wèn)題:
- 共享主機(jī)的 PID 或 NET namespace(--pid=host or --network=host)
- 外部的存儲(chǔ)、數(shù)據(jù)卷驅(qū)動(dòng)可能不兼容、不支持 user namespace
- 使用 --privileged 而不指定 --userns=host
總結(jié)
Docker 是支持 user namespace 的,并且配置的方式也非常簡(jiǎn)便。在開(kāi)啟 user namespace 之后我們享受到了安全性的提升,但同時(shí)也會(huì)因?yàn)榉N種限制讓其它的個(gè)別功能出現(xiàn)問(wèn)題。這時(shí)我們需要作出選擇,告別一刀切的決策,讓合適的功能出現(xiàn)的合適的場(chǎng)景中。
參考:
Understanding how uid and gid work in Docker containers
Introduction to User Namespaces in Docker Engine
Isolate containers with a user namespace
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
cgroup?blkio子系統(tǒng)對(duì)容器的讀寫(xiě)限速詳解
這篇文章主要為大家介紹了cgroup?blkio子系統(tǒng)對(duì)容器的讀寫(xiě)限速詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08Docker 搭建 Tomcat 運(yùn)行環(huán)境的方法
這篇文章主要介紹了Docker 搭建 Tomcat 運(yùn)行環(huán)境的方法,Docker與虛擬機(jī)及搭建過(guò)程,介紹的非常詳細(xì),需要的朋友可以參考下2016-10-10Docker images導(dǎo)出和導(dǎo)入操作
這篇文章主要介紹了Docker images導(dǎo)出和導(dǎo)入操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03Docker?發(fā)布自定義鏡像到公共倉(cāng)庫(kù)的方法實(shí)現(xiàn)
在本文我們將學(xué)習(xí)如何使用Docker從公共倉(cāng)庫(kù)拉取Nginx鏡像,定制該鏡像,添加自定義配置文件,并將定制后的鏡像發(fā)布到公共倉(cāng)庫(kù),同時(shí)指定自定義的名稱、描述和版本號(hào),感興趣的可以了解一下2024-01-01docker中使用mysql數(shù)據(jù)庫(kù)詳解(在局域網(wǎng)訪問(wèn))
這篇文章主要給大家介紹了在docker中使用mysql數(shù)據(jù)庫(kù),在局域網(wǎng)訪問(wèn)的相關(guān)資料,文中通過(guò)圖文以及示例代碼介紹的非常詳細(xì),對(duì)大家具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起看看吧。2017-06-06分享Ubuntu19無(wú)法安裝docker源問(wèn)題
這篇文章主要介紹了Ubuntu19無(wú)法安裝docker源問(wèn)題,本文通過(guò)實(shí)例代碼給大家講解的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-11-11詳解java項(xiàng)目打包docker鏡像的幾種方式
本文主要介紹了詳解java項(xiàng)目打包docker鏡像的幾種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01