# Kubernetes 部署安全最佳实践

原文：[Security Best Practices for Kubernetes Deployment](https://kubernetes.io/blog/2016/08/security-best-practices-kubernetes-deployment)

> 本文作者是来自 Aqua Security 的  Amir Jerbi 和 Michael Cherny，他们以大量的案例和经验为基础，总结并描述了 Kubernetes 部署中的最佳安全实践。

Kubernetes 提供了很多能够提高应用安全的方法。要进行这些配置，就要掌握 Kubernetes 的相关知识，同时也要清楚的了解安全需求。这里我们关注的安全内容集中在容器的生命周期上：构建、传输以及运行，并且针对 Kubernetes 进行了特别的裁剪。[我们自己的 SaaS](http://blog.aquasec.com/running-a-security-service-in-google-cloud-real-world-example) 就是运行在 Google Cloud Platform 上的 Kubernetes 中，已经采用了这些最佳实践。

下面是我们对于安全部署 Kubernetes 应用的一些建议。

## 确保镜像无漏洞

运行带有漏洞的容器会让你的环境身处险境。只要运行中的系统的所有组件都不存在已知漏洞，就能够避免很多被攻击的机会。

### 安全漏洞的持续扫描

容器中可能有一些过期组件，这些过期组件往往会包含已知漏洞（CVE）。新的漏洞层出不穷，因此对安全漏洞的扫描工作必须持续进行。


### 适时应用安全更新

一旦在运行的容器中发现了安全漏洞，就该对源镜像进行更新并部署。为了避免破坏镜像和容器的继承性，尽量不要在容器**中**直接进行更新（例如 `apt-update`）。 Kubernetes 的滚动更新功能可以渐进式的为运行中的应用更新镜像，这一功能让应用更新变得简单优雅。

## 只使用可靠的镜像

要避免受到有漏洞甚至恶意的容器的威胁，镜像的准入就需要受到有效管理。和随意下载运行软件一样，下载运行不可靠的镜像也是高危行为，必须杜绝。

使用私库来保存你的镜像，并保证只向其推送可靠镜像。这样就缩小了战场面积，避免大量不确认的公开镜像涌入你的环境。另外建议在持续构建流程中加入漏洞扫描之类的安全环节。

持续集成管线要控制门槛，只允许使用受确认的代码进行镜像构建。镜像构建成功后，应该进行漏洞扫描，排除问题后才能推入私库，进行下一步的部署。过程中发现问题，应该终端构建过程，阻止安全质量低下的镜像进入私库。

目前 Kubernetes 正在开发镜像认证插件（将在 1.4 推出），用以阻挡未认证镜像的进入，相关信息请参看 [**pull request**](https://github.com/kubernetes/kubernetes/pull/27129)。

## 限制对 Kubernetes Node 的直接访问

对 Kubernetes Node 的 SSH 访问会降低主机的安全性。应该让用户尽量使用 `kubectl exec`，这一命令提供了对容器环境的直接访问，而不需要接触宿主机。

还可以使用 Kubernetes 的 [Authorization Plugins](http://kubernetes.io/docs/admin/authorization/) 来对用户的资源访问进行进一步控制。这一插件允许定义对命名空间、容器以及操作的基于角色的访问控制。

## 在资源之间建立管理边界

限制用户权限能够降低出错和入侵造成的危害。Kubernetes 命名空间让你可以把资源分割为不同名称的群组之中。一个命名空间中创建的资源对其他命名空间是不可见的。缺省情况下，Kubernetes 用户创建的资源都存在于 default 命名空间中。可以创建其他的命名空间，并把资源和用户绑定上去。可以使用 Kubernetes Authorization 插件来创建策略，让不同用户分别访问各自的命名空间和对应的资源。

例如下面的策略让 **"Alice"** 能够从命名空间 **"fronto"** 中读取 Pod：

~~~yaml
{
  "apiVersion": "abac.authorization.kubernetes.io/v1beta1", 
  "kind": "Policy", 
  "spec": {
    "user": "alice",
    "namespace": "fronto",
    "resource": "pods",
    "readonly": true
  }
}
~~~

## 设定资源配额

容器运行中如果没有资源限制，那么系统就可能处于 **DoS** 或[**邻里不和**](https://en.wikipedia.org/wiki/Cloud_computing_issues#Performance_interference_and_noisy_neighbors)的情境之中。要降低或阻止这一风险，就需要设定资源配额。缺省情况下，所有的 Kubernetes 集群资源都可以不受限的访问 CPU 和内存。可以为命名空间创建配额策略，来限制 Pod 的 CPU 和内存消费。

下面的例子是一个命名空间的资源配额定义，限制运行 Pod 数量为 4，CPU 的使用限制在 1-2 之间，内存使用在 1-2 G 之间：

~~~yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    pods: "4"
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi
~~~

将资源配额指派给命名空间：

~~~bash
kubectl create -f ./compute-resources.yaml --namespace=myspace
~~~

## 规划网络分区

在同一个 Kubernetes 集群上运行不同的应用，引入了一个风险就是应用之间的互相访问。要确保容器只能访问允许访问的范围，网络分区是很重要的。Kubernetes 中的一大挑战就是在 Pod、Service 以及容器之间的网络划分，造成这一问题的根本在于容器网络的动态分配过程，让容器可以跨越 Node 进行网络互访。

Google Cloud Platform 用户收益于自动防火墙规则功能，能够阻止跨集群的通信。使用 SDN 或者防火墙能够达到类似的效果。Kuberntes [**Network SIG**](https://github.com/kubernetes/community/wiki/SIG-Network) 正在进行这方面的努力，目的是增强 Pod 之间的通信策略。新的网络策略 API 将会用于创建 Pod 之间的防火墙规则，限制容器应用的网络访问。

下面的例子是一条网络策略，用于控制 "backend" Pod，只允许来自于 "frontend" Pod 的访问。

~~~yaml
POST /apis/net.alpha.kubernetes.io/v1alpha1/namespaces/tenant-a/networkpolicys
{
  "kind": "NetworkPolicy",
  "metadata": {
    "name": "pol1"
  },
  "spec": {
    "allowIncoming": {
      "from": [{
        "pods": { "segment": "frontend" } 
      }],
      "toPorts": [{
        "port": 80,
        "protocol": "TCP" 
      }]
    },
    "podSelector": { 
      "segment": "backend" 
    }
  }
}
~~~

网络策略的更多信息可以阅读 [**SIG-Networking: Kubernetes Network Policy APIs Coming in 1.3**](http://blog.kubernetes.io/2016/04/Kubernetes-Network-Policy-APIs.html)。

## Pod 和容器的安全上下文

设计容器和 Pod 的时候，一定要配置 Pod、容器以及卷的安全上下文。安全上下文是部署 Yaml 中的一个属性，他控制了 pod/container/volume 的安全参数，下面列出一些重要的参数：

|安全上下文设置|描述|
|---|---|
|SecurityContext->runAsNonRoot|容器应该用非 root 用户运行|
|SecurityContext->Capabilities|设置 Linux 分配给容器的性能|
|SecurityContext->readOnlyRootFilesystem|容器是否可以写入 root 文件系统|
|PodSecurityContext->runAsNonRoot|阻止 Pod 中的容器以 root 用户运行|

下面是一个带有安全上下文的 Pod 定义：

~~~yaml
apiVersion: v1
kind: Pod
metadata:
  name: hello-world
spec:
  containers:
  # specification of the pod’s containers
  # ...
  securityContext:
    readOnlyRootFilesystem: true
    runAsNonRoot: true
~~~

[参考](http://kubernetes.io/docs/api-reference/v1/definitions/#_v1_podsecuritycontext)

如果用特权形式（`--privileged`）运行容器，可以用 `DenyEscalatingExec` 控制。这一开关拒绝在特权容器上使用 Exec 和 Attach 命令。具体情况可以参考 [Admission 文档](http://kubernetes.io/docs/admin/admission-controllers/)

## 记录日志

Kubernetes 支持集群级别的日志，集中收集日志到中央服务。当集群创建之后，STDOUT 和 STDERR 就能够被 Node 中的 Fluent 搜集起来，并汇总到 Google Stackdriver Logging 或者 Elasticsearch，并用 Kibana 进行查看。

## 总结

Kubernetes 为安全提供了很多特性。对这些特性进行学习和了解，才能够制定出符合应用需求的安全方案。

我们建议实施文中提到的最佳实践，使用 Kubernetes 的动态配置能力，结合持续集成，无缝提高安全保障能力。
