本文要讨论的系统中的Secret信息,可以理解为在系统运行所需的配置项中,例如数据库连接用户/密钥、对接API服务所需的appId/appSecret、基本的Basic Auth信息等类似的敏感信息。在第一篇中将针对系统中使用Secret信息遇到的一些问题入手,探索一些对应的解决方案
Secret管理中心
一般来说,我们可能会将这些信息明文或加密后固化在系统启动的配置文件中,又或者经由启动环境变量参数传入系统并覆盖到相应的配置项中。前者可能存在的问题可能会有:系统配置与环境的耦合严重,例如修改DB的认证信息后旧有的配置不再适用;安全性问题客观存在,即使是数据加密后存放在配置文件中,本质上也会在代码中暴露出来;固化的配置方式,天然不支持动态口令的获取,我们只能通过一些定时任务来更新这些动态口令。后者的问题在于:虽然通过参数传入的方式可以减少一部分固化的配置代码,但是本质上这些配置代码只是转移到了系统启动的运维指令中,同时也会给运维行为增加了额外的负担;另外,动态的口令的问题依旧存在,仍然需要自行定时获取
进一步思考,随着系统的增长,整个系统的体系中可能会存在不同技术选型的子系统,这些系统可能会分布在不同的节点上,更极端的情况下,这些节点不会是都存在在一个可信的、封闭的内部环境中。此时我们就很可能需要更高安全级的Secret信息获取的方式,同时需要对Secret信息获取的行为进行审计,甚至在这些信息变更的时候通知到子系统
一种解决的思路是将这些分布在系统各处的零散Secret信息交由一个中心来进行统一管理,各个系统可以通过这个中心来获取自己所需的Secret信息。实现这个Secret信息管理中心可以按照保护等级大致分为三个等级:
![Refrence [1]](https://cdn-images-1.medium.com/max/800/1*9AAsLm7ATw8Fl8aVbJQdYw.png)
- Limited Access
Secret信息被存放在一个限制访问的仓库/服务中,例如一个私有的git仓库 - Encrypted Secrets
Secret信息经过编码后,再存储到存储层 - Management
提供更高级别、更细粒度的控制的应用,提供诸如获取信息权限管控、审计日志 (audit log)等功能- ACL
Access Control List 这里指获取权限控制 - TTL
Time to Live 对某些Secret信息设定存活时间,实现动态口令/凭据
- ACL
不同级别的安全等级相应的也会提升对应的复杂度,上述的三个级别也可以看做是我们实现Secret信息管理的几个里程碑。实际落地时,我们除了需要考虑当前的系统客观环境因素之外,还应该考虑一个关键的问题–在什么时候可以达到我们可以接受的安全程度,在什么时候停止 (Where does it end)
解决方案
git repository
我们可以简单的只对Secret信息的获取进行限制,使用一个私有的git仓库进行管理。私有的git仓库天然支持用户的信息校验,只有注册到该仓库的用户才有pull相应信息的权限,同时对于不同环境的隔离也可以通过branch来进行分隔,另外我们也可以利用git的版本管理来快速实现配置项的版本回退。相应在客户端,我们需要checkout对应分支的配置文件,进行读取而后使用这些配置
在这种解决方案中,由于配置是在文件级别上进行操作,在运维人员提交配置的时候,可能会引起配置文件的冲突,那么就很可能需要运维人员多次进行冲突的修改;另外,对于客户端的应用也并不方便,需要我们在启动之前拉取代码并加以读取,进行更多的文件读操作,影响启动速度。同时对于客户端的权限有可能不够细致,不能直接做到对某个具体的客户端只授予某个文件或某个文件夹的权限 (当然使用hook机制可以实现这一功能)
Config Management Server
我们也可以使用统一的配置中心服务来对Sercet信息进行集中管理,例如Spring Cloud Config。这样的情况下,配置中心会单独成为一个服务,暴露出相应的配置接口以供各个子系统调用。配置中心会使用自己的Backend来对数据进行存储,例如git、DB、consul… 在使用配置中心时,为了保证获取Sercet信息的安全性,需要我们对获取行为、数据存储添加一些保密措施,例如自行对数据进行加密操作,或者是简单集成一个可以提供这样的保护措施的组件来作为配置中心的存储后端
Hashicorp Vault
以下我们重点关注的是 Hashicorp Vault (后文简称为Vault) ,参考我们之前提到的实现Secret Management的三个阶段,Vault可以辅助我们快速构建第三级别的Mangement Server,它提供了完备且强大的功能来帮助我们完成这一目标
Vault提供了什么
- Secure Secret Storage
Vault可以对key/value形式的Secret信息进行加密并存储,因而仅仅获取到原始的存储数据并不能获取到真正的信息。持久化的方式包括写到磁盘、Consul… - Dynamic Secrets
Vault可以为某些系统生产动态口令,例如Database;在创建动态口令之后,Vault可以自动吊销那些过期的口令 - Data Encryption
Vault可以在不存储的情况下对数据进行加密/解密操作。在一些需要设计加密算法的场景时,可以使用这样的功能进行辅助操作 - Leasing and Renewal
Vault中所有的Secret信息都会与Vault订立一个”租约”,实质上是这些信息会有一定的有效期,当有效期截止的时候,Vault会自动吊销相应的信息。相应的,客户端需要通过Vault的内置刷新API来对想要的信息进行刷新操作 - Revocation
Vault不仅仅可以对单个的Secret信息进行吊销操作,还可以对Secret树进行吊销操作。例如吊销一个用户的所有相关信息
Vault可以做什么
- Secret Storage
Vault可以存储Secret信息,e.g., 敏感环境变量、API key、db credential。相较明文存储到文件、db、配置中心中,Vault对信息进行加密存储,且通过暴露API来获取数据,提供了更为安全的访问机制 - Dynamic Credential Generator
利用Vault的Dynamic Secret Engine,可以方便生成动态口令。同时借助Vault内部的过期、吊销机制和相应API,还可以做到对口令的生命周期的灵活管理 - Data Encryption
借助Vault的加密机制,可以在无需关注如何实现加密操作的情况下,将Vault作为一个加密/解密的工具
如下图,在一个简单的Vault使用场景中,Client通过认证后得到token,并以此凭据来通过API获取存储在Vault中的Secret信息。Secret的加密、解密操作都会由Vault接管
Vault是怎么做到的
Vault将Secret Manager的挑战抽象为如何处置分散在各处的明文Secret信息、如何辨别并隔离获取信息的客户端,以及如何保证Vault保护自身的信息三大方面
Vault将分散在各处的Secret信息经过加密以后存储起来,同时对信息获取的权限进行细粒度的控制并附加审计记录,将自身作为一个Secret Server Application,暴露API和提供Cli与客户端进行交互
为了辨别究竟是哪个客户端来请求信息,Vault提供了动态的Secret。相较于长期有效的凭证,动态Secret可以视作是短时间有效的凭证 (short-lived ephemreal credentials),由此可以用短时效的凭据来为每一个client分配唯一的凭据,并基于唯一的凭据,对不同的client的信息、权限等信息进行隔离
为了保护应用自身的信息,一般来说会使用一个加密key来进行加密操作。但是对于简单使用key加密、解密数据的方案来说,需要保证所有的client和server都需要使用正确的方式来实现加密操作,一方面增加了实现复杂度,同时也增加了client端侵入负担
因而,Vault将加密、解密作为一个服务提供出来供client端直接使用 (encrypt as a service):Vault创建了一系列的内部命名key,并提供了加密/解密、注册、校验等API。内部的key management会管理这一系列的key,开发者不会真的看到这些内部的key。如此一来,开发者无需关心加密/解密的实现,同时也可以使用API来减轻对key声明周期管理的复杂度
如下图,Vault的架构可以概括为三层:暴露给外部的HTTP/HTTPS API层、由Barrier包裹保护的核心层,以及最终持久化的存储端。其中,Barrier将保证只有经过加密后的数据才能被写入,只有经过校验和解密后的数据才能被输出
![Reference [2]](https://www.vaultproject.io/img/layers.png)
Vault启动时会处于密封状态 (sealed),执行任何操作前,Vault需要解封 (unseal)。启封操作是通过提供unseal keys实现的,当Vault init的时候,会产生一个encryption key来保护所有的数据,即加密/解密的key。这个key由master key来保护,默认地,Vault使用 Shamir's secret sharing 算法,将master key分为5个key,至少需要其中的任意3个key来重建master key
当Vault解封以后,请求就可以经过API传入核心层。当client首次连接到Vault,需要进行身份认证。Vault提供了多种的认证方式,例如username/password、GitHub,以及application适用的public/private key和token认证方式。经过认证后,Vault会产生一个新的token作为访问令牌返回给client。随后client的就可以使用token作为凭据,发起请求。Vault通过识别token对应的client,得到其身份信息、对应的访问权限信息,并以此作为ACL的基础信息
请求进入核心层后会被路由到对应的Secret Engine中,如果engine返回了一个secret,核心层会将其注册到过期管理组件中 (expiration manager) 并与一个lease ID进行关联。这个lease ID用于client端刷新、吊销secret的唯一ID
同时,核心层还会将所有req和res的记录都传递给Audit Broker,并由其传递给配置的Audit Device中。另外,Vault也会针对一些失败场景,做先先写log后写入的事务操作 (write ahead logging),同时配备相应的回滚管理,这一部分的实现也是对用户透明的
最后,Vault的灵活的可插拔的组件设计,使得Vault在保证灵活性的同时,也为Vault的HA提供了基础。Vault的认证、Secret Engine,以及最终存储数据的Storage都可以灵活配置,来避免单点风险。同时由于Vault的master key的分享算法,可以使得系统中的不同节点仍然可以在保证安全的同时可以重建master key最终加密/解密数据
小结
本篇中简单的讨论了集中管理Secret信息的一些解决方案,并引出了Vault作为一种成熟快捷的解决方案。Vault可以在减少实现复杂度、减少对各个服务的侵入的同时,提供更为细粒度的权限控制、更加便利安全的加密/解密方法,以及相对更灵活的Secret生产机制 (k-v, Dynamic Secrets),以及便利的监控审计功能
Reference
[1] Secret Management Architectures: Finding the balance between security and complexity
[2] Hashicorp Vault