在 Kubeadm 中使用 pod 安全策略

网友投稿 700 2022-05-30

Pod 安全策略是一种机制,用于限制容器在 k8s 上运行时可执行的操作,例如防止其作为特权容器的运行,和主机网络运行等。

本文内容较多,所以在此做了一个完整的简介说明:

在 master 节点运行 kubeadm init 并启用 PodSecurityPolicy admission controller;

使用 RBAC 配置添加一些 pod 安全策略 - 足够启动 CNI 和 DNS 等;

没有它,CNI daemonsets 将无法启动

应用您的 CNI 插件,该插件可以使用以前创建的一个 pod 安全策略

通过 kubeadm join 完成向集群添加节点的配置

在向集群添加更多工作负载时,请检查是否需要额外的 pod 安全策略和 RBAC 配置

我们将要做什么

以下是在 kubeadm 上安装运行 pod 安全策略所采取的步骤:

为 master 节点的初始化配置 pod 安全策略 admission controller

为网络层组件配置一些 pod 安全策略

配置 CNI 插件 - 这里将使用 flannel

然后将使用下面的代码演示一些其他的 pod 安全策略方案

安装一个具有一些特定要求的 nginx-ingress controller - 这只是为了说明添加额外的策略

安装没有特定 pod 安全策略要求的常规服务 - 基于 httpbin.org

环境

将只在单个节点说明,而不是具有高可用集群的多节点集群

Ubuntu:根据 kubeadm 的要求切换,为 UTC 配置时区

Docker:假设当前用户在 docker 组中

kubernetes 1.11.3 - 使用 RBAC

master 节点配置

按照说明安装 kubeadm

安装 jq,用它来进行一些 json 输出处理

sudo apt-get update   sudo apt-get install -y jq

验证 kubeadm 的版本

sudo kubeadm version

接着我们将为下面创建的内容创建一个目录,在以下所有说明中我们假设您在此目录中

mkdir ~/psp-inv   cd ~/psp-inv

kubeadm 配置文件

将创建这个文件,并将其用于 master 上的 kubeadm init,使用此内容创建一个 kubeadm-config.yaml 文件 - 请注意我们必须为 flannel 指定 pod 子网为 10.244.0.0/16

请注意,此文件对于此演示是最小的,如果您使用更高版本的 kubeadm,则可能需要更改 apiVersion

apiVersion: kubeadm.k8s.io/v1alpha2   kind: MasterConfiguration   apiServerExtraArgs:    enable-admission-plugins: PodSecurityPolicy   controllerManagerExtraArgs:   address: 0.0.0.0   kubernetesVersion: v1.11.3   networking:   podSubnet: 10.244.0.0/16   schedulerExtraArgs:   address: 0.0.0.0

Master init

根据上面命令输出的说明获取您自己的 kubeconfig 文件副本,如果要将工作节点添加到集群,请注意加入以下信息,并检查 master 节点的状态

NAME STATUS ROLES AGE VERSION pmcgrath-k8s-master NotReady master 1m v1.11.3

这说明节点还没有准备好,因为它正在等待 CNI,检查一下 pods

No resources found.

所以,如果我们没有启用 pod security policy admission control,通常看不到任何正在运行的 pod,检查 docker

docker container ls --format '{{ .Names }}' k8s\_kube-scheduler\_kube-scheduler-pmcgrath-k8s-master\_kube-system\_a00c35e56ebd0bdfcd77d53674a5d2a1\_0 k8s\_kube-controller-manager\_kube-controller-manager-pmcgrath-k8s-master\_kube-system\_fd832ada507cef85e01885d1e1980c37\_0   k8s\_etcd\_etcd-pmcgrath-k8s-master\_kube-system\_16a8af6b4a79e9b0f81092f85eab37cf\_0   k8s\_kube-apiserver\_kube-apiserver-pmcgrath-k8s-master\_kube-system\_db201a8ecaf8e99623b425502a6ba627\_0   k8s\_POD\_kube-controller-manager-pmcgrath-k8s-master\_kube-system\_fd832ada507cef85e01885d1e1980c37\_0   k8s\_POD\_kube-scheduler-pmcgrath-k8s-master\_kube-system\_a00c35e56ebd0bdfcd77d53674a5d2a1\_0   k8s\_POD\_kube-apiserver-pmcgrath-k8s-master\_kube-system\_db201a8ecaf8e99623b425502a6ba627\_0   k8s\_POD\_etcd-pmcgrath-k8s-master\_kube-system\_16a8af6b4a79e9b0f81092f85eab37cf\_0

