共计 3770 个字符,预计需要花费 10 分钟才能阅读完成。
系列专栏申明:比拟流水,次要是写一些踩坑的点,和实际中与文档差距较大的中央的思考。这个专栏的典型特色可能是 次佳实际
,争取能在大量的最佳实际中生存。
Open Policy Agent 吸引我的点在于 Policy as Code
,所以看上去整个云原生概念吸引我的点就在于 X as Code
或者更精确的说是 X as Declarative DSL
。
TODO:会有 JWT
的局部,然而第一稿来不及写了,能够珍藏当前来看。
在 PBAC: Policy Base Authorization Control
中有三个外围的概念,PIP
、PDP
、PEP
。
OPA
是一个 PDP
,即 规定引擎
。审计方在这里用 自然语言的 DSL
形容权限管制规定,OPA
执行这些规定并返回 allow
或 deny
。晚期的权限规定是单向的,只能从需要被翻译成面向过程的代码,而很难从曾经实现的代码反推出理论被执行的规定,也很难对规定进行更新或迁徙。应用 DSL
是量变的要害,它使得需要和代码能够双向翻译了。
一、申请流向
这是下面概念图落地后的实例图,有两个比拟显著的简化点是:
- 本文实际上只是第一道网关,只做简略的准入管制,不做行、列粒度的数据权限管制。所以和
Envoy(PEP)
配套的OPA(PDP)
没有去申请额定的PIP
取得更多属性,而是仅应用Envoy
带来的属性包含JWT
。因而这里通常只会携带以后用户的id
和要害role
。如果要做细粒度的权限管制,应该在Service
处再加一套PEP + OPA(PDP)
。 OPA
要求所有的Policy
都在内存里。它提供了 restful api 用来更新内存中的 policy,这是大部分入门介绍的形式,但看上去没有实际意义。另外还提供了从数据源实时同步的形式,这个应该才是 正确的形式,不在本文中详细描述了。本文在启动时从文件中加载。
docker-compose.yml
app: | |
container_name: app | |
image: hub.docker.com/example/app:latest | |
networks: | |
- traefik | |
# 这里不须要配置 traefik 相干的 labels,因为理论对外裸露的是 envoy | |
app-envoy: | |
container_name: app-envoy | |
image: envoyproxy/envoy-alpine:v1.17.4 | |
networks: | |
- traefik | |
labels: | |
- "traefik.enable=true" | |
- "traefik.http.routers.app.rule=Host(`api.example.com`)" | |
# 省略其它必须的 traefik labels | |
volumes: | |
- "/path/to/envoy.yaml:/etc/envoy/envoy.yaml" | |
app-opa: | |
container_name: app-opa | |
image: openpolicyagent/opa:0.35.0-envoy-5-rootless | |
networks: | |
- traefik | |
volumes: | |
- "/path/to/policy.rego:/config/policy.rego" | |
command: | |
- run | |
- --log-level=debug | |
- --set=decision_logs.console=true | |
- --skip-version-check # 这里敞开向 open telemetry 上报埋点 | |
- --server | |
- --set=plugins.envoy_ext_authz_grpc.addr=:9191 | |
- --set=plugins.envoy_ext_authz_grpc.path=com/example/app/opa/allow | |
- /config/policy.rego |
几个踩坑点:
opa
应用的是内置envoy plugin
的 版本
envoy.yaml
static_resources: | |
listeners: | |
- address: | |
socket_address: | |
address: 0.0.0.0 | |
port_value: 80 | |
filter_chains: | |
- filters: | |
- name: envoy.http_connection_manager | |
typed_config: | |
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager | |
# 省略 | |
route_config: | |
# 省略 | |
http_filters: | |
- name: envoy.filters.http.ext_authz | |
typed_config: | |
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz | |
transport_api_version: V3 | |
grpc_service: | |
envoy_grpc: | |
cluster_name: app-opa | |
timeout: 0.5s | |
# 省略 | |
- name: envoy.filters.http.cors | |
- name: envoy.filters.http.router | |
typed_config: {} | |
clusters: | |
- name: app | |
connect_timeout: 0.25s | |
type: strict_dns | |
dns_lookup_family: V4_ONLY | |
lb_policy: round_robin | |
load_assignment: | |
cluster_name: app | |
endpoints: | |
- lb_endpoints: | |
- endpoint: | |
address: | |
socket_address: | |
address: app | |
port_value: 8080 | |
- name: app-opa | |
connect_timeout: 0.25s | |
type: strict_dns | |
typed_extension_protocol_options: | |
envoy.extensions.upstreams.http.v3.HttpProtocolOptions: | |
"@type": type.googleapis.com/envoy.extensions.upstreams.http.v3.HttpProtocolOptions | |
explicit_http_config: | |
http2_protocol_options: {} | |
load_assignment: | |
cluster_name: app-opa | |
endpoints: | |
- lb_endpoints: | |
- endpoint: | |
address: | |
socket_address: | |
address: app-opa | |
port_value: 9191 |
几个踩坑点:
envoy 1.17
开始默认应用v3
,所以降级上来的不少配置语法要改,文档比拟难找,没有手把手教程,但理论改好当前平滑降级- 模仿在一个
Pod
内的场景,cluster
的定义用的是在 docker 内的 name,没有去 traefik 再兜一圈进来,所以也不须要为 app 配置 traefik labels 依据 host 做路由
policy.rego
package com.example.app.opa | |
import input.attributes.request.http as http_request | |
default allow = { | |
"allowed": false, | |
"headers": {"x-envoy-opa": "deny", "content-type": "application/json"}, | |
"body": "{\"code\": -1, \"message\": \"deny\"}", | |
"http_status": 403 | |
} | |
allow = response { | |
http_request.method == "OPTIONS" | |
response := { | |
"allowed": true, | |
"headers": {"x-envoy-opa": "allow"} | |
} | |
} | |
allow = response {allowed_methods := ["GET", "POST", "PUT", "DELETE"] | |
http_request.method == allowed_methods[_] | |
http_request.headers["Authorzation"] == "Basic BasicAccessToken" | |
glob.match("/app/open-api/*", [], http_request.path) | |
response := { | |
"allowed": true, | |
"headers": {"x-envoy-opa": "allow"} | |
} | |
} |
几个踩坑点:
- 挺顺利的,
Rego
的语法值得详解一下,前面补充 - 应用的是 opa-envoy-plugin,所以有些语法不是 opa 自带的,而是 envoy-plugin 集成进来的,比方
glob.match
- 等有空钻研 opa 调实时接口更新规定,再补充一下
参考资料:
- Envoy Getting Started
- Getting Started With Rego,对于 Rego 的一些反直觉的语法,但你可能须要适应这些语法,在 DSL 里写代码的确是全新的体验
- Policy Language
- https://github.com/NewbMiao/o…
正文完