这是题为《保护Kubernetes for Cloud Native Applications》系列文章的倒数第二篇,延续我们关于如何保护集群重要组件的讨论,如API server和 Kubelet。 在本文中,我们将使用集群的一些固有安全机制来解决如何应用安全控制的最佳实践问题。如果将Kubernetes比作内核,那么我们将讨论保护用户空间,位于内核之上的层,也就是我们的工作负载运行的地方。让我们从身份验证开始。
X.509 Certificates
X.509证书是使用TLS加密与API服务器的任何客户端通信的必需组件。X.509证书也可以用作使用API服务器进行身份验证的方法之一,其中客户端的身份在证书的属性中提供:公共名称(Common Name)提供用户名,而可变数量的组织属性提供身份属于哪个组。
X.509证书是经过验证的身份验证方法,但在Kubernetes的上下文中有一些限制:
如果身份不再有效(可能是个人已离开您的组织),则可能需要撤销与该身份相关联的证书。目前,Kubernetes无法通过证书吊销列表(CRL)或使用在线证书状态协议(OSCP)的响应来查询证书的有效性。有几种方法可以解决这个问题(例如,重新创建CA并重新发出每个客户端证书),或者可能会认为依赖于授权步骤就足以拒绝已经使用已撤销证书进行身份验证的常规用户的访问权限。这意味着我们应该谨慎选择证书的Organization属性中的组。 如果我们无法撤销的证书包含一个具有无法删除的关联默认绑定的组(例如,system:masters),那么我们就不能依赖授权步骤来阻止访问。
如果要管理大量身份,则颁发和轮换证书的任务变得繁重。在这种情况下,除非涉及一定程度的自动化,否则开销可能变得过高。
另一种日益流行的客户端身份验证方法是利用内置的Kubernetes对OpenID Connect(OIDC)的支持,以及由外部身份提供商提供的身份验证。OpenID Connect是一个位于OAuth 2.0之上的身份验证层,它使用JSON Web令牌(JWT)对用户的身份及其声明进行编码。身份提供者提供的ID令牌(存储为用户的kubeconfig的一部分)在每次用户尝试API请求时作为承载令牌提供。由于ID令牌无法撤销,因此它们的生命周期往往较短,这意味着它们只能在有效期内用于身份验证。通常,还会向用户发放刷新令牌——可以与ID令牌一起保存 - 并用于在到期时获取新的ID令牌。
正如我们可以将用户名及其关联组体现为X.509证书的属性一样,我们也可以使用JWT ID令牌完全相同。这些属性与令牌中体现的身份声明相关联,并使用kube-apiserver的配置选项进行映射。
Kubernetes可以配置为使用几种流行的OIDC身份提供商中的任何一种,例如Google Identity Platform和Azure Active Directory。但是,如果您的组织使用目录服务(例如LDAP)来保存用户身份,会发生什么?一种基于OIDC的解决方案,支持LDAP身份验证,是一种开源的Dex身份服务,通过“连接器”充当许多类型的身份提供者的身份验证中介。除了LDAP之外,Dex还为使用OAuth的GitHub,GitLab和Microsoft帐户提供连接器。 (如果你想深入快速学习Kubernetes, 可以报名参加我们组织的为期3天的Kubernetes实战培训 ,一线资深讲师带你从0开始上手Kubernetes。)
虽然可以将授权推迟到外部授权机制,但Kubernetes的事实上的标准授权模式是内置的基于角色的访问控制(RBAC)模块。由于大多数预打包的应用程序清单都预先定义了RBAC角色和绑定,所以除非有充分的理由使用替代方法,否则RBAC应该是授权API请求的首选方法。
RBAC通过定义角色来实现,然后角色使用“角色绑定(role bindings)”绑定到主题。让我们对这些术语进行一些解释。
Roles(角色)——定义可以对哪些对象执行哪些操作。 该角色可以限制在特定的命名空间中,在这种情况下,它可以在Role对象中定义,也可以是在ClusterRole对象中定义的集群范围的角色。在以下示例集群范围角色中,绑定到角色的主体可以对“pods”和“pods/log”资源对象执行get和list操作——不多也不少:
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list"]
如果这是一个命名空间角色,那么对象类型将是Role而不是ClusterRole,并且元数据部分中将存在具有关联值的命名空间键。
Role Bindings(角色绑定)——将角色绑定到一组主题。 RoleBinding对象将Role或ClusterRole绑定到特定命名空间范围内的主题,而ClusterRoleBinding将ClusterRole绑定到集群范围内的主题。
Subjects(主题)——用户和组(由合适的身份验证方案提供),以及服务帐户(Service Account),它们是用于提供需要访问Kubernetes API的Pod的API对象,具有标识。
在考虑应该在角色中定义的访问级别时,始终遵循最小特权原则。换句话说,只提供具有实现其目的绝对必要的访问权的角色。从实际角度来看,在创建新角色的定义时,更容易从现有角色(例如,编辑角色)开始,并删除所有不需要的角色。如果您发现配置过于严格,并且需要确定哪些角色需要为特定操作或一组操作创建,则可以使用audit2rbac[1],它将根据从API服务器观察到的审核日志自动生成必要的角色和角色绑定。
在通过服务帐户为Pod运行的应用程序提供API访问时,可能很容易将新角色绑定到为每个命名空间创建的默认服务帐户,该帐户可供命名空间中的每个Pod使用。我们应该为需要API访问的Pod创建特定服务帐户和角色,然后将该角色绑定到新的服务帐户。
显然,仔细考虑谁需要访问API服务器,API的哪些部分以及它们可以通过API执行哪些操作,对于维护安全的Kubernetes集群至关重要。给它时间和应有的关注,如果你需要一些额外的帮助,Giant Swarm有一些你可能会觉得有用的深入文档[2]。
Pod安全策略
Pod安全策略定义了Pod传递许可所需的安全配置,允许在集群中创建或更新它们。 控制器将Pod的已定义安全上下文与Pod的创建者(部署或用户)允许“使用”的任何策略进行比较,并且在安全上下文超出策略的情况下,它将拒绝创建或更新Pod。该策略还可用于通过定义最小限制策略来提供默认值,该策略可绑定到非常通用的授权组,例如system:authenticated(适用于所有经过身份验证的用户),以限制这些用户具有的访问权限到API服务器。
Pod安全域
可以在PodSecurityPolicy(PSP)对象中定义相当多的可配置安全选项,您选择定义的策略将非常依赖于工作负载的性质和组织的安全状态。以下是API对象的一些示例字段:
privileged:指定Pod是否可以在特权模式下运行,允许它访问主机的设备,这在正常情况下无法执行。
allowedHostPaths:在主机上提供文件系统路径的白名单,Pod可以将其用作hostPath卷。
runAsUser:允许控制运行pod容器的UID。
allowedCapabilities:将提供的功能列入白名单,这些功能可以添加到提供给Pod容器的默认列表之上。
利用Pod安全策略
启用PodSecurityPolicy许可控制器时的警告: 除非已在PSP中定义了策略,否则将无法创建容器,因为许可控制器(admission controller)的默认行为是拒绝创建不存在与策略匹配的容器——无策略,不匹配。 Pod安全策略API是独立于许可控制器(admission controller)启用的,因此完全可以在启用它之前定义策略。
值得指出的是,与RBAC不同,预先打包的应用程序很少在其清单中包含PSP,这意味着这些应用程序的用户需要创建必要的策略。
一旦定义了PSP,它们就不能用于验证Pod,除非创建Pod的用户或与Pod关联的服务帐户有权使用该策略。权限授权通常都是通过RBAC来实现的,通过定义允许使用特定PSP的角色以及将角色绑定到用户和/或服务帐户的角色绑定来实现授予权限。
从实际角度来看——特别是在生产环境中,用户不太可能直接创建Pod。 作为更高级别工作负载抽象的一部分(例如部署Deployment),通常会创建Pod,因此,它是与Pod关联的服务帐户,需要角色才能使用任何给定的PSP。
Giant Swarm的文档[2]再次提供了一些关于使用PSP为应用程序提供特权访问的深刻解释。
当然,确保这一点的一种方法是将敏感应用程序隔离到其自己的集群中。 在单独的集群中运行应用程序可确保最高程度地隔离应用程序。但是,有时候,这种程度的隔离可能不是绝对必要的,而且我们可以使用Kubernetes中提供的一些内置隔离功能。 我们来看看这些内置隔离功能。
命名空间
命名空间是Kubernetes中的一种机制,用于为您可能认为相关的所有对象提供不同的环境,并且需要与其他不相关的对象分开。 它们提供了分区工作负载,团队,环境,客户以及您认为值得隔离的任何事物的方法。
通常,创建Kubernetes集群时默认初始化了三个默认命名空间:
kube-system:用于由Kubernetes自己创建的对象。
kube-public:用于公开可用的可读对象。
default:用于在没有与特定命名空间显式关联的情况下创建的所有对象。
采用命名空间来隔离工作负载也有助于管理集群资源的使用。如果我们将集群视为隔离为不同命名空间的共享计算资源,则可以在每个命名空间的基础上应用资源配额。Resource Hungry和明智地通过命名空间来隔离关键工作负载可以最大化利用共享资源。
Kubernetes NetworkPolicy API使我们能够将入口和出口规则应用于选定的Pod,用于第3层和第4层流量,并依赖于实现容器网络接口(CNI)的兼容网络插件的部署。并非所有Kubernetes网络插件都支持网络策略,但流行的选择(如Calico,Weave Net和Romana)都可以。
网络策略是名称空间作用域,并根据匹配标签(例如,tier:backend)的选择应用于Pod。当NetworkPolicy对象的Pod选择器与Pod匹配时,根据策略中定义的入口和出口规则来管理进出Pod的流量。 所有来自或发往该Pod的流量都会被拒绝,除非有允许它的规则。
要在Kubernetes集群中正确隔离网络和传输层的应用程序,网络策略应以“拒绝所有”的默认前提开始。然后,应将每个应用程序组件及其所需源和目标的规则逐个列入白名单,并进行测试以确保流量模式按预期工作。
帮助我们实现这一目标的解决方案依赖于应用于网络堆栈5-7层的策略,并且是云原生应用程序的开发功能。Istio就是这样一种工具,其目的是将应用程序工作负载管理为服务网格,包括: 高级流量管理和服务可观察性,以及基于策略的身份验证和授权。Istio将一个sidecar容器部署到每个Pod舱中,该Pod基于Envoy反向代理。Sidecar容器形成网格,并且考虑到定义的流量规则和安全策略,以及来自不同服务的Pod之间的代理流量。
Istio的服务到服务通信的认证机制基于双向TLS,服务实体的身份体现在X.509证书中。这些身份符合每个人的安全生产身份框架(SPIFFE)规范,该规范旨在提供向工作负载发布身份的标准。 SPIFFE是由Cloud Native Computing Foundation(CNCF)托管的项目。
Istio具有极其强大的功能,但是它的功能套件并非都是必需的,部署带来的运营开销和维护可能会超过它所带来的好处。基于SPIFFE的经过身份验证的服务标识的替代解决方案是SPIRE,一组用于创建和发布标识的开源工具。
另一个用于保护Kubernetes集群中服务之间通信的解决方案是开源Cilium项目,该项目使用Linux内核中的伯克利数据包过滤器(BPF)来为第7层流量实施定义的安全策略。 除了HTTP之外,Cilium还支持其他第7层协议,如Kafka和gRPC。
然而,这不仅仅是关于定义策略和规则。同样重要的是确保以及满足组织更广泛的安全目标,您的安全配置支持着您的团队的组织方式以及他们的工作方式。这需要仔细考虑和规划。
在本系列的下一篇也就是最后一篇文章《管理Kubernetes容器工作负载的安全性》中,我们将讨论与容器工作负载内容相关的安全性,以及如何将安全性作为端到端工作流的一部分。
相关链接:
https://github.com/liggitt/audit2rbac
https://docs.giantswarm.io/guides/securing-with-rbac-and-psp
原文链接:https://blog.giantswarm.io/applying-best-practice-security-controls-to-a-kubernetes-cluster/
![640?wx_fmt=jpeg](https://i-blog.csdnimg.cn/blog_migrate/09b0ea9d5ad6bcb03c3a10c861b93912.jpeg)