8

如何优雅的给 Kubernetes Pod 启用安全策略-51CTO.COM

 1 year ago
source link: https://os.51cto.com/article/713068.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
589d2d743da8a948ce7789152a8cc8456a57cb.jpg

文中的演示和示例均在 v1.18.17 集群中通过验证。

Pod Security Policies

Pod Security Policies (下文简称 psp 或 pod 安全策略)是一种集群级别的全局资源,能够对 pod 的创建和更新进行细粒度的授权控制。具体来说,一个 psp 对象定义了一组安全性条件,一个 pod 的 spec 字段必须满足这些条件以及适用相关字段的默认值,其创建或更新请求才会被 apiserver 所接受。

具体的 pod 字段和安全条件可见文档 what-is-a-pod-security-policy[1] 。

启用 Pod Security Policies

Kubernetes 默认不开启 pod 安全策略功能,在集群中启用 pod 安全策略的步骤大体上分为三步:

  • 授予用户访问安全策略资源的权限,通常会授权给整个命名空间的 service account。
  • 在集群中创建指定的安全策略资源。
  • 启用 apiserver 的 admission-controller 插件。

注意步骤 1、2 的顺序不重要,因为它们不会产生实际影响。

但步骤 3 推荐在最后一步执行,否则一旦启用 admission-controller 插件,如果集群中没有可用的 pod 安全策略或者未对安全策略资源预先授权,所有 pod 的创建都会被拒绝,包括 kube-system 命名空间下的系统管理组件如 apiserver(但由于是受 kubelet 管理的静态 pod 实际上容器依然会运行)。

RBAC 身份认证

  1. 创建可访问所有安全策略资源的 ClusterRole:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
 name: all-psp
rules:
- apiGroups: ['policy']
 resources: ['podsecuritypolicies']
 verbs:     ['use']
  1. 通过 ClusterRoleBinding 将创建的角色绑定到指定命名空间下的所有 service account(也可以授权给指定的 sa 或者用户):
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
 name: cluster-psp-bind
roleRef:
 kind: ClusterRole
 name: all-psp
 apiGroup: rbac.authorization.k8s.io
subjects:
# 授权给指定命名空间下的所有 service account(推荐做法):
- kind: Group
 apiGroup: rbac.authorization.k8s.io
 name: system:nodes
 namespace: kube-system
- kind: Group
 apiGroup: rbac.authorization.k8s.io
 name: system:serviceaccounts:kube-system
- kind: Group
 apiGroup: rbac.authorization.k8s.io
 name: system:serviceaccounts:security-test
# 也可授权给指定的 service account 或者用户(不推荐):
- kind: ServiceAccount
 name: <authorized service account name>
 namespace: <authorized pod namespace>
- kind: User
 apiGroup: rbac.authorization.k8s.io
 name: <authorized user name>
# 授权给所有的 service accounts:
- kind: Group
 apiGroup: rbac.authorization.k8s.io
 name: system:serviceaccounts
# 授权给所有已认证的用户:
- kind: Group
 apiGroup: rbac.authorization.k8s.io
 name: system:authenticated

创建安全策略资源

  1. 在集群中创建一个 PodSecurityPolicy 资源。宽松权限版本:
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'
  1. 严格权限版本:
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
 name: restricted
 annotations:
   seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
   apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
   apparmor.security.beta.kubernetes.io/defaultProfileName:  'runtime/default'
spec:
 privileged: false
 # Required to prevent escalations to root.
 allowPrivilegeEscalation: false
 requiredDropCapabilities:
   - ALL
 # Allow core volume types.
 volumes:
   - 'configMap'
   - 'emptyDir'
   - 'projected'
   - 'secret'
   - 'downwardAPI'
   # Assume that ephemeral CSI drivers & persistentVolumes set up by the cluster admin are safe to use.
   - 'csi'
   - 'persistentVolumeClaim'
   - 'ephemeral'
 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

启用 admission controller 插件

启用 admission controller 的 psp 插件有两种方式:

  1. 在已存在的集群中通过修改 apiserver 的静态 manifest 文件,为 apiserver 增加启动参数 enable-admission-plugins=PodSecurityPolicy。kubelet 会自动检测到变更并重启 apiserver。下面的示例使用 sed 对原有参数进行了替换:
$ sed -i 's/enable-admission-plugins=NodeRestriction/enable-admission-plugins=NodeRestriction,PodSecurityPolicy/' /etc/kubernetes/manifests/kube-apiserver.yaml
  1. 或者在初始化集群时,在 kubeadm 配置文件中添加额外参数。
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
apiServer:
extraArgs:
enable-admission-plugins: "PodSecurityPolicy"

验证 psp 的安全限制

我们在上文授权过的 security-test 命名空间进行测试,验证 psp 对 pod 的限制条件。

首先确保在集群中应用了严格版本的 psp 资源,然后尝试通过 deployment 创建一个需要使用 hostNetwork 的 pod:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: nginx-hostnetwork
spec:
 selector:
   matchLabels:
     run: nginx
 template:
   metadata:
     labels:
       run: nginx
   spec:
     hostNetwork: true
     containers:
     - image: nginx
       imagePullPolicy: Always
       name: nginx-privileged

创建并查看结果:

$ kubectl create -f hostnetwork-pod.yaml -n security-test
deployment.apps/nginx-hostnetwork created
$ kubectl get deploy -n security-test nginx-hostnetwork
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
nginx-hostnetwork   0/1     0            0           17s
$ kubectl -n security-test get event | grep "pod security policy"
103s        Warning   FailedCreate             deployment/nginx-hostnetwork                                Error creating: pods "nginx-hostnetwork-" is forbidden: unable to validate against any pod security policy: [spec.securityContext.hostNetwork: Invalid value: true: Host network is not allowed to be used]

