前提条件
开通实例后,系统默认生成OPA相关的ServiceEntry和opa-ext-auth-grpc。
验证opa-ext-auth-grpc
在服务网格控制台->网格安全中心->自定义授权服务。
可以看到产生了一个GRPC协议的授权服务,端口为19191。
验证ServiceEntry
在服务网格控制台->集群与工作负载->服务条目中,选定istio-system命名空间,可以看到产生了ext-authz,点击编辑可以查看详情。
详情中可以看到端口为19191端口,指向127.0.0.1。
全局放行18181和18282端口
在sidecar管理->sidecar代理配置菜单下选择命名空间tab,选择我们验证功能要用的命名空间,这里选择default,配置sidecar入流量不拦截18181和18282端口(OPA agent的配置端口和健康检查端口)。
开启OPA功能
操作步骤
- 在控制台->网格管理->OPA功能开关菜单下打开OPA开关,主要是安装OPA控制面组件。
- 给default命名空间打上标签,自动注入istio sidecar和OPA sidecar。
kubectl label namespace default opa-istio-injection="enabled"
kubectl label namespace default istio-injection="enabled"
- 部署bookinfo应用和sleep应用,可以看到pod里除了业务容器之外还有两个容器,分别是istio sidecar和OPA sidecar,OPA sidecar用于实现外部授权服务。
- 定义AuthorizationPolicy授权策略,注意引用的外部授权服务需要和上面定义的外部授权服务名称一致,可以根据业务的需要执行OPA外部授权策略,如下示例对匹配标签app: productpage的:
apiVersion: istio.ctyun.cn/v1beta1
kind: AuthorizationPolicy
metadata:
name: ext-authz
spec:
selector:
matchLabels:
app: productpage
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: opa-ext-authz-grpc
rules:
# The rules specify when to trigger the external authorizer.
- to:
- operation:
paths: ["/*"]
- 上面的配置中,我们通过workloadSelector指定对productpage应用进行访问授权,我们从sleep应用发起请求,访问productpage,此时采用默认OPA策略,请求总是被拒绝。
kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name} -n default) -c istio-proxy -n default -- curl http://productpage.default:9080/api/v1/products --user bob:password -o /dev/null -s -w '%{http_code}\n'
403
-
配置OPA策略:以下OPA策略定义了guest和admin角色,guest和admin可以使用GET方法访问/productpage路径,admin另外还可以使用GET方法访问/api/v1/products路径;外部访问时会先解析出用户名,获取用户角色,进一步获取用户访问权限,并与请求进行比对,满足条件则放过,否则拦截。
apiVersion: opacontroller.k8s.io/v1 kind: OpaPolicy metadata: name: object-policy namespace: default spec: policy: |- package istio.authz import input.attributes.request.http as http_request import input.parsed_path allow { roles_for_user[r] required_roles[r] } roles_for_user[r] { r := user_roles[user_name][_] } required_roles[r] { perm := role_perms[r][_] perm.method = http_request.method perm.path = http_request.path } user_name = parsed { [_, encoded] := split(http_request.headers.authorization, " ") [parsed, _] := split(base64url.decode(encoded), ":") } user_roles = { "alice": ["guest"], "bob": ["admin"] } role_perms = { "guest": [ {"method": "GET", "path": "/productpage"}, ], "admin": [ {"method": "GET", "path": "/productpage"}, {"method": "GET", "path": "/api/v1/products"}, ], } workloadSelector: labels: version: v1
-
验证
以bob的身份访问/api/v1/products和/productpage,由于bob是admin权限,两个路径都可以访问,返回200。
kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name} -n default) -c istio-proxy -n default -- curl http://productpage.default:9080/api/v1/products --user bob:password -o /dev/null -s -w '%{http_code}\
n'
200
kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name} -n default) -c istio-proxy -n default -- curl http://productpage.default:9080/productpage --user bob:password -o /dev/null -s -w '%{http_code}\n'
200
以alice的身份访问/api/v1/products和/productpage,由于alice是guest权限,所以对/api/v1/products的访问会被拒绝,返回403;对/productpage的访问可以通过,返回200。
kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name} -n default) -c istio-proxy -n default -- curl http://productpage.default:9080/api/v1/products --user alice:password -o /dev/null -s -w '%{http_code
}\n'
403
kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name} -n default) -c istio-proxy -n default -- curl http://productpage.default:9080/productpage --user alice:password -o /dev/null -s -w '%{http_code}\n'
200
- 修改OPA策略,给alice加上admin权限。
user_roles = {
"alice": ["guest", "admin"],
"bob": ["admin"]
}
重新验证以alice身份访问/api/v1/products,返回200。
kubectl exec $(kubectl get pod -l app=sleep -o jsonpath={.items..metadata.name} -n default) -c istio-proxy -n default -- curl http://productpage.default:9080/api/v1/products --user alice:password -o /dev/null -s -w '%{http_code}\n'
200