使用服务网格提高安全性:Christian Posta 带你探索 Istio 的新功能

阅读数:786 2018 年 8 月 29 日

主要结论

  • Istio 意在让“服务网格(Service Mesh)”这个概念变得更具体、更好理解,最新发布的 Istio 1.0 有望获得业界的广泛关注。
  • Istio 试图解决通过云平台运行应用程序时可能面临的一些困难挑战:应用程序网络、可靠性以及可观测性。
  • Istio 还希望解决安全性这个问题。借助 Istio,网格中不同服务间的通信将更安全,并默认加密。
  • Istio 还有助于解决“源头”和“最终用户”的 JWT 标识令牌验证问题。
  • 这些基础的安全功能可以帮助我们构建“零信任”网络,借此根据标识、上下文情境以及具体情况来分配信任,而不再让“调用方恰巧位于同一个内部网络中”。

Istio 意在让“服务网格”这个概念变得更具体、更好理解,最新发布的 Istio 1.0 有望获得业界的广泛关注。InfoQ 之前的文章中,Jasmine Jaksic 曾经详细介绍了Istio 和服务网格,因此本文我想重点介绍Istio 在安全领域采取的措施,这些功能将会为云服务的开发和运维人员提供巨大价值。

Istio 的用例

Istio 试图解决通过云平台运行应用程序时可能面临的一些困难挑战。具体来说,Istio 主要解决了应用程序网络、可靠性和可观测性等问题。以往我们通常会使用预构建的应用程序库来应对这些挑战,如断路器、客户端负载均衡、指标集(Metrics collection)等。然而跨越不同语言、框架、运行时等内容这样做,会造成大部分组织都无法承受的运营负担。

此外,这种做法也很难确保在每种语言的实现中维持一致,更不用提需要改动或修复 Bug 时用固定的步骤将它们一次升级到位了。围绕可靠性、可观测性以及策略的实施会遇到大量挑战,任何企业无一例外都会面临类似情况。虽然不同企业面临的问题基本类似,但如果不加以重视都会对业务造成巨大应向,因此必须妥善解决。Istio 的目标就是解决这些难题。

网络安全的重要性

对应用程序团队来说,安全性是另一个共同面临,并且难以解决的问题。某些情况下,安全性只是一种事后想法,而我们往往会在最后一刻试图将它硬塞进自己的应用中。为什么?因为安全性很难做到足够好。举例来说,诸如“加密应用程序流量”这种基本工作应该很常见并且很简单,对吧?在服务端配置 TLS/HTTPS 也应该很简单,对吧?甚至之前的项目中可能很早就这样做过了。然而在我看来,做的到底是否正确,就不那么好判断了。加密是否使用了正确的证书?证书是否由客户端认可的 CA 颁发?是否选择了正确的加密算法?是否将其正确导入至证书存储 / 密钥库?难道不是直接在 TLS/HTTPS 配置中启用“—insecure”标记就行了吗?

此类错误的配置可能非常危险。Istio 则能提供必要帮助。Istio 会在负责处理应用程序网络流量的每个应用程序实例旁部署(基于Envoy 代理的)旁侧代理(Sidecar proxy)。当应用程序试图与 http://foo.com通信时,将通过这个旁侧代理(使用环回网络接口)通信,随后Istio 会将通信重定向至另一个服务的旁侧代理,并最终将通信代理至实际的上游 http://foo.com。通过在请求路径上放置这些代理,即可实现类似不被应用程序知晓的透明自动加密等操作。借此无需要求每个应用程序的开发团队“做正确的事”,一样可以实现一致的流量加密。

在配置和维护 TLS 以及为服务架构搭建共用的 TLS 过程中,证书的管理也是难题之一。Istio 的控制界面提供了 Citadel 组件,可以用于管理应用程序实例的证书和密钥。Citadel 能够生成供每个工作负载表明自身身份所需的证书和密钥,并能定期更换,这样任何被攻陷的证书都会尽快失效。借助这样的证书,支持 Istio 的集群就可以自动获得共用的 TLS。当然用户也可以在必要时加入自己的 CA 提供商根证书。

借助 Istio,网格中不同服务间的通信将更安全,并默认加密。用户无需折腾证书和 CA 证书链即可获得能投入实用的 TLS。运营商也不再需要祈祷每位开发者正确实现并配置自己的 TLS/HTTPS 设置。这一切都可以通过 Istio 的配置自动实现。

使用 Istio 实现 mTLS

Istio 在配置方面采取了与 Kubernetes 类似的路径。实际上,在 Kubernetes 中,我们可以通过 Kubernetes Custom Resource Definition(CRD)对象配置 Istio。用于配置 Istio 安全策略的两个重要对象包括 Policy 和 DestinationRule 对象。Policy 对象可配置服务(或一组服务)的安全设置,例如,若要配置 Kubernetes 中运行的一个客户名称空间中的所有服务,可以使用下列 Policy 对象:

复制代码
apiVersion: “authentication.istio.io/v1alpha1”
kind: “Policy”
metadata:
name: “default”
namespace: “customer”
spec:
peers:

通过上述配置,该客户名称空间中运行的所有服务均会接受使用 mTLS 的传入流量。但为了使用 mTLS,我们还需要告诉客户端在调用服务时使用 mTLS。为此可以使用 Istio 的 DestinationRule 对象。Istio 中的 DestinationRule 可用于配置客户端与服务的通信方式,我们可以在 DestinationRule 中指定很多设置,如断路器、负载均衡和 TLS。若要启用 mTLS,可使用下列配置:

