保护DigitalOceanKubernetes集群的推荐步骤

来自菜鸟教程
跳转至:导航、​搜索

作者选择 Open Sourcing Mental Illness 作为 Write for DOnations 计划的一部分来接受捐赠。

介绍

Kubernetes,开源容器编排平台,正稳步成为自动化、扩展和管理高可用性集群的首选解决方案。 由于其越来越受欢迎,Kubernetes 的安全性变得越来越重要。

考虑到 Kubernetes 中涉及的移动部件和各种部署场景,保护 Kubernetes 有时可能很复杂。 因此,本文的目标是为 DigitalOcean Kubernetes (DOKS) 集群 提供坚实的安全基础。 请注意,本教程涵盖了 Kubernetes 的基本安全措施,旨在作为一个起点,而不是详尽的指南。 更多步骤,请参阅 Kubernetes 官方文档

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

先决条件

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

  • 一个 DigitalOcean Kubernetes (DOKS) 托管集群,具有 3 个标准节点 ,每个配置至少 2 GB RAM1 个 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

根据您的配置,输出将与此类似:

OutputKubernetes 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 集群对本地客户端进行身份验证。

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

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 以文本形式打印出证书请求。

输出将显示证书请求,其开头将如下所示:

OutputCertificate 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 证书。 这使您能够确保请求的访问权限适合给定的用户。 在本节中,您将发送 sammy 的证书请求并批准它。

要将 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 document,此命令使用 cat 将证书请求传递给 kubectl apply

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

  • name: sammy-authentication 创建一个元数据标识符,在本例中称为 sammy-authentication
  • request: $(cat ~/certs/sammy.csr | base64 | tr -d '\n')sammy.csr 证书签名请求发送到编码为 Base64 的集群。
  • server authclient auth 指定证书的预期用途。 在这种情况下,目的是用户认证。

输出将类似于以下内容:

Outputcertificatesigningrequest.certificates.k8s.io/sammy-authentication created

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

kubectl get csr

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

OutputNAME                   AGE   REQUESTOR                 CONDITION
sammy-authentication   37s   your_DO_email    Pending

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

kubectl certificate approve sammy-authentication

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

Outputcertificatesigningrequest.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

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

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

构建远程用户 Kubeconfig

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

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

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

接下来,编辑新文件:

nano ~/.kube/config-sammy

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

配置-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-certificateclient-key,使用其对应证书位置的绝对路径。 否则,kubectl 将产生错误。


保存并退出文件。

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

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

您将看到与此类似的错误:

OutputTo 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 连接成功,并且 sammy 身份验证凭据已被 Kubernetes API 接受。

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

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

在此步骤中,您将使用 kubectl 将预定义角色 edit 分配给 default 命名空间中的用户 sammy。 在生产环境中,您可能希望使用自定义角色和/或自定义角色绑定。

授予权限

在 Kubernetes 中,授予权限意味着将所需的角色分配给用户。 使用以下命令为 default 命名空间中的用户 sammy 分配 edit 权限:

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

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

Outputrolebinding.rbac.authorization.k8s.io/sammy-edit-role created

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

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

接下来,通过列出 default 命名空间中的 pod 来验证用户权限。 如果没有显示错误,您可以判断 RBAC 授权是否按预期工作。

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

您将获得以下输出:

Outputyes

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

撤销权限

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

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

kubectl delete rolebinding sammy-edit-role

您将获得以下输出:

Outputrolebinding.rbac.authorization.k8s.io "sammy-edit-role" deleted

通过列出 default 命名空间 pod 来验证用户权限是否已按预期撤销:

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

您将收到以下错误:

OutputError 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 时,您可以让它使用 default 服务帐户,也可以定义您选择的服务帐户。 将单个 SA 分配给应用程序和进程的能力使管理员可以根据需要自由授予或撤销权限。 此外,将特定 SA 分配给生产关键型应用程序被认为是最佳安全实践。 由于服务帐户用于身份验证,因此用于 RBAC 授权检查,集群管理员可以通过更改服务帐户访问权限和隔离违规进程来遏制安全威胁。

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

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

kubectl create sa nginx-sa

你会得到:

Outputserviceaccount/nginx-sa created

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

kubectl get sa

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

OutputNAME       SECRETS   AGE
default    1         22h
nginx-sa   1         80s

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

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

运行它将产生以下结果:

Outputrolebinding.rbac.authorization.k8s.io/nginx-sa-edit created

此命令使用与用户 sammy 相同的格式,除了 --serviceaccount=default:nginx-sa 标志,您在 default 命名空间中分配 nginx-sa 服务帐户.

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

kubectl get rolebinding

这将给出以下输出:

OutputNAME            AGE
nginx-sa-edit   23s

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

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

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

该命令的第一部分在端口 :80 上创建一个运行 nginx Web 服务器的新 pod,最后一部分 --serviceaccount="nginx-sa" 表示该 pod 应该使用 [ X176X] 服务帐户,而不是 default SA。

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

Outputdeployment.apps/nginx created

使用 kubectl describe 验证新应用程序是否正在使用服务帐户:

kubectl describe deployment nginx

这将输出部署参数的冗长描述。 在 Pod Template 部分下,您将看到类似以下的输出:

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

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

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

接下来,您将设置准入控制器来控制资源并防止资源匮乏攻击。

第 4 步 — 设置准入控制器

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

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

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

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

nano resource-quota-default.yaml

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

资源配额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"

此定义使用hard关键字设置硬约束,如podsconfigmapsPersistentVolumeClaimsReplicationControllerssecretsservicesloadbalancers。 这也限制了计算资源,例如:

  • requests.cpu,在milliCPU中设置请求的最大CPU值,或者CPU核心的千分之一。
  • requests.memory,以字节为单位设置请求的最大内存值。
  • limits.cpu,以毫秒为单位设置最大 CPU 值限制。
  • limits.memory,以字节为单位设置限制的最大内存值。

保存并退出文件。

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

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

这将产生以下结果:

Outputresourcequota/resource-quota-default created

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

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

kubectl describe quota --namespace=default

输出将与此类似,但您在 resource-quota-default.yaml 文件中设置了硬限制:

OutputName:                   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 准入控制器。 类似于 ResourceQuota 对命名空间实施限制的方式,LimitRange 实施通过验证和变异容器声明的限制。

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

nano limit-range-default.yaml

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

限制范围-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

这将给出以下输出:

Outputlimitrange/limit-range-default created

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

kubectl describe limits --namespace=default

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

OutputName:       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

这将给出以下输出:

Outputpod/nginx created

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

kubectl get pod nginx -o yaml

这将提供多行输出。 查看容器规范部分以找到 LimitRange 准入控制器中指定的资源限制:

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
...

这与您在容器规范中手动声明 resourcesrequests 相同。

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

结论

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

如果您想了解更多关于 Kubernetes 的信息,请查看我们的 Kubernetes 资源页面,或关注我们的 Kubernetes 全栈开发人员自学课程