Kubernetes开发人员和平台工程师通常承受着巨大的压力,以使应用程序部署保持健壮性。凭借Kubernetes的规模和功能,这可能令人望而生畏。
为了确保应用程序的健壮性,你现在应该在集群中运行以下五个Kubernetes准入控制策略。
注意:以下每个策略都可以通过开放策略代理(OPA)实施,这是针对云原生的事实上的策略引擎。更简单的是,所有这些OPA策略都可以使用Styra声明式授权服务(DAS)在几分钟内实施。
1.受信任的镜像仓库
此策略很简单,但功能强大:仅允许从受信任的镜像仓库中拉取容器镜像。
这是因为,从网上随意拉取未知镜像会带来风险,例如恶意软件。通过确保镜像仅来自受信任的镜像仓库,你可以紧密控制镜像安全,减轻软件被攻击的风险,随之提高集群的整体安全性。
相关策略:
- 禁止使用带有” latest “标签的镜像
- 仅使用签名的镜像,或与特定哈希/SHA匹配的镜像
示例:
package kubernetes.validating.images deny[msg] { some i input.request.kind.kind == "Pod" image := input.request.object.spec.containers[i].image not startswith(image, "hooli.com/") msg := sprintf("Image '%v' comes from untrusted registry", [image]) }
2.安全的标签
该策略要求所有Kubernetes资源都包含指定的标签,并使用适当的格式。由于标签确定了Kubernetes对象和策略的分组,包括工作负载可以在何处运行,以及哪些资源可以发送流量,因此错误的标签会导致生产中出现难以置信的部署和运维问题。
此外,由于没有访问权限来控制标签的应用方式(增删改),因此集群缺乏基本的安全性。手动输入标签的危险会使错误会蔓延,特别是因为标签在Kubernetes中既非常灵活又非常强大。
因此,应用此策略,并确保正确地实施,可以提高Kubernetes集群的安全性。
相关策略:
- 确保每个工作负载都需要特定的注释
- 指定污点(taints)和容忍度( olerations)以限制可以在何处部署镜像
示例:
package kubernetes.validating.existence deny[msg] { not input.request.object.metadata.labels.costcenter msg := "Every resource must have a costcenter label" } deny[msg] { value := input.request.object.metadata.labels.costcenter not startswith(value, "cccode-") msg := sprintf("Costcenter code must start with `cccode-`; found `%v`", [value]) }
3.禁止使用特权模式
此策略,确保默认情况下容器不能在特权模式下运行。
通常,你要避免在特权模式下运行容器,因为它提供对主机资源和内核功能(包括禁用主机级保护的功能)的访问。
尽管容器在某种程度上是隔离的,但它们最终共享同一内核。这意味着,如果特权容器遭到破坏,则它可能成为破坏整个系统的起点。如果想要在特权模式下运行镜像-仅确保这些时间是例外,而不是常规。
相关策略:
- 禁止不安全的能力
- 禁止容器以root身份运行(要以非root身份运行)
- 设置用户ID
示例:
package kubernetes.validating.privileged deny[msg] { some c input_container[c] c.securityContext.privileged msg := sprintf("Container '%v' should not run in privileged mode.", [c.name]) } input_container[container] { container := input.request.object.spec.containers[_] } input_container[container] { container := input.request.object.spec.initContainers[_] }
4.定义和控制流量入口
流量入口策略,允许你根据需要公开特定的服务,或者根据需要不公开某些服务。在Kubernetes中,服务被意外暴露太容易了(在Kubernetes Failure Stories上有很多这样的例子)。同时,过度放任的Ingress可能会导致你启动不必要的外部LoadBalancers,这也会变得非常昂贵(如每月预算支出)!此外,当两个服务尝试共享同一个Ingress时,它可能会轻易地破坏你的应用程序。
下面的策略示例,可防止不同名称空间中的Ingress对象共享相同的主机名。这就可以确保新的工作负载会从现有工作负载中“窃取”互联网流量,不然带来一系列负面影响,如数据泄露等等。
相关策略:
- 需要TLS
- 禁止/允许特定端口
示例:
package kubernetes.validating.ingress deny[msg] { is_ingress input_host := input.request.object.spec.rules[_].host some other_ns, other_name other_host := data.kubernetes.ingresses[other_ns][other_name].spec.rules[_].host [input_ns, input_name] != [other_ns, other_name] input_host == other_host msg := sprintf("Ingress host conflicts with ingress %v/%v", [other_ns, other_name]) } input_ns = input.request.object.metadata.namespace input_name = input.request.object.metadata.name is_ingress { input.request.kind.kind == "Ingress" input.request.kind.group == "extensions" input.request.kind.version == "v1beta1" }
5.定义和控制流量出口
每个应用程序都需要护栏,来控制出口流量的流动方式,该策略使你可以指定集群内和集群外通信。与Ingress一样,流量出口也容易被暴露。
使用此策略,可以允许定义流量出口何时有效以及针对哪些服务进行访问。
相关策略:
- 请参阅上方的流量入口策略
示例:
package kubernetes.validating.egress allow_list := { "10.10.0.0/16", "192.168.100.1/32" } deny[reason] { network_policy_allows_all_egress reason := "Network policy allows access to any IP address." } deny[reason] { count(allow_list) > 0 input.request.kind.kind == "NetworkPolicy" input.request.object.spec.policyTypes[_] == "Egress" ipBlock := input.request.object.spec.egress[_].to[_].ipBlock not any({t | t := net.cidr_contains(allow_list[_], ipBlock.cidr)}) reason := "Network policy allows egress traffic outside of allowed IP ranges." } network_policy_allows_all_egress { input.request.kind.kind == "NetworkPolicy" input.request.object.spec.policyTypes[_] == "Egress" egress := input.request.object.spec.egress[_] not egress.to }
有了这些策略,你就可以专注于构建一个安全性高的平台,该平台可以防止你的应用开发人员无意间将整个过程拖垮,将数据暴露等。如果你想为Kubernetes添加更多基本策略,请访问openpolicyagent.org或Styra DAS策略库。
译文链接:https://thenewstack.io/open-policy-agent-the-top-5-kubernetes-admission-control-policies/