在Kubernetes部署中如何引導安全性
譯文【51CTO.com快譯】Kubernetes是當前流行和常用的容器編排工具之一。Kubernetes工作負載就像簡單的nginx服務器或cron作業一樣執行的應用程序。Kubernetes部署成為了一種最常用的工作負載,因為它可以輕松更新、擴展和管理。
最近發布的Kubernetes Hardening指南是一個很好的資源,它提供了有關如何有效保護Kubernetes的指導。該指南提供的信息清楚地表明,保護和強化Kubernetes不僅是Kubernetes管理員的工作,也是在集群上部署工作負載的開發人員的工作。
本文討論部署Kubernetes工作負載的開發人員如何通過應用“Kubernetes強化指南”提供的一些指南來引導安全性。
這是一個實用的指南,將采用一個簡單的Dockerfile,然后逐步添加安全優秀實踐來創建模板部署清單文件,開發人員可以快速重用該文件。
先決條件
- Docker是必需的,因為將從頭開始構建。
- 像minikube這樣的單節點Kubernetes集群應該遵循這一指南以及kubectl實用程序。開發人員可以使用官方minikube文檔在其環境中進行設置。
使用由與WSL2綁定的Docker Desktop創建的獨立集群作為后端。
本指南假設有一個可通過kubectl實用程序訪問的正在運行的集群,如下面的代碼所示。
- Shell
- git clone git@github.com:salecharohit/bootstrapsecurityinkubernetesdeployment.git
- cd springbootmaven
- docker build . -f Dockerfile.basic -t springbootmaven
- docker run --name springboot -d -p 8080:8080 springbootmaven
- curl http://localhost:8080
- Expected Response:
- Hello World From Spring Boot Build Using Maven on Alpine OS!
安全部署
保護Kubernetes工作負載可以有效地劃分為“構建時”的安全性和“運行時”的安全性。為了運行這些示例,將使用這個簡單的Spring Boot Hello World應用程序并將其部署在Kubernetes中,并應用構建時安全性和運行時安全性。相關網址如下: https://github.com/salecharohit/bootstrapsecurityinkubernetesdeployment
而在開始之前,先要克隆這個存儲庫,構建Docker容器,并在本地運行應用程序。
構建時的安全性
構建時的安全性更多地關注如何以減少的占用空間構建底層容器,并編程以盡可能少的權限執行。
以下將使用問題的解決方法討論這兩種方法:
(1)減少攻擊面
在容器中構建應用程序時,主要目標是讓應用程序在不考慮運行環境的情況下始終獨立運行,無論其運行環境是數據中心、云平臺還是內部部署設施。然而,在構建這些應用程序時有一條不成文的規則:它應該是一個獨立的應用程序,并且沒有很多依賴項。
以SpringBoot應用程序為例。這個應用程序運行的唯一依賴是它需要一個JVM或Java運行時。任何其他在容器中的東西實際上都是無用的。
例如,在基于AlpineOS構建的SpringBoot容器中,沒有任何特定需要安裝apk包管理器。
- Shell
- docker exec -it springboot /bin/sh
- apk add curl
因此,可以嘗試刪除apk二進制文件并重建或Docker映像。
此時將使用Dockerfile.asr來重建Docker容器,其共享如下:
- Dockerfile
- FROM maven:3.8.1-openjdk-17-slim AS MAVEN_BUILD
- WORKDIR /build/
- COPY pom.xml /build/
- COPY src /build/src/
- RUN mvn package
- FROM openjdk:17-alpine
- RUN rm -f /sbin/apk && \
- rm -rf /etc/apk && \
- rm -rf /lib/apk && \
- rm -rf /usr/share/apk && \
- rm -rf rm -rf /var/lib/apk
- COPY --from=MAVEN_BUILD /build/target/springbootmaven.jar /springbootmaven.jar
- EXPOSE 8080
- CMD java -jar /springbootmaven.jar
在此重建并重新運行:
- Shell
- # First let's stop the previously running container
- docker stop springboot
- # Next let's re-build and re-run
- docker build . -f Dockerfile.asr -t springbootmaven
- docker run --name springboot -p 8080:8080 springbootmaven
- docker run --name springboot -d -p 8080:8080 springbootmaven
- curl http://localhost:8080
現在嘗試再次運行apk add curl命令。
- Shell
- docker exec -it springboot /bin/sh
- apk add curl
因此成功擺脫了apk依賴,并且應用程序運行成功!
下面是一些專門為強化Alpine OS編寫的優秀腳本。根據編程語言進行挑選,并相應地強化基本alpine圖像。以下是一些參考的網址:
- https://gist.github.com/kost/017e95aa24f454f77a37
- https://github.com/ironpeakservices/iron-alpine/blob/master/Dockerfile
另一方面,還可以查看由谷歌公司創建的distroless容器,這也是非常值得推薦的。
(2)切換用戶場景
有人可能會爭辯說,如果網絡攻擊者在容器內獲得RCE,他們可能無法安裝curl、wget等包來建立持久性。
但是,仍然以“root”用戶身份運行,從技術上講,仍然可以重新安裝apk。
在此重新運行Docker容器并檢查它當前運行的權限。
- Shell
- docker exec -it springboot /bin/sh
- whoami
- ping rohitsalecha.com
因此,重要的是不要以root身份運行容器,而是以只有有限權限的用戶身份運行容器。
Dockerfile.lpr顯示了添加更多命令,這些命令添加了一個名為“boot”的用戶和組,并為其分配一個工作目錄(這是它的主目錄)。還為用戶和組分配了數值,以下將在Pod安全場景部分詳細討論。
- Dockerfile
- FROM maven:3.8.1-openjdk-17-slim AS MAVEN_BUILD
- WORKDIR /build/
- COPY pom.xml /build/
- COPY src /build/src/
- RUN mvn package
- FROM openjdk:17-alpine
- # Removing apk package manager
- RUN rm -f /sbin/apk && \
- rm -rf /etc/apk && \
- rm -rf /lib/apk && \
- rm -rf /usr/share/apk && \
- rm -rf rm -rf /var/lib/apk
- # Adding a user and group called "boot"
- RUN addgroup boot -g 1337 && \
- adduser -D -h /home/boot -u 1337 -s /bin/ash boot -G boot
- # Changing the context that shall run the below commands with User "boot" instead of root
- USER boot
- WORKDIR /home/boot
- # By default even in a non-root context, Docker copies the file as root. Hence its best practice to chown
- # the files being copied as the user. https://stackoverflow.com/a/44766666/1679541
- COPY --chown=boot:boot --from=MAVEN_BUILD /build/target/springbootmaven.jar /home/boot/springbootmaven.jar
- EXPOSE 8080
- CMD java -jar /home/boot/springbootmaven.jar
重建并重新運行:
- # First let's stop the previously running container
- docker stop springboot
- # Next let's re-build and re-run
- docker build . -f Dockerfile.lpr -t springbootmaven docker run --name springboot -d -p 8080:8080 springbootmaven curl http://localhost:8080
現在嘗試運行whoami命令,并檢查現在正在運行哪個容器的哪些權限。
- Shell
- docker exec -it springboot /bin/sh
- whoami
- ping rohitsalecha.com
運行時的安全性
現在人們對構建時安全性有了很大的信心,其中已經學會了刪除包并更新用戶場景,以使用有限的權限運行容器。這些安全特性是在構建Docker容器時應用的;但是,還需要關注容器在Kubernetes環境中運行時的安全狀況,這將在下面進行探討。
在開始保護Kubernetes部署之前,首先將Docker容器推送到hub.docker.com,在Kubernetes集群上運行的應用程序。可以使用這一指南開始相同的操作。
- Shell
- docker build . -f Dockerfile.lpr -t springbootmaven
- docker tag springbootmaven salecharohit/springbootmaven
- docker push salecharohit/springbootmaven
- docker run -d -p 8080:8080 --name springboot salecharohit/springbootmaven
- curl http://localhost:8080
現在Docker鏡像已經準備好了,應用kubernetes-basic.yaml文件來部署這個應用程序以及一個可以幫助連接到它的服務。
- YAML
- # Create Namespace
- apiVersion: v1
- kind: Namespace
- metadata:
- name: boot
- ---
- # Create SpringBoot Deployment
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- labels:
- app: springbootmaven
- name: springbootmaven
- namespace: boot
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: springbootmaven
- template:
- metadata:
- labels:
- app: springbootmaven
- spec:
- containers:
- - image: salecharohit/springbootmaven
- name: springbootmaven
- ports:
- - containerPort: 8080
- ---
- # Create Service for SpringBoot Deployment
- apiVersion: v1
- kind: Service
- metadata:
- labels:
- app: springbootmaven
- name: springbootmaven
- namespace: boot
- spec:
- ports:
- - name: "http"
- port: 8080
- targetPort: 8080
- selector:
- app: springbootmaven
如果Pod需要與Kubernetes API-Server通信,則需要服務帳戶令牌進行身份驗證。
- Shell
- kubectl apply -f kubernetes-basic.yaml
- kubectl get deploy -n boot
- # Run a temporary container that will only curl our bootservice
- kubectl run -it testpod --image=radial/busyboxplus:curl --restart=Never --rm -- curl http://springbootmaven.boot.svc.cluster.local:8080
- Expected Output:
- Hello World From Spring Boot Build Using Maven on Alpine OS!pod "testpod" deleted
(1)服務帳戶令牌
如果Pod需要與Kubernetes API服務器通信,則需要服務帳戶令牌進行身份驗證。
在默認情況下,每個Pod都會分配一個服務帳戶令牌,該令牌安裝在/var/run/secrets/kubernetes.io/serviceaccount/token上。可以通過部署SpringBoot應用程序在實踐中查看這一點。
- Shell
- kubectl get pods -n boot
- kubectl exec -it springbootmaven-7d7c5c8597-mndv9 -n boot -- /bin/sh
- TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
- curl -k -H "Authorization:Bearer $TOKEN" https://kubernetes.docker.internal:6443/version
應用程序上的RCE漏洞可以將此訪問令牌泄露給網絡攻擊者,他們可以濫用該令牌來讀寫同一命名空間中的資源,甚至具有全局讀取權限。
解決這一問題有兩個解決方案,具體取決于具體情況:一是Pod不需要訪問API-Server。 二是Pod需要訪問API-Server。
- 不需要訪問API-Server的Pod
這種情況很容易解決,只需在Kubernetes清單文件中添加兩行,如下所示:
- YAML
- 1 serviceAccountName: ""
- 2 automountServiceAccountToken: false
完整的部署文件kubernetes-nosa.yaml如下:
- YAML
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- labels:
- app: springbootmaven
- name: springbootmaven
- namespace: boot
- spec:
- eplicas: 1
- selector:
- matchLabels:
- app: springbootmaven
- template:
- metadata:
- labels:
- app: springbootmaven
- spec:
- containers:
- image: salecharohit/springbootmaven
- name: springbootmaven
- ports:
- - containerPort: 8080
- serviceAccountName: ""
- automountServiceAccountToken: false
然后檢查服務帳戶令牌現在是否已安裝。
- Shell
- # Ensure our previous deploy is deleted.
- kubectl delete ns boot
- # Apply with no service account token
- kubectl apply -f kubernetes-nosa.yaml
- kubectl get pods -n boot
- kubectl exec -it springbootmaven-5568b9874f-8nml8 -n boot -- /bin/sh
- cat /var/run/secrets/kubernetes.io/serviceaccount/token
如上所示,不再掛載默認服務帳戶令牌。
- 需要訪問API-Server的Pod
在這種情況下,需要創建將ServiceAccount映射到角色的ServiceAccount、Role和RoleBinding。以下是Kubernetes清單:
- 為特定命名空間(即boot)創建一個名為bootserviceaccount的ServiceAccount。
- 創建一個名為bootservicerole的角色,該角色只有查看正在運行的Pod的權限。
- 創建一個名為bootservicerolebinding的RoleBinding。
- 掛載ServiceAccount,從而在部署中使用以下幾行進行創建。
- YAML
- ---
- spec:
- containers:
- - image: salecharohit/springbootmaven
- name: springbootmaven
- ports:
- - containerPort: 8080
- serviceAccountName: bootserviceaccount
- ---
這將允許僅讀取“boot”命名空間中的Pod。
完整的部署文件kubernetes-withsa.yaml如下:
- YAML
- # Create Namespace
- apiVersion: v1
- kind: Namespace
- metadata:
- name: boot
- ---
- apiVersion: v1
- kind: ServiceAccount
- metadata:
- name: bootserviceaccount
- namespace: boot
- ---
- kind: Role
- apiVersion: rbac.authorization.k8s.io/v1
- metadata:
- name: bootservicerole
- namespace: boot
- rules:
- - apiGroups: [""]
- resources: ["pods"]
- verbs: ["get", "list", "watch"]
- ---
- kind: RoleBinding
- apiVersion: rbac.authorization.k8s.io/v1
- metadata:
- name: bootservicerolebinding
- namespace: boot
- subjects:
- - kind: ServiceAccount
- name: bootserviceaccount
- namespace: boot
- roleRef:
- kind: Role
- name: bootservicerole
- apiGroup: rbac.authorization.k8s.io
- ---
- # Create SpringBoot Deployment
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- labels:
- app: springbootmaven
- name: springbootmaven
- namespace: boot
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: springbootmaven
- template:
- metadata:
- labels:
- app: springbootmaven
- spec:
- containers:
- - image: salecharohit/springbootmaven
- name: springbootmaven
- ports:
- - containerPort: 8080
- serviceAccountName: bootserviceaccount
- ---
- # Create Service for SpringBoot Deployment
- apiVersion: v1
- kind: Service
- metadata:
- labels:
- app: springbootmaven
- name: springbootmaven
- namespace: boot
- spec:
- ports:
- - name: "http"
- port: 8080
- targetPort: 8080
- selector:
- app: springbootmaven
現在運行并檢查的應用程序是否運行良好。
- # Ensure our previous deploy is deleted.
- kubectl delete ns boot
- kubectl apply -f kubernetes-withsa.yaml
- kubectl run -it testpod --image=radial/busyboxplus:curl --restart=Never --rm -- curl http://springbootmaven.boot.svc.cluster.local:8080
#確保刪除以前的部署。
(2)Pod的安全場景
盡管已將基本Docker映像配置為以非root權限運行,但仍需要添加少量配置作為安全優秀實踐。因此需要:
①限制容器和Pod的能力。
②禁用權限提升。
③將容器配置為使用先前在Dockerfile.lpr中創建的特定uid/gid運行。
在Kubernetes清單文件中,定義了兩種類型的“安全場景(SecurityContexts)”。
- 在 Pod 級別運行,這將應用到在這個Pod中運行的所有容器
- YAML
- ---
- securityContext:
- fsGroup: 1337
- runAsNonRoot: true
- runAsUser: 1337
- containers:
- ---
- 在容器級別運行
- YAML
- ---
- securityContext:
- allowPrivilegeEscalation: false
- privileged: false
- runAsUser: 1337
- capabilities:
- drop: ["SETUID", "SETGID"]
- serviceAccountName: ""
- automountServiceAccountToken: false
- ---
嵌入PodSecurity場景的完整部署文件kubernetes-ps.yaml如下:
- YAML
- # Create Namespace
- apiVersion: v1
- kind: Namespace
- metadata:
- name: boot
- ---
- # Create SpringBoot Deployment
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- labels:
- app: springbootmaven
- name: springbootmaven
- namespace: boot
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: springbootmaven
- template:
- metadata:
- labels:
- app: springbootmaven
- spec:
- securityContext:
- fsGroup: 1337
- runAsNonRoot: true
- runAsUser: 1337
- containers:
- - image: salecharohit/springbootmaven
- name: springbootmaven
- ports:
- - containerPort: 8080
- securityContext:
- allowPrivilegeEscalation: false
- privileged: false
- runAsUser: 1337
- capabilities:
- drop: ["SETUID", "SETGID"]
- erviceAccountName: ""
- automountServiceAccountToken: false
- ---
- # Create Service for SpringBoot Deployment
- apiVersion: v1
- kind: Service
- metadata:
- labels:
- app: springbootmaven
- name: springbootmaven
- namespace: boot
- spec:
- ports:
- - name: "http"
- port: 8080
- targetPort: 8080
- selector:
- app: springbootmaven
運行并測試應用程序是否正在運行。
- Shell
- # Ensure our previous apply is deleted
- kubectl delete ns boot
- kubectl apply -f kubernetes-ps.yaml
- kubectl run -it testpod --image=radial/busyboxplus:curl --restart=Never --rm -- curl http://springbootmaven.boot.svc.cluster.local:8080
- kubectl get pods -n boot
- kubectl exec -it springbootmaven-56c64ff85-mqz2z -n boot -- /bin/sh
- whoami
- id
- ping google.com
開發人員可以根據自己的需求刪除更多功能。
AppArmor或SecComp等功能需要控制平面組件的附加配置。因此,我的討論僅限于開箱即用的功能,這些功能可以輕松激活并確保良好的安全保證水平。
(3)不可變文件系統
在容器化環境中運行的應用程序很少寫入數據,因為這實際上違背了擁有不可變系統的邏輯。但是,有時可能需要它來緩存或臨時交換/處理文件。因此,為了向開發人員提供此功能,可以將emptyDir裝載為臨時卷,一旦容器被終止,該臨時卷就會丟失。
有了它,還可以添加另一個名為“readOnlyRootFilesystem”的安全場景屬性,并將其設置為true,因為在容器中運行的應用程序不再需要在文件系統上除“tmp”目錄以外的任何位置寫入。
可以按如下所示配置上述要求。
- YAML
- ---
- containers:
- - image: salecharohit/springbootmaven
- name: springbootmaven
- ports:
- - containerPort: 8080
- securityContext:
- readOnlyRootFilesystem: true
- volumeMounts:
- - mountPath: /tmp
- name: tmp
- volumes:
- - emptyDir: {}
- name: tmp
- ---
完整的部署文件kubernetes-rofs.yaml如下面的代碼所示:
- YAML
- # Create Namespace
- apiVersion: v1
- kind: Namespace
- metadata:
- name: boot
- ---
- # Create SpringBoot Deployment
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- labels:
- app: springbootmaven
- name: springbootmaven
- namespace: boot
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: springbootmaven
- template:
- metadata:
- labels:
- app: springbootmaven
- spec:
- securityContext:
- fsGroup: 1337
- runAsNonRoot: true
- runAsUser: 1337
- containers:
- - image: salecharohit/springbootmaven
- name: springbootmaven
- ports:
- - containerPort: 8080
- securityContext:
- allowPrivilegeEscalation: false
- readOnlyRootFilesystem: true
- privileged: false
- runAsUser: 1337
- capabilities:
- drop: ["SETUID", "SETGID"]
- volumeMounts:
- - mountPath: /tmp
- name: tmp
- serviceAccountName: ""
- automountServiceAccountToken: false
- volumes:
- - emptyDir: {}
- name: tmp
- ---
- # Create Service for SpringBoot Deployment
- apiVersion: v1
- kind: Service
- metadata:
- labels:
- app: springbootmaven
- name: springbootmaven
- namespace: boot
- spec:
- ports:
- - name: "http"
- port: 8080
- targetPort: 8080
- selector:
- app: springbootmaven
開始應用并測試應用程序是否正在運行。
- Shell
- # Ensure our previous apply is deleted
- kubectl delete ns boot
- kubectl apply -f kubernetes-rofs.yaml
- kubectl run -it testpod --image=radial/busyboxplus:curl --restart=Never --rm -- curl http://springbootmaven.boot.svc.cluster.local:8080
- kubectl get pods -n boot
- kubectl exec -it springbootmaven-56c64ff85-mqz2z -n boot -- /bin/sh
- pwd
- touch test.txt
結論
如今已經了解了可以在容器化應用程序中嵌入哪些不同的控件,還了解了如何啟用運行時保護機制,這些機制可以使網絡攻擊者難以在容器化系統中站穩腳跟。
kubernetes-rofs.yaml可以作為一個很好的模板,供開發人員在kubernetes環境中部署時使用默認的安全功能對其應用程序進行容器化。
當然,企業需要為特定的應用程序創建Dockerfile。
原文標題:Bootstrap Security in Kubernetes Deployments,作者:Rohit Salecha
【51CTO譯稿,合作站點轉載請注明原文譯者和出處為51CTO.com】