Authentication Policy(0.8)
Istio 身份认证策略使运维为一个服务(或者服务们)指定身份认证要求。Istio 身份认证策略由两部分组成:
- Peer:验证进行连接的一方,即直接客户端。常见的认证机制是相互TLS认证( mutual TLS)。
- Origin:验证发出请求的一方(例如最终用户,设备等),原始客户端。JWT是目前唯一受支持的原始认证机制。
Istio 配置服务端执行身份验证,但是,它不强制客户端的策略。对于相互TLS身份认证,用户可以使用 destination rules 来配置客户端遵守预期的协议。对于其他情况,应用程序负责获取并附加凭证(如JWT)到请求。
如果适用,来自两个认证部分的身份会输出到下一层(如授权,Mixer)。为了简化授权规则,该策略还可以指定应将哪个身份(peer或origin)用作“委托人”。默认情况下,它将被设置为peer的身份。
Architecture
认证策略被保存在Istio的config仓库中(在0.7时,使用K8s CRD实现仓库),并且通过控制面(control plane)分配。根据网格的大小,配置传播可能需要几秒到几分钟的时间。在转换过程中,你可能会预计流量丢失或者身份验证结果不一致。
策略的作用范围是命名空间,其中(可选)目标选择器规则可缩小应用策略的服务集(与策略相同的命名空间内)。这与基于K8s RBAC的ACL模型一致。更具体地说,只有命名空间地管理员才能为该命名空间中的服务设置策略。
认证由Istio sidecars实施。例如,使用Envoy sidecar,它是SSL设置和HTTP过滤器的组合。如果认证失败,请求将被拒绝(使用SSL握手错误代码或http 401,具体取决于身份验证机制的类型)。如果身份认证成功,将会生成如下认证属性:
source.principal: peer主体。如果peer认证未被使用,这个属性不会设置。
request.auth.principal: 取决于政策主体的约束力,这可以是peer主体(如果是
USE_PEER
)或origin主体(如果是USE_ORIGIN
)。- request.auth.audiences: 反映原始JWT(JWT用于原始认证)的听众(
aud
)声明。 - request.auth.presenter: 同样,反映了原始JWT的授权演示者 (
azp
)声明。 - request.auth.claims: 来自原始JWT的所有未加工字符串声明。
Origin主体(原始认证主体)未明确输出。一般来说,它总是可以通过“/”分隔符连接 (iss
) 和主体 (sub
) 声明来重建(例如,如果iss
和sub
声明分别是 “googleapis.com”和“123456”,则原始主体是“googleapis.com/123456”)。另一方面,如果主体绑定是USE_ORIGIN
,那么 request.auth.principal 将携带与原始主体相同的值。
Anatomy of a policy
Target selectors
定义规则以查找应该应用策略的服务。如果未提供规则,则策略将与策略的相同命名空间中的所有服务匹配,即所谓的命名空间级策略(与具有非空选择器规则的服务级策略相反)。Istio使用服务级策略(如果可用),否则将回退到命名空间级策略。如果两者都未定义,则使用基于服务网格配置和/或服务注释的默认策略,该策略只能设置互相TLS认证(这些是在Istio 0.7之前为Istio服务网格配置相互TLS的机制)。可以看 testing Istio mutual TLS.
从0.8开始,认证策略是启用/禁用每个服务的互相TLS的推荐方式。未来版本中将删除使用服务注释的选项。
运维负责避免冲突,例如创建与同一服务匹配的多个服务级策略(或同一命名空间上的多个命名空间级策略)。
示例:规则选择product-page服务(在任何端口上),reviews:9000.
targets:
- name: product-page
- name: reviews
ports:
- number: 9000
Peer authentication
定义对peer验证的验证方法(和相关参数)。它可以列出多种方法;只有其中一个满足认证才能通过。然而,从0.7版本开始,只支持相互TLS。如果不需要peer认证,请忽略此选项。
使用互相TLS的peer认证的示例:
peers:
- mtls:
从Istio 0.7开始,mtls
设置不需要任何参数(因此 -mtls: {}
, - mtls:
or - mtls: null
声明就足够了)。未来,它可能携带参数来提供不同的相互TLS实现。
Origin authentication
定义对origin认证的验证方法(和相关参数)。只有JWT支持此功能,但是,该政策可以由不同的发行人列出多个JWT。类似于peer验证,只需满足其中一个列出的方法即可通过身份验证。
origins:
- jwt:
issuer: "https://accounts.google.com"
jwksUri: "https://www.googleapis.com/oauth2/v3/certs"
Principal binding
定义认证的主体是什么。默认情况下,它将是peer的主体(如果没有应用peer认证,它将保持不变)。策略编写者可以选择使用USE_ORIGIN
覆盖它。未来,我们将同样支持conditional-binding。(如 当peer是X为USE_PEER
,否则为USE_ORIGIN
)
Istio Role-Based Access Control(RBAC)
Overview
Istio基于角色访问控制(RBAC)为服务网格中的服务提供命名空间级、服务级、方法级的访问控制。其特点:
1. 基于角色的语义,便于使用。
2. 服务到服务,终端用户到服务的授权。
3. 通过在角色和角色绑定中支持自定义属性实现灵活性。
Architecture
下图是Istio RBAC架构。管理员指定Istio RBAC 策略。策略被保存在Istio Config Store。
Istio RBAC引擎做了两件事:
1. 取得RBAC策略。 Istio RBAC引擎观测RBAC 策略的变化。如果发现任何变化,它会取得更新后的策略。
2. 批准请求。 在运行期,当一个请求发送过来,请求上下文经过Istio RBAC引擎。RBAC引擎比对RBAC策略来评估请求上下文,并返回审批结果(ALLOW或DENY)。
Request Context
当前版本,Istio RBAC引擎被实现为一个Mixer 适配器。请求上下文是由一个审批模板的实例提供的。请求上下文包含了一个审批模块需要知道环境及请求的所有信息。实际上,它由两部分:
1. subject 包含识别调用者标识的属性列表,包括 "user"
name/ID,subject所属的"groups"
, 或任何关于subject的附加信息,如命名空间,服务名。
2. action 指定“如何访问一个服务”。它包含正在执行的action的 "namespace"
, "service"
, "path"
, "method"
,及任何关于action的附加属性。
下面是一个请求上下文的例子。
apiVersion: "config.istio.io/v1alpha2"
kind: authorization
metadata:
name: requestcontext
namespace: istio-system
spec:
subject:
user: source.user | ""
groups: ""
properties:
service: source.service | ""
namespace: source.namespace | ""
action:
namespace: destination.namespace | ""
service: destination.service | ""
method: request.method | ""
path: request.path | ""
properties:
version: request.headers["version"] | ""
Istio RBAC Policy
Istio RBAC引入服务角色和服务角色绑定,它们在k8s中被定义为CustomResourceDefinition (CRD) 对象。
1. ServiceRole 定义了网格中访问服务的角色。
2. ServiceRoleBinding 授予主体(如一个用户,一个组,一个服务)一个角色。
ServiceRole
一个服务角色说明包含一组规则。每个规则都有以下标准字段:
1. services: 一组服务名,匹配请求上下文中的action.service
字段。
2. methods: 一组方法名,匹配请求上下文的 action.method
字段。请求上下文中有HTTP或gRPC方法。注意gRPC的方法格式为 “packageName.serviceName/methodName” (区分大小写)
3. paths: 一组HTTP路径,匹配请求上下文的action.path
字段。在gRPC中该地段被忽略。
一个服务角色说明仅适用在 "metadata"
中指定的命名空间。 “services” 和“methods”是“rules”中的必要字段。“path”是可选的。如果没有指定或设置为“*”,它适用“any”实例。
下面是一个简单的 “service-admin”角色,在 “default” 命名空间中有对所有服务的访问权限。
apiVersion: "config.istio.io/v1alpha2"
kind: ServiceRole
metadata:
name: service-admin
namespace: default
spec:
rules:
- services: ["*"]
methods: ["*"]
另一个 “products-viewer”角色,对“default”命名空间中的“products.default.svc.cluster.local”服务有读权限(“GET” and “HEAD”)。
apiVersion: "config.istio.io/v1alpha2"
kind: ServiceRole
metadata:
name: products-viewer
namespace: default
spec:
rules:
- services: ["products.default.svc.cluster.local"]
methods: ["GET", "HEAD"]
此外,在rule中对所有字段支持前后缀匹配。例如,你可以定义一个 “tester”角色,在“default” 命名空间中有如下权限:
1. 对所有前缀为“test-”服务拥有所有权限(如“test-bookstore”, “test-performance”, “test-api.default.svc.cluster.local”)。
2. 对“bookstore.default.svc.cluster.local”服务中所有路径后缀为“/reviews”拥有读权限 (“GET”)。(如“/books/reviews”, “/events/booksale/reviews”, “/reviews”)
apiVersion: "config.istio.io/v1alpha2"
kind: ServiceRole
metadata:
name: tester
namespace: default
spec:
rules:
- services: ["test-*"]
methods: ["*"]
- services: ["bookstore.default.svc.cluster.local"]
paths: ["*/reviews"]
methods: ["GET"]
在服务角色中,“namespace”+”services”+”paths”+”methods” 的结合定义了“一个服务被允许怎样被访问”。在一些场景中,你可能需要指定一个rule适用的附加约束。如,一个rule可能仅适用一个服务的某一“version”,或仅适用标签为“foo”的服务。你可以轻易的适用自定义字段指定这些约束。
例如,下面的ServiceRole定义扩展了之前的 “products-viewer”角色,新增了服务版本属于“v1” or “v2”的约束。注意“version” 属性有请求上下文的 "action.properties.version"
提供。
apiVersion: "config.istio.io/v1alpha2"
kind: ServiceRole
metadata:
name: products-viewer-version
namespace: default
spec:
rules:
- services: ["products.default.svc.cluster.local"]
methods: ["GET", "HEAD"]
constraints:
- key: "version"
values: ["v1", "v2"]
ServiceRoleBinding
一个ServiceRoleBinding说明包含两部分:
1. roleRef 指在同一命名空间中的ServiceRole对象。
2. 一组对比角色的subjects。
一个subject可能是一个 “user”,一个“group”或表示一组“properties”。每个条目(“user” or “group” or an entry in “properties”)必须匹配请求上下文实例中的“subject”部分的某一字段(“user” or “groups” or an entry in “properties”)。
下面是一个“test-binding-products”服务角色绑定对象,绑定“product-viewer”服务角色的两个subjects:
1. 用户“alice@yahoo.com”。
2. 在命名空间“abc”中的“reviews.abc.svc.cluster.local”服务。
apiVersion: "config.istio.io/v1alpha2"
kind: ServiceRoleBinding
metadata:
name: test-binding-products
namespace: default
spec:
subjects:
- user: "alice@yahoo.com"
- properties:
service: "reviews.abc.svc.cluster.local"
namespace: "abc"
roleRef:
kind: ServiceRole
name: "products-viewer"
如果你想在上述案例中使一个服务可以被公开访问,你可以设置 user: “*” 。它会为所有用户/服务指派一个ServiceRole。
apiVersion: "config.istio.io/v1alpha2"
kind: ServiceRoleBinding
metadata:
name: binding-products-allusers
namespace: default
spec:
subjects:
- user: "*"
roleRef:
kind: ServiceRole
name: "products-viewer"
Enabling Istio RBAC
Istio RBAC可以添加如下Mixer适配器规则。该规则包含两部分。第一部分定义一个RBAC handler。它有两个参数,"config_store_url"
and "cache_duration"
.
1. "config_store_url"
参数指定RBAC引擎在何处获取RBAC策略。默认值是"k8s://"
,表示k8s API server。相反,如果你在本地测试RBAC策略,你需要设置本地目录,如"fs:///tmp/testdata/configroot"
.
2. "cache_duration"
参数指定审批结果在Mixer client(i.e., Istio proxy)中缓存的持续时间。默认值1分钟。
第二部分定义一个规则,指定RBAC handler应该被上面定义的请求上下文调用。
下面的例子,Istio RBAC在命名空间 “default”中起效,缓存时间30s。
apiVersion: "config.istio.io/v1alpha2"
kind: rbac
metadata:
name: handler
namespace: istio-system
spec:
config_store_url: "k8s://"
cache_duration: "30s"
---
apiVersion: "config.istio.io/v1alpha2"
kind: rule
metadata:
name: rbaccheck
namespace: istio-system
spec:
match: destination.namespace == "default"
actions:
# handler and instance names default to the rule's namespace.
- handler: handler.rbac
instances:
- requestcontext.authorization
---
Mutual TLS Authentication
Overview
Istio Auth的目的是增强微服务间通信的安全性,且不需修改服务代码。它负责:
1. 为每个服务提供一个强大的标识,保证它能在集群和云中互通。
2. 确保服务到服务、终端用户到服务的通信。
3. 提供密钥管理系统以自动化密钥和证书的生成、分发、轮换和撤销。
Architecture
下图是Istio Auth的架构,包含三个主要组件: identity, key management, and communication security。该图描述了Istio Auth如何确保‘frontend’ 服务作为‘frontend-team’ 服务账户与‘backend’服务作为 ‘backend-team’服务账户之间的服务到服务通信。Istio支持运行在k8s容器和VM/裸机中的服务。
如图,Istio Auth利用秘密卷从Istio CA向k8s容器交付密钥/证书。对于运行在VM/裸机中的服务,我们引入节点代理,它是一个运行在每个VM/裸机中的进程。它在本地生成私钥和CSR(证书签名请求),发送CSR到Istio CA去签名,然后和私钥一同生成证书并分发到Envoy。
Components
Identity
Istio Auth 使用k8s服务账户去确认谁运行服务:
1. 在Istio中的服务账户形式为: “spiffe://<domain>/ns/<namespace>/sa/<serviceaccount>”
1). domain是当前的cluster.local
。我们在未来会支持自定义domain。
2). namespace是k8s服务账户的命名空间。
3). serviceaccount 是k8s服务账户名。
2. 一个服务账户是一个工作负载运行的身份(或角色),代表了工作负载的权限。对于需要强安全性的系统,一个工作负载的权限总数不应用一个随机字符 (i.e., service name, label, etc),或是被部署的二进制文件标识。
1). 如,假设我们有一个从多租户数据库中拉取数据的工作负载。如果Alice可以运行这个工作负载,她拉取的数据就和Bob运行这个工作负载时拉取的数据有所不同。
3. 服务账户可以通过识别一个机器,一个用户,一个工作负载或一组工作负载(不同工作负载可以作为相同服务账户运行)提供的灵活性来实现强安全策略。
4. 工作负载运行的服务账户在工作负载的生命周期内不会更改。
5. 服务账户唯一性可通过域名限制来确保。
Communication security
服务到服务通信客户端Envoy和服务端Envoy的通道实现。端到端安全通信通过如下确保:
1. 服务和Envoy间的本地TCP连接。
2. 代理间的相互TLS连接。
3. 安全命名:在握手过程中,客户端Envoy检查运行在目标服务上的服务端证书提供的服务账户。
Key management
Istio v2.0 支持服务运行在k8s pods和VM/裸机上。我们针对每种情况提供了不同的密钥配置机制。
对于运行在k8s pods上的服务,每个集群Istio CA自动化密钥&证书管理流程。主要表现为下面四个关键操作:
1. 为每个服务账户生成一个 SPIFFE key和证书对。
2. 根据服务账户将密钥和证书对分发到每个pod。
3. 定期轮换密钥和证书。
4. 必要时撤销特定密钥和证书对。
对于运行在VM/裸机上的服务,上面四个操作由节点代理和Istio CA共同完成。
Workflow
Istio Auth 工作流由两阶段组成,部署和运行期。部署阶段,我们分别讨论两种情况(k8s和VM/裸机)。一旦密钥和证书部署完成,两种场景的运行期阶段就是相同的。本节我们简要介绍工作流。
Deployment phase(Kubernetes Scenario)
- Istio CA观测k8s API Server,为每个已存在和新建的服务账户创建SPIFFE密钥和证书对,并将它们发送到API Server。
- 当一个pod被创建时,API Server 根据使用Kubernetes secrets的服务账户装载密钥和证书对。
- Pilot使用适当的密钥和证书以及安全的命名信息生成配置,定义了哪些服务账户可以运行某种服务,并将其传递给Envoy。
Deployment phase(VM/bare-metal Machines Scenario)
- Istio CA创建一个gRPC服务去采取CSR请求。
- 节点代理创建私钥和CSR,发送CSR到Istio CA签名。
- Istio CA确认CSR携带的认证信息,签署CSR并生成证书。
- 节点代理把从CA获取的证书和私钥推送给Envoy。
- 上述CSR过程定期重复进行轮换。
Runtime phase
- 来自客户服务的出站流量重新路由到本地Envoy。
- 客户端Envoy和服务端Envou相互TLS握手。握手期间,同时进行安全的命名检查,以确保服务端证书提供的服务账户可以运行服务端服务。
- mTLS连接建立后,流量将转发到服务端Envoy,随后通过本地TCP连接转发到服务端服务。
Best practices
这节,我们提供一些部署指南,然后讨论一个真实世界的场景。
Deployment guidelines
- 如果在一个集群中有多种服务运维(又名 SREs)部署不同服务,我们推荐为每个SRE组创建一个隔离的命名空间来隔离它们的权限。例如,你可能为team1创建命名空间 “team1-ns” ,为team2创建“team2-ns” ,这两组都不能访问另一组的服务。
- 如果Istio CA受到威胁,集群中所有托管的密钥和证书都可能被暴露。我们强烈建议在专有命名空间(如istio-ca-ns)上运行Istio CA,只有集群管理员才能访问该命名空间。
Example
我们看一个包含三个服务的三层应用:photo-frontend, photo-backend, and datastore。Photo-frontend and photo-backend 服务被photo SRE team管理,而datastore 服务被datastore SRE team管理。Photo-frontend可以访问photo-backend, photo-backend可以访问datastore。但是,photo-frontend不能访问datastore。
在这个场景中,一个集群管理员创建3个命名空间: istio-ca-ns, photo-ns, and datastore-ns。管理员有所有命名空间的权限。每个team都有自己命名空间的权限。 photo SRE team 在命名空间 photo-ns中分别创建两个服务账户去运行photo-frontend 和photo-backend 。datastore SRE team 在命名空间datastore-ns中创建一个服务账户运行datastore 。并且,我们需要在Istio Mixer强制执行服务访问控制,使photo-frontend 不能访问datastore。
在此设置中,Istio CA能够为所有命名空间提供密钥和证书管理,并将微服务部署彼此隔离。
Future work
- 集群间 服务到服务认证。
- 强大的审批机制:ABAC,RBAC等
- 每项服务允许授权的支持
- 安全的Istio组件(Mixer, Pilot)
- 使用JWT/OAuth2/OpenID_Connect 实现终端用户到服务认证。
- 支持GCP服务账户
- 用于服务和Envoy间本地通信的Unix域套接字。
- 支持中间代理
- 可插入密钥管理组件