红帽白皮书新鲜出炉!点击获取,让你的云战略更胜一筹! 了解详情
写点什么

通过 Istio 重新实现微服务 (五):认证和授权

  • 2019-05-30
  • 本文字数:5038 字

    阅读完需:约 17 分钟

通过 Istio 重新实现微服务 (五):认证和授权

断路器和舱壁模式

在微服务架构中,有两个重要的模式,它们能够让服务实现自愈的效果。


断路器模式(Circuit Breake)能够阻止请求发送到不健康的服务实例上,这样的话,服务能够进行恢复,同时,客户端的请求将会转发到服务的健康实例上(增加了成功率)。


舱壁模式会隔离失败,避免整个系统宕机,举例来说,服务 B 处于有问题的状态,另外一个服务(服务 B 的客户端)往服务 B 发送请求将会导致客户端耗尽线程池,从而不能为其他请求提供服务(即便其他的请求与服务 B 毫无关系也是如此)。


这里我不再展示这些模式的实现了,因为我迫不及待想要分享认证和授权的功能,这些模式的实现你可以参考官方文档

Istio 中的认证和授权

我从来没有想到有一天我会对认证和授权感到如此兴奋。在技术领域,Istio 到底做了什么能够让我对这样恐怖的话题感到兴奋呢,更重要的是它为什么能够让你也为此感到兴奋呢?


这是因为,Istio 将这些责任从我们的服务中剥离了出去,并将其委托给了 Envoy 代理,这意味着当请求抵达我们的服务时,它们已经经过了认证和授权,我们只需要编写提供业务价值的代码就可以了。


让我们来深入了解一下!

使用 Auth0 进行认证

我们会使用 Auth0 作为身份识别和访问管理的服务器,它有一个试用方案,对我来说非常便利,我非常喜欢它!这也就是说,相同的理念可以用于任意的OpenID Connect实现,比如 KeyCloak、IdentityServer 等等。


首先,使用预设账号导航至Auth0 Portal,在 Applications > Default App 下创建一个租户,并记住 Domain,如下所示:



图 22 Auth0 管理门户中的 Default App


更新resource-manifests/istio/security/auth-policy.yaml文件,以便于使用该 Domain:


apiVersion: authentication.istio.io/v1alpha1 kind: Policymetadata:  name: auth-policyspec:  targets:  - name: sa-web-app  - name: sa-feedback  origins:  - jwt:    issuer: "https://{YOUR_DOMAIN}/"    jwksUri: "https://{YOUR_DOMAIN}/.well-known/jwks.json"   principalBinding: USE_ORIGIN

复制代码


借助该资源,Pilot 将会配置 envoy 在将请求转发至 sa-web-app 和 sa-feedback 服务之前进行认证。同时,它不会将该规则应用于服务sa-frontend的 envoy,这样的话,前端能够不用进行认证。为了应用该策略,我们需要执行如下命令:


$ kubectl apply -f resource-manifests/istio/security/auth-policy.yamlpolicy.authentication.istio.io "auth-policy" created
复制代码


回到页面并发送一个请求,我们将会看到 401 Unauthorized,现在,我们让来自前端的用户使用 Auth0 进行认证。

使用 Auth0 认证请求

要认证终端用户的请求,我们需要在 Auth0 中创建一个 API,它代表了经过认证的服务,比如评论、详情和评级。为了创建这样的 API,我们需要导航至 Auth0 Portal > APIs > Create API,如下图所示:



图 23 创建 API


这里重要的信息是 Identifier,在随后的脚本中,我们会以如下的形式进行使用:


  • Audience: {YOUR_AUDIENCE}


其余所需的细节在 Auth0 Portal 的 Applications 下进行配置,选择自动创建的 Test Application,其名称与 API 相同。


这里需要注意:


  • Domain: {YOUR_DOMAIN}

  • Client Id: {YOUR_CLIENT_ID}


在 Test Application 中向下滚动至 Allowed Callback URLs 文本域,在这里我们可以指定在认证完成后要转发到哪个 URL,在我们的场景中,应该是:


http://{EXTERNAL_IP}/callback


Allowed Logout URLs 中添加如下的 URL:


http://{EXTERNAL_IP}/logout


接下来,我们更新前端。

更新前端