如果有 pod 违反了安全策略,解决方案是要么调整 pod 的规格,要么修改 pod 安全策略资源。psp 资源是全局生效的,不能针对不同的命名空间设置不同的安全策略级别,这是一个很明显的局限性。

另外对 psp 的授权机制也比较复杂。如果没有授权或者未创建安全策略,结果是所有 pod 被拒绝,这也导致在集群中默认开启该功能的操作难度很大。

从 Kubernetes v1.21 开始,Pod Security Policy 将被弃用,并将在 v1.25 中删除。Kubernetes 引入了 Pod Security Admission 作为其替代者,我们将在下文中详细解析。

Pod Security Admission

为什么要替换 psp

KEP-2579[2] 详细阐述了使用 Pod Security Admission 替代 Pod Security Policy 的三点主要理由:

  1. 将策略与用户或 service account 绑定的模型削弱了安全性。
  2. 功能无法流畅切换,在没有安全策略的情况下无法关闭。
  3. API 不一致且缺乏灵活性。

新的 Pod Security Admission 机制在易用性和灵活性上都有了很大提升,从使用角度有以下四点显着不同:

  1. 可以在集群中默认开启,只要不添加约束条件就不会触发对 pod 的校验。
  2. 只在命名空间级别生效,可以为不同命名空间通过添加标签的方式设置不同的安全限制。
  3. 可以为特定的用户、命名空间或者运行时设置豁免规则。
  4. 根据实践预设了三种安全等级,不需要由用户单独去设置每一个安全条件。

Pod Security Admission 将原来 Pod Security Policy 的安全条件划分成三种预设的安全等级:

  • privileged: 不受限,向 pod 提供所有可用的权限。
  • baseline:最低限度的限制策略,防止已知的特权升级。
  • restricted:严格限制策略,遵循当前 Pod 加固的最佳实践。

三种等级从宽松到严格递增,各自包含了不同限度的安全条件[3],适用于不同的 pod 工作场景。此外还可以将安全等级设置为固定的 Kubernetes 版本,这样即使集群升级到了新的版本且新版本的安全等级定义发生变化,依然可以按旧版本的安全条件对 pod 进行约束。

当 pod 与安全等级冲突时,我们可通过三种模式来选择不同的处理方式:

  • enforce:只允许符合安全等级要求的 pod,拒绝与安全等级冲突的 pod。
  • audit:只将安全等级冲突记录在集群 event 中,不会拒绝 pod。
  • warn:与安全等级冲突时会向用户返回一个警告信息,但不会拒绝 pod。

audit 和 warn 模式是独立的,如果同时需要两者的功能必须分别设置两种模式。

应用安全策略不再需要创建单独的集群资源,只需要为命名空间设置控制标签:

pod-security.kubernetes.io/<mode>: <level>
pod-security.kubernetes.io/<mode>-version: <version>

下文会有更完整的示例。

在旧版本启用 psa

虽然 Pod Security Admission 是一个在 Kubernetes v1.22 引入的功能,但旧版本可以通过安装 PodSecurity admission webhook 来启用该功能,具体步骤如下:

$ git clone https://github.com/kubernetes/pod-security-admission.git
$ cd pod-security-admission/webhook
$ make certs
$ kubectl apply -k .

以上来自官方文档的步骤在 v1.18.17 会有两个兼容性问题,具体问题和解决方案如下:

  1. kubectl 内置的 kustomize 版本不支持 "replacements" 字段:
$ kubectl apply -k .
   error: json: unknown field "replacements"

 解决方案:安装最新版本的 kusomize 然后在同一目录执行

$ kustomize build . | kubectl apply -f -
  1. manifest/50-deployment.yaml 文件中定义的 Deployment.spec.template.spec.containers[0].securityContext 字段在 v1.19 版本才开始引入,因此 v1.18 需要将该字段修改为对应的 annotation 版本,详见 Seccomp[4]:
error: error validating "STDIN": error validating data: ValidationError(Deployment.spec.template.spec.containers[0].securityContext): unknown field "seccompProfile" in io.k8s.api.core.v1.SecurityContext; if you choose to ignore these errors, turn validation off with --validate=false

验证 psa 的安全限制

首先创建一个新的命名空间 psa-test 用于测试,并将其定义强制应用 baseline 安全等级,并对 restricted 等级进行警告和审计:

apiVersion: v1
kind: Namespace
metadata:
 name: psa-test
 labels:
   pod-security.kubernetes.io/enforce: baseline
   pod-security.kubernetes.io/enforce-version: v1.18
   # We are setting these to our _desired_ `enforce` level.
   pod-security.kubernetes.io/audit: restricted
   pod-security.kubernetes.io/audit-version: v1.18
   pod-security.kubernetes.io/warn: restricted
   pod-security.kubernetes.io/warn-version: v1.18

接着在该命名空间中创建上文示例中用过的 deployment:

$ kubectl create -f hostnetwork-pod.yaml -n psa-test
deployment.apps/nginx-hostnetwork created
$ kubectl get deploy -n psa-test nginx-hostnetwork
NAME                READY   UP-TO-DATE   AVAILABLE   AGE
nginx-hostnetwork   0/1     0            0           17s
$ kubectl -n psa-test get event | grep PodSecurity
104s        Warning   FailedCreate        replicaset/nginx-hostnetwork-644cdd6598   Error creating:

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK