kubernetes高級之pod安全策略

系列目錄

什麼是pod安全策略

pod安全策略是集群級別的用於控制pod安全相關選項的一種資源.PodSecurityPolicy定義了一系列pod相要進行在系統中必須滿足的約束條件,以衣一些默認的約束值.它允許管理員控制以下方面內容

Control Aspect Field Names
以特權運行容器 privileged
使用宿主名稱空間 hostPID, hostIPC
使用宿主網絡和端口 hostNetwork, hostPorts
使用存儲卷類型 volumes
使用宿主機文件系統 allowedHostPaths
flex存儲卷白名單 allowedFlexVolumes
分配擁有 Pod 數據卷的 FSGroup fsGroup
只讀root文件系統 readOnlyRootFilesystem
容器的用戶id和組id runAsUser, runAsGroup, supplementalGroups
禁止提升到root權限 allowPrivilegeEscalation, defaultAllowPrivilegeEscalation
Linux能力 defaultAddCapabilities, requiredDropCapabilities, allowedCapabilities
SELinux上下文 seLinux
允許容器加載的proc類型 allowedProcMountTypes
The AppArmor profile used by containers annotations
The seccomp profile used by containers annotations
The sysctl profile used by containers annotations

啟用pod安全策略

pod安全策略作為可選的(但強烈建議的)admission controller的實現.pod安全策略通過啟用admission controller來實現,但是僅僅啟用而沒有對策略授權則會導致整個集群無法創建pod!

由於pod安全策略api(policy/v1beta1/podsecuritypolicy)獨立於admission controller之外啟用,對於已經存在的集群建議在啟用admission controller之前添加並授權策略.

授權策略

當一個pod安全策略資源被創建(前面說過,psp(PodSecurityPolicy )pod安全策略是一種kubernetes資源),它什麼都不會做.為了使用它,請求操作的用戶或者目標pod的serviceaccount必須通過策略的use動詞來授權.

絕大部分kubernetes pod並不是直接由用戶直接創建的.相反,典型使用場景是它們通過Deployment或者ReplicaSet間接被創建,或者通過控制器管理器的其它模板控制器來創建.對控制器進行策略授權也將對它所創建的所有pod進行策略授權.因此首選的授權方法是對pod的serviceaccount進行策略授權(後面有示例).

通過RBAC授權

RBAC是kubernetes標準的授權模式,並且很容易用於授權安全策略使用.

首先,一個角色(role)或者集群角色(clusterRole)需要被授權使用(use動詞)它想要的策略.對角色的授權類似下面

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: <role name>
rules:
- apiGroups: ['policy']
  resources: ['podsecuritypolicies']
  verbs:     ['use']
  resourceNames:
  - 一系列要進行授權的資源名稱

然後把集群角色(或角色)與授權的用戶綁定

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: 綁定名稱
roleRef:
  kind: ClusterRole
  name: 角色名稱
  apiGroup: rbac.authorization.k8s.io
subjects:
# Authorize specific service accounts:
- kind: ServiceAccount
  name: 授權的serviceaccount名稱
  namespace: <authorized pod namespace>
# Authorize specific users (not recommended):
- kind: User
  apiGroup: rbac.authorization.k8s.io
  name: 授權的用戶名

如果一個角色綁定(不是集群角色綁定)被使用,它僅對和它處於同一名稱空間下的pod才能進行有效策略授權,這樣同樣適用於用戶和用戶組

# Authorize all service accounts in a namespace:
- kind: Group
  apiGroup: rbac.authorization.k8s.io
  name: system:serviceaccounts
# Or equivalently, all authenticated users in a namespace:
- kind: Group
  apiGroup: rbac.authorization.k8s.io
  name: system:authenticated

故障排除

控制器管理器必須運行在安全的api端口上,並且不能有超級權限.不然請求就會繞過認證和授權模塊,將導致所有的策略均被允許,並且用戶可以創建特權pod

策略順序

除了限制pod的創建和更新,pod安全策略還用於提供它所控制的諸多字段的默認值.當有多個策略時,pod安全策略根據以下因素來選擇策略

  • 任何成功通過驗證沒有警告的策略將被使用

  • 如果是請求創建pod,則按通過驗證的策略按字母表順序被選用

  • 否則,如果是一個更新請求,將會返回錯誤.因為在更新操作過程中不允許pod變化

示例

以下示例假定你運行的集群開啟了pod安全策略admission controller並且你有集群管理員權限

初始設置

我們為示例創建一個名稱空間和一個serviceaccount.我們使用這個serviceaccount來模擬一個非管理員用戶

kubectl create namespace psp-example
kubectl create serviceaccount -n psp-example fake-user
kubectl create rolebinding -n psp-example fake-editor --clusterrole=edit --serviceaccount=psp-example:fake-user

為了方便辨認我們使用的賬戶,我們創建兩個別名