切换至仓库的 auth0 分支。在这个分支中,前端包含了将用户转发至 Auth0 进行认证的代码变更,并在发送至其他服务的请求中包含了 JWT Token,如下所示:


analyzeSentence() {    fetch('/sentiment', {        method: 'POST',        headers: {        'Content-Type': 'application/json',        'Authorization': `Bearer ${auth.getAccessToken()}` // Access Token     },    body: JSON.stringify({ sentence: this.textField.getValue() })   })  .then(response => response.json())   .then(data => this.setState(data));}

复制代码


为了更新前端以便于使用租户的详细信息,我们导航至sa-frontend/src/services/Auth.js文件并替换如下的值,也就是在前文中我提醒注意的地方:


const Config = {  clientID: '{YOUR_CLIENT_ID}',  domain:'{YOUR_DOMAIN}',  audience: '{YOUR_AUDIENCE}',  ingressIP: '{EXTERNAL_IP}' // Used to redirect after authentication}

复制代码


应用已经就绪了,在如下的命令指定 docker user id,然后构建和部署变更:


$ docker build -f sa-frontend/Dockerfile \  -t {DOCKER_USER_ID}/sentiment-analysis-frontend:istio-auth0 sa-frontend
$ docker push {DOCKER_USER_ID}/sentiment-analysis-frontend:istio-auth0$ kubectl set image deployment/sa-frontend \ sa-frontend={DOCKER_USER_ID}/sentiment-analysis-frontend:istio-auth0
复制代码


我们可以尝试运行一下应用。访问的时候,你会被导航至 Auth0,在这里必要要登录(或注册),再次之后会重新转发会页面,这时候就可以发送认证后的请求了。如果你使用之前的 curl 命令的话,将会遇到 401 状态码,表明请求没有经过授权。


接下来,我们更进一步,对请求进行授权。

使用 Auth0 进行授权

认证能够让我们知道用户是谁,但是我们需要授权信息才能得知他们能够访问哪些内容。Istio 也提供了这样的工具。


作为样例,我们会创建两组用户(如图 24 所示):


  • Users:只能访问 SA-WebApp 和 SA-Frontend 服务;

  • Moderators:能够访问所有的三个服务。



图 24 授权的概念


为了创建用户组,我们需要使用 Auth0 Authorization 扩展,随后借助 Istio,我们为它们提供不同级别的访问权限。

安装和配置 Auth0 授权

在 Auth0 Portal 中,切换至 Extensions,并安装“Auth0 Authorization”扩展。安装之后,切换至 Authorization Extension 并点击右上角你的租户并选择“Configuration”选项进行配置。启用组并点击“Publish rule”按钮。



图 25 在 Token Contents 中激活组

创建 Group

在 Authorization Extension 中,导航至 Groups 并创建 Moderators 组。我们会将所有已认证过的用户视为普通的 User,这样的话,就没有必要额外再建立一个组了。


选择 Moderators 组并点击 Add Members,然后添加你的主账号。请将一些用户不放到任何的组中,以便于检验禁止他们访问(你可以通过 Auth0 Portal > Users > Create User 手动注册新用户)。

添加 Group Claim 到 Access Token

用户现在已经添加到组中了,但是这些信息还没有反映到 Access Token 中。为了确保 OpenID Connect 的一致性,同时又能返回组,我们需要在 token 中添加自定义的命名空间声明。这可以通过 Auth0 规则来实现。


要在 Auth0 Portal 中创建规则,我们需要导航至 Rules,点击“Create Rule”,并从模板中选择一个 empty rule



图 26 Rules 截屏


粘贴如下的代码并将规则命名为“Add Group Claim”。


function (user, context, callback) {   context.accessToken['https://sa.io/group'] = user.groups[0];   return callback(null, user, context);}
复制代码


注意: 这个规则会获取 Authorization Extension 中定义的第一个用户组,并将其添加到 access token 中,作为自定义命名空间的声明。


回到 Rules 页面,确保我们按照如下顺序有了两个角色:


  • auth0-authorization-extension

  • Add Group Claim


这里的顺序是非常重要的,因为组的字段会由auth0-authorization-extension异步获取,然后由第二个规则将其添加为命名空间声明,这将会形成如下的 access token:


{"https://sa.io/group": "Moderators","iss": "https://bookinfo.eu.auth0.com/", "sub": "google-oauth2|196405271625531691872"// [shortened for brevity]}
复制代码


现在,我们必须配置 Envoy 代理,从而能够在返回的 access token 中抽取https://sa.io/group声明的组。这是下一部分的主题,我们马上来看一下。

在 Istio 中配置授权

为了实现授权,我们为 Istio 启用 RBAC。为了实现这一点,要将如下的配置用到 Mesh 中:


apiVersion: "rbac.istio.io/v1alpha1" kind: RbacConfigmetadata:  name: defaultspec:  mode: 'ON_WITH_INCLUSION'  # 1  inclusion:    services:               # 2    - "sa-frontend.default.svc.cluster.local"    - "sa-web-app.default.svc.cluster.local"    - "sa-feedback.default.svc.cluster.local"

复制代码


  1. 仅为“Inclusion”字段中的服务和命名空间启用 RBAC;

  2. 包含如下服务。


执行如下命令,应用该配置:


$ kubectl apply -f resource-manifests\istio\security\enable-rbac.yamlrbacconfig.rbac.istio.io "default" created
复制代码


现在,所有的服务都需要基于角色的访问控制(Role-Based Access Control),换句话说,访问所有的服务都会遭到拒绝并且响应中会包含“RBAC: access denied”。我们下一节的主题就是启用对授权用户的访问。

配置常规的用户访问

所有的用户应该都能访问 SA-Frontend SA-WebApp 服务,这是通过如下的 Istio 资源实现的:


  • ServiceRole:指定用户具有的权限;

  • ServiceRoleBinding:指定 ServiceRole 应用到哪些用户上。


对于常规用户,我们允许访问指定的服务:


apiVersion: "rbac.istio.io/v1alpha1" kind: ServiceRolemetadata:  name: regular-user   namespace: defaultspec:   rules:  - services:    - "sa-frontend.default.svc.cluster.local"    - "sa-web-app.default.svc.cluster.local"     paths: ["*"]    methods: ["*"]
复制代码


然后,借助 regular-user-binding,我们将该 ServiceRole 应用到页面的所有访问者上:


apiVersion: "rbac.istio.io/v1alpha1" kind: ServiceRoleBindingmetadata:  name: regular-user-binding  namespace: default spec:  subjects:  - user: "*"  roleRef:    kind: ServiceRole    name: "regular-user"
复制代码


这里所说的所有用户,是不是意味着未认证的用户也能访问 SA WebApp 呢?并非如此,该策略依然会检查 JWT token 的合法性。


通过如下命令,应用该配置:


$ kubectl apply -f resource-manifests/istio/security/user-role.yamlservicerole.rbac.istio.io/regular-user createdservicerolebinding.rbac.istio.io/regular-user-binding created
复制代码

配置 Moderator 用户访问

对于 Moderator,我们想要启用对所有服务的访问:


apiVersion: "rbac.istio.io/v1alpha1" kind: ServiceRolemetadata:  name: mod-user  namespace: default spec:  rules:  - services: ["*"]    paths: ["*"]      methods: ["*"]
复制代码


但是,我们只想要绑定 Access Token 中https://sa.io/group声明的值为 Moderators 的用户。


apiVersion: "rbac.istio.io/v1alpha1" kind: ServiceRoleBindingmetadata:  name: mod-user-binding  namespace: default spec:  subjects:  - properties:    request.auth.claims[https://sa.io/group]: "Moderators"  roleRef:    kind: ServiceRole    name: "mod-user"
复制代码


要应用该配置,需要执行下述命令:


$ kubectl apply -f resource-manifests/istio/security/mod-role.yamlservicerole.rbac.istio.io/mod-user unchanged servicerolebinding.rbac.istio.io/mod-user-binding unchanged
复制代码


Envoy 中会有缓存,所以授权规则可能需要几分钟之后才能生效,在此之后,我们就可以校验 Users 和 Moderators 会有不同级别的访问权限。


坦白讲,你见过像这样毫不费力就能构建可扩展的认证和授权吗,还有比这更简单的过程吗?反正我是没有见过!接下来,我们总结一下得到的结论。

结论

想象不到吧?我们将其称为怪兽(Beast-io),因为它确实太强大了。借助它,我们能够:


  • 实现服务的可观察性,借助 Kiali,我们能够回答哪个服务在运行、它们的表现如何以及它们是如何关联在一起的;

  • 借助 Prometheus Grafana,能够实现指标收集和可视化,完全是开箱即用的

  • 使用 Jaeger(德语中猎人的意思)实现请求跟踪;

  • 对网络流量的完整且细粒度控制,能够实现金丝雀部署、A/B 测试和影子镜像

  • 很容易实现重试、超时和断路器

  • 极大地减少了为集群引入新服务的开销;

  • 为各种语言编写的微服务添加了认证和授权功能,服务端代码无需任何修改。



图 27 Beast-io!


有个这个 Beastio,我们就能让团队真正地提供业务价值,并将资源聚焦在领域问题上,不用再担心服务的开销,让服务真正做到“微型化”。


我是 Rinor Maloku,非常感谢 Istio 之旅。你可以在Twitter 关注我,也可以访问我的主页 rinormaloku.com


2019-05-30 08:009615

评论

发布
暂无评论
发现更多内容

人肉智能#003:为什么 Instagram 曾经值得尊敬

光毅

facebook Instagram

架构实战营-模块三作业

🐢先生

架构实战营

EasyRecovery16操作简单的电脑数据恢复软件

茶色酒

EasyRecovery16

阿里巴巴内网Java面试2000题解析(2023最新版)

架构师之道

编程 java面试

2023-02-25:请用go语言调用ffmpeg,解码mp4文件并保存为YUV420SP格式文件,YUV420P不要转换成YUV420SP。

福大大架构师每日一题

golang ffmpeg 福大大

Zebec社区上线ZIP-2(地平线升级行动)提案,海量激励将被释放

威廉META

CorelDRAW2023序列号及安装下载教程

茶色酒

CorelDraw2023

EasyRecovery Photo16最新版本有哪些新功能?

茶色酒

EasyRecovery Photo16

Zebec社区上线ZIP-2(地平线升级行动)提案,海量激励将被释放

股市老人

FL Studio21水果最新完整版音乐编曲软件

茶色酒

FL Studio 21 FL Studio21

Java 数据类型(全网最全)

kcodez

Java 后端

三天吃透MySQL八股文(2023最新整理)

程序员大彬

Java MySQL 数据库

time_point 的基本用法举例

老王同学

C++11

Zebec社区上线ZIP-2(地平线升级行动)提案,海量激励将被释放

EOSdreamer111

Python:Excel自动化实践入门篇 甲【送图书门票】

eng八戒

python excel

Python电影售票系统

漫步桔田

秒懂算法 | 回归算法中的贝叶斯

TiAmo

算法 贝叶斯公式 贝叶斯算法

Amazon S3 服务15岁生日快乐!

亚马逊云科技 (Amazon Web Services)

数据库 云计算

数字孪生在智慧物流中的应用价值

申扬科技

智慧物流 数字孪生

速来~与 Werner Vogels 博士一起探索敏捷性与创新速度一起提升的秘方

亚马逊云科技 (Amazon Web Services)

电影排行榜(requests + bs4 & scrapy)

漫步桔田

for循环中声明变量的一个问题回顾

老王同学

c++

Zebec社区上线ZIP-2(地平线升级行动)提案,海量激励将被释放

西柚子

极客时间架构训练营模块八作业-消息队列存储消息数据的 MySQL 表格

张Dave

精华推荐 |【算法数据结构专题】「延时队列算法」史上非常详细分析和介绍如何通过时间轮(TimingWheel)实现延时队列的原理指南

洛神灬殇

数据结构 延时队列 算法框架 DelayedQueue 时间轮(TimeWheel)

EasyRecovery16和谐版手机数据恢复软件下载

茶色酒

EasyRecovery16

如何使用开源构建可信赖的人工智能

开源雨林

人工智能 开源

引领下一代操作系统技术方向 第一届OpenHarmony技术峰会成功召开

Geek_2d6073

PHP Windows 下 XAMPP 的 xdebug 配置

HoneyMoose

Zebec社区上线ZIP-2(地平线升级行动)提案,海量激励将被释放

鳄鱼视界

Python 数学练习题(一)

漫步桔田

通过 Istio 重新实现微服务 (五):认证和授权_文化 & 方法_Rinor Maloku_InfoQ精选文章