回到Istio的微服务。 第三部分



注意事项 佩雷夫 :本系列的第一部分致力于介绍Istio的功能并在实际中进行演示, 第二部分则是对路由和网络流量管理的精细调整。 现在,我们将讨论安全性:为了演示与安全性相关的基本功能,作者使用Auth0身份服务,但是可以通过类比来配置其他提供程序。

我们建立了一个Kubernetes集群,其中部署了Istio和Sentiment Analysis微服务应用程序,这就是Istio的演示方式。

使用Istio,我们能够将服务保持在较小的规模,因为它们不需要实现诸如重试,退出,超时,断路器,跟踪,监视之类的“层” 。 此外,我们使用了先进的测试和部署技术:A / B测试,镜像和Canary推出。



在新材料中,我们将探讨实现商业价值的最后几层:身份验证和授权-在Istio中,这是一种真正的乐趣!

Istio中的身份验证和授权


我永远都不会相信身份验证和授权会启发我。 Istio能提供什么技术来使这些话题变得有趣,甚至可以激发您的灵感?

答案很简单:Istio将这些功能的责任从您的服务转移到Envoy代理。 在请求到达服务时,它们已经通过身份验证和授权,因此您只需要编写对业务有用的代码。

听起来不错吗? 让我们看看里面!

使用Auth0进行身份验证


我们将使用Auth0作为身份和访问管理服务器,该服务器具有一个试用版,使用直观,我很喜欢。 但是,可以将相同的原理应用于任何其他OpenID Connect实现 :KeyCloak,IdentityServer等。

首先,使用您的帐户转到Auth0门户 ,创建一个租户(租户-“租户”,逻辑隔离单元,有关更多详细信息,请参见文档 -Transl。)然后转到Applications> Default App ,选择Domain ,如下面的屏幕快照所示。 :



resource-manifests/istio/security/auth-policy.yaml )中指定此域:

 apiVersion: authentication.istio.io/v1alpha1 kind: Policy metadata: name: auth-policy spec: 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 (Istio中Control Plane的三个基本组件之一-大约翻译)将Envoy配置为在将请求重定向到服务之前对请求进行身份验证: sa-web-appsa-feedback 。 同时,该配置不适用于sa-frontend ,使我们可以使前端未经身份验证。 要应用策略,请执行以下命令:

 $ kubectl apply -f resource-manifests/istio/security/auth-policy.yaml policy.authentication.istio.io “auth-policy” created 

返回页面并进行请求-您将看到它以401未经授权状态结束。 现在,我们将前端用户重定向到使用Auth0进行身份验证。

使用Auth0进行身份验证


要验证最终用户的请求,您需要在Auth0中创建一个API,该API代表已验证的服务(评论,详细信息和等级)。 要创建API,请转到Auth0门户> API>创建API并填写以下表格:



这里的重要信息是标识符 ,我们将在脚本的后面部分使用它。 让我们这样写出来:

  • 受众群体 :{YOUR_AUDIENCE}

我们需要的其余详细信息位于“ 应用程序”部分的Auth0门户上-选择“ 测试应用程序” (使用API​​自动创建)。

在这里我们写:

  • :{YOUR_DOMAIN}
  • 客户编号 :{YOUR_CLIENT_ID}

在“ 测试应用程序”中 ,滚动到“ 允许的回调URL”文本框(回调的允许的URL),在其中我们指示身份验证完成后应将呼叫发送到的URL。 在我们的例子中是:

 http://{EXTERNAL_IP}/callback 

对于允许的注销URL (允许注销的URL ),添加:

 http://{EXTERNAL_IP}/logout 

让我们继续前进到前端。

前端更新


切换到[istio-mastery]存储库的auth0分支。 在此线程中,更改了前端代码,以将用户重定向到Auth0进行身份验证,并在对其他服务的请求中使用JWT令牌。 后者的实现如下( App.js ):

 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)); } 

