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

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

笔记。 翻译。: 的第一部分 本系列致力于了解 Istio 的功能并在实践中展示它们, 第二 — 精细调整的路由和网络流量管理。 现在我们来谈谈安全性:为了演示与之相关的基本功能,作者使用了Auth0身份服务,但其他提供商也可以以类似的方式进行配置。

我们设置了一个 Kubernetes 集群,在其中部署了 Istio 和一个示例微服务应用程序“情感分析”,以演示 Istio 的功能。

借助 Istio,我们能够保持服务规模较小,因为它们不需要实现重试、超时、断路器、跟踪、监控等层。 此外,我们还使用了先进的测试和部署技术:A/B 测试、镜像和金丝雀部署。

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

在新材料中,我们将处理商业价值之路上的最后几层:身份验证和授权 - 在 Istio 中这真是一种乐趣!

Istio 中的身份验证和授权

我永远不会相信我会受到身份验证和授权的启发。 从技术角度来看,Istio 可以提供什么来使这些主题变得有趣,甚至更能启发您?

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

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

使用 Auth0 进行身份验证

作为身份和访问管理的服务器,我们将使用 Auth0,它有试用版,使用起来很直观,我就是喜欢它。 然而,相同的原则可以应用于任何其他 OpenID 连接实施:KeyCloak、IdentityServer 等等。

要开始使用,请访问 Auth0 门户 使用您的帐户创建租户 (租户 - “租户”,隔离的逻辑单元,更多详细信息请参见 文件资料 - 大约。 译) 并前往 应用程序 > 默认应用程序选择 域名,如下图所示:

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

在文件中指定该域 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 中的三个基本控制平面组件之一 - 大约翻译) 配置 Envoy 在将请求转发到服务之前对请求进行身份验证: sa-web-app и sa-feedback。 同时,该配置不适用于服务Envoys 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 并填写表格:

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

这里的重要信息是 识别码,我们稍后将在脚本中使用它。 让我们这样写:

  • 目的:{YOUR_AUDIENCE}

我们需要的其余详细信息位于 Auth0 门户的 部分 应用领域 - 选择 测试申请 (与 API 一起自动创建)。

这里我们会写:

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

滚动到 测试申请 到文本字段 允许的回调 URL (回调的已解析 URL),其中我们指定身份验证完成后应将调用发送到的 URL。 在我们的例子中是:

http://{EXTERNAL_IP}/callback

允许的注销 URL (允许注销的 URL)添加:

http://{EXTERNAL_IP}/logout

让我们继续讨论前端。

前端更新

切换到分支 auth0 存储库 [istio-mastery]。 在此分支中,前端代码已更改为将用户重定向到 Auth0 进行身份验证,并在对其他服务的请求中使用 JWT 令牌。 后者的实现如下(应用程序.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 服务;
  • 版主 (主持人) - 可以访问所有三项服务。

回到 Istio 的微服务。 第3部分
授权理念

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

Auth0授权的安装和配置

在 Auth0 门户中,转到扩展 (扩展)并安装 Auth0 授权。 安装完成后,进入 授权扩展,然后 - 通过单击右上角并选择适当的菜单选项来查看租户的配置 (配置)。 激活群组 (团体) 然后点击发布规则按钮 (发布规则).

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

创建群组

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

选择一个组 版主, 按 新增成员,添加您的主帐户。 让某些用​​户不属于任何组,以确保他们被拒绝访问。 (可以通过手动创建新用户 Auth0 门户 > 用户 > 创建用户.)

将组声明添加到访问令牌

用户已添加到组中,但此信息也必须反映在访问令牌中。 为了遵守 OpenID Connect 并同时返回我们需要的组,令牌需要添加自己的 定制索赔。 通过Auth0规则实现。

要创建规则,请转至 Auth0 Portal 规则, 按 创建规则 并从模板中选择一个空规则。

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

复制下面的代码并将其另存为新规则 添加团体声明 (命名空间组.js):

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

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

返回页面 规则 并检查您是否有按以下顺序编写的两条规则:

  • auth0-授权-扩展
  • 添加团体声明

顺序很重要,因为组字段异步接收规则 auth0-授权-扩展 然后根据第二条规则将其添加为声明。 结果是一个像这样的访问令牌:

{
 "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 — 仅对字段中列出的服务和命名空间启用 RBAC Inclusion;
  • 2 — 我们列出了我们的服务列表。

让我们使用以下命令应用配置:

$ kubectl apply -f resource-manifests/istio/security/enable-rbac.yaml
rbacconfig.rbac.istio.io/default created

所有服务现在都需要基于角色的访问控制。 换句话说,所有服务的访问都被禁止,并且会导致响应 RBAC: access denied。 现在让我们允许授权用户访问。

普通用户的访问配置

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

  • 服务角色 ——确定用户拥有的权利;
  • 服务角色绑定 — 确定此 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 应用于所有页面访问者(常规用户服务角色绑定.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 有意义 Moderators (mod-服务-角色绑定.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

由于 Envoy 中存在缓存,授权规则可能需要几分钟才能生效。 然后,您可以确保用户和主持人具有不同的访问级别。

这部分的结论

但说真的,您见过更简单、轻松、可扩展且安全的身份验证和授权方法吗?

只需三个 Istio 资源(RbacConfig、ServiceRole 和 ServiceRoleBinding)即可实现对最终用户访问服务的身份验证和授权的细粒度控制。

此外,我们还通过特使服务解决了这些问题,实现了:

  • 减少可能包含安全问题和错误的通用代码的数量;
  • 减少愚蠢情况的数量,在这种情况下,一个端点可从外部访问但忘记报告;
  • 无需在每次添加新角色或权限时更新所有服务;
  • 新服务仍然简单、安全和快速。

结论

Istio 允许团队将资源集中在关键业务任务上,而不增加服务开销,将其恢复到微观状态。

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

译者PS

另请阅读我们的博客:

来源: habr.com

添加评论