如何使用SAML设置Kubernetes SSO

2020-10-16 10:38:35

Kubernetes有一些令人印象深刻的基于角色的内置访问控制(RBAC)。这些控件允许管理员在查询Kubernetes资源(如Pods、Deployment、ReplicaSets等)时定义细微差别的权限。对于熟悉Kubernetes的人来说,RBAC的价值一目了然。单个Kubernetes集群可以包含您组织的整个CI/CD管道、高度可用的SaaS产品或正在迁移到云中的基础设施。对于事关重大的事情来说,薄弱的访问控制是不可能的。

授权只是战斗的一半。如果身份验证可以被欺骗,即使是最严格的访问策略也是过时的。因此,Kubernetes支持一系列常见的身份验证模式,例如静态令牌、客户端证书、OIDC和其他方法。有了所有可用的组件,安全访问应该很简单:(1)通过SSO请求访问群集,(2)让K8S API根据您的身份提供商(IdP)进行身份验证,以及(3)根据身份信息应用规则。瞧啊。

不幸的是,如果您选择的协议是SAML(许多企业组织都是这样),则此设置需要扩展。我们可以通过在用户和Kubernetes API之间放置一个代理来实现这一点,该代理可以读取SAML断言,并将属性转换为API构建用于读取和处理的格式。有三种现成文档的方法可以完成此任务,即身份验证代理、Web挂钩或用户模拟。这篇博客文章特别关注用户模拟。为什么?因为如果您使用的是托管的Kubernetes提供程序(由于其复杂性,许多人都在这么做),则用户无法控制设置WebHook或身份验证代理所需的Kube-apiserver标志。

单点登录(SSO)是跨多个不同系统联合身份的重要工具。SSO消除了为访问的每个应用程序创建一组新凭据的需要。取而代之的是,身份验证过程外包给OKTA、GSuite或Keyloak等身份提供商。这使组织可以在所有内部公司服务中实施一致的身份验证方法。

要为特定应用程序启用SSO,它必须支持身份验证协议。其中一个协议是安全断言标记语言(SAML)。这些协议允许身份提供者代表应用程序对用户进行身份验证,并将授权信息传递回应用程序。如果您不熟悉SAML,我建议您通读一下SAML 2.0身份验证的工作原理。

为了理解什么是模拟以及它为什么有用,让我们退一步,快速了解Kubernetes RBAC。RBAC API使用Role和ClusterRole对象定义一组权限。然后,这些角色分别使用RoleBindings和ClusterRoleBindings绑定到应用角色的帐户。请看这段代码,它将授予对所有Pod的只读访问权限。

#pod-read-role.yamrapiVersion:rbac.authization.k8s.io/v1Kind:Rolemetadata:命名空间:默认名称:pod-readerules:-apiGroups:[";";]资源:[";pods";]谓词:[";get";,";watch";,";list";]。

#pod-read-jane-binding.yamrapiVersion:rbac.authization.k8s.io/v1Kind:RoleBindingMetadata:Name:Read-Pods命名空间:defaultSubjects:-Kind:用户名:Jane apiGroup:rbac.authization.k8s.ioroleRef:Kind:角色名称:Pod-Reader apiGroup:rbac.authization.k8s.io

在Jane根据核心API对自己进行身份验证之后,RBAC层将把Jane映射到Pod-Reader角色,并设置她的权限。但是由于Kubernetes不支持SAML作为身份验证协议,Jane无法通过第一步。API将不知道为Jane或任何其他帐户分配什么角色。相反,我们需要一种扩展身份验证方法以包括SAML的方法。

诀窍是不让Jane直接使用API对自己进行身份验证,而是使用ServiceAccount作为代理标识,该标识将采用Jane的帐户属性。ServiceAccount仍然需要向API进行身份验证,但是因为它没有绑定到外部IDP,所以SAML根本不会进入等式。相反,ServiceAccount将直接使用Kubernetes API进行身份验证,并转换诸如name:Jane之类的特征。这种模拟方法不需要Kubernetes API直接对Jane进行身份验证,同时仍然允许RBAC API分配Pod-Reader角色。实际的模拟过程是通过在模拟代理和Kubernetes API之间传递HTTP头(如imperation-user和imperassate-Group)来完成的。

