概述
应用服务网格支持接入第三方鉴权服务,用于网格内的服务鉴权,当前支持定义HTTP协议和gRPC协议的自定义鉴权服务。
添加自定义授权服务
- 从控制台 选择 网格安全中心 -> 自定义授权服务菜单,选择创建。
- 填写参数,提交即可。
参数说明如下:
配置 | 说明 |
---|---|
协议 | 调用外部授权服务的协议,当前支持HTTP和gRPC。 |
名称 | 唯一标识一个外部授权服务。 |
服务地址 | 外部授权服务的地址。 |
服务端口 | 外部授权服务的端口号。 |
超时时间 | 调用外部授权服务时的超时时间,单位秒。 |
针对HTTP协议的外部授权服务支持以下额外配置选项:
配置 | 说明 |
---|---|
鉴权服务不可用时放行请求 | 打开此开关后,访问鉴权服务出现异常时将放行请求。 |
在鉴权请求中携带header | 将请求中指定的头部带到鉴权服务。 |
在鉴权请求中新增header | 在发往鉴权服务的请求中新增头部。 |
鉴权通过时覆盖Header | 使用鉴权请求Response中Header覆盖发往目标服务的请求中的Header。 |
鉴权失败时覆盖Header | 使用鉴权请求Response中Header覆盖Response中的Header。 |
在鉴权请求中携带请求Body | 打开此开关后,将在访问鉴权服务时携带请求Body。 |
鉴权请求携带Body的最大长度(Byte) | 限制发往鉴权服务的Body大小。 |
允许将不完整消息发送至鉴权服务 | 在请求Body超出最大长度限制时,若不启用该选项则将拒绝请求并返回HTTP 413。 |
针对gRPC协议的外部授权服务支持以下额外配置选项:
配置 | 说明 |
---|---|
鉴权服务不可用时放行请求 | 打开此开关后,访问鉴权服务出现异常时将放行请求。 |
在鉴权请求中携带请求Body | 打开此开关后,将在访问鉴权服务时携带请求Body。 |
鉴权请求携带Body的最大长度(Byte) | 限制发往鉴权服务的Body大小。 |
允许将不完整消息发送至鉴权服务 | 在请求Body超出最大长度限制时,若不启用该选项则将拒绝请求并返回HTTP 413。 |
鉴权请求Body编码方式及存放位置 | 当前支持两种选项: 1,UTF8 String(Body):授权请求将编码为UTF8字符串放到请求body字段。 2,RawBytes(RawBody):授权请求将编码为原始字节串放到请求的raw_body字段。 |
修改自定义授权服务
- 从控制台 选择 网格安全中心 -> 自定义授权服务菜单,默认会展示当前定义的所有外部授权服务。
- 选择要编辑的服务编辑即可。
删除自定义授权服务
- 从控制台 选择 网格安全中心 -> 自定义授权服务菜单,默认会展示当前定义的所有外部授权服务。
- 选择要编辑的服务删除即可。
自定义授权服务使用示例
创建测试命名空间
kubectl create ns foo
打开sidecar自动注入:
kubectl label ns foo istio-injection=enabled
部署sleep和httpbin应用
apiVersion: v1
kind: ServiceAccount
metadata:
name: sleep
---
apiVersion: v1
kind: Service
metadata:
name: sleep
labels:
app: sleep
service: sleep
spec:
ports:
- port: 80
name: http
selector:
app: sleep
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: sleep
spec:
replicas: 1
selector:
matchLabels:
app: sleep
template:
metadata:
labels:
app: sleep
spec:
terminationGracePeriodSeconds: 0
serviceAccountName: sleep
containers:
- name: sleep
image: registry-vpc-crs-huadong1.cnsp-internal.ctyun.cn/library/curl
command: ["/bin/sleep", "infinity"]
imagePullPolicy: IfNotPresent
volumeMounts:
- mountPath: /etc/sleep/tls
name: secret-volume
volumes:
- name: secret-volume
secret:
secretName: sleep-secret
optional: true
---
部署外部授权服务
apiVersion: v1
kind: Service
metadata:
name: ext-authz
labels:
app: ext-authz
spec:
ports:
- name: http
port: 8000
targetPort: 8000
- name: grpc
port: 9000
targetPort: 9000
selector:
app: ext-authz
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ext-authz
spec:
replicas: 1
selector:
matchLabels:
app: ext-authz
template:
metadata:
labels:
app: ext-authz
spec:
containers:
- image: registry-vpc-crs-huadong1.cnsp-internal.ctyun.cn/library/ext-authz:1.16.2
imagePullPolicy: IfNotPresent
name: ext-authz
ports:
- containerPort: 8000
- containerPort: 9000
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: httpbin
---
apiVersion: v1
kind: Service
metadata:
name: httpbin
labels:
app: httpbin
service: httpbin
spec:
ports:
- name: http
port: 8000
targetPort: 80
selector:
app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: httpbin
spec:
replicas: 1
selector:
matchLabels:
app: httpbin
version: v1
template:
metadata:
labels:
app: httpbin
version: v1
spec:
serviceAccountName: httpbin
containers:
- image: registry-vpc-crs-huadong1.cnsp-internal.ctyun.cn/library/httpbin
imagePullPolicy: IfNotPresent
name: httpbin
ports:
- containerPort: 80
部署完成后,在foo命名空间下看到3个服务。
不使用授权策略的情况下,验证从sleep应用访问httpbin用没有被拦截(返回状态码200):
kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n"
200
查看外部授权服务已经启动,HTTP和gRPC授权服务分别监听8000和9000端口:
kubectl logs "$(kubectl get pod -l app=ext-authz -n foo -o jsonpath={.items..metadata.name})" -n foo -c ext-authz
2023/08/14 11:26:54 Starting HTTP server at [::]:8000
2023/08/14 11:26:54 Starting gRPC server at [::]:9000
添加外部授权服务
将上面的HTTP和gRPC服务添加到服务网格的外部授权服务内。
定义授权策略&验证访问
定义如下授权策略,对httpbin应用的/headers路径的请求将被转发到第三方授权服务进行验证:
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: ext-authz
spec:
selector:
matchLabels:
app: httpbin
action: CUSTOM
provider:
# The provider name must match the extension provider defined in the mesh config.
# You can also replace this with sample-ext-authz-http to test the other external authorizer definition.
name: sample-ext-authz-grpc
rules:
# The rules specify when to trigger the external authorizer.
- to:
- operation:
paths: ["/headers"]
从sleep应用请求httpbin的/headers路径,由于请求头带了"x-ext-authz:deny",请求被拦截:
kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/headers" -H "x-ext-authz: deny" -s
denied by ext_authz for not found header `x-ext-authz: allow` in the request
修改请求,带上"x-ext-authz: allow"头部,请求放行:
kubectl exec "$(kubectl get pod -l app=sleep -n foo -o jsonpath={.items..metadata.name})" -c sleep -n foo -- curl "http://httpbin.foo:8000/headers" -H "x-ext-authz: allow" -s
{
"headers": {
"Accept": "*/*",
"Host": "httpbin.foo:8000",
"User-Agent": "curl/8.2.1",
"X-B3-Parentspanid": "eb42a8165099e7db",
"X-B3-Sampled": "1",
"X-B3-Spanid": "cd6540ba1cfd9e8c",
"X-B3-Traceid": "e7e15cc10dc66630eb42a8165099e7db",
"X-Envoy-Attempt-Count": "1",
"X-Ext-Authz": "allow",
"X-Ext-Authz-Additional-Header-Override": "grpc-additional-header-override-value",
"X-Ext-Authz-Check-Received": "source:{address:{socket_address:{address:\"10.1.0.25\" port_value:37654}} principal:\"spiffe://cluster.local/ns/foo/sa/sleep\"} destination:{address:{socket_address:{address:\"10.1.0.24\" port_value:80}} principal:\"spiffe://cluster.local/ns/foo/sa/httpbin\"} request:{time:{seconds:1692015383 nanos:990298000} http:{id:\"7462237371770661564\" method:\"GET\" headers:{key:\":authority\" value:\"httpbin.foo:8000\"} headers:{key:\":method\" value:\"GET\"} headers:{key:\":path\" value:\"/headers\"} headers:{key:\":scheme\" value:\"http\"} headers:{key:\"accept\" value:\"*/*\"} headers:{key:\"user-agent\" value:\"curl/8.2.1\"} headers:{key:\"x-b3-sampled\" value:\"1\"} headers:{key:\"x-b3-spanid\" value:\"eb42a8165099e7db\"} headers:{key:\"x-b3-traceid\" value:\"e7e15cc10dc66630eb42a8165099e7db\"} headers:{key:\"x-envoy-attempt-count\" value:\"1\"} headers:{key:\"x-ext-authz\" value:\"allow\"} headers:{key:\"x-forwarded-client-cert\" value:\"By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=c32db24acfa670a8bbe46f0897ebb70b9ccc0e630ee32afcd1ec037d6616e6c5;Subject=\\\"\\\";URI=spiffe://cluster.local/ns/foo/sa/sleep\"} headers:{key:\"x-forwarded-proto\" value:\"http\"} headers:{key:\"x-request-id\" value:\"aba7b68c-6bee-9d58-b163-7454075c6ece\"} path:\"/headers\" host:\"httpbin.foo:8000\" scheme:\"http\" protocol:\"HTTP/1.1\"}} metadata_context:{}",
"X-Ext-Authz-Check-Result": "allowed",
"X-Forwarded-Client-Cert": "By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=c32db24acfa670a8bbe46f0897ebb70b9ccc0e630ee32afcd1ec037d6616e6c5;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/sleep"
}
}