Kubernetes-深入分析集群安全機制


Kubernetes過一系列機制來實現集群的安全機制,包括API Server的認證授權、准入控制機制及保護敏感信息的Secret機制等。集群的安全性必須考慮以下的幾個目標:

  1. 保證容器與其所在宿主機的隔離;
  2. 限制容器給基礎設施及其他容器帶來消極影響的能力;
  3. 最小權限原則,合理限制所有組件權限,確保組件只執行它被授權的行為,通過限制單個組件的能力來限制他所能達到的權限范圍;
  4. 明確組件間邊界的划分;
  5. 划分普通用戶和管理員角色;
  6. 在必要的時候允許將管理員權限賦給普通用戶;
  7. 允許擁有Secret數據(Keys、Certs、Passwords)的應用在集群中運行;

下面分別從Authentication、Authorization、Admission Control、Secret和Service Account等方面來說明集群的安全機制。

API Server認證

Kubernetes集群中所有資源的訪問和變更都是通過Kubernetes API Server的REST API來實現的,所以集群安全的關鍵點在於識別認證客戶端身份(Authentication)以及訪問權限的授權(Authorization)。

Kubernetes提供管理三種級別的客戶端身份認證方式:

  1. 最嚴格的HTTPS證書認證:基於CA根證書簽名的雙向數字證書認證方式;
  2. HTTP Token認證:通過一個Token來識別合法用戶;
  3. HTTP Base認證:通過用戶名+密碼的方式認證;

SSL雙向認證步驟:

  1. HTTPS通信雙方的務器端向CA機構申請證書,CA機構是可信的第三方機構,它可以是一個公認的權威的企業,也可以是企業自身。企業內部系統一般都使用企業自身的認證系統。CA機構下發根證書、服務端證書及私鑰給申請者;
  2. HTTPS通信雙方的客戶端向CA機構申請證書,CA機構下發根證書、客戶端證書及私鑰個申請者;
  3. 客戶端向服務器端發起請求,服務端下發服務端證書給客戶端。客戶端接收到證書后,通過私鑰解密證書,並利用服務器端證書中的公鑰認證證書信息比較證書里的消息,例如域名和公鑰與服務器剛剛發送的相關消息是否一致,如果一致,則客戶端認為這個服務器的合法身份;
  4. 客戶端發送客戶端證書給服務器端,服務端接收到證書后,通過私鑰解密證書,獲得客戶端的證書公鑰,並用該公鑰認證證書信息,確認客戶端是否合法;
  5. 客戶端通過隨機秘鑰加密信息,並發送加密后的信息給服務端。服務器端和客戶端協商好加密方案后,客戶端會產生一個隨機的秘鑰,客戶端通過協商好的加密方案,加密該隨機秘鑰,並發送該隨機秘鑰到服務器端。服務器端接收這個秘鑰后,雙方通信的所有內容都都通過該隨機秘鑰加密;

CA認證流程圖:

上述是雙向SSL協議的具體通信過程,這種情況要求服務器和用戶雙方都有證書。單向認證SSL協議不需要客戶擁有CA證書,對應上面的步驟,只需將服務器端驗證客戶端證書的過程去掉,以及在協商對稱密碼方案和對稱通話秘鑰時,服務器端發送給客戶端的是沒有加過密的(這並不影響SSL過程的安全性)密碼方案。

HTTP Token原理:HTTP Token的認證是用一個很長的特殊編碼方式的並且難以被模仿的字符串——Token來表明客戶身份的一種方式。在通常情況下,Token是一個復雜的字符串,比如我們用私鑰簽名一個字符串的數據就可以作為一個Token,此外每個Token對應一個用戶名,存儲在API Server能訪問的一個文件中。當客戶端發起API調用請求時,需要在HTTP Header里放入Token,這樣一來API Server就能夠識別合法用戶和非法用戶了。

HTTP Base:常見的客戶端賬號登錄程序,這種認證方式是把“用戶名+冒號+密碼”用BASE64算法進行編碼后的字符串放在HTTP REQUEST中的Header Authorization域里發送給服務端,服務端收到后進行解碼,獲取用戶名及密碼,然后進行用戶身份的鑒權過程。

