在 Kubeadm 中使用 pod 安全策略
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
创建一个 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
参考: https://www.infoq.cn/article/QEKvAlu4UoxqVxo1Uh5F
- 点赞
- 收藏
- 关注作者
评论(0)