Skip to main content

11個保護你 Kubernetes 集群的技巧與觀念(下)

Preface

本篇文章的原文為 11 Ways Not to Get Hacked

本文作者將這 11 個技巧與觀念分成三大部分來 分別是

  1. The Control Plane
  2. Workloads
  3. The Future

本篇文章延續 11個保護你 Kubernetes 集群的技巧與觀念(上)

繼續探討原文作者後半部分的概念

在上篇文章中,我們專注於 The Control Plane 這邊相關的安全管理,而本篇文章我們則會專注於後續的兩個方向,分別是 Workloads 以及 The Future

Workloads

相對於 The Control Plane 著重於整個集群架構上的安全問題, Workloads 則是著重於運行於 kubernetes 內的各種工作服務,如 Pods,Deployments, Jobs 等 雖然這些工作服務在部署階段是受到 kubernetes 經過檢查確認合法才會往下執行的,但是由於這些工作服務都會直接面向使用者,若這些工作容器內本身的權限過高且遭受到非預期的攻擊時,就會衍生出其他的安全性問題。

因此作者認為,針對這些容器相關的部署,其權限能夠愈低愈好,針對其用途開啟特定權限,不能為了方便偷懶就將全部的權限開啟。

Use Linux Security Features and PodSecurityPolicies

針對 Linuux 相關的容器來說,Linux Kernel 其實提供了不少安全控管的機制,譬如

  1. Linux Capability
  2. AppArmor
  3. SELinux
  4. seccomp

Linux Capability

Linux Capability 是針對 Thread 為單位的權限控管,部分跟系統底層的操作都需要特定的 capability 才能夠操作,譬如擁有 NET_ADMIN 能力的則可以透過相關指令去修改系統上的網路設定(Routing/Interface), 而擁有 NET_RAW 的則有能力可以去聽取 Raw Socket 的封包。 在 Kubernetes 內也可以透過 SecurityContext 對每個 Container 去進行相關的權限設定. 類似用法如下

pods/security/security-context-4.yaml
apiVersion: v1
kind: Pod
metadata:
name: security-context-demo-4
spec:
containers:
- name: sec-ctx-4
image: gcr.io/google-samples/node-hello:1.0
securityContext:
capabilities:
add: ["NET_ADMIN", "SYS_TIME"]

AppArmor

AppArmor 則是一種基於 Process 程序為單位去限制其存取檔案及操作的一種機制。 針對每個 Process 可以去描述只能存取哪些檔案,藉此降低該程序被攻擊時所造成的傷害.

對於 Kubernetes來說,自從 1.4.0 開始就有支援 AppArmor 相關的功能,可以事先在每個節點上安裝 AppArmor 並且透過 Profile 的方式去設定 AppArmor 的權限,然後每個 Pod 透過 Annotation 的方式去掛載特定的 AppArmor 來限制該容器的權限。 詳細的範例可以參考官網的範例Kubernetes AppArmor

apiVersion: v1
kind: Pod
metadata:
name: hello-apparmor
annotations:
# Tell Kubernetes to apply the AppArmor profile "k8s-apparmor-example-deny-write".
# Note that this is ignored if the Kubernetes node is not running version 1.4 or greater.
container.apparmor.security.beta.kubernetes.io/hello: localhost/k8s-apparmor-example-deny-write
spec:
containers:
- name: hello
image: busybox
command: [ "sh", "-c", "echo 'Hello AppArmor!' && sleep 1h" ]

SELinux

Security-Enhanced Linux (SELinux) 主旨在於提供更強安全性的 Linux 系統,藉由取代原先的 自主式存取控制 (Discretionary Access Control, DAC)委任式存取控制 (Mandatory Access Control, MAC) 的方法 來針對特定的應用程式與特定的使用者達成更細部的權限管理,可達成即使是 root 身份在執行特定檔案/指令時也會被降權成非root權限。