API Server授權

對合法用戶進行授權(Authorization)並且隨后在用戶訪問時進行鑒權,是權限與安全系統的重要一環。授權就是授予不同用戶不同訪問權限,API Server目前支持一下集中授權策略:

  • AlwaysDeny:拒絕所有請求,該配置一般用於測試;
  • AlwaysAllow:接收所有請求,如果集群不需要授權流程,可以采用該策略,此為Kubernetes默認的策略;
  • ABAC:(Attribute-Base Access Control)為基於屬性的訪問控制,表示使用用戶配置的授權規則去匹配用戶的請求;

為了簡化授權的復雜度,對於ABAC模式的授權策略,Kubernetes僅有下面四個基本屬性:

  1. 用戶名(代表一個已經被認證的用戶的字符型用戶名)
  2. 是否是只讀請求(REST的GET操作是只讀的)
  3. 被訪問的是哪一類資源,例如Pod資源/api/v1/namespaces/default/pods
  4. 被訪問對象所屬的Namespace

當API Server啟用ABAC模式時,需要指定授權文件的路徑和名字(--authorization_policy_file=SOME_FILENAME),授權策略文件里的每一行都是一個Map類型的JOSN對象,被稱為訪問策略對象,我們可以通過設置“訪問策略對象”中的如下屬性來確定具體的授權行為:

  • user:字符串類型,來源於Token文件或基本認證文件中的用戶名字段的值;
  • readonly:true時表示該策略允許GET請求通過;
  • resource:來自於URL的資源,例如“Pod”;
  • namespace:表明該策略允許訪問某個namespace的資源;

eg:

  • {"user":"alice"}
  • {"user":"kubelet","resource":"Pods","readonly":true}
  • {"user":"kubelet","resource":"events"}
  • {"user":"bob","resource":"Pods","readonly":true,"ns":"myNamespace"}

Admission Control准入控制

通過認證和鑒權之后,客戶端並不能得到API Server的真正響應,這個請求還需通過Admission Control所控制的一個“准入控制鏈”的層層考驗,Admission Control配備有一個“准入控制器”的列表,發送給API Server的任何請求都需要通過列表中每個准入控制器的檢查,檢查不通過API Server拒絕此調用請求。此外,准入控制器還能夠修改請求參數以完成一些自動化的任務。比如Service Account這個控制器,當前可配置的准入控制如下:

  • AlwaysAdmit:允許所有請求;
  • AlwaysPullmages:在啟動容器之前總去下載鏡像,相當於在每個容器的配置項imagePullPolicy=Always
  • AlwaysDeny:禁止所有請求,一般用於測試;
  • DenyExecOnPrivileged:它會攔截所有想在Privileged Container上執行命令的請求,如果你的集群支持Privileged Container,你又希望限制用戶在這些Privileged Container上執行命令,強烈推薦你使用它;
  • Service Account:這個plug-in將ServiceAccount實現了自動化,默認啟用,如果你想使用ServiceAccount對象,那么強烈你推薦使用它;
  • SecurityContextDeny:這個插件將使用SecurityContext的Pod中的定義全部失效。SecurityContext在Container中定義了操作系統級別的安全設定(uid,gid,capabilityes,SELinux等)
  • ResourceQuota:用於配額管理目的,作用於namespace上,它會觀察所有請求,確保在namespace上的配額不會超標。推薦在Admission Control參數列表中這個插件排最后一個;
  • LimitRanger:用於配額管理,作用於Pod與Container,確保Pod與Container上的配額不會超標;
  • NamespaceExists(已過時):對所有請求校驗namespace是否已存在,如果不存在則拒絕請求,已合並至NamespaceLifecycle。
  •  NamespaceAutoProvision(已過時):對所有請求校驗namespace,如果不存在則自動創建該namespace,推薦使用NamespaceLifecycle。
  • NamespaceLifecycle:如果嘗試在一個不存在的namespace中創建資源對象,則該創建請求將被拒絕。當刪除一個namespace時,系統將會刪除該namespace中所有對象,保存Pod,Service等。

