mirror of
https://github.com/alibaba/higress.git
synced 2026-06-08 20:27:31 +08:00
feat: wasm support opa (Open Policy Agent) (#760)
This commit is contained in:
134
plugins/wasm-go/extensions/opa/main.go
Normal file
134
plugins/wasm-go/extensions/opa/main.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// 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 (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/alibaba/higress/plugins/wasm-go/pkg/wrapper"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm"
|
||||
"github.com/tetratelabs/proxy-wasm-go-sdk/proxywasm/types"
|
||||
"github.com/tidwall/gjson"
|
||||
)
|
||||
|
||||
func main() {
|
||||
wrapper.SetCtx(
|
||||
"opa",
|
||||
wrapper.ParseConfigBy(parseConfig),
|
||||
wrapper.ProcessRequestHeadersBy(onHttpRequestHeaders),
|
||||
wrapper.ProcessRequestBodyBy(onHttpRequestBody),
|
||||
)
|
||||
}
|
||||
|
||||
type Metadata struct {
|
||||
Input map[string]interface{} `json:"input"`
|
||||
}
|
||||
|
||||
func parseConfig(json gjson.Result, config *OpaConfig, log wrapper.Log) error {
|
||||
policy := json.Get("policy").String()
|
||||
if strings.TrimSpace(policy) == "" {
|
||||
return errors.New("policy not allow empty")
|
||||
}
|
||||
|
||||
timeout := json.Get("timeout").String()
|
||||
if strings.TrimSpace(timeout) == "" {
|
||||
return errors.New("timeout not allow empty")
|
||||
}
|
||||
|
||||
duration, err := time.ParseDuration(timeout)
|
||||
if err != nil {
|
||||
return errors.New("timeout parse fail: " + err.Error())
|
||||
}
|
||||
|
||||
var uint32Duration uint32
|
||||
|
||||
if duration.Milliseconds() > int64(^uint32(0)) {
|
||||
} else {
|
||||
uint32Duration = uint32(duration.Milliseconds())
|
||||
}
|
||||
config.timeout = uint32Duration
|
||||
|
||||
client, err := Client(json)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
config.client = client
|
||||
config.policy = policy
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func onHttpRequestHeaders(ctx wrapper.HttpContext, config OpaConfig, log wrapper.Log) types.Action {
|
||||
return opaCall(ctx, config, nil, log)
|
||||
}
|
||||
|
||||
func onHttpRequestBody(ctx wrapper.HttpContext, config OpaConfig, body []byte, log wrapper.Log) types.Action {
|
||||
return opaCall(ctx, config, body, log)
|
||||
}
|
||||
|
||||
func opaCall(ctx wrapper.HttpContext, config OpaConfig, body []byte, log wrapper.Log) types.Action {
|
||||
request := make(map[string]interface{}, 6)
|
||||
headers, _ := proxywasm.GetHttpRequestHeaders()
|
||||
|
||||
request["method"] = ctx.Method()
|
||||
request["scheme"] = ctx.Scheme()
|
||||
request["path"] = ctx.Path()
|
||||
request["headers"] = headers
|
||||
if len(body) != 0 {
|
||||
request["body"] = body
|
||||
}
|
||||
parse, _ := url.Parse(ctx.Path())
|
||||
query, _ := url.ParseQuery(parse.RawQuery)
|
||||
request["query"] = query
|
||||
|
||||
data, _ := json.Marshal(Metadata{Input: map[string]interface{}{"request": request}})
|
||||
if err := config.client.Post(fmt.Sprintf("/v1/data/%s/allow", config.policy),
|
||||
[][2]string{{"Content-Type", "application/json"}},
|
||||
data, rspCall, config.timeout); err != nil {
|
||||
log.Errorf("client opa fail %v", err)
|
||||
return types.ActionPause
|
||||
}
|
||||
return types.ActionPause
|
||||
}
|
||||
|
||||
func rspCall(statusCode int, _ http.Header, responseBody []byte) {
|
||||
if statusCode != http.StatusOK {
|
||||
proxywasm.SendHttpResponse(uint32(statusCode), nil, []byte("opa state not is 200"), -1)
|
||||
return
|
||||
}
|
||||
var rsp map[string]interface{}
|
||||
if err := json.Unmarshal(responseBody, &rsp); err != nil {
|
||||
proxywasm.SendHttpResponse(http.StatusInternalServerError, nil, []byte(fmt.Sprintf("opa parse rsp fail %+v", err)), -1)
|
||||
return
|
||||
}
|
||||
|
||||
result, ok := rsp["result"].(bool)
|
||||
if !ok {
|
||||
proxywasm.SendHttpResponse(http.StatusInternalServerError, nil, []byte("rsp type conversion fail"), -1)
|
||||
return
|
||||
}
|
||||
|
||||
if !result {
|
||||
proxywasm.SendHttpResponse(http.StatusUnauthorized, nil, []byte("opa server not allowed"), -1)
|
||||
return
|
||||
}
|
||||
proxywasm.ResumeHttpRequest()
|
||||
}
|
||||
Reference in New Issue
Block a user