Skip to content

Commit

Permalink
feat: wasm support opa (Open Policy Agent) (alibaba#760)
Browse files Browse the repository at this point in the history
  • Loading branch information
baerwang authored Jan 30, 2024
1 parent e67ed48 commit 612c94d
Show file tree
Hide file tree
Showing 16 changed files with 717 additions and 15 deletions.
101 changes: 101 additions & 0 deletions plugins/wasm-go/extensions/opa/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# 功能说明

该插件实现了 `OPA` 策略控制

# 该教程使用k8s,[k8s配置文件](../../../../test/e2e/conformance/tests/go-wasm-opa.yaml)

支持client `k8s,nacos,ip,route` 策略去访问

## 配置字段

| 字段 | 数据类型 | 填写要求 | 默认值 | 描述 |
|---------------|--------|------|-----|--------------------------------------|
| policy | string | 必填 | - | opa 策略 |
| timeout | string | 必填 | - | 访问超时时间设置 |
| serviceSource | string | 必填 | - | k8s,nacos,ip,route |
| host | string | 非必填 | - | 服务主机(serviceSource为`ip`必填) |
| serviceName | string | 非必填 | - | 服务名称(serviceSource为`k8s,nacos,ip`必填) |
| servicePort | string | 非必填 | - | 服务端口(serviceSource为`k8s,nacos,ip`必填) |
| namespace | string | 非必填 | - | 服务端口(serviceSource为`k8s,nacos`必填) |

这是一个用于OPA认证配置的表格,确保在提供所有必要的信息时遵循上述指导。

## 配置示例

```yaml
serviceSource: k8s
serviceName: opa
servicePort: 8181
namespace: higress-backend
policy: example1
timeout: 5s
```
# 在宿主机上执行OPA的流程
## 启动opa服务
```shell
docker run -d --name opa -p 8181:8181 openpolicyagent/opa:0.35.0 run -s
```

## 创建opa策略

```shell
curl -X PUT '127.0.0.1:8181/v1/policies/example1' \
-H 'Content-Type: text/plain' \
-d 'package example1
import input.request
default allow = false
allow {
# HTTP method must GET
request.method == "GET"
}'
```

## 查询策略

```shell
curl -X POST '127.0.0.1:8181/v1/data/example1/allow' \
-H 'Content-Type: application/json' \
-d '{"input":{"request":{"method":"GET"}}}'
```

# 测试插件

## 打包 WASM 插件

> `wasm-go` 目录下把Dockerfile文件改成`PLUGIN_NAME=opa`,然后执行以下命令
```shell
docker build -t build-wasm-opa --build-arg GOPROXY=https://goproxy.cn,direct --platform=linux/amd64 .
```

## 拷贝插件

> 在当前的目录执行以下命令,将插件拷贝当前的目录
```shell
docker cp wasm-opa:/plugin.wasm .
```

## 运行插件

> 运行前修改envoy.yaml 这两个字段 `OPA_SERVER` `OPA_PORT` 替换宿主机上的IP和端口
```shell
docker compose up
```

## 使用curl测试插件

```shell
curl http://127.0.0.1:10000/get -X GET -v
```

```shell
curl http://127.0.0.1:10000/get -X POST -v
```
1 change: 1 addition & 0 deletions plugins/wasm-go/extensions/opa/VERSION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
1.0.0
82 changes: 82 additions & 0 deletions plugins/wasm-go/extensions/opa/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"errors"
"strings"

"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
"github.com/tidwall/gjson"
)

type OpaConfig struct {
policy string
timeout uint32

client wrapper.HttpClient
}

func Client(json gjson.Result) (wrapper.HttpClient, error) {
serviceSource := strings.TrimSpace(json.Get("serviceSource").String())
serviceName := strings.TrimSpace(json.Get("serviceName").String())
servicePort := json.Get("servicePort").Int()

host := strings.TrimSpace(json.Get("host").String())
if host == "" {
if serviceName == "" || servicePort == 0 {
return nil, errors.New("invalid service config")
}
}

var namespace string
if serviceSource == "k8s" || serviceSource == "nacos" {
if namespace = strings.TrimSpace(json.Get("namespace").String()); namespace == "" {
return nil, errors.New("namespace not allow empty")
}
}

switch serviceSource {
case "k8s":
return wrapper.NewClusterClient(wrapper.K8sCluster{
ServiceName: serviceName,
Namespace: namespace,
Port: servicePort,
}), nil
case "nacos":
return wrapper.NewClusterClient(wrapper.NacosCluster{
ServiceName: serviceName,
NamespaceID: namespace,
Port: servicePort,
}), nil
case "ip":
return wrapper.NewClusterClient(wrapper.StaticIpCluster{
ServiceName: serviceName,
Host: host,
Port: servicePort,
}), nil
case "dns":
return wrapper.NewClusterClient(wrapper.DnsCluster{
ServiceName: serviceName,
Port: servicePort,
Domain: json.Get("domain").String(),
}), nil
case "route":
return wrapper.NewClusterClient(wrapper.RouteCluster{
Host: host,
}), nil
}
return nil, errors.New("unknown service source: " + serviceSource)
}
50 changes: 50 additions & 0 deletions plugins/wasm-go/extensions/opa/config_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) 2022 Alibaba Group Holding Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
"testing"

