46 KiB
approvers, title, content_template, weight
| approvers | title | content_template | weight | |
|---|---|---|---|---|
|
Pod 安全策略 | templates/concept | 20 |
{{% capture overview %}}
{{< feature-state state="beta" >}}
Pod 安全策略支持针对 pod 创建和更新进行精细的权限控制。
{{% /capture %}}
{{% capture body %}}
什么是 Pod 安全策略?
Pod 安全策略 是集群级别的资源,它能够控制 Pod 规范中对安全敏感的方面。 [PodSecurityPolicy](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#podsecuritypolicy-v1beta1-policy) 对象定义了一组 pod 必须在其上运行才能被系统接受的条件,以及相关字段的默认设置。 它们允许管理员控制如下方面:
| 所控制的方面 | 字段名称 |
|---|---|
| 运行特权容器 | privileged |
| 使用主机 PID 的命名空间 | hostPID, hostIPC |
| 主机网络的使用 | hostNetwork,hostPorts |
| 控制卷类型的使用 | volumes |
| 主机路径的使用 | allowedHostPaths |
| FlexVolume 卷驱动的白名单 | allowedFlexVolumes |
| 分配拥有 Pod 数据卷的 FSGroup | fsGroup |
| 必须使用一个只读的 root 文件系统 | [readOnlyRootFilesystem(#volumes-and-file-systems) |
| 容器的用户和组的 ID | runAsUser, runAsGroup, supplementalGroups |
| 限制提升为 root 特权 | allowPrivilegeEscalation, defaultAllowPrivilegeEscalation |
| 为容器添加默认的一组能力 | defaultAddCapabilities, requiredDropCapabilities, allowedCapabilities |
| 容器的 SELinux 上下文 | seLinux |
| 容器允许的 Proc 挂载类型 | allowedProcMountTypes |
| 容器使用的 AppArmor 配置 | annotations |
| 容器使用的 seccomp 配置 | annotations |
| 容器使用的 sysctl 配置 | forbiddenSysctls,allowedUnsafeSysctls |
启用 Pod 安全策略
Pod 安全策略控制是 admission 控制器 的一个可选实现。PodSecurityPolicy通过 启用 admission 控制器 被强制启用, 但是如果集群内没有授权任何策略时这样做 将导致任何 pod 都无法被创建。
由于 Pod 安全策略 API (policy/v1beta1/podsecuritypolicy) 时独立于 admission 控制器启用的,
所以对于现有集群推荐在启用 admission 控制器之前就添加并授权安全策略。
授权策略
当一个 PodSecurityPolicy 资源被创建后,不会执行任何动作。想要应用这个策略,请求用户或者目标 pod 的
服务账户 必须在策略中使用 use 动词,从而被授权使用这个策略。
多数 Kubernetes pod 并非由用户直接创建。相反,通常他们作为 Deployment、 ReplicaSet 或者其他的模板控制器的组成部分由控制器管理器被间接地创建。 授权策略给控制器即授权给 所有 由该控制器创建的 pod,因此授权策略的授权方式应该时授权给 pod 的 service account (参见 示例)。
使用 RBAC
RBAC 是一个标准的 Kubernetes 授权模式,同时也能够方便地用于授权策 略的使用。
首先,需要有一个 Role 或者 ClusterRole 被授予访问权限来 use 预期策略。授予访问权限的策略如下所示:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: <角色名称>
rules:
- apiGroups: ['policy']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames:
- <需要授权的策略列表>
随后该 (Cluster)Role 即绑定给了授权的用户:
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: <绑定名称>
roleRef:
kind: ClusterRole
name: <角色名称>
apiGroup: rbac.authorization.k8s.io
subjects:
# 授权给特定的服务账户:
- kind: ServiceAccount
name: <授权的服务账户名称>
namespace: <授权的 pod 命名空间>
# 授权给指定用户 (不推荐):
- kind: User
apiGroup: rbac.authorization.k8s.io
name: <授权的用户名>
如果一个 RoleBinding (非 ClusterRoleBinding) 被使用,它只会授权给那些运行在与绑定相同命名空间下的 pod。
这可以与系统组一起授权给所有运行在该命名空间下的所有 pod。
# 授权命名空间下的所有 service accounts:
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:serviceaccounts
# 或者同样的,授权给命名空间下的所有已验证的用户:
- kind: Group
apiGroup: rbac.authorization.k8s.io
name: system:authenticated
更多关于 RBAC 绑定的示例,查看 角色绑定示例。 关于授权 PodSecurityPolicy 的完整示例,查看 下面。
故障诊断
- 控制器管理器 必须运行于 安全的 API 端口, 并且必须不能具备超级用户权限。否则请求将可绕过身份验证和授权模块,所有的 PodSecurityPolicy 对象将被允许,并且所有的用户将能够创建特权容器。 关于配置控制器管理器授权的更多细节,查看 控制器角色。
策略顺序
除了限制 pod 的创建和更新之外,pod 安全策略也可以用于为由其控制的许多字段提供默认值。当同时多个策略可用时,pod 安全策略控制器依据以下标准选择策略:
- 允许 pod 维持原状,不改变默认设置或者变更 pod 的 PodSecurityPolicy 是首选。这些非变更的 PodSecurityPolicy 的顺序无关紧要。
- 如果 pod 必须默认或者变更,则使用第一个 PodSecurityPolicy(按名称排序)以允许该 pod。
{{< note >}}
在更新操作期间(在 pod 规范不允许变更的期间)只有非变更的 PodSecurityPolicy 会用于校验 pod。 {{< /note >}}
示例
这个示例假定你有一个运行中且已启用 PodSecurityPolicy admission 控制器的集群,并且你拥有集群管理员权限。
设置
以此为例设置一个命名空间和 service account. 我们将使用这个 service account 来模拟一个非管理员用户.
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
在一个文件中定义 PodSecurityPolicy 对象实例。这里的策略只是用来禁止创建有特权 要求的 Pods。 PodSecurityPolicy 对象的命名必须是合法的 DNS 子域名.
{{< codenew file="policy/example-psp.yaml" >}}
然后通过 kubectl 命名创建它:
kubectl-admin create -f example-psp.yaml
现在作为一个非特权用户, 尝试创建一个简单的 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: []
发生了什么? 尽管 PodSecurityPolicy 已经创建, pod 的 service account 和 fake-user 都没有权限使用这个策略:
kubectl-user auth can-i use podsecuritypolicy/example
no
创建 rolebinding 来为示例策略中的 fake-user 的 use 动词授权:
{{< note >}}
不推荐这种方式! 更推荐的方式请查看 下一部分. {{< /note >}}
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 的尝试都应当被拒绝:
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:
kubectl-user delete pod pause
运行另一个 pod
我们来再试一次, 相比之前略微有些不同:
kubectl-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
发生了什么? 我们已经将 psp:unprivileged 角色绑定给了 fake-user, 为何仍然报错
Error creating: pods "pause-7774d79b5-" is forbidden: no providers available to validate pod request?
答案在 replicaset-controller 源码中. Fake-user 成功创建了 deployment (并且该 deployment 成功创建了一个 replicaset),
然而当该 replicaset 想要创建 pod 时却没有权限使用我们的示例 pod 安全策略.
为了修复这个问题,将 psp:unprivileged 角色绑定给 pod 的 service account. 在这种情况下(由于我们没有显式指定) 该 service account 为 default:
kubectl-admin create rolebinding default:psp:unprivileged \
--role=psp:unprivileged \
--serviceaccount=psp-example:default
rolebinding "default:psp:unprivileged" created
现在如果你给它一分钟让它重试, replicaset-controller 应该最终能够成功创建出 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
注意 PodSecurityPolicy 并非命名空间限定的资源类型, 必须单独清理:
kubectl-admin delete psp example
podsecuritypolicy "example" deleted
示例策略
这是一个你能够创建的最小化的限制性策略, 相当于不使用 pod 安全策略准入控制器:
{{< codenew file="policy/privileged-psp.yaml" >}}
这是一个限制性策略示例,要求用户以非特权用户运行, 屏蔽了权限提升至 root 的可能性, 同时需要使用几个安全机制.
{{< codenew file="policy/restricted-psp.yaml" >}}
策略参考
特权
特权 - 决定 pod 中的任意容器能否启用特权模式。默认情况下一个容器不允许访问主机上的任意设备, 但是一个 "特权" 容器是允许访问主机上的所有设备的。 这样容器具备的权限几乎等同于主机上运行的进程。 这对于像比如操作网络堆栈和访问设备等需要使用 linux capabilities 的容器来说非常有用。
主机命名空间
HostPID - 控制 pod 容器能否共享主机进程 ID 命名空间。 注意当它与 ptrace 搭配使用时可被利用于在容器外部提升权限(ptrace 默认被禁用)。
HostIPC - 控制 pod 容器能够共享主机 IPC
HostNetwork - 控制 pod 能否使用节点网络命名空间。 这样做可允许 pod 访问回环设备、监听 localhost 的服务以及能够用于探测同意节点上其他 pod 的网络活动。
HostPorts - 提供主机网络名称空间中允许端口范围的白名单。定义为 HostPortRange 列表,其中包含 min(包含) 和 max(包含)。默认不允许任何主机端口。
存储卷和文件系统
Volumes - 提供允许的存储卷类型的白名单。允许值对应于在创建卷时定义的源卷。完整的卷类型列表,请查阅 卷类型。
此外 * 可用于允许所有卷类型。
对于新 PSP 允许的卷推荐的最小化组合 是:
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- secret
- projected
{{< warning >}}
PodSecurityPolicy 并不会对可能被 PersistentVolumeClaim 引用的 PersistentVolume 资源对象的类型做限制,
并且 hostPath 类型的 PersistentVolumes 并不支持只读访问模式。只有可信用户才应当给予创建 PersistentVolume 资源对象的权限。
{{< /warning >}}
FSGroup - 控制应用于某些卷的补充组。
- MustRunAs - 要求至少指定一个
range。使用第一个范围的最小值作为默认值。针对所有范围进行验证。 - MayRunAs - 要求至少指定一个
range。允许FSGroups不提供默认设置。如果设置了FSGroups, 则针对所有范围进行验证。 - RunAsAny - 不提供默认值。允许指定任意
fsGroupID。
AllowedHostPaths - 它指定主机路径的白名单,允许 hostPath 卷使用这些白名单。空列表意味着对使用的主机路径没有限制。
这被定义为一个对象列表,其中有一个 pathPrefix 字段,它允许 hostPath 卷挂载一个以允许的前缀开头的路径,以及一个 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
{{< warning >}}
对于能够无限制访问主机文件系统的容器来说,有许多方式可以进行特权提升,包括从其他容器读取数据,以及滥用系统服务(如 Kubelet)的凭据。
可写的 hostPath 目录卷允许容器以允许它们在 pathPrefix 之外遍历主机文件系统的方式写入文件系统。readOnly: true 在 Kubernetes 1.11+ 可用,
必须应用于 所有 allowedHostPaths 以有效限制对指定 pathPrefix 的访问。
{{< /warning >}}
ReadOnlyRootFilesystem - 要求容器必须运行于一个只读的根文件系统(即不可写的层)。
FlexVolume 驱动
这指定了 FlexVolume 驱动程序的白名单,允许 FlexVolume 使用这些驱动程序。空列表或 nil 表示对驱动程序没有限制。
请确保 volumes 字段包含 flexVolume 卷类型;否则任何 FlexVolume 驱动都不允许。
例如:
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: allow-flex-volumes
spec:
# ... 其他特性字段
volumes:
- flexVolume
allowedFlexVolumes:
- driver: example/lvm
- driver: example/cifs
用户和组
RunAsUser - 控制容器以哪个用户 ID 运行。
- MustRunAs - 要求至少指定一个
range。使用第一个范围的最小值作为默认值。针对所有范围进行验证。 - MustRunAsNonRoot - 要求 pod 使用非零的
runAsUser提交,或者在镜像中定义USER命令 (使用数字 UID)。 没有指定runAsNonRoot或runAsUser设置的 pod 将被修改为设置runAsNonRoot=true, 因此需要在容器中定义一个非零的数字USER命令。不提供默认值。这个策略强烈建议设置allowPrivilegeEscalation=false。 - RunAsAny - 不提供默认值。允许指定任意
runAsUser。
RunAsGroup - 控制容器以哪个首要组 ID 运行。
- MustRunAs - 要求至少指定一个
range。使用第一个范围的最小值作为默认值。针对所有范围进行验证。 - MayRunAs - 不要求指定 RunAsGroup。但是,当指定 RunAsGroup 时,它们必须落在定义的范围内。
- RunAsAny - 不提供默认值。允许指定任意
runAsGroup。
SupplementalGroups - 控制容器添加那些组 ID。
- MustRunAs - 要求至少指定一个
range。使用第一个范围的最小值作为默认值。针对所有范围进行验证。 - MayRunAs - 要求至少指定一个
range。允许在不提供默认值的情况下不设置supplementalGroups。 如果设置了supplementalGroups,则对所有范围进行验证。 - RunAsAny - 不提供默认值。允许指定任意
supplementalGroups。
特权提升
这个选项控制容器的 allowPrivilegeEscalation 选项。这个布尔值直接控制 no_new_privs
标志是否设置到容器进程上。这个标志将防止 setuid 二进制文件改变有效的用户 ID,并防止文件启用额外的功能 (例如,它将防止使用 ping 工具)。
这种行为前提是需要有效地强制指定 MustRunAsNonRoot。
AllowPrivilegeEscalation - 是否允许用户将容器的安全上下文设置为 allowPrivilegeEscalation=true。
这是默认设置,以便不破坏 setuid 二进制文件。将其设置为 false 可以确保容器的子进程不能获得比其父进程更多的特权。
DefaultAllowPrivilegeEscalation - 设置 allowPrivilegeEscalation 选项的默认值。没有这个设置的默认行为是允许
特权升级,以避免破坏 setuid 二进制文件。如果这种行为并非有意,可以使此字段默认为不允许,但仍然允许
显式请求 allowPrivilegeEscalation 的 pods。
能力
Linux 能力提供了对传统上与超级用户相关的特权的细粒度分解。其中一些能力可用于特权提升或容器断接,并且可能受到 PodSecurityPolicy 的限制。 有关Linux功能的更多信息,查看 capabilities(7)。
以下字段列出了能力列表,在 ALL_CAPS 中指定为不带 CAP_ 前缀的能力名称。
AllowedCapabilities - 提供可添加到容器中的能力白名单。默认的能力集是隐式允许的。
空集意味着在默认集之外不能添加任何附加能力。* 可用于允许所有能力。
RequiredDropCapabilities - 必须从容器中删除的能力。这些能力将从默认设置中删除,并且不能被添加。
在 requireddropcapability 中列出的能力不能包含在 allowedcapability 或 defaultaddcapability 中。
DefaultAddCapabilities - 除了运行时默认设置外,默认添加到容器中的能力。查看 Docker 文档 中关于使用 Docker 运行时时默认的能力列表。
SELinux
- MustRunAs - 要求设置
seLinuxOptions。使用seLinuxOptions作为默认值。针对所有范围进行验证。 - RunAsAny - 不提供默认值。允许指定任意
seLinuxOptions。
AllowedProcMountTypes
allowedProcMountTypes 是允许 ProcMountTypes 的白名单。空列表或 nil 表示只允许 DefaultProcMountType 指定的默认值。
DefaultProcMount 对只读路径使用容器运行时默认值,对/proc 使用掩码路径。大多数容器运行时屏蔽 /proc 中的某些路径,
以避免特殊设备或信息的意外安全性暴露。使用字符串 Default 表示。
另一个 ProcMountType 是 UnmaskedProcMount,它绕过了容器运行时的默认屏蔽行为,并确保新创建的 /proc 容器保持不变。
使用字符串 unmask 表示。
AppArmor
通过 PodSecurityPolicy 上面的注解进行控制。参考 AppArmor 文档。
Seccomp
可以通过 PodSecurityPolicy 上的注解来控制 pod 中 seccomp 配置文件的使用。 Seccomp 是 Kubernetes 的一个 alpha 特性。
seccomp.security.alpha.kubernetes.io/defaultProfileName - 指定要应用于容器的默认 seccomp 配置文件的注解。 可选值有:
unconfined- 如果没有提供替代方案,则 Seccomp 不会应用于容器进程 (这是 Kubernetes 中的默认设置)。runtime/default- 使用默认的容器运行时配置。docker/default- 使用 Docker 默认的 seccomp 配置。于 Kubernetes 1.11 弃用。使用runtime/default代替。localhost/<path>- 将配置文件指定为位于节点上<seccomp_root>/<path>目录下的文件, 其中通过 Kubelet 上的--seccomp-profile-root标志来定义<seccomp_root>。
seccomp.security.alpha.kubernetes.io/allowedProfileNames - 用于指定 pod seccomp 注解允许哪些取值的注解。
指定为允许值的逗号分隔列表。可选值是上面列出的值,加上 * 以允许所有配置文件。缺少此注解意味着无法更改默认值。
Sysctl
默认情况下允许所有安全的 sysctl。
forbiddenSysctls- 排除特定的 sysctl。您可以在列表中禁止安全的和不安全的 sysctl 的组合。要禁止设置任意 sysctl,请单独使用*。allowedUnsafeSysctls- 允许在默认列表中被禁用的特定 sysctl,只要这些 sysctl 没有在forbiddenSysctls中列出。
参考 Sysctl 文档。 {{% /capture %}}
{{% capture whatsnext %}}
关于 api 细节参考 [Pod 安全策略指南](/docs/reference/generated/kubernetes-api/{{< param "version" >}}/#podsecuritypolicy-v1beta1-policy)。
{{% /capture %}}