我们可以从头开始配置代理,创建一个ServiceAccount或普通用户并赋予其模拟权限。但这不会让我们将SAML属性映射到Kubernetes组,因为普通帐户无法将属性转换为组。取而代之的是,我们使用一种名为Teleport的开放源码软件(OSS)工具,出于稍后解释的原因,它为我们提供了这种能力。如果此映射对您不重要,那么本文的其余部分仍然相关,但是只需创建一个通用代理帐户,而不是使用Teleport代理。

远程端口由两个服务组成:代理服务和身份验证服务,代理服务接受来自客户端的凭据,身份验证服务颁发并验证这些凭据。虽然远程端口要多得多,但这个简化的描述就足够了。

上图显示了如何使用Teleport作为网关访问Kubernetes集群的一般实现。流程如下:

验证后,用户将收到由身份验证服务上托管的证书颁发机构生成的证书。

在用户请求时,代理将根据Kubernetes API进行身份验证,并模拟用户的请求。

在我们接触模拟之前,我们必须首先设置到代理的SAML连接器。嗯,真的,首先我们必须设置服务,但为了时间关系,让我们做一些假设:

Recall模拟使用帐户作为API之间的代理。这意味着通过SAML进行用户身份验证的过程与使用Kubernetes API进行代理身份验证是完全不同的过程。用户身份验证使用代理和身份验证服务,而不是API。使用一些技术术语,Teleport是我们的服务提供商,向我们选择的IDP请求身份验证和身份信息。

为此,我们必须将SAML连接器定义并创建为YAML文件。以下代码段基于图2构建,并将创建一个资源供Teleport使用,该资源包含Auth0 SAML端点和一个重定向回Teleport的ACS URL。

#saml-connector.yamlkind:saml版本:v2METADATA:名称:saml_connectorspec:显示:";授权";acs:https://teleport-proxy.acme.com:3080/v1/webapi/saml/acs属性_to_角色:-{名称:";组";,值:";管理员";,角色:[";根";]}。

现在,让我们忽略ATTRIBUTES_TO_ROLES。我稍后再谈这个问题。我们的集成看起来更像是:

要让Teleport模拟用户,需要授权它这样做。因此,正如Jane具有上面的Pod-Reader角色一样,我们必须创建一个类似的对象并将其绑定到代理。

ApiVersion:rbac.authization.k8s.io/v1Kind:ClusterRolemetadata:名称:模拟器规则:-apiGroups:[";,";]资源:[";Users";,";Groups";,";serviceAccounts";]谓词:[";imperassate";]-apiGroups:[";Authization.k8s.io";]资源:[";selfsubjectaccessReview";]谓词:[";create";]-apiGroups:[";authization.k8s.io";]资源:[";selfsubtaccessReview";]谓词:[";create";]。

因为Kubernetes API是RESTful编写的,所以它使用HTTP谓词来操作Kubernetes资源。但是有些谓词有特殊的含义,身份验证层使用“imperassate”谓词来允许HTTP标头前面加上imperassate-。上面的YAML文件允许ServiceAccount模拟用户、组、服务帐户。

我们完成ClusterRoleBinding,并指向RBAC层应该将集群角色应用到的帐户的API。

ApiVersion:rbac.authization.k8s.io/v1Kind:ClusterRoleBindingMetadata:Name:Teleport-proxyroleRef:apiGroup:rbac.authization.k8s.io Kind:ClusterRole Name:imperatorSubjects:-Kind:ServiceAccount#Teleport Proxy ServiceAccount Name:Teleport-serviceAccount Namespace:Default。

将模拟器集群角色绑定到telport-serviceaccount后,Kubernetes API可以在身份验证时验证Teleport是否具有模拟权限。

