Istio 的认证授权机制分析
随着虚拟化技术的不断发展,以容器为核心的微服务概念被越来越多人认可,Istio 因其轻量级、服务网格管理理念、兼容各大容器编排平台等优势在近两年脱颖而出,许多公司以 Istio 的架构设计理念作为模板来管理自己的微服务系统,但在其势头发展猛劲之时,也有许多专家针对 Istio 的安全机制提出了疑问,比如在 Istio 管理下,服务间的通信数据是否会泄露及被第三方劫持的风险;服务的访问控制是否做到相对安全;Istio 如何做安全数据的管理等等,这些都是 Istio 目前面临的安全问题,而我们只有深入分析其机制才能明白 Istio 是如何做安全的。
首先介绍 Istio 背景及主要架构,再从身份认证和授权鉴权两方面对 Istio 的认证授权机制加以剖析,最后通过实验分析具体讲述 Istio 如何做访问控制,希望能给各位读者带来收获。
Istio 概述
Istio 是由 Google、IBM、Lyft 联合开发的开源项目,它是一款微服务管理框架,也被称为第二代微服务 Service Mesh 代表。Istio 的架构思想类似软件定义网络(SDN),其主要分为控制平面和数据平面两个部分,下图为 Istio 官方架构图:
图 1 Istio 架构图
数据平面由一组 Sidecar 方式部署的智能代理 Envoy(上图中的 Proxy)组成,Envoy 是 Istio 默认的数据平面代理,这些代理用于调节和控制服务及 Mixer 之间的所有网络通信。
控制平面按各自功能分为以下四个组件:
Mixer:负责前置条件检查(例如 ACL 检查、白名单检查、日志检查),遥测报告上报(日志、监控、配额),配额管理(限流);
Pilot:负责管理和配置 Envoy,将流量路由到各服务中;
Galley:在 Istio 1.1 版本被引入,是整个控制平面的配置管理中心,除了提供配置验证功能以外,还负责配置的管理和分发,使用网络配置协议和其它组件进行配置交互;
Citadel:负责管理密钥和证书,用于保证数据平面各服务的通信安全;
下面本文将着重介绍 Istio 在安全方面的设计和实现机制。
Citadel 认证授权机制
Istio 的认证授权机制主要是由 Citadel 完成,同时需要和其它组件一起配合,参与到其中的组件还有 Pilot、Envoy、Mixer,它们四者在整个流程中的作用分别为:
Citadel:用于负责密钥和证书的管理,在创建服务时会将密钥及证书下发至对应的 Envoy 代理中;
Pilot:用于接收用户定义的安全策略并将其整理下发至服务旁的 Envoy 代理中;
Envoy:用于存储 Citadel 下发的密钥和证书,保障服务间的数据传输安全;
Mixer:负责管理授权完成审计工作。
Istio 通过以上四个组件的协同工作可实现服务间的身份认证以及授权鉴权功能。
下面从 Istio 在 Kubernetes 中的工作流程来举例,如下图所示:
图 2 Istio 拓扑图
具体工作流程可描述如下:
Kubernetes 某集群节点新部署了服务 A 和服务 B,此时集群中有两个 Pod 被启动,每个 Pod 由 Envoy 代理容器和 Service 容器构成,在启动过程中 Istio 的 Citadel 组件会将密钥及证书依次下发至每个 Pod 中的 Envoy 代理容器中,以保证后续服务 A、B 之间的安全通信。
用户通过 Rules API 下发安全策略至 Pilot 组件,Pilot 组件通过 Pilot-discovery 进程整理安全策略中 Kubernetes 服务注册和配置信息并以 Envoy API 方式暴露给 Envoy。
Pod A、B 中的 Envoy 代理会通过 Envoy xDS API 方式定时去 Pilot 拉取安全策略配置信息,并将信息保存至 Envoy 代理容器中。
当服务 A 访问服务 B 时,会调用各自 Envoy 容器中的证书及密钥实现服务间的 mTLS 通信,同时 Envoy 容器还会根据用户下发的安全策略进行更细粒度的访问控制。
Mixer 在整个工作流中核心功能为前置条件检查和遥测报告上报,在每次请求进出服务 A,B 时,服务 A、B 中的 Envoy 代理会向 Mixer 发送 check 请求,检查是否满足一些前提条件,比如 ACL 检查,白名单检查,日志检查等,如果前置条件检查通过,处理完后再通过 Envoy 向 Mixer 上报日志,监控等数据,从而完成审计工作。
身份认证分析
Istio 官方的身份认证架构如下图所示:
图 3 Istio 的身份认证架构图
Istio 的身份认证流程可以概括为以下步骤:
Administrators 制定三个策略,其中一个策略作用域在 Foo 命名空间,目标服务为 Service A,另两个策略作用域在 Bar 命名空间,目标服务分别为 Service B 和 All。
Pilot 根据 Administrators 下发的三个策略中的元数据(Pod、Service、Deployment 等)调用 Kubernetes API Server 监视配置存储,有任何策略变更后,会将新策略转换为适当配置并将身份验证以及凭证中的其它声明(如果适用)输出至服务对应的 Envoy 代理。
Envoy 代理根据身份验证信息以及凭证信息对服务进行访问验证。
以传输身份认证举例,传输身份验证可以理解为服务到服务的身份验证,Istio 提供 mTLS(双向 TLS)功能来实现。
在整个身份认证过程中,涉及到两种证书和私钥,分别为 root-cert.pem、cert-chain.pem、key.pem。其中:
root-cert.pem:用于证书校验的根证书,所有 Envoy 共用同一个 root-cert.pem;
cert-chain.pem:Envoy 的证书,由 root-cert.pem 签署,会在需要时提供给对端的服务;
key.pem:Envoy 的私钥,和 cert-chain.pem 中的证书相匹配;
这三个文件在当服务被创建时,由 Citadel 组件管理并传递至服务对应的 Envoy 代理中。
以 Istio 官方的 bookinfo demo 举例,我们可以进到 productpage 服务的 Envoy 代理容器中查看/etc/certs 目录,如下图所示:
图 4 Envoy 代理容器的证书及密钥
图中红框部分即为证书和私钥,证书的有效期限默认为三个月,过期后 Citadel 会对证书密钥进行更换。
当开启了 mTLS 后,服务间的流量为加密流量,并且相互根据证书以及密钥进行访问从而保障服务间的通信安全。值得一提的是,在同一个 Pod 中服务与 Envoy 代理之间通信采用的是 localhost,不使用加密流量。
另外,一般会出现这种情况,即没有注入 Envoy 代理容器的服务与设置了 mTLS 的服务之间通讯,通常情况下为了微服务安全性需要为未注入 Sidecar 容器的服务进行证书配置,或是在访问时附加上密钥及证书信息。
授权鉴权分析
Istio 中服务间的授权鉴权主要由 Kubernetes 的 RBAC(基于角色的访问控制)功能实现。
在微服务架构中,服务间的调用我们可以理解为一个 C-S 模式。在默认不开启授权的情况下,服务间的访问是无限制的。开启授权模式后,默认情况下必须显示进行授权才能对服务进行访问。
这里会有两种级别的访问控制:
命名空间级别
指定命名空间内的所有(或部分)服务可以被另一命名空间的所有(或部分)服务所访问,需要用户创建 ServiceRole、ServiceRoleBinding 策略来实现此过程。
服务级别
指定服务可以被另一个服务访问,需要用户创建 Service Account、ServiceRole、ServiceRoleBinding 策略来实现此过程。
Istio 官方授权鉴权的架构图如下所示:
图 5 Istio 授权、鉴权架构图
根据图 5 整个工作流程可描述为:
用户使用 yaml 文件指定 Istio 授权策略。部署后,Istio 将策略存至 Istio Config Store 中;
Pilot 通过 Kubernetes API Server 监控 Istio 授权策略变更,如果有更改,则获取最新的授权策略;
Pilot 将授权策略下发至服务的 Envoy 代理;
每个 Envoy 代理都运行一个授权引擎,在引擎运行时对请求进行授权。
实验分析
同样以 bookinfo demo 举例,默认情况下,各个服务间通过页面可以进行访问,我们通过:
可以开启授权模式,yaml 文件如下图所示:
图 6 开启授权模式 yaml 文件内容
再次访问 bookinfo 页面时如下图所示:
图 7 访问 productpage 页面
由图 7 可知,Istio 授权行为默认为拒绝,所以必须要显示授权,才能对其服务进行访问。
实验前提:
部署 bookinfo 实例时用 TLS 模式,即 istio-demo-auth.yaml。
在下面实验里,我们会在 Service account 的基础上启用访问控制,为了给不同的微服务以不同的访问授权,需要建立一系列不同的 Service account,用这些账号来分别运行 Bookinfo 中的微服务。
创建 Service Account 需执行以下命令:
此命令创建了两个 ServiceAccount,如下图所示:
图 8 查看添加的两个 Service Account
命名空间级的访问控制
Bookinfo demo 中,productpage、reviews、details 以及 ratings 服务被部署在 default 命名空间中,而 istio-ingressgateway 等 Istio 组件是部署在 istio-system 命名空间中。我们可以定义一个策略,default 命名空间中所有服务,如果其 app label 取值在 productpage、reviews、details 以及 ratings 范围内,就可以被 default 命名空间内以及 istio-system 命名空间内的服务进行访问。
执行以下命令:
返回结果如下:
图 9 创建基于命名空间的授权策略
我们简单看下这个策略文件内容:
图 10 基于命名空间的策略内容
如上图红框部分所示,这个策略文件定义了两个对象分别为 ServiceRole 和 ServiceRoleBinding,含义如下所示:
ServiceRole:创建名为 service-viewer 的 ServiceRole,允许访问 default 命名空间中所有 app 标签值在 productpage、reviews、details 以及 ratings 范围之内的服务。
ServiceRoleBinding:创建 ServiceRoleBinding 对象,用来把 service-viewer 角色指派给所有 istio-system 和 default 命名空间的服务。
执行命令后,再通过网页访问 bookinfo 样例,发现服务正常了,如下图所示:
图 11 访问加载了命名空间的策略页面
服务级的访问控制
执行下一策略之前先将之前的策略移除,如图 12 所示:
图 12 移除基于命名空间访问的策略
服务级的访问控制由于 Bookinfo demo 的服务调用关系,需要逐步进行访问控制,首先允许到 ProductPage 服务的访问,执行以下命令:
返回结果如下:
图 13 productpage 页面的访问策略执行
我们简单看下 productpage-policy.yaml 文件的内容:
图 14 productpage 页面的访问策略内容
如图 14 所示,该策略首先创建了一个名为 productpage-viewer 的 ServiceRole,其作用为允许到 productpage 服务的读取访问;再创建一个名为 bind-productpage-viewer 的 ServiceRoleBinding,将角色 productpage-viewer 赋给所有用户。
再次浏览 bookinfo demo 页面,如下图所示:
图 15 访问加载了 productpage 策略的页面
由上图我们可以看出,访问 productpage 服务没有问题了,但左边红框的 detail 服务和右边红框的 review 服务显示无法访问,这是因为我们还没有给 productpage 访问 details 和 reviews 的权限,所以我们再执行下一条命令:
返回结果如下:
图 16 details 和 reviews 的访问策略执行
我们简单看下 details-reviews-policy.yaml 这个文件的内容:
图 17 details 和 reviews 的访问策略内容
由图 17 可看出,该策略首先创建了一个名为 details-reviews-viewer 的 ServiceRole 对象,它的作用为允许对 details 和 reviews 服务进行只读访问;紧接着又创建了一个名为 bind-details-reviews 的 ServiceRoleBinding 对象,用于将 details-reviews-viewers 对象绑定到 cluster.local/ns/default/sa/bookinfo-productpage 中去,也就是 bookinfo-productpage 服务的 ServiceAccount,再次访问页面,如下图所示:
图 18 访问加载 details 和 reviews 策略的页面
由图 18 可看出,details 和 reviews 服务可以被访问了,但 ratings 服务仍然无法访问,原因是 reviews 服务无权访问 ratings 服务,要更正这一问题,就应该给 ratings 服务授权,使其能够访问 reviews 服务,所以执行以下命令:
我们简单看下文件内容,如下图所示:
图 19 对 ratings 服务授权的 yaml 文件内容
执行文件后再次访问页面如下图所示:
图 20 访问加载了 ratings 策略的页面
可以看到已经可以正常访问所有的服务。
通过以上两个实验结果可以看出,服务之间想要互相访问首先需要服务账户(Service Account),以上两个实验的 Service Account 为 productpage 和 reviews,具体为何需要这两个 Service Account 是因为 productpage 服务需要允许对 reviews 和 details 两个服务的访问权限,reviews 服务需要允许对 Ratings 服务的访问权限。这就是实验开始前添加这两个 ServiceAccount 的原因,相信各位读者看到这里应该知道 Istio 是如何做访问控制了,我们可以看出其核心还是调用了 Kubernetes 中的 RBAC 机制。
总结
将单一应用程序拆分为微服务带来了各种好处,包括更好的灵活性、可伸缩性以及服务复用的能力,但微服务也面临着许多特殊的安全需求,比如防止中间人攻击,需要服务间进行流量加密;为了提供灵活的访问控制,需要 mTLS 和更细粒度的访问策略;还有大量的数据需要审计工具来进行审计。
Istio 在设计之初就将部分安全机制考虑了进去,Istio 官方宣称在安全上目标要达到默认安全(即应用程序代码和基础结构无需更改)、深度防御(与现有安全系统集成,提供多层防御)、零信任网络(在不受信任的网络上构建安全解决方案)。
目前看来 Istio 在安全方面的实现还是主要依靠 Kubernetes 一些内置的安全机制。对于微服务的身份认证主要依赖于与双向 TLS 及 JSON Web Token;授权鉴权策略主要依赖于 Kubernetes 中的 RBAC 机制,使用 ServiceRole 以及 ServiceRoleBinding 等 CRD(CustomResourceDefinition)对象来实现。微服务安全任重而道远,Istio 想实现全面的安全防护还需更多的精力投入。
评论