在API Server上設置--admission-control參數,即可定制我們需要的准入控制鏈,如果啟用多種准入控制選項,則建議的設置如下:

  • --admission-control=NamespaceLifecycle,LimitRanger,SecurityContextDeny,ServiceAccount,ResourceQuota

下面着重介紹三個准入控制器:

SecurityContextDeny

Security Context時運用於容器的操作系統安全設置(uid、gid、capabilities、SELinux role等),Admission Control的SecurityContextDeny插件的作用是,禁止創建設置了Security Context的Pod,例如包含以下配置項的Pod:

  • spec.containers.securityContext.seLinuxOptions
  • spec.containers.securityContext.runAsUser

ResourceQuota

ResourceQuota不僅能夠限制某個Namespace中創建資源的數量,而且能夠限制某個namespace中被Pod所請求的資源總量。該准入控制器和資源對象ResourceQuota一起實現了資源的配額管理;

LimitRanger

准入控制器LimitRanger的作用類似於上面的ResourceQuota控制器,這對Namespace資源的每個個體的資源配額。該插件和資源對象LimitRange一起實現資源限制管理。

Service Account

Servuce Account是一種賬號,但他並不是給Kubernetes的集群的用戶(系統管理員、運維人員、租戶用戶等),而是給運行在Pod里的進程用的,它為Pod里的進程提供必要的身份證明。

Pod中訪問Kubernetes API Server服務的時候,是以Service方式訪問服務名為kubernetes這個服務的,而kubernetes服務又只在HTTPS安全端口443上提供服務,那么如何進行身份認證呢?在Kubernetes的官方文檔並沒有清除的說明這個問題。

通過查看源碼獲知這是在用一種類似HTTP Token的新的認證方式--ServiceAccount Auht,Pod中的客戶端調用Kubernetes API的時候,在HTTP Header中傳遞了一個Token字符串,這類似於之前提到的HTTP Token認證方式,存在以下幾個不同點:

  • 此處的Token的內容來源於Pod里指定路徑下的一個文件(/run/secrets/kubernetes.io/serviceaccount/token),這種token是動態生成的,確切的說,是由KubernetesController進程用API Server的私鑰(--service-account-private-key-file指定的私鑰)簽名生成的一個JWT Secret。
  • 官方提供的客戶端REST框架代碼里,通過HTTPS方式與API Server建立鏈接后,會用Pod里指定路徑下的一個CA證書(/run/secrets/kubernetes.io/serviceaccount/ca.crt)驗證API Server發來的證書,驗證是否是被CA證書簽名的合法證書。
  • API Server收到這個Token以后,采用自己的私鑰(實際是使用參數service-account-key-file)指定的私鑰,如果此參數沒有設置,則默認采用tls-private-key-file指定的參數,即自己的私鑰,對token進行合法性驗證。

明白原理之后。接下來分析認證過程中涉及的Pod中的三個文件:

  • /run/secrets/kubernetes.io/serviceaccount/token
  • /run/secrets/kubernetes.io/serviceaccount/ca.crt
  • /run/secrets/kubernetes.io/serviceaccount/namespace(客戶端采用這里指定的namespace作為參數調用Kubernetes API)

這三個文件由於參與到Pod進程與API Server認證的過程中,起到了類似Secret(私密憑據)的作用,所以他們被稱為Kubernetes Secret對象。Secret從屬於ServiceAccount資源對象,屬於Service Account的一部分,一個ServiceAccount對象里面可以包括多個不同的Secret對象,分別用於不同目的的認證活動。

下面通過命令來直觀的加深對ServiceAccount的認識:

查看系統中ServiceAccount對象,可以看到一個名為default的Service Account對象,包含一個名為default-token-xxx的Secret,這個Secret同時是“Mountable secrets”,表明他是需要被Mount到Pod上的。

  • kubectl describe serviceaccounts
  • kubectl describe secrets default-token-xxx

default-token-xxx包括三個數據項:

  • token
  • ca.crt
  • namespace

