04 . Docker安全與Docker底層實現

men發表於2020-06-14

Docker安全

Docker安全性時,主要考慮三個方面

# 1. 由核心的名字空間和控制組機制提供的容器內在安全![](https://img2020.cnblogs.com/blog/1871335/202006/1871335-20200614153954679-1675606117.png)

# 2. Docker程式(特別是服務端)本身的抗攻擊性
# 3. 核心安全性的加強機制對容器安全性的影響

核心名稱空間

Docker容器和LXC容器很相似,所提供的安全特性也差不多。當用docker run啟動一個容器時,在後臺Docker為容器建立了一個獨立的名稱空間和控制組集合。

名稱空間提供了最基礎也是最直接的隔離,在容器中執行的程式不會被執行在主機上的程式和其它容器發 現和作用。

每個容器都有自己獨有的網路棧,意味著它們不能訪問其他容器的sockets或介面。不過,如果主機系統上做了相應的設定,容器可以像跟主機互動一樣的和其他容器互動。當指定公共埠或使用links來連線2個容器時,容器就可以相互通訊了(可以根據配置來限制通訊的策略).

從網路架構的角度來看,所有的容器通過本地主機的網橋介面相互通訊,就像物理機器通過物理交換機通訊一樣。

那麼,核心中實現名稱空間和私有網路的程式碼是否足夠成熟?

核心名字空間從2.6.15版本(2008年7月釋出)之後被引入,數年間,這些機制的可靠性在諸多大型生產系統中被實踐驗證。

實際上,名字空間的想法和設計提出的時間要更早,最初是為了在核心中引入一種機制來實現 OpenVZ 的特性。而OpenVZ專案早在2005年就釋出了,其設計和實現都已經十分成熟。

控制組

控制組是Linux容器機制的另外一個關鍵元件,負責實現資源的審計和限制.

它提供了很多有用的特性;以及確保各個容器可以公平地分享主機的記憶體、CPU、磁碟IO等資源;當然,更重要的是,控制組確保了當容器內的資源使用產生壓力時不會連累主機系統。

儘管控制組不負責隔離容器之間相互訪問、處理資料和程式,它在防止拒絕服務(DDOS)攻擊方面是必不可少的。尤其是在多使用者的平臺(比如公有或私有的PaaS)上,控制組十分重要。例如,當某些應用程式表現異常的時候,可以保證一致地正常執行和效能。

控制組始於2006年,核心從2.6.24版本開始被引入.

Docker服務端的防護

執行一個容器或應用程式的核心是通過Docker服務端。Docker服務的執行目前需要root許可權,因此其安全性十分關鍵。

首先,確保只有可信的使用者才可以訪問Docker服務。Docker允許使用者在主機和容器間共享資料夾,同時不需要限制容器的訪問許可權,這就容易讓容器突破資源限制。例如,惡意使用者啟動容器的時候將主機的根目錄/對映到容器的 /host目錄中,那麼容器理論上就可以對主機的檔案系統進行任意修改了。這聽起來很瘋狂?但是事實上幾乎所有虛擬化系統都允許類似的資源共享,而沒法禁止使用者共享主機根檔案系統到虛擬機器系統

這將會造成很嚴重的安全後果。因此,當提供容器建立服務時(例如通過一個web伺服器),要更加註意進行引數的安全檢查,防止惡意的使用者用特定引數來建立一些破壞性的容器

為了加強對服務端的保護,Docker的REST API(客戶端用來跟服務端通訊)在0.5.2之後使用本地的Unix套接字機制替代了原先繫結在 127.0.0.1 上的TCP套接字,因為後者容易遭受跨站指令碼攻擊。現在使用者使用Unix許可權檢查來加強套接字的訪問安全。

使用者仍可以利用HTTP提供REST API訪問。建議使用安全機制,確保只有可信的網路或VPN,或證書保 護機制(例如受保護的stunnel和ssl認證)下的訪問可以進行。此外,還可以使用HTTPS和證書來加強 保護。

最近改進的Linux名字空間機制將可以實現使用非root使用者來執行全功能的容器。這將從根本上解決了容器和主機之間共享檔案系統而引起的安全問題。

終極目標是改進 2 個重要的安全特性:

  • 將容器的root使用者對映到本地主機上的非root使用者,減輕容器和主機之間因許可權提升而引起的安全問題;
  • 允許Docker服務端在非root許可權下執行,利用安全可靠的子程式來代理執行需要特權許可權的操作。這些子程式將只允許在限定範圍內進行操作,例如僅僅負責虛擬網路設定或檔案系統管理、配置操作等。
  • 最後,建議採用專用的伺服器來執行Docker 和相關的管理服務(例如管理服務比如ssh監控和程式監控、管理工具nrpe、collectd等)。其它的業務服務都放到容器中去執行。

核心能力機制

