在 Kubeadm 中使用 pod 安全策略

举报
鲲鹏小玩家 发表于 2020/02/12 10:55:13 2020/02/12
【摘要】 Pod 安全策略是一种机制,用于限制容器在 k8s 上运行时可执行的操作,例如防止其作为特权容器的运行,和主机网络运行等。本文内容较多,所以在此做了一个完整的简介说明:在 master 节点运行 kubeadm init 并启用 PodSecurityPolicy admission controller;使用 RBAC 配置添加一些 pod 安全策略 - 足够启动 CNI 和 DNS 等;...

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

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

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

  • 使用 RBAC 配置添加一些 pod 安全策略 - 足够启动 CNI 和 DNS 等;
    没有它,CNI daemonsets 将无法启动

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

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

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

我们将要做什么

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

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

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

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

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

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

  6. 安装没有特定 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

创建一个 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


【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。