歡迎光臨
每天分享高質量文章

Kubernetes管理員的11條安全軍規

自從Kubernetes專案開天闢地以來, 其安全性已經取得了長足的發展, 但它目前依然還有些要點值得註意。 本文列舉了11條軍規來幫助讓你的集群在穩定運行時加固安全,以及在受到危害時對抗衝擊。這些軍規涵蓋了對控制面板的配置, 對工作負載的防護, 乃至對未來的展望。
第一部分:控制面板

作為Kubernetes的核心大腦, 控制面板全面展示了在集群中所有容器和Pod的運行情況。 它可以直接調度新的Pod(可能包含對宿主節點有root權限的容器), 還可以用來讀取集群中所有的secret。 控制面板中展示的內容非常重要, 需要好好的保護起來以免發生意外泄漏和惡意訪問——無論這些內容是被訪問時, 在儲存狀態, 又或者是在網絡中傳輸時。
1. 全面啟用TLS
應該在所有組件上都使用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

而在GKE中通過另一個引數來禁用ABAC:
--no-enable-legacy-authorization

網上有很多不錯的例子和文件描述瞭如何在集群中使用RBAC策略[4]。 除此之外, 管理員還可以通過audit2rbac[5]生成的審計日誌來分析調優RBAC規則。
如果將RBAC規則設置得過大或者不正確, 一旦集群中出現有問題的Pod會對整個集群產生安全威脅。 RBAC規則應該基於最小特權原則進行維護, 並持續地加之以審閱和改善。 這應被團隊視為技術負債的清除手段被整合進開發流程之中。
審計日誌功能(1.10中為beta階段)提供了可定製化地對集群各組件的訪問流量內容(例如請求和響應)及元資料(例如發起用戶和時間戳)記錄日誌的功能。 日誌等級可以根據組織內的安全策略進行調整。 在GKE上也有相應的設置來配置這個功能。
對於讀取類請求(比如get, list和watch) ,只有請求內容會被記錄在審計日誌中, 響應內容不會儲存。 對於涉及到敏感資料(比如Configmap和Secret)的請求, 只有元資料會被記錄。 對於其他型別的請求, 請求和相應物件都會被記錄。
切記:如果將審計日誌保留在集群內部, 在集群已被入侵時會有安全威脅。 像這樣的所有安全相關的日誌都應轉移到集群之外, 以避免出現安全漏洞時被篡改。
3. 為API Server啟用第三方認證
在一個組織內部通過集中化的手段管理認證和授權(又被稱為單點登錄)有助於管理用戶的添加, 刪除以及一致的權限控制。
通過將Kubernetes與第三方的身份驗證服務(比如Google和GitHub)整合,這樣可以使用第三方平臺的身份保證機制(包括雙因子驗證等機制), 以避免管理員不得不重新配置API Server來添加或刪除用戶。
Dex[6]是個OpenID Connect(OIDC)和OAuth2.0的身份服務組件。 它帶有可插拔的連接器。 Pusher公司通過這個機制將Dex作為認證鏈的中間件將Kubernetes與其他第三方認證服務關聯。 除此之外還有其他工具可以達成類似目的。
4. 將etcd集群隔離出來並加上防火牆控制

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集群間也應該設置防火牆, 以避免由於私鑰被盜而被從工作節點上發起攻擊。
5. 定期替換密鑰

