本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。
11 种(不)被黑客攻击的方法
Kubernetes 安全性自项目启动以来取得了长足进步,但仍然存在一些陷阱。本文从控制平面开始,逐步介绍工作负载和网络安全,最后展望未来的安全性,提供了一系列实用技巧,以帮助加固您的集群并提高其在受损后的恢复能力。
第一部分:控制平面
控制平面是 Kubernetes 的大脑。它全面掌握集群中运行的每个容器和 pod,可以调度新的 pod(其中可能包含对其父节点具有 root 访问权限的容器),并可以读取集群中存储的所有 secrets。这些宝贵的数据需要保护,防止意外泄漏和恶意意图:在访问时、在静止时以及在网络传输时。
1. 随处使用 TLS
应为支持 TLS 的每个组件启用 TLS,以防止流量嗅探,验证服务器身份,以及(对于相互 TLS)验证客户端身份。
请注意,某些组件和安装方法可能会通过 HTTP 启用本地端口,管理员应熟悉每个组件的设置,以识别潜在不安全的流量。
Lucas Käldström 绘制的此网络图展示了 TLS 应该理想地应用的一些地方:主节点上的每个组件之间,以及 Kubelet 和 API 服务器之间。Kelsey Hightower 的经典之作 《Kubernetes The Hard Way》提供了详细的手动说明,etcd 的安全模型文档也提供了类似说明。

自动扩展 Kubernetes 节点在历史上一直很困难,因为每个节点都需要一个 TLS 密钥才能连接到主节点,而将 secrets 烘焙到基础镜像中并非良好实践。Kubelet TLS 引导提供了一种能力,允许新的 kubelet 创建证书签名请求,以便在启动时生成证书。

2. 启用 RBAC 并遵循最小权限原则,禁用 ABAC,并监控日志
基于角色的访问控制为用户访问资源(例如访问命名空间)提供了细粒度的策略管理。