對應於 Kubernetes 來說,首先要先在節點上面開啟 SELinux 相關的功能,並且正確設定完畢。 接下來對於要採用該 SELinuxPod 中去設定 SecurityContext,並且透過 seLinuxOptions 的方式來設定相關的參數 整體的參數結構可以參考 Kubernetes API Reference而使用範例可參考如下

...
securityContext:
seLinuxOptions:
level: "s0:c123,c456"

Seccomp

seccomp-bpf 是一套以 process 程序為單位去限制其能夠呼叫 Linux Kernel System Call 的機制。 一般的應用程式大部分可能都只會用到 Read,Write 等基本功能,再來可能會牽扯到 Fork/Clone 這類型,更甚至會牽扯到網路相關的網卡綁定。

因此透過 seccomp 的功能去限制能夠執行的 System Call 也能夠大幅的降低當應用程式被攻擊時所造成的傷害。

對應於 kubernetes 來說,該功能目前還是屬於 Alpha 階段,目前的使用方式大概是

  1. 在對應的節點上放置相關的 seccomp 的檔案,用來描述相關的設定
  2. 在運行的 Pod 的檔案內,透過 Annotation 的方式將該 Pod 與對應的 seccomp 設定檔連結來達到限制的功用。 詳細的操作可以參考Kubernetes Pod-Security-Policy

除了上述提到跟 Linux Kernel 提供的安全機制外,Kubernetes v1.11 本身也提供了一個新的機制 Pod Securirt Policy(Beta).

該機制提供了一種Cluster-Wide的安全性設定,能夠將相關的安全性設定設定成一份全域的設定檔案,透過 RBAC 以及 ServiceAccount 的綁定,就可以讓所有的 Pod 自動套用這些安全性設定,減少了每個單獨設定的繁瑣程序。 相像的文件可以參考Kubernetes Pod-Security-Policy

Statically Analyse YAML

相對於前面提到這種RunTime時期的安全性檢查,這邊要介紹的則是一種相輔相成的概念,Statiscally Analyse YAML,針對部署服務的 Yaml 設定檔案去分析,評估該 Yaml 內的設定是否足夠安全。