聯想到“Mountable secrets”的標記,以及之前看到的Pod中的三個文件的文件名:每個namespace下有一個名為default的默認的ServiceAccount對象,這個ServiceAccount里有一個名為Tokens的可以作為Volume一樣被Mount到Pod里的Secret,當Pod啟動時這個Secret會被自動Mount到Pod的指定目錄下,用來協助完成Pod中的進程訪問API Server時的身份鑒權過程。

一個ServiceAccount可以包括多個Secrets對象:

  • 名為Tokens的Secret用於訪問API Server的Secret,也被稱為ServiceAccountSecret;
  • 名為Image Pull secrets的Secret用於下載容器鏡像時的認證過程,通常鏡像庫運行在Insecure模式下,所以這個Secret為空;
  • 用戶自定義的其他Secret,用於用戶的進程;

如果一個Pod在定義時沒有指定spec.service.AccountName屬性,則系統會自動為其賦值為“Default”,即使用同一namespace下默認的ServiceAccount,如果某個Pod需要使用非default的ServiceAccount,需要在定義時指定:

apiVersion:v1

kind:Pod

metadata:

    name:mypod

spec:

    containers:

    - name:mycontainer

      image:

    serviceAccountName:myserviceaccount

Kubernetes之所以要創建兩套獨一的賬號系統,原因如下:

  • User賬號是給人用的,ServiceAccount是給Pod里的進程使用的,面向對象不同;
  • User賬號是全局性的,ServiceAccount則屬於某個具體的Namespace;
  • 通常來說,User賬號是與后端的用戶數據庫同步的,創建一個新用戶通常要走一套復雜的業務流程才能實現,ServiceAccount的創建則需要極輕量級實現方式,集群管理員可以很容易為某些特定任務組創建一個ServiceAccount。
  • 對於這兩種不同的賬戶,其審計要求通常不同;
  • 對於一個復雜的系統來說,多個組件通常擁有各種賬號的配置信息,ServiceAccount是Namespace隔離的,可以針對組件進行一對一的定義,同時具備很好的“便攜性”。

下面分析Service Account與Secret相關的一些運行機制:

 Controller manager創建了ServiceAccountController與Token Controllerl兩個安全相關的控制器。其中ServiceAccountController一直監聽Service Account和Namespace的事件,如果一個Namespace中沒有default Service Account,那么Service Account Controller就會為該Namespace創建一個默認的(default)的Service Account,這就是我們之前看到的每個namespace下都有一個名為default的ServiceAccount的原因。

如果Controller manager進程在啟動時指定了API Server私鑰(service-account-private-key-file)參數,那么Controller manager會創建Token Controller。Token Controller也監聽Service Account的事件,如果發現新建的Service Account里沒有對應的Service Account Secret,則會用API Server私鑰創建一個Token(JWT Token),並用該Token、CA證書Namespace名稱等三個信息產生一個新的Secret對象,然后放入剛才的Service Account中;如果監聽到的事件是刪除Service Account事件,則自動刪除與該Service Account相關的所有Secret。此外,Token Controller對象同時監聽Secret的創建、修改和刪除事件,並根據事件的不同做不同的處理。

當我們在API Server的鑒權過程中啟用了Service Account類型的准入控制器,即在kube-apiserver的啟動參數中包括下面的內容時:

  • --admission_control=ServiceAccount

則針對Pod新增或修改的請求,Service Account准入控制器會驗證Pod里Service Account是否合法。

  1. 如果spec.serviceAccount域沒有被設置,則Kubernetes默認為其制定名字為default的Serviceaccount;
  2. 如果Pod的spec.serviceAccount域指定了default以外的ServiceAccount,而該ServiceAccount沒有事先被創建,則該Pod操作失敗;
  3. 如果在Pod中沒有指定“ImagePullSecrets”,那么該sec.serviceAccount域指定的ServiceAccount的“ImagePullSecrets”會被加入該Pod;
  4. 給Pod添加一個新的Volume,在該Volume中包含ServiceAccountSecret中的Token,並將Volume掛載到Pod中所有容器的指定目錄下(/var/run/secrets/kubernetes.io/serviceaccount);

