第9章 可靠通信
微服务提倡 分散治理,不追求统一的技术平台。
9.1 零信任网络
边界安全:
着重对经过网络区域边界的流量进行检查,对可信任区域(内网)内部的机器之间的流量则给与直接信任或者较为宽松的处理策略。
9.1.1 零信任安全模型的特征
零信任安全的中心思想是:不应当以某种固有特征来自动信任任何流量,除非明确得到了代表请求来源的身份凭证,否则一律不会有默认的信任关系。
传统网络安全模型与云原生时代零信任安全模型对比:
1.传统、边界安全模型 / 2.云原生、零信任模型 / 3.具体需求
a) 基于防火墙等设施,认为边界内可信;
b) 服务到服务通信需认证,环境内的服务之间默认没有信任
c) 保护网络边界(仍然有效);服务之间默认没有信任
a) 用于特定的IP和硬件(机器)
b) 资源利用率更高,重用、共享效果更好,包括ip和硬件
c) 受信任的机器运行来源已知的代码
a) 基于IP的身份
b) 基于服务的身份
c) 同上
a) 服务运行已知的、可预期的服务机器上
b) 服务运行在环境中的任何地方,包括私有云/公有云混合部署
c) 同上
a) 安全相关的需求由应用来实现,每个应用单独实现
b) 由基础设施来实现,基础设施中集成了共享的安全性要求
c) 集中策略实施点,一致的应用到所有服务
a) 对服务如何构建、评审、实施的安全需求的约束力较弱
b) 安全相关的需求一致的应用到所有服务
c) 同上
a) 安全组件的客观测性较弱
b) 有安全策略及其是否生效的全局视图
c) 同上
a) 发布不标准,发布频率较低
b) 标准化的构建和发布流程,每个微服务变更独立,变更频繁
c) 简单、自动、标准化的变更发布流程
a) 工作负载通常作为虚拟机部署或部署到物理机,并使用物理机或管理程序进行隔离
b) 封装的工作负载及其进程在共享的操作系统中运行,并由管理平台提供的某种机制来进行隔离
c) 在共享的操作系统的工作负载之间进行隔离
2.论文的主要观点
1.零信任网络不等同于放弃在边界上的保护措施
虽然防火墙等位于网络边界的设施是属于边界安全而不是零信任安全的概念,但它仍然是一种提升安全性的有效且必要的做法。在微服务集中的前端
部署防火墙,把内部服务节点间的流量与来自互联网的流量隔离开,这种做法无论何时都值得提倡的,至少能够让内部服务避开来自互联网未经授权流量的
饱和攻击,如ddos。
2.身份只来源于服务
传统应用一般是部署在特定的服务器上,这些机器的ip、MAC地址很少会发生变化,此时系统的拓扑状态是相对静态的。基于这个前提,安全策略才会
使用ip地址、主机名等作为身份标识符,无条件信任具有特性身份表示的服务。在如今的微服务系统,尤其是云原生环境中的微服务系统中,虚拟化基础
设施已经得到大范围应用,这使得服务所部署的ip地址、服务实例的数量随时可能发生变化,因此,身份只能来源于服务本身所能初始的身份凭证(通常是
数字证书),而不再是服务所在的ip地址、主机名或者其他特征。
3.服务之间没有固定的信任关系
这点决定了只有已知的、明确的授权的调用者此案访问服务,阻止攻击者通过某个服务节点中的代码漏洞来越权调用其他服务。如果某个服务节点被
成功入侵,这一原则可以阻止攻击者扩大其入侵范围,与微服务设计模式中使用断路器、舱壁隔离实现容错来防止雪崩效应类似,在安全方面也应当采用
这种"不信任"的模式来减小入侵危害的影响范围。
4.集中、共享的安全策略实施点
这点与微服务的分散治理刚好相反,微服务提倡每个服务自己独立的负载自身所有的功能性和非功能性需求。而google这个观点相当于为分散治理
原则做了一个补充---分散治理,但涉及安全的非功能性需求(如身份管理、安全传输层、数据安全层)最好除外。一方面,要写出高度安全的代码极不容易,
为此付出的精力甚至远高于业务逻辑本身。另外一方面,也是更重要的一方面,让服务各自处理安全问题很容易出现实现不一致或者出现漏洞时要反复修改
多处地方的情况。还有一些安全问题如果不立足于全局是很难彻底解决的。因此,google明确提出应该有集中式的"安全策略实施点",安全需求应该从
微服务的应用代码下沉至云原生的基础设施里。
5.受信的机器运行已知的代码
这点限制了服务只能使用认证过的代码和配置,并且只能运行在认证过的环境中。分布式软件除了促使软件架构发生重大变化之外,也使软件的发布
流程发生较大的改变,使其严重依赖持续集成和持续部署。从开发人员编写代码、到自动化测试、自动集成,再到漏洞扫描,最后发布上线,这整套CI/CD
流程被称作"软件供应链"。安全不仅仅局限于软件的运行阶段。为此,零信任安全针对软件供应链的每一步都加入了安全控制策略。
6.自动化、标准化的变更管理
这点也是为何提倡通过基础设施而不是应用代码去实现安全功能的另外一个重要理由。如果将安全放在应用上,由于应用本身的分散治理,决定了
安全也必然是难以统一和标准化的。做不到标准化就意味着做不到自动化,相反,一套独立于应用的安全基础设施,可以让运维人员轻松了解基础设施
变更对安全性的影响,也可以在几乎不影响生产环境的情况下发布安全补丁程序。
7.强隔离的工作负载
"工作负载"的概念贯穿了google内部的Borg系统和后来的Kubernetes系统,它是指在虚拟化技术的支持下运行的一组能够协同提供服务的镜像。
容器化,它仅仅是虚拟化的一个子集。与传统虚拟机相比,容器的隔离能力是有所降低的,这种设计针对性能非常有利,却对安全相对不利,因此在强调
安全性的应用里,会有专门关注强隔离性的容器运行的工具出现。
9.1.2 Google的实践探索
google 认为零信任安全模型的最终目标是实现整个基础设施之上的自动化安全控制,服务所需的安全能力可以与服务自身一起,以相同的方式自动进行伸缩扩展。
对于程序来说,安全是日常,风险是例外;对于人类来说,做到袖手旁观是日常,主动干预是例外。
google设计了一系列内部工具,才最终得以实现前面所说的安全原则:
1.为了在网络边界上保护内部服务免受ddos攻击,设计了名为 Google Front End(最终用户访问请求的终点)的边缘代理,负责保证此后所有流量都在TLS
之上传输,并自动将流量路由到合适的可用区域之中;
2.为了强制身份只来源于服务,设计了名为 Application Layer Transport Security(应用层传输安全)的服务认证机制,这是一个用于双向认证和
传输加密的系统,可以自动将服务于它的身份标识符绑定,使得所有服务间流量都不必再使用服务名称、主机IP来判断对方的身份。
3.为了确保服务间不再有默认的信任关系,设计了 Service Access Policy(服务访问策略)来管理一个服务向另外一个服务发起请求时所需提供的认证、
鉴权和审计策略,并支持全局视角的访问控制和分析,以满足"集中、共享的安全策略实施点"的原则。
4.为了实现仅以受信的机器来运行来源已知的代码,设计了 Binary Authorization(二进制授权)的部署时检查机制,确保在软件供应链的每一个阶段,
都符合内部安全检查策略,并对此进行授权与鉴权。同时设计了 Host Integrity(宿主主机完整性)的机器安全启动程序,在创建宿主时自动验证包括
BIOS、BMC、Bootloader和操作系统内核的数字签名。
5.为了工作负载能够具有强隔离性,设计了名为 gVisor 的轻量级虚拟化方案,这个方案与此前由Intel 发起的Kaka Containers的思路有异曲同工之妙。
目的都是弥补容器共享操作系统内核而导致隔离性不足的安全缺陷,做法都是为每个容器提供一个独立的虚拟Linux内核,譬如gVisor是用Go写的一个名为
Sentry的能够提供传统操作系统内核功能的进程。严格来说,无论是gVisor还是Kaka Containers,尽管披着容器运行时的外衣,但本质上都是轻量级
虚拟机。
在微服务时代以前,传统的软件系统与研发模式的确很难承受零信任安全模型引发的代价,只有带了云原生时代,虚拟化的基础设施长足发展,能将复杂性隐藏于
基础设置之内,开发者不需要为达成每一条安全原则而专门开发或引入可感知的安全设施;只有容器与虚拟化网络的性能足够高,可以弥补安全隔离于安全通信的额外
的损耗的前提下,零信任网络的安全模型才能生根发芽。
9.2 服务安全
9.2.1 建立信任
零信任网络里不存在默认的信任关系,一切服务调用、资源访问成功与否,均需以调用者与提供者间已建立的信任关系为前提。真实世界里,能够达成信任的基本
途径不外乎基于 共同私密信息的信任和基于权威公证人的信任两种。在网络世界里,因为客户端和服务端之间一般没有什么共同的私密信息,所以真正才能的就只能是
基于权威公证人的信任,这种信任有个标准的名字:公开密钥基础设施(Public Key Infrastructure,PKI)。
PKI是构建在传输安全层(Transport Layer Security,TLS)的必要基础。在任何网络设施都不可信任的假设前提下,无论是dns服务器、代理服务器、负载
均衡还是路由器,传输路径上的每一个节点都有可能监听或者篡改通信双方传输的信息。要保证通信过程不受到中间人的攻击,启用tls对传输通道本身进行加密,让
发送者发出去的内容只有接收者可以解密唯一具备可行的方案。建立tls传输,说起来似乎不复杂,只要在部署服务器时预置好CA根证书,以后该CA为部署的服务签发
tls证书便是。
微服务中tls认证的频次也显著的高于传统的应用,比起公众互联网中单向的tls认证,在零信任网络中,往往要启用双向tls认证(Mutual TLS
Authentication,常简写为 mTLS),即不仅要确认服务器的身份,还要确认调用者的身份。
1.单向TLS认证
只需要服务端提供证书,客户端通过服务端证书验证服务器的身份,但服务器并不验证客户端的身份。单向tls用于公开的服务,即任何客户端都被允许连接
到服务进行访问,它保护的重点是客户端免受冒牌服务器的欺骗。
2.双向TLS认证
客户端、服务端双方都要提供证书,双方各自通过对方提供的证书来验证对方的身份。双向tls用于私密的服务,即服务只允许特定身份的客户端来访问,
它除了可以保护客户端不连接到冒牌服务器外,也可以保护服务端不遭受非法用户的越权访问。
9.2.2 认证
根据认证的目标对象可以把认证分为两种类型:一种是以机器作为认证对象,即访问服务的流量来源是另外一个服务,称为服务认证(Peer Authentication,
直译过来是"节点认证");另外一种是以人类作为认证对象,即访问服务的流量来自于最终用户,称为请求认证(Request Authentication)。
1.服务认证
a) Istio
得以于 Istio 提供的基础设施的支持,我们不需要 Google Front End、Application Layer Trasport Security 这些安全组件,也不需要
部署PKI和CA,甚至无需改动任何代码就可以启用 mTLS 认证。
如果你的分布式系统还没有到达完全云原生的程度,其中仍存在部分不受Istio管理(即未注入边车)的服务端或者客户端,你也可以将 mTLS 传输声明为
"宽容模式"(Permissive Mode)。宽容模式的含义是 受 Istio 管理的服务会允许同时接收纯文本和mTLS两种流量。纯文本流量仅用于那些不受Istio管理
的节点进行交互,你需要自己解决纯文本流量的认知问题;而对于服务网格内部的流量,就可以用mTLS认证。
b) Spring Cloud
笔者选择的方案是借用 OAuth 2 协议的客户端模式来进行认证,分为2步:
1.每一个要调用服务的客户端都与认证服务器约定好一组只有自己知道的密钥(Client Secret),这个约定的过程应该由运维人员在线下自行完成,
通过参数传给服务,而不是由开发人员在源码或者配置文件中直接设定。密钥就是客户端的身份证明,客户端调用服务时,会先使用该密钥向认证服务器
申请jwt令牌,然后通过令牌证明自己的身份,最后访问服务。
2.每一个对外提供服务的服务端,都扮演者 OAuth 2 中的资源服务器的角色,它们均声明为要求提供客户端模式的凭证。
客户端要调用受保护的服务,就必须先出示能证明调用者身份的jwt令牌,否则就会遭到拒绝,这个操作本质上是授权,但是在授权过程中已实现了服务的
身份认证。
由于每个微服务在都同时具有服务端和客户端两种身份,所以在每个微服务中都应该包含这些代码。面对可能的中间人攻击,tls是唯一可行的办法,
言下之意是即使应用层的认证能一定程度上保护服务不被身份不明的客户端越权调用,但对传输过程中内容被监听、篡改,以及被攻击者在传输过程中拿到jwt
令牌后去冒充调用者是无能为力的。这种方案不适用于零信任安全模型,只能默认在内网节点间具备信任关系的边界模型上良好工作。
2.用户认证
a) Istio
当来自最终用户的请求进入服务网格时,Istio 会自动根据配置的 JWKS(JSON Web Key Set)验证令牌的合法性,如果令牌没有被篡改过且在有效期
内,就信任负载中的用户身份,并从令牌的Iss字段中获得Principal。
JWKS 代表一个密钥仓库。我们知道在分布式系统中,JWT应采用非对称的签名算法(RSA SHA256 等,默认的 HMAC SHA256 属于对称加密算法),
由认证服务器使用私钥对负载进行签名,再由资源服务器使用公钥对签名进行验证。常与jwt配合使用的JWK(JSON Web Key)就是一种存储密钥的纯文本格式,
本质上和 JKS(Java Key Storage)、P12(Predecessor of PKCS#12),PEM(Privacy Enhanced Mail)这些常见的密钥格式在功能上没有什么
差别。JKWS顾名思义就是一组JWK的集合,支持JKWS的系统,能通过jwt令牌中的Header中的KID(Key ID)来自动匹配应该使用哪个JWK来验证签名。
b) Spring Cloud
如果jwt令牌合法,Spring Security 的过滤器就会放行调用请求,并从令牌中提取Principal,放到自己的安全上下文中。实际开发中,你可以根据
需要自行决定Principal具体的形式,既可以像Istio中那样直接从令牌中取出来,以字符串形式原样存储,节省一些数据库或者缓存的开销;也可以统一做
一些额外的转换处理,以便后续业务使用。
9.2.3 授权
经过认证之后,合法的调用者就用了可信任的身份,此时就已经不在需要区分调用者到底是机器还是用户了,只根据身份角色来进行权限访问控制即可,即RBAC。