alias kubectl-admin='kubectl -n psp-example'
alias kubectl-user='kubectl --as=system:serviceaccount:psp-example:fake-user -n psp-example'

創建一個策略和一個pod

以下定義文件定義了一個簡單pod安全策略(PodSecurityPolicy),這個策略僅僅阻止創建特權pod

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: example
spec:
  privileged: false  # Don't allow privileged pods!
  # The rest fills in some required fields.
  seLinux:
    rule: RunAsAny
  supplementalGroups:
    rule: RunAsAny
  runAsUser:
    rule: RunAsAny
  fsGroup:
    rule: RunAsAny
  volumes:
  - '*'

我們使用kubectl命令來應用以上文件.

現在,做為一個非特權用戶,我們創建一個簡單pod

kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
  name:      pause
spec:
  containers:
    - name:  pause
      image: k8s.gcr.io/pause
EOF
Error from server (Forbidden): error when creating "STDIN": pods "pause" is forbidden: unable to validate against any pod security policy: []

發生了什麼?儘管pod安全策略已創建,不管是pod的serviceaccount還是fack-user都沒有權限使用這個策略.

kubectl-user auth can-i use podsecuritypolicy/example
no

創建一個rolebing來授權fake-user來使用example策略(example是前面創建的策略的名稱)

但是請注意這裏並不是首選方式!後面的示例將介紹首選的方式

kubectl-admin create role psp:unprivileged \
    --verb=use \
    --resource=podsecuritypolicy \
    --resource-name=example
role "psp:unprivileged" created

kubectl-admin create rolebinding fake-user:psp:unprivileged \
    --role=psp:unprivileged \
    --serviceaccount=psp-example:fake-user
rolebinding "fake-user:psp:unprivileged" created

kubectl-user auth can-i use podsecuritypolicy/example
yes

此時,再重新嘗試創建pod

kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
  name:      pause
spec:
  containers:
    - name:  pause
      image: k8s.gcr.io/pause
EOF
pod "pause" created

這次正如我們期待的一樣,可以正常工作.但是試圖創建特權pod仍然會被阻止(因此策略本身阻止創建特權pod)

kubectl-user create -f- <<EOF
apiVersion: v1
kind: Pod
metadata:
  name:      privileged
spec:
  containers:
    - name:  pause
      image: k8s.gcr.io/pause
      securityContext:
        privileged: true
EOF
Error from server (Forbidden): error when creating "STDIN": pods "privileged" is forbidden: unable to validate against any pod security policy: [spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]

再運行一個其它pod

我們再嘗試創建一個pod,這次有一點不同

ubectl-user run pause --image=k8s.gcr.io/pause
deployment "pause" created

kubectl-user get pods
No resources found.

kubectl-user get events | head -n 2
LASTSEEN   FIRSTSEEN   COUNT     NAME              KIND         SUBOBJECT                TYPE      REASON                  SOURCE                                  MESSAGE
1m         2m          15        pause-7774d79b5   ReplicaSet                            Warning   FailedCreate            replicaset-controller                   Error creating: pods "pause-7774d79b5-" is forbidden: no providers available to validate pod request

從以上可以看到deployment已經成功創建(kubectl run 實際上會創建一個deployment).但是使用kubectl get pod命令卻沒有發現pod被創建.這是為什麼?問題的答案隱藏在replicaset控制器里.Fake-user成功創建的deployment(deployment又成功創建replicaset),但是當replicaset嘗試創建pod的時候,它並沒有被授權使用example定義的策略.

為了解決這個問題,需要把psp:unprivileged角色(前面創建的)綁定到pod的serviceaccount上(前面我們是綁定在了fake-user上).這裏serviceaccount是default(因為我們沒有指定其它用戶)

看到這裏如果你仍然覺得難以理解,可以回頭再看看,還是無法理解的話則需要補充關於角色,用戶和RBAC相關的知識.

kubectl-admin create rolebinding default:psp:unprivileged \
    --role=psp:unprivileged \
    --serviceaccount=psp-example:default
rolebinding "default:psp:unprivileged" created

這時候等待若干分鐘,replicaset的控制器最終會成功創建pod

kubectl-user get pods --watch
NAME                    READY     STATUS    RESTARTS   AGE
pause-7774d79b5-qrgcb   0/1       Pending   0         1s
pause-7774d79b5-qrgcb   0/1       Pending   0         1s
pause-7774d79b5-qrgcb   0/1       ContainerCreating   0         1s
pause-7774d79b5-qrgcb   1/1       Running   0         2s

清理工作

刪除名稱空間以刪除絕大部分示例中用到的資源

kubectl-admin delete ns psp-example
namespace "psp-example" deleted

注意現在剛剛創建的pod安全策略已經沒有了名稱空間,並且需要單獨被清除

kubectl-admin delete psp example
podsecuritypolicy "example" deleted

策略示例