只需两步,我们就具备了通过SAML身份验证的用户访问Kubernetes资源所需的一切。结合上面配置的所有内容,让我们看看当Jane在她的命令行中执行kubectl get pods时会发生什么。

Jane必须首先向Teleport验证自己的身份,然后才能运行她的命令。考虑到这是Jane第一次访问Kubernetes API,她没有本地存储的凭据来自动验证她的身份。相反,Teleport从我们创建的连接器文件中提取并通过Web浏览器重定向她以进行SSO。

在通过用户名/通行证、mfa、yybikey等进行身份验证之后,Jane将重定向回https://teleport-proxy.acme.com:3080/v1/webapi/saml/acs并将她的SAML断言传递给远程端口。

成功地对自己进行身份验证后,身份验证服务将为Jane生成并签署一个新证书,其中包含用户身份、生存时间和角色等信息。该证书本地存储在她的客户端上,因此每当Jane查询Kubernetes资源时,她都可以传递凭据,而不必再次登录,直到证书过期。

在对自己进行身份验证并提供有效凭据后,代理现在可以模拟Jane。Telport-serviceaccount将通过HTTP将自己的凭据和模拟头部传递给Kubernetes API。换句话说,代理将发送其ServiceAccount令牌,并在HTTP标头中包含imperassate-user:jane。

Kubernetes API将首先验证telport-serviceaccount并检查模拟权限。由于我们已经创建了模拟器角色并将其绑定到帐户,因此Kubernetes RBAC层将允许telport-serviceaccount模拟用户Jane。然后,Kubernetes API将根据模拟的信息对动作进行授权。在本例中,Jane被绑定到Pod-Reader角色,并且将限制她对所有Pod的访问权限为只读。

您可能想知道为什么需要Teleport的证书颁发机构。这是一个很合理的问题。毕竟,我们到目前为止所做的一切都可以使用任何ServiceAccount来完成,如果SAML可以进行身份验证和授权,为什么我们还需要另一组凭据呢?虽然使用证书颁发机构的原因有很多,但必要的组件是跨多个不同类型的计算资源同步RBAC。换句话说,当在Auth0中分配管理员角色时,它还应该跨SSH、Kubernetes、数据库、应用程序等通知管理员角色。(阅读有关安全基础设施访问最佳实践的更多信息。)。对于像OIDC这样受支持的协议,通过使用标志--oidc-groups-Claim,Kubernetes API将作为用户组应用声明,为我们提供IdP和Kubernetes之间的RBAC奇偶校验。但是当XML文件断言:

API不会将SAML属性应用到已知的Kubernetes组,因为我们知道,SAML不受支持。因此,正如我们需要代理来模拟用户一样,我们也需要相同的代理来模拟从SAML属性派生的Kubernetes组。这就是第二组凭据的用武之地。Teleport将采用SAML属性,将它们映射到Teleport角色,然后将其映射到Kubernetes组,该组显示为imperassate-Group:标头的一部分。

回到SAML连接器,作为规范的一部分,我们忽略了ATTRIBUTES_TO_ROLES:{name:";groups";,value:";admins";,Roles:[";root";]}。此行允许我们将SAML属性映射到特定的远程端口角色。在本例中,SAML属性Groups:Admins被映射到Teleport根角色。我们还需要为root创建一个角色对象,这样才能完成SAML属性到Kubernetes组的映射:

KIND:ROLEVERSION:v3METADATA:NAME:ROOTSPEC:ALLOW:#如果未指定用户,则用户将模拟自己(Jane)Kubernetes_Groups:[";System:Master";]。

当Jane使用SAML登录Auth0时,她将被自动放入System:Master组。

在本文中,我们了解了如何使用模拟单点登录到Kubernetes集群来扩展对SAML的身份验证支持。简而言之,我们学会了:

所有这些步骤都允许我们使用来自Kubernetes API本地不支持的身份验证协议的信息来充分利用Kubernetes的RBAC层。