凡是产生连接关系,就必定带来安全问题,人类社会如此,服务网格世界,亦是如此。
今天,我们就来谈谈Istio第二主打功能**---**保护服务。
那么,便引出3个问题:
l Istio凭什么保护服务?
l Istio具体如何保护服务?
l 如何告诉Istio发挥保护能力?
1 Istio凭什么保护服务?
将单体应用程序分解为一个个服务,为大型软件系统的开发和维护带来了诸多好处,比如更好的灵活性、可伸缩性和可复用性。但这也带来了一些安全问题:
l 为了抵御中间人攻击,需要对流量进行加密
l 为了提供灵活的服务访问控制,需要 mTLS(双向的安全传输层协议)和细粒度的访问策略
l 要审计谁在什么时候做了什么,需要审计工具
Istio 尝试提供全面的安全解决方案来解决这3个问题。
如上图所示,
Istio 安全的三大目标是:
l 默认安全(Security by default):应用程序代码和基础结构,无需更改。
l 深度防御(Defense in depth):与现有安全系统集成,提供多层防御。
l 零信任网络(Zero-trust network):在不受信任的网络上,构建安全解决方案。
为了实现这3个目标,Istio 安全功能提供了4大守护系统:
l 强大的身份(Identity)系统
l 健壮的策略(Policy)系统
l 认证,授权和审计(AAA:Authentication,Authorization,Accounting)系统,用于保护服务和数据
l 透明的 TLS 加密(Encryption)系统。
就保护对象而言,Istio 安全系统可以抵御来自内部或外部的威胁,这些威胁主要针对服务网格内的端点(Endpoints),通信(Communication),平台(Platform)和数据(Data)。
2 Istio具体如何保护服务?
在安全方面,Istio具备3个远大的目标,配备了4大守护系统,那么它到底是通过怎样的架构实现这个目标的呢,又通过什么样的安全基础设施,和kubernetes配合呢?
2.1 Istio安全架构
如上图,与Istio的4大守护系统相对应,Istio 中涉及安全的组件有:
l Pilot :将授权策略和安全命名信息分发给代理
l Proxy :实现客户端和服务端之间的安全通信
l Citadel :用于密钥和证书管理
l Mixer :管理授权和审计
由此可见,Pilot不仅负责流量规则和策略的分发,还负责安全相关策略的下发,有点像皇上的贴身太监,负责宣读圣旨;Proxy有点像各州属的州官,负责奉天承运;Citadel有点像玉玺和虎符,负责鉴真去假;Mixer有点像三省六部,负责授权审计。
2.2 两个安全基本概念
2.2.1 Identity
身份(Identity)是几乎所有安全基础架构的基本概念。在服务和服务的通信开始前,双方必须用其身份信息交换凭证,以达到相互认证的目的。在客户端,根据安全命名(secure naming)信息,检查服务端的标识,以查看它是否是该服务的授权运行程序;在服务端,服务端可以根据授权策略(authorization policies)信息,确定客户端可以访问哪些数据,审计其在什么时间访问了什么,拒绝未授权客户端的访问。
在 Istio 身份模型中,Istio 使用一流的服务标识来确定服务的身份。这为表示人类用户,单个服务或一组服务提供了极大的灵活性和粒度。在没有此类身份的平台上,Istio 可以使用可以对服务实例进行分组的其他身份,例如服务名称。
不同平台上的 Istio 服务标识:
l Kubernetes: Kubernetes 服务帐户
l GKE/GCE: 可以使用 GCP 服务帐户
l AWS: AWS IAM 用户/角色 帐户
l On-premises (non-Kubernetes): 用户帐户,自定义服务帐户,服务名称,istio 服务帐户或 GCP 服务帐户。
做个类比,京东和天猫都有自己的一套非常成熟的服务账户系统,淘宝只需要复用天猫的账户系统即可,无需重新开发一套,这样我们就可以用天猫的账号,直接登录淘宝。而Istio也更倾向于复用业界一流的服务账户系统,如Kubernetes和AWS的,但也可以自定义服务账户,并按需复用Kubernetes的账户系统。
2.2.2 PKI
Istio PKI(Public Key Infrastructure)建立在 Istio Citadel 之上,可为每个工作负载提供安全且强大的工作负载标识。Istio 使用 X.509 证书来携带 SPIFFE 格式的身份信息。PKI 还可以大规模自动化地进行密钥和证书轮换。
Istio 支持在 Kubernetes pod 和本地计算机上运行的服务。目前,Istio为每个方案使用不同的证书密钥配置机制,下面试举例Kubernetes方案的配置过程:
- Citadel 监视 Kubernetes apiserver,为每个现有和新的服务帐户创建 SPIFFE 证书和密钥对。 Citadel 将证书和密钥对存储为 Kubernetes secrets。
- 创建 pod 时,Kubernetes 会根据其服务帐户通过 Kubernetes secret volume 将证书和密钥对挂载到 pod。
- Citadel 监视每个证书的生命周期,并通过重写 Kubernetes secret 自动轮换证书。
- Pilot 生成安全命名信息,该信息定义了哪些服务帐户可以运行某个服务。接着Pilot 将安全命名信息传递给 Envoy。
3 如何告诉Istio发挥保护能力?
如上一章节所言,Istio基于控制面组件,引入了一流的服务账户系统,结合强大的PKI,实现了对服务网格的安全守护。同时,Istio也开放了接口,让我们可以进行精细化的配置,全方位满足我们对服务的安全需求。
服务安全,总是离不开两个具体过程:认证(Authentication)和鉴权(Authorization)。Istio通过Policy和MeshPolicy文件,实现对认证相关功能的定义;通过RbacConfig、ServiceRole和ServiceRoleBinding文件,实现对鉴权相关功能的启用和定义。
让我们举个几个通俗的例子来区分认证和鉴权:
进火车站需要提供身份证和火车票,身份证可以证明你就是你,这是认证;火车票可以证明你有权上那趟火车,这是授权。
又例如,你要访问自己淘宝的购物车,需要先登录,这是认证。你要访问朋友的购物车,就需要他的允许,这是授权。
再例如,有经验的朋友能发现浏览器经常会面对两个错误码:401和403。通常而言,401就是未登录的意思,需要认证;403就是禁止访问的意思,需要授权。
3.1 认证
Istio 提供两种类型的身份认证:
l 传输身份认证,也称为服务到服务身份认证:对直连客户端进行验证。Istio 提供双向TLS作为传输身份认证的全栈解决方案。我们可以轻松启用此功能,而无需更改服务代码。这个解决方案:
l 为每个服务提供强大的身份认定,以实现跨群集和跨云的互操作性。
l 保护服务到服务通信和最终用户到服务通信。
l 提供密钥管理系统,以自动执行密钥和证书生成,分发和轮换。
l 来源身份认证,也称为终端用户身份认证:对来自终端用户或设备的原始客户端请求进行验证。Istio 通过 JSON Web Token(JWT)、Auth0、Firebase Auth、Google Auth 和自定义身份认证来简化开发者的工作,使之轻松实现请求级别的身份认证。
在这两种情况下,Istio 都通过自定义 Kubernetes API 将身份认证策略存储在 Istio 配置存储(Istio config store)中。Pilot会在适当的时候进行同步,为每个Proxy更新其最新状态以及密钥。此外,Istio 支持在许可模式下进行身份认证,以帮助我们理解策略变更前后,服务的安全状态是如何变化的。
3.1.1 认证架构
我们可以使用身份认证策略,为 Istio 网格中接收请求的服务指定身份认证要求。我们使用 .yaml 文件来配置策略,策略将保存在 Istio 配置存储中。在任何策略变更后,Pilot 会将新策略转换为适当的配置,下发给Envoy,告知其如何执行所需的身份认证机制。Pilot 可以获取公钥并将其附加到 JWT 进行配置验证。或者,Pilot 提供 Istio 系统管理的密钥和证书的路径,并将它们安装到负载 Pod 中,以进行双向 TLS。
本文多次提到双向TLS认证,让我们理解一下其在Istio里的实现。Istio 通过客户端和服务端各自配备的Envoy进行通信,也就是说,客户端和服务端的流量,是被各自的Envoy接管了的。对于客户端调用服务端,遵循的步骤是:
- Istio 将出站流量从客户端重新路由到客户端的本地 Envoy。
- 客户端 Envoy 与服务端 Envoy 开始双向 TLS 握手。在握手期间,客户端 Envoy 还执行安全命名检查,以验证服务证书中提供的服务帐户是否有权运行目标服务。
- 客户端 Envoy 和服务端 Envoy 建立了一个双向的 TLS 连接,Istio 将流量从客户端 Envoy 转发到服务端 Envoy。
- 授权后,服务端 Envoy 通过本地 TCP 连接将流量转发到服务端的服务。
3.1.2 认证策略配置
和其他的 Istio 配置一样,可以用 .yaml 文件的形式来编写认证策略,然后使用 Istioctl 二进制工具进行部署。如下图的配置,通过配置Policy文件,对reviews服务进行了传输身份认证的配置,要求其必须使用双向TLS做认证。
apiVersion: "authentication.Istio.io/v1alpha1"
kind: "Policy"
metadata:
name: "reviews"
spec:
targets:
- name: reviews
peers:
- mtls: {}
3.2 授权
Istio 的授权功能,也称为基于角色的访问控制(RBAC),为 Istio 服务网格中的服务提供命名空间级别,服务级别和方法级别的访问控制。它的特点是:
l 基于角色的语义,简单易用。
l 包含服务到服务和终端用户到服务两种授权模式。
l 通过自定义属性灵活定制授权策略,例如条件,角色和角色绑定。
l 高性能,因为 Istio 授权功能是在 Envoy 里执行的。
3.2.1 授权架构
上图显示了基本的 Istio 授权架构。和认证的生效过程一样,运维人员使用.yaml文件指定 Istio 授权策略。部署后,Istio 将策略保存在Istio Config Store中。Pilot 会一直监视 Istio 授权策略的变更,如果发现任何更改,它将获取更新的授权策略,并将 Istio 授权策略分发给与服务实例位于同一 pod 内的 Envoy 代理。
每个 Envoy 代理都运行一个授权引擎,该引擎在运行时授权请求。当请求到达代理时,授权引擎根据当前授权策略评估请求上下文,并返回授权结果ALLOW或DENY。
3.2.2 授权策略配置
我们可以使用 RbacConfig 启用授权策略,并使用ServiceRole和ServiceRoleBinding配置授权策略。
RbacConfig是一个网格维度的单例,其固定名称值为default,也就是说我们只能在网格中配置一个RbacConfig实例。与其他 Istio 配置对象一样,RbacConfig被定义为Kubernetes CustomResourceDefinition (CRD)对象。
在RbacConfig中,运算符可以指定mode值,它可以是:
l OFF:禁用 Istio 授权。
l ON:为网格中的所有服务启用了 Istio 授权。
l ON_WITH_INCLUSION:仅对包含字段中指定的服务和命名空间启用 Istio 授权。
l ON_WITH_EXCLUSION:除了排除字段中指定的服务和命名空间外,网格中的所有服务都启用 Istio 授权。
在以下示例中,为default命名空间启用了 Istio 授权,。
apiVersion: "rbac.Istio.io/v1alpha1"
kind: RbacConfig
metadata:
name: default
namespace: Istio-system
spec:
mode: 'ON_WITH_INCLUSION'
inclusion:
namespaces: ["default"]
针对服务和命名空间启用授权后,我们还需要配置具体的授权策略,这通过配置ServiceRole和ServiceRoleBinding实现。与其他 Istio 配置对象一样,它们同样被定义为CRD对象。
ServiceRole定义了一组访问服务的权限。ServiceRoleBinding向特定对象授予 ServiceRole,例如用户,组或服务。
ServiceRole 和 ServiceRoleBinding 组合规定了: 允许谁在哪些条件下做什么,具体而言:
l 谁指的是 ServiceRoleBinding 中的 subject 部分。
l 做什么指的是 ServiceRole 中的 rule 部分。
l 哪些条件指的是我们可以在 ServiceRole 或 ServiceRoleBinding 中使用Istio Attributes指定的 condition 部分。
让我们再举一个简单的例子,如下图,ServiceRole和 ServiceRoleBinding的配置规定:将所有用户(user=“*”)绑定为(products-viewer)角色,这个角色可以对products这个服务发起GET或HEAD请求,但是其限制条件是请求头必须包含version,且值为v1或v2。
apiVersion: "rbac.Istio.io/v1alpha1"
kind: ServiceRole
metadata:
name: products-viewer
namespace: default
spec:
rules:
- services: ["products"]
methods: ["GET", "HEAD"]
constraints:
- key: request.headers[version]
values: ["v1", "v2"]
---
apiVersion: "rbac.Istio.io/v1alpha1"
kind: ServiceRoleBinding
metadata:
name: binding-products-allusers
namespace: default
spec:
subjects:
- user: "*"
roleRef:
kind: ServiceRole
name: "products-viewer"
至此,我们做个简单的总结:单体应用程序拆分成成千上百个服务后,带来了安全问题,Istio尝试在由服务组成的服务网格里,加入了一套全栈解决方案。这套方案里,Istio默默处理了大部分安全基础设施,但也暴露了认证和授权两个功能让用户进行自定义配置。我们通过Policy、MeshPolicy以及RbacConfig、ServiceRole、ServiceRoleBinding就可以完成对认证和授权环节所有功能的配置,而不需要侵入地改动任何服务的代码。