自從Kubernetes專案開天闢地以來, 其安全性已經取得了長足的發展, 但它目前依然還有些要點值得註意。 本文列舉了11條軍規來幫助讓你的叢集在穩定執行時加固安全,以及在受到危害時對抗衝擊。這些軍規涵蓋了對控制面板的配置, 對工作負載的防護, 乃至對未來的展望。
作為Kubernetes的核心大腦, 控制面板全面展示了在叢集中所有容器和Pod的執行情況。 它可以直接排程新的Pod(可能包含對宿主節點有root許可權的容器), 還可以用來讀取叢集中所有的secret。 控制面板中展示的內容非常重要, 需要好好的保護起來以免發生意外洩漏和惡意訪問——無論這些內容是被訪問時, 在儲存狀態, 又或者是在網路中傳輸時。
應該在所有元件上都使用TLS, 以防止網路嗅探, 驗證伺服器端的身份,以及在啟動雙向TLS認證時驗證客戶端的身份。
需要註意的是, 一些元件在安裝時可能會有預設設定啟用http埠。管理員需要熟悉每個元件的設定從而識別出這種潛在會產生不安全流量的情況。
這份來自Lucas Käldström[1]的網路圖展示了幾處應該配置TLS的地方, 包括元件與主節點的互動, 以及Kubelet與API Server的互動。 Kelsey Hightower的大作Kubernetes The Hard Way[2]中對此提供了詳細的手工操作指導。 etcd的安全模型檔案[3]裡也有細緻描述。
自動伸縮Kubernetes的節點在過去是非常困難的操作, 因為每個節點都需要一個TLS的金鑰來連線主節點, 而將金鑰打包放進基礎映象又不是一個好的實踐方式。 現在Kubelet的TLS引導程式提供了讓新的kubelet在啟動時生成證書簽名申請的能力。
2. 以最小特權原則設定RBAC, 關閉ABAC, 以及監控日誌
RBAC提供了細粒度的規則管理功能, 以限制使用者對資源(比如namespace)的訪問。
ABAC於Kubernetes的1.6版開始為RBAC所取代,已不建議啟用。 在普通的Kubernetes叢集中透過設定以下啟動引數在API Server中啟用RBAC:
--authorization-mode=RBAC
--no-enable-legacy-authorization
網上有很多不錯的例子和檔案描述瞭如何在叢集中使用RBAC策略[4]。 除此之外, 管理員還可以透過audit2rbac[5]生成的審計日誌來分析調優RBAC規則。
如果將RBAC規則設定得過大或者不正確, 一旦叢集中出現有問題的Pod會對整個叢集產生安全威脅。 RBAC規則應該基於最小特權原則進行維護, 並持續地加之以審閱和改善。 這應被團隊視為技術負債的清除手段被整合進開發流程之中。
審計日誌功能(1.10中為beta階段)提供了可定製化地對叢集各元件的訪問流量內容(例如請求和響應)及元資料(例如發起使用者和時間戳)記錄日誌的功能。 日誌等級可以根據組織內的安全策略進行調整。 在GKE上也有相應的設定來配置這個功能。
對於讀取類請求(比如get, list和watch) ,只有請求內容會被記錄在審計日誌中, 響應內容不會儲存。 對於涉及到敏感資料(比如Configmap和Secret)的請求, 只有元資料會被記錄。 對於其他型別的請求, 請求和相應物件都會被記錄。
切記:如果將審計日誌保留在叢集內部, 在叢集已被入侵時會有安全威脅。 像這樣的所有安全相關的日誌都應轉移到叢集之外, 以避免出現安全漏洞時被篡改。
在一個組織內部透過集中化的手段管理認證和授權(又被稱為單點登入)有助於管理使用者的新增, 刪除以及一致的許可權控制。
透過將Kubernetes與第三方的身份驗證服務(比如Google和GitHub)整合,這樣可以使用第三方平臺的身份保證機制(包括雙因子驗證等機制), 以避免管理員不得不重新配置API Server來新增或刪除使用者。
Dex[6]是個OpenID Connect(OIDC)和OAuth2.0的身份服務元件。 它帶有可插拔的聯結器。 Pusher公司透過這個機制將Dex作為認證鏈的中介軟體將Kubernetes與其他第三方認證服務關聯。 除此之外還有其他工具可以達成類似目的。
etcd中儲存了叢集的狀態和secret, 對Kubernetes而言是至關重要的元件。 它的防護措施應該與叢集的其他部分割槽別處理。
對API Server的etcd擁有寫許可權相當於獲取了整個叢集的root許可權。 甚至僅透過對etcd的讀取操作都可以相當簡單地自行提權。
Kubernetes的排程器會搜尋etcd找出那些已定義但還沒被排程到節點上的Pod, 然後把這樣的Pod傳送到空閑的Kubelet節點進行部署。 在Pod資訊被寫進etcd之前, API Server會檢查提交的Pod定義。 因此一些有惡意使用者會直接把Pod定義寫入etcd, 以跳過許多諸如PodSecurityPolicy這樣的安全機制。
無論是etcd叢集的節點與節點, 還是節點與API Server, 他們的通訊都應該配置TLS證書, 並且etcd叢集應部署在專屬的節點之上。 etcd叢集與Kubernetes叢集間也應該設定防火牆, 以避免由於私鑰被盜而被從工作節點上發起攻擊。
定期替換金鑰和證書是一條安全方面的最佳實踐。 這樣能減小當金鑰被盜時所遭受的損害範圍。
Kubernetes會在某些現有證書過期時建立新的證書簽名申請來自動替換Kubelet的客戶端和伺服器端證書。
但是API Server用來加密etcd資料的對稱金鑰是無法自動替換的。 它只能手工替換。 這樣的操作需要有對主節點操作的許可權, 因此託管Kubernetes服務(比如谷歌的GKE和微軟的AKS)會自行解決這個問題而無需管理員操心。
透過對控制面板實施最小化可用安全策略,可以使叢集工作得更安全。 但這樣還不夠。 打個比方, 對於一艘裝運了危險貨物的船,船上的集裝箱也必須加以防護,這樣在出現意外事故或破壞時集裝箱還能承載貨物。對於Kubernetes的工作負載物件(Pod, Deployment,Job和Set等)也是一樣。 它們在部署時還是可信的,然而當面對外網流量時總還是會有被攻破的風險。 要減輕此類風險,除了針對工作負載物件實施最小特權原則外,還需要加固它們執行時的配置。
6. 使用Linux的安全功能以及Kubernetes的Pod安全規則
Linux核心提供了許多互相有功能重疊的安全擴充套件(capabilities, SELinux, AppArmor, seccomp-bpf)。 他們可被配置用來為應用提供最小特權。
像bane[7]這樣的工具可以用來生成AppArmor的配置檔案和seccomp的docker-slim配置檔案。 然而使用者需要詳細測試自己應用的所有路徑, 驗證這些配置對應用是否會造成副作用。
而Pod安全規則可以用來授權使用Kubernetes的安全擴充套件和其他安全指令。 這些規則描述了一個Pod提交到API Server後所必須遵守的最小安全契約。 這些契約包括了安全配置,提權標誌,以及共享主機網路,行程或行程間通訊的名稱空間。
這些安全規則相當重要, 因為他們有助於防止容器內的行程超越其隔離邊界。 Tim Allclair提供的PodSecurityPolicy範例[8]相當詳盡。 你可以自定義這個範例, 將其用在自己的使用場景裡。
在使用Pod安全規則來控制Pod訪問API Server的同時, 也可以在開發工作流中使用靜態檔案分析來構築組織的合規需求或滿足風險偏好。
在Pod型別(比如Deployment, Pod, Set等)的YAML檔案中不應該存放敏感資料, 而包含敏感資料的Configmap和Secret應該由諸如vault(透過CoreOS提供的operator), git-crypt, sealed secret或者雲廠商提供的金鑰管理服務來進行加密處理。
靜態分析YAML配置可以對執行時的安全情況構建一條基準線。 以下是kubesec工具為某個資源生成風險評分的例子:
{
"score": -30,
"scoring": {
"critical": [{
"selector": "containers[] .securityContext .privileged == true",
"reason": "Privileged containers can allow almost completely unrestricted host access"
}],
"advise": [{
"selector": "containers[] .securityContext .runAsNonRoot == true",
"reason": "Force the running image to run as a non-root user to ensure least privilege"
}, {
"selector": "containers[] .securityContext .capabilities .drop",
"reason": "Reducing kernel capabilities available to a container limits its attack surface",
"href": "https://kubernetes.io/docs/tasks/configure-pod-container/security-context/"
}]
}
}
而kubetest工具是個針對Kubernetes的YAML檔案的單元測試框架。 程式碼例子如下:
#// vim: set ft=python:
def test_for_team_label():
if spec["kind"] == "Deployment":
labels = spec["spec"]["template"]["metadata"]["labels"]
assert_contains(labels, "team", "should indicate which team owns the deployment")
test_for_team_label()
以上這些工具都透過將檢查和驗證工作在軟體開發週期中提前(shift left])到更早的開發階段的方式, 使開發人員能更早地獲得對於程式碼和配置的反饋, 以避擴音交在之後的人工或自動檢查中被退回。這樣也可以減少引入更多安全實踐的障礙。
執行在root使用者下的容器大多擁有了大大超過其所承載的服務需要的許可權。 當叢集被侵入時,這樣的容器會讓攻擊者有能力執行更進一步的破壞。
容器技術依然基於傳統的Unix安全模型, 叫做自主訪問控制(簡稱DAC)。在這個模型之下,所有東西都是檔案, 而許可權是可以被賦予使用者或使用者組。
使用者名稱空間在Kubernetes下並沒有啟用, 這意味著容器中的使用者表會被對映到宿主機的使用者表中, 而在容器裡由root身份執行的行程相當於在宿主機上也是以root身份執行。 儘管說我們有層級安全機制來防範容器發生問題, 但在容器中用root身份執行行程依然是不推薦的做法。
許多容器映象使用root使用者執行一號行程。 如果這個行程被攻破, 那麼攻擊者在容器中就有了root的許可權, 從而會輕易放大由於叢集誤配置造成的安全漏洞。
Bitnami公司在將容器映象遷移到非root使用者這方面做了很多工作[9](主要由於OpenShift平臺預設要求非root身份執行容器)。參考他們提供的檔案可以降低管理員實施類似遷移的難度。
下列Pod安全規則的程式碼片段給出了防止容器中的行程以root身份執行, 以及防止行程提權到root身份的方法:
allowPrivilegeEscalation: false
runAsUser:
rule: 'MustRunAsNonRoot'
非root的容器無法系結小於1024的埠(可以透過配置核心引數CAP_NET_BIND_SERVICE來啟用), 但使用service的特性可以使對外埠配置為1024以下。 在以下例子中,MyApp這個應用在容器中系結了8443埠,而service將相關流量代理到443這個埠中對外暴露應用:
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 443
targetPort: 8443
在使用者名稱空間在Kubernetes中可用之前,或者非root功能在容器執行時元件中獲得直接支援之前, 使用非root使用者執行容器依然是推薦的做法。
預設狀態下, Kubernetes中Pod與Pod之間的網路是互通的。 可以透過使用網路規則來對此加以限制。
傳統的服務一般給每個應用配置靜態IP和埠範圍。 這樣的靜態IP一般很少會改變,從而會被當作服務的一種標識來對待。 因此傳統服務可以透過防火牆來加以限制和保護。 容器為了能快速失敗快速重新排程,一般很少使用固定IP,而是透過使用服務發現的機制來替代。 容器這樣的特性使得防火牆會更難配置和審查。
由於Kubernetes將所有系統狀態都儲存在etcd裡, 只要CNI網路外掛支援網路規則, 藉助etcd中的資料就可以配置動態防火牆。 當前Calico, Cilium, kube-router, Romana和Weave Net這些外掛都能支援網路規則。
值得註意的是,這些網路規則是失效關閉的(fail-closed), 因此在下列YAML中缺少podSelector配置意味著這個規則會應用到所有容器:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector:
下列的網路規則的例子描述瞭如何關閉除了UDP 53(DNS埠)之外的所有對外流量。 這樣也防止了進入應用的連線——這是因為網路規則是有狀態面向連線的, 同一個網路連線中應用對外請求的響應也依舊會傳回到應用中。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: myapp-deny-external-egress
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Egress
egress:
- ports:
- port: 53
protocol: UDP
- to:
- namespaceSelector: {}
Kubernetes的網路規則並不能被應用到DNS名字上。 這是因為一個DNS名字會可能被輪詢排程解析到許多個IP地址,或者依據呼叫IP動態被解析到不同的標的IP地址, 而網路規則只能被應用到靜態的IP或podSelector(也就是Kubernetes的動態IP機制)上。
安全上有一條最佳實踐建議一開始對Kubernetes的namespace先拒絕所有流量, 然後再逐漸新增路由允許應用能透過測試場景。 這樣做是非常複雜的, 可以透過下列ControlPlane公司出品的netassert這個開源工具來幫助達成最佳實踐。 netassert[10]是一個安全運維工作流方面的網路安全工具, 透過高度併發運用nmap來掃描和嗅探網路:
k8s:
deployment:
test-frontend:
test-microservice: 80
test-database: -80
169.254.169.254: -80, -443
metadata.google.internal: -80, -443
new-namespace:test-microservice:
test-database.new-namespace: 80
test-frontend.default: 80
169.254.169.254: -80, -443
metadata.google.internal: -80, -443
雲廠商的元資料API一般也會是被攻擊的源頭之一(參見最近發生的Shopify受攻擊的案例[11]), 因此也需要有專門的測試來確定這一類API在容器網路中被關閉, 以避免誤配置的可能。
Web伺服器作為它所部署的網路上的一個可被攻擊的切入口,它映象上的檔案系統需要被完全掃描,以避免存在已知的漏洞被攻擊者利用來取得遠端控制容器的許可權。使用IDS可以檢測已知漏洞。
Kubernetes透過一系列的Admission Controller來控制Pod型別的資源(如Deployment等)是否能被部署進叢集。 Admission controller會檢驗每個被提交進來的Pod的定義,或者有時會修改Pod定義的內容。 現在已經支援後臺webhook掛載外部應用。
Webhook功能可以和容器映象掃描工具結合起來。 在容器被部署進叢集之前掃描映象內容。 一旦未透過掃描檢查, 容器便會被Admission Controller拒絕部署。
使用工具掃描容器映象中是否包含已有的漏洞, 這樣能減小攻擊者利用公開漏洞(CVE, Common Vulnerabilities & Exposures)的時間視窗。 像CoreOS出品的Clair和Aqua出品的Micro Scanner都應該被整合到部署管道中, 以避免部署的容器中包含致命漏洞。
像Grafeas這樣的工具可以儲存映象的元資料, 用來針對容器的特有簽名(基於內容定址的雜湊雜湊)進行持續的合規與漏洞檢查。 透過這個簽名來掃描本地容器映象等同於掃描部署在生產環境的容器, 這樣就可以持續地做本地檢查而不需要連線到生產環境中。
而未知的瞬時攻擊漏洞(Zero Day)永遠會存在。 為了應對這樣的威脅, 需要在叢集中部署一些諸如Twistlock,Aqua和Sysdig Security這樣的工具。而另外一些IDS會檢測出容器中發生的不尋常的行為, 暫停或者終止這樣的容器。 典型的工具有Sysdig出品的開源規則引擎Falco[12]。
目前看來服務網格當屬安全在“雲原生進化(cloud native evolution)”發展中的下一階段形態。 當然真正應用服務網格尚需時日。 遷移工作涉及到將應用層中含有的相關複雜邏輯轉移為依賴服務網格的基礎構件, 而企業也會很想詳細理解遷移和運用服務網格相關的最佳實踐。
服務網格是透過類似Envoy和Linkerd這樣的高效能邊車(sidecar)代理伺服器在叢集中組建應用間加密網路的基礎設施。 它提供了流量管理, 監控和規則控制的能力而無需微服務為此修改程式碼。
服務網格意味著微服務中的安全與網路管理相關的程式碼可以被去除, 而這部分責任將被轉移到經過實戰驗證的公共元件中。 之前透過Linkerd就已經可以達成這樣的功能。 如今由Google, IBM和Lyft聯合推出了Istio在服務網格領域也可以作為一個可選方案。 Istio透過基於SPIFFE規範的Pod間相互進行身份識別的能力以及其他一系列的功能, 來簡化服務網格這個下一代網路安全的部署。 在“永不信任,始終驗證”(Zero Trust)的網路模型中, 每次互動都應在互信TLS(mTLS, mutual TLS)下發生, 以保證互動雙方不僅鏈路安全,而且身份已知。 這樣的話或許就沒有必要使用傳統的防火牆或者Kubernetes的網路規則了。
對於仍懷有傳統網路思維的那些人來說,我們預計向雲原生安全原則的思維轉變不會容易。 此處推薦閱讀來自SPIFFE專案委員會的Evan Gilman撰寫的Zero Trust Networking[13]這本書來瞭解這一領域的入門知識。
Istio 0.8 LTS版本已經推出。 這個專案正快速接近1.0版本的釋出(譯者:Istio 1.0版已於2018年7月31號釋出)。 其版本編號習慣會與Kubernetes遵循的模型相同:核心版本穩定增長, 每個API都會透過自己版本中的alpha/beta來描述其穩定性。 可以期待Istio在之後幾個月的採用率會有上升。
雲原生應用擁有一系列帶有細粒度配置能力的輕量級安全元件來控制工作負載與基礎架構。 這些工具的威力和靈活性對管理員來說既是祝福又是詛咒。 如果自動化安全能力不足, 這些工具很容易會暴露不安全的網路流量,導致容器或隔離結構爆發問題。
防護工具層出不窮, 但管理員仍需警惕誤配置的潛在可能, 並儘量減少對外易受攻擊的點。
然而如果安全性要求會減緩組織交付功能的速度, 那麼安全就永遠無法成為一類需求。透過在軟體交付流程中應用持續交付的原則, 可以讓組織在不影響業務的情況下也能合規和治理標的,實現持續審計。
在有完整的測試套件的支援之下,想要在安全方面快速迭代是非常容易的。 而這樣的迭代並非是用定點執行滲透測試來運作,而是持續安全(Continuous Security)。 持續安全透過持續的管道驗證的方式來保證組織的受攻擊點清晰, 相關風險明確並能有效地被管理起來。
以下推薦一下ControlPlane公司的服務:如果您需要在組織內啟動持續安全的流程, 或者需要Kubernetes安全和運維的培訓,又或者想要把服務和開發流程遷移到安全的雲原生方式, 請聯絡我們。
-
https://docs.google.com/presentation/d/1Gp-2blk5WExI_QR59EUZdwfO2BWLJqa626mK2ej-huo/edit#slide=id.g1e639c415b_0_56
-
https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/1.9.0/docs/04-certificate-authority.md
-
https://coreos.com/etcd/docs/latest/op-guide/security.html
-
https://github.com/uruddarraju/kubernetes-rbac-policies
-
https://github.com/liggitt/audit2rbac
-
https://github.com/coreos/dex
-
https://github.com/genuinetools/bane
-
https://gist.github.com/tallclair/11981031b6bfa829bb1fb9dcb7e026b0
-
https://engineering.bitnami.com/articles/running-non-root-containers-on-openshift.html
-
https://github.com/controlplaneio/netassert
-
https://hackerone.com/reports/341876
-
https://github.com/draios/falco
-
https://amzn.to/2Gg6Pav
原文連結:https://kubernetes.io/blog/2018/07/18/11-ways-not-to-get-hacked/#1-tls-everywhere
基於Kubernetes的DevOps實踐培訓將於2018年8月24日在北京開課,3天時間帶你係統掌握Kubernetes。本次培訓包括:容器特性、映象、網路;Kubernetes架構、核心元件、基本功能;Kubernetes設計理念、架構設計、基本功能、常用物件、設計原則;Kubernetes的資料庫、執行時、網路、外掛已經落地經驗;微服務架構、元件、監控方案等,點選下方圖片檢視詳情。
長按二維碼向我轉賬
受蘋果公司新規定影響,微信 iOS 版的贊賞功能被關閉,可透過二維碼轉賬支援公眾號。
微信掃一掃
使用小程式