我们作为链家网的基础架构团队,提供了许多服务给业务线使用。在这个过程中,我们逐渐发现,(业务)服务和(基础)服务之间,“资源隔离”是初期强需求,“权限管理”是远期强需求。
当前,我们有的系统通过客户端传递“用户名/密码”,隐式地将数据隔离在当前用户这个 namespace 下;有的系统使用 AppId 和 AppKey 的方式标明用户,在数据库中更为灵活地记录权限信息。
我们除了作为服务的提供方,还往往需要充当系统管理员的角色:我们通过代码/后台/数据库等方式配置管理权限。这类琐碎的需求隔三岔五出现,可能需要我们线下测试、线上修改,打断了原本的开发节奏;用户可能需要较长时间的等待,压低了预期。
随着提供服务的增多,每个系统都可能需要开发一套用户/资源/权限的子系统,这越来越成为一种负担,拖慢了上层业务的开发,且带来了不一致的用户体验。我们在思考,有没有一个通用服务,覆盖那部分通用性强的,业务无关的权限管理?它只需要很小的成本接入,就能够提供统一的身份识别和方便的权限管理。
实际上,SaaS 平台上早有了成熟的解决方案,它就是 Identity and Access Management 系统。在链家,我们同时使用了自建 IDC 和公有云服务 Amazon Web Service。我们期待自己的服务,也能集成类似 AWS IAM这样的系统。
介绍 AWS IAM
用户体系
在 AWS IAM 中,用户分为四类实体:
Account(Root User)
帐号对拥有的资源,有所有权限
帐号可分发权限给用户,或赋予用户权限代为管理
资源隔离在帐户下,不可跨帐号使用
User
帐号下的权限隔离
推荐日常使用
Group
用户可加入到用户组
方便权限的聚合管理
Role
方便第三方跨帐户使用
用户可 AssumeRole 换取临时 AK/SK,切换到对应角色
其中,帐号和用户可以有访问凭证,分别是:
用户名/密码,后台访问
AccessKeyId/SecrectAccessKey, 程序使用 HTTP API 访问
认证流程
对于用户请求的身份识别,通常有两种方案:
基于 Session,Session 过期后重新登录。
对每次请求加密,一次一密。
AWS IAM 选用了方案二,后端无状态,请求更安全。
用户在程序请求时,使用申请好的 AccessKeyId 和 SecrectAccessKey,针对 Request 的当前时间,Header 字段, URL Query 参数,Body Digest 等元素,按照一定规则拼接字符串,生成签名。
Server 校验签名,确认当前用户,判断是否有对应权限,记录请求以供审计,完成请求返回结果。
策略文档
AWS IAM 使用了 JSON 文档的方式描述策略 Policy,并提供了策略编辑器工具帮助不熟悉的用户撰写文档。
我认为这是很棒的设计:
JSON 是程序员们广泛接受,能够手写的格式
JSON 支持 Array, Map 等复杂类型,方便聚合权限
官方示例 Policy 如下:
Policy 基于字符串的匹配,加上 Allow/Deny Effect 的组合,更通用、易理解的变量名称,和外部系统深度集成的 Condition 条件语句,灵活强大,自成体系。
最终,Policy 通过内嵌(Inline Policy)或附加(Managed Policy)的方式,挂接到帐户里的用户、用户组和/或角色,完成了权限的分发。
整个解决方案,用户使用简洁灵活,可以从策略文档,挂接方式,用户类型上管控;服务提供方只需要暴露出全局性的 Resource 和梳理提供的 Action 即可。用户通过一套机制,完成了对 AWS 所有服务的对接,在 IAM 后台完成了绝大部分权限管理的工作(比如,S3 除了支持 IAM 基于访问用户的权限,还提供了基于对象的管理)
自研
AWS IAM 是个私有服务,我们只能感知客户端 SDK 和后台 Console 的行为动作,S3/EC2 之类的服务和 IAM 之间交互是透明的,无法感知的。我们翻出了私有云的实现 - OpenStack KeyStone,看看是否不用重复造轮子。
在调研阶段,我们发现,KeyStone 只是一个 Identity 服务,只实现了身份的识别,权限信息仍保留在各自的 Service 上。并且,KeyStone 是基于 Role 的设计,即便是一个细微的权限问题,我们也要先建一个 Role 与之对应,然后绑定用户,异常笨重。由于从一开始就存在较大的阻抗匹配,我们放弃了基于 KeyStone 的二次开发方案,使用 Golang 从头开发了自己的实现:OpenIAM。
在决定自研后,我们投入了两个人力(其中一个是没有写过 Golang,帅帅的大三实习生,彬彬同学),在一个多月的时间内,完成了一期目标,完全兼容了 AWS CLI,用 Vue.js 复刻了 AWS 的 Admin Console。
我们除了提供 JSON/XML 的 HTTP 接口供用户日常管理之外,还提供了高性能的 gRPC 接口供服务提供方接入。
我们设定,服务提供方不应知道 SecrectAccessKey 和 Policy 信息。它接到业务请求,只需抽取 AccessKeyId、签名和签名所用字符串,申明签名方式(如 HMac 哈希,Base64 摘要等),再加上服务自身感知到的 Resource 和 Action,一起交由 OpenIAM 完成认证和鉴权两步操作。我们使用这种方式,打通了业务/服务/OpenIAM 三者之间的闭环。
整体的流程如下图:
OpenIAM 在授权认证时的简化活动图:
那么,在 OpenIAM 服务上线后,我们预期会带来哪些改变呢?
服务提供方仅需告知 OpenIAM 创建资源的帐号;后期鉴权均由 OpenIAM 完成,对提供方透明。
资源的拥有者,直接在 OpenIAM 上管理权限;服务提供方无权管理。
客户端逐步统一访问方式,降低接入成本,一套 AccessKeyId/ScrectAccessKey 即可跨多服务使用。
OpenIAM 可整合接入服务的服务后台,提供一致的使用体验。
使用场景
我们可以看到,IAM 解决的是服务和服务间的身份认证/权限管理。
在 AWS 里面所有服务都是基础服务,均有较强的资源概念。在公司里,存在更多的业务系统,没有清晰的资源概念,OpenIAM 能够适配这种情况吗?
例如,一个 Restful 的 API 接口,提供了对业务实体的 CRUD 操作。我们可以将每一个业务实体当成一个资源,将 GET/POST/PUT/DELETE 映射成 Action;服务提供方在 Resource 定位符中,直接告知 OpenIAM 资源的拥有者,OpenIAM 即可在此帐号下完成对业务实体的权限认证。
问题二,服务有自己的用户权限体系,用户请求可能涉及到第三方服务的权限问题。用户权限跟 IAM 权限有什么关系,该如何划分边界?事实上,OpenIAM 不关注用户权限信息。业务系统可以有如下两种选择:
1.一般情况下,业务系统的 AK/SK 拥有对第三方服务所需的所有权限,普通用户的权限由业务系统自行判定。
2.业务系统可以预设几个 IAM 子用户,将应用级别的用户映射到不同的 IAM 用户,不同 IAM 用户拥护不同的第三方服务权限。用户请求时,将请求转用对应的 AK/SK 加密,由 OpenIAM 决策允许或拒绝。
总结
在微服务日益火爆的今天,服务的数量会积极膨胀。OpenIAM 希望能解决服务和服务间的资源隔离问题。我们期待能跟大家更深入地分享 OpenIAM 设计与实现,如果感兴趣请留言
本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。
原文链接:
https://mp.weixin.qq.com/s/eY9HNeF4zroaOpU3oE8fKQ
评论