以下是一個最小限制的策略,和不使用pod安生策略admission controller效果一樣

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: privileged
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
  privileged: true
  allowPrivilegeEscalation: true
  allowedCapabilities:
  - '*'
  volumes:
  - '*'
  hostNetwork: true
  hostPorts:
  - min: 0
    max: 65535
  hostIPC: true
  hostPID: true
  runAsUser:
    rule: 'RunAsAny'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'RunAsAny'
  fsGroup:
    rule: 'RunAsAny'

以下的一個示例有限制性策略,需要用戶是一個非特權用戶,阻止pod的權限提升

之所以要求是非特權用戶,因為特權用戶將會繞過限制

apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
  annotations:
    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'
    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
    seccomp.security.alpha.kubernetes.io/defaultProfileName:  'docker/default'
    apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
spec:
  privileged: false
  # Required to prevent escalations to root.
  allowPrivilegeEscalation: false
  # This is redundant with non-root + disallow privilege escalation,
  # but we can provide it for defense in depth.
  requiredDropCapabilities:
    - ALL
  # Allow core volume types.
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    # Assume that persistentVolumes set up by the cluster admin are safe to use.
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    # Require the container to run without root privileges.
    rule: 'MustRunAsNonRoot'
  seLinux:
    # This policy assumes the nodes are using AppArmor rather than SELinux.
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  fsGroup:
    rule: 'MustRunAs'
    ranges:
      # Forbid adding the root group.
      - min: 1
        max: 65535
  readOnlyRootFilesystem: false

策略參考

特權的

它決定了pod中的所有容器是否被允許以特權方式運行.默認情況下容器不允許訪問主機的設備,但是特權容器卻被允許訪問.這將允許容器有幾乎和它所在的進程一樣的訪問主機的權利.這將非常有用當容器想要使用主機的功能,比如訪問網絡的設備.

Host名稱空間

HostPID – 控制容器是否可以共享主機的進程id名稱空間

HostIPC – 控制容器是否可以共享主機的IPC名稱空間

HostNetwork – 控制容器是否可以使用所在節點的網絡名稱空間.這將允許pod訪問迴環設備,監聽localhost,並且可以窺探同一節點上其它pod的網絡活動狀況

AllowedHostPaths – 控制允許訪問的宿主機路徑

存儲卷和文件系統

Volumes – 提供了一系列的存儲卷類型白名單.這些允許的值和創建存儲卷時定義的資源類型相對應.想要獲取所有存儲卷類型,可以查看存儲卷類型列表.此外,*可以被用來允許所有的存儲卷類型

以下是推薦的最小化的允許存儲卷類型的安全策略配置

  • configMap
  • downwardAPI
  • emptyDir
  • persistentVolumeClaim
  • secret
  • projected

AllowedHostPaths– 它定義了一個hostPath類型的存儲卷可用的宿主機路徑的白名單.空集群意味着對宿主機的path無使用限制.它被定義為一個包含了一系列對象的單個pathPrefix字段,允許hostpath類型的存儲卷掛載以pathPrefix字段開頭的宿主機路徑.readonly字段意味着必須以readonly方式掛載(即不能寫入,只能讀)

allowedHostPaths:
  # This allows "/foo", "/foo/", "/foo/bar" etc., but
  # disallows "/fool", "/etc/foo" etc.
  # "/foo/../" is never valid.
  - pathPrefix: "/foo"
    readOnly: true # only allow read-only mounts

警告,一個可以無限制訪問宿主機文件系統的容器可以有很多方式提升權限,包括讀取其它容器內的數據,濫用系統服務的密鑰,比如kubecctl

可寫的hostpath目錄存儲卷允許容器寫入到宿主機文件系統,並且可以遍歷pathPrefix以外的文件系統,readOnly: true在kubernetes 1.11+版本以後才能使用,並且在allowedHostPaths必須使用以有效限制訪問特定的pathPrefix

ReadOnlyRootFilesystem – 限制容器必須以只讀的root文件系統運行(沒有可寫層)

特權提升

這個選項控制着容器的allowPrivilegeEscalation選項.這個布爾值直接控制着no_new_privs是否設置到容器運行的進程.它將阻止setuid來改變user ID,並且阻止文件有其它的能力(比如禁止使用ping工具).這個行為需要啟用MustRunAsNonRoot

AllowPrivilegeEscalation– 它決定着容器的安全上下文是否可以設置allowPrivilegeEscalation=true,為true是默認值.設置為false將使得容器所有的子進程沒有比父進程更高的特權

DefaultAllowPrivilegeEscalation,為allowPrivilegeEscalation設置默認值,從上面可以看到,默認的值為true.如果這個行為不是我們期待的,這個字段可以用於把它設置為不允許,但是仍然pod顯式請求allowPrivilegeEscalation

【精選推薦文章】

自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

台北網頁設計公司這麼多,該如何挑選?? 網頁設計報價省錢懶人包"嚨底家"