本文发布已超过一年。较旧的文章可能包含过时的内容。请检查页面上的信息自发布以来是否已变得不正确。
11 种(不)被黑的方式
自该项目启动以来,Kubernetes 的安全性已经取得了长足的进步,但仍然存在一些陷阱。从控制平面开始,到工作负载和网络安全,最后展望未来的安全,这里列出了一些方便的技巧,以帮助您加强集群并提高其在被入侵时的弹性。
第一部分:控制平面
控制平面是 Kubernetes 的大脑。它可以全面了解集群上运行的每个容器和 pod,可以调度新的 pod(其中可能包括对其父节点具有 root 访问权限的容器),并且可以读取存储在集群中的所有密钥。这些有价值的货物需要保护,防止意外泄漏和恶意意图:在访问时、静止时以及在网络上传输时。
1. везде 都使用 TLS
应为支持它的每个组件启用 TLS,以防止流量嗅探,验证服务器的身份,并(对于相互 TLS)验证客户端的身份。
请注意,某些组件和安装方法可能会在 HTTP 上启用本地端口,管理员应熟悉每个组件的设置,以识别可能不安全的流量。
Lucas Käldström 的此网络图演示了理想情况下应应用 TLS 的一些位置:主服务器上的每个组件之间,以及 Kubelet 和 API 服务器之间。 Kelsey Hightower 的规范 Kubernetes The Hard Way 提供了详细的手动说明,etcd 的安全模型文档也是如此。

自动缩放 Kubernetes 节点在历史上很困难,因为每个节点都需要 TLS 密钥才能连接到主服务器,并且将密钥烘焙到基础镜像中不是好的做法。Kubelet TLS 自引导提供了新 kubelet 创建证书签名请求的能力,以便在启动时生成证书。

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

自 1.6 版本以来,Kubernetes 的 ABAC(基于属性的访问控制)已被 RBAC 取代,不应在 API 服务器上启用。请改用 RBAC
--authorization-mode=RBAC
或者使用此标志在 GKE 中禁用它
--no-enable-legacy-authorization
有许多 很好的示例,用于集群服务的 RBAC 策略,以及 文档。并且不仅如此 - 可以使用 audit2rbac 从审计日志中提取细粒度的 RBAC 策略。
如果 pod 被入侵,不正确或过度宽松的 RBAC 策略是一种安全威胁。维护最小权限,并持续审查和改进 RBAC 规则,应被视为团队在其开发生命周期中构建的“技术债务卫生”的一部分。
审计日志记录(在 1.10 中为 beta 版)在有效负载(例如,请求和响应)以及元数据级别提供可自定义的 API 日志记录。可以根据组织的安全策略调整日志级别 - GKE 提供了合理的默认设置来帮助您入门。
对于诸如 get、list 和 watch 之类的读取请求,仅将请求对象保存在审计日志中;不保存响应对象。对于涉及诸如 Secret 和 ConfigMap 之类的敏感数据的请求,仅导出元数据。对于所有其他请求,请求和响应对象都会保存在审计日志中。
别忘了:如果被入侵,将这些日志保留在集群内部是一种安全威胁。与所有其他安全敏感日志一样,这些日志应传输到集群外部,以防止在发生违规时被篡改。
3. 使用第三方身份验证 API 服务器
在整个组织中集中身份验证和授权(又名单点登录)有助于用户加入、退出以及一致的权限.
将 Kubernetes 与第三方身份验证提供程序(如 Google 或 GitHub)集成,使用远程平台的身份保证(以 2FA 之类的内容为后盾),并防止管理员必须重新配置 Kubernetes API 服务器以添加或删除用户。
Dex 是一个具有可插拔连接器的 OpenID Connect Identity (OIDC) 和 OAuth 2.0 提供程序。Pusher 使用 一些自定义工具 将其更进一步,并且还有一些具有略微不同用例的 其他 辅助程序可用。
4. 分离并防火墙您的 etcd 集群
etcd 存储有关状态和密钥的信息,并且是 Kubernetes 的关键组件 - 它应该与集群的其余部分分开保护。
对 API 服务器的 etcd 的写入访问权限等同于获得整个集群的 root 权限,即使是读取访问权限也可以用来相当容易地升级权限。
Kubernetes 调度程序将在 etcd 中搜索没有节点的 pod 定义。然后,它将找到的 pod 发送到可用的 kubelet 进行调度。在将提交的 pod 写入 etcd 之前,API 服务器会对它们进行验证,因此直接写入 etcd 的恶意用户可以绕过许多安全机制 - 例如,PodSecurityPolicies。
etcd 应配置 对等和客户端 TLS 证书,并部署在专用节点上。为了缓解从工作节点窃取和使用私钥的情况,还可以使用防火墙将集群与 API 服务器隔离。
5. 轮换加密密钥
一种安全最佳实践是定期轮换加密密钥和证书,以限制密钥泄露的“爆炸半径”。
Kubernetes 将通过在现有凭据过期时创建新的 CSR 来自动轮换一些证书(特别是,kubelet 客户端和服务器证书)。
但是,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、集合等)中,并且敏感的 configmap 和密钥应使用诸如 vault(带有 CoreOS 的 operator)、git-crypt、密封的密钥或 云提供商 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 之间的流量;可以使用 网络策略 来限制此流量。

传统的服务受到防火墙的限制,防火墙对每个服务使用静态 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 为此领域增加了一个替代方案。通过添加用于每个 Pod 加密身份的 SPIFFE 和大量的其他功能,Istio 可以简化下一代网络安全的部署。
在“零信任”网络中,可能不需要传统的防火墙或 Kubernetes 网络策略,因为每次交互都是通过 mTLS(相互 TLS)进行的,从而确保双方不仅安全地通信,而且还知道两个服务的身份。
这种从传统网络转向云原生安全原则的转变,对于那些具有传统安全思维的人来说,我们认为并非易事,而来自 SPIFFE 的 Evan Gilman 撰写的 零信任网络书是了解这个美好新世界的强烈推荐入门读物。
Istio 0.8 LTS 已经发布,该项目正迅速接近 1.0 版本。它的稳定性版本控制与 Kubernetes 模型相同:一个稳定的核心,每个 API 在其自己的 alpha/beta 稳定性命名空间下标识自己。预计未来几个月 Istio 的采用率将会上升。
结论
云原生应用程序拥有一组更细粒度的轻量级安全原语,可以锁定工作负载和基础设施。这些工具的强大功能和灵活性既是福也是祸 - 在自动化不足的情况下,更容易暴露不安全的工作负载,这些工作负载允许从容器或其隔离模型中逃脱。
现在可用的防御工具比以往任何时候都多,但必须谨慎以减少攻击面和错误配置的可能性。
但是,如果安全性减慢了组织交付功能的速度,那么它永远不会成为头等公民。将持续交付原则应用于软件供应链,可以让组织在不影响业务底线的情况下实现合规性、持续审计和强制治理。
当有全面的测试套件支持时,最容易快速迭代安全性。这是通过持续安全实现的 - 一种替代时间点的渗透测试的方法,通过持续的管道验证,确保组织了解攻击面,并不断理解和管理风险。
这是 ControlPlane 的工作模式:如果我们能够帮助启动持续安全规程,提供 Kubernetes 安全和操作培训,或为您共同实施安全的云原生演变,请联系我们。
Andrew Martin 是 @controlplaneio 的联合创始人,并在 @sublimino 上发布有关云原生安全性的推文。