目录
- 1. Istio Security概览
- 2. Istio 身份
- 2.1 Istio身份的具体示例
- 2.2 Istio workload身份的承载物 - X.509证书
- 3. 认证Authentication
- 3.1 认证策略 Authentication policies
- 3.1.1 PeerAuthentication
- 3.1.2 RequestAuthentication
- 3.1.3 认证通过后输出的principal
- 4. 授权Authorization
- 4.1 授权策略 Authorization policies
- 5. 总结
- 6. 后续 TODO
1. Istio Security概览
Istio Security可整体概括为下图:
Istio安全保护工具:
身份(strong identity)
-
策略(powerful policy)
:包括认证策略、授权策略 传输加密(TLS encryption)
-
AAA(authentication, authrorization, audit)
:认证、授权、审计
Istio Security目标:
- Security by default
- Defense in depth
- Zero-trust network
Istio Security整体架构
核心组件:
-
CA(证书权威机构Certificate Authority)
:负责密钥key和证书cert管理 -
Configuration API server(Authen policies, Author policies, Secure naming information)
:负责向proxies分发策略 -
PEPs(Policy Enforcement Points,例如sidecar, perimeter proxies)
:接收安全策略并执行相关安全策略(通讯加密) -
Envoy proxy extensions
: manage telemetry and auditing
2. Istio 身份
身份是任何安全框架的基础,后续的认证、授权都是在身份的基础上进行的,
我们做应用开发时,身份可以表示具体的用户、企业、设备、服务等等。
2.1 Istio身份的具体示例
Istio使用了一个更广泛的service identity
的概念,service identity
可以代表:
-
一个人类用户
对应IstioRequestAuthentication JWT
认证,
在Istio中对应属性:request.auth.principal={iss}/{sub},
即通过JWT令牌
标识一个用户 -
单独的workload
对应IstioPeerAuthentication mTls
验证,mTls证书中编码了workload身份,
即K8s service account
标识一个workload,
在Istio中对应属性:source.principal=cluster.local/ns/{namespace}/sa/{serviceAccountName} -
一组workloads
可以理解为一个K8s service account对应多个workload,
又或者如形式:cluster.local/ns/default/sa/* - 自定义的用户账户、服务账户、服务名称、Istio service account …
2.2 Istio workload身份的承载物 - X.509证书
Istio中每个workload都有一个身份,这个身份是通过X.509证书来进行表示的
(即每个workload都有一套由istiod CA签发的证书,而证书中编码了当前worload的身份信息),在Istio中workload的身份是由K8s Service account来进行表示的
,
格式:cluster.local/ns/{namespace}/sa/{serviceAccountName}
具体证书流转流程如下:
- Istio agent(存在于sidecar 容器中的 pilot-agent 进程)负责生成key+csr
- Istio agent 请求Istiod CA生成证书(grpc服务)
- Istiod CA验证csr请求,生成并返回证书给Istio agent
- workload启动完成后,Istio agent通过
Envoy secret discovery service (SDS)
向Envory发送key+cert - Istio agent监控cert过期时间并重复发起上述流程(即cert过期后会自动向Istiod CA续签证书 )
3. 认证Authentication
Isito提供两种认证类型:
Peer Authentication
- 服务(工作负载)间的认证(service-to-service)
Istio通过envoy间的双向TLS(mutual TLS)
来实现服务间的认证
和通信加密
,
同时client需要对server端证书cert中的service account
进行安全命名(Secure Naming)检查
注:Istio使用的mTLS版本 >= TLSv1_2版本Request Authentication
- 终端用户(end-user)的请求(单个request)认证
Istio启用请求级别的JWT验证
,且支持集成自定义认证服务器
(Authentication provider、OIDC provider),如:Keycloak, Auth0, Google Auth, ORY hydra等。
注:需要应用Application自己去获取、添加JWT到请求request中
安全命名(Secure Naming)Server Identities
到Service Name
的映射,
即K8s Service Account
到K8s Service
的映射,
Secure Naming Check即用于client检查server端cert中的service account是否被授权运行server端服务,
可用于防止DNS spoofing, BGP/route hijacking, ARP spoofing等。
注:对于非HTTP/HTTPS流量,安全命名不能保护其免于 DNS 欺骗
例如:
Server Identity | Service Name | 说明 |
A | B | A被授权运行服务B |
3.1 认证策略 Authentication policies
所有的的认证策略都是作用在Server服务端,即:
- 为Server服务端指定Client客户端的认证策略
- 由Server服务端对Client客户端执行认证
之前提到过Istio认证分为2种认证类型,2种认证类型即对应各自同名的认证策略配置如下表:
认证类型 | 认证策略配置 | 说明 |
Peer Authentication | PeerAuthentication | - 服务(工作负载)间的认证(service-to-service) - Istio通过envoy间的 |
Request Authentication | RequestAuthentication | - 终端用户(end-user)的请求(单个request)认证 - Istio启用请求级别的 - 需要应用Application自己去获取、添加JWT到请求request中 |
认证策略的作用范围:
- Mesh-wide policy - 网格范围
定义在root命名空间(即istio-system)下的策略,且没有selector或空selector,则对整个网格内的workload都生效 - Namespace-wide policy - 命名空间范围
定义在非root命名空间下,且没有selector或空selector,则对该命名空间下的workload都生效 - workload-specific policy - 特定workload
定义在非root命名空间下,且selector不为空,则对该命名空间下selector选中的workload生效
3.1.1 PeerAuthentication
PeerAuthentication主要用来给目标workload(或特定port)指定mutual TLS模式,
具体mtls.mode如下:
- PERMISSIVE(宽容模式,默认)
同时支持明文、mtls流量 - STRICT(严格)
仅支持mtls流量 - DISABLE(禁用)
禁用mtls,即仅支持明文传输
注:
-
PeerAuthentication mtls
用于配置指定workload对应sidecar
需要接受的mTLS流量类型
。 -
DestinationRule tls
用于配置客户端workload对应的siedecar
需要向指定服务端workload sidecar
发送的TLS流量类型
。
若PeerAuthentication开启mtls.mode:STRICT模式,
则DestinationRule需开启trafficPolicy.tls.mode: ISTIO_MUTUAL
(DestinationRule默认不配置则Istio网格内服务默认开启ISTIO_MUTUAL)。
PeerAuthentication和DestinationRule具体差别参见:
https://preliminary.istio.io/latest//docs/ops/configuration/traffic-management/tls-configuration/#sidecars
PeerAuthentication具体示例如下:
# 假设在命名空间foo下存在服务exmaple-service,服务提供8000端口,对应容器80端口
# 对应K8s service定义如下:
apiVersion: v1
kind: Service
metadata:
name: example-service
namespace: foo
spec:
ports:
- name: http
port: 8000
protocol: TCP
targetPort: 80
selector:
app: example-app
# 需求1:命名空间foo下的所有服务都需要开启mTls模式(即双向TLS认证)
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: "example-policy"
namespace: "foo"
spec:
# selector不存在
# 则对命名空间foo下的所有workload均生效
# 设置mtls.mode为STRICT,则开启mtls(仅接收mtls流量)
mtls:
mode: STRICT
# 需求2:命名空间foo下的服务example-service的80端口禁用mTls模式(即支持明文传输)
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: "example-workload-policy"
namespace: "foo"
spec:
# 通过selector指定label选择特定的workload
selector:
matchLabels:
app: example-app
# 端口级别的mTls模式设置
portLevelMtls:
# 指定workload的目标端口80的mtls模式为禁用
80:
mode: DISABLE
TODO: 多个规则合并、优先级
3.1.2 RequestAuthentication
RequestAuthentication主要用来设置对目标worload发起request请求的JWT验证,
即对JWT中标识的终端用户(end-user)的请求(request)认证。
RequestAuthentication整体配置结构如下:
-
namespace
策略生效的命名空间 -
selector -> matchLables
指定策略作用的目标workload jwtRules
jwt验证规则的配置
-
issuer
jwt的发布者 -
audiences
受众,即jwt的接受者 -
jwksUri | jwks
jwks(公钥),即用来验证jwt签名的公钥,jwksUri和jwks二选一 fromHeaders
jwt在请求头的位置
-
name
header名称 -
prefix
token前缀,如"Bearer "
-
fromParams
jwt在query参数中的参数名称 -
outputPayloadToHeader
指定jwt验证通过后的payload传递给哪个请求header -
forwardOriginalToken
是否保留原始token,默认false
jwtRules
概括起来分为如下几类配置:
- jwt token的位置(jwtRules.fromHeaders, jwtRules.fromParams)
- jwt.payload等相关属性(jwtRules.issuer, jwtRules.audiences)、request
- jwt公钥,即JWKS(JSON Web Key Set),用来验证jwt的签名(jwtRules.jwksUri, jwtRules.jwks)
- 是否保留jwt(jwtRules.outputPayloadToHeader, jwtRules.forwardOriginalToken)
RequestAuthentication配置示例
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: httpbin
namespace: foo
spec:
# worload的label选择器
selector:
matchLabels:
app: httpbin
# jwt验证规则
jwtRules:
# jwt的发布者
- issuer: "issuer-foo"
# 受众,即jwt的接受者
audiences:
- bookstore_android.apps.example.com
bookstore_web.apps.example.com
# jwks(公钥)uri发现地址,即用来验证jwt签名的公钥
jwksUri: https://example.com/.well-known/jwks.json
# jwks(此属性与jwksUri需二选一)
jwks: "jwks json"
# jwt在请求头的位置
fromHeaders:
- name: x-jwt-assertion
prefix: "Bearer "
# jwt在query参数中的参数名称
fromParams:
- "my_token"
# 指定jwt验证通过后的payload传递给哪个请求header
# 传递格式:base64_encoded(jwt_payload_in_JSON)
outputPayloadToHeader: payload_header
# 是否保留原始token(若保留则继续传递token到upstream请求),默认false
forwardOriginalToken: false
注:
- jwt验证通过,则正常接受请求
- jwt验证失败,则拒绝请求
jwt为空,默认接受请求(需通过设置Authoriation policies来拒绝不带token的请求)
- 支持多个不同位置的jwt token,
- 但仅支持一个有效的token(多个有效的token会导致输出的principal不确定)
3.1.3 认证通过后输出的principal
在认证(Authentication Policies)
通过后,Istio会将各自的认证策略中抽取到的身份信息传递给Istio 身份属性
,可供后续授权策略(AuthorizationPolies)
使用
认证策略 | 抽取出的身份属性 (Istio属性) | 授权策略 AuthorizationPolicy | 说明 |
PeerAuthentication | source.principal | rules.from.principals | workload的身份,需开启mTls获取 格式:cluster.local/ns/{namespace}/sa/{serviceAccountName} 示例:cluster.local/ns/default/sa/productPage |
RequestAuthentication | request.auth.principal | rules.from.requestPrinciplas | 用户身份,即 jwt令牌(认证通过)中提取的身份, 格式:{issuer}/{subject} 示例: issuer.example.com/subject-admin |
4. 授权Authorization
Isito默认开启对网格内的workload的访问控制,
由server端的Envoy代理(根据AuthorizationPolicy配置)对inbound流量执行access control,
Istio授权(Authorization)提供如下特性:
- workload-to-workload, end-user-to-workload的访问控制
- 提供简单、唯一的授权策略配置:
AuthorizationPolicy
- 兼容多协议:gRPC, HTTP, HTTPS, HTTP/2, TCP
Istio根据action类型( CUSTOM | DENY | ALLOW)
分层验证AuthorizationPolicy,
即依次验证:CUSTOM -> DENY -> ALLOW
,具体授权策略优先级如下图:
注: 若目标workload的操作
没有设置AuthorizationPolicy,则表示没有限制,放行所有对目标workload的请求
4.1 授权策略 Authorization policies
Istio提供唯一的授权策略配置:AuthorizationPolicy
,其整体结构如下:
-
namespace
策略生效的命名空间(即目标workload所属的namespace) -
selector -> matchLables
指定策略作用的目标workload -
actions: CUSTOM | DENY | ALLOW
是否允许请求 rules
指定触发action的条件(即满足rules定义的请求,则执行action)
-
from -> source[]
指定请求来源(source满足的条件),为空则表示全部来源
pincipals(workload身份)、requestPrincipals(用户身份)、namespaces、ipBlocks、remoteIpBlocks及相应not属性 -
to -> operation[]
指定请求的操作(request满足的条件),为空则表示所有操作
hosts、ports、methods、paths及相应not属性 when
指定额外的附件条件(key对应Istio属性)
-
key -> values[] | notValues[]
request.headers[header_name]、
request.auth.[principal | audiences | presenter | claims[claimName] ]、
source.[ip | namespace | principal ]、remote.ip、
destination.[ip | port]、 connection.sni
AuthorizationPolicy配置概括起来就是:允许(或拒绝)
来源workload 或 用户
对目标workload
进行什么操作
,并且需要满足什么条件
。
具体配置示例:
# 作用目标: workload(namespace=foo, label(app=httpbin, version=v1))
# 动作action: 允许访问ALLOW
# 来源rules.from.source: principals=="cluster.local/ns/default/sa/sleep" or namepsace=="dev"
# 操作rules.to.operation:method=="GET"
# 条件rules.when: request.auth.claims[iss]=="https://accounts.google.com"
# 即[仅允许][cluster.local/ns/default/sa/sleep身份(PeerAuthentication mTls验证通过)、或者dev命名空间]的workload访问[httpbin:v1服务的GET请求]
# 且[用户RequestAuthentication JWT验证通过、且jwt.claims[iss]=="https://accounts.google.com"]
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: httpbin
namespace: foo
spec:
selector:
matchLabels:
app: httpbin
version: v1
action: ALLOW
rules:
- from:
- source:
# 指定来源workload身份
# principals: ["*"]则表示需要PeerAuthentication mtls验证通过
principals: ["cluster.local/ns/default/sa/sleep"]
- source:
# 指定来源namespace
namespaces: ["dev"]
to:
- operation:
# 执行GET请求
methods: ["GET"]
when:
# 指定用户jwt需验证通过,且iss为指定值
- key: request.auth.claims[iss]
values: ["https://accounts.google.com"]
# [拒绝][不属于foo命名空间的服务]访问[httpbin:v1服务]
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:
# 不是来自于foo命名空间
notNamespaces: ["foo"]
# 访问非/healthz的请求,均需要JWT验证通过,
# 而访问/healthz的请求,可无需JWT验证(即请求未携带JWT令牌、或者携带的JWT令牌为空)
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:
# 访问除了/healthz以外的path
notPaths: ["/healthz"]
from:
- source:
# 需要RequestAuthentication JWT验证通过
requestPrincipals: ["*"]
5. 总结
记得刚接触Istio时,就看过Istio官网上关于Istio/Concepts/Security 这一章节,当时看的不是很懂,后来陆续接触了
- Oauth2
- Spring Security
- Spring Security Oauth
- RBAC
- OIDC
- CAS
- SSO, SLO
- IDasS
- …
最近回过头来再看Istio Security,渐渐读懂了Istio Security想要做什么了。
可以对比Spring Security
,Spring Security是Java语言在Spring平台的安全管理框架,
通过编码集成用户登录认证、用户鉴权等操作,
后续出现了Oauth, OIDC后,我们可以搭建统一认证服务器AS(Authorization server), OP(OpenID Provider),
如Keycloak、IdentityServer、又或者购买IDaaS,
然后通过Spring Security Oauth框架编码、配置集成Client端、Resource server端,
在Client端我们通过Spring Security框架注册了认证中心,由认证中心完成统一的登录认证并颁发token,
而在Resource server端由Spring Security框架帮我们完了JWT认证,
而具体的鉴权(Authorization、Access control)操作(API path、数据权限、菜单、按钮)还是需要我们通过编码来完成。
而Istio Security
是站在更高层次(Service mesh)看问题,
Istio不关心具体服务使用的是什么编程语言(Java、Python、Go…)、什么安全框架(Spring Security、Shiro…),
它以不侵入代码的形式(Sidecar Envoy),
站在通信协议(HTTP、HTTPS、gRPC、TCP)、安全协议(Oauth2、OIDC)层面看问题,
通过Istio自定义的Yaml配置(PeerAuthentication、RequestAuthentication、AuthorizationPolicy)来实现一定程度的认证、授权配置。
目前简单来看Istio可以替代部分传统安全框架(基于编码开发)的功能:
传统方式 | Istio方式 | 说明 |
Spring Security Oauth - Resource Server | RequestAuthentication | JWT验证(iss, aud…) |
编码方式mTLS证书配置 容器、中间件trustStore配置 | PeerAuthentication | 应用间(workload间)的mTls认证 |
编码方式的鉴权 | AuthorizationPolicy | workload-to-workload end-user-to-workload Istio除了支持传统的用户鉴权, 还支持Mesh内workload间的访问控制, Istio鉴权粒度有限:workloads、hosts、paths、methods、ports |
Spring Security Oauth - Client | AuthorizationPolicy (action.CUSTOM + provider) | Istio实验特性 Istio支持集成外部认证中心 |
编码获取、添加JWT到请求中 | 同 | Istio安全框架也需要应用本身自己负责获取、添加JWT到请求中 |
开发、搭建统一认证服务器 | 同 | Istio安全框架不提供统一认证中心,需要应用平台自己搭建统一认证中心 |
6. 后续 TODO
- 安全命名(Secure Naming)
- Istio集成外部认证中心:External Authorization
- Istio Security与Spring Security对比