自 1.6 版本发布以来,Kubernetes 的 ABAC(基于属性的访问控制)已被 RBAC 取代,不应在 API 服务器上启用。请改用 RBAC。
--authorization-mode=RBAC
或者在 GKE 中使用此标志将其禁用
--no-enable-legacy-authorization
有许多关于集群服务的 RBAC 策略的良好示例,以及文档。而且,这并非终点——可以使用 audit2rbac 从审计日志中提取细粒度的 RBAC 策略。
不正确或权限过高的 RBAC 策略在 pod 受损时会带来安全威胁。维护最小权限,并持续审查和改进 RBAC 规则,应被视为团队纳入其开发生命周期的“技术债务卫生”的一部分。
审计日志(1.10 版中的测试版)提供了可定制的 API 日志记录,包括有效负载(例如请求和响应)和元数据级别。日志级别可以根据您组织的安全策略进行调整——GKE 提供了合理的默认设置供您入门。
对于读取请求(如 get、list 和 watch),只有请求对象会保存在审计日志中;响应对象不会。对于涉及敏感数据(如 Secret 和 ConfigMap)的请求,只导出元数据。对于所有其他请求,请求和响应对象都会保存在审计日志中。
别忘了:将这些日志保存在集群内存在安全威胁,以防遭到破坏。这些日志,就像所有其他安全敏感日志一样,应传输到集群外部,以防止在发生泄露时被篡改。
3. 为 API 服务器使用第三方身份验证
在组织中集中身份验证和授权(也称为单点登录)有助于用户的入职、离职和一致权限管理。.
将 Kubernetes 与第三方身份验证提供商(如 Google 或 GitHub)集成,可以利用远程平台的身份保障(由 2FA 等支持),并避免管理员不得不重新配置 Kubernetes API 服务器来添加或删除用户。
Dex 是一个具有可插拔连接器的 OpenID Connect 身份(OIDC)和 OAuth 2.0 提供商。Pusher 通过一些自定义工具更进一步,并且还有其他辅助工具可用于略有不同的用例。
4. 隔离并防火墙您的 etcd 集群
etcd 存储状态和机密信息,是 Kubernetes 的关键组件——它应与集群的其余部分区别对待,进行保护。
对 API 服务器的 etcd 具有写访问权限相当于获得整个集群的 root 权限,即使是读访问权限也可以相当容易地用于提升权限。
Kubernetes 调度器将在 etcd 中搜索没有节点的 Pod 定义。然后它将找到的 Pod 发送给可用的 Kubelet 进行调度。提交的 Pod 的验证由 API 服务器在将其写入 etcd 之前执行,因此直接写入 etcd 的恶意用户可以绕过许多安全机制——例如 PodSecurityPolicies。
etcd 应配置 对等和客户端 TLS 证书,并部署在专用节点上。为了缓解私钥被盗用和从工作节点使用的问题,集群也可以通过防火墙保护到 API 服务器。
5. 轮换加密密钥
安全最佳实践是定期轮换加密密钥和证书,以限制密钥泄露的“爆炸半径”。
Kubernetes 会自动轮换某些证书(特别是 kubelet 客户端和服务器证书),方法是在现有凭据过期时创建新的 CSR。
然而,API 服务器用于加密 etcd 值的对称加密密钥不会自动轮换——它们必须手动轮换。这需要主节点访问权限,因此托管服务(如 GKE 或 AKS)将此问题抽象化,无需操作员干预。
第二部分:工作负载
控制平面上有了最低限度的安全保障,集群就能安全运行。但是,就像一艘载有潜在危险货物的船一样,船上的集装箱必须受到保护,以在发生意外事故或违规时容纳这些货物。Kubernetes 工作负载(pod、部署、作业、集合等)也是如此——它们在部署时可能受到信任,但如果它们面向互联网,则总存在后续被利用的风险。以最小权限运行工作负载并加固其运行时配置有助于降低此风险。
6. 使用 Linux 安全特性和 PodSecurityPolicies
Linux 内核具有许多重叠的安全扩展(功能、SELinux、AppArmor、seccomp-bpf),可以配置为向应用程序提供最小权限。.
像 bane 这样的工具可以帮助生成 AppArmor 配置文件,而 docker-slim 可以生成 seccomp 配置文件,但请注意——在验证应用这些策略的副作用时,需要一套全面的测试套件来执行应用程序中的所有代码路径。
PodSecurityPolicies 可以用于强制使用安全扩展和其他 Kubernetes 安全指令。它们提供了一个 pod 必须满足才能提交到 API 服务器的最低契约——包括安全配置文件、特权标志以及主机网络、进程或 IPC 命名空间的共享。
这些指令很重要,因为它们有助于防止容器化进程逃逸其隔离边界,而 Tim Allclair 的PodSecurityPolicy 示例是一个全面的资源,您可以根据自己的用例进行定制。
7. 静态分析 YAML
在 PodSecurityPolicies 拒绝访问 API 服务器的情况下,静态分析也可用于开发工作流,以模拟组织的合规性要求或风险承受能力。
敏感信息不应存储在 pod 类型 YAML 资源(部署、pod、集合等)中,敏感的配置映射和秘密应使用 vault(通过 CoreOS 的操作器)、git-crypt、sealed secrets 或 云提供商 KMS 等工具进行加密。
YAML 配置的静态分析可用于建立运行时安全基线。kubesec 为资源生成风险评分。
{
"score": -30,
"scoring": {
"critical": [{
"selector": "containers[] .securityContext .privileged == true",
"reason": "Privileged containers can allow almost completely unrestricted host access"
}],
"advise": [{
"selector": "containers[] .securityContext .runAsNonRoot == true",
"reason": "Force the running image to run as a non-root user to ensure least privilege"
}, {
"selector": "containers[] .securityContext .capabilities .drop",
"reason": "Reducing kernel capabilities available to a container limits its attack surface",
"href": "/docs/tasks/configure-pod-container/security-context/"
}]
}
}
而 kubetest 是一个用于 Kubernetes 配置的单元测试框架。
#// vim: set ft=python:
def test_for_team_label():
if spec["kind"] == "Deployment":
labels = spec["spec"]["template"]["metadata"]["labels"]
assert_contains(labels, "team", "should indicate which team owns the deployment")
test_for_team_label()
这些工具“左移”(将检查和验证提前到开发周期)。在开发阶段进行安全测试可以为用户提供关于可能被后续手动或自动化检查拒绝的代码和配置的快速反馈,并可以减少引入更安全实践的阻力。
8. 以非 root 用户运行容器
作为 root 运行的容器通常拥有比其工作负载所需的权限多得多的权限,这在受到攻击时可能有助于攻击者进一步攻击。
容器仍然依赖传统的 Unix 安全模型(称为自主访问控制或 DAC)——一切都是文件,权限授予用户和组。
Kubernetes 中未启用用户命名空间。这意味着容器的用户 ID 表映射到主机的用户表,并且在容器内部以 root 用户运行进程会将其作为主机的 root 运行。尽管我们有分层安全机制来防止容器逃逸,但仍不建议在容器内部以 root 身份运行。
许多容器镜像使用 root 用户运行 PID 1——如果该进程受到攻击,攻击者将在容器中获得 root 权限,并且任何错误配置都更容易被利用。
Bitnami 做了大量工作,将其容器镜像迁移到非 root 用户(特别是因为 OpenShift 默认要求这样做),这可能有助于迁移到非 root 容器镜像。
此 PodSecurityPolicy 片段可防止在容器内部以 root 身份运行进程,并防止权限提升至 root。
# Required to prevent escalations to root.
allowPrivilegeEscalation: false
runAsUser:
# Require the container to run without root privileges.
rule: 'MustRunAsNonRoot'
非 root 容器不能绑定到 1024 以下的特权端口(这由 CAP_NET_BIND_SERVICE 内核能力控制),但可以使用服务来掩盖这一事实。在此示例中,虚构的 MyApp 应用程序在其容器中绑定到端口 8443,但服务通过将请求代理到 targetPort 来将其公开在 443 端口。
kind: Service
apiVersion: v1
metadata:
name: my-service
spec:
selector:
app: MyApp
ports:
- protocol: TCP
port: 443
targetPort: 8443
在用户命名空间可用之前,或者在无 root 运行容器的持续工作在容器运行时中实现之前,以非 root 用户身份运行工作负载的情况不会改变。
9. 使用网络策略
默认情况下,Kubernetes 网络允许所有 Pod 到 Pod 的流量;这可以使用网络策略进行限制。

