零信任是指无论在网络边界内部还是外部,都没有任何隐含的信任。阿里云服务网格ASM是重要的云原生零信任体系落地载体之一,将身份认证和授权从应用程序代码集成到ASM,开箱即用、动态可配、更新策略更加容易且立即生效。本文介绍为什么要使用ASM实现零信任以及如何使用ASM中的零信任体系。
背景信息
微服务提供了诸多价值,包括可伸缩性、敏捷性、独立扩展、业务逻辑隔离、独立生命周期管理和更容易的分布式开发。然而,这些分布众多的微服务也会增加安全的挑战,每个微服务都是一个被攻击的目标。Kubernetes为托管和编排您的微服务提供了一个出色的平台。但是,默认情况下,微服务之间的所有交互都不安全。它们通过纯文本HTTP进行通信,但这不足以满足安全要求。只依赖网络边界来保证安全是不够的,因为一旦内部的某个服务被攻陷,攻击者能够以该机器为跳板来攻击内网。所以,内部的调用也必须安全,这就是零信任安全的价值。零信任是指任何地方都需要显式认证,并使用最小权限原则来限制对资源的访问。
服务网格技术的一个重要的价值主张就是它如何有效地保护应用的生产环境,同时又不降低开发人员的生产力。通过服务网格技术,为微服务架构采用零信任网络安全方法提供必要的基础,以此实现所有访问都经过强身份认证、基于上下文授权、记录监控等安全目标。使用这些网格功能,您可以为属于网格的所有应用程序提供安全控制能力,例如所有流量都已加密、到应用程序的所有流量都通过策略执行点(PEP)的验证等。
在使用Kubernetes Network Policy实现三层网络安全控制之上,ASM提供了包括对等身份和请求身份认证能力、Istio授权策略以及更为精细化管理的基于OPA(Open Policy Agent)的策略控制能力。阿里云ASM提供的这些零信任安全能力,帮助用户实现上述这些安全目标。
构建ASM能力的理论体系包括了以下几个方面:
工作负载身份:零信任的基础,为云原生工作负载提供了统一的身份。ASM为服务网格下的每一个工作负载提供了简单易用的身份定义,并根据特定场景提供定制机制用于扩展身份构建体系,同时兼容社区SPIFFE标准。
安全证书:零信任的载体,ASM提供了证书签发以及证书生命周期管理、轮转等机制。通过X509 TLS证书建立身份,每个代理都使用该证书,并提供证书和私钥轮换。
策略执行:零信任的引擎,基于策略的信任引擎是构建零信任的核心,ASM除了支持Istio RBAC授权策略之外,还提供了基于OPA提供更加细粒度的授权策略。
可视化与分析:零信任的洞察,ASM提供了可观测机制用于监视策略执行的日志和指标,来判断每一个策略的执行情况。
为什么要使用ASM实现零信任
与直接在应用程序代码中构建安全机制的传统方法相比,ASM体系结构具有以下多种安全性好处:
Sidecar代理的生命周期与应用程序保持独立,因此可以更轻松地管理这些Sidecar代理。
允许动态配置,更新策略变得更加容易,更新立即生效,而无需重新部署应用程序。
ASM的集中控制架构使企业的安全团队可以构建、管理和部署适用于整个企业的安全策略,从而默认情况下就能确保应用开发人员构建的业务应用的安全。他们无需额外的工作即可立即使用这些安全策略。
ASM提供了对附加到请求的终端用户凭据进行身份认证的能力,如JWT。
使用ASM体系结构,可以将身份认证和授权系统作为服务部署在网格中。如同网格中的其他服务一样,这些安全系统从网格中也可以得到安全保证,包括传输中的加密、身份识别、策略执行点、终端用户凭据的身份认证和授权等。
借助ASM,可以使用单个控制平面来实施强大的身份和访问管理、透明的TLS和加密、身份认证和授权以及审核日志记录。ASM开箱即用地提供了这些功能,简单地安装和管理使开发人员、系统管理员和安全团队可以保护其微服务应用程序。
如何使用ASM中的零信任体系
ASM能够减小云原生环境中的被攻击面积,并提供零信任应用网络所需的基础框架。通过ASM管理服务到服务的安全性,可以确保ASM的端到端加密、服务级别身份认证和细粒度授权策略。
在ASM体系下,可以支持:
在服务之间实施双向TLS认证或者面向Server侧的TLS认证,支持证书的自动轮转等生命周期管理。网格内的通信都经过身份认证和加密处理。
启用基于身份的细粒度授权,以及基于其他维度参数的授权。基于角色访问控制 (RBAC) 的基础,支持“最低权限”的立场,也就是只有经过授权的服务才能根据ALLOW或DENY规则相互通信。
当前ASM提供了工作负载身份、对等身份认证、请求身份认证、授权策略、OPA策略的零信任安全基本能力。
工作负载身份
当应用程序在ASM环境中运行时,ASM会为每个服务提供一个唯一标识。连接到ASM中运行的其他微服务时,将会使用该标识。服务标识可用于服务的双向验证,以此进行验证是否允许服务间的访问,同时该服务标识也可用于授权策略中。
当使用ASM管理运行在Kubernetes上的工作负载时,ASM会为每个工作负载提供服务身份。该身份基于工作负载的服务账户令牌实现。
ASM中的服务身份符合SPIFFE标准,并具有以下格式:spiffe://<trust-domain>/ns/<namespace>/sa/<service-account>
。
您可以登录ASM控制台,查看基于Kubernetes集群接入的服务。
登录ASM控制台,在左侧导航栏,选择 。
在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择 。
在工作负载身份页面顶部,设置数据面为集群ID,然后选择命名空间,查看Kubernetes集群接入服务网格的工作负载身份。
对等身份认证
ASM提供了两种身份认证:对等身份认证和请求身份认证。对等身份认证是指当两个微服务相互交互时,可以启用双向TLS进行对等身份认证。
如果请求的客户端和服务端都注入了Sidecar,默认情况下会启用ASM的mTLS通信。
如果只有客户端注入了Sidecar,客户端会自行根据服务端的配置,决定是否启用mTLS通信。
如果只有服务端注入了Sidecar,默认的mTLS模式是PERMISSIVE,即可以同时接受明文和加密流量。如果您此时给服务端配置了PeerAuthentication,且设置了mTLS模式为STRICT,请求就会失败。
请求身份认证
ASM提供了两种身份认证:对等身份认证和请求身份认证。请求身份认证允许最终用户和系统使用请求身份认证,与微服务进行交互。通常使用JSON Web令牌(JWT)执行该操作。
当请求微服务时,您可以创建请求身份验证策略,对服务的请求执行JWT身份验证。请求身份验证会对带有JWT Token的请求进行JWT身份验证,只有正确的JWT Token才能访问服务成功。请求身份验证对不带有JWT Token的请求不会进行JWT身份验证,不带有JWT Token的请求不受限制可以正常访问服务。
您可以使用授权策略和请求身份认证,实现只有正确的JWT Token才能访问服务,无效的JWT Token或者不带有JWT Token的请求都将访问失败。
部署被请求的bookinfo应用。具体操作,请参见在ASM实例关联的集群中部署应用。
部署发起请求的sleep应用。
在ACK集群对应的KubeConfig环境下,创建sleep.yaml。
执行以下命令,部署sleep应用。
kubectl apply -f sleep.yaml -n default
创建请求身份认证策略,对details服务的入站请求强制执行JWT身份认证。
登录ASM控制台,在左侧导航栏,选择 。
在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择 ,然后单击创建。
在创建页面,配置如下信息,为工作负载details定义JWT规则,然后单击创建。
部分配置项说明如下:
issuer:JWT的颁发者,本示例设置为testing@secure.istio.io。
audiences:JWT受众列表。设置哪些服务可以使用JWT Token访问目标服务。本示例设置为空,表示对访问的服务不受限制。
jwks:设置JWT请求信息,本示例设置的JWT请求信息如下。更多信息,请参见jwks.json。
{ "keys":[ {"e":"AQAB","kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ","kty":"RSA","n":"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ"}]}
使用JWT工具将JWT请求信息编码成JWT Token。
{ "keys":[ {"e":"AQAB","kid":"DHFbpoIUqrY8t2zpA2qXfCmr5VO5ZEr4RzHU_-envvQ","kty":"RSA","n":"xAE7eB6qugXyCAG3yhh7pkDkT65pHymX-P7KfIupjf59vsdo91bSP9C8H07pSAGQO1MV_xFj9VswgsCg4R6otmg5PV2He95lZdHtOcU5DXIg_pbhLdKXbi66GlVeK6ABZOUW3WYtnNHD-91gVuoeJT_DwtGGcp4ignkgXfkiEm4sw-4sfb4qdt5oLbyVpmW6x9cfa7vs2WTfURiCrBoUqgBo_-4WTiULmmHSGZHOjzwa8WtrtOQGsAFjIbno85jp6MnGGGZPYZbDAa_b3y5u-YpW7ypZrvD8BgtKVjgtQgZhLAGezMt0ua3DRrWnKqTZ0BJ_EyxOGuHJrLsn00fnMQ"}]}
预期编码成以下令牌:
eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg
验证请求身份认证是否生效。
执行以下命令,使用上文编码的JWT Token访问details服务。
export TOKEN=eyJhbGciOiJSUzI1NiIsImtpZCI6IkRIRmJwb0lVcXJZOHQyenBBMnFYZkNtcjVWTzVaRXI0UnpIVV8tZW52dlEiLCJ0eXAiOiJKV1QifQ.eyJleHAiOjQ2ODU5ODk3MDAsImZvbyI6ImJhciIsImlhdCI6MTUzMjM4OTcwMCwiaXNzIjoidGVzdGluZ0BzZWN1cmUuaXN0aW8uaW8iLCJzdWIiOiJ0ZXN0aW5nQHNlY3VyZS5pc3Rpby5pbyJ9.CfNnxWP2tcnR9q0vxyxweaF3ovQYHYZl82hAUsn21bwQd9zP7c-LS9qd_vpdLG4Tn1A15NxfCjp5f7QNBUo-KC9PJqYpgGbaXhaGx7bEdFWjcwv3nZzvc7M__ZpaCERdwU7igUmJqYGBYQ51vr2njU9ZimyKkfDe3axcyiBZde7G6dabliUosJvvKOPcKIWPccCgefSj_GNfwIip3-SsFdlR7BtbVUcqR-yv-XOxJ3Uc1MI0tz3uMiiZcyPV7sNCU4KRnemRIMHVOfuvHsU60_GhGbiSFzgPTAa9WTltbnarTbxudb_YEOx12JiwYToeX0DCPb43W1tzIBxgm8NxUg kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c sleep -- curl http://details:9080/details/1 -o /dev/null --header "Authorization: Bearer $TOKEN" -s -w '%{http_code}\n'
返回200,说明访问成功。
执行以下命令,使用无效的JWT Token访问details服务。
kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c sleep -- curl http://details:9080/details/1 -o /dev/null --header "Authorization: Bearer badtoken" -s -w '%{http_code}\n'
返回403,说明访问失败。
执行以下命令,不带JWT Token访问details服务。
kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c sleep -- curl http://details:9080/details/1 -o /dev/null -s -w '%{http_code}\n'
返回200,说明访问成功。
根据以上结果,可以看到使用正确的JWT Token访问服务成功,使用无效的JWT Token访问服务失败,不带有JWT Token的请求访问成功。说明请求身份认证生效。
授权策略
当请求微服务时,您可以使用授权策略对请求的端口、IP、来源等进行限制,只有符合要求的请求才能访问服务。以下授权策略对请求来源进行限制,限制请求中必须带有固定签发者版发的JWT Token才能访问服务。
部署被请求的bookinfo应用。具体操作,请参见在ASM实例关联的集群中部署应用。
部署发起请求的sleep应用。
使用以下内容,创建sleep.yaml。
在ACK集群对应的KubeConfig环境下,执行以下命令,部署sleep应用。
kubectl apply -f sleep.yaml -n default
创建授权策略。
登录ASM控制台,在左侧导航栏,选择 。
在网格管理页面,单击目标实例名称,然后在左侧导航栏,选择 ,然后单击使用YAML创建。
在创建页面,选择default命名空间,配置如下YAML,然后单击创建。
关于字段的说明,请参见Authorization Policy。
apiVersion: security.istio.io/v1beta1 kind: AuthorizationPolicy metadata: name: require-jwt namespace: default spec: action: ALLOW rules: - from: - source: requestPrincipals: - testing@secure.istio.io/testing@secure.istio.io selector: matchLabels: app: details
执行以下命令,使用不带有JWT Token的请求访问服务。
kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name}) -c sleep -- curl http://details:9080/details/1 -o /dev/null -s -w '%{http_code}\n'
预期输出:
403
不使用JWT Token访问details服务访问失败,说明授权策略生效。授权策略限制了所有请求必须带有
testing@secure.istio.io
颁发的JWT Token才能访问服务成功。
OPA策略
OPA是一个策略引擎,可为您的应用程序实现细粒度的访问控制。OPA作为通用策略引擎,可以与微服务一起部署为独立服务。为了保护应用程序,必须先授权对微服务的每个请求,然后才能对其进行处理。为了检查授权,微服务对OPA进行API调用,以确定请求是否被授权。更多信息,请参见OPA。
ASM集成了开放策略代理(OPA)插件,通过OPA定义访问控制策略,可以使您的应用实现细粒度的访问控制,并支持动态更新OPA策略。具体操作,请参见在ASM中实现动态更新OPA策略。
总结及参考案例
综上所述,ASM提供了以下增强安全性的组件:
提供具有完整证书生命周期管理的托管证书基础设施,解决了证书颁发和CA轮换的复杂性。
托管的控制面API,用于向Envoy代理分发身份认证策略、授权策略和安全命名信息。
Sidecar代理通过提供策略执行点PEP来帮助确保网格的安全。
Envoy代理扩展允许遥测数据收集和审计。
每一个工作负载通过X509 TLS证书建立身份,每个Sidecar代理都使用该证书。ASM会提供并定期轮换证书和私钥。如果某个特定的私钥被盗用,ASM很快就会用新的私钥替换它,从而大大减少了攻击面。
参考案例
使用授权策略在入口网关上实施基于IP的访问控制或者基于自定义外部授权的访问控制等。
某互联金融客户在解决跨集群多语言应用的访问权限控制方面,使用ASM提供的授权策略隔离外联区域和应用区域。同时结合出口网关审计网格的流量,配合授权策略,控制应用对第三方服务的访问权限。