kubernetes部署的最佳安全实践

AguClark 8年前
   <p>本文阐述了作者在部署一个安全的kubernetes应用时的最佳实践,包括:镜像无漏洞,使用授权镜像,限制节点访问,限制资源访问,定义资源配额,实现网络分割,运用安全上下文,处处记录日志,等等,并建议读者将这些措施无缝集成到持续集成流水线中。</p>    <p>kubernetes提供了许多能有效提升你的应用安全性的规则。配置这些规则需要你精通kubernetes,并且明确部署的安全需求。此处我们强调的最佳实践指的是容器的生命周期管理:构建,打包和运行,并且特指kubernetes的部署。我们在 我们自己的SaaS部署 中采用了这些最佳实践,它是运行在谷歌云平台上的kubernetes。</p>    <p>以下就是我们关于部署一个安全的kubernetes应用的建议。</p>    <h3>确保镜像没有漏洞</h3>    <p>用有漏洞的镜像来跑容器会使你的环境面临更容易被攻破的风险。确保你的所有软件都没有已知漏洞,就能减少很多攻击。</p>    <ul>     <li><strong>进行持续的安全漏洞扫描</strong> -- 容器可能包含那些过期的携带已知漏洞的包。漏洞扫描不能只是个一次性过程,因为新的漏洞每天都在发布。一个持续评估镜像安全性的过程,对确保一个必要的安全态势是至关重要的。</li>     <li><strong>定期对环境进行安全更新</strong> -- 一旦运行中的容器发现了漏洞,你必须更新源镜像并重新部署容器。尽量避免直接更新运行中的容器(如使用 apt-update),因为这会破坏镜像和容器的关系。使用 kubernetes 的 rolling update 特性来升级容器是相当简单的,它允许逐步更新运行中的应用,通过更新镜像到最新版本。</li>    </ul>    <h3>确保环境中只使用授权的镜像</h3>    <p>如果不能确保只允许附带组织策略的镜像运行,那么该组织将冒着运行有漏洞甚至是恶意容器的风险。从未知源下载并运行镜像是危险的。这相当于在生产环境服务器上运行未知供应商提供的软件。千万不要这么做。</p>    <p>使用私有仓库来存储你的认证过的镜像,并确保只上传认证过的镜像到这些私有仓库。单单这条就已经缩小范围了,它从成千上万的公开可用的镜像中减少到只剩一部分潜在的镜像可以进入到你的流水线。构建一条 CI 流水线来集成安全评估(比如漏洞扫描),使它成为构建过程的一部分。</p>    <p>CI 流水线必须确保只有评审过的代码(允许上生产环境)才能用来构建镜像。一旦镜像构建好,它必须扫描安全漏洞,并且只有当没发现问题时,这个镜像才能被上传到私有仓库,从该仓库中部署到生产环境就完成了。安全评估中的失败应该在流水线中创建一个失败,从而阻止差的安全质量的镜像上传到镜像仓库。</p>    <p>kubernetes 的镜像认证插件正在完工中(期望在 kubernetes 1.4 中完工),它将允许阻止未认证的镜像打包。详情请看这个 <a href="/misc/goto?guid=4959729128455498802" rel="nofollow,noindex">pull request</a> 。</p>    <h3>限制对 Kubernetes 节点的直接访问。</h3>    <p>你应该限制对 kubernetes 节点的 SSH 访问,减少主机资源未认证就被访问的风险。相应的,你应该要求用户使用 kubectl exex 命令来访问,它将提供对容器环境的直接访问,而不涉及对主机的访问。</p>    <p>你可以使用 kubernetes 的 授权插件 来进一步控制用户的资源访问权限。它允许对特定的命名空间,容器和操作定义细粒度访问的控制规则。</p>    <h3>资源之间创建管理边界</h3>    <p>限制用户权限的范围可以减少误操作或者恶意操作的影响。kubernetes 的命令空间允许你将已创建的资源划分成逻辑命名组。一个命名空间内创建的资源可以和其它命名空间隔离开。默认情况下,用户在kubernetes集群中创建的每个资源都在default命名空间下。你可以创建额外的命名空间并将资源和用户挂到该空间下。你可以通过kubernetes的授权插件(Authorization plugins)创建策略,隔离不同用户对空间下的资源的访问权限。</p>    <p>例如,以下策略允许“alice”读取命名空间“fronto”下的 pods。</p>    <pre>  {      "apiVersion": "abac.authorization.kubernetes.io/v1beta1",      "kind": "Policy",      "spec": {          "user": "alice",          "namespace": "fronto",          "resource": "pods",          "readonly": true      }  </pre>    <p>}</p>    <h3>定义资源配额</h3>    <p>运行资源不受限的容器会使你的系统面临DoS或者“吵闹的邻居(noisy neighbor)”的风险。为了阻止或者最小化这些风险,你应该定义资源配额。默认情况下,kubernetes集群中创建的所有资源都不限制CPU和内存。你可以创建资源配额策略并关联到命名空间下,以限制pod对CPU和内存的消耗。</p>    <p>以下是命名空间下资源配额定义的例子,它限制了该命令空间下,pod数量为4,总的CPU使用为1到2个,总的内存为1到2G。</p>    <p>compute-resources.yaml:</p>    <pre>  apiVersion: v1    kind: ResourceQuota    metadata:      name: compute-resources    spec:      hard:        pods: "4"        requests.cpu: "1"        requests.memory: 1Gi        limits.cpu: "2"        limits.memory: 2Gi  </pre>    <p>将资源配额关联到命名空间方法如下:</p>    <pre>  kubectl create -f ./compute-resources.yaml --namespace=myspace  </pre>    <h3>实现网络分割</h3>    <p>不同的应用都运行在同一个kubernetes集群中,会面临一个缺乏抵抗力的应用攻击它的邻居应用的风险。网络分割对确保容器只能和那些它们允许访问的容器交流来说就很重要。</p>    <p>kubernetes部署中的其中一个挑战就是在pod,服务和容器间创建网络分割。说它是挑战,是因为容器网络标识(IPs)的“动态”本质,以及另一个事实,即容器可以在同一节点和不同节点间通信。</p>    <p>谷歌云平台的用户受益于平台的自动防火墙规则,可以阻止跨集群的通信。使用网络防火墙或者SDN方案,我们也可以在本地部署一个相似的实现。kubernetes的 网络SIG 正在该领域做相关工作,它能明显改善pod和pod间通信的策略。一个新的网络策略API应该满足用户的需求,在pod间创建防火墙规则,以限制容器间的网络访问。</p>    <p>以下是个网络策略的例子,它控制"backend"pod的网络,只允许“frontend”pod 的网络访问。</p>    <pre>  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"    }    }  </pre>    <p>}</p>    <h3>给pod和容器使用安全上下文</h3>    <p>在设计容器和pod的时候,请确保为你的pod,容器和volumes配置了安全的上下文。一个安全的上下文是部署yaml中定义的一个属性。它控制了将赋给pod/容器/volume的安全参数。某些重要参数如下:</p>    <p>Security Context Setting Description</p>    <p>SecurityContext->runAsNonRoot Indicates that containers should run as non-root user</p>    <p>SecurityContext->Capabilities Controls the Linux capabilities assigned to the container.</p>    <p>SecurityContext->readOnlyRootFilesystem Controls whether a container will be able to write into the root filesystem.</p>    <p>PodSecurityContext->runAsNonRoot Prevents running a container with ‘root’ user as part of the pod</p>    <p>以下是一个带安全上下文参数的pod定义例子:</p>    <pre>  apiVersion: v1    kind: Pod    metadata:    name: hello-world    spec:    containers:    # specification of the pod’s containers    # ...    securityContext:    readOnlyRootFilesystem: true    runAsNonRoot: true  </pre>    <p>当你要用提升的权限(--privileged)来运行容器的时候,你应该考虑使用“DenyEscalatingExec”接入控制。这个控制拒绝 exec 和 attach 命令发到 pod ,那些pod用提升权限在运行,允许主机访问。这些pod包括以特权运行的,对主机IPC命名空间有访问权限的,以及对主机PID命名空间有访问权限的。</p>    <h3>每个地方都记录日志</h3>    <p>kubernetes提供基于集群的日志,允许记录容器活动到一个中央日志仓库。当一个集群创建的时候,每个容器的标准输出和标准错误输出都可以通过一个运行在每个节点上的Fluentd代理获取到,这些输出被输入到谷歌Stackdriver日志系统或者Elasticsearch,并通过Kibana来查看。</p>    <h3>总结</h3>    <p>kubernetes应用了许多选项来创建安全的部署。不存在一刀切的解决方案,适用于任何地方。因此对这些选项要有一定的熟悉度,以及理解它们如何增强你的应用的安全性。</p>    <p>我们建议使用本文强调的那些最佳实践,使用kubernetes的灵活配置能力将安全过程并入到持续集成流水线中,使得整个过程能自动化得无缝接入。</p>    <p> </p>    <p>来自:http://dockone.io/article/1904</p>    <p> </p>