所有容器都在运行,但这不是通过 kubectl 获取的信息, 还需检查事件

会看到类似这样的错误:pods “kube-proxy-“ is forbidden: no providers available to validate pod request

配置 pod 安全策略

当前已经配置了:

任何工作负载都可以使用默认的 pod 安全策略,它没有什么特权,但应该适用于大多数工作负载

将创建一个 RBAC ClusterRole

将为所有经过身份验证的用户创建 RBAC ClusterRoleBinding

我授予 kube-system 命名空间中的节点和所有服务帐户访问权限的特权 pod 安全策略

考虑这个权限应该被限制在这个命名空间内

应该只在此命名空间中运行 k8s 组件

将创建一个 RBAC ClusterRole

将在 kube-system 命名空间中创建 RBAC RoleBinding

使用此内容创建 default-psp-with-rbac.yaml 文件

apiVersion: policy/v1beta1   kind: PodSecurityPolicy   metadata:    annotations:    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'    apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default'    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'    seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default'    name: default   spec: allowedCapabilities: \[\] # default set of capabilities are implicitly allowed    allowPrivilegeEscalation: false    fsGroup:    rule: 'MustRunAs'    ranges:    # Forbid adding the root group.   \- min: 1    max: 65535    hostIPC: false    hostNetwork: false    hostPID: false    privileged: false    readOnlyRootFilesystem: false    runAsUser:    rule: 'MustRunAsNonRoot'    seLinux:    rule: 'RunAsNonRoot'    supplementalGroups:    rule: 'RunAsNonRoot'    ranges:    # Forbid adding the root group.   \- min: 1    max: 65535    volumes:   \- 'configMap'   \- 'downwardAPI'   \- 'emptyDir'   \- 'persistentVolumeClaim'   \- 'projected'   \- 'secret'    hostNetwork: false    runAsUser:    rule: 'RunAsAny'    seLinux:    rule: 'RunAsAny'    supplementalGroups:    rule: 'RunAsAny'    fsGroup:    rule: 'RunAsAny' \--- \# Cluster role which grants access to the default pod security policy  apiVersion: rbac.authorization.k8s.io/v1   kind: ClusterRole   metadata:    name: default-psp   rules:   \- apiGroups:    - policy    resourceNames:    - default    resources:    - podsecuritypolicies    verbs:    - use\--- \# Cluster role binding for default pod security policy granting all authenticated users access apiVersion: rbac.authorization.k8s.io/v1   kind: ClusterRoleBinding   metadata:    name: default-psp   roleRef:    apiGroup: rbac.authorization.k8s.io    kind: ClusterRole    name: default-psp   subjects:   \- apiGroup: rbac.authorization.k8s.io    kind: Group    name: system:authenticated

使用此内容创建 privileged-psp-with-rbac.yaml 文件

# Should grant access to very few pods, i.e. kube-system system pods and possibly CNI pods   apiVersion: policy/v1beta1   kind: PodSecurityPolicy   metadata:    annotations:    # See https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp    seccomp.security.alpha.kubernetes.io/allowedProfileNames: '\*'    name: privileged   spec:    allowedCapabilities:   \- '\*'    allowPrivilegeEscalation: true    fsGroup:    rule: 'RunAsAny'    hostIPC: true    hostNetwork: true    hostPID: true    hostPorts:   \- min: 0    max: 65535    privileged: true    readOnlyRootFilesystem: false    runAsUser:    rule: 'RunAsAny'    seLinux:    rule: 'RunAsAny'    supplementalGroups:    rule: 'RunAsAny'    volumes:   \- '\*' \--- \# Cluster role which grants access to the privileged pod security policy  apiVersion: rbac.authorization.k8s.io/v1   kind: ClusterRole   metadata:    name: privileged-psp   rules:   \- apiGroups:    - policy    resourceNames:    - privileged    resources:    - podsecuritypolicies    verbs:    - use\--- \# Role binding for kube-system - allow nodes and kube-system service accounts - should take care of CNI i.e. flannel running in the kube-system namespace  \# Assumes access to the kube-system is restricted   apiVersion: rbac.authorization.k8s.io/v1   kind: RoleBinding   metadata:    name: kube-system-psp    namespace: kube-system   roleRef:    apiGroup: rbac.authorization.k8s.io    kind: ClusterRole    name: privileged-psp   subjects:   \# For the kubeadm kube-system nodes   \- apiGroup: rbac.authorization.k8s.io    kind: Group    name: system:nodes   \# For all service accounts in the kube-system namespace   \- apiGroup: rbac.authorization.k8s.io    kind: Group    name: system:serviceaccounts:kube-system

