作者选择了开放源性精神疾病作为Write for DOnations计划的一部分接受捐赠。

Introduction

开源容器编排平台Kubernetes逐渐成为自动化,扩展和管理高可用性集群的首选解决方案。由于其日益普及,Kubernetes安全已变得越来越重要。

考虑到Kubernetes涉及的活动部分以及各种部署方案,保护Kubernetes有时可能很复杂。因此,本文的目的是为DigitalOcean Kubernetes(DOKS)群集提供坚实的安全基础。请注意,本教程涵盖了Kubernetes的基本安全措施,并且仅供参考,而不是详尽的指南。有关其他步骤,请参见正式的Kubernetes文档。

在本指南中,您将采取基本步骤来保护DigitalOcean Kubernetes群集。您将使用TLS / SSL证书配置安全的本地身份验证,使用基于角色的访问控制(RBAC)向本地用户授予权限,使用服务帐户向Kubernetes应用程序和部署授予权限以及使用ResourceQuota和LimitRange接纳控制器设置资源限制。

先决条件

为了完成本教程,您将需要:

  • 由DigitalOcean Kubernetes(DOKS)管理的群集,其中包含3个标准节点,每个节点至少配置2 GB RAM和1个vCPU。有关如何创建DOKS集群的详细说明,请阅读我们的Kubernetes快速入门指南。本教程使用DOKS版本1.16.2-do.1。
  • 本地客户端配置为管理DOKS群集,该群集具有从DigitalOcean控制面板下载并另存为〜/ .kube / config的群集配置文件。有关如何配置远程DOKS管理的详细说明,请阅读我们的指南“如何连接到DigitalOcean Kubernetes群集”。特别是,您将需要:    安装在本地计算机上的kubectl命令行界面。您可以在其官方文档中阅读有关安装和配置kubectl的更多信息。本教程将使用kubectl版本1.17.0-00。  官方DigitalOcean命令行工具doctl。有关如何安装此程序的说明,请参阅doctl GitHub页面。本教程将使用doctl版本1.36.0。

步骤1 —启用远程用户身份验证

完成前提条件后,您将获得一个Kubernetes超级用户,该用户通过预定义的DigitalOcean承载令牌进行身份验证。但是,共享这些凭据不是一个好的安全做法,因为此帐户可能会对您的群集造成大规模且可能具有破坏性的更改。为了减轻这种可能性,您可以设置要从其各自的本地客户端进行身份验证的其他用户。

在本部分中,您将使用安全的SSL / TLS证书从本地客户端向远程DOKS集群认证新用户。这将分为三个步骤:首先,您将为每个用户创建证书签名请求(CSR),然后您将通过kubectl在群集中直接批准这些证书。最后,您将使用适当的证书为每个用户构建一个kubeconfig文件。有关Kubernetes支持的其他身份验证方法的更多信息,请参阅Kubernetes身份验证文档。

为新用户创建证书签名请求

开始之前,请从先决条件中配置的本地计算机检查DOKS群集连接:

  • kubectl cluster-info

根据您的配置,输出将类似于以下内容:

Output
Kubernetes master is running at https://a6616782-5b7f-4381-9c0f-91d6004217c7.k8s.ondigitalocean.com CoreDNS is running at https://a6616782-5b7f-4381-9c0f-91d6004217c7.k8s.ondigitalocean.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

这意味着您已连接到DOKS群集。

接下来,为客户的证书创建一个本地文件夹。就本指南而言,〜/ certs将用于存储所有证书:

  • mkdir ~/certs

在本教程中,我们将授权一个名为sammy的新用户访问群集。随意将其更改为您选择的用户。使用SSL和TLS库OpenSSL,使用以下命令为您的用户生成一个新的私钥:

  • openssl genrsa -out ~/certs/sammy.key 4096

-out标志将使输出文件〜/ certs / sammy .key,并且4096将密钥设置为4096位。有关OpenSSL的更多信息,请参见《 OpenSSL Essentials指南》。

现在,创建一个证书签名请求配置文件。使用文本编辑器打开以下文件(在本教程中,我们将使用nano):

  • nano ~/certs/sammy.csr.cnf

将以下内容添加到sammy.csr.cnf文件中,以在主题中将所需的用户名指定为公用名(CN),将组指定为组织(O):

~/certs/sammy.csr.cnf
[ req ]
default_bits = 2048
prompt = no
default_md = sha256
distinguished_name = dn
[ dn ]
CN = sammy
O = developers
[ v3_ext ]
authorityKeyIdentifier=keyid,issuer:always
basicConstraints=CA:FALSE
keyUsage=keyEncipherment,dataEncipherment
extendedKeyUsage=serverAuth,clientAuth

