在当今的软件工程领域,微服务架构占主导地位。虽然这种基础设施方法有很多优点,但它已经形成了一个非常复杂的管理网络。IBM 确认了这一点,共享该应用程序包含“数十个、数百甚至数千个不同的、可独立部署和可更新的服务”。除保持服务可靠性外,管理员还必须有效地管理数百个甚至数千个用户的权限。
这就是说,在用户访问特定服务之前,后端必须对其进行身份验证和授权。关键是,用户实际上是以自己的身份登录的,并且在此之后拥有执行特定操作所需的权限。决定权限、基于角色的访问控制(role-based access control,RBAC)以及其他控制已经变得非常棘手。另外,你管理服务相关权限的粒度是可变的。
为保证长期安全性、服务可用性和微服务可扩展性,设计清晰的用户权限策略是必不可少的。你无法使用“一扇摇摆的门”来保护你的 API 端点。在会话过程中控制用户看到和执行的操作是应用程序管理的基础。
评估标准
本文介绍了微服务中一些有用的用户权限策略,并对其进行了分解。这样做可以帮助你了解哪些策略最适合你的组织的服务。对于每个权限策略,我们将基于以下要点评估:
易用性:这种方法对用户的友好程度如何?每天使用基本功能有多简单?
可维护性:管理员如何能够在需要扩展或更改之后,快速地更改权限、组和结构?
稳定性:解决方案对于非预期故障的弹性有多大,以及其背后的 API 或机制是否能够长期有效地发挥作用?
这些标准构成了衡量策略有效性的强大基线。
权限策略简介
对你的权限进行管理时,有两种主要途径,每一种都有其细微差别。首先,你可以选择自己管理所有的权限,换句话说,你不需要依靠集中的第三方工具。这就是所谓的细粒度方法。采用这种方法的团队通常想要对他们的部署有不受约束的控制。
无需深入研究策略(稍后将详细介绍),可以根据用户和应用程序之间的关系、会话设计、服务器安排和 API 依赖关系等因素选择许多子方法。
相反,还有一种集中式服务的方法。一般情况下,这取决于对外部工具的集成和第三方服务的集成,这些服务在一定程度上保留了你的后端访问的权限。这样,你就可以使用统一的界面(包括命令行界面)来管理权限和角色。通常,对更改实际服务代码的依赖较少;你的重点是配置。
每一个策略都各有利弊。用例也随基础设施的不同而不同。在这种情况下,让我们深入研究。
策略 1:管理自己的权限
免责声明:自我管理你的权限可能是一个费力的过程。但是,还需要做一些权宜之计,根据特定服务,对权限进行粒度控制。因为你将承担大部分设置工作,所以理解自我管理微服务架构所带来的挑战是值得的。
请记住,用户的权限首先与身份验证和授权密切相关。你的权限设置直接影响用户会话从登陆到注销的过程。如果将这些行为扩展到多个用户账户,则会出现一些明显的障碍:
一种应用程序将多个微服务流程统一使用,因此,有必要在权限方面对这些服务进行隔离。必须在后端分别处理用户对每个服务的访问请求。
你必须协调服务级身份验证和授权逻辑与全局逻辑之间的关系;尽管代码重用是可能的,但是它会产生依赖性,最终会阻碍系统的灵活性。
微服务必须处理其自身的业务逻辑,并且维护全局权限逻辑存在的单一责任。
HTTP 的无状态设计非常适合于服务器和节点的负载平衡,但是为了与有状态登录会话兼容,所需的漏洞会降低服务的可扩展性。
身份验证和授权本质上变得更加复杂,因为支持应用程序功能的交互是多样的。
任何自治权限策略的前提是进行创造性地避开这些限制。有多种方法可以做到这一点,我们现在来探讨一下。
无状态会话管理
微服务通常最好是保持无状态。多个容器都为共享的资源池而竞争,尤其是当服务经历了大量的用户活动时。这些使用高峰将产生大量内存需求;因此,无状态请求所需的内存开销要小得多。这些新事物是轻量级和敏捷的。服务器还可以并行处理大量请求,提高了稳定性和性能。这在多个用户同时登陆并访问资源时非常重要。这对所有用户来说都是相同的。
这就是说,我们可以通过三种方式将无状态方法的好处与会话结合起来。
第一种是通过使用称为粘性会话(sticky session)的方法,在这个方法中,服务器会处理用户最初请求,从而 ping 任何后续请求。负载均衡器对允许这种情况的发生至关重要,但只允许水平向扩展集群。但是,这种添加机器的实例(而不是通过升级当前机器来向上扩展)更可取,因为它允许成百上千的并发用户会话。当然,这些会话需要基于它们的行为进行身份验证和重复授权。切记,在这种情况下,强制的服务器交换将转储用户会话数据——要求重新登陆并访问授权服务器。
第二种是会话复制(session replication),它是在网络上保存用户会话数据并同步的。对用户数据的任何更改将自动推送到所有机器。所以,这种处理用户的方法会消耗更多的资源,通常采用带宽形式。这里有很好的一致性;服务器不会忘记用户的权限,或者忘记一个账户的后端关联。遗憾的是,可扩展性相对有限。
第三种是集中式会话存储(centralized session storage),它将用户凭证和相关数据放在一个共享位置。登录状态保持不透明,这意味着服务器不会将凭证解释为纯文本。这样做对安全性来说是很好的。尽管可以扩展,但是这种方法需要共享存储的保护机制。
客户端令牌
令牌可以帮助微服务及其服务器之间的无状态 - 有状态冲突。令牌并非将用户会话存储在服务器上,而是作为用户身份细节的存储容器。这在利用 cookie 的基于 Web 的服务中最为常见。用户将其令牌和会话信息保存在设备上。
通过使用这个令牌来确认所有用户对服务器的请求,然后决定每个用户的权限如何配合。这样,用户就可以看到、交互甚至修改哪些内容。不透明令牌是在某些情况下使用的专门令牌;对于 OAuth 来说,这些令牌是专有的,并且不可访问,同时指向服务器上持久存储的信息。OAuth 是一家流行的身份验证服务供应商,它提供了管理 API 和自定义 API 访问令牌。
此外,JSON Web 令牌(JWT)是一种流行的令牌格式,它是标准化的,并且基于三个元素构建。头部包含了类型和哈希算法(因为令牌必须加密)。有效负载包含用户 ID、用户名和到期日期。它还可以包含角色。最后,签名将验证令牌身份并为客户端提供验证。令牌通常会在短时间后刷新来保持安全性,以防攻击者窃取它们。
令牌化过程如下:
发出初始化登录的 API 请求。
服务器创建令牌。
令牌返回到存储它的客户端浏览器。
当执行操作时,用户通过头部将令牌发送给服务器。
验证签名,并传唤用户信息。
向客户端发送适当的响应。
令牌化还可以与 API 网关配对。请求并不直接进入服务器,而是通过中间网关审查操作并将其传递。微服务不会向用户公开,不管是检测出问题还是用户注销,网关可以撤销令牌。在客户端隐藏了授权令牌,因此无法如此轻松地解密。
单点登录
单点登录(Single sign-on,SSO)可能是最简化的访问管理方法,因为它允许用户的登录验证(身份验证步骤)在一系列捆绑的服务中对同一个用户进行认证。目前,应用程序通常在其功能范围内承载多个服务。逐一登录所有的服务,对用户来说是非常乏味的。对所有服务的访问都通过一个集中的认证服务进行路由。尽管在很多方面都很方便,但是这种方法很容易出错或网络流量激增。
互助式身份验证
当服务处于活动状态时,微服务通常彼此通信。这样,互联的微服务就会在用户开展业务时共享大量流量。由于每个服务唯一的身份验证证书是通过 TLS 交换的,所以双向 SSL 连接允许相互身份验证。应用实例不断变化的的性质会对这个身份验证过程造成麻烦,但是也有一些好消息:私有证书中心可以帮助确定如何为所有适用的服务进行颁发、撤销和更新证书。
忠告
所有这些选项中的共同缺点是易用性。每一种选项都有一定的取舍,并需要一定程度的手工设置才能成功。虽然内置的自动化可以间接地或直接简化权限处理过程,但在你的团队中需要特定的专业知识。合理地执行这些策略需要熟练掌握 API、安全性和微服务架构。经验不足的人更容易犯错,而这些错误会破坏权限处理。
诸如基于角色的访问控制和基于属性的访问控制(attribute-based access control,ABAC)这样的机制,都是活的概念,需要在服务的生命周期内持续地维护。很多情况下,自我管理的身份验证和授权都可能会出问题。以不可见的方式实施(就用户而言),也是系统安全的核心。糟糕的编码解决方案可能会提供过多关于后端权限结构的信息。
开源资源能否缓解开发之痛?尽管在很多情况下答案是肯定的,但事实是,成功的整合仍然是一个挑战。文档并非“百发百中”,跨语言的逻辑共享令人怀疑,而编码工作可能很大。
身份验证后授权用户
当你的服务确定你(或你的用户)是谁之后,它们将决定在应用程序中实际可以做什么。可以单独对每一个服务执行此操作,尽管这一过程需要一些时间,并且会带来潜在的问题。
尽管有利安全性和细粒度控制,但这要求为所有微服务重写安全逻辑。这样会导致臃肿的重复构建。这也会将每个服务与它没有的外部授权数据连接起来。最终,随着服务规模的扩展,这些零碎的设计在监控上变得更加复杂。
为了避开这些核心问题,团队通常会采用一个与每个服务共享的身份验证库的形式。他们还可以建立起将授权和身份验证连接起来的全局服务——通常是通过授权数据库。不幸的是,后一种方法使用全局服务来处理单个用户角色。这些检查会引起安全问题,并且会增加延迟。
有没有更好的办法处理授权呢?很多人认为,集中式的服务对监督他们的团队很有效,而且不需要太多的维护。这样就避免了传统 API 网关和单端点之间的强耦合问题。现有的第三方服务显然简化了这些步骤。
策略 2:使用集中式服务
虽然单个管理可能很复杂,但是集中式的方法可以提供你急需的简单性。这种策略使用一个中央微服务,将部署到现有应用程序中。这种形式一般采用补充式容器。
一些第三方服务,比如 Cerbos,甚至可以作为 sidecar 来运行:它与相关的应用程序松散耦合,同时以锁步方式完成基于权限的工作。这些 sidecar 很有用,因为它们是基于现有服务进行扩展的。
提出授权和身份验证请求的所有服务都是通过这种专门的权限微服务进行路由的。该响应返回到客户端,以确定其请求是否成功。这个集中的部分强制执行了所有基于权限的决定。但是,如果同事运行多个节点,并且其中一个节点被分区,就可能会出现问题。
假定节点与其他系统节点有效分离。在集中式设置中,这个节点无法接受外部服务的任何权限决定。可能会失败关闭——拒绝所有的身份验证请求,或者失败开放。后者是非常有问题的,因为所有的身份验证请求都被批准。失败关闭会引起连锁反应,即所有客户对分区服务的请求都被拒绝。
在考虑像 Cerbos 这样的东西时,值得庆幸的是,情况会变得更清晰一些。对于非开发者来说,这个解决方案非常友好,因为它不需要了解任何规则和语言。权限逻辑的更改会自动推送到你的基础设施的每个角落,从而节省时间和精力。这个解决方案没有任何依赖性,仍然包含在本地网络中。像这样的集中式工具甚至可以在促进 GitOps 的同时确定角色。
集中式的服务在让事情变得简单时很有用。它们是微服务权限规则的单一真相来源,运行时不会使原本错综复杂的系统复杂化。现代衍生产品被设计用于容器化环境,并利用流行的 API 协议来有效地运行。
使用集中式服务会使没有深厚权限安全知识的小团队受益良多。尽管配置仍然很强大,但是在执行身份验证和授权时,需要做的工作更少。如果你在整个部署过程中使用多个框架或语言,那么集中式解决方案将使你能够快速实施你所需的不可知性。其中甚至包括了一些非常有用的测试,因为权限和微服务会随着时间而变化。
维护成本自然也降低了。因为中央服务可以向所有服务推送更改,你不必花费开发资源来分别更新每个服务。运行大量服务的组织可以从这一事实中得到一些安慰。
结论
在自我管理与集中化的较量中,选出一个赢家并不是那么非黑即白。一个团队对其基础设施的舒适程度、某些技术和预算将决定适当的行动方案。即使是像 OAuth + JWT 这样的流行设置,也是安全和可靠的,但它们并非万无一失。一个集中式服务可以为让团队在不太挑剔或者打扰的情况下检查很多功能框。为微服务设计强大的权限策略,通常是公司能采取的最困难的安全措施。这样,集中化可以让不是用普通的无状态方法的团队更容易地做到这一点。
原文链接:
https://cerbos.dev/blog/comparing-microservice-permissions-strategies
评论