能力機制(Capability)是Linux核心一個強大的特性,可以提供細粒度的許可權訪問控制。 Linux核心自2.2版本起就支援能力機制,它將許可權劃分為更加細粒度的操作能力,既可以作用在程式上,也可以作用在檔案上。

例如,一個Web服務程式只需要繫結一個低於1024的埠的許可權,並不需要root許可權。那麼它只需要被授權 net_bind_service能力即可。此外,還有很多其他的類似能力來避免程式獲取root許可權。

預設情況下,Docker啟動的容器被嚴格限制只允許使用核心的一部分能力.

使用能力機制對加強Docker容器的安全有很多好處。通常,在伺服器上會執行一堆需要特權許可權的程式,包括有ssh、cron、syslogd、硬體管理工具模組(例如負載模組)、網路配置工具等等。容器跟這些程式是不同的,因為幾乎所有的特權程式都由容器以外的支援系統來進行管理。

# 1. ssh訪問被主機上ssh服務來管理;
# 2. cron通常應該作為使用者程式執行執行,許可權交給使用他服務的應用來處理;
# 3. 日誌系統可由Docker或第三方服務管理;
# 4. 硬體管理無關緊要,容器中也就無需執行udevd以及類似服務;
# 5. 網路管理也都在主機上設定,除非特殊需求,容器不需要對網路進行配置.

從上面的例子可以看出,大部分情況下,容器並不需要真正的root許可權,容器只需要少數的能力即可.為了加強安全,容器可以禁用一些沒必要的許可權:

# 1. 完全禁止任何mount操作.
# 2. 禁止直接訪問本地主機的套接字.
# 3. 禁止訪問一些檔案系統的操作,比如建立新的裝置,修改檔案屬性等.
# 4. 禁止模組載入.

這樣,就算攻擊者在容器中取得了root許可權,也不能獲得本地主機的較高許可權,能進行的破壞也有限.

預設認情況下,Docker採用 白名單 機制,禁用必需功能之外的其它許可權。 當然,使用者也可以根據自身需求來為Docker容器啟用額外的許可權。

其他安全特性.

除了能力機制之外,還可以利用一些現有的安全機制來增強docker的安全性,例如TOMOYO,AppArmor,SELinux,GRSEC等.

Docker 當前預設只開啟了能力機制,使用者可以採用多種方案來加強Docker主機的安全,例如:

  1. 在核心中啟用GRSEC和PAX,這將增加很多編譯和執行時的安全檢查,通過地址隨機化避免惡意探測等,並且,啟用該特性不需要Docker進行任何配置.

  2. 使用一些有增強安全特性的容器模板,比如帶AppArmor的模板和Redhat帶Selinux策略的模板.這些模板提供了額外的安全特性.

  3. 使用者可以自定義訪問控制機制來定製安全策略.

    跟其他新增Docker容器的第三方工具一樣(比如網路拓撲和檔案系統共享),有很多類似的機制,在不改變Docker核心情況下就可以加固現有的容器.

小結

總體來說,Docker容器還是十分安全的,特別是在容器不使用root許可權來執行程式的話.

另外,使用者可以使用現有工具,比如Apparmor,SELinux,GRSEC來增強安全性,甚至自己在核心中實現更復雜的安全機制.

Docker底層實現

Docker底層的核心技術包括Linux上的名稱空間(Namespaces)、控制組(ControlGroups),Union檔案系統(Union file systems)和容器格式(Container format).

我們知道,傳統的虛擬機器通過在宿主主機中執行hypervisor來模擬一套完整的硬體環境提供給虛擬機器的作業系統.虛擬機器系統看到的環境是可限制的,也是彼此隔離的,這種直接的做法實現了對資源完整的封裝,但很多時候往往意味著系統資源的浪費,例如,以宿主機和虛擬機器系統都為linux系統為例,虛擬機器中執行的應用其實是可以利用宿主機系統中的執行環境。

我們知道,在作業系統中,包括核心、檔案系統、網路、PID、UID、IPC、記憶體、硬碟、CPU等等,所有的資源都是應用程式直接共享的,要想實現虛擬化,除了要實現對記憶體、CPU、網路IO、硬碟IO、儲存空間等的限制外,還要實現檔案系統、網路、PID、UID、IPC等等的相互隔離,前者相對容易實現一些,後者則需要宿主機系統的深入支援.

隨著Linux系統對於名稱空間功能的完善實現,程式設計師可以實現上面的所有需要,讓某些程式在彼此隔離的名稱空間中執行,大家雖然都共用一個核心和某些執行時環境(l例如一些系統命令和系統等),但是彼此卻看不到,大家都以為系統中只有自己的存在,這種機制就是容器,利用名稱空間來做許可權的隔離控制,利用cgroups來做資源分配.

容器的基本架構

Dcoker採用了c/s架構,包括客戶端和服務端,Docker守護程式(Daemon)作為服務端接受來自客戶端的請求,並處理這些請求(建立、執行、分發容器).

客戶端和服務端既可以執行在一個機器上,也可以通過socket或者RESTful API來進行通訊.

