Istio系列一:Istio的认证授权机制分析

【编者的话】随着虚拟化技术的不断发展,以容器为核心的微服务概念被越来越多人认可,Istio因其轻量级、服务网格管理理念、兼容各大容器编排平台等优势在近两年脱颖而出,许多公司以Istio的架构设计理念作为模板来管理自己的微服务系统,但在其势头发展猛劲之时,也有许多专家针对Istio的安全机制提出了疑问,比如在Istio管理下,服务间的通信数据是否会泄露及被第三方劫持的风险;服务的访问控制是否做到相对安全;Istio如何做安全数据的管理等等,这些都是Istio目前面临的安全问题,而我们只有深入分析其机制才能明白Istio是如何做安全的。

本文为Istio系列的首篇,后续还有三篇分别对Istio组件Envoy、Pilot、Mixer的原理解读,本篇作为开胃菜,首先介绍Istio背景及主要架构,再从身份认证和授权鉴权两方面对Istio的认证授权机制加以剖析,最后通过实验分析具体讲述Istio如何做访问控制,希望能给各位读者带来收获。 如果你想和更多Istio技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

Istio概述

Istio是由Google、IBM、Lyft联合开发的开源项目,它是一款微服务管理框架,也被称为第二代微服务Service Mesh代表。Istio的架构思想类似软件定义网络(SDN),其主要分为控制平面和数据平面两个部分,下图为Istio官方架构图:

image

图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官方授权鉴权的架构图如下所示:

image

图5 Istio授权、鉴权架构图

根据图5整个工作流程可描述为:

  • 用户使用yaml文件指定Istio授权策略。部署后,Istio将策略存至Istio Config Store中;
  • Pilot通过Kubernetes API Server监控Istio授权策略变更,如果有更改,则获取最新的授权策略;
  • Pilot将授权策略下发至服务的Envoy代理;
  • 每个Envoy代理都运行一个授权引擎,在引擎运行时对请求进行授权。

实验分析

同样以bookinfo demo举例,默认情况下,各个服务间通过页面可以进行访问,我们通过:

kubectl create -f samples/bookinfo/platform/kube/rbac/rbac-config-ON.yaml

可以开启授权模式,yaml文件如下图所示:

image

图6 开启授权模式yaml文件内容

再次访问bookinfo页面时如下图所示:

image

图7 访问productpage页面

由图7可知,Istio授权行为默认为拒绝,所以必须要显示授权,才能对其服务进行访问。

实验前提:

  • 部署bookinfo实例时用TLS模式,即istio-demo-auth.yaml。
  • 在下面实验里,我们会在 Service account的基础上启用访问控制,为了给不同的微服务以不同的访问授权,需要建立一系列不同的 Service account,用这些账号来分别运行Bookinfo中的微服务。

创建Service Account需执行以下命令:

kubectl create -f bookinfo-add-serviceaccount.yaml

此命令创建了两个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命名空间内的服务进行访问。

执行以下命令:

istioctl create -f samples/bookinfo/platform/kube/rbac/namespace-policy.yaml

返回结果如下:

图9 创建基于命名空间的授权策略

我们简单看下这个策略文件内容:

image

图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服务的访问,执行以下命令:

kubectl apply -f productpage-policy.yaml

返回结果如下:

图13 productpage页面的访问策略执行

我们简单看下productpage-policy.yaml文件的内容:

image

图14 productpage页面的访问策略内容

如图14所示,该策略首先创建了一个名为productpage-viewer的ServiceRole,其作用为允许到productpage服务的读取访问;再创建一个名为bind-productpage-viewer的ServiceRoleBinding,将角色productpage-viewer赋给所有用户。

再次浏览bookinfo demo页面,如下图所示:

图15 访问加载了productpage策略的页面

由上图我们可以看出,访问productpage服务没有问题了,但左边红框的detail服务和右边红框的review服务显示无法访问,这是因为我们还没有给productpage访问details和reviews的权限,所以我们再执行下一条命令:

istioctl create -f samples/bookinfo/platform/kube/rbac/details-reviews-policy.yaml

返回结果如下:

图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服务,所以执行以下命令:

istioctl create -f samples/bookinfo/platform/kube/rbac/ratings-policy.yaml

我们简单看下文件内容,如下图所示:

image

图19 对ratings服务授权的yaml文件内容

执行文件后再次访问页面如下图所示:

可以看到已经可以正常访问所有的服务。

通过以上两个实验结果可以看出,服务之间想要互相访问首先需要服务账户(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想实现全面的安全防护还需更多的精力投入。

下一篇将介绍Istio的数据平面组件Envoy,笔者会详细解释Envoy在Istio中是如何部署以及如何对入站出站流量进行代理转发及流量劫持,欢迎各位读者持续关注。

原文链接:https://mp.weixin.qq.com/s/Qpj_AAHmFN3w-Sos_C-mHg