证书签名请求配置文件包含用户的所有必要信息,用户身份和正确的使用参数。最后一个参数extendedKeyUsage = serverAuth,clientAuth将允许用户在证书签名后使用DOKS集群对本地客户端进行身份验证。

接下来,创建大型证书签名请求:

  • openssl req -config ~/certs/sammy.csr.cnf -new -key ~/certs/sammy.key -nodes -out ~/certs/sammy.csr

-config允许您指定CSR的配置文件,-new信号表示您正在为-key指定的密钥创建新的CSR。

您可以通过运行以下命令来检查证书签名请求:

  • openssl req -in ~/certs/sammy.csr -noout -text

在这里,您使用-in传递CSR,并使用-text以文本形式打印出证书请求。

输出将显示证​​书请求,其开始看起来像这样:

Output
Certificate Request: Data: Version: 1 (0x0) Subject: CN = sammy, O = developers Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (4096 bit) ...

重复相同的步骤为其他用户创建CSR。将所有证书签名请求保存在管理员的〜/ certs文件夹中后,请继续下一步以批准它们。

使用Kubernetes API管理证书签名请求

您可以使用kubectl命令行工具批准或拒绝颁发给Kubernetes API的TLS证书。这使您能够确保请求的访问适合给定用户。在本部分中,您将发送证书请求,以进行拒绝并进行验证。

要将CSR发送到DOKS集群,请使用以下命令:

cat <<EOF | kubectl apply -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
  name: sammy-authentication
spec:
  groups:
  - system:authenticated
  request: $(cat ~/certs/sammy.csr | base64 | tr -d '\n')
  usages:
  - digital signature
  - key encipherment
  - server auth
  - client auth
EOF

使用Bash here文档,此命令使用cat将证书请求传递给kubectl apply。

让我们仔细看看证书申请:

  • 名称:sammy-authentication创建元数据标识符,本例中称为sammy-authentication。
  • 请求:$(cat〜/ certs / sammy .csr | base64 | tr -d'\ n')将sammy.csr证书签名请求发送到编码为Base64的群集。
  • 服务器身份验证和客户端身份验证指定证书的预期用途。在这种情况下,目的是用户身份验证。

输出将类似于以下内容:

Output
certificatesigningrequest.certificates.k8s.io/sammy-authentication created

您可以使用以下命令检查证书签名请求状态:

  • kubectl get csr

Depending on your cluster configuration, the output will be similar to this:

Output
NAME AGE REQUESTOR CONDITION sammy-authentication 37s your_DO_email Pending

接下来,使用以下命令批准CSR:

  • kubectl certificate approve sammy-authentication

您将收到一条消息,确认操作:

Output
certificatesigningrequest.certificates.k8s.io/sammy-authentication approved

注意:作为管理员,您还可以使用命令kubectl certificate deny sammy-authentication拒绝CSR。有关管理TLS证书的更多信息,请阅读Kubernetes官方文档。

现在已经批准了CSR,您可以通过运行以下命令将其下载到本地计算机:

  • kubectl get csr sammy-authentication -o jsonpath='{.status.certificate}' | base64 --decode > ~/certs/sammy.crt

此命令将kubectl解码以正确使用Base64证书,然后将其保存为〜/ certs / sammy .crt。

现在,有了已签名的证书,您可以构建用户的kubeconfig文件。

构建远程用户Kubeconfig

接下来,您将为sammy用户创建一个特定的kubeconfig文件。这样您就可以更好地控制用户对集群的访问。

构建新的kubeconfig的第一步是复制当前kubeconfig文件。就本指南而言,新的kubeconfig文件将称为config-sammy:

  • cp ~/.kube/config ~/.kube/config-sammy

接下来,编辑新文件:

  • nano ~/.kube/config-sammy

保留此文件的前八行,因为它们包含与群集的SSL / TLS连接所必需的信息。然后从用户参数开始,用以下突出显示的行替换文本,以便文件看起来类似于以下内容:

config-sammy
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: certificate_data
  name: do-nyc1-do-cluster
contexts:
- context:
    cluster: do-nyc1-do-cluster
    user: sammy
  name: do-nyc1-do-cluster
current-context: do-nyc1-do-cluster
kind: Config
preferences: {}
users:
- name: sammy
  user:
    client-certificate: /home/your_local_user/certs/sammy.crt
    client-key: /home/your_local_user/certs/sammy.key

注意:对于client-certificate和client-key,请使用指向其相应证书位置的绝对路径。否则,kubectl将产生错误。