Docker守護程式一般在宿主主機後臺執行,等待來自客戶端的訊息.

Docker客戶端則為使用者提供一系列可執行的命令,使用者用這些命令實現跟Docker守護程式互動.

名稱空間

名稱空間是Linux核心一個強大的特性,每個容器都有自己單獨的名稱空間,執行在其中的應用都像是在獨立的作業系統執行一樣,名稱空間保證了容器之間彼此互不影響.

pid名稱空間

不同使用者的程式就是通過pid名稱空間隔離開的,且不同名稱空間可以有相同的pid,所有的LXC程式在Docker中的父程式為Docker程式,每個LXC程式具有不同的名稱空間,同時由於巢狀,因此可以很方便的實現巢狀的Docker容器.

net名稱空間

有了pid名稱空間,每個名稱空間的pid能夠實現相互隔離,但是網路埠還是共享host的埠,網路隔離是通過net名稱空間實現的,每個net名稱空間有單獨的網路裝置,IP地址,路由表,/proc/net目錄,這樣每個容器的網路就能隔離開來,Docker預設採用veth的方式,將容器中的虛擬網路卡host上的一個Docker網橋docker0連線在一起.

IPC名稱空間

容器中程式互動採用了Linux常見的程式互動方法,包括訊號量,訊息佇列和共享記憶體等,然而同VM不同的是,容器的程式互動實際山還是host上具有相同pid名稱空間的程式互動,因此需要在IPC資源中申請加入名稱空間資訊,每個IPC資源有一個唯一的32位id。

mnt名稱空間

類似chroot,將一個程式放到一個特定的目錄執行,mnt名稱空間允許不同名稱空間的程式看到的檔案結構不同,這樣每個名稱空間中的程式所看到的檔案目錄就被隔離開了,同chroot不同,每個名稱空間的容器在/proc/mounts的資訊只包含所在名稱空間的mount point。

uts名稱空間

UTS名稱空間允許每個容器擁有獨立的hostname和domain name,使其在網路上可以被視作一個獨立的節點而非主機上的一個程式.

每個容器可以有不同的使用者和組id,也就是說可以在容器內用容器內部的使用者執行程式而非主機上的使用者.

控制組

控制組(cgroups)是一個Linux核心的一個特性,主要用來對資源進行隔離、限制、審計等,只有能控制分配到容器的資源,才能避免當多個容器同時執行時對系統資源的競爭.

控制組技術最早由Google的程式設計師在2006年提出,Linux核心從2.6.24開始支援.

控制組可以提供對容器的記憶體、CPU、磁碟IO等資源的限制和審計管理.

聯合檔案系統

聯合檔案系統(UnionFS)是一種分層、輕量級並且高效能的檔案系統,他支援對檔案系統的修改作為一次提交來一層層的疊加,同時可以將不同目錄掛載到同一個虛擬檔案系統.

聯合檔案系統是Docker映象的基礎,映象可以通過分層來進行繼承,基於基礎映象(沒有父映象),可以製作各種具體的應用映象.

另外,不同Docker容器就可以共享一些基礎的檔案系統層,同時加上自己獨有的改動層,大大提高了儲存的效率.

Docker中使用的AUFS就是一種聯合檔案系統,AUFS支援為每一個成員目錄(類似Git的分支)設定只讀(readonly)、讀寫(readwrite)和寫出(whiteout-able)許可權,同時AUFS裡有一個類似分層的概念,對只讀許可權的分支可以邏輯上進行增量的修改(不影響只讀部分的).

Docker目前支援的聯合檔案系統包括OverlayFS,AUFS,BtrFS,VFS,ZFS和Device Mapper。

有可能的情況下,推薦使用overlay2儲存驅動,overlay2是目前Docker預設的儲存驅動,以前是aufs,可以通過配置以上提到的其他型別儲存驅動.

容器格式

最初,Docker採用了LXC中的容器格式,從0.7版本開始以後去除LXC,轉而使用自行開發的libcontainer,從1.11開始,進一步演進為runC和containerd。

網路實現

Docker的網路實現其實就是利用了Linux上的網路名稱空間和虛擬網路裝置(特別是vethpair).

基本原理

首先,要實現網路通訊,機器需要至少一個網路介面(物理介面或虛擬介面)來收發資料包,此外,如果不同子網之間要進行通訊,需要路由機制.

Docker中的網路介面預設都是虛擬的介面,虛擬介面的優勢之一就是轉發效率較高,Linux通過在核心中進行資料複製來實現虛擬介面之間的資料轉發,傳送介面的傳送快取中的資料包直接複製到接收介面的接受快取中,對於本地系統和容器內系統來看就像是一個正常的乙太網卡,只是他不需要真正同外部網路裝置通訊,速度要快很多.

Docker容器網路就是利用了這項技術,他在本地主機和容器內分別建立一個虛擬介面,並讓他們彼此連通(這樣的一對介面叫做veth pair).

相關文章