1. 从古典朴素的安全哲学谈起
1.1 网络安全现状
现在最常见的企业网络安全架构便是在企业网络边界处做安全防护,而在企业网络内部不做安全防范。这确实为企业的安全建设省了成本也为企业提供了一定的防护能力。但是这类比于现实情况的一个小区,这个小区里面所有的房屋都没有门,小区的门口站着一个保安,由他来鉴别谁能进入小区,谁不能进入小区,只要保安放行了一个人进入小区,这个人就可以在小区里为所欲为。那么大家会住在这个小区吗?我想大家都是不会的。
为什么我们不会呢,因为这样的小区太脆弱了,只要有办法绕过门口的保安,这种防护就形同虚设。例如一个小偷冒充里面的住户、尾随一个用户或者勾结里面的用户里应外合都能突破保安这道防线。类比于网络环境中,一个黑客盗用员工账号、重放链接复用、勾结企业员工都可以突破网络边界处的安全。
1.2 零信任的要求
零信任在这样的背景下应运而生,零信任要求将所有的安全防护沉淀到应用级别,即每个应用都需要对请求进行身份认证和鉴权。
a. 认证就是确认你是谁。
我们依旧以刚刚的小区举例。颁发身份就是你拿着房产证等等材料去居委会,居委会给你一个通行证,这个通行证就标识了你是这个小区的住户,且标明了你的身份是谁。当然这个通行证旧了,你还得拿着材料去居委会换一张新的。
在零信任的要求中,一个客户端或者服务端需要携带 token 向证书颁发机构拿取证书,这个证书就唯一标明了这个客户端或服务端的身份,之后客户端、服务端向其他客户端、服务端请求时,均需要拿着这个证书以声明自己的身份。证书在一定时间后就会过期,那么就需要重新执行去拿去证书。
b. 鉴权就是确认你有什么权利。
住户拿着通行证可以获得对应房屋的钥匙,这把钥匙只能打开指定房屋的门。
在零信任的要求中,每个证书身份都有对应的权利,比如你只能访问 A 服务,不能访问 B 服务等等。每个服务会执行相应的规则,拦截不符合规则的请求,放行符合规则的请求。
零信任除了上述的基本功能外,仍需一定额外的功能,例如:
a. 认证可变化:认证和授权必须严格执行,并且要求动态可变。对于授权的给予,需要根据网络情况、企业组织架构、网络威胁、身份变换不断地评估资源。对于授权的给予应该有动态策略驱动。
b. 可见性和监控:必须收集、分析和使用有关资产当前状态及其通信的信息,以改善组织的安全态势。必须持续监控所有资产的完整性和安全性,并且必须及时缓解安全状况的偏差。此外,这些发现应反馈到客户风险评估和身份验证程序中,以进一步提高其质量。
c. 审计与合规性:安全数据保留和全面的合规报告是应对监管的刚需。
2. Istio:零信任的优秀构建者
2.1 Istio 的零信任构建方案
服务网络 Istio 在零信任方面构建较为完善,在服务网络中,存在 3 种角色:
- 控制面 Istiod:每个服务网络中存在一个 Istiod,作为控制面,统筹管理服务网络中的所有应用。
- 用户应用:用户运行的应用。
- 数据面 proxy:每个运行在服务网络中的用户应用均拥有一个 proxy 容器,其代理了用户应用的所有流量。所有从用户应用出的流量和入的流量均需经过 proxy 容器。
在服务网络中,关于零信任最核心的两个功能,认证和鉴权的具体行为如下:
- 认证:在 proxy 启动时,proxy 会携带 k8s 塞在容器里面的 token 向 istiod拿取证书。之后任何服务之间的调用均需双向认证。例如 A 向 B 发起请求,A需要验证 B 的证书,B 也需要验证 A 的证书。
- 鉴权:控制面 istiod 会向所有 proxy 分发鉴权规则,每个 proxy 会根据规则,放行请求或者拒绝请求。
2.2 Istio 方案的一些不足
Istio 的零信任方案已经非常优秀了,如果硬要说有哪些不足,我们在可以从如下方面考虑:
a. 在认证策略方面:Istio 的信任来源于 k8s 集群不会被人攻破,pod 中的 token 不会被人窃取。验证 token 时以信任 apiserver 为基础;istio 的认证策略单一。
b. 在身份标识方面:Istio 颁发的证书中,用于标识身份的字段 object 使用 spiff 标准。需要强依赖 K8s 的 namespace 和 serviceaccount;身份标识不够通用。
c. 在鉴权规则方面:Istio 的鉴权规则粒度十分细,但是有许多规则匹配与 K8s 的概念强依赖,例如要求请求必须来源于某个 namespace;其次仍有部分规则的缺失,例如应用部署环境级别的鉴权等等。
3. Sentinel 2.0:我们期望微服务下的安全底座该是什么样?
3.1 Sentinel 是什么
Sentinel 开源伊始,注重于对应用运行态时保护,侧重于流控降级,包括热点流控、熔断降级、自适应过载保护、并发隔离、流量控制与平滑等。
2022 年,Sentinel 宣布品牌升级,从 Sentinel 1.0 升级为 Sentinel 2.0,并与 OpenSergo 联动,注重于应用全生命周期的保护。包括标准化、流量控制与自愈、服务容错、服务隔离、流量路由与染色、零信任等。
Sentinel 中的零信任功能涉及零信任最核心的两个功能,即证书管理与鉴权规则。
Sentinel 2.0 overview
我们也在 Sentinel 社区提了相关的 issue[1],希望可以建设起 Sentinel2.0 的零信任安全能力。
With the development of cloud-native technologies, network boundaries are gradually disappearing, and the concept of zero trust therefore prevails. The most important functions of zero trust are certificate management and request authentication. As a generic, cloud-native traffic governance component, Sentinel 2.0 will support zero-trust capabilities for certificate management and request authentication:
- Obtain the certificate from the external data source and use it to configure https when the web service is enabled.
- Obtains authentication rules from the external data source and permits or blocks each request based on the authentication rules.
3.2 Sentinel 的零信任如何做
让我们来详细聊聊 Sentinel 2.0 的零信任能力如何做更适合社区的发展,能为我们的微服务生态提供安全的底座能力。目前在 Sentinel 和 OpenSergo 的生态中:
- OpenSergo:作为规则管理与下发的中心放,定位类似于服务网络中的控制面。
- Sentinel:作为依赖包被应用引入,定位类似于服务网络中的一个数据面。
对于零信任的两个核心功能认证和鉴权,Sentinel 和 OpenSergo 将会如下做:
- 认证:Sentinel 携带 token 向 OpenSergo 获取相应的证书。OpenSergo 会根据相应的策略判断 token 的合法性,颁发相应的身份证书,Sentinel 会在证书过期时自动轮转证书。Sentinel 首先会支持与 Istio 的 k8s-apiserver 的认证策略,并在后续继续支持 OPA、IDaas 等认证策略。
- 鉴权:用户可以配置鉴权的 CRD 至 OpenSergo,OpenSergo 将会转化为标准的 XDS 下发至 Sentinel,Sentinel 会保存关于本应用的所有鉴权规则,并根据鉴权规则为应用自动放行或者拒绝请求。鉴权规则总共包含 JWT 规则、deny 规则和 allow 规则。
为了更好地让零信任能力演进,需要统一的零信任 CRD 规则,CRD 在兼容 Istio 的基础上适当拓展。该 CRD 总共涉及 3 个方面,分别为:
a. TlsMode:认证策略,请求是否需要验证双方身份。
b. JWT: JWT 策略,如何验证请求中符合 JWT 规范的 token。
c. Auth:鉴权策略,判断何种请求会通过,何种请求会不通过。
一个 Auth 的实例 CRD 如下:
apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: httpbin namespace: default spec: action: DENY rules: - from: - source: # 需要匹配的身份 principals: [ "principal1","principal2" ] # 需要不匹配的身份 notPrincipals: [ "notPrincipal1","notPrincipal2" ] # 需要匹配的JWT中iss+"/"+sub requestPrincipals: [ "jwtp1","jwtp2" ] # 需要不匹配的JWT中iss+"/"+sub notRequestPrincipals: [ "notjwtp1","notjwtp2" ] # 需要匹配的命名空间 namespaces: [ "namespace1","namespace2" ] # 需要不匹配的命名空间 notNamespaces: [ "notNamespace1","notNamespace2" ] # 需要匹配的直接来源ip ipBlocks: [ "10.1.1.1","10.1.1.0/24" ] # 需要不匹配的直接来源ip notIpBlocks: [ "11.1.1.1","11.1.1.0/24" ] # 需要匹配的请求最初ip,最初请求的来源ip来自于header中X-Forwarded-For的值 remoteIpBlocks: [ "12.1.1.1","12.1.1.0/24" ] # 需要不匹配的请求最初ip ,最初请求的来源ip来自于header中X-Forwarded-For的值 notRemoteIpBlocks: [ "13.1.1.1","13.1.1.0/24" ] - source: principals: [ "principal3" ] to: - operation: # 需要匹配的到达域名 hosts: [ "www.host1.com","www.host2.com" ] # 需要不匹配的到达域名 notHosts: [ "www.nothost1.com","www.nothost2.com" ] # 需要匹配的到达端口 ports: [ "8080","443" ] # 需要不匹配的到达端口 notPorts: [ "18080","1443" ] # 需要匹配的请求方法 methods: [ "GET","POST" ] # 需要不匹配的请求方法 notMethods: [ "PUT","DELETE" ] # 需要匹配的请求path paths: [ "/info1*","/info2" ] # 需要不匹配的请求path notPaths: [ "/notinfo1*","/info2" ] - operation: hosts: [ "www.host3.com" ]
我们也在 OpenSergo 社区提了相关的 issue[2],希望可以建设起规范的零信任 CRD。
We want to add a standard CRD on the zero-trust direction to OpenSergo.
The CRD will be expanded to be compatible with istio.
The CRD involves three aspects in total, namely
1. TlsMode: Authentication policy, whether to authenticate both parties.
2. JWT: JWT policy, how to verify tokens in a request that comply with the JWT specification.
3. Auth: Authentication policy that determines which requests are approved and which requests are rejected.
3.3 Sentinel 的代码结构
在 sentinel 的代码中关于零信任的部分,将会分为三层:
- extension:在 sentinel 的 extension 包中将会完成证书拉取、XDS 的接收、鉴权规则的转换。
- core:在 sentinel 的 core 层定义证书、鉴权规则的实体,并存储证书、鉴权规则。
- adapter:在 sentinel 的 adapter 层将会提供适配于 mvc、webflux、dubbo 的各种适配器。
Spring Cloud Alibaba、Dubbo、Spring Boot 应用可以使用适配器或者直接使用 core 的实体让证书以及规则生效。
根据 3.2 中所示,Spring Cloud Alibaba、Dubbo、Spring Boot 适配 Sentinel 中存储的证书、规则实体均需要一定改变。
SpringCloud Alibaba 适配
如何适配 TlsMode:
SpringCloud Alibaba 无法实现单端口双协议,也就是无法实现 TlsMode 中的兼容模式。因此仅支持双模式:PERMISSIVE、DISABLE 均为明文模式,STRICT 为严格模式。
适配 JWT、RBAC:
SpringCloud Alibaba 是在用户的 springcloud 上使用,因此本身就处理 http 请求,因此完全适配 JWT、RBAC。
Dubbo 适配
适配 TlsMode:
Dubbo 适配单端口双协议,适配该模式
适配 JWT:
- Dubbo 的传输协议中无 header 和 params 的概念,但是存在 Attachments 的字段,
- 对于 CRD 中的 fromHeaders 映射从 Attachments 字段拿取。
- 对于 CRD 中的 fromParams 规则失效。
适配 RBAC:Dubbo 中无 paths、methods、header 的概念。
- 对于 CRD 中 header 的规则生效于 Dubbo 的 Attachments 字段。
- 对于 CRD 中的 methods 的规则无效。
- 对于 CRD 中的 paths 规则变为 /package.service/method
Dubbo 适配 CRD 的策略我们参考自 istio 对于 rpc 风格的说明:
Optional. A list of paths as specified in the HTTP request. See the Authorization Policy Normalization for details of the path normalization. For gRPC service, this will be the fully-qualified name in the form of “/package.service/method”.
If not set, any path is allowed. Must be used only with HTTP.
4. 平滑升级:微服务零信任仍需努力的道路
4.1 平滑升级的问题
虽然 Sentinel 中构建了拉取证书、获取鉴权规则、接入起效的基本框架,但是接入时,仍存在平滑兼容的问题。零信任中最基础的能力为将所有调用链路从明文传输升级为 mtls 传输,即双向的身份认证。
我们以最常见的应用之间调用协议 http 协议为例,零信任的要求为所有调用链路均设置为 https。当然,理想情况是用户将所有的应用停止,将所有的应用的 http 端口升级为 https 端口,之后全部重启。然而,这并不现实,真实的情况是,用户将所有的应用的实例依次接入零信任的功能。
如图所示,假定调用链路为业务网关->应用 A->应用 B->应用 C,并设定应用 A 和应用 B 有两个实例,应用 C 有 1 个实例。
在用户初始态时,应用之间所有的调用均使用 http 协议。
之后,进入用户切换中间态,用户依次升级应用并接入零信任能力,在该过程中,有如下要求:
a. 调用链路的前后侧均接入零信任时使用 https 协议,例如,在图中的用户切换中间态时应用 A 实例 1 接入零信任功能,应用 B 实例 1 接入零信任功能,因此应用 A 实例 1->应用 B 实例 1 为 https 协议。
b. 如果调用链路前侧未接入零信任功能,后侧接入零信任功能,应该使用 http 协议,例如,应用 A 实例 2 未接入零信任功能,应用 B 实例 1 接入零信任功能,因此应用 A 实例 2->应用 B 实例 1 为 http 协议。
c. 如果调用链路前侧接入零信任功能,后侧未接入零信任功能,那么应该没有调用链路,例如,应用 A 实例 1 接入零信任功能,应用 B 实例 2 未接入零信任功能,因此应用 A 实例 2 和应用 B 实例 2 不存在调用链路。
最终,在用户最终态时,所有应用均接入了零信任能力,所有的调用链路均需为 https 协议。
4.2 兼容方案
从上述发现,在切换中间态时,一个应用的实例在接入零信任之后,必须同时拥有处理 https 和 http 的能力,在终态时,关闭处理 http 的能力,仅保留 https 的能力。
根据上述情况,并依据 istio 对于 PeerAuthentication 的定义,可以设定如下模式:
- STRICT(严格模式,终态):应用之间必须使用 https 通信。
- PERMISSIVE(兼容模式,中间态):应用之间优先选用 https 协议通信。例如,对于调用链路应用 A->应用 B(如图中的切换中间态,实例 1 开启 https 端口,实例 2 还未开启 https 端口),应用 A 选择实例 1 通信时使用 https 端口,选择实例 2 通信时使用 http 端口。
- DISABLE(明文模式,初始态):应用之间必须使用 http 通信。
那么如果用户选择升级应用实例,使其最终从 http 变为 https,并且在过程中流量不能损失,可以根据如下步骤升级应用实例:
用户重启应用实例并接入零信任功能,在启动后默认明文模式 -> 启用兼容模式 -> 监控应用实例流量已经全部转入 https -> 使用严格模式。
istio 对于 PeerAuthentication 的定义包含 4 个模式,分别为:
UNSET:Inherit from parent, if has one. Otherwise treated as PERMISSIVE.DISABLE:Connection is not tunneled.PERMISSIVE:Connection can be either plaintext or mTLS tunnel.STRICT:Connection is an mTLS tunnel (TLS with client cert must be presented).
那么根据上述要求,必须做一个应用实例的中间态,该中间态必须保证应用实例能够同时处理 http 和 https 请求,可以给出了如下两个方案:
a. 额外开启 https 端口的方案:该方案为新开启一个与 http 端口不重复的https端口,并在注册中心注明 2 个端口。例如,可以在注册中心的 metedata 中带入如下信息,标明双端口:
该方案最大的问题是兼容性不好,与服务网络、网关均无法很好适配,其次于大多数用户,新起端口有报备要求。
b. 单端口双协议的方案:该方案最大的问题是不好实现,对于最常用的 tomcat而言,tomcat 原生不支持单端口双协议,但是对于 tomcat 中的 nio 模式,可以通过改造源码或者使用字节码增强技术实现单端口双协议。tomcat 的 arp 模式使用 native 模式根本无法实现切面。
让微服务应用无感、平滑升级至零信任方案,这是零信任方案是否能被大规模使用的关键条件之一,我们希望能够帮助我们企业微服务做到无感的默认安全。
5. 展望
为了让零信任更好、更便捷地落地,我们还有很多工作需要去完成。例如:
a. CI/CD 集成:将零信任的规则集成在 CI/CD 中,更加自动化的配置安全策略
b. 自适应调整:当一个应用外部环境改变,比如部署环境改变、应用从属部门改变,安全策略自适应的改变。
c. 默认安全:如何保证应用发布时无需配置,根据环境、开发者身份等等信息,自动生成安全策略,使得应用默认时安全的。
让微服务能够真正实现简单方便的零信任,仍有许多的道路要走,这或许只是个开始。也欢迎各位志同道合的同学们一起参与建设。
相关链接:
[1] issue
[2] OpenSergo issue
作者:涯客、十眠