复制代码
apiVersion: “networking.istio.io/v1alpha3”
kind: “DestinationRule”
metadata:
name: “default”
namespace: “foo”
spec:
host: “*.customer.svc.cluster.local”
trafficPolicy:
tls:
mode: ISTIO_MUTUAL

上述 DestinationRule 会要求客户端在发出调用,与客户名称空间中的服务通信时使用 mTLS。我们还可以将 TLS 模式配置为 ISTIO_MUTUAL,这意味着我们允许让 Istio 管理证书和密钥,并将其载入服务(使用 Kubernetes 中的 Kubernetes 机密),随后服务代理即可用它们来发起 TLS 通信。如果希望自行控制客户端证书,则可使用 MUTUAL 模式,并通过磁盘位置提供证书,这样客户端即可找到所需证书和私钥。

使用 Istio 验证来源身份(借助 JWT)

按照上述方式使用 mTLS 时,不仅可以加密连接,并且更重要的是可以清楚地知道谁在调用谁。Istio 使用了 Secure Production Identity Framework for Everyone( SPIFFE )规范,标识可直接编码到 mTLS 使用的证书中。通过这样的方式,当服务 B 与服务 A 通信时,服务 A 就可以确认服务 B 的真实身份。我们可以针对这些标识编写规则,借此决定服务网格必须应用的规则或策略。举例来说,如果服务 A 不允许与服务 B 通信,即可在每个应用程序旁侧运行的代理中,使用建立 mTLS 所必须的标识来实施这样的策略。

但如果服务 A 是代表用户 X 向服务 B 发出请求又会怎样?如果服务 A 有权调用并访问服务 B 来执行某些操作(例如检查账户余额),但用户 X 无权这样做,如何确认并实现这一目标?在服务架构中,服务与最终用户或来源标识(登录的用户)进行通信的典型方式是传递标识令牌,例如 JSON Web Token。这些令牌可以代表已经通过身份验证的用户以及这些用户的主张。

Istio 有助于实现“来源”或“最终用户”JWT 标识令牌的验证。以往这也是个非常麻烦的领域,因为不同的应用程序语言 / 框架组合使得我们很难通过库来处理验证工作并解包 JWT 令牌。举例来说,在流行的 Keycloak Identity 和 SSO 项目中,为了处理这类操作,甚至为每种流行的语言提供了语言插件。但如果使用 Istio,就可以更自由地实现这一切。例如,若要配置 Istio 在请求中使用 mTLS,同时验证 JWT 令牌(如果令牌不存在、无效或过期,则让请求失败),我们可以配置 Policy 对象。毕竟 Policy 对象就是用于指定希望服务表现出的行为的:

复制代码
apiVersion: “authentication.istio.io/v1alpha1”
kind: “Policy”
metadata:
name: “customer-jwt-policy”
spec:
targets:
- name: customer
peers:
- mtls:
origins:
- jwt:
issuer: http://keycloak:8080/auth/realms/istio
jwksUri: http://keycloak:8080/auth/realms/istio/protocol/openid-connect/certs
principalBinding: USE_ORIGIN

通过上述配置,如果客户端试图连接至客户的服务,除非 JWT 身份验证成功,否则请求将无法到达服务。使用 Istio 的另一个优势在于请求依然可获得 mTLS 的保护,借此可保护 JWT 令牌不被泄露并用于某种类型的重播攻击。

未来:零信任网络

本文介绍了在构建云原生应用过程中,Istio 在改善安全性方面提供的几种方法。通过在服务相互之间,以及与来源 / 最终用户通信过程中使用强标识机制,我们可以编写出一些非常强大的访问控制规则,借此对系统行为加以约束。这些基本功能为“零信任”网络的搭建奠定了基础。在零信任网络中,我们可以根据标识、上下文情境以及具体情况来分配信任,而不再让“调用方恰巧位于同一个内部网络中”。随着逐渐步入全面互联模式以及混合云部署模式,我们也需要重新思考如何以最佳方式让整个架构获得安全性。Istio 可以解决当今架构所面临的很多挑战,并在未来提供更丰富的选项。

Istio 提供了非常强大的功能,服务团队可以借此解决自己面临的很多问题。它还提供了实用的 API 与配置对象,借此可在应用程序服务之外进行配置。以高度去中心化方式实现的这一切面对故障将获得更高弹性。如果你需要构建服务网格,并且希望实现一致的高安全性,那么 Istio 值得一试。

关于本文作者

Christian Posta(@christianposta)是 Red Hat 的云应用程序首席架构师,并以图书作者(Introducing Istio Service Mesh,O’Reilly 2018;Microservices for Java Developers,O’Reilly 2016)、博主、演讲人、开源传道士,以及包括 Istio、Apache ActiveMQ、Fabric8 等在内众多开源项目贡献者的身份闻名于社区。Christian 在大规模企业有多年工作经验,现在致力于帮助企业创建并部署大规模、高弹性、分布式架构,也就是现在我们所说的微服务。他喜欢辅导、培训并领导团队从事有关分布式系统、微服务、DevOps、云原生应用程序设计的项目。

阅读英文原文 Increasing Security with a Service Mesh: Christian Posta Explores the Capabilities of Istio

收藏

评论

微博

发表评论

注册/登录 InfoQ 发表评论