要将前端转换为使用Auth0中的租户数据,请打开sa-frontend/src/services/Auth.js并替换上面我们在其中写入的值( Auth.js ):

 const Config = { clientID: '{YOUR_CLIENT_ID}', domain:'{YOUR_DOMAIN}', audience: '{YOUR_AUDIENCE}', ingressIP: '{EXTERNAL_IP}' //      } 

该应用程序已准备就绪。 在构建和部署所做更改时,在以下命令中指示您的Docker 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也为此提供工具。

作为示例,我们将创建两个用户组(请参见下图):

  • 用户 (用户) -只能访问SA-WebApp和SA-Frontend服务;
  • 主持人 -可以访问所有三项服务。


授权概念

要创建这些组,我们将使用Auth0授权扩展,并使用Istio为它们提供不同的访问级别。

Auth0授权的安装和配置


在Auth0门户上,转到“ 扩展”并安装Auth0 Authorization 。 安装完成后,请转到“ 授权扩展” ,然后通过单击右上角并选择适当的菜单选项(“配置”)进入租户的配置。 激活 ,然后单击发布规则按钮。



组创建


在授权扩展中,转到“ 组”并创建一个“ 主持人”组。 由于我们将所有经过身份验证的用户视为普通用户,因此无需为他们创建其他组。

选择主持人组,单击添加成员 ,添加您的主帐户。 使某些用户没有任何组,以确保拒绝他们的访问。 (可以通过Auth0 Portal>用户>创建用户手动创建新用户。)

将组声明添加到访问令牌


将用户添加到组中,但是此信息应反映在访问令牌中。 为了遵守OpenID Connect并同时返回我们需要的组,令牌将需要添加其自定义Claim 。 它通过Auth0的规则实现。

要创建规则,请转到Auth0门户网站规则 ,单击创建 规则 ,然后从模板中选择一个空规则。



复制下面的代码,并将其保存为新的“ 添加组声明”规则( namespacedGroup.js ):

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

注意 :此代码采用授权扩展中定义的第一个用户组,并将其作为自定义声明(根据Auth0的要求,在其命名空间下)添加到访问令牌中。

返回“ 规则”页面,并验证您是否按照以下顺序编写了两个规则:

  • auth0-授权扩展
  • 添加群组声明

该顺序很重要,因为组字段异步接收auth0-authorization-extension规则,然后由第二个规则作为声明添加。 结果是这样的访问令牌:

 { "https://sa.io/group": "Moderators", "iss": "https://sentiment-analysis.eu.auth0.com/", "sub": "google-oauth2|196405271625531691872" // [  ] } 

现在,您需要配置Envoy代理来检查用户访问权限,为此用户将从请求的访问令牌( https://sa.io/group )中拉出该组。 这是本文下一部分的主题。

Istio授权配置


为使授权生效,您必须为Istio启用RBAC。 为此,请使用以下配置:

 apiVersion: "rbac.istio.io/v1alpha1" kind: RbacConfig metadata: name: default spec: 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.yaml rbacconfig.rbac.istio.io/default created 

现在,所有服务都需要基于角色的访问控制。 换句话说,对所有服务的访问都被拒绝,这将导致RBAC: access denied响应RBAC: access denied 。 现在允许访问授权用户。

普通用户的访问配置


所有用户都必须有权访问SA-Frontend和SA-WebApp服务。 使用以下Istio资源实现:

  • ServiceRole-定义用户拥有的权限;
  • ServiceRoleBinding-确定此ServiceRole属于谁。

对于普通用户,允许访问某些服务( servicerole.yaml ):

 apiVersion: "rbac.istio.io/v1alpha1" kind: ServiceRole metadata: name: regular-user namespace: default spec: rules: - services: - "sa-frontend.default.svc.cluster.local" - "sa-web-app.default.svc.cluster.local" paths: ["*"] methods: ["*"] 

通过regular-user-binding将ServiceRole应用于页面的所有访问者( regular-user-service-role-binding.yaml ):

 apiVersion: "rbac.istio.io/v1alpha1" kind: ServiceRoleBinding metadata: name: regular-user-binding namespace: default spec: subjects: - user: "*" roleRef: kind: ServiceRole name: "regular-user" 

“所有用户”是否意味着未经身份验证的用户将有权访问SA WebApp? 不会,该策略将验证JWT令牌的有效性。

应用配置:

 $ kubectl apply -f resource-manifests/istio/security/user-role.yaml servicerole.rbac.istio.io/regular-user created servicerolebinding.rbac.istio.io/regular-user-binding created 

主持人的访问权限配置


对于主持人,我们希望启用对所有服务的访问( mod-service-role.yaml ):

 apiVersion: "rbac.istio.io/v1alpha1" kind: ServiceRole metadata: name: mod-user namespace: default spec: rules: - services: ["*"] paths: ["*"] methods: ["*"] 

但是,我们只希望对访问令牌的声明为https://sa.io/group且其值为Moderatorsmod-service-role-binding.yaml )的那些用户使用这些权限:

 apiVersion: "rbac.istio.io/v1alpha1" kind: ServiceRoleBinding metadata: 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.yaml servicerole.rbac.istio.io/mod-user created servicerolebinding.rbac.istio.io/mod-user-binding created 

由于特使缓存,授权规则可能要花几分钟的时间才能生效。 此后,您可以确保用户和主持人具有不同的访问级别。

结论部分


好吧,认真:您是否见过一种更简单,轻松,可扩展且安全的身份验证和授权方法?

为了实现对最终用户访问服务的身份验证和授权的精细控制,仅需要三个Istio资源(RbacConfig,ServiceRole和ServiceRoleBinding)。

此外,我们在特使的服务范围内照顾了这些问题,从而实现了:

  • 减少可能包含安全性问题和错误的示例代码数量;
  • 减少愚蠢的情况,其中一个端点原来可以从外部访问并且忘记了报告;
  • 无需在每次添加新角色或权利时都更新所有服务;
  • 新服务保持简单,安全和快速。

结论


Istio允许团队将资源集中在业务关键型任务上,而不会增加服务的开销,而使服务返回微状态。

本文(分为三部分)提供了在实际项目中开始使用Istio的基础知识和现成的实践指导。

译者的PS


另请参阅我们的博客:

Source: https://habr.com/ru/post/zh-CN443668/


All Articles