传统服务受到防火墙的限制,防火墙对每个服务使用静态 IP 和端口范围。由于这些 IP 很少更改,因此历史上一直被用作一种身份形式。容器很少有静态 IP——它们旨在快速失败、快速重新调度,并使用服务发现而不是静态 IP 地址。这些特性意味着防火墙的配置和审查变得更加困难。
由于 Kubernetes 将其所有系统状态存储在 etcd 中,因此如果 CNI 网络插件支持,它可以配置动态防火墙。Calico、Cilium、kube-router、Romana 和 Weave Net 都支持网络策略。
需要注意的是,这些策略是故障关闭的,因此此处缺少 podSelector 默认为通配符。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
spec:
podSelector:
这是一个网络策略示例,它拒绝除 UDP 53(DNS)之外的所有出口流量,这也阻止了传入应用程序的连接。网络策略是有状态的,因此对出站请求的回复仍然会到达应用程序。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: myapp-deny-external-egress
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Egress
egress:
- ports:
- port: 53
protocol: UDP
- to:
- namespaceSelector: {}
Kubernetes 网络策略不能应用于 DNS 名称。这是因为 DNS 可以轮询解析为多个 IP,或根据调用 IP 动态解析,因此网络策略只能应用于固定 IP 或 podSelector(用于动态 Kubernetes IP)。
最佳实践是首先拒绝命名空间的所有流量,然后逐步添加路由以允许应用程序通过其验收测试套件。这可能变得复杂,因此 ControlPlane 团队合力开发了 netassert——用于 DevSecOps 工作流的网络安全测试,具有高度并行化的 nmap。
k8s: # used for Kubernetes pods
deployment: # only deployments currently supported
test-frontend: # pod name, defaults to `default` namespace
test-microservice: 80 # `test-microservice` is the DNS name of the target service
test-database: -80 # `test-frontend` should not be able to access test-database’s port 80
169.254.169.254: -80, -443 # AWS metadata API
metadata.google.internal: -80, -443 # GCP metadata API
new-namespace:test-microservice: # `new-namespace` is the namespace name
test-database.new-namespace: 80 # longer DNS names can be used for other namespaces
test-frontend.default: 80
169.254.169.254: -80, -443 # AWS metadata API
metadata.google.internal: -80, -443 # GCP metadata API
云提供商元数据 API 是权限提升的持续来源(正如最近的 Shopify 漏洞赏金所证明的),因此专门的测试以确认 API 在容器网络上被阻止有助于防范意外的错误配置。
10. 扫描镜像并运行 IDS
Web 服务器向其所连接的网络提供攻击面:扫描镜像的已安装文件可确保不存在攻击者可能利用的已知漏洞,从而获得对容器的远程访问。IDS(入侵检测系统)会在发生攻击时检测到它们。
Kubernetes 允许 Pod 通过一系列准入控制器门进入集群,这些门应用于 Pod 和其他资源(如部署)。这些门可以验证每个 Pod 的准入或更改其内容,现在它们支持后端 webhook。

