服务网格(istio)istio安全
微服务带来灵活性、可伸缩性和复用性的同时,还需要考虑以下安全问题:
- 使用流量加密组织中间人攻击
- 提供灵活的访问控制,比如双向TLS加密和细粒度的访问策略
- 使用审计工具检索谁在什么时间做了什么操作
Istio可以处理内外部的威胁,给数据、端点、通信和平台提供安全性。
Istio提供了强身份认证、权限策略、传输TLS加密以及认证、授权和审计(AAA)工具。
上层架构
Istio的安全性涉及多个部分:
- 用于密钥和证书管理的证书颁发机构(CA)
- 配置API服务器分发给代理
- 认证策略
- 鉴权策略
- 安全命名信息
- 作为策略执行点的僚机和参数代理,用于客户端和服务端之间的安全通信
- 一组用于管理遥测和审计的代理扩展
Istio中的身份
在两个工作负载通信前必须先交换包含身份信息的凭证用于双向认证。客户端要验证服务器的安全命名信息,判断对方是否有工作负载授权运行。在服务端,服务器必须验证客户端有没有权限访问,以及根据审计策略,记录谁在什么时间访问,根据客户端使用的工作负载向其收费,并拒绝任何未能支付帐单的客户端访问工作负载。
Istio身份模型使用第一类服务身份判断请求的原始身份。可以更灵活,细粒度地表示用户、工作负载或一组负载。
身份和凭证管理
Istio使用X.509提供强身份识别。Istio代理,和Envoy一起运行,配合Istiod一起完成自动化大规模的密钥和证书轮换。
- istiod 提供一个gRPC服务接受凭证注册请求/certificate signing requests (CSRs)。
- 最开始,istio代理生成私钥,把私钥和CSR一起发送给istiod,请求注册。
- istiod中的CA验证CSR中携带的凭证,成功后,注册CSR,生成凭证。
- 工作服在启动后,Envoy通过密钥发现服务(SDS)接口请求同一个容器中的Istio代理,获取凭证和密钥。
- istio代理把从istiod收到的私钥和凭证通过Envoy SDS API发给Envoy。
- istio代理监控工作负载凭证的过期时间,定期重复上述过程。
认证
istio有两种认证类型:
- peer authentication:同行认证用于服务见通信时认证发起连接的客户端。使用双向TLS加密作为全栈解决方案。
- request authentication:校验请求中携带的凭证认证终端用户。Istio支持JWT认证,以及一些OIDC供应商的定制话认证。
在所有情况下,Istio都通过自定义Kubernetes API将身份验证策略存储在Istio配置存储中。Istiod为每个代理保留最新的密钥。此外,Istio支持在许可模式下进行身份验证,以帮助您在强制执行之前了解策略更改如何影响安全态势。
双向身份验证
Istio通过Envoy代理作为策略执行点,建立服务到服务的通信。双向TLS下的请求会被如下处理:
- Istio把请求从客户端重定向到客户端本地的Envoy
- 客户端本地的Envoy向服务端的Envoy开启双向TLS握手。同时客户端Envoy对服务端凭证中的服务账户做安全命名检查,确认目标地址被授权运行该工作负载
- 建立安全连接后,Istio把流量从客户端Envoy发给服务端Envoy
- 服务端Envoy对请求鉴权,通过后使用TCP发送给服务
许可模式 / Permissive mode
许可模式允许服务同时接受未加密流量和双向TLS加密流量。
安全命名
服务器标识在证书中编码,但服务名称通过发现服务或DNS检索。安全命名信息将服务器标识映射到服务名称。标识A到服务名称B的映射意味着“A被授权运行服务B”。控制面板监视apiserver,生成安全的命名映射,并将它们安全地分发给策略执行点。下面的示例解释了为什么安全命名在身份验证中至关重要。
假设运行服务数据存储的合法服务器只使用infra团队的身份。恶意用户拥有测试组的身份证书和密钥。恶意用户打算模拟服务来劫持从客户端发送的数据。恶意用户使用测试团队的身份证书和密钥部署伪造的服务器。假设恶意用户成功劫持(通过DNS欺骗、BGP/路由劫持、ARP欺骗等)发送到数据存储的流量,并将其重定向到伪造的服务器。
当客户端请求数据存储服务,它会吧测试团队的身份从服务器凭证中提取出来,并通过安全命名信息检查测试团队是否允许运行数据存储服务。客户端查询到测试团队不允许运行数据存储服务,则鉴权失败。
请注意,对于非HTTP/HTTPS流量,安全命名不能防止DNS欺骗,在这种情况下,攻击者会修改服务的目标IP。由于TCP流量不包含主机信息,并且Envoy只能依赖目的IP进行路由,因此Envoy会将流量路由到被劫持IP上的服务。这种DNS欺骗甚至可以在客户端特使接收流量之前发生。
认证架构
认证策略
认证策略用于服务端收到的请求。通过yaml文件配置。
下面的策略要求有app:reviews
的工作负载必须使用双向TLS加密。
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "example-peer-policy"
namespace: "foo"
spec:
selector:
matchLabels:
app: reviews
mtls:
mode: STRICT
策略存储
Istio把没有配置selector,用于全网格范围的认证策略存在root命名空间,指定了应用的命名空间的策略存在相应命名空间内。peer authentication和request authentication分别存储。
selector字段
selector字段制定策略要应用在有哪些标签的工作负载上。下例应用在有app:product-page
标签的工作负载上:
selector:
matchLabels:
app: product-page
全网格范围只允许有一个peer authentication策略,每个命名空间下也只允许一个peer authentication策略。Istio会忽略新配置的peer authentication。
istio按一下顺序匹配工作负载应用的策略:
- 指定工作负载的策略
- 命名空间范围的策略
- 网格范围的策略
对于request authentication策略,istio可以组合匹配到的v策略,但是最好网格范围和命名空间范围内只配置一条request authentication策略。
peer authentication
用于配置双向TLS加密。支持一下模式:
- PERMISSIVE:同时接受未加密连接和双向加密连接
- STRICT:只接受加密连接
- DISABLE:关闭双向加密连接
未设置时向上继承,网格范围内默认是PERMISSIVE模式。
下例配置foo命名空间下所有工作负载使用双向TLS:
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "example-policy"
namespace: "foo"
spec:
mtls:
mode: STRICT
也可以分端口指定,下例配置有app: example-app
标签的应用80端口不用加密,其他都要加密:
apiVersion: "security.istio.io/v1beta1"
kind: "PeerAuthentication"
metadata:
name: "example-workload-policy"
namespace: "foo"
spec:
selector:
matchLabels:
app: example-app
portLevelMtls:
80:
mode: DISABLE
只有配置下面的服务,把example-app工作负载的请求绑定到80端口,才有效:
apiVersion: v1
kind: Service
metadata:
name: example-service
namespace: foo
spec:
ports:
- name: http
port: 8000
protocol: TCP
targetPort: 80
selector:
app: example-app
请求认证
指定验证JWT中的值。
Istio会拒绝携带的oken无效的请求。请求没有token时会被接受。要拒绝没有utoken的请求,需要提供健全策略。
鉴权
提供网格范围、命名空间范围和工作负载范围的访问控制。
鉴权架构
每个Envoy代理都会运行一个鉴权服务实时检查请求的权限,返回ALLOW或DENY两种鉴权结果。
鉴权策略
通过配置AuthorizationPolicy资源配置鉴权策略。其中包含选择器、行为和一组规则:
- selector字段指出策略应用目标
- action字段指出允许或是拒绝请求
- rules字段指出何时触发鉴权
- from字段标明请求的来源
- to字段标明请求的操作
- when字段标明应用条件
下例展示允许两个来源,cluster.local/ns/default/sa/sleep
服务账户和dev
命名空间的请求携带JWT访问foo
命名空间下有app: httpbin
标签和version: v1
标签的工作负载
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: httpbin
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
version: v1
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/sleep"]
- source:
namespaces: ["dev"]
to:
- operation:
methods: ["GET"]
when:
- key: request.auth.claims[iss]
values: ["https://accounts.google.com"]
下例拒绝不是foo
命名空间下的请求
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: httpbin-deny
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
version: v1
action: DENY
rules:
- from:
- source:
notNamespaces: ["foo"]
拒绝策略有限与允许策略。
策略目标
通过 metadata/namespace
或者selector
制定策略的应用范围。设置 metadata/namespace
为root时,策略会应用到所有的命名空间,默认值是istio-system
。
下例,允许使用GET或HEAD方法访问default命名空间下label为app: products
的工作负载。
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-read
namespace: default
spec:
selector:
matchLabels:
app: products
action: ALLOW
rules:
- to:
- operation:
methods: ["GET", "HEAD"]
值匹配
授权策略的大部分字段支持一下匹配模式:
- Exact:精确匹配字符串
- Prefix:用*结尾的字符串,比如
test.abc.*
可以匹配"test.abc.com", “test.abc.com.cn”, "test.abc.org"等 - Suffix:用*开头的字符串,比如
*.abc.com
可以匹配"eng.abc.com", "test.eng.abc.com"等 - Presence:*用于表示非空值。
但是以下字段只支持精确匹配: - key字段置于when字段内时
- ipBlocks字段置于source字段时
- ports字段置于to字段内时
例:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: tester
namespace: default
spec:
selector:
matchLabels:
app: products
action: ALLOW
rules:
- to:
- operation:
paths: ["/test/*", "*/info"]
排除匹配
Istio支持排除匹配,用于匹配诸如when字段中的notValues、source字段中的notIpBlocks。To字段中的notPorts等负条件,。如果请求路径不是/healthz,那么下面的示例需要一个有效的请求主体,该主体派生自JWT身份验证。因此,该策略从JWT身份验证中排除对/healthz路径的请求:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: disable-jwt-for-healthz
namespace: default
spec:
selector:
matchLabels:
app: products
action: ALLOW
rules:
- to:
- operation:
notPaths: ["/healthz"]
from:
- source:
requestPrincipals: ["*"]
下例拒绝对没有请求主体的请求的/admin路径的请求:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: enable-jwt-for-admin
namespace: default
spec:
selector:
matchLabels:
app: products
action: DENY
rules:
- to:
- operation:
paths: ["/admin"]
from:
- source:
notRequestPrincipals: ["*"]
全部允许和全部拒绝
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-all
namespace: default
spec:
action: ALLOW
rules:
- {}
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: deny-all
namespace: admin
spec:
{}
自定义条件
使用when字段指定额外的条件。比如下例包含一条条件request.headers[version]
要么是v1要么是v2。这里的键,request.headers[version],是Istio属性request.headers中的实体,request.headers是一个map对象。
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: httpbin
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
version: v1
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/sleep"]
to:
- operation:
methods: ["GET"]
when:
- key: request.headers[version]
values: ["v1", "v2"]
支持的键可以在条件页查询。
认证与未认证身份
要允许工作负载可以公共访问,则让source
字段为空
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: httpbin
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
version: v1
action: ALLOW
rules:
- to:
- operation:
methods: ["GET", "POST"]
如果只允许认证的用户访问,则把principals
设为’*’:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: httpbin
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
version: v1
action: ALLOW
rules:
- from:
- source:
principals: ["*"]
to:
- operation:
methods: ["GET", "POST"]
- 点赞
- 收藏
- 关注作者
评论(0)