使用 Kyverno-Envoy-Plugin 轻松实现外部授权。
作者:Sanskar Gurdasani
微服务通过将应用程序划分为更小、独立的组件,提高了各个开发团队的生产力。然而,单靠微服务并不能解决分布式系统中长期存在的身份验证和授权问题。由于微服务环境的多样性和短暂性,这些问题可能变得更加难以管理。
随着越来越多的组织转向微服务,迫切需要能够跨不同微服务工作的独立身份验证和授权机制。
在这篇博文中,我们将介绍 Kyverno-Envoy-Plugin[1],讲解其工作原理,以及如何使用这一版本的 Kyverno 在不修改微服务或应用程序代码的情况下,强制执行细粒度、上下文感知的访问控制策略。
什么是 Envoy
Envoy[2] 是一个为大规模现代服务导向架构量身定制的第七层代理和通信总线。从 1.7.0 版本开始,Envoy 包含一个外部授权过滤器[3],连接到授权服务,以检查传入请求是否被授权。这一功能允许将授权决策卸载到外部服务,该服务可以访问请求上下文。请求上下文包括网络活动的来源和目的地,以及网络请求(如 HTTP 请求)的详细信息。这些信息使外部服务能够对由 Envoy 处理的传入请求进行明智的授权决策。
什么是 Kyverno-Envoy Plugin
Kyverno-Envoy 插件通过实现 Envoy 外部授权 API 的 gRPC 服务器,扩展了 Kyverno-JSON[4]。这使你能够在服务网格环境中对进出流量强制执行 Kyverno 策略,为你的应用程序提供额外的安全性和控制。你可以使用这一版本的 Kyverno 在不修改微服务的情况下,强制执行细粒度、上下文感知的访问控制策略。
原理
除了 Envoy 边车,你的应用程序 Pod 还将包含一个 Kyverno-Envoy-Plugin 组件,作为边车或独立 Pod。该 Kyverno-Envoy-Plugin 将被配置为与 Kyverno-Envoy-Plugin gRPC 服务器进行通信。当 Envoy 接收到针对你的微服务的 API 请求时,它会咨询 Kyverno-Envoy-Plugin 服务器,以确定请求是否应被允许。
以下是 Kyverno-Envoy-Plugin 作为应用程序边车部署的架构:
以下是 Kyverno-Envoy-Plugin 作为独立 Pod 部署的架构:
在 Envoy 本地执行策略评估具有优势,因为它消除了授权检查所需的额外网络跳转,从而提高了性能和可用性。
Kyverno-Envoy-Plugin 可以与基于 Envoy 的服务网格(如 Istio[5]、Gloo[6]、Kuma[7] 等)一起部署。
开始使用
在本博客中,我们将把 Kyverno-Envoy-Plugin 部署为应用程序容器旁边的边车容器。该插件将处理对应用程序的传入请求的授权。此外,还提供了将插件作为独立 Pod 部署的文档[8]。
在查看 Kyverno-Envoy-Plugin 之前,我们需要一个 Kubernetes 集群。可以使用 minikube[9] 和 kubectl[10] 创建本地集群。
创建本地集群
使用以下命令启动 minikube 集群:
minikube start
安装 Kyverno-Envoy-Plugin 边车与应用程序
将 Envoy 和 Kyverno-Envoy-Plugin 作为边车容器安装应用程序。
$ kubectl apply -f https://raw.githubusercontent.com/kyverno/kyverno-envoy-plugin/main/quick_start.yaml
application.yaml
清单定义了以下资源:
该 Deployment 包含一个示例 Go 应用程序,提供图书馆书籍集合的信息,并暴露 API 以获取、创建和删除书籍集合。有关 Go 测试应用程序的更多信息,请查看此处[11]。 Deployment 还包含一个 Kyverno-Envoy-Plugin 边车容器,以及 Envoy 边车容器。当 Envoy 接收到针对 Go 测试应用程序的 API 请求时,它会检查 Kyverno-Envoy-Plugin,以决定请求是否应被允许。Kyverno-Envoy-Plugin 边车容器被配置为查询 Kyverno-JSON 引擎,以获取关于传入请求的政策决策。 一个 ConfigMap policy-config
用于在默认命名空间中将策略传递给 Kyverno-Envoy-Plugin 边车。一个 ConfigMap envoy-config
用于传递带有外部授权过滤器的 Envoy 配置,以将授权检查导向 Kyverno-Envoy-Plugin 边车。Deployment 还包括一个 init 容器,用于安装 iptables 规则,将所有容器流量重定向到 Envoy 代理边车容器。有关 init 容器的更多信息,请查看此处[12]。
使测试应用程序在集群中可访问
kubectl expose deployment testapp --type=NodePort --name=testapp --port=8080
将 SERVICE_URL
环境变量设置为服务的 IP/端口
minikube:
export SERVICE_PORT=$(kubectl get service testapp -o jsonpath='{.spec.ports[?(@.port==8080)].nodePort}')
export SERVICE_HOST=$(minikube ip)
export SERVICE_URL=$SERVICE_HOST:$SERVICE_PORT
echo $SERVICE_URL
调用示例测试应用程序并验证授权
为了方便起见,我们将 Alice 和 Bob 的令牌存储在环境变量中(不推荐用于生产环境)。Bob 被分配为管理员角色,Alice 被分配为访客角色。
export ALICE_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjIyNDEwODE1MzksIm5iZiI6MTUxNDg1MTEzOSwicm9sZSI6Imd1ZXN0Iiwic3ViIjoiWVd4cFkyVT0ifQ.ja1bgvIt47393ba_WbSBm35NrUhdxM4mOVQN8iXz8lk"
export BOB_TOKEN="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjIyNDEwODE1MzksIm5iZiI6MTUxNDg1MTEzOSwicm9sZSI6ImFkbWluIiwic3ViIjoiWVd4cFkyVT0ifQ.veMeVDYlulTdieeX-jxFZ_tCmqQ_K8rwx2OktUHv5Z0"
我们传递给 Kyverno-Envoy-Plugin 边车的政策在 ConfigMap policy-config
中配置为检查传入请求的条件,如果用户是访客且请求方法为 POST,路径为 /book
,则拒绝该请求。
apiVersion: json.kyverno.io/v1alpha1
kind: ValidatingPolicy
metadata:
name: checkrequest
spec:
rules:
- name: deny-guest-request-at-post
assert:
any:
- message: "POST method calls at path /book are not allowed to guests users"
check:
request:
http:
method: POST
headers:
authorization:
(split(@, ' ')[1]):
(jwt_decode(@ , 'secret').payload.role): admin
path: /book
- message: "GET method call is allowed to both guest and admin users"
check:
request:
http:
method: GET
headers:
authorization:
(split(@, ' ')[1]):
(jwt_decode(@ , 'secret').payload.role): admin
path: /book
- message: "GET method call is allowed to both guest and admin users"
check:
request:
http:
method: GET
headers:
authorization:
(split(@, ' ')[1]):
(jwt_decode(@ , 'secret').payload.role): guest
path: /book
检查 Alice,她可以获取书籍,但不能创建书籍。
curl -i -H "Authorization: Bearer "$ALICE_TOKEN"" http://$SERVICE_URL/book
curl -i -H "Authorization: Bearer "$ALICE_TOKEN"" -d '{"bookname":"Harry Potter", "author":"J.K. Rowling"}' -H "Content-Type: application/json" -X POST http://$SERVICE_URL/book
检查 Bob,他可以获取书籍,也可以创建书籍。
curl -i -H "Authorization: Bearer "$BOB_TOKEN"" http://$SERVICE_URL/book
curl -i -H "Authorization: Bearer "$BOB_TOKEN"" -d '{"bookname":"Harry Potter", "author":"J.K. Rowling"}' -H "Content-Type: application/json" -X POST http://$SERVICE_URL/book
查看日志:
kubectl logs "$(kubectl get pod -l app=testapp -o jsonpath={.items..metadata.name})" -c kyverno-envoy-plugin -f
第一次、第三次和最后一次请求通过,但第二次请求失败。
$ kubectl logs "$(kubectl get pod -l app=testapp -n demo -o jsonpath={.items..metadata.name})" -n demo -c kyverno-envoy-plugin -f
Starting HTTP server on Port 8000
Starting GRPC server on Port 9000
Request is initialized in kyvernojson engine .
2024/04/26 17:11:42 Request passed the deny-guest-request-at-post policy rule.
Request is initialized in kyvernojson engine .
2024/04/26 17:22:11 Request violation: -> POST method calls at path /book are not allowed to guests users
-> any[0].check.request.http.headers.authorization.(split(@, ' ')[1]).(jwt_decode(@ , 'secret').payload.role): Invalid value: "guest": Expected value: "admin"
-> GET method call is allowed to both guest and admin users
-> any[1].check.request.http.headers.authorization.(split(@, ' ')[1]).(jwt_decode(@ , 'secret').payload.role): Invalid value: "guest": Expected value: "admin"
-> any[1].check.request.http.method: Invalid value: "POST": Expected value: "GET"
-> GET method call is allowed to both guest and admin users
-> any[2].check.request.http.method: Invalid value: "POST": Expected value: "GET"
Request is initialized in kyvernojson engine .
2024/04/26 17:23:13 Request passed the deny-guest-request-at-post policy rule.
Request is initialized in kyvernojson engine .
2024/04/26 17:23:55 Request passed the deny-guest-request-at-post policy rule.
配置
要部署 Kyverno-Envoy-Plugin,请在 Kubernetes Deployment 中包含以下容器:
- name: kyverno-envoy-plugin
image: sanskardevops/plugin:0.0.34
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8181
- containerPort: 9000
volumeMounts:
- readOnly: true
args:
- "serve"
- "--policy=/policies/policy.yaml"
- "--address=:9000"
- "--healthaddress=:8181"
livenessProbe:
httpGet:
path: /health
scheme: HTTP
port: 8181
initialDelaySeconds: 5
periodSeconds: 5
readinessProbe:
httpGet:
path: /health
scheme: HTTP
port: 8181
initialDelaySeconds: 5
periodSeconds: 5
总结
本博文演示了如何有效使用 Kyverno-Envoy 插件为微服务架构中的传入请求做出外部授权决策。通过利用 Kyverno 策略的强大功能和 Envoy 的外部授权过滤器,你可以实现细粒度、上下文感知的访问控制,而无需修改应用程序代码。这种方法不仅简化了安全策略的管理,还通过确保授权检查在微服务中始终一致应用,提高了服务的安全性。
Kyverno-Envoy-Plugin 为希望在服务网格中强制实施基于策略的访问控制的组织提供了可靠的解决方案。按照本指南中的步骤,你可以轻松部署和配置该插件,充分利用 Kyverno 的政策能力与 Envoy 的强大代理功能。
欲进一步探索,请查看:
🔗 查看 GitHub 上的项目:https://github.com/kyverno/kyverno-envoy-plugin 📚 浏览文档:https://kyverno.github.io/kyverno-envoy-plugin
Kyverno-Envoy-Plugin: https://github.com/kyverno/kyverno-envoy-plugin
[2]Envoy: https://www.envoyproxy.io/docs/envoy/latest/intro/what_is_envoy
[3]外部授权过滤器: https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/security/ext_authz_filter.html
[4]Kyverno-JSON: https://kyverno.github.io/kyverno-json/latest/
[5]Istio: https://istio.io/
[6]Gloo: https://gloo.solo.io/
[7]Kuma: https://kuma.io/
[8]文档: https://kyverno.github.io/kyverno-envoy-plugin/dev/
[9]minikube: https://minikube.sigs.k8s.io/docs/
[10]kubectl: https://kubernetes.io/docs/tasks/tools/install-kubectl/
[11]此处: https://github.com/Sanskarzz/kyverno-envoy-demos/tree/main/test-application
[12]此处: https://github.com/kyverno/kyverno-envoy-plugin/tree/main/demo/standalone-envoy/envoy_iptables
点击【阅读原文】阅读网站原文。
CNCF概况(幻灯片)
扫描二维码联系我们!
CNCF (Cloud Native Computing Foundation)成立于2015年12月,隶属于Linux Foundation,是非营利性组织。
CNCF(云原生计算基金会)致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用。请关注CNCF微信公众号。