保存并退出文件。

您可以使用kubectl cluster-info测试新的用户连接:

  • kubectl --kubeconfig=/home/your_local_user/.kube/config-sammy cluster-info

您将看到类似于以下错误:

Output
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. Error from server (Forbidden): services is forbidden: User "sammy" cannot list resource "services" in API group "" in the namespace "kube-system"

因为用户sammy还没有授权列出群集上的任何资源,所以预期会出现此错误。下一步将介绍授予用户授权。目前,输出确认SSL / TLS连接成功,并且Kubernetes API接受了可靠的身份验证凭据。

第2步-通过基于角色的访问控制(RBAC)授权用户

验证用户身份后,API将使用Kubernetes内置的基于角色的访问控制(RBAC)模型确定其权限。 RBAC是基于分配给它的角色来限制用户权限的有效方法。从安全角度来看,RBAC允许设置细粒度权限,以限制用户访问敏感数据或执行超级用户级命令。有关用户角色的更多详细信息,请参考Kubernetes RBAC文档。

在此步骤中,您将使用kubectl将默认角色编辑分配给默​​认名称空间中的sammy用户。在生产环境中,您可能需要使用自定义角色和/或自定义角色绑定。

授予权限

在Kubernetes中,授予权限意味着向用户分配所需的角色。使用以下命令为默认名称空间中的用户sammy分配编辑权限:

  • kubectl create rolebinding sammy-edit-role --clusterrole=edit --user=sammy --namespace=default

这将产生类似于以下内容的输出:

Output
rolebinding.rbac.authorization.k8s.io/sammy-edit-role created

让我们更详细地分析此命令:

  • 创建角色绑定sammy-edit-role创建一个新的角色绑定,在本例中为sammy-edit-role。
  • --clusterrole = edit在全局范围内分配预定义的角色编辑(群集角色)。
  • --user = sammy指定将角色绑定到的用户。
  • --namespace = default授予用户角色在指定名称空间内的权限,在本例中为default。

接下来,通过在默认名称空间中列出Pod来验证用户权限。如果未显示任何错误,则可以判断RBAC授权是否按预期工作。

  • kubectl --kubeconfig=/home/your_local_user/.kube/config-sammy auth can-i get pods

您将获得以下输出:

Output
yes

现在,您已经为sammy分配了权限,现在可以在下一部分中练习撤销这些权限。

吊销权限

撤销Kubernetes中的权限是通过删除用户角色绑定来完成的。

对于本教程,通过运行以下命令从用户sammy中删除编辑角色:

  • kubectl delete rolebinding sammy-edit-role

您将获得以下输出:

Output
rolebinding.rbac.authorization.k8s.io "sammy-edit-role" deleted

通过列出默认名称空间容器,验证用户权限是否被撤销:

  • kubectl --kubeconfig=/home/localuser/.kube/config-sammy --namespace=default get pods

您将收到以下错误:

Output
Error from server (Forbidden): pods is forbidden: User "sammy" cannot list resource "pods" in API group "" in the namespace "default"

这表明授权已被撤销。

从安全的角度来看,Kubernetes授权模型使集群管理员可以灵活地按需更改用户权限。而且,基于角色的访问控制不限于物理用户;而是基于角色的访问控制。您还可以授予和删除对群集服务的权限,这将在下一节中学习。

有关RBAC授权以及如何创建自定义角色的更多信息,请阅读官方文档。

第3步-使用服务帐户管理应用程序权限

如上一节所述,RBAC授权机制已扩展到人类用户之外。非人类集群用户(例如在pod内运行的应用程序,服务和进程)使用Kubernetes称为服务帐户的API服务器进行身份验证。在命名空间中创建Pod时,可以让其使用默认服务帐户,也可以定义您选择的服务帐户。将单个SA分配给应用程序和流程的能力使管理员可以自由地根据需要授予或撤消权限。此外,将特定的SA分配给生产关键型应用程序被认为是最佳的安全实践。由于服务帐户用于身份验证,因此用于RBAC授权检查,因此群集管理员可以通过更改服务帐户访问权限并隔离违规过程来遏制安全威胁。

为了演示服务帐户,本教程将使用Nginx Web服务器作为示例应用程序。

在为应用程序分配特定的SA之前,您需要创建SA。在默认名称空间中创建一个名为nginx-sa的新服务帐户:

  • kubectl create sa nginx-sa

你会得到:

Output
serviceaccount/nginx-sa created

通过运行以下命令验证服务帐户是否已创建:

  • kubectl get sa

这将为您提供服务帐户列表:

Output
NAME SECRETS AGE default 1 22h nginx-sa 1 80s

现在,您将为nginx-sa服务帐户分配一个角色。对于此示例,向nginx-sa授予与sammy用户相同的权限:

  • kubectl create rolebinding nginx-sa-edit \
  • --clusterrole=edit \
  • --serviceaccount=default:nginx-sa \
  • --namespace=default

运行此命令将产生以下结果:

Output
rolebinding.rbac.authorization.k8s.io/nginx-sa-edit created

该命令使用与用户sammy相同的格式,但--serviceaccount = default:nginx-sa标志除外,该标志用于在默认名称空间中分配nginx-sa服务帐户。

使用以下命令检查角色绑定是否成功:

  • kubectl get rolebinding

这将给出以下输出:

Output
NAME AGE nginx-sa-edit 23s

确认已成功配置服务帐户的角色绑定后,就可以将服务帐户分配给应用程序。为应用程序分配特定的服务帐户将使您可以实时管理其访问权限,从而增强群集安全性。

就本教程而言,nginx pod将用作示例应用程序。创建新的pod并使用以下命令指定nginx-sa服务帐户:

  • kubectl run nginx --image=nginx --port 80 --serviceaccount="nginx-sa"

该命令的第一部分创建一个新的Pod,该Pod在端口:80上运行nginx Web服务器,最后一部分--serviceaccount =“ nginx-sa”指示该Pod应该使用nginx-sa服务帐户,而不是默认的SA。 。

这将为您提供类似于以下内容的输出:

Output
deployment.apps/nginx created

通过使用kubectl describe来验证新应用程序正在使用服务帐户:

  • kubectl describe deployment nginx

这将输出有关部署参数的详细说明。在“窗格模板”部分下,您将看到类似以下的输出:

Output
... Pod Template: Labels: run=nginx Service Account: nginx-sa ...

在本节中,您在默认名称空间中创建了nginx-sa服务帐户,并将其分配给了nginx Web服务器。现在,您可以通过根据需要更改其角色来实时控制nginx权限。您还可以通过为每个应用程序分配相同的服务帐户来对应用程序进行分组,然后对权限进行批量更改。最后,您可以通过为关键应用程序分配唯一的SA来隔离它们。

总而言之,为应用程序/部署分配角色的想法是微调权限。在实际的生产环境中,您可能有多个部署需要不同的权限,从只读权限到完整的管理权限。使用RBAC可使您灵活地根据需要限制对群集的访问。

Next, you will set up admission controllers to control resources and safeguard against resource starvation attacks.

步骤4 —设置准入控制器

Kubernetes准入控制器是可选插件,已编译到kube-apiserver二进制文件中以扩展安全性选项。准入控制器在通过身份验证和授权阶段后拦截请求。一旦请求被拦截,准入控制器就会在应用请求之前执行指定的代码。

尽管身份验证或授权检查的结果是允许或拒绝请求的布尔值,但是准入控制器可以更加多样化。准入控制器可以采用与身份验证相同的方式来验证请求,但也可以更改或更改请求以及在准入对象之前对其进行修改。

在此步骤中,您将使用ResourceQuota和LimitRange准入控制器通过更改可能导致资源匮乏或拒绝服务攻击的请求来保护您的集群。 ResourceQuota准入控制器允许管理员限制计算资源,存储资源和名称空间中任何对象的数量,而LimitRange准入控制器将限制容器使用的资源数量。同时使用这两个准入控制器将保护您的群集免受攻击,使您的资源不可用。

为了演示ResourceQuota的工作原理,您将在默认名称空间中实现一些限制。首先创建一个新的ResourceQuota对象文件:

  • nano resource-quota-default.yaml

添加以下对象定义以设置默认名称空间中资源消耗的约束。您可以根据节点的物理资源根据需要调整值:

resource-quota-default.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: resource-quota-default
spec:
  hard:
    pods: "2"
    requests.cpu: "500m"
    requests.memory: 1Gi
    limits.cpu: "1000m"
    limits.memory: 2Gi
    configmaps: "5"
    persistentvolumeclaims: "2"
    replicationcontrollers: "10"
    secrets: "3"
    services: "4"
    services.loadbalancers: "2"

This definition uses the hard keyword to set hard constraints, such as the maximum number of pods , configmaps , PersistentVolumeClaims , ReplicationControllers , secrets , services , and loadbalancers . This also set contraints on compute resources, like:

  • request.cpu,它以milliCPU或CPU内核的千分之一设置请求的最大CPU值。
  • requests.memory,它设置请求的最大内存值(以字节为单位)。
  • limits.cpu,它设置最大CPU限制值(以milliCPU为单位)。
  • limits.memory,它设置限制的最大内存值(以字节为单位)。