作者認為敏感機密的資訊不應該被放在任何的 Yaml 內容之中,若採用 ConfigMap 以及 Secret 等方式來存放任何機密資訊的話,也必須要將這些資料透過一些工具來進行加密,譬如 Valut(with CoreOS's operator, git-crypt, sealed secrets 以及 cloud procider KMS

此外,這邊也提出了另外一套我覺得滿有趣的工具 kubectl-kubesec 該工具是一個 kubelet 的 plugin, 能夠針對部署資源的 Yaml 檔案將行掃描,根據安全性相關的設定給予評分,讓管理者能夠知道盡可能地將安全性相關的權限給濃縮到最小,減少被惡意攻擊時能夠造成的傷害。 舉例來說針對 kubernetes-dashboard 的部署,得到下列結果。

kubernetes-dashboard kubesec.io score 7
-----------------
Advise
1. containers[] .securityContext .runAsNonRoot == true
Force the running image to run as a non-root user to ensure least privilege
2. containers[] .securityContext .capabilities .drop
Reducing kernel capabilities available to a container limits its attack surface
3. containers[] .securityContext .readOnlyRootFilesystem == true
An immutable root filesystem can prevent malicious binaries being added to PATH and increase attack cost
4. containers[] .securityContext .runAsUser > 10000
Run as a high-UID user to avoid conflicts with the host's user table
5. containers[] .securityContext .capabilities .drop | index("ALL")
Drop all capabilities and add only those required to reduce syscall attack surface

其建議大概如下

  1. 不要用 Root去運行,應該修改 Container 改成 non-root 就可以執行
  2. Linux Capabilityes 應該要移除掉沒有用到的部分
  3. 針對 / Root File System 應該只能夠唯獨就好
  4. 執行 User 的 UID 應該要超過 10000,可以避免該 UID 與本機端的 Host 衝突(主要是因為 User Namespace 還沒有完全實作於 k8s container 中)

Run Containers as a Non-Root User

作者說道,大部分使用 Root 去運行的 Container 應用程式其實根本都不需要 Root 這麼大的權限,以 Root 身份去執行擁有的權限已經超過的該應用程式真正需要的權限,這樣的設定會讓任何惡意的攻擊者在成功突入後有更大的權限去對系統造成更大的傷害。

首先,目前的 Linux 在針對檔案權限的部分預設還是採用 自主式存取控制 (Discretionary Access Control, DAC) 這種依據檔案的 User/Group UID/GID 對應的方式來判別是否有權限進行讀寫。

同時,在 Kubernetes 的環境下,創建各式各樣的 Container 的同時並沒有啟動User namespace 的隔離,這意味者容器內使用者的ID (UID) 實際上是可以對應到外面節點上的使用者資料集中。

以實際的案例來說明上述的情況就是若以 Root 的身份去運行一個容器於 kubernetes 集群之中,在對應的工作節點上可以觀察到實際上也是用一個Root的身份去運行該容器的應用程式。 以下面的範例來說,我們在 kubernetes 節點上可以觀察到 coredns 這個 pod 實際上是用 root 身份在節點上去運行對應的應用程式。

$ps axuw |grep dns
vagrant 4656 0.0 0.0 12916 936 pts/3 S+ 07:38 0:00 grep --color=auto dns
root 7886 0.2 0.5 45224 21924 ? Ssl Jul27 5:56 /coredns -conf /etc/coredns/Corefile
root 8011 0.2 0.5 45224 22180 ? Ssl Jul27 6:04 /coredns -conf /etc/coredns/Corefile

雖然目前已經有很多的技術被提出來避免 Container 內的應用程式可以突破限制進而影響外部宿主機上面的資源,但是作者認為能夠避免使用 Root 去執行還是避免使用,畢竟安全性這種東西沒有完美的一天。

此外,大部分的容器應用程式都會使用 Root 的身份執行第一個應用程式,也就是 PID=1 的應用程序。如果該應用程式被攻擊者掌控了也就意味者攻擊者獲得了 Root的權限,因此攻擊者就能夠執行更進一步的操作。

文中特別提及一篇文章 Running Non-Root Containers On Openshift, 說到 Bitnami 將他們用到的 Container 從 Root 轉移到 Non-Root 的過程以及方法,並且說明為什麼要轉移到 Non-Root 的執行環境。

最後,作者在此提到了 kubernetes v1.11 的新功能 (PodSecuurityPolicy), 使用了一個簡單的 Yaml 來描述如何限制所有的 Pod 都必須要以 Non-Root 的身份來執行

# Required to prevent escalations to root.
allowPrivilegeEscalation: false
runAsUser:
# Require the container to run without root privileges.
rule: 'MustRunAsNonRoot'

最後作者提到,到 User Namespace 隔離的功能完成之前,使用 Non-Root 身份去運行容器依然是一個必要且不可以避免的選擇,為了整個集群以及容器的安全性,還是需要多花時間進行轉換。

Use Network Policies

講完了跟容器部署的安全性之後,接下來要探討的是Pod之間互相存取的安全性問題 在預設的情況下,kubernetes 集群內的Pod彼此都可以互相透過網路存取,即使是不同 namespace 的容易也可以。

如果想要限制Pod間的網路存取,這邊可以引入 kubernetes 提供的功能 Network Policy, 透過 Network Policy, 管理者可以限制Pod間的存取規則,除了單純的 Pod 之外,還可以限制連接埠,Namespace 以及相關的網路協定 (TCP/UDP).

但是因為 Network Policy 實際上並不是 kubernetes 自行實作的內容,這部分要仰賴 kubernetes 集群使用的 CNI(Container Network Interface). 如果想要嚐鮮的人可以考慮使用看看 Calico, Weave 等有支援 Network PolicyCNI

就如同之前所提供的概念一樣,針對安全性的部分都是盡可能的縮小權限,非必要的就不要開啟。 套用在 Network Policy 這邊則是非必要的連線都不允許通過,所以一開始可以先講所有的連線全部都阻擋起來,再根據需要的連線一條一條的規則打開。 這樣的做法聽起來有效,然而卻非常的複雜與繁瑣,因此這邊又介紹另外一個有趣的工具 netassert,一套基於 nmap的工具,可以根據設定去測試當前的封包走向是否運作,藉此減少撰寫 NetworkPolicy 時要不停的打流量來測試當前的 Network Policy是否如設定般運作。

Scan Images and Run IDS

最後一個要探討的安全部分則是針對 Image 本身進行安全性的探討。 在 kubernetes 的流程中,當 Pod 本身的 yaml 送進到 kubernetes 集群中後,會受到 Admission Controllers 一系列的檢查,去確保這次資源的部屬是合法的。 這中間的檢查,有一個非常有趣的就是 webhook. 透過 webhook, 我們可以透過第三方的應用程式去檢查當前的 Image 安全與否,若不安全就阻擋下來,讓該 Pod 沒有機會被部署到 kubernetes 集群內。

關於 webhook 的使用方法,可以參考官方文件的說明 kuubernetes webhook.

至於檢查 Image 這個行為,我們暫時稱作 IDS(Intrusion Detection System), 這邊作者提供了兩套相關的工具來提供這類型的服務,分別是 Clair 以及 Micro Scanner.

感謝網友SCLin指正,上述的說法有誤,Clair 以及 Micro Scanner 這兩個服務都是包在 Image 階段執行 Vulnerability assessment 以及 static code analysis 的工具。

而真正符合 IDS(Intrustion Detection System) 的工具則是Sysdig's Falco這個 Project. 該 Project 的介紹就是 Behavioral Activity Monitoring, 該 IDS 工具能夠偵測下列的行為

  • A shell is run inside a container
  • A container is running in privileged mode, or is mounting a sensitive path like /proc from the host.
  • A server process spawns a child process of an unexpected type
  • Unexpected read of a sensitive file (like /etc/shadow)
  • A non-device file is written to /dev
  • A standard system binary (like ls) makes an outbound network connection

除了上述工具外,作者也提到另外一個服務的概念, grafeas, 該服務會維護一個資料庫,該資料庫內會以每個 Image 的 Hash tag 作為索引資料,來記住對應的 Image 是否有任何安全性的問題。 所以可以藉由此工具來掃描當前所屬的 Image 是否內部繼承了任何有問題的 Image 來判斷是否有任何安全性問題。

至於 Zero Day 這種類型的安全性問題也有相關的工具可以幫忙處理,如twistlock,aquasec 以及 Sysdig Secure

The Future

最後,在學習了 Control-Plane 以及 Workload 這種兩類型相關的安全性防護後,作者認為接下來的趨勢則是 Service Mesh.

Service Mesh 簡單來說就是在不修改所有已經存在的微服務容器前提下,透過一些第三方的應用程式讓你在不同容器中間建立一條加密的連線,將這些服務串起來。

此外這些容器的連線中也有所謂的 Zero Trust連線,藉由 TLS 相關的加密處理安全性問題,減少了需要 Network Policy 額外的封包處理。

基本上現在最火紅的 Service Mesh 服務就是 Istio,這部分的概念與用法非常龐大,短短一篇文章沒有辦法訴說完畢,之後若有時間可以再好好的寫一篇文章來介紹 Istio 的概念與用法

Summary

總算將這一篇安全性相關的文章給唸完了,裡面的內容其實很短,但是這 11 個重點其實每一個都可以獨立寫成一篇文章來仔細探討。

作者將整個安全性分成三個部分,總共 11 點來探討,這 11 點除了介紹概念之外,也都提供了不少相關 kubernetes 的用法或是額外的工具來協助各式各樣的安全性問題。

最後,我認為安全性真的很重要,如果今天你要提供一個kubernetes平台供外部使用者使用的話,在最後進行到 Production 前一定要花時間注意當前集群內的安全性問題,存 Control-Plane 先確保基礎架設的安全,接下來從 Workloads 方面去限制相關資源部署的權限問題,使用最小的權限來執行所需要的應用程式,降低問題發生時的傷害。