使用 RBAC 配置应用上述 pod 安全策略

kubectl apply -f default-psp-with-rbac.yaml   kubectl apply -f privileged-psp-with-rbac.yaml

校验

一段时间后,网络层 pods 将处于运行状态,coredns pods 将挂起 - 等待 CNI

kubectl get pods --all-namespaces --output wide --watch

在配置 CNI 之前, 网络层 pods 将再次启动失败,因为节点仍未准备就绪

安装 flannel**

只有当前存在有特权 pod 安全策略才能完成此操作,并且 kube-system 中的 flannel 服务帐户将能够使用,如果使用不同的 CNI 插件,则应使用其安装说明,可能需要修改用于 kubeadm init 的 kubeadm-config.yaml 文件中的 podSubnet。

延伸链接

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

再次校验

kubectl get pods --all-namespaces --output wide --watch

所有 pods 最终将进入运行状态,包括 coredns pod(s)

kubectl get nodes

节点现已准备就绪。

允许 master 上的工作负载

如果想要启动工作节点,可以正常使用 kubeadm join 命令,利用 kubeadm init 输出,在这里跳过。

这些工作节点上没有特别需要加入集群 pod 安全策略,允许 master 节点上的工作负载,因为我们只是尝试在单个节点集群上进行验证。

kubectl taint nodes --all node-role.kubernetes.io/master-

nginx ingress

使用下方链接的清单

创建一个新的命名空间和单个实例 ingress controller,足以说明额外的 pod 安全策略。

由于命名空间尚不存在,因此我们可以创建,以便引用服务帐户

kubectl create namespace ingress-nginx

在 Kubeadm 中使用 pod 安全策略

创建一个 pod 安全策略

本 pod 安全策略基于 deployment 清单,使用此内容创建文件 nginx-ingress-psp-with-rbac.yaml

apiVersion: policy/v1beta1   kind: PodSecurityPolicy   metadata:    annotations:    # Assumes apparmor available    apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'    apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default'    seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default'    seccomp.security.alpha.kubernetes.io/defaultProfileName: 'docker/default'    name: ingress-nginx   spec:    # See nginx-ingress-controller deployment at https://github.com/kubernetes/ingress-nginx/blob/master/deploy/mandatory.yaml    # See also https://github.com/kubernetes-incubator/kubespray/blob/master/roles/kubernetes-apps/ingress\_controller/ingress\_nginx/templates/psp-ingress-nginx.yml.j2    allowedCapabilities:   \- NET\_BIND\_SERVICE    allowPrivilegeEscalation: true    fsGroup:    rule: 'MustRunAs'    ranges:   \- min: 1    max: 65535    hostIPC: false    hostNetwork: false    hostPID: false    hostPorts:   \- min: 80    max: 65535    privileged: false    readOnlyRootFilesystem: false    runAsUser:    rule: 'MustRunAsNonRoot'    ranges:   \- min: 33    max: 65535    seLinux:    rule: 'RunAsAny'    supplementalGroups:    rule: 'MustRunAs'    ranges:    # Forbid adding the root group.   \- min: 1    max: 65535    volumes:   \- 'configMap'   \- 'downwardAPI'   \- 'emptyDir'   \- 'projected'   \- 'secret' \--- apiVersion: rbac.authorization.k8s.io/v1    kind: Role   metadata:    name: ingress-nginx-psp    namespace: ingress-nginx   rules:   \- apiGroups:    - policy    resourceNames:    - ingress-nginx    resources:    - podsecuritypolicies    verbs:    - use\--- apiVersion: rbac.authorization.k8s.io/v1    kind: RoleBinding   metadata:    name: ingress-nginx-psp    namespace: ingress-nginx   roleRef:    apiGroup: rbac.authorization.k8s.io    kind: Role    name: ingress-nginx-psp   subjects:   \# Lets cover default and nginx-ingress-serviceaccount service accounts   \# Could have altered default-http-backend deployment to use the same service acccount to avoid granting the default service account access   \- kind: ServiceAccount    name: default   \- kind: ServiceAccount    name: nginx-ingress-serviceaccount