保存并退出文件。

现在,在名称空间中运行以下命令创建对象:

  • kubectl create -f resource-quota-default.yaml --namespace=default

这将产生以下结果:

Output
resourcequota/resource-quota-default created

请注意,您正在使用-f标志向Kubernetes指示ResourceQuota文件的位置,并使用--namespace标志指定将更新哪个命名空间。

创建对象后,您的ResourceQuota将处于活动状态。您可以使用describe quota检查默认的名称空间配额:

  • kubectl describe quota --namespace=default

输出看起来与此类似,但具有在resource-quota-default .yaml文件中设置的硬限制:

Output
Name: resource-quota-default Namespace: default Resource Used Hard -------- ---- ---- configmaps 0 5 limits.cpu 0 1 limits.memory 0 2Gi persistentvolumeclaims 0 2 pods 1 2 replicationcontrollers 0 10 requests.cpu 0 500m requests.memory 0 1Gi secrets 2 3 services 1 4 services.loadbalancers 0 2

ResourceQuotas以绝对单位表示,因此添加其他节点不会自动增加此处定义的值。如果添加了更多节点,则需要在此处手动编辑值以按比例分配资源。 ResourceQuotas可以根据需要进行多次修改,但是除非删除整个命名空间,否则无法删除它们。

如果需要修改特定的ResourceQuota,请更新相应的.yaml文件并使用以下命令应用更改:

  • kubectl apply -f resource-quota-default.yaml --namespace=default

有关ResourceQuota准入控制器的更多信息,请参阅官方文档。

现在您的ResourceQuota已经设置好了,您将继续配置LimitRange Admission Controller。与ResourceQuota对名称空间实施限制的方式类似,LimitRange实施通过验证和变异容器声明的限制。

与之前类似,从创建目标文件开始:

  • nano limit-range-default.yaml

现在,您可以根据需要使用LimitRange对象限制资源使用。添加以下内容作为典型用例的示例:

limit-ranges-default.yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: limit-range-default
spec:
  limits:
  - max:
      cpu: "400m"
      memory: "1Gi"
    min:
      cpu: "100m"
      memory: "100Mi"
    default:
      cpu: "250m"
      memory: "800Mi"
    defaultRequest:
      cpu: "150m"
      memory: "256Mi"
    type: Container

limit-ranges-default .yaml中使用的样本值将容器内存限制为最大1Gi,将CPU使用限制为最大400m,这是Kubernetes度量标准,相当于400 milliCPU,这意味着容器被限制使用其内存的几乎一半。核心。

接下来,使用以下命令将对象部署到API服务器:

  • kubectl create -f limit-range-default.yaml --namespace=default

这将给出以下输出:

Output
limitrange/limit-range-default created

现在,您可以使用以下命令检查新限制:

  • kubectl describe limits --namespace=default

您的输出将类似于以下内容:

Output
Name: limit-range-default Namespace: default Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio ---- -------- --- --- --------------- ------------- ----------------------- Container cpu 100m 400m 150m 250m - Container memory 100Mi 1Gi 256Mi 800Mi -

要查看运行中的LimitRanger,请使用以下命令部署标准nginx容器:

  • kubectl run nginx --image=nginx --port=80 --restart=Never

这将给出以下输出:

Output
pod/nginx created

通过运行以下命令,检查准入控制器如何更改容器:

  • kubectl get pod nginx -o yaml

这将提供许多行输出。在容器规范部分中查找以在LimitRange Admission Controller中指定的资源限制:

Output
... spec: containers: - image: nginx imagePullPolicy: IfNotPresent name: nginx ports: - containerPort: 80 protocol: TCP resources: limits: cpu: 250m memory: 800Mi requests: cpu: 150m memory: 256Mi ...

这与您在容器规范中手动声明资源和请求的情况相同。

在此步骤中,您使用了ResourceQuota和LimitRange准入控制器来防止对集群资源的恶意攻击。有关LimitRange准入控制器的更多信息,请阅读官方文档。

结论

在整个指南中,您配置了基本的Kubernetes安全模板。这样就建立了用户身份验证和授权,应用程序特权以及群集资源保护。结合本文涵盖的所有建议,您将为生产Kubernetes集群部署奠定坚实的基础。在这里,您可以根据情况开始强化群集的各个方面。

如果您想了解有关Kubernetes的更多信息,请查看我们的Kubernetes资源页面,或遵循我们的Kubernetes for Full-Stack Developers自学课程。