綜上所述,ServiceAccount正常運行需要以下幾個控制器:

  • Admission Controller
  • Token Controller
  • Service Account Controller

Secret私密憑據

Secret主要作用是保管私密數據,比如密碼、OAuth Tokens、SSH Keys等信息。將這些私密信息放在Secret對象中比直接放在Pod或Docker Image中要更安全,也便於使用和分發。

  • 創建Secret

secret.yaml

apiVersion:v1

kind:Secret

metadata:

    name:mysecret

type: Opaque

data:

    password:dmfsdWUtMg0k

    username:dmfsdWUtMg0k

kubectl create -f secret.yaml

在上面的data域中的各子域的值必須為BASE64編碼值,其中password域和username域BASE64編碼前的值分別為value-1和value-2。一旦secret被創建,可以通過以下三個方式使用它:

  • 在創建Pod時,通過為Pod指定ServiceAccount來自動使用該Seret;
  • 通過掛載該Secret到Pod來使用它;
  • Docker鏡像下載時使用,通過指定Pod的spec.ImagePullSecrets來引用它;

 

  • 第一種方式主要用在API Server鑒權方面;
  • 第二種方式如下:

apiVersion:v1

kind:Pod

metadata:

    name:mypod

    namespace:myns

spec:

    containers:

    - name:mycontainer

      image:redis

      volumeMounts:

      - name:foo

        mountPath:“/etc/foo”

        readOnly:true

volumes:

- name:foo

  secret:

      secretName:mysecret

  • 第三種方式如下:
    • 執行login登錄私有registry
    • docker login localhost:5000(輸入賬戶及密碼,則會創建新用戶,並把相關信息寫入~/。dockercfg文件中)
    • 用BASE64編碼dockercfg的內容;
    • cat ~/.dockercfg|grep base64
    • 將上一步命令的輸出結果作為secret的“data.dockercfg”域的內容,由此來創建一個Secret:

 image-pull-secret.yaml:

apiVersion:v1

kind:Secret

metadata:

    name:myregistrykey

data:

    .dockercfg:xxx

type:kubernetes.io/dockercfg

  • kubectl create -f image-pull-secret.yaml
  • 在創建Pod的時候引用該Secret:

pods.yaml

apiVersion:v1

kind:Pod

metadata:

    name:mypod2

spec:

    containers:

    - name:foo

      image:xxxxxx:v1

imagePullSecrets:

- name:myregistrykey

  • kubectl create -f pods.yaml

每個單獨的Secret大小不能超過1M,Kubernetes不鼓勵創建大尺寸的Secret,因為如果使用大尺寸的Secret,則將大量占用API Server和kubelet的內存。當然創建許多小的Secret也能耗盡API Server和kubelet的內存。

在使用Mount方式掛載Secret時,Container中Secret的“data”域的各個域的key值作為目錄中的文件,Value值被BASE64編碼后存儲在相應的文件中。前面的例子中創建的Secret,被掛載到一個叫做mycontainer的container中,在該container中可以通過命令查看所生產的文件和文件中的內容:

  • ls /etc/foo

username

password

  • cat /etc/foo/username

value-1

  • cat /etc/foo/password

value-2

我們可以通過Secret保管其他系統的敏感信息(比如數據庫用戶名和密碼),並以Mount的方式將Secret掛載到Container中,然后通過訪問目錄中的文件的方式獲取該敏感信息。當Pod被API Server創建時,API Server不會校驗該Pod引用的Secret是否存在。一旦這個Pod被調度,則Kubelet將試着獲取Secret的值。如果Secret不存在或暫時無法連接到API Server,則kubelet將按一定的時間間隔定期重試獲取該Secret,並發送一個Event來解釋Pod沒有啟動的原因。一旦Secret被Pod獲取,則Kubelet將創建並Mount包含Secret的Volume。只有所有的Volume被Mount后,Pod中的Container才會被啟動。在kubelet啟動Pod中container后,Container中和Secret相關的Volume將不會被改變,即使Secret本身被修改了。為了使用更新后的Secret,必須刪除舊的Pod,並重新創建一個新的Pod,因此更新Secret的流程和部署一個新的Image是一樣的。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM