从古典朴素的安全哲学谈起
Aliware
网络安全现状
现在最常见的企业网络安全架构便是在企业网络边界处做安全防护,而在企业网络内部不做安全防范。这确实为企业的安全建设省了成本也为企业提供了一定的防护能力。但是这类比于现实情况的一个小区,这个小区里面所有的房屋都没有门,小区的门口站着一个保安,由他来鉴别谁能进入小区,谁不能进入小区,只要保安放行了一个人进入小区,这个人就可以在小区里为所欲为。那么大家会住在这个小区吗?我想大家都是不会的。
为什么我们不会呢,因为这样的小区太脆弱了,只要有办法绕过门口的保安,这种防护就形同虚设。例如一个小偷冒充里面的住户、尾随一个用户或者勾结里面的用户里应外合都能突破保安这道防线。类比于网络环境中,一个黑客盗用员工账号、重放链接复用、勾结企业员工都可以突破网络边界处的安全。
零信任的要求
零信任在这样的背景下应运而生,零信任要求将所有的安全防护沉淀到应用级别,即每个应用都需要对请求进行身份认证和鉴权。
a. 认证就是确认你是谁。
我们依旧以刚刚的小区举例。颁发身份就是你拿着房产证等等材料去居委会,居委会给你一个通行证,这个通行证就标识了你是这个小区的住户,且标明了你的身份是谁。当然这个通行证旧了,你还得拿着材料去居委会换一张新的。
在零信任的要求中,一个客户端或者服务端需要携带 token 向证书颁发机构拿取证书,这个证书就唯一标明了这个客户端或服务端的身份,之后客户端、服务端向其他客户端、服务端请求时,均需要拿着这个证书以声明自己的身份。证书在一定时间后就会过期,那么就需要重新执行去拿去证书。
b. 鉴权就是确认你有什么权利。
住户拿着通行证可以获得对应房屋的钥匙,这把钥匙只能打开指定房屋的门
在零信任的要求中,每个证书身份都有对应的权利,比如你只能访问 A 服务,不能访问 B 服务等等。每个服务会执行相应的规则,拦截不符合规则的请求,放行符合规则的请求。
零信任除了上述的基本功能外,仍需一定额外的功能,例如:
a. 认证可变化:认证和授权必须严格执行,并且要求动态可变。对于授权的给予,需要根据网络情况、企业组织架构、网络威胁、身份变换不断地评估资源。对于授权的给予应该有动态策略驱动。
b. 可见性和监控:必须收集、分析和使用有关资产当前状态及其通信的信息,以改善组织的安全态势。必须持续监控所有资产的完整性和安全性,并且必须及时缓解安全状况的偏差。此外,这些发现应反馈到客户风险评估和身份验证程序中,以进一步提高其质量。
c. 审计与合规性:安全数据保留和全面的合规报告是应对监管的刚需。
Istio:零信任的优秀构建者
Aliware
Istio 的零信任构建方案
服务网络 Istio 在零信任方面构建较为完善,在服务网络中,存在 3 种角色:
控制面 Istiod:每个服务网络中存在一个 Istiod,作为控制面,统筹管理服务网络中的所有应用。
用户应用:用户运行的应用。
数据面 proxy:每个运行在服务网络中的用户应用均拥有一个 proxy 容器,其代理了用户应用的所有流量。所有从用户应用出的流量和入的流量均需经过 proxy 容器。
在服务网络中,关于零信任最核心的两个功能,认证和鉴权的具体行为如下:
认证:在 proxy 启动时,proxy 会携带 k8s 塞在容器里面的 token 向 istiod拿取证书。之后任何服务之间的调用均需双向认证。例如 A 向 B 发起请求,A需要验证 B 的证书,B 也需要验证 A 的证书。
鉴权:控制面 istiod 会向所有 proxy 分发鉴权规则,每个 proxy 会根据规则,放行请求或者拒绝请求。
Istio 方案的一些不足
Istio 的零信任方案已经非常优秀了,如果硬要说有哪些不足,我们在可以从如下方面考虑:
a. 在认证策略方面:Istio 的信任来源于 k8s 集群不会被人攻破,pod 中的 token 不会被人窃取。验证 token 时以信任 apiserver 为基础;istio 的认证策略单一。
b. 在身份标识方面:Istio 颁发的证书中,用于标识身份的字段 object 使用 spiff 标准。需要强依赖 K8s 的 namespace 和 serviceaccount;身份标识不够通用。
c. 在鉴权规则方面:Istio 的鉴权规则粒度十分细,但是有许多规则匹配与 K8s 的概念强依赖,例如要求请求必须来源于某个 namespace;其次仍有部分规则的缺失,例如应用部署环境级别的鉴权等等。
Sentinel 2.0:我们期望微服务下的安全底座该是什么样?
Aliware
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.
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.
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.
平滑升级:微服务零信任仍需努力的道路
Aliware
平滑升级的问题
虽然 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 协议。
兼容方案
从上述发现,在切换中间态时,一个应用的实例在接入零信任之后,必须同时拥有处理 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).
引文:
https://istio.io/latest/docs/reference/config/security/peer_authentication/#PeerAuthentication
那么根据上述要求,必须做一个应用实例的中间态,该中间态必须保证应用实例能够同时处理 http 和 https 请求,可以给出了如下两个方案:
a. 额外开启 https 端口的方案:该方案为新开启一个与 http 端口不重复的https端口,并在注册中心注明 2 个端口。例如,可以在注册中心的 metedata 中带入如下信息,标明双端口:
该方案最大的问题是兼容性不好,与服务网络、网关均无法很好适配,其次于大多数用户,新起端口有报备要求
b. 单端口双协议的方案:该方案最大的问题是不好实现,对于最常用的 tomcat而言,tomcat 原生不支持单端口双协议,但是对于 tomcat 中的 nio 模式,可以通过改造源码或者使用字节码增强技术实现单端口双协议。tomcat 的 arp 模式使用 native 模式根本无法实现切面。
让微服务应用无感、平滑升级至零信任方案,这是零信任方案是否能被大规模使用的关键条件之一,我们希望能够帮助我们企业微服务做到无感的默认安全。
展望
Aliware
为了让零信任更好、更便捷地落地,我们还有很多工作需要去完成。例如:
a. CI/CD 集成:将零信任的规则集成在 CI/CD 中,更加自动化的配置安全策略
b. 自适应调整:当一个应用外部环境改变,比如部署环境改变、应用从属部门改变,安全策略自适应的改变。
c. 默认安全:如何保证应用发布时无需配置,根据环境、开发者身份等等信息,自动生成安全策略,使得应用默认时安全的。
让微服务能够真正实现简单方便的零信任,仍有许多的道路要走,这或许只是个开始。也欢迎各位志同道合的同学们一起参与建设。
在开发者技术沙龙分享有关思考实践
相关链接:
[1] issue
https://github.com/alibaba/Sentinel/issues/3166
[2] OpenSergo issue
https://github.com/opensergo/opensergo-specification/issues/85