即创建

kubectl apply -f nginx-ingress-psp-with-rbac.yaml

创建 nginx-ingress 工作负载

删除 controller-publish-service arg,因为在这里不需要

curl -s https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml | sed '/--publish-service/d' | kubectl apply -f -

检查 pods

kubectl get pods --namespace ingress-nginx --watch

现在可以看到 pod 安全策略附加了注释

kubectl get pods --namespace ingress-nginx --selector app.kubernetes.io/name=ingress-nginx -o json | jq -r '.items\[0\].metadata.annotations."kubernetes.io/psp"'

Httpbin.org 工作负载

部署一个工作负载,默认的 pod 安全策略就足够了,使用此内容创建一个 httpbin.yaml 文件

apiVersion: apps/v1   kind: Deployment   metadata:    labels:    app.kubernetes.io/name: httpbin    name: httpbin   spec:    replicas: 1    selector:    matchLabels:    app.kubernetes.io/name: httpbin    template:    metadata:    labels:    app.kubernetes.io/name: httpbin    spec:    containers:   \- args: \["-b", "0.0.0.0:8080", "httpbin:app"\]    command: \["gunicorn"\]    image: docker.io/kennethreitz/httpbin:latest    imagePullPolicy: Always    name: httpbin    ports:   \- containerPort: 8080    name: http    restartPolicy: Always \--- apiVersion: extensions/v1beta1    kind: Ingress   metadata:    annotations:    kubernetes.io/ingress.class: "nginx"    labels:    app.kubernetes.io/name: httpbin    name: httpbin   spec:    rules:    - host: my.httpbin.com    http:    paths:    - path:    backend:    serviceName: httpbin    servicePort: 8080\--- apiVersion: v1    kind: Service   metadata:    labels:    app.kubernetes.io/name: httpbin    name: httpbin   spec:    ports:    - name: http    port: 8080    selector:    app.kubernetes.io/name: httpbin

创建命名空间并在工作负载里运行

kubectl create namespace demo   kubectl apply --namespace demo -f httpbin.yaml

检查 pod 是否存在以及是否使用了默认策略

kubectl get pods --namespace demo  kubectl get pods --namespace demo --selector app.kubernetes.io/name=httpbin -o json | jq -r '.items\[0\].metadata.annotations."kubernetes.io/psp"'

测试工作负载

通过调用 ingress controller pod 实例

\# Get nginx ingress controller pod IP   nginx\_ip=$(kubectl get pods --namespace ingress-nginx --selector app.kubernetes.io/name=ingress-nginx --output json | jq -r .items\[0\].status.podIP) \# Test ingress and out httpbin workload curl -H 'Host: my.httpbin.com' http://$nginx\_ip/get

如果打乱它,可以重置并重启

\# Note: Will loose PKI also which is fine here as kubeadm master init will re-create   sudo kubeadm reset \# Should flush iptable rules after a kubeadm reset, see https://blog.heptio.com/properly-resetting-your-kubeadm-bootstrapped-cluster-nodes-heptioprotip-473bd0b824aa  sudo iptables -F && sudo iptables -t nat -F && sudo iptables -t mangle -F && sudo iptables -X

参考文档

安全建议

GCE 政策

参考: https://www.infoq.cn/article/QEKvAlu4UoxqVxo1Uh5F

容器 安全 Kubernetes

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:“我们的开源项目”活动发起人——庄表伟专访
下一篇:【我的物联网成长记11】8招带你玩转规则引擎
相关文章