定期替換密鑰和證書是一條安全方面的最佳實踐。 這樣能減小當密鑰被盜時所遭受的損害範圍。
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]相當詳盡。 你可以自定義這個範例, 將其用在自己的使用場景里。
7. 靜態分析YAML檔案
在使用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])到更早的開發階段的方式, 使開發人員能更早地獲得對於代碼和配置的反饋, 以避免提交在之後的人工或自動檢查中被退回。這樣也可以減少引入更多安全實踐的障礙。
8. 使用非root的用戶運行容器
運行在root用戶下的容器大多擁有了大大超過其所承載的服務需要的權限。 當集群被侵入時,這樣的容器會讓攻擊者有能力執行更進一步的破壞。
容器技術依然基於傳統的Unix安全模型, 叫做自主訪問控制(簡稱DAC)。在這個模型之下,所有東西都是檔案, 而權限是可以被賦予用戶或用戶組。
用戶命名空間在Kubernetes下並沒有啟用, 這意味著容器中的用戶表會被映射到宿主機的用戶表中, 而在容器里由root身份運行的行程相當於在宿主機上也是以root身份運行。 儘管說我們有層級安全機制來防範容器發生問題, 但在容器中用root身份運行行程依然是不推薦的做法。
許多容器鏡像使用root用戶運行一號行程。 如果這個行程被攻破, 那麼攻擊者在容器中就有了root的權限, 從而會輕易放大由於集群誤配置造成的安全漏洞。
Bitnami公司在將容器鏡像遷移到非root用戶這方面做了很多工作[9](主要由於OpenShift平臺預設要求非root身份運行容器)。參考他們提供的文件可以降低管理員實施類似遷移的難度。
下列Pod安全規則的代碼片段給出了防止容器中的行程以root身份運行, 以及防止行程提權到root身份的方法:
# Required to prevent escalations to root.
allowPrivilegeEscalation: false
runAsUser:
  # Require the container to run without root privileges.
  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用戶運行容器依然是推薦的做法。
9. 使用網絡規則
預設狀態下, 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: # used for Kubernetes pods
  deployment: # only deployments currently supported
    test-frontend: # pod name, defaults to `default` namespace
      test-microservice: 80  # `test-microservice` is the DNS name of the target service
      test-database: -80     # `test-frontend` should not be able to access test-database’s port 80
      169.254.169.254: -80, -443           # AWS metadata API
      metadata.google.internal: -80, -443  # GCP metadata API

    new-namespace:test-microservice:  # `new-namespace` is the namespace name
      test-database.new-namespace: 80 # longer DNS names can be used for other namespaces
      test-frontend.default: 80
      169.254.169.254: -80, -443           # AWS metadata API
      metadata.google.internal: -80, -443  # GCP metadata API

雲廠商的元資料API一般也會是被攻擊的源頭之一(參見最近發生的Shopify受攻擊的案例[11]), 因此也需要有專門的測試來確定這一類API在容器網絡中被關閉, 以避免誤配置的可能。
10. 掃描容器鏡像和運行IDS(入侵檢測系統)
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)”發展中的下一階段形態。 當然真正應用服務網格尚需時日。 遷移工作涉及到將應用層中含有的相關複雜邏輯轉移為依賴服務網格的基礎構件, 而企業也會很想詳細理解遷移和運用服務網格相關的最佳實踐。 
11. 使用服務網格
服務網格是通過類似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安全和運維的培訓,又或者想要把服務和開發流程遷移到安全的雲原生方式, 請聯繫我們。
相關鏈接:
  1. https://docs.google.com/presentation/d/1Gp-2blk5WExI_QR59EUZdwfO2BWLJqa626mK2ej-huo/edit#slide=id.g1e639c415b_0_56

  2. https://github.com/kelseyhightower/kubernetes-the-hard-way/blob/1.9.0/docs/04-certificate-authority.md

  3. https://coreos.com/etcd/docs/latest/op-guide/security.html

  4. https://github.com/uruddarraju/kubernetes-rbac-policies

  5. https://github.com/liggitt/audit2rbac

  6. https://github.com/coreos/dex

  7. https://github.com/genuinetools/bane

  8. https://gist.github.com/tallclair/11981031b6bfa829bb1fb9dcb7e026b0

  9. https://engineering.bitnami.com/articles/running-non-root-containers-on-openshift.html

  10. https://github.com/controlplaneio/netassert

  11. https://hackerone.com/reports/341876

  12. https://github.com/draios/falco

  13. https://amzn.to/2Gg6Pav

原文鏈接:https://kubernetes.io/blog/2018/07/18/11-ways-not-to-get-hacked/#1-tls-everywhere

基於Kubernetes的DevOps實踐培訓

基於Kubernetes的DevOps實踐培訓將於2018年8月24日在北京開課,3天時間帶你系統掌握Kubernetes本次培訓包括:容器特性、鏡像、網絡;Kubernetes架構、核心組件、基本功能;Kubernetes設計理念、架構設計、基本功能、常用物件、設計原則;Kubernetes的資料庫、運行時、網絡、插件已經落地經驗;微服務架構、組件、監控方案等,點擊下方圖片查看詳情。

赞(0)

分享創造快樂