"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
"github.com/stretchr/testify/assert"
"github.com/tidwall/gjson"
)

func TestConfig(t *testing.T) {
json := gjson.Result{Type: gjson.JSON, Raw: `{"serviceSource": "k8s","serviceName": "opa","servicePort": 8181,"namespace": "example1","policy": "example1","timeout": "5s"}`}
config := &OpaConfig{}
assert.NoError(t, parseConfig(json, config, wrapper.Log{}))
assert.Equal(t, config.policy, "example1")
assert.Equal(t, config.timeout, uint32(5000))
assert.NotNil(t, config.client)

type tt struct {
raw string
result bool
}

tests := []tt{
{raw: `{}`, result: false},
{raw: `{"policy": "example1","timeout": "5s"}`, result: false},
{raw: `{"serviceSource": "route","host": "example.com","policy": "example1","timeout": "5s"}`, result: true},
{raw: `{"serviceSource": "nacos","serviceName": "opa","servicePort": 8181,"policy": "example1","timeout": "5s"}`, result: false},
{raw: `{"serviceSource": "nacos","serviceName": "opa","servicePort": 8181,"namespace": "example1","policy": "example1","timeout": "5s"}`, result: true},
}

for _, test := range tests {
json = gjson.Result{Type: gjson.JSON, Raw: test.raw}
assert.Equal(t, parseConfig(json, config, wrapper.Log{}) == nil, test.result)
}
}
16 changes: 16 additions & 0 deletions plugins/wasm-go/extensions/opa/docker-compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
version: '3.7'
services:
envoy:
image: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/gateway:1.3.1
entrypoint: /usr/local/bin/envoy
command: -c /etc/envoy/envoy.yaml --component-log-level wasm:debug
networks:
- wasmtest
ports:
- "10000:10000"
volumes:
- ./envoy.yaml:/etc/envoy/envoy.yaml
- ./plugin.wasm:/etc/envoy/plugin.wasm

networks:
wasmtest: { }
69 changes: 69 additions & 0 deletions plugins/wasm-go/extensions/opa/envoy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
admin:
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 9901
static_resources:
listeners:
- name: listener_0
address:
socket_address:
protocol: TCP
address: 0.0.0.0
port_value: 10000
filter_chains:
- filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: [ "*" ]
routes:
- match:
prefix: "/"
route:
cluster: opa-server
http_filters:
- name: wasmdemo
typed_config:
"@type": type.googleapis.com/udpa.type.v1.TypedStruct
type_url: type.googleapis.com/envoy.extensions.filters.http.wasm.v3.Wasm
value:
config:
name: wasmdemo
vm_config:
runtime: envoy.wasm.runtime.v8
code:
local:
filename: /etc/envoy/plugin.wasm
configuration:
"@type": "type.googleapis.com/google.protobuf.StringValue"
value: |
{
"serviceSource": "route",
"host": "OPA_SERVER:OPA_PORT",
"policy": "example1",
"timeout": "5s"
}
- name: envoy.filters.http.router
clusters:
- name: opa-server
connect_timeout: 0.5s
type: STRICT_DNS
lb_policy: ROUND_ROBIN
dns_refresh_rate: 5s
dns_lookup_family: V4_ONLY
load_assignment:
cluster_name: opa-server
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: OPA_SERVER # opa server Host IP
port_value: OPA_PORT # opa server Host PORT
23 changes: 23 additions & 0 deletions plugins/wasm-go/extensions/opa/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
module github.com/alibaba/higress/plugins/wasm-go/extensions/opa

go 1.19

require (
github.com/alibaba/higress/plugins/wasm-go v0.0.0
github.com/stretchr/testify v1.8.4
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0
github.com/tidwall/gjson v1.14.4
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 // indirect
github.com/magefile/mage v1.14.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

replace github.com/alibaba/higress/plugins/wasm-go => ../..
24 changes: 24 additions & 0 deletions plugins/wasm-go/extensions/opa/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520 h1:IHDghbGQ2DTIXHBHxWfqCYQW1fKjyJ/I7W1pMyUDeEA=
github.com/higress-group/nottinygc v0.0.0-20231101025119-e93c4c2f8520/go.mod h1:Nz8ORLaFiLWotg6GeKlJMhv8cci8mM43uEnLA5t8iew=
github.com/magefile/mage v1.14.0 h1:6QDX3g6z1YvJ4olPhT1wksUcSa/V0a1B+pJb73fBjyo=
github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0 h1:kS7BvMKN+FiptV4pfwiNX8e3q14evxAWkhYbxt8EI1M=
github.com/tetratelabs/proxy-wasm-go-sdk v0.22.0/go.mod h1:qkW5MBz2jch2u8bS59wws65WC+Gtx3x0aPUX5JL7CXI=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
github.com/tidwall/gjson v1.14.4/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading

0 comments on commit 612c94d

Please sign in to comment.