This is an automated email from the ASF dual-hosted git repository.
baerwang pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/dubbo-go-pixiu-samples.git
The following commit(s) were added to refs/heads/main by this push:
new 8d46df6 Merge pull request #117 from nanjiek/add-the-opa-remote-test
8d46df6 is described below
commit 8d46df66203b18572d428a77ae15e2400ab7888e
Author: Sirui Huang <[email protected]>
AuthorDate: Tue Dec 23 23:07:45 2025 +0800
Merge pull request #117 from nanjiek/add-the-opa-remote-test
add the opa tests for embedded and the sammple of server-mode
---
README.md | 2 +-
README_CN.md | 2 +-
plugins/opa/README.md | 250 ++++++++++-----------
plugins/opa/README_CN.md | 234 ++++++++++---------
.../opa/{ => embedded}/docker/docker-compose.yml | 54 ++---
plugins/opa/{ => embedded}/pixiu/conf.yaml | 150 ++++++-------
plugins/opa/{ => embedded}/server/app/server.go | 5 +-
plugins/opa/embedded/test/pixiu_test.go | 92 ++++++++
plugins/opa/server-mode/docker/docker-compose.yml | 47 ++++
plugins/opa/{ => server-mode}/pixiu/conf.yaml | 142 ++++++------
plugins/opa/server-mode/remote/policy.rego | 25 +++
.../server/app/server.go} | 47 ++--
plugins/opa/server-mode/test/pixiu_test.go | 91 ++++++++
13 files changed, 696 insertions(+), 445 deletions(-)
diff --git a/README.md b/README.md
index 65d615f..39cce3c 100644
--- a/README.md
+++ b/README.md
@@ -57,7 +57,7 @@ This project includes multiple samples covering conversions
such as HTTP to Dubb
* **plugins**: Pixiu plugin examples
* `ratelimit`: Pixiu rate limiting plugin
- * `opa`: Pixiu Open Policy Agent (OPA) integration example for policy-based
access control
+ * `opa`: Pixiu Open Policy Agent (OPA) integration example for policy-based
access control (embedded Rego sample and server-mode sample)
* **seata**: Demonstrates how to configure the Seata filter to interact with
Seata TC for distributed transaction coordination
diff --git a/README_CN.md b/README_CN.md
index 39b0020..2755f12 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -51,7 +51,7 @@
- plugins:此目录包含 pixiu 的一些插件
- plugins/ratelimit:pixiu 的 ratelimit 插件
- - plugins/opa: pixiu 的 Open Policy Agent 策略控制能力集成示例
+ - plugins/opa: pixiu 的 Open Policy Agent 策略控制能力集成示例(包含嵌入式 Rego 与 Server
模式两种示例)
- seata:演示了如何配置 Seata filter 与 Seata TC 交互对分布式事务进行协调
diff --git a/plugins/opa/README.md b/plugins/opa/README.md
index 283cbee..83a0d51 100644
--- a/plugins/opa/README.md
+++ b/plugins/opa/README.md
@@ -1,128 +1,122 @@
-# This sample for OPA filter.
-
-English | [中文](README_CN.md)
-
-The OPA filter can provide out-of-the-box authorization capability to ensure
service security and stability.
-
-> The filter is based on [Open Policy
Agent](https://www.openpolicyagent.org/), see more here
[Documentation](https://www.openpolicyagent.org/docs/latest/).
-
-### Create the API:
-
-- Create a simple Http API, Reference the [Create a simple Http
API](../../dubbogo/http/README.md), then we got a path.
-
-- just test it: `curl http://localhost:8888/UserService -H "Test_header: 1"`
-
-### Define Policy
-
-- The first step, Define the policy. The OPA filter requires a Rego policy
provided inline. Example:
-
-```yaml
- policy: |
- package http.authz
-
- default allow = false
-
- allow {
- input.method == "GET"
- input.path == "/status"
- }
-```
-
-### Define Entrypoint
-
-- The second step, Define entrypoint.The entrypoint must match the package
-
-```yaml
- entrypoint: "data.http.authz.allow"
-```
-
-
-
-- The third step, Ensure filter order. **The OPA filter must be placed before
the HTTP proxy filter** in `http_filters`:
-
-```yaml
-filters:
- - name: dgp.filter.httpconnectionmanager
- config:
- route_config:
- # ... your routes
- http_filters:
- - name: dgp.filter.http.opa
- config:
- policy: |
- package http.authz
-
- default allow = false
-
- allow {
- input.method == "GET"
- input.path == "/status"
- }
- entrypoint: "data.http.authz.allow"
- # HTTP proxy filter should be after OPA filter
- - name: dgp.filter.http.proxy
- config:
-```
-
-
-
-### Test:
-
-```cmd
-go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c
/path_to/dubbo-go-pixiu-samples/plugins/opa/pixiu/conf.yaml
-```
-
-```
-go run /path_to/dubbo-go-pixiu-samples/plugins/opa/server/app/*
-```
-
-
-
-##### Go Test
-
-```
-go test -v /path_to/dubbo-go-pixiu-samples/plugins/opa/test
-```
-
-
-
-```
-=== RUN TestUserServiceAllow
---- PASS: TestUserServiceAllow (0.02s)
-=== RUN TestUserServiceDeny
---- PASS: TestUserServiceDeny (0.00s)
-=== RUN TestOtherServiceDeny
---- PASS: TestOtherServiceDeny (0.00s)
-PASS
-```
-
-
-
-###### Use Curl to Test
-
-- Denied request:
-
-```bash
-curl -s http://127.0.0.1:8888/OtherService
-```
-
- Expected result:
-
-```
-null
-```
-
-
-
-- Allowed request:
-
-```
-curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"
-```
-
- Expected result:
-
-```
-{"message":"UserService","result":"pass"}
-```
-
+# Sample for OPA filter
+
+English | [中文](README_CN.md)
+
+The OPA filter can provide out-of-the-box authorization capability to ensure
service security and stability.
+
+> The filter is based on [Open Policy
Agent](https://www.openpolicyagent.org/), see more here
[Documentation](https://www.openpolicyagent.org/docs/latest/).
+
+**Recommendation:** Use server mode (remote OPA) for production; embedded mode
is for compatibility and simple demos.
+
+### Entrypoint Reminder
+- `entrypoint` must match your policy package/rule path (e.g.,
`data.pixiu.authz.allow`).
+- Filter order: OPA must be before the HTTP proxy filter.
+
+### Embedded Mode
+
+- Backend: `go run
/path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/server/app/server.go`
+- Pixiu: `go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c
/path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/pixiu/conf.yaml`
+- Pixiu key config (inline policy):
+ ```yaml
+ http_filters:
+ - name: dgp.filter.http.opa
+ config:
+ policy: |
+ package pixiu
+ default allow := false
+ allow {
+ input.path == "/UserService"
+ input.headers["Test_header"][0] == "1"
+ }
+ entrypoint: data.pixiu.allow
+ - name: dgp.filter.http.httpproxy
+ config: {}
+ clusters:
+ - name: "user"
+ endpoints:
+ - socket_address:
+ address: 127.0.0.1
+ port: 1314
+ ```
+- Go test:
+ ```bash
+ cd /path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/test
+ go test -v .
+ # Expected:
+ # === RUN TestEmbeddedUserServiceAllow
+ # --- PASS: TestEmbeddedUserServiceAllow
+ # === RUN TestEmbeddedUserServiceDeny
+ # --- PASS: TestEmbeddedUserServiceDeny
+ # === RUN TestEmbeddedOtherServiceDeny
+ # --- PASS: TestEmbeddedOtherServiceDeny
+ # PASS
+ ```
+- Curl (through Pixiu):
+ - Deny: `curl -s http://127.0.0.1:8888/UserService` / `curl -s
http://127.0.0.1:8888/OtherService`
+ - Allow: `curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"`
+
+### Server Mode
+
+- OPA + uploader:
+ ```bash
+ cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/docker
+ docker-compose up
+ ```
+ - Host port 8182 -> container 8181, policy auto uploaded to `pixiu-authz`.
+- Backend: `cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode && go
run server/app/*.go`
+- Pixiu: `go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c
/path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/pixiu/conf.yaml`
+- Pixiu key config (remote OPA):
+ ```yaml
+ http_filters:
+ - name: dgp.filter.http.opa
+ config:
+ server_url: "http://127.0.0.1:8182"
+ decision_path: "/v1/data/pixiu/authz/allow"
+ timeout_ms: 500
+ - name: dgp.filter.http.httpproxy
+ config: {}
+ clusters:
+ - name: "user"
+ endpoints:
+ - socket_address:
+ address: 127.0.0.1
+ port: 1314
+ ```
+- OPA policy (auto uploaded by compose):
`plugins/opa/server-mode/remote/policy.rego`
+ ```rego
+ package pixiu.authz
+ default allow := false
+ allow {
+ input.path == "/UserService"
+ input.headers["Test_header"][0] == "1"
+ }
+ ```
+- Go test:
+ ```bash
+ cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/test
+ go test -v .
+ # Expected:
+ # === RUN TestServerModeUserServiceAllow
+ # --- PASS: TestServerModeUserServiceAllow
+ # === RUN TestServerModeUserServiceDeny
+ # --- PASS: TestServerModeUserServiceDeny
+ # === RUN TestServerModeOtherServiceDeny
+ # --- PASS: TestServerModeOtherServiceDeny
+ # PASS
+ ```
+- Curl (through Pixiu):
+ - Deny: `curl -s http://127.0.0.1:8888/UserService` / `curl -s
http://127.0.0.1:8888/OtherService`
+ - Allow: `curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"`
+- Curl (direct OPA decision, host port 8182):
+ - Allow:
+ ```bash
+ curl -s -X POST -H "Content-Type: application/json" \
+ -d '{"input":{"path":"/UserService","headers":{"Test_header":["1"]}}}' \
+ http://127.0.0.1:8182/v1/data/pixiu/authz/allow
+ ```
+ - Deny:
+ ```bash
+ curl -s -X POST -H "Content-Type: application/json" \
+ -d '{"input":{"path":"/UserService","headers":{}}}' \
+ http://127.0.0.1:8182/v1/data/pixiu/authz/allow
+ ```
diff --git a/plugins/opa/README_CN.md b/plugins/opa/README_CN.md
index b4739f5..48991dd 100644
--- a/plugins/opa/README_CN.md
+++ b/plugins/opa/README_CN.md
@@ -1,112 +1,122 @@
-# OPA 插件示例
-
-[English](README.md) | 中文
-
-OPA 过滤器可以提供开箱即用的授权能力,确保服务的安全性和稳定性。
-
-> 该过滤器基于 [Open Policy Agent](https://www.openpolicyagent.org/),更多内容请参阅
[官方文档](https://www.openpolicyagent.org/docs/latest/)。
-
-### 创建 API:
-
-- 创建一个简单的 Http API,参考 [创建一个简单的Http
API](../../dubbogo/http/README.md),然后我们获得了一个可访问的路径。
-
-- 测试命令: `curl http://localhost:8888/UserService`
-
-### 设置过滤器
-
-- 第一步,定义策略。OPA 过滤器需要在配置中内联 Rego 策略。例如:
-
-```yaml
- policy: |
- package http.authz
-
- default allow = false
-
- allow {
- input.method == "GET"
- input.path == "/status"
- }
- entrypoint: "data.http.authz.allow"
-```
-
-### 设置 **entrypoint**
-
-- 第二步,设置 entrypoint。entrypoint 必须同包匹配
-
-```yaml
- entrypoint: "data.http.authz.allow"
-```
-
-
-
-- 第三步,确保过滤器顺序。**OPA 过滤器必须放在 HTTP proxy 过滤器之前**,如下所示:
-
-```yaml
-filters:
- - name: dgp.filter.httpconnectionmanager
- config:
- route_config:
- # ... 你的路由
- http_filters:
- - name: dgp.filter.http.opa
- config:
- policy: |
- package http.authz
-
- default allow = false
-
- allow {
- input.method == "GET"
- input.path == "/status"
- }
- entrypoint: "data.http.authz.allow"
- # HTTP proxy 过滤器必须在 OPA 过滤器之后
- - name: dgp.filter.http.proxy
- config:
-```
-
-### 测试:
-
-```cmd
-go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c
/path_to/dubbo-go-pixiu-samples/plugins/opa/pixiu/conf.yaml
-go run /path_to/dubbo-go-pixiu-samples/plugins/opa/server/app/*
-```
-
-##### Go Test
-
-```
-go test -v /path_to/dubbo-go-pixiu-samples/plugins/opa/test
-=== RUN TestUserServiceAllow
---- PASS: TestUserServiceAllow (0.02s)
-=== RUN TestUserServiceDeny
---- PASS: TestUserServiceDeny (0.00s)
-=== RUN TestOtherServiceDeny
---- PASS: TestOtherServiceDeny (0.00s)
-PASS
-```
-
-###### 使用 Curl 测试
-
-- 拒绝请求:
-
-```bash
-curl -s http://127.0.0.1:8888/OtherService
-```
-
- 预期输出:
-
-```
-null
-```
-
-- 允许请求:
-
-```
-curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"
-```
-
- 预期输出:
-
-```
-{"message":"UserService","result":"pass"}
-```
\ No newline at end of file
+# OPA 插件示例
+
+[English](README.md) | 中文
+
+OPA 过滤器提供基于策略的鉴权能力。
+
+> 过滤器基于 [Open Policy
Agent](https://www.openpolicyagent.org/),更多见官方文档:https://www.openpolicyagent.org/docs/latest/。
+
+**推荐:** 生产环境优先使用 Server 模式(远程 OPA),嵌入模式主要用于兼容和简单演示。
+
+### Entrypoint
+- `entrypoint` 必须与策略包名/规则路径匹配(如 `data.pixiu.authz.allow`)。
+- 过滤器顺序:OPA 必须放在 HTTP proxy 过滤器之前。
+
+### 嵌入模式
+
+- 后端:`go run
/path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/server/app/server.go`
+- Pixiu:`go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c
/path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/pixiu/conf.yaml`
+- Pixiu 关键配置(内联策略):
+ ```yaml
+ http_filters:
+ - name: dgp.filter.http.opa
+ config:
+ policy: |
+ package pixiu
+ default allow := false
+ allow {
+ input.path == "/UserService"
+ input.headers["Test_header"][0] == "1"
+ }
+ entrypoint: data.pixiu.allow
+ - name: dgp.filter.http.httpproxy
+ config: {}
+ clusters:
+ - name: "user"
+ endpoints:
+ - socket_address:
+ address: 127.0.0.1
+ port: 1314
+ ```
+- Go test:
+ ```bash
+ cd /path_to/dubbo-go-pixiu-samples/plugins/opa/embedded/test
+ go test -v .
+ # 预期:
+ # === RUN TestEmbeddedUserServiceAllow
+ # --- PASS: TestEmbeddedUserServiceAllow
+ # === RUN TestEmbeddedUserServiceDeny
+ # --- PASS: TestEmbeddedUserServiceDeny
+ # === RUN TestEmbeddedOtherServiceDeny
+ # --- PASS: TestEmbeddedOtherServiceDeny
+ # PASS
+ ```
+- Curl(通过 Pixiu):
+ - 拒绝:`curl -s http://127.0.0.1:8888/UserService` / `curl -s
http://127.0.0.1:8888/OtherService`
+ - 允许:`curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"`
+
+### Server 模式
+
+- OPA + 上传器:
+ ```bash
+ cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/docker
+ docker-compose up
+ ```
+ - 宿主 8182 -> 容器 8181,`policy-uploader` 自动上传策略 `pixiu-authz`。
+- 后端:`cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode && go run
server/app/*.go`
+- Pixiu:`go run /path_to/dubbo-go-pixiu/cmd/pixiu/*.go gateway start -c
/path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/pixiu/conf.yaml`
+- Pixiu 关键配置(远程 OPA):
+ ```yaml
+ http_filters:
+ - name: dgp.filter.http.opa
+ config:
+ server_url: "http://127.0.0.1:8182"
+ decision_path: "/v1/data/pixiu/authz/allow"
+ timeout_ms: 500
+ - name: dgp.filter.http.httpproxy
+ config: {}
+ clusters:
+ - name: "user"
+ endpoints:
+ - socket_address:
+ address: 127.0.0.1
+ port: 1314
+ ```
+- OPA 策略(compose 自动上传):`plugins/opa/server-mode/remote/policy.rego`
+ ```rego
+ package pixiu.authz
+ default allow := false
+ allow {
+ input.path == "/UserService"
+ input.headers["Test_header"][0] == "1"
+ }
+ ```
+- Go test:
+ ```bash
+ cd /path_to/dubbo-go-pixiu-samples/plugins/opa/server-mode/test
+ go test -v .
+ # 预期:
+ # === RUN TestServerModeUserServiceAllow
+ # --- PASS: TestServerModeUserServiceAllow
+ # === RUN TestServerModeUserServiceDeny
+ # --- PASS: TestServerModeUserServiceDeny
+ # === RUN TestServerModeOtherServiceDeny
+ # --- PASS: TestServerModeOtherServiceDeny
+ # PASS
+ ```
+- Curl(通过 Pixiu):
+ - 拒绝:`curl -s http://127.0.0.1:8888/UserService` / `curl -s
http://127.0.0.1:8888/OtherService`
+ - 允许:`curl -s http://127.0.0.1:8888/UserService -H "Test_header: 1"`
+- Curl(直接 OPA 决策,宿主 8182):
+ - 允许:
+ ```bash
+ curl -s -X POST -H "Content-Type: application/json" \
+ -d '{"input":{"path":"/UserService","headers":{"Test_header":["1"]}}}' \
+ http://127.0.0.1:8182/v1/data/pixiu/authz/allow
+ ```
+ - 拒绝:
+ ```bash
+ curl -s -X POST -H "Content-Type: application/json" \
+ -d '{"input":{"path":"/UserService","headers":{}}}' \
+ http://127.0.0.1:8182/v1/data/pixiu/authz/allow
+ ```
diff --git a/plugins/opa/docker/docker-compose.yml
b/plugins/opa/embedded/docker/docker-compose.yml
similarity index 94%
rename from plugins/opa/docker/docker-compose.yml
rename to plugins/opa/embedded/docker/docker-compose.yml
index 6472ecb..a3d294f 100644
--- a/plugins/opa/docker/docker-compose.yml
+++ b/plugins/opa/embedded/docker/docker-compose.yml
@@ -1,27 +1,27 @@
-#
-# Licensed to Apache Software Foundation (ASF) under one or more contributor
-# license agreements. See the NOTICE file distributed with
-# this work for additional information regarding copyright
-# ownership. Apache Software Foundation (ASF) licenses this file to you 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.
-#
-
-version: '3'
-
-services:
- zookeeper:
- image: zookeeper
- ports:
- - 2181:2181
- restart: on-failure
+#
+# Licensed to Apache Software Foundation (ASF) under one or more contributor
+# license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright
+# ownership. Apache Software Foundation (ASF) licenses this file to you 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.
+#
+
+version: '3'
+
+services:
+ zookeeper:
+ image: zookeeper
+ ports:
+ - 2181:2181
+ restart: on-failure
\ No newline at end of file
diff --git a/plugins/opa/pixiu/conf.yaml b/plugins/opa/embedded/pixiu/conf.yaml
similarity index 94%
copy from plugins/opa/pixiu/conf.yaml
copy to plugins/opa/embedded/pixiu/conf.yaml
index 12f1a67..5687208 100644
--- a/plugins/opa/pixiu/conf.yaml
+++ b/plugins/opa/embedded/pixiu/conf.yaml
@@ -1,75 +1,75 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-#
----
-static_resources:
- listeners:
- - name: "net/http"
- protocol_type: "HTTP"
- address:
- socket_address:
- address: "0.0.0.0"
- port: 8888
- filter_chains:
- filters:
- - name: dgp.filter.httpconnectionmanager
- config:
- route_config:
- routes:
- - match:
- prefix: "/UserService"
- route:
- cluster: "user"
- cluster_not_found_response_code: 505
- - match:
- prefix: "/OtherService"
- route:
- cluster: "user"
- cluster_not_found_response_code: 505
- http_filters:
- - name: dgp.filter.http.opa
- config:
- policy: |
- package pixiu
- import future.keywords.if
- default allow := false
-
- allow if {
- input.path == "/UserService"
- input.headers["Test_header"][0] == "1"
- }
- entrypoint: data.pixiu.allow
- - name: dgp.filter.http.httpproxy
- config:
-
- config:
- idle_timeout: 5s
- read_timeout: 5s
- write_timeout: 5s
- clusters:
- - name: "user"
- lb_policy: "lb"
- endpoints:
- - id: 1
- socket_address:
- address: 127.0.0.1
- port: 1314
- shutdown_config:
- timeout: "60s"
- step_timeout: "10s"
- reject_policy: "immediacy"
\ No newline at end of file
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+---
+static_resources:
+ listeners:
+ - name: "net/http"
+ protocol_type: "HTTP"
+ address:
+ socket_address:
+ address: "0.0.0.0"
+ port: 8888
+ filter_chains:
+ filters:
+ - name: dgp.filter.httpconnectionmanager
+ config:
+ route_config:
+ routes:
+ - match:
+ prefix: "/UserService"
+ route:
+ cluster: "user"
+ cluster_not_found_response_code: 505
+ - match:
+ prefix: "/OtherService"
+ route:
+ cluster: "user"
+ cluster_not_found_response_code: 505
+ http_filters:
+ - name: dgp.filter.http.opa
+ config:
+ policy: |
+ package pixiu
+ import future.keywords.if
+ default allow := false
+
+ allow if {
+ input.path == "/UserService"
+ input.headers["Test_header"][0] == "1"
+ }
+ entrypoint: data.pixiu.allow
+ - name: dgp.filter.http.httpproxy
+ config: {}
+
+ config:
+ idle_timeout: 5s
+ read_timeout: 5s
+ write_timeout: 5s
+ clusters:
+ - name: "user"
+ lb_policy: "lb"
+ endpoints:
+ - id: 1
+ socket_address:
+ address: 127.0.0.1
+ port: 1314
+ shutdown_config:
+ timeout: "60s"
+ step_timeout: "10s"
+ reject_policy: "immediacy"
diff --git a/plugins/opa/server/app/server.go
b/plugins/opa/embedded/server/app/server.go
similarity index 93%
rename from plugins/opa/server/app/server.go
rename to plugins/opa/embedded/server/app/server.go
index 231becf..86f461b 100644
--- a/plugins/opa/server/app/server.go
+++ b/plugins/opa/embedded/server/app/server.go
@@ -6,7 +6,7 @@
* (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
+ * 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,
@@ -37,7 +37,6 @@ func main() {
msg := route[strings.LastIndex(route, "/")+1:]
http.HandleFunc(route, func(w http.ResponseWriter, r
*http.Request) {
-
log.Printf("[backend] %s %s Headers=%v", r.Method,
r.URL.Path, r.Header)
w.Header().Set("Content-Type", "application/json")
@@ -48,6 +47,6 @@ func main() {
})
}
- log.Println("Starting sample server on :1314 ...")
+ log.Println("Starting sample backend on :1314 ...")
log.Fatal(http.ListenAndServe(":1314", nil))
}
diff --git a/plugins/opa/embedded/test/pixiu_test.go
b/plugins/opa/embedded/test/pixiu_test.go
new file mode 100644
index 0000000..9f427b8
--- /dev/null
+++ b/plugins/opa/embedded/test/pixiu_test.go
@@ -0,0 +1,92 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 test
+
+import (
+ "io"
+ "net"
+ "net/http"
+ "strings"
+ "testing"
+ "time"
+)
+
+import (
+ "github.com/stretchr/testify/assert"
+)
+
+const embeddedGatewayBaseURL = "http://localhost:8888"
+
+func doEmbeddedRequest(t *testing.T, path string, headerVal *string) (int,
string) {
+ t.Helper()
+
+ client := &http.Client{Timeout: 5 * time.Second}
+ req, err := http.NewRequest("GET", embeddedGatewayBaseURL+path, nil)
+ if err != nil {
+ t.Fatalf("failed to build request: %v", err)
+ }
+
+ if headerVal != nil {
+ req.Header.Set("Test_header", *headerVal)
+ }
+
+ resp, err := client.Do(req)
+ if err != nil {
+ // Surface clearer guidance when gateway is not running
+ if ne, ok := err.(net.Error); ok && ne.Timeout() {
+ t.Fatalf("request timeout: ensure embedded Pixiu
gateway and backend are running: %v", err)
+ }
+ t.Fatalf("request failed: ensure embedded Pixiu gateway and
backend are running: %v", err)
+ }
+ if resp == nil {
+ t.Fatalf("response is nil")
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatalf("failed to read response body: %v", err)
+ }
+ return resp.StatusCode, string(body)
+}
+
+func TestEmbeddedUserServiceAllow(t *testing.T) {
+ header := "1"
+ status, body := doEmbeddedRequest(t, "/UserService", &header)
+
+ assert.Equal(t, http.StatusOK, status)
+ assert.True(t, strings.Contains(body, "pass"))
+ assert.True(t, strings.Contains(body, "UserService"))
+}
+
+func TestEmbeddedUserServiceDeny(t *testing.T) {
+ status, body := doEmbeddedRequest(t, "/UserService", nil)
+
+ assert.Equal(t, http.StatusForbidden, status)
+ assert.False(t, strings.Contains(body, "pass"))
+ assert.False(t, strings.Contains(body, "UserService"))
+}
+
+func TestEmbeddedOtherServiceDeny(t *testing.T) {
+ header := "1"
+ status, body := doEmbeddedRequest(t, "/OtherService", &header)
+
+ assert.Equal(t, http.StatusForbidden, status)
+ assert.False(t, strings.Contains(body, "pass"))
+ assert.False(t, strings.Contains(body, "OtherService"))
+}
diff --git a/plugins/opa/server-mode/docker/docker-compose.yml
b/plugins/opa/server-mode/docker/docker-compose.yml
new file mode 100644
index 0000000..9ff17ea
--- /dev/null
+++ b/plugins/opa/server-mode/docker/docker-compose.yml
@@ -0,0 +1,47 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+
+version: "3.8"
+
+
+services:
+ opa:
+ image: openpolicyagent/opa:latest
+ command:
+ - "run"
+ - "--server"
+ - "--addr=:8181"
+ ports:
+ - "8182:8181"
+
+ policy-uploader:
+ image: curlimages/curl:8.6.0
+ depends_on:
+ - opa
+
+ volumes:
+ - ../remote/policy.rego:/policy/policy.rego:ro
+ entrypoint: >
+ sh -c "for i in $(seq 1 5); do
+ if curl -v -X PUT -H 'Content-Type: text/plain' --data-binary
@/policy/policy.rego http://opa:8181/v1/policies/pixiu-authz; then
+ exit 0;
+ fi;
+ echo 'waiting for opa...';
+ sleep 1;
+ done;
+ echo 'upload policy failed';
+ exit 1"
\ No newline at end of file
diff --git a/plugins/opa/pixiu/conf.yaml
b/plugins/opa/server-mode/pixiu/conf.yaml
similarity index 79%
rename from plugins/opa/pixiu/conf.yaml
rename to plugins/opa/server-mode/pixiu/conf.yaml
index 12f1a67..7ddb9dc 100644
--- a/plugins/opa/pixiu/conf.yaml
+++ b/plugins/opa/server-mode/pixiu/conf.yaml
@@ -1,75 +1,67 @@
-#
-# Licensed to the Apache Software Foundation (ASF) under one
-# or more contributor license agreements. See the NOTICE file
-# distributed with this work for additional information
-# regarding copyright ownership. The ASF licenses this file
-# to you 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.
-#
----
-static_resources:
- listeners:
- - name: "net/http"
- protocol_type: "HTTP"
- address:
- socket_address:
- address: "0.0.0.0"
- port: 8888
- filter_chains:
- filters:
- - name: dgp.filter.httpconnectionmanager
- config:
- route_config:
- routes:
- - match:
- prefix: "/UserService"
- route:
- cluster: "user"
- cluster_not_found_response_code: 505
- - match:
- prefix: "/OtherService"
- route:
- cluster: "user"
- cluster_not_found_response_code: 505
- http_filters:
- - name: dgp.filter.http.opa
- config:
- policy: |
- package pixiu
- import future.keywords.if
- default allow := false
-
- allow if {
- input.path == "/UserService"
- input.headers["Test_header"][0] == "1"
- }
- entrypoint: data.pixiu.allow
- - name: dgp.filter.http.httpproxy
- config:
-
- config:
- idle_timeout: 5s
- read_timeout: 5s
- write_timeout: 5s
- clusters:
- - name: "user"
- lb_policy: "lb"
- endpoints:
- - id: 1
- socket_address:
- address: 127.0.0.1
- port: 1314
- shutdown_config:
- timeout: "60s"
- step_timeout: "10s"
- reject_policy: "immediacy"
\ No newline at end of file
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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.
+#
+---
+static_resources:
+ listeners:
+ - name: "net/http"
+ protocol_type: "HTTP"
+ address:
+ socket_address:
+ address: "0.0.0.0"
+ port: 8888
+ filter_chains:
+ filters:
+ - name: dgp.filter.httpconnectionmanager
+ config:
+ route_config:
+ routes:
+ - match:
+ prefix: "/UserService"
+ route:
+ cluster: "user"
+ cluster_not_found_response_code: 505
+ - match:
+ prefix: "/OtherService"
+ route:
+ cluster: "user"
+ cluster_not_found_response_code: 505
+ http_filters:
+ - name: dgp.filter.http.opa
+ config:
+ server_url: "http://127.0.0.1:8182"
+ decision_path: "/v1/data/pixiu/authz/allow"
+ timeout_ms: 500
+ - name: dgp.filter.http.httpproxy
+ config: {}
+ config:
+ idle_timeout: 5s
+ read_timeout: 5s
+ write_timeout: 5s
+ clusters:
+ - name: "user"
+ lb_policy: "lb"
+ endpoints:
+ - id: 1
+ socket_address:
+ address: 127.0.0.1
+ port: 1314
+ shutdown_config:
+ timeout: "60s"
+ step_timeout: "10s"
+ reject_policy: "immediacy"
diff --git a/plugins/opa/server-mode/remote/policy.rego
b/plugins/opa/server-mode/remote/policy.rego
new file mode 100644
index 0000000..f65619e
--- /dev/null
+++ b/plugins/opa/server-mode/remote/policy.rego
@@ -0,0 +1,25 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you 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 pixiu.authz
+
+default allow := false
+
+allow if {
+ input.path == "/UserService"
+ input.headers["Test_header"][0] == "1"
+}
diff --git a/plugins/opa/test/pixiu_test.go
b/plugins/opa/server-mode/server/app/server.go
similarity index 52%
rename from plugins/opa/test/pixiu_test.go
rename to plugins/opa/server-mode/server/app/server.go
index bbb591d..86f461b 100644
--- a/plugins/opa/test/pixiu_test.go
+++ b/plugins/opa/server-mode/server/app/server.go
@@ -6,7 +6,7 @@
* (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
+ * 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,
@@ -15,37 +15,38 @@
* limitations under the License.
*/
-package test
+package main
import (
- "io"
+ "encoding/json"
+ "log"
"net/http"
"strings"
- "testing"
- "time"
)
-import (
- "github.com/stretchr/testify/assert"
-)
+type Resp struct {
+ Message string `json:"message"`
+ Result string `json:"result"`
+}
-func TestUserServiceAllow(t *testing.T) {
- url := "http://localhost:8888/UserService"
- client := &http.Client{Timeout: 5 * time.Second}
+func main() {
+ routers := []string{"/UserService", "/OtherService"}
- req, err := http.NewRequest("GET", url, nil)
- assert.NoError(t, err)
+ for _, rt := range routers {
+ route := rt
+ msg := route[strings.LastIndex(route, "/")+1:]
- // Must add header to pass OPA
- req.Header.Set("Test_header", "1")
+ http.HandleFunc(route, func(w http.ResponseWriter, r
*http.Request) {
+ log.Printf("[backend] %s %s Headers=%v", r.Method,
r.URL.Path, r.Header)
- resp, err := client.Do(req)
- assert.NoError(t, err)
- assert.NotNil(t, resp)
- assert.Equal(t, 200, resp.StatusCode)
+ w.Header().Set("Content-Type", "application/json")
+ _ = json.NewEncoder(w).Encode(Resp{
+ Message: msg,
+ Result: "pass",
+ })
+ })
+ }
- body, _ := io.ReadAll(resp.Body)
- // OPA allows -> backend returns "pass" JSON
- assert.True(t, strings.Contains(string(body), "pass"))
- assert.True(t, strings.Contains(string(body), "UserService"))
+ log.Println("Starting sample backend on :1314 ...")
+ log.Fatal(http.ListenAndServe(":1314", nil))
}
diff --git a/plugins/opa/server-mode/test/pixiu_test.go
b/plugins/opa/server-mode/test/pixiu_test.go
new file mode 100644
index 0000000..bf18fce
--- /dev/null
+++ b/plugins/opa/server-mode/test/pixiu_test.go
@@ -0,0 +1,91 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 test
+
+import (
+ "io"
+ "net"
+ "net/http"
+ "strings"
+ "testing"
+ "time"
+)
+
+import (
+ "github.com/stretchr/testify/assert"
+)
+
+const serverModeGatewayBaseURL = "http://localhost:8888"
+
+func doServerModeRequest(t *testing.T, path string, headerVal *string) (int,
string) {
+ t.Helper()
+
+ client := &http.Client{Timeout: 5 * time.Second}
+ req, err := http.NewRequest("GET", serverModeGatewayBaseURL+path, nil)
+ if err != nil {
+ t.Fatalf("failed to build request: %v", err)
+ }
+
+ if headerVal != nil {
+ req.Header.Set("Test_header", *headerVal)
+ }
+
+ resp, err := client.Do(req)
+ if err != nil {
+ if ne, ok := err.(net.Error); ok && ne.Timeout() {
+ t.Fatalf("request timeout: ensure server-mode Pixiu,
OPA server, and backend are running: %v", err)
+ }
+ t.Fatalf("request failed: ensure server-mode Pixiu, OPA server,
and backend are running: %v", err)
+ }
+ if resp == nil {
+ t.Fatalf("response is nil")
+ }
+ defer resp.Body.Close()
+
+ body, err := io.ReadAll(resp.Body)
+ if err != nil {
+ t.Fatalf("failed to read response body: %v", err)
+ }
+ return resp.StatusCode, string(body)
+}
+
+func TestServerModeUserServiceAllow(t *testing.T) {
+ header := "1"
+ status, body := doServerModeRequest(t, "/UserService", &header)
+
+ assert.Equal(t, http.StatusOK, status)
+ assert.True(t, strings.Contains(body, "pass"))
+ assert.True(t, strings.Contains(body, "UserService"))
+}
+
+func TestServerModeUserServiceDeny(t *testing.T) {
+ status, body := doServerModeRequest(t, "/UserService", nil)
+
+ assert.Equal(t, http.StatusForbidden, status)
+ assert.False(t, strings.Contains(body, "pass"))
+ assert.False(t, strings.Contains(body, "UserService"))
+}
+
+func TestServerModeOtherServiceDeny(t *testing.T) {
+ header := "1"
+ status, body := doServerModeRequest(t, "/OtherService", &header)
+
+ assert.Equal(t, http.StatusForbidden, status)
+ assert.False(t, strings.Contains(body, "pass"))
+ assert.False(t, strings.Contains(body, "OtherService"))
+}