这些 webhook 可以被容器镜像扫描工具用来在镜像部署到集群之前验证镜像。未通过检查的镜像可以被拒绝准入。
扫描容器镜像是否存在已知漏洞可以缩短攻击者利用已披露 CVE 的时间窗口。CoreOS 的 Clair 和 Aqua 的 Micro Scanner 等免费工具应在部署管道中使用,以防止部署具有严重可利用漏洞的镜像。
像 Grafeas 这样的工具可以存储镜像元数据,以便针对容器的唯一签名(内容可寻址哈希)进行持续的合规性和漏洞检查。这意味着使用该哈希扫描容器镜像与扫描生产环境中部署的镜像相同,并且可以在不访问生产环境的情况下持续进行。
未知的零日漏洞将始终存在,因此应在 Kubernetes 中部署诸如 Twistlock、Aqua 和 Sysdig Secure 之类的入侵检测工具。IDS 检测容器中的异常行为并暂停或终止它——Sysdig 的 Falco 是一个开源规则引擎,也是这个生态系统的入口点。
第三部分:未来
安全“云原生演进”的下一阶段似乎是服务网格,尽管其普及可能需要时间——迁移涉及将复杂性从应用程序转移到网格基础设施,组织将热切希望了解最佳实践。

11. 运行服务网格
服务网格是加密的持久连接网络,通过高性能的“边车”代理服务器(如 Envoy 和 Linkerd)建立。它增加了流量管理、监控和策略——所有这些都无需更改微服务。
将微服务安全和网络代码卸载到一套共享的、经过实战检验的库中,这在使用 Linkerd 时已经可能实现,而 Google、IBM 和 Lyft 引入的 Istio 为这一领域提供了另一种选择。随着 SPIFFE 提供每个 Pod 的加密身份以及众多其他功能的加入,Istio 可以简化下一代网络安全的部署。
在“零信任”网络中,可能不需要传统的防火墙或 Kubernetes 网络策略,因为每次交互都通过 mTLS(相互 TLS)进行,确保双方不仅安全通信,而且知道两个服务的身份。
这种从传统网络到云原生安全原则的转变对于那些具有传统安全思维的人来说并非易事,强烈推荐阅读 SPIFFE 的Evan Gilman撰写的《零信任网络》一书,作为进入这个勇敢新世界的入门。
Istio 0.8 LTS 已经发布,项目正迅速接近 1.0 版本。其稳定性版本控制与 Kubernetes 模型相同:稳定的核心,各个 API 在各自的 alpha/beta 稳定性命名空间下标识自己。预计未来几个月 Istio 的采用率将有所上升。
结论
云原生应用程序拥有一套更细粒度、轻量级的安全原语来锁定工作负载和基础设施。这些工具的强大和灵活性既是福也是祸——在自动化不足的情况下,更容易暴露不安全的工作负载,从而允许从容器或其隔离模型中进行突破。
可用的防御工具比以往任何时候都多,但必须谨慎行事,以减少攻击面和错误配置的可能性。
然而,如果安全阻碍了组织的功能交付速度,那么它永远不会成为一流公民。将持续交付原则应用于软件供应链,可以使组织在不影响业务底线的情况下实现合规性、持续审计和强制治理。
在全面的测试套件支持下,快速迭代安全性最容易实现。这通过持续安全来实现——持续安全是传统渗透测试的替代方案,通过持续的管道验证确保组织的攻击面是已知的,并且风险得到持续理解和管理。
这是 ControlPlane 的工作方式:如果我们可以帮助您启动持续安全规范、提供 Kubernetes 安全和运营培训,或与您共同实施安全的云原生演进,请联系我们。
Andrew Martin 是 @controlplaneio 的联合创始人,并在 @sublimino 发布有关云原生安全的推文。