Compare commits

..

114 Commits

Author SHA1 Message Date
johnlanni
7697af9d2b update chart.lock 2024-11-08 14:07:12 +08:00
澄潭
3660715506 release 2.0.3 (#1494) 2024-11-08 13:59:12 +08:00
Squidward
7bd438877b add textin embedding for ai-cache (#1493) 2024-11-08 13:48:32 +08:00
Ranjana761
0fbeb39cac docs: Added back to top , contributors section and star history graph (#1440) 2024-11-08 10:18:16 +08:00
Kent Dong
d02c974af4 feat: Ensure all images are loaded to K8s before starting e2e tests (#1389) 2024-11-08 10:15:51 +08:00
澄潭
8ad4970231 update rust makefile (#1491) 2024-11-08 10:14:02 +08:00
007gzs
aee37c5e22 Implement Rust Wasm Plugin Build & Publish Action (#1483) 2024-11-07 20:20:42 +08:00
Kent Dong
73cf32aadd feat: Update istio codebase (#1485) 2024-11-07 11:40:57 +08:00
澄潭
1ab69fcf82 Update README.md 2024-11-07 11:20:25 +08:00
mamba
9b995321bb feat: 🎸 支持多版本能力:根据不同路由映射不同的Version版本。 (#1429) 2024-11-07 09:11:57 +08:00
澄潭
00cac813e3 add clusterrole for gateway api (#1480) 2024-11-06 13:37:18 +08:00
澄潭
548cf2f081 move nottinygc to proxy-wasm-go-sdk (#1477) 2024-11-05 20:59:51 +08:00
007gzs
c1f2504e87 Ai data mask deny word match optimize (#1453) 2024-11-05 15:26:55 +08:00
rinfx
7e8b0445ad modify log-format, then every plugin log is associated with access log (#1454) 2024-11-04 22:04:20 +08:00
littlejian
63d5422da6 feat:Support downstream and upstram, which can be configured through helm parameters (#1399) 2024-11-01 22:39:19 +08:00
韩贤涛
035e81a5ca fix: Control gateway-api Listener with global.enableGatewayAPI in Helm (#1461) 2024-10-31 21:36:40 +08:00
澄潭
9a1edcd4c8 Fix the issue where wasmplugin does not work when kingress exists (#1450) 2024-10-29 20:27:11 +08:00
007gzs
2219a17898 [feat] Support redis call with wasm-rust (#1417) 2024-10-29 19:35:02 +08:00
纪卓志
93c1e5c2bb feat: lazy formatted log (#1441) 2024-10-29 17:06:03 +08:00
澄潭
7c2d2b2855 fix destinationrule merge logic (#1439) 2024-10-29 09:01:06 +08:00
澄潭
b1550e91ab rel: Release v2.0.2 (#1438) 2024-10-28 19:14:57 +08:00
澄潭
0b42836e85 Update CODEOWNERS 2024-10-28 18:56:09 +08:00
tmsnan
7c33ebf6ea bugfix: remove config check and fix memory leak in traffic-tag (#1435) 2024-10-28 16:26:29 +08:00
Yang Beining
acec48ed8b [ai-cache] Implement a WASM plugin for LLM result retrieval based on vector similarity (#1290) 2024-10-27 16:21:04 +08:00
澄潭
d309bf2e25 fix model-router plugin (#1432) 2024-10-25 16:48:37 +08:00
韩贤涛
496d365a95 add PILOT_ENABLE_ALPHA_GATEWAY_API and fix gateway status update (#1421) 2024-10-24 20:57:46 +08:00
rinfx
d952fa562b bugfix: plugin will block GET request (#1428) 2024-10-24 17:34:26 +08:00
纪卓志
e7561c30e5 feat: implements text/event-stream(SSE) MIME parser (#1416)
Co-authored-by: 007gzs <007gzs@gmail.com>
2024-10-24 16:58:45 +08:00
澄潭
cdd71155a9 Update README.md 2024-10-24 11:25:59 +08:00
Ankur Singh
a5ccb90b28 Improve the grammar of the sentence (#1426) 2024-10-24 09:38:22 +08:00
007gzs
d76f574ab3 plugin ai-data-mask add log (#1423) 2024-10-23 13:19:02 +08:00
mamba
bb6c43c767 feat: 【frontend-gray】添加 skipedRoutes以及skipedByHeaders 配置 (#1409)
Co-authored-by: Kent Dong <ch3cho@qq.com>
2024-10-23 09:34:00 +08:00
fengxsong
b8f5826a32 fix: do not create ingressclass when it's empty (#1419)
Signed-off-by: fengxusong <fengxsong@outlook.com>
2024-10-23 09:03:07 +08:00
Bingkun Zhao
0d79386ce2 fix a bug of ip-restriction plugin (#1422) 2024-10-22 22:17:56 +08:00
007gzs
871ae179c3 Ai data masking fix (#1420) 2024-10-22 21:39:01 +08:00
澄潭
f8d62a8ac3 add model router plugin (#1414) 2024-10-21 16:46:18 +08:00
澄潭
badf4b7101 ai cache plugin support set skip ai cache header (#1380) 2024-10-21 15:43:01 +08:00
Ikko Eltociear Ashimine
fc6902ded2 docs: add Japanese README and CONTRIBUTING files (#1407) 2024-10-21 15:20:45 +08:00
007gzs
d96994767c Change http_content to Rc in HttpWrapper (#1391) 2024-10-21 09:44:01 +08:00
rinfx
32e5a59ae0 fix special charactor handle in ai-security-guard plugin (#1394) 2024-10-18 16:32:48 +08:00
Lisheng Zheng
49bb5ec2b9 fix: add HTTP2 protocol options to skywalking and otel cluster (#1379) 2024-10-18 15:34:34 +08:00
mamba
11ff2d1d31 [frontend-gray] support grayKey from localStorage (#1395) 2024-10-18 13:58:52 +08:00
澄潭
c67f494b49 Update README.md 2024-10-18 09:54:10 +08:00
澄潭
299621476f Update README.md 2024-10-17 14:33:08 +08:00
澄潭
7e6168a644 Update README.md 2024-10-17 14:31:26 +08:00
Smoothengineer
e923cbaecc Update README_EN.md (#1402) 2024-10-17 12:53:04 +08:00
Kent Dong
6f86c31bac feat: Update submodules: envoy/envoy, istio/isitio (#1398) 2024-10-16 19:00:18 +08:00
Kent Dong
51c956f0b3 fix: Fix clean targets in Makefile (#1397) 2024-10-16 18:42:49 +08:00
澄潭
d0693d8c4b Update SECURITY.md 2024-10-16 11:17:44 +08:00
Jun
e298078065 add dns&static registry e2e test (#1393) 2024-10-16 10:21:03 +08:00
澄潭
85f8eb5166 key-auth consumer support set independent key source (#1392) 2024-10-15 20:52:03 +08:00
澄潭
0a112d1a1e fix mcp service port protocol name (#1383) 2024-10-15 11:50:43 +08:00
Kent Dong
04ce776f14 feat: Support route fallback by default (#1381) 2024-10-14 18:50:45 +08:00
rinfx
952c9ec5dc Ai proxy support coze (#1387) 2024-10-14 12:45:53 +08:00
澄潭
1a53c7b4d3 fix mcpbridge endpoint port (#1382) 2024-10-11 11:39:46 +08:00
澄潭
ae6dab919d fix istio ns name (#1378) 2024-10-10 16:07:57 +08:00
澄潭
601b205abc Update Makefile.core.mk 2024-10-10 15:31:48 +08:00
澄潭
9972e7611a rel: Release 2.0.1 (#1375) 2024-10-09 20:10:00 +08:00
澄潭
c30ca5dd9e fix static cluster of skywalking service (#1372) 2024-10-09 20:09:48 +08:00
lixf311
e26a2a37d7 feat: add api-workflow plugin (#1229) 2024-10-09 19:52:16 +08:00
Kent Dong
f20c48e960 fix: Update the envoy.yaml template used by hgctl (#1370) 2024-10-09 18:00:44 +08:00
007gzs
e126f3a888 Rust wrappers (#1367) 2024-10-09 17:58:43 +08:00
韩贤涛
93317adbc7 feat: Support status sync for Gateway API resources (#1315) 2024-10-09 17:22:31 +08:00
澄潭
ecf52aecfc Supports MCP service configuration protocol and SNI, along with various other fixes. (#1369) 2024-10-09 15:54:19 +08:00
Iris
3ed28f2a66 fix: when there is a non-ip IPAddr in Eureka, delete it to avoid a failure in EDS (#1322) 2024-10-08 14:00:16 +08:00
mamba
4d0d8a7f50 fix: 🐛 [frontend-gray] 修复 请求非首页资源时候,路由配置 (#1353) 2024-10-08 13:15:58 +08:00
Kent Dong
1f8d50c0b1 feat: Update the latest tag when building a new plugin image (#1354) 2024-10-08 10:42:42 +08:00
YeHaitao
14b11dcb05 feat: AI Proxy Wasm Plugin Integration with GitHub Models #1304 (#1362) 2024-10-06 17:12:48 +08:00
Kent Dong
71aae9ddf6 fix: Fix the quotation issue of deny message in ai-security-guard (#1352) 2024-09-27 18:45:51 +08:00
rinfx
1b119ed371 add default deny message (#1347)
Co-authored-by: Kent Dong <ch3cho@qq.com>
2024-09-27 13:25:50 +08:00
Hazel0928
ea99159d51 feat: support frontend-gray plugin's envoy.yaml file to host HTML (#1343)
Co-authored-by: Kent Dong <ch3cho@qq.com>
2024-09-26 22:38:33 +08:00
Jun
567d7c25f3 add buildrc (#1348) 2024-09-26 21:39:45 +08:00
Kent Dong
708e7af79a feat: Support configuring a global provider list in ai-proxy plugin (#1334) 2024-09-26 11:27:22 +08:00
Benny
260772926c Standardize the data structure returned by the AI security security a… (#1344)
Co-authored-by: Kent Dong <ch3cho@qq.com>
2024-09-26 11:07:44 +08:00
mamba
af4e34b7ed chore: 🤖 [frontend-gray]优化关于处理index page 处理逻辑 (#1345)
Co-authored-by: Kent Dong <ch3cho@qq.com>
2024-09-26 09:16:00 +08:00
澄潭
8293042c25 Update README_EN.md 2024-09-25 10:55:01 +08:00
澄潭
1acaaea222 Update README.md 2024-09-25 10:54:37 +08:00
rinfx
e004321cb0 Update ai security guard (#1261) 2024-09-24 19:42:34 +08:00
rinfx
b82853c653 Update ai statistics (#1303) 2024-09-24 19:42:10 +08:00
rinfx
bef9139753 Ai proxy support doubao (#1337) 2024-09-24 18:45:40 +08:00
澄潭
dc61bfc5c5 add istio workload sds (#1332) 2024-09-24 17:26:25 +08:00
澄潭
b24731593f Update README.md 2024-09-23 20:26:25 +08:00
澄潭
e7761a2ecc Update README.md 2024-09-23 20:24:55 +08:00
fengxsong
86239c4a4b feat: create podmonitor cr in helm chart (#1157)
Signed-off-by: fengxusong <fengxsong@outlook.com>
2024-09-23 14:54:06 +08:00
brother-戎
c923e5cb42 feat: add annotation for mirror svc (#1121) 2024-09-23 13:53:08 +08:00
mamba
ee67553816 [frontend-gray] Increase gray types according to the ratio-weight gray (#1291) 2024-09-22 16:49:54 +08:00
jk-tonycui
ffc5458a91 support ai proxy for cohere (#960) (#1328) 2024-09-20 21:15:14 +08:00
澄潭
55f6ed7953 Update README.md
fix typo
2024-09-20 11:40:20 +08:00
澄潭
9e5188cfca ext auth plugin support set service host (#1320)
Co-authored-by: Kent Dong <ch3cho@qq.com>
2024-09-18 18:38:53 +08:00
rinfx
f51408d7ff add bailian support (#1319)
Co-authored-by: Kent Dong <ch3cho@qq.com>
2024-09-18 16:35:13 +08:00
xingyunyang01
0471249e7f ai-agent插件新版本 (#1311)
Co-authored-by: Kent Dong <ch3cho@qq.com>
2024-09-18 10:52:23 +08:00
Kent Dong
59fe661cd2 feat: Add links to Higress related repositories to README files (#1312) 2024-09-14 16:22:43 +08:00
澄潭
7610c9f504 update ai data masking doc (#1310) 2024-09-13 15:50:04 +08:00
澄潭
452bd4ef2d Fix hgctl release (#1308) 2024-09-13 11:08:47 +08:00
澄潭
1db37f988a update console version (#1307) 2024-09-13 10:47:18 +08:00
澄潭
c7c4ae1da2 update plugins doc (#1305) 2024-09-12 21:48:40 +08:00
dotdotdot
0f9113ed82 Update Helm (#982) 2024-09-12 15:18:26 +08:00
澄潭
3eda1f3c70 rel: Release v2.0.0 (#1298) 2024-09-12 15:03:22 +08:00
co63oc
6439b2d4da Fix typos (#1300) 2024-09-11 17:19:23 +08:00
澄潭
a4e696b957 Add traffic tag plugin (#1296) 2024-09-11 11:44:37 +08:00
澄潭
f471c0a99f rename wasm proto file name (#1294) 2024-09-10 20:13:01 +08:00
jaymie9019
ffe3aceefc fix: Wasm-rust plugin crashed when header name or value is not a valid UTF8 string (#1295)
Co-authored-by: jaymie9019 <jaymie9019@gmail.com>
2024-09-10 20:00:26 +08:00
澄潭
d18e22432a support raw url argument in wasm-go httpcall (#1293) 2024-09-10 10:17:03 +08:00
澄潭
d682e05c75 fix proxy wasm cpp host (#1287) 2024-09-05 22:40:50 +08:00
澄潭
a382bb201c some optimize&fix of istio and envoy (#1286) 2024-09-05 20:57:12 +08:00
澄潭
6777d29c6e Update README.md 2024-09-05 11:51:25 +08:00
澄潭
93fd1a5772 Update README.md 2024-09-05 09:38:34 +08:00
澄潭
d6a67c05e6 Update README.md 2024-09-05 09:36:47 +08:00
Kent Dong
28d5145514 feat: Update istio submodule to the latest version (#1283) 2024-09-04 21:24:36 +08:00
澄潭
1ab8025c96 Update CODEOWNERS 2024-09-04 16:50:32 +08:00
Jingze
ea862cfd4c fix: update README.md and support semicolon in scope (#1279) 2024-09-04 15:05:12 +08:00
澄潭
f03ce572be Update Dockerfile 2024-09-04 11:37:32 +08:00
Kent Dong
aefa3b94c4 feat: Upgrade Wasm go plugin builder image (#1273) 2024-09-03 19:47:39 +08:00
Kent Dong
b7a30669a1 fix: Replace Docker Hub with AliCloud ACR (#1278) 2024-09-03 19:31:58 +08:00
301 changed files with 22967 additions and 5359 deletions

View File

@@ -3,9 +3,16 @@ name: Build and Push Wasm Plugin Image
on:
push:
tags:
- "wasm-go-*-v*.*.*" # 匹配 wasm-go-{pluginName}-vX.Y.Z 格式的标签
- "wasm-*-*-v*.*.*" # 匹配 wasm-{go|rust}-{pluginName}-vX.Y.Z 格式的标签
workflow_dispatch:
inputs:
plugin_type:
description: 'Type of the plugin'
required: true
type: choice
options:
- go
- rust
plugin_name:
description: 'Name of the plugin'
required: true
@@ -23,32 +30,40 @@ jobs:
env:
IMAGE_REGISTRY_SERVICE: ${{ vars.IMAGE_REGISTRY || 'higress-registry.cn-hangzhou.cr.aliyuncs.com' }}
IMAGE_REPOSITORY: ${{ vars.PLUGIN_IMAGE_REPOSITORY || 'plugins' }}
RUST_VERSION: 1.82
GO_VERSION: 1.19
TINYGO_VERSION: 0.28.1
ORAS_VERSION: 1.0.0
steps:
- name: Set plugin_name and version from inputs or ref_name
- name: Set plugin_type, plugin_name and version from inputs or ref_name
id: set_vars
run: |
if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
plugin_type="${{ github.event.inputs.plugin_type }}"
plugin_name="${{ github.event.inputs.plugin_name }}"
version="${{ github.event.inputs.version }}"
builder_image="higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-rust-builder:rust${{ env.RUST_VERSION }}-oras${{ env.ORAS_VERSION }}"
else
ref_name=${{ github.ref_name }}
plugin_type=${ref_name#*-} # 删除插件类型前面的字段(wasm-)
plugin_type=${plugin_type%-*} # 删除插件类型后面的字段(-{plugin_name}-vX.Y.Z)
plugin_name=${ref_name#*-*-} # 删除插件名前面的字段(wasm-go-)
plugin_name=${plugin_name%-*} # 删除插件名后面的字段(-vX.Y.Z)
version=$(echo "$ref_name" | awk -F'v' '{print $2}')
builder_image="higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go${{ env.GO_VERSION }}-tinygo${{ env.TINYGO_VERSION }}-oras${{ env.ORAS_VERSION }}"
fi
echo "PLUGIN_TYPE=$plugin_type" >> $GITHUB_ENV
echo "PLUGIN_NAME=$plugin_name" >> $GITHUB_ENV
echo "VERSION=$version" >> $GITHUB_ENV
echo "BUILDER_IMAGE=$builder_image" >> $GITHUB_ENV
- name: Checkout code
uses: actions/checkout@v3
- name: File Check
run: |
workspace=${{ github.workspace }}/plugins/wasm-go/extensions/${PLUGIN_NAME}
workspace=${{ github.workspace }}/plugins/wasm-${PLUGIN_TYPE}/extensions/${PLUGIN_NAME}
push_command="./plugin.tar.gz:application/vnd.oci.image.layer.v1.tar+gzip"
# 查找spec.yaml
@@ -75,10 +90,10 @@ jobs:
echo "PUSH_COMMAND=\"$push_command\"" >> $GITHUB_ENV
- name: Run a wasm-go-builder
- name: Run a wasm-builder
env:
PLUGIN_NAME: ${{ env.PLUGIN_NAME }}
BUILDER_IMAGE: higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go${{ env.GO_VERSION }}-tinygo${{ env.TINYGO_VERSION }}-oras${{ env.ORAS_VERSION }}
BUILDER_IMAGE: ${{ env.BUILDER_IMAGE }}
run: |
docker run -itd --name builder -v ${{ github.workspace }}:/workspace -e PLUGIN_NAME=${{ env.PLUGIN_NAME }} --rm ${{ env.BUILDER_IMAGE }} /bin/bash
@@ -89,9 +104,11 @@ jobs:
push_command=${push_command%\"} # 删除PUSH_COMMAND中的双引号确保oras push正常解析
target_image="${{ env.IMAGE_REGISTRY_SERVICE }}/${{ env.IMAGE_REPOSITORY}}/${{ env.PLUGIN_NAME }}:${{ env.VERSION }}"
target_image_latest="${{ env.IMAGE_REGISTRY_SERVICE }}/${{ env.IMAGE_REPOSITORY}}/${{ env.PLUGIN_NAME }}:latest"
echo "TargetImage=${target_image}"
echo "TargetImageLatest=${target_image_latest}"
cd ${{ github.workspace }}/plugins/wasm-go/extensions/${PLUGIN_NAME}
cd ${{ github.workspace }}/plugins/wasm-${PLUGIN_TYPE}/extensions/${PLUGIN_NAME}
if [ -f ./.buildrc ]; then
echo 'Found .buildrc file, sourcing it...'
. ./.buildrc
@@ -99,7 +116,7 @@ jobs:
echo '.buildrc file not found'
fi
echo "EXTRA_TAGS=${EXTRA_TAGS}"
if [ "${PLUGIN_TYPE}" == "go" ]; then
command="
set -e
cd /workspace/plugins/wasm-go/extensions/${PLUGIN_NAME}
@@ -108,7 +125,23 @@ jobs:
tar czvf plugin.tar.gz plugin.wasm
echo ${{ secrets.REGISTRY_PASSWORD }} | oras login -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin ${{ env.IMAGE_REGISTRY_SERVICE }}
oras push ${target_image} ${push_command}
oras push ${target_image_latest} ${push_command}
"
docker exec builder bash -c "$command"
elif [ "${PLUGIN_TYPE}" == "rust" ]; then
command="
set -e
cd /workspace/plugins/wasm-rust/extensions/${PLUGIN_NAME}
cargo build --target wasm32-wasi --release
cp target/wasm32-wasi/release/*.wasm plugin.wasm
tar czvf plugin.tar.gz plugin.wasm
echo ${{ secrets.REGISTRY_PASSWORD }} | oras login -u ${{ secrets.REGISTRY_USERNAME }} --password-stdin ${{ env.IMAGE_REGISTRY_SERVICE }}
oras push ${target_image} ${push_command}
oras push ${target_image_latest} ${push_command}
"
else
command="
echo "unkown type ${PLUGIN_TYPE}"
"
fi
docker exec builder bash -c "$command"

View File

@@ -64,14 +64,6 @@ jobs:
restore-keys: |
${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v4
with:
path: |-
.git/modules
key: ${{ runner.os }}-submodules-cache-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules-cache
- run: git stash # restore patch
- name: "Run Ingress WasmPlugins Tests"

View File

@@ -37,14 +37,6 @@ jobs:
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: ${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v4
with:
path: |-
.git/modules
key: ${{ runner.os }}-submodules-cache-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules-cache
- run: git stash # restore patch
# test
@@ -83,14 +75,6 @@ jobs:
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: ${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v4
with:
path: |-
.git/modules
key: ${{ runner.os }}-submodules-cache-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules-cache
- run: git stash # restore patch
- name: "Build Higress Binary"
@@ -137,14 +121,6 @@ jobs:
~/go/pkg/mod
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: ${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v4
with:
path: |-
.git/modules
key: ${{ runner.os }}-submodules-cache-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules-cache
- run: git stash # restore patch

View File

@@ -44,16 +44,6 @@ jobs:
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: ${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v4
with:
path: |-
envoy
istio
.git/modules
key: ${{ runner.os }}-submodules-new-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules-new
- name: Calculate Docker metadata
id: docker-meta
uses: docker/metadata-action@v5
@@ -120,16 +110,6 @@ jobs:
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: ${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v4
with:
path: |-
envoy
istio
.git/modules
key: ${{ runner.os }}-submodules-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules-new
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
@@ -208,16 +188,6 @@ jobs:
key: ${{ runner.os }}-go-${{ github.run_id }}
restore-keys: ${{ runner.os }}-go
- name: Setup Submodule Caches
uses: actions/cache@v4
with:
path: |-
envoy
istio
.git/modules
key: ${{ runner.os }}-submodules-${{ github.run_id }}
restore-keys: ${{ runner.os }}-submodules-new
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

24
.github/workflows/release-crd.yaml vendored Normal file
View File

@@ -0,0 +1,24 @@
name: Release CRD to GitHub
on:
push:
tags:
- "v*.*.*"
workflow_dispatch: ~
jobs:
release-crd:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: generate crds
run: |
cat helm/core/crds/customresourcedefinitions.gen.yaml helm/core/crds/istio-envoyfilter.yaml > crd.yaml
- name: Upload hgctl packages to the GitHub release
uses: softprops/action-gh-release@v2
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
crd.yaml

View File

@@ -3,6 +3,7 @@
/istio @SpecialYang @johnlanni
/pkg @SpecialYang @johnlanni @CH3CHO
/plugins @johnlanni @WeixinX @CH3CHO
/plugins/wasm-rust @007gzs @jizhuozhi
/registry @NameHaibinZhang @2456868764 @johnlanni
/test @Xunzhuo @2456868764 @CH3CHO
/tools @johnlanni @Xunzhuo @2456868764

View File

@@ -1,6 +1,6 @@
# Contributing to Higress
It is warmly welcomed if you have interest to hack on Higress. First, we encourage this kind of willing very much. And here is a list of contributing guide for you.
Your interest in contributing to Higress is warmly welcomed. First, we encourage this kind of willing very much. And here is a list of contributing guide for you.
[[中文贡献文档](./CONTRIBUTING_CN.md)]

195
CONTRIBUTING_JP.md Normal file
View File

@@ -0,0 +1,195 @@
# Higress への貢献
Higress のハッキングに興味がある場合は、温かく歓迎します。まず、このような意欲を非常に奨励します。そして、以下は貢献ガイドのリストです。
[[中文](./CONTRIBUTING.md)] | [[English Contributing Document](./CONTRIBUTING_EN.md)]
## トピック
- [Higress への貢献](#higress-への貢献)
- [トピック](#トピック)
- [セキュリティ問題の報告](#セキュリティ問題の報告)
- [一般的な問題の報告](#一般的な問題の報告)
- [コードとドキュメントの貢献](#コードとドキュメントの貢献)
- [ワークスペースの準備](#ワークスペースの準備)
- [ブランチの定義](#ブランチの定義)
- [コミットルール](#コミットルール)
- [コミットメッセージ](#コミットメッセージ)
- [コミット内容](#コミット内容)
- [PR 説明](#pr-説明)
- [テストケースの貢献](#テストケースの貢献)
- [何かを手伝うための参加](#何かを手伝うための参加)
- [コードスタイル](#コードスタイル)
## セキュリティ問題の報告
セキュリティ問題は常に真剣に扱われます。通常の原則として、セキュリティ問題を広めることは推奨しません。Higress のセキュリティ問題を発見した場合は、公開で議論せず、公開の問題を開かないでください。代わりに、[higress@googlegroups.com](mailto:higress@googlegroups.com) にプライベートなメールを送信して報告することをお勧めします。
## 一般的な問題の報告
正直なところ、Higress のすべてのユーザーを非常に親切な貢献者と見なしています。Higress を体験した後、プロジェクトに対するフィードバックがあるかもしれません。その場合は、[NEW ISSUE](https://github.com/alibaba/higress/issues/new/choose) を通じて問題を開くことを自由に行ってください。
Higress プロジェクトを分散型で協力しているため、**よく書かれた**、**詳細な**、**明確な**問題報告を高く評価します。コミュニケーションをより効率的にするために、問題が検索リストに存在するかどうかを検索することを希望します。存在する場合は、新しい問題を開くのではなく、既存の問題のコメントに詳細を追加してください。
問題の詳細をできるだけ標準化するために、問題報告者のために [ISSUE TEMPLATE](./.github/ISSUE_TEMPLATE) を設定しました。テンプレートのフィールドに従って指示に従って記入してください。
問題を開く場合は多くのケースがあります:
* バグ報告
* 機能要求
* パフォーマンス問題
* 機能提案
* 機能設計
* 助けが必要
* ドキュメントが不完全
* テストの改善
* プロジェクトに関する質問
* その他
また、新しい問題を記入する際には、投稿から機密データを削除することを忘れないでください。機密データには、パスワード、秘密鍵、ネットワークの場所、プライベートなビジネスデータなどが含まれる可能性があります。
## コードとドキュメントの貢献
Higress プロジェクトをより良くするためのすべての行動が奨励されます。GitHub では、Higress のすべての改善は PRプルリクエストの略を通じて行うことができます。
* タイプミスを見つけた場合は、修正してみてください!
* バグを見つけた場合は、修正してみてください!
* 冗長なコードを見つけた場合は、削除してみてください!
* 欠落しているテストケースを見つけた場合は、追加してみてください!
* 機能を強化できる場合は、**ためらわないでください**
* コードが不明瞭な場合は、コメントを追加して明確にしてください!
* コードが醜い場合は、リファクタリングしてみてください!
* ドキュメントの改善に役立つ場合は、さらに良いです!
* ドキュメントが不正確な場合は、修正してください!
* ...
実際には、それらを完全にリストすることは不可能です。1つの原則を覚えておいてください
> あなたからの PR を楽しみにしています。
Higress を PR で改善する準備ができたら、ここで PR ルールを確認することをお勧めします。
* [ワークスペースの準備](#ワークスペースの準備)
* [ブランチの定義](#ブランチの定義)
* [コミットルール](#コミットルール)
* [PR 説明](#pr-説明)
### ワークスペースの準備
PR を提出するために、GitHub ID に登録していることを前提とします。その後、以下の手順で準備を完了できます:
1. Higress を自分のリポジトリに **FORK** します。この作業を行うには、[alibaba/higress](https://github.com/alibaba/higress) のメインページの右上にある Fork ボタンをクリックするだけです。その後、`https://github.com/<your-username>/higress` に自分のリポジトリが作成されます。ここで、`your-username` はあなたの GitHub ユーザー名です。
2. 自分のリポジトリをローカルに **CLONE** します。`git clone git@github.com:<your-username>/higress.git` を使用してリポジトリをローカルマシンにクローンします。その後、新しいブランチを作成して、行いたい変更を完了できます。
3. リモートを `git@github.com:alibaba/higress.git` に設定します。以下の2つのコマンドを使用します
```bash
git remote add upstream git@github.com:alibaba/higress.git
git remote set-url --push upstream no-pushing
```
このリモート設定を使用すると、git リモート設定を次のように確認できます:
```shell
$ git remote -v
origin git@github.com:<your-username>/higress.git (fetch)
origin git@github.com:<your-username>/higress.git (push)
upstream git@github.com:alibaba/higress.git (fetch)
upstream no-pushing (push)
```
これを追加すると、ローカルブランチを上流ブランチと簡単に同期できます。
### ブランチの定義
現在、プルリクエストを通じたすべての貢献は Higress の [main ブランチ](https://github.com/alibaba/higress/tree/main) に対するものであると仮定します。貢献する前に、ブランチの定義を理解することは非常に役立ちます。
貢献者として、プルリクエストを通じたすべての貢献は main ブランチに対するものであることを再度覚えておいてください。Higress プロジェクトには、リリースブランチ0.6.0、0.6.1)、機能ブランチ、ホットフィックスブランチなど、いくつかの他のブランチがあります。
正式にバージョンをリリースする際には、リリースブランチが作成され、バージョン番号で命名されます。
リリース後、リリースブランチのコミットを main ブランチにマージします。
特定のバージョンにバグがある場合、後のバージョンで修正するか、特定のホットフィックスバージョンで修正するかを決定します。ホットフィックスバージョンで修正することを決定した場合、対応するリリースブランチに基づいてホットフィックスブランチをチェックアウトし、コード修正と検証を行い、main ブランチにマージします。
大きな機能については、開発と検証のために機能ブランチを引き出します。
### コミットルール
実際には、Higress ではコミット時に2つのルールを真剣に考えています
* [コミットメッセージ](#コミットメッセージ)
* [コミット内容](#コミット内容)
#### コミットメッセージ
コミットメッセージは、提出された PR の目的をレビュアーがよりよく理解するのに役立ちます。また、コードレビューの手続きを加速するのにも役立ちます。貢献者には、曖昧なメッセージではなく、**明確な**コミットメッセージを使用することを奨励します。一般的に、以下のコミットメッセージタイプを推奨します:
* docs: xxxx. 例:"docs: add docs about Higress cluster installation".
* feature: xxxx. 例:"feature: use higress config instead of istio config".
* bugfix: xxxx. 例:"bugfix: fix panic when input nil parameter".
* refactor: xxxx. 例:"refactor: simplify to make codes more readable".
* test: xxx. 例:"test: add unit test case for func InsertIntoArray".
* その他の読みやすく明確な表現方法。
一方で、以下のような方法でのコミットメッセージは推奨しません:
* ~~バグ修正~~
* ~~更新~~
* ~~ドキュメント追加~~
迷った場合は、[Git コミットメッセージの書き方](http://chris.beams.io/posts/git-commit/) を参照してください。
#### コミット内容
コミット内容は、1つのコミットに含まれるすべての内容の変更を表します。1つのコミットに、他のコミットの助けを借りずにレビュアーが完全にレビューできる内容を含めるのが最善です。言い換えれば、1つのコミットの内容は CI を通過でき、コードの混乱を避けることができます。簡単に言えば、次の3つの小さなルールを覚えておく必要があります
* コミットで非常に大きな変更を避ける;
* 各コミットが完全でレビュー可能であること。
* コミット時に git config`user.name``user.email`)を確認して、それが GitHub ID に関連付けられていることを確認します。
```bash
git config --get user.name
git config --get user.email
```
* pr を提出する際には、'changes/' フォルダーの下の XXX.md ファイルに現在の変更の簡単な説明を追加してください。
さらに、コード変更部分では、すべての貢献者が Higress の [コードスタイル](#コードスタイル) を読むことをお勧めします。
コミットメッセージやコミット内容に関係なく、コードレビューに重点を置いています。
### PR 説明
PR は Higress プロジェクトファイルを変更する唯一の方法です。レビュアーが目的をよりよく理解できるようにするために、PR 説明は詳細すぎることはありません。貢献者には、[PR テンプレート](./.github/PULL_REQUEST_TEMPLATE.md) に従ってプルリクエストを完了することを奨励します。
### 開発前の準備
```shell
make prebuild && go mod tidy
```
## テストケースの貢献
テストケースは歓迎されます。現在、Higress の機能テストケースが高優先度です。
* 単体テストの場合、同じモジュールの test ディレクトリに xxxTest.go という名前のテストファイルを作成する必要があります。
* 統合テストの場合、統合テストを test ディレクトリに配置できます。
//TBD
## 何かを手伝うための参加
GitHub を Higress の協力の主要な場所として選択しました。したがって、Higress の最新の更新は常にここにあります。PR を通じた貢献は明確な助けの方法ですが、他の方法も呼びかけています。
* 可能であれば、他の人の質問に返信する;
* 他のユーザーの問題を解決するのを手伝う;
* 他の人の PR 設計をレビューするのを手伝う;
* 他の人の PR のコードをレビューするのを手伝う;
* Higress について議論して、物事を明確にする;
* GitHub 以外で Higress 技術を宣伝する;
* Higress に関するブログを書くなど。
## コードスタイル
//TBD
要するに、**どんな助けも貢献です。**

View File

@@ -32,7 +32,7 @@ export BUILD_WITH_CONTAINER ?= 0
ifeq ($(BUILD_WITH_CONTAINER),1)
# An export free of arugments in a Makefile places all variables in the Makefile into the
# An export free of arguments in a Makefile places all variables in the Makefile into the
# environment. This is needed to allow overrides from Makefile.overrides.mk.
export
@@ -60,7 +60,7 @@ else
$(shell mkdir -p out)
$(shell $(shell pwd)/tools/hack/setup_env.sh envfile > out/.env)
include out/.env
# An export free of arugments in a Makefile places all variables in the Makefile into the
# An export free of arguments in a Makefile places all variables in the Makefile into the
# environment. This behavior may be surprising to many that use shell often, which simply
# displays the existing environment
export

View File

@@ -25,7 +25,7 @@ TARGET_ARCH ?= amd64
GOARCH_LOCAL := $(TARGET_ARCH)
GOOS_LOCAL := $(TARGET_OS)
RELEASE_LDFLAGS='$(GO_LDFLAGS) -extldflags -static -s -w -X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn'
RELEASE_LDFLAGS='$(GO_LDFLAGS) -extldflags -static -s -w'
export OUT:=$(TARGET_OUT)
export OUT_LINUX:=$(TARGET_OUT_LINUX)
@@ -68,21 +68,21 @@ default: build
.PHONY: go.test.coverage
go.test.coverage: prebuild
go test -ldflags "-X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn" ./cmd/... ./pkg/... -race -coverprofile=coverage.xml -covermode=atomic
go test ./cmd/... ./pkg/... -race -coverprofile=coverage.xml -covermode=atomic
.PHONY: build
build: prebuild $(OUT)
GOPROXY=$(GOPROXY) GOOS=$(GOOS_LOCAL) GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh $(OUT)/ $(HIGRESS_BINARIES)
GOPROXY="$(GOPROXY)" GOOS=$(GOOS_LOCAL) GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh $(OUT)/ $(HIGRESS_BINARIES)
.PHONY: build-linux
build-linux: prebuild $(OUT)
GOPROXY=$(GOPROXY) GOOS=linux GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh $(OUT_LINUX)/ $(HIGRESS_BINARIES)
GOPROXY="$(GOPROXY)" GOOS=linux GOARCH=$(GOARCH_LOCAL) LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh $(OUT_LINUX)/ $(HIGRESS_BINARIES)
$(AMD64_OUT_LINUX)/higress:
GOPROXY=$(GOPROXY) GOOS=linux GOARCH=amd64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/linux_amd64/ $(HIGRESS_BINARIES)
GOPROXY="$(GOPROXY)" GOOS=linux GOARCH=amd64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/linux_amd64/ $(HIGRESS_BINARIES)
$(ARM64_OUT_LINUX)/higress:
GOPROXY=$(GOPROXY) GOOS=linux GOARCH=arm64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/linux_arm64/ $(HIGRESS_BINARIES)
GOPROXY="$(GOPROXY)" GOOS=linux GOARCH=arm64 LDFLAGS=$(RELEASE_LDFLAGS) tools/hack/gobuild.sh ./out/linux_arm64/ $(HIGRESS_BINARIES)
.PHONY: build-hgctl
build-hgctl: prebuild $(OUT)
@@ -101,11 +101,11 @@ build-hgctl-multiarch: prebuild $(OUT)
.PHONY: build-hgctl-macos-arm64
build-hgctl-macos-arm64: prebuild $(OUT)
CGO_ENABLED=1 GOPROXY=$(GOPROXY) GOOS=darwin GOARCH=arm64 LDFLAGS="-X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn" PROJECT_DIR="$(HGCTL_PROJECT_DIR)" tools/hack/gobuild.sh ../out/darwin_arm64/ $(HGCTL_BINARIES)
CGO_ENABLED=1 STATIC=0 GOPROXY=$(GOPROXY) GOOS=darwin GOARCH=arm64 PROJECT_DIR="$(HGCTL_PROJECT_DIR)" tools/hack/gobuild.sh ../out/darwin_arm64/ $(HGCTL_BINARIES)
.PHONY: build-hgctl-macos-amd64
build-hgctl-macos-amd64: prebuild $(OUT)
CGO_ENABLED=1 GOPROXY=$(GOPROXY) GOOS=darwin GOARCH=amd64 LDFLAGS="-X google.golang.org/protobuf/reflect/protoregistry.conflictPolicy=warn" PROJECT_DIR="$(HGCTL_PROJECT_DIR)" tools/hack/gobuild.sh ../out/darwin_amd64/ $(HGCTL_BINARIES)
CGO_ENABLED=1 STATIC=0 GOPROXY=$(GOPROXY) GOOS=darwin GOARCH=amd64 PROJECT_DIR="$(HGCTL_PROJECT_DIR)" tools/hack/gobuild.sh ../out/darwin_amd64/ $(HGCTL_BINARIES)
# Create targets for OUT_LINUX/binary
# There are two use cases here:
@@ -144,7 +144,7 @@ docker-buildx-push: clean-env docker.higress-buildx
export PARENT_GIT_TAG:=$(shell cat VERSION)
export PARENT_GIT_REVISION:=$(TAG)
export ENVOY_PACKAGE_URL_PATTERN?=https://github.com/higress-group/proxy/releases/download/v2.0.0-rc.1/envoy-symbol-ARCH.tar.gz
export ENVOY_PACKAGE_URL_PATTERN?=https://github.com/higress-group/proxy/releases/download/v2.0.0/envoy-symbol-ARCH.tar.gz
build-envoy: prebuild
./tools/hack/build-envoy.sh
@@ -187,8 +187,8 @@ install: pre-install
cd helm/higress; helm dependency build
helm install higress helm/higress -n higress-system --create-namespace --set 'global.local=true'
ENVOY_LATEST_IMAGE_TAG ?= a6c313d41b3b54f0e3ed81fc676c520160cfed05
ISTIO_LATEST_IMAGE_TAG ?= a9a55b3895bbf64a1ad8f724b2de3de017831e38
ENVOY_LATEST_IMAGE_TAG ?= 2.0.1
ISTIO_LATEST_IMAGE_TAG ?= 2.0.1
install-dev: pre-install
helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'pilot.tag=$(ISTIO_LATEST_IMAGE_TAG)' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true'
@@ -221,11 +221,15 @@ clean-higress: ## Cleans all the intermediate files and folders previously gener
rm -rf $(DIRS_TO_CLEAN)
clean-istio:
rm -rf external/api
rm -rf external/client-go
rm -rf external/istio
rm -rf external/pkg
clean-gateway: clean-istio
rm -rf external/envoy
rm -rf external/proxy
rm -rf external/go-control-plane
rm -rf external/package/envoy.tar.gz
clean-env:
@@ -284,28 +288,32 @@ delete-cluster: $(tools/kind) ## Delete kind cluster.
.PHONY: kube-load-image
kube-load-image: $(tools/kind) ## Install the Higress image to a kind cluster using the provided $IMAGE and $TAG.
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/higress $(TAG)
tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/pilot $(ISTIO_LATEST_IMAGE_TAG)
tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/gateway $(ENVOY_LATEST_IMAGE_TAG)
tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/dubbo-provider-demo 0.0.3-x86
tools/hack/docker-pull-image.sh docker.io/alihigress/nacos-standlone-rc3 1.0.0-RC3
tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/nacos-standlone-rc3 1.0.0-RC3
tools/hack/docker-pull-image.sh docker.io/hashicorp/consul 1.16.0
tools/hack/docker-pull-image.sh docker.io/charlie1380/eureka-registry-provider v0.3.0
tools/hack/docker-pull-image.sh docker.io/bitinit/eureka latest
tools/hack/docker-pull-image.sh docker.io/alihigress/httpbin 1.0.2
tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/httpbin 1.0.2
tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echo-server 1.3.0
tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echo-server v1.0
tools/hack/docker-pull-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echo-body 1.0.0
tools/hack/docker-pull-image.sh openpolicyagent/opa latest
tools/hack/docker-pull-image.sh curlimages/curl latest
tools/hack/docker-pull-image.sh registry.cn-hangzhou.aliyuncs.com/2456868764/httpbin 1.0.2
tools/hack/docker-pull-image.sh registry.cn-hangzhou.aliyuncs.com/hinsteny/nacos-standlone-rc3 1.0.0-RC3
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/dubbo-provider-demo 0.0.3-x86
tools/hack/kind-load-image.sh docker.io/alihigress/nacos-standlone-rc3 1.0.0-RC3
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/nacos-standlone-rc3 1.0.0-RC3
tools/hack/kind-load-image.sh docker.io/hashicorp/consul 1.16.0
tools/hack/kind-load-image.sh docker.io/alihigress/httpbin 1.0.2
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/httpbin 1.0.2
tools/hack/kind-load-image.sh docker.io/charlie1380/eureka-registry-provider v0.3.0
tools/hack/kind-load-image.sh docker.io/bitinit/eureka latest
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echo-server 1.3.0
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echo-server v1.0
tools/hack/kind-load-image.sh higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/echo-body 1.0.0
tools/hack/kind-load-image.sh openpolicyagent/opa latest
tools/hack/kind-load-image.sh curlimages/curl latest
tools/hack/kind-load-image.sh registry.cn-hangzhou.aliyuncs.com/2456868764/httpbin 1.0.2
tools/hack/kind-load-image.sh registry.cn-hangzhou.aliyuncs.com/hinsteny/nacos-standlone-rc3 1.0.0-RC3

View File

@@ -1,3 +1,4 @@
<a name="readme-top"></a>
<h1 align="center">
<img src="https://img.alicdn.com/imgextra/i2/O1CN01NwxLDd20nxfGBjxmZ_!!6000000006895-2-tps-960-290.png" alt="Higress" width="240" height="72.5">
<br>
@@ -8,26 +9,28 @@
[![Build Status](https://github.com/alibaba/higress/actions/workflows/build-and-test.yaml/badge.svg?branch=main)](https://github.com/alibaba/higress/actions)
[![license](https://img.shields.io/github/license/alibaba/higress.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[**官网**](https://higress.io/) &nbsp; |
&nbsp; [**文档**](https://higress.io/docs/latest/user/quickstart/) &nbsp; |
&nbsp; [**博客**](https://higress.io/blog/) &nbsp; |
&nbsp; [**开发指引**](https://higress.io/docs/latest/dev/architecture/) &nbsp; |
&nbsp; [**AI插件**](https://higress.io/plugin/) &nbsp;
[**官网**](https://higress.cn/) &nbsp; |
&nbsp; [**文档**](https://higress.cn/docs/latest/overview/what-is-higress/) &nbsp; |
&nbsp; [**博客**](https://higress.cn/blog/) &nbsp; |
&nbsp; [**电子书**](https://higress.cn/docs/ebook/wasm14/) &nbsp; |
&nbsp; [**开发指引**](https://higress.cn/docs/latest/dev/architecture/) &nbsp; |
&nbsp; [**AI插件**](https://higress.cn/plugin/) &nbsp;
<p>
<a href="README_EN.md"> English <a/> | 中文
<a href="README_EN.md"> English <a/>| 中文 | <a href="README_JP.md"> 日本語 <a/>
</p>
Higress 是基于阿里内部多年的 Envoy Gateway 实践沉淀,以开源 [Istio](https://github.com/istio/istio) 与 [Envoy](https://github.com/envoyproxy/envoy) 为核心构建的云原生 API 网关。
Higress 是一款云原生 API 网关,内核基于 Istio 和 Envoy可以用 Go/Rust/JS 等编写 Wasm 插件提供了数十个现成的通用插件以及开箱即用的控制台demo 点[这里](http://demo.higress.io/)
Higress 在阿里内部作为 AI 网关,承载了通义千问 APP、百炼大模型 API、机器学习 PAI 平台等 AI 业务的流量
Higress 在阿里内部为解决 Tengine reload 对长连接业务有损,以及 gRPC/Dubbo 负载均衡能力不足而诞生
Higress 能够用统一的协议对接国内外所有 LLM 模型厂商,同时具备丰富的 AI 可观测、多模型负载均衡/fallback、AI token 流控、AI 缓存等能力
阿里云基于 Higress 构建了云原生 API 网关产品,为大量企业客户提供 99.99% 的网关高可用保障服务能力
![](https://img.alicdn.com/imgextra/i1/O1CN01fNnhCp1cV8mYPRFeS_!!6000000003605-0-tps-1080-608.jpg)
Higress 基于 AI 网关能力,支撑了通义千问 APP、百炼大模型 API、机器学习 PAI 平台等 AI 业务。同时服务国内头部的 AIGC 企业(如零一万物),以及 AI 产品(如 FastGPT
![](https://img.alicdn.com/imgextra/i2/O1CN011AbR8023V8R5N0HcA_!!6000000007260-2-tps-1080-606.png)
## Summary
@@ -57,32 +60,41 @@ docker run -d --rm --name higress-ai -v ${PWD}:/data \
- 8080 端口:网关 HTTP 协议入口
- 8443 端口:网关 HTTPS 协议入口
**Higress 的所有 Docker 镜像都一直使用自己独享的仓库,不受 Docker Hub 境内不可访问的影响**
**Higress 的所有 Docker 镜像都一直使用自己独享的仓库,不受 Docker Hub 境内访问受限的影响**
K8s 下使用 Helm 部署等其他安装方式可以参考官网 [Quick Start 文档](https://higress.io/docs/latest/user/quickstart/)。
K8s 下使用 Helm 部署等其他安装方式可以参考官网 [Quick Start 文档](https://higress.cn/docs/latest/user/quickstart/)。
## 使用场景
- **AI 网关**:
Higress 提供了一站式的 AI 插件集,可以增强依赖 AI 能力业务的稳定性、灵活性、可观测性,使得业务与 AI 的集成更加便捷和高效。
Higress 能够用统一的协议对接国内外所有 LLM 模型厂商,同时具备丰富的 AI 可观测、多模型负载均衡/fallback、AI token 流控、AI 缓存等能力:
![](https://img.alicdn.com/imgextra/i1/O1CN01fNnhCp1cV8mYPRFeS_!!6000000003605-0-tps-1080-608.jpg)
- **Kubernetes Ingress 网关**:
Higress 可以作为 K8s 集群的 Ingress 入口网关, 并且兼容了大量 K8s Nginx Ingress 的注解,可以从 K8s Nginx Ingress 快速平滑迁移到 Higress。
支持 [Gateway API](https://gateway-api.sigs.k8s.io/) 标准,支持用户从 Ingress API 平滑迁移到 Gateway API。
相比 ingress-nginx资源开销大幅下降路由变更生效速度有十倍提升
![](https://img.alicdn.com/imgextra/i1/O1CN01bhEtb229eeMNBWmdP_!!6000000008093-2-tps-750-547.png)
![](https://img.alicdn.com/imgextra/i1/O1CN01bqRets1LsBGyitj4S_!!6000000001354-2-tps-887-489.png)
- **微服务网关**:
Higress 可以作为微服务网关, 能够对接多种类型的注册中心发现服务配置路由,例如 Nacos, ZooKeeper, Consul, Eureka 等。
并且深度集成了 [Dubbo](https://github.com/apache/dubbo), [Nacos](https://github.com/alibaba/nacos), [Sentinel](https://github.com/alibaba/Sentinel) 等微服务技术栈,基于 Envoy C++ 网关内核的出色性能,相比传统 Java 类微服务网关,可以显著降低资源使用率,减少成本。
![](https://img.alicdn.com/imgextra/i4/O1CN01v4ZbCj1dBjePSMZ17_!!6000000003698-0-tps-1613-926.jpg)
- **安全防护网关**:
Higress 可以作为安全防护网关, 提供 WAF 的能力,并且支持多种认证鉴权策略,例如 key-auth, hmac-auth, jwt-auth, basic-auth, oidc 等。
Higress 可以作为安全防护网关, 提供 WAF 的能力,并且支持多种认证鉴权策略,例如 key-auth, hmac-auth, jwt-auth, basic-auth, oidc 等。
## 核心优势
@@ -164,7 +176,7 @@ K8s 下使用 Helm 部署等其他安装方式可以参考官网 [Quick Start
### 交流群
![image](https://img.alicdn.com/imgextra/i2/O1CN01qPd7Ix1uZPVEsWjWp_!!6000000006051-0-tps-720-405.jpg)
![image](https://img.alicdn.com/imgextra/i2/O1CN01BkopaB22ZsvamFftE_!!6000000007135-0-tps-720-405.jpg)
### 技术分享
@@ -172,3 +184,23 @@ K8s 下使用 Helm 部署等其他安装方式可以参考官网 [Quick Start
![](https://img.alicdn.com/imgextra/i1/O1CN01WnQt0q1tcmqVDU73u_!!6000000005923-0-tps-258-258.jpg)
### 关联仓库
- Higress 控制台https://github.com/higress-group/higress-console
- Higress独立运行版https://github.com/higress-group/higress-standalone
### 贡献者
<a href="https://github.com/alibaba/higress/graphs/contributors">
<img alt="contributors" src="https://contrib.rocks/image?repo=alibaba/higress"/>
</a>
### Star History
[![Star History](https://api.star-history.com/svg?repos=alibaba/higress&type=Date)](https://star-history.com/#alibaba/higress&Date)
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ 返回顶部 ↑
</a>
</p>

View File

@@ -1,3 +1,4 @@
<a name="readme-top"></a>
<h1 align="center">
<img src="https://img.alicdn.com/imgextra/i2/O1CN01NwxLDd20nxfGBjxmZ_!!6000000006895-2-tps-960-290.png" alt="Higress" width="240" height="72.5">
<br>
@@ -15,7 +16,7 @@
<p>
English | <a href="README.md">中文<a/>
English | <a href="README.md">中文<a/> | <a href="README_JP.md">日本語<a/>
</p>
Higress is a cloud-native api gateway based on Alibaba's internal gateway practices.
@@ -47,7 +48,7 @@ Powered by [Istio](https://github.com/istio/istio) and [Envoy](https://github.co
Higress can function as a microservice gateway, which can discovery microservices from various service registries, such as Nacos, ZooKeeper, Consul, Eureka, etc.
It deeply integrates of [Dubbo](https://github.com/apache/dubbo), [Nacos](https://github.com/alibaba/nacos), [Sentinel](https://github.com/alibaba/Sentinel) and other microservice technology stacks.
It deeply integrates with [Dubbo](https://github.com/apache/dubbo), [Nacos](https://github.com/alibaba/nacos), [Sentinel](https://github.com/alibaba/Sentinel) and other microservice technology stacks.
- **Security gateway**:
@@ -57,7 +58,7 @@ Powered by [Istio](https://github.com/istio/istio) and [Envoy](https://github.co
- **Easy to use**
Provide one-stop gateway solutions for traffic scheduling, service management, and security protection, support Console, K8s Ingress, and Gateway API configuration methods, and also support HTTP to Dubbo protocol conversion, and easily complete protocol mapping configuration.
Provides one-stop gateway solutions for traffic scheduling, service management, and security protection, support Console, K8s Ingress, and Gateway API configuration methods, and also support HTTP to Dubbo protocol conversion, and easily complete protocol mapping configuration.
- **Easy to expand**
@@ -73,7 +74,7 @@ Powered by [Istio](https://github.com/istio/istio) and [Envoy](https://github.co
- **Security**
Provides JWT, OIDC, custom authentication and authentication, deeply integrates open source web application firewall.
Provides JWT, OIDC, custom authentication and authentication, deeply integrates open-source web application firewall.
## Community
@@ -81,4 +82,25 @@ Powered by [Istio](https://github.com/istio/istio) and [Envoy](https://github.co
### Thanks
Higress would not be possible without the valuable open-source work of projects in the community. We would like to extend a special thank-you to Envoy and Istio.
Higress would not be possible without the valuable open-source work of projects in the community. We would like to extend a special thank you to Envoy and Istio.
### Related Repositories
- Higress Console: https://github.com/higress-group/higress-console
- Higress Standalone: https://github.com/higress-group/higress-standalone
### Contributors
<a href="https://github.com/alibaba/higress/graphs/contributors">
<img alt="contributors" src="https://contrib.rocks/image?repo=alibaba/higress"/>
</a>
### Star History
[![Star History Chart](https://api.star-history.com/svg?repos=alibaba/higress&type=Date)](https://star-history.com/#alibaba/higress&Date)
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ Back to Top ↑
</a>
</p>

206
README_JP.md Normal file
View File

@@ -0,0 +1,206 @@
<a name="readme-top"></a>
<h1 align="center">
<img src="https://img.alicdn.com/imgextra/i2/O1CN01NwxLDd20nxfGBjxmZ_!!6000000006895-2-tps-960-290.png" alt="Higress" width="240" height="72.5">
<br>
AIゲートウェイ
</h1>
<h4 align="center"> AIネイティブAPIゲートウェイ </h4>
[![Build Status](https://github.com/alibaba/higress/actions/workflows/build-and-test.yaml/badge.svg?branch=main)](https://github.com/alibaba/higress/actions)
[![license](https://img.shields.io/github/license/alibaba/higress.svg)](https://www.apache.org/licenses/LICENSE-2.0.html)
[**公式サイト**](https://higress.cn/) &nbsp; |
&nbsp; [**ドキュメント**](https://higress.cn/docs/latest/overview/what-is-higress/) &nbsp; |
&nbsp; [**ブログ**](https://higress.cn/blog/) &nbsp; |
&nbsp; [**電子書籍**](https://higress.cn/docs/ebook/wasm14/) &nbsp; |
&nbsp; [**開発ガイド**](https://higress.cn/docs/latest/dev/architecture/) &nbsp; |
&nbsp; [**AIプラグイン**](https://higress.cn/plugin/) &nbsp;
<p>
<a href="README_EN.md"> English <a/> | <a href="README.md">中文<a/> | 日本語
</p>
Higressは、IstioとEnvoyをベースにしたクラウドネイティブAPIゲートウェイで、Go/Rust/JSなどを使用してWasmプラグインを作成できます。数十の既製の汎用プラグインと、すぐに使用できるコンソールを提供していますデモは[こちら](http://demo.higress.io/))。
Higressは、Tengineのリロードが長時間接続のビジネスに影響を与える問題や、gRPC/Dubboの負荷分散能力の不足を解決するために、Alibaba内部で誕生しました。
Alibaba Cloudは、Higressを基盤にクラウドネイティブAPIゲートウェイ製品を構築し、多くの企業顧客に99.99%のゲートウェイ高可用性保証サービスを提供しています。
Higressは、AIゲートウェイ機能を基盤に、Tongyi Qianwen APP、Bailian大規模モデルAPI、機械学習PAIプラットフォームなどのAIビジネスをサポートしています。また、国内の主要なAIGC企業ZeroOneやAI製品FastGPTにもサービスを提供しています。
![](https://img.alicdn.com/imgextra/i2/O1CN011AbR8023V8R5N0HcA_!!6000000007260-2-tps-1080-606.png)
## 目次
- [**クイックスタート**](#クイックスタート)
- [**機能紹介**](#機能紹介)
- [**使用シナリオ**](#使用シナリオ)
- [**主な利点**](#主な利点)
- [**コミュニティ**](#コミュニティ)
## クイックスタート
HigressはDockerだけで起動でき、個人開発者がローカルで学習用にセットアップしたり、簡易サイトを構築するのに便利です。
```bash
# 作業ディレクトリを作成
mkdir higress; cd higress
# Higressを起動し、設定ファイルを作業ディレクトリに書き込みます
docker run -d --rm --name higress-ai -v ${PWD}:/data \
-p 8001:8001 -p 8080:8080 -p 8443:8443 \
higress-registry.cn-hangzhou.cr.aliyuncs.com/higress/all-in-one:latest
```
リスンポートの説明は以下の通りです:
- 8001ポートHigress UIコンソールのエントリーポイント
- 8080ポートゲートウェイのHTTPプロトコルエントリーポイント
- 8443ポートゲートウェイのHTTPSプロトコルエントリーポイント
**HigressのすべてのDockerイメージは専用のリポジトリを使用しており、Docker Hubの国内アクセス不可の影響を受けません**
K8sでのHelmデプロイなどの他のインストール方法については、公式サイトの[クイックスタートドキュメント](https://higress.cn/docs/latest/user/quickstart/)を参照してください。
## 使用シナリオ
- **AIゲートウェイ**:
Higressは、国内外のすべてのLLMモデルプロバイダーと統一されたプロトコルで接続でき、豊富なAI可観測性、多モデル負荷分散/フォールバック、AIトークンフロー制御、AIキャッシュなどの機能を備えています。
![](https://img.alicdn.com/imgextra/i1/O1CN01fNnhCp1cV8mYPRFeS_!!6000000003605-0-tps-1080-608.jpg)
- **Kubernetes Ingressゲートウェイ**:
HigressはK8sクラスターのIngressエントリーポイントゲートウェイとして機能し、多くのK8s Nginx Ingressの注釈に対応しています。K8s Nginx IngressからHigressへのスムーズな移行が可能です。
[Gateway API](https://gateway-api.sigs.k8s.io/)標準をサポートし、ユーザーがIngress APIからGateway APIにスムーズに移行できるようにします。
ingress-nginxと比較して、リソースの消費が大幅に減少し、ルーティングの変更が10倍速く反映されます。
![](https://img.alicdn.com/imgextra/i1/O1CN01bhEtb229eeMNBWmdP_!!6000000008093-2-tps-750-547.png)
![](https://img.alicdn.com/imgextra/i1/O1CN01bqRets1LsBGyitj4S_!!6000000001354-2-tps-887-489.png)
- **マイクロサービスゲートウェイ**:
Higressはマイクロサービスゲートウェイとして機能し、Nacos、ZooKeeper、Consul、Eurekaなどのさまざまなサービスレジストリからサービスを発見し、ルーティングを構成できます。
また、[Dubbo](https://github.com/apache/dubbo)、[Nacos](https://github.com/alibaba/nacos)、[Sentinel](https://github.com/alibaba/Sentinel)などのマイクロサービス技術スタックと深く統合されています。Envoy C++ゲートウェイコアの優れたパフォーマンスに基づいて、従来のJavaベースのマイクロサービスゲートウェイと比較して、リソース使用率を大幅に削減し、コストを削減できます。
![](https://img.alicdn.com/imgextra/i4/O1CN01v4ZbCj1dBjePSMZ17_!!6000000003698-0-tps-1613-926.jpg)
- **セキュリティゲートウェイ**:
Higressはセキュリティゲートウェイとして機能し、WAF機能を提供し、key-auth、hmac-auth、jwt-auth、basic-auth、oidcなどのさまざまな認証戦略をサポートします。
## 主な利点
- **プロダクションレベル**
Alibabaで2年以上のプロダクション検証を経た内部製品から派生し、毎秒数十万のリクエストを処理する大規模なシナリオをサポートします。
Nginxのリロードによるトラフィックの揺れを完全に排除し、構成変更がミリ秒単位で反映され、ビジネスに影響を与えません。AIビジネスなどの長時間接続シナリオに特に適しています。
- **ストリーム処理**
リクエスト/レスポンスボディの完全なストリーム処理をサポートし、Wasmプラグインを使用してSSEServer-Sent Eventsなどのストリームプロトコルのメッセージをカスタマイズして処理できます。
AIビジネスなどの大帯域幅シナリオで、メモリ使用量を大幅に削減できます。
- **拡張性**
AI、トラフィック管理、セキュリティ保護などの一般的な機能をカバーする豊富な公式プラグインライブラリを提供し、90以上のビジネスシナリオのニーズを満たします。
Wasmプラグイン拡張を主力とし、サンドボックス隔離を通じてメモリの安全性を確保し、複数のプログラミング言語をサポートし、プラグインバージョンの独立したアップグレードを許可し、トラフィックに影響を与えずにゲートウェイロジックをホットアップデートできます。
- **安全で使いやすい**
Ingress APIおよびGateway API標準に基づき、すぐに使用できるUIコンソールを提供し、WAF保護プラグイン、IP/Cookie CC保護プラグインをすぐに使用できます。
Let's Encryptの自動証明書発行および更新をサポートし、K8sを使用せずにデプロイでき、1行のDockerコマンドで起動でき、個人開発者にとって便利です。
## 機能紹介
### AIゲートウェイデモ展示
[OpenAIから他の大規模モデルへの移行を30秒で完了
](https://www.bilibili.com/video/BV1dT421a7w7/?spm_id_from=333.788.recommend_more_video.14)
### Higress UIコンソール
- **豊富な可観測性**
すぐに使用できる可観測性を提供し、GrafanaPrometheusは組み込みのものを使用することも、自分で構築したものを接続することもできます。
![](./docs/images/monitor.gif)
- **プラグイン拡張メカニズム**
公式にはさまざまなプラグインが提供されており、ユーザーは[独自のプラグインを開発](./plugins/wasm-go)し、Docker/OCIイメージとして構築し、コンソールで構成して、プラグインロジックをリアルタイムで変更できます。トラフィックに影響を与えずにプラグインロジックをホットアップデートできます。
![](./docs/images/plugin.gif)
- **さまざまなサービス発見**
デフォルトでK8s Serviceサービス発見を提供し、構成を通じてNacos/ZooKeeperなどのレジストリに接続してサービスを発見することも、静的IPまたはDNSに基づいて発見することもできます。
![](./docs/images/service-source.gif)
- **ドメインと証明書**
TLS証明書を作成および管理し、ドメインのHTTP/HTTPS動作を構成できます。ドメインポリシーでは、特定のドメインに対してプラグインを適用することができます。
![](./docs/images/domain.gif)
- **豊富なルーティング機能**
上記で定義されたサービス発見メカニズムを通じて、発見されたサービスはサービスリストに表示されます。ルーティングを作成する際に、ドメインを選択し、ルーティングマッチングメカニズムを定義し、ターゲットサービスを選択してルーティングを行います。ルーティングポリシーでは、特定のルーティングに対してプラグインを適用することができます。
![](./docs/images/route-service.gif)
## コミュニティ
### 感謝
EnvoyとIstioのオープンソースの取り組みがなければ、Higressは実現できませんでした。これらのプロジェクトに最も誠実な敬意を表します。
### 交流グループ
![image](https://img.alicdn.com/imgextra/i2/O1CN01BkopaB22ZsvamFftE_!!6000000007135-0-tps-720-405.jpg)
### 技術共有
WeChat公式アカウント
![](https://img.alicdn.com/imgextra/i1/O1CN01WnQt0q1tcmqVDU73u_!!6000000005923-0-tps-258-258.jpg)
### 関連リポジトリ
- Higressコンソールhttps://github.com/higress-group/higress-console
- Higressスタンドアロン版https://github.com/higress-group/higress-standalone
### 貢献者
<a href="https://github.com/alibaba/higress/graphs/contributors">
<img alt="contributors" src="https://contrib.rocks/image?repo=alibaba/higress"/>
</a>
### スターの歴史
[![スターの歴史チャート](https://api.star-history.com/svg?repos=alibaba/higress&type=Date)](https://star-history.com/#alibaba/higress&Date)
<p align="right" style="font-size: 14px; color: #555; margin-top: 20px;">
<a href="#readme-top" style="text-decoration: none; color: #007bff; font-weight: bold;">
↑ トップに戻る ↑
</a>
</p>

View File

@@ -4,6 +4,7 @@
| Version | Supported |
| ------- | ------------------ |
| 2.x.x | :white_check_mark: |
| 1.x.x | :white_check_mark: |
| < 1.0.0 | :x: |

View File

@@ -1 +1 @@
v2.0.0-rc.1
v2.0.3

View File

@@ -17,7 +17,7 @@
// versions:
// protoc-gen-go v1.31.0
// protoc (unknown)
// source: extensions/v1alpha1/wasm.proto
// source: extensions/v1alpha1/wasmplugin.proto
// $schema: higress.extensions.v1alpha1.WasmPlugin
// $title: WasmPlugin
@@ -84,11 +84,11 @@ func (x PluginPhase) String() string {
}
func (PluginPhase) Descriptor() protoreflect.EnumDescriptor {
return file_extensions_v1alpha1_wasm_proto_enumTypes[0].Descriptor()
return file_extensions_v1alpha1_wasmplugin_proto_enumTypes[0].Descriptor()
}
func (PluginPhase) Type() protoreflect.EnumType {
return &file_extensions_v1alpha1_wasm_proto_enumTypes[0]
return &file_extensions_v1alpha1_wasmplugin_proto_enumTypes[0]
}
func (x PluginPhase) Number() protoreflect.EnumNumber {
@@ -97,7 +97,7 @@ func (x PluginPhase) Number() protoreflect.EnumNumber {
// Deprecated: Use PluginPhase.Descriptor instead.
func (PluginPhase) EnumDescriptor() ([]byte, []int) {
return file_extensions_v1alpha1_wasm_proto_rawDescGZIP(), []int{0}
return file_extensions_v1alpha1_wasmplugin_proto_rawDescGZIP(), []int{0}
}
// The pull behaviour to be applied when fetching an OCI image,
@@ -146,11 +146,11 @@ func (x PullPolicy) String() string {
}
func (PullPolicy) Descriptor() protoreflect.EnumDescriptor {
return file_extensions_v1alpha1_wasm_proto_enumTypes[1].Descriptor()
return file_extensions_v1alpha1_wasmplugin_proto_enumTypes[1].Descriptor()
}
func (PullPolicy) Type() protoreflect.EnumType {
return &file_extensions_v1alpha1_wasm_proto_enumTypes[1]
return &file_extensions_v1alpha1_wasmplugin_proto_enumTypes[1]
}
func (x PullPolicy) Number() protoreflect.EnumNumber {
@@ -159,7 +159,7 @@ func (x PullPolicy) Number() protoreflect.EnumNumber {
// Deprecated: Use PullPolicy.Descriptor instead.
func (PullPolicy) EnumDescriptor() ([]byte, []int) {
return file_extensions_v1alpha1_wasm_proto_rawDescGZIP(), []int{1}
return file_extensions_v1alpha1_wasmplugin_proto_rawDescGZIP(), []int{1}
}
type EnvValueSource int32
@@ -194,11 +194,11 @@ func (x EnvValueSource) String() string {
}
func (EnvValueSource) Descriptor() protoreflect.EnumDescriptor {
return file_extensions_v1alpha1_wasm_proto_enumTypes[2].Descriptor()
return file_extensions_v1alpha1_wasmplugin_proto_enumTypes[2].Descriptor()
}
func (EnvValueSource) Type() protoreflect.EnumType {
return &file_extensions_v1alpha1_wasm_proto_enumTypes[2]
return &file_extensions_v1alpha1_wasmplugin_proto_enumTypes[2]
}
func (x EnvValueSource) Number() protoreflect.EnumNumber {
@@ -207,7 +207,7 @@ func (x EnvValueSource) Number() protoreflect.EnumNumber {
// Deprecated: Use EnvValueSource.Descriptor instead.
func (EnvValueSource) EnumDescriptor() ([]byte, []int) {
return file_extensions_v1alpha1_wasm_proto_rawDescGZIP(), []int{2}
return file_extensions_v1alpha1_wasmplugin_proto_rawDescGZIP(), []int{2}
}
type FailStrategy int32
@@ -246,11 +246,11 @@ func (x FailStrategy) String() string {
}
func (FailStrategy) Descriptor() protoreflect.EnumDescriptor {
return file_extensions_v1alpha1_wasm_proto_enumTypes[3].Descriptor()
return file_extensions_v1alpha1_wasmplugin_proto_enumTypes[3].Descriptor()
}
func (FailStrategy) Type() protoreflect.EnumType {
return &file_extensions_v1alpha1_wasm_proto_enumTypes[3]
return &file_extensions_v1alpha1_wasmplugin_proto_enumTypes[3]
}
func (x FailStrategy) Number() protoreflect.EnumNumber {
@@ -259,7 +259,7 @@ func (x FailStrategy) Number() protoreflect.EnumNumber {
// Deprecated: Use FailStrategy.Descriptor instead.
func (FailStrategy) EnumDescriptor() ([]byte, []int) {
return file_extensions_v1alpha1_wasm_proto_rawDescGZIP(), []int{3}
return file_extensions_v1alpha1_wasmplugin_proto_rawDescGZIP(), []int{3}
}
// <!-- crd generation tags
@@ -347,7 +347,7 @@ type WasmPlugin struct {
func (x *WasmPlugin) Reset() {
*x = WasmPlugin{}
if protoimpl.UnsafeEnabled {
mi := &file_extensions_v1alpha1_wasm_proto_msgTypes[0]
mi := &file_extensions_v1alpha1_wasmplugin_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -360,7 +360,7 @@ func (x *WasmPlugin) String() string {
func (*WasmPlugin) ProtoMessage() {}
func (x *WasmPlugin) ProtoReflect() protoreflect.Message {
mi := &file_extensions_v1alpha1_wasm_proto_msgTypes[0]
mi := &file_extensions_v1alpha1_wasmplugin_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -373,7 +373,7 @@ func (x *WasmPlugin) ProtoReflect() protoreflect.Message {
// Deprecated: Use WasmPlugin.ProtoReflect.Descriptor instead.
func (*WasmPlugin) Descriptor() ([]byte, []int) {
return file_extensions_v1alpha1_wasm_proto_rawDescGZIP(), []int{0}
return file_extensions_v1alpha1_wasmplugin_proto_rawDescGZIP(), []int{0}
}
func (x *WasmPlugin) GetUrl() string {
@@ -490,7 +490,7 @@ type MatchRule struct {
func (x *MatchRule) Reset() {
*x = MatchRule{}
if protoimpl.UnsafeEnabled {
mi := &file_extensions_v1alpha1_wasm_proto_msgTypes[1]
mi := &file_extensions_v1alpha1_wasmplugin_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -503,7 +503,7 @@ func (x *MatchRule) String() string {
func (*MatchRule) ProtoMessage() {}
func (x *MatchRule) ProtoReflect() protoreflect.Message {
mi := &file_extensions_v1alpha1_wasm_proto_msgTypes[1]
mi := &file_extensions_v1alpha1_wasmplugin_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -516,7 +516,7 @@ func (x *MatchRule) ProtoReflect() protoreflect.Message {
// Deprecated: Use MatchRule.ProtoReflect.Descriptor instead.
func (*MatchRule) Descriptor() ([]byte, []int) {
return file_extensions_v1alpha1_wasm_proto_rawDescGZIP(), []int{1}
return file_extensions_v1alpha1_wasmplugin_proto_rawDescGZIP(), []int{1}
}
func (x *MatchRule) GetIngress() []string {
@@ -569,7 +569,7 @@ type VmConfig struct {
func (x *VmConfig) Reset() {
*x = VmConfig{}
if protoimpl.UnsafeEnabled {
mi := &file_extensions_v1alpha1_wasm_proto_msgTypes[2]
mi := &file_extensions_v1alpha1_wasmplugin_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -582,7 +582,7 @@ func (x *VmConfig) String() string {
func (*VmConfig) ProtoMessage() {}
func (x *VmConfig) ProtoReflect() protoreflect.Message {
mi := &file_extensions_v1alpha1_wasm_proto_msgTypes[2]
mi := &file_extensions_v1alpha1_wasmplugin_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -595,7 +595,7 @@ func (x *VmConfig) ProtoReflect() protoreflect.Message {
// Deprecated: Use VmConfig.ProtoReflect.Descriptor instead.
func (*VmConfig) Descriptor() ([]byte, []int) {
return file_extensions_v1alpha1_wasm_proto_rawDescGZIP(), []int{2}
return file_extensions_v1alpha1_wasmplugin_proto_rawDescGZIP(), []int{2}
}
func (x *VmConfig) GetEnv() []*EnvVar {
@@ -625,7 +625,7 @@ type EnvVar struct {
func (x *EnvVar) Reset() {
*x = EnvVar{}
if protoimpl.UnsafeEnabled {
mi := &file_extensions_v1alpha1_wasm_proto_msgTypes[3]
mi := &file_extensions_v1alpha1_wasmplugin_proto_msgTypes[3]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
@@ -638,7 +638,7 @@ func (x *EnvVar) String() string {
func (*EnvVar) ProtoMessage() {}
func (x *EnvVar) ProtoReflect() protoreflect.Message {
mi := &file_extensions_v1alpha1_wasm_proto_msgTypes[3]
mi := &file_extensions_v1alpha1_wasmplugin_proto_msgTypes[3]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
@@ -651,7 +651,7 @@ func (x *EnvVar) ProtoReflect() protoreflect.Message {
// Deprecated: Use EnvVar.ProtoReflect.Descriptor instead.
func (*EnvVar) Descriptor() ([]byte, []int) {
return file_extensions_v1alpha1_wasm_proto_rawDescGZIP(), []int{3}
return file_extensions_v1alpha1_wasmplugin_proto_rawDescGZIP(), []int{3}
}
func (x *EnvVar) GetName() string {
@@ -675,124 +675,125 @@ func (x *EnvVar) GetValue() string {
return ""
}
var File_extensions_v1alpha1_wasm_proto protoreflect.FileDescriptor
var File_extensions_v1alpha1_wasmplugin_proto protoreflect.FileDescriptor
var file_extensions_v1alpha1_wasm_proto_rawDesc = []byte{
0x0a, 0x1e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x61,
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x77, 0x61, 0x73, 0x6d, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x12, 0x1b, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x1a, 0x1e, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77,
0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73,
0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x8d, 0x06, 0x0a, 0x0a,
0x57, 0x61, 0x73, 0x6d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72,
0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06,
0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x68,
0x61, 0x32, 0x35, 0x36, 0x12, 0x53, 0x0a, 0x11, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x75,
0x6c, 0x6c, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x27, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x75,
0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x50,
0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x2a, 0x0a, 0x11, 0x69, 0x6d, 0x61,
0x67, 0x65, 0x5f, 0x70, 0x75, 0x6c, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x05,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x75, 0x6c, 0x6c, 0x53,
0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79,
0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74,
0x52, 0x0c, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1f,
0x0a, 0x0b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x08, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12,
0x3e, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x28,
0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69,
0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x50, 0x6c, 0x75,
0x67, 0x69, 0x6e, 0x50, 0x68, 0x61, 0x73, 0x65, 0x52, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x12,
0x37, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0a, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x08,
0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x4e, 0x0a, 0x0d, 0x66, 0x61, 0x69, 0x6c,
0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0e, 0x32,
0x29, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73,
0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x46, 0x61,
0x69, 0x6c, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x0c, 0x66, 0x61, 0x69, 0x6c,
0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x42, 0x0a, 0x09, 0x76, 0x6d, 0x5f, 0x63,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x68, 0x69,
0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73,
0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x56, 0x6d, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x52, 0x08, 0x76, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0e,
0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x65,
0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0d, 0x64,
0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x47, 0x0a, 0x0b,
0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x72, 0x75, 0x6c, 0x65, 0x73, 0x18, 0x66, 0x20, 0x03, 0x28,
0x0b, 0x32, 0x26, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65,
var file_extensions_v1alpha1_wasmplugin_proto_rawDesc = []byte{
0x0a, 0x24, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x61,
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2f, 0x77, 0x61, 0x73, 0x6d, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1b, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e,
0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
0x68, 0x61, 0x31, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x77, 0x72, 0x61, 0x70, 0x70, 0x65, 0x72, 0x73, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x62, 0x75, 0x66, 0x2f, 0x73, 0x74, 0x72, 0x75, 0x63, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x22, 0x8d, 0x06, 0x0a, 0x0a, 0x57, 0x61, 0x73, 0x6d, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e,
0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75,
0x72, 0x6c, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x18, 0x03, 0x20, 0x01,
0x28, 0x09, 0x52, 0x06, 0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x12, 0x53, 0x0a, 0x11, 0x69, 0x6d,
0x61, 0x67, 0x65, 0x5f, 0x70, 0x75, 0x6c, 0x6c, 0x5f, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x18,
0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x27, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e,
0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
0x68, 0x61, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x52, 0x0f,
0x69, 0x6d, 0x61, 0x67, 0x65, 0x50, 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12,
0x2a, 0x0a, 0x11, 0x69, 0x6d, 0x61, 0x67, 0x65, 0x5f, 0x70, 0x75, 0x6c, 0x6c, 0x5f, 0x73, 0x65,
0x63, 0x72, 0x65, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x69, 0x6d, 0x61, 0x67,
0x65, 0x50, 0x75, 0x6c, 0x6c, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x76,
0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6b, 0x65, 0x79, 0x18,
0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74,
0x69, 0x6f, 0x6e, 0x4b, 0x65, 0x79, 0x12, 0x3c, 0x0a, 0x0d, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e,
0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0c, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x5f, 0x6e,
0x61, 0x6d, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x70, 0x6c, 0x75, 0x67, 0x69,
0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x3e, 0x0a, 0x05, 0x70, 0x68, 0x61, 0x73, 0x65, 0x18, 0x09,
0x20, 0x01, 0x28, 0x0e, 0x32, 0x28, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x65,
0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68,
0x61, 0x31, 0x2e, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x50, 0x68, 0x61, 0x73, 0x65, 0x52, 0x05,
0x70, 0x68, 0x61, 0x73, 0x65, 0x12, 0x37, 0x0a, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74,
0x79, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x49, 0x6e, 0x74, 0x33, 0x32, 0x56,
0x61, 0x6c, 0x75, 0x65, 0x52, 0x08, 0x70, 0x72, 0x69, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x4e,
0x0a, 0x0d, 0x66, 0x61, 0x69, 0x6c, 0x5f, 0x73, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18,
0x0d, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e,
0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70,
0x68, 0x61, 0x31, 0x2e, 0x46, 0x61, 0x69, 0x6c, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
0x52, 0x0c, 0x66, 0x61, 0x69, 0x6c, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x42,
0x0a, 0x09, 0x76, 0x6d, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x0b, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x25, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65,
0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e,
0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x75, 0x6c, 0x65, 0x52, 0x0a, 0x6d, 0x61, 0x74, 0x63, 0x68,
0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74,
0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18,
0x67, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x22, 0xaf, 0x01, 0x0a, 0x09,
0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x75, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x69, 0x6e, 0x67,
0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x69, 0x6e, 0x67, 0x72,
0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20,
0x03, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x12, 0x2f, 0x0a, 0x06, 0x63,
0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f,
0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74,
0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e,
0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x04,
0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x69, 0x73, 0x61,
0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x18, 0x05,
0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x22, 0x41, 0x0a,
0x08, 0x56, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x35, 0x0a, 0x03, 0x65, 0x6e, 0x76,
0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73,
0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c,
0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x52, 0x03, 0x65, 0x6e, 0x76,
0x22, 0x7e, 0x0a, 0x06, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61,
0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4a,
0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x72, 0x6f, 0x6d, 0x18, 0x03, 0x20, 0x01,
0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x78, 0x74,
0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31,
0x2e, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52,
0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x46, 0x72, 0x6f, 0x6d, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61,
0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x2a, 0x45, 0x0a, 0x0b, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x50, 0x68, 0x61, 0x73, 0x65, 0x12,
0x15, 0x0a, 0x11, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x50,
0x48, 0x41, 0x53, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x55, 0x54, 0x48, 0x4e, 0x10,
0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x55, 0x54, 0x48, 0x5a, 0x10, 0x02, 0x12, 0x09, 0x0a, 0x05,
0x53, 0x54, 0x41, 0x54, 0x53, 0x10, 0x03, 0x2a, 0x42, 0x0a, 0x0a, 0x50, 0x75, 0x6c, 0x6c, 0x50,
0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x16, 0x0a, 0x12, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
0x46, 0x49, 0x45, 0x44, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43, 0x59, 0x10, 0x00, 0x12, 0x10, 0x0a,
0x0c, 0x49, 0x66, 0x4e, 0x6f, 0x74, 0x50, 0x72, 0x65, 0x73, 0x65, 0x6e, 0x74, 0x10, 0x01, 0x12,
0x0a, 0x0a, 0x06, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10, 0x02, 0x2a, 0x26, 0x0a, 0x0e, 0x45,
0x6e, 0x76, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x12, 0x0a, 0x0a,
0x06, 0x49, 0x4e, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x00, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x4f, 0x53,
0x54, 0x10, 0x01, 0x2a, 0x2d, 0x0a, 0x0c, 0x46, 0x61, 0x69, 0x6c, 0x53, 0x74, 0x72, 0x61, 0x74,
0x65, 0x67, 0x79, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x41, 0x49, 0x4c, 0x5f, 0x43, 0x4c, 0x4f, 0x53,
0x45, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x41, 0x49, 0x4c, 0x5f, 0x4f, 0x50, 0x45, 0x4e,
0x10, 0x01, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x61, 0x6c, 0x69, 0x62, 0x61, 0x62, 0x61, 0x2f, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73,
0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f,
0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x56, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x08, 0x76, 0x6d, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x3e, 0x0a, 0x0e, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f,
0x6e, 0x66, 0x69, 0x67, 0x18, 0x65, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f,
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72,
0x75, 0x63, 0x74, 0x52, 0x0d, 0x64, 0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x47, 0x0a, 0x0b, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x5f, 0x72, 0x75, 0x6c, 0x65,
0x73, 0x18, 0x66, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73,
0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31, 0x61,
0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x75, 0x6c, 0x65, 0x52,
0x0a, 0x6d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x34, 0x0a, 0x16, 0x64,
0x65, 0x66, 0x61, 0x75, 0x6c, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x69,
0x73, 0x61, 0x62, 0x6c, 0x65, 0x18, 0x67, 0x20, 0x01, 0x28, 0x08, 0x52, 0x14, 0x64, 0x65, 0x66,
0x61, 0x75, 0x6c, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c,
0x65, 0x22, 0xaf, 0x01, 0x0a, 0x09, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x52, 0x75, 0x6c, 0x65, 0x12,
0x18, 0x0a, 0x07, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09,
0x52, 0x07, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x64, 0x6f, 0x6d,
0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69,
0x6e, 0x12, 0x2f, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x03, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x25, 0x0a, 0x0e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5f, 0x64, 0x69, 0x73,
0x61, 0x62, 0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0d, 0x63, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x65, 0x72,
0x76, 0x69, 0x63, 0x65, 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x73, 0x65, 0x72, 0x76,
0x69, 0x63, 0x65, 0x22, 0x41, 0x0a, 0x08, 0x56, 0x6d, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x35, 0x0a, 0x03, 0x65, 0x6e, 0x76, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x68,
0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e,
0x73, 0x2e, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x6e, 0x76, 0x56, 0x61,
0x72, 0x52, 0x03, 0x65, 0x6e, 0x76, 0x22, 0x7e, 0x0a, 0x06, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72,
0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04,
0x6e, 0x61, 0x6d, 0x65, 0x12, 0x4a, 0x0a, 0x0a, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x5f, 0x66, 0x72,
0x6f, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2b, 0x2e, 0x68, 0x69, 0x67, 0x72, 0x65,
0x73, 0x73, 0x2e, 0x65, 0x78, 0x74, 0x65, 0x6e, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x76, 0x31,
0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x2e, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x09, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x46, 0x72, 0x6f, 0x6d,
0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x2a, 0x45, 0x0a, 0x0b, 0x50, 0x6c, 0x75, 0x67, 0x69, 0x6e,
0x50, 0x68, 0x61, 0x73, 0x65, 0x12, 0x15, 0x0a, 0x11, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
0x46, 0x49, 0x45, 0x44, 0x5f, 0x50, 0x48, 0x41, 0x53, 0x45, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05,
0x41, 0x55, 0x54, 0x48, 0x4e, 0x10, 0x01, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x55, 0x54, 0x48, 0x5a,
0x10, 0x02, 0x12, 0x09, 0x0a, 0x05, 0x53, 0x54, 0x41, 0x54, 0x53, 0x10, 0x03, 0x2a, 0x42, 0x0a,
0x0a, 0x50, 0x75, 0x6c, 0x6c, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x12, 0x16, 0x0a, 0x12, 0x55,
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x5f, 0x50, 0x4f, 0x4c, 0x49, 0x43,
0x59, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x49, 0x66, 0x4e, 0x6f, 0x74, 0x50, 0x72, 0x65, 0x73,
0x65, 0x6e, 0x74, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x41, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x10,
0x02, 0x2a, 0x26, 0x0a, 0x0e, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x53, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x12, 0x0a, 0x0a, 0x06, 0x49, 0x4e, 0x4c, 0x49, 0x4e, 0x45, 0x10, 0x00, 0x12,
0x08, 0x0a, 0x04, 0x48, 0x4f, 0x53, 0x54, 0x10, 0x01, 0x2a, 0x2d, 0x0a, 0x0c, 0x46, 0x61, 0x69,
0x6c, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x0e, 0x0a, 0x0a, 0x46, 0x41, 0x49,
0x4c, 0x5f, 0x43, 0x4c, 0x4f, 0x53, 0x45, 0x10, 0x00, 0x12, 0x0d, 0x0a, 0x09, 0x46, 0x41, 0x49,
0x4c, 0x5f, 0x4f, 0x50, 0x45, 0x4e, 0x10, 0x01, 0x42, 0x34, 0x5a, 0x32, 0x67, 0x69, 0x74, 0x68,
0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x61, 0x6c, 0x69, 0x62, 0x61, 0x62, 0x61, 0x2f, 0x68,
0x69, 0x67, 0x72, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x6e,
0x73, 0x69, 0x6f, 0x6e, 0x73, 0x2f, 0x76, 0x31, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x31, 0x62, 0x06,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_extensions_v1alpha1_wasm_proto_rawDescOnce sync.Once
file_extensions_v1alpha1_wasm_proto_rawDescData = file_extensions_v1alpha1_wasm_proto_rawDesc
file_extensions_v1alpha1_wasmplugin_proto_rawDescOnce sync.Once
file_extensions_v1alpha1_wasmplugin_proto_rawDescData = file_extensions_v1alpha1_wasmplugin_proto_rawDesc
)
func file_extensions_v1alpha1_wasm_proto_rawDescGZIP() []byte {
file_extensions_v1alpha1_wasm_proto_rawDescOnce.Do(func() {
file_extensions_v1alpha1_wasm_proto_rawDescData = protoimpl.X.CompressGZIP(file_extensions_v1alpha1_wasm_proto_rawDescData)
func file_extensions_v1alpha1_wasmplugin_proto_rawDescGZIP() []byte {
file_extensions_v1alpha1_wasmplugin_proto_rawDescOnce.Do(func() {
file_extensions_v1alpha1_wasmplugin_proto_rawDescData = protoimpl.X.CompressGZIP(file_extensions_v1alpha1_wasmplugin_proto_rawDescData)
})
return file_extensions_v1alpha1_wasm_proto_rawDescData
return file_extensions_v1alpha1_wasmplugin_proto_rawDescData
}
var file_extensions_v1alpha1_wasm_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
var file_extensions_v1alpha1_wasm_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_extensions_v1alpha1_wasm_proto_goTypes = []interface{}{
var file_extensions_v1alpha1_wasmplugin_proto_enumTypes = make([]protoimpl.EnumInfo, 4)
var file_extensions_v1alpha1_wasmplugin_proto_msgTypes = make([]protoimpl.MessageInfo, 4)
var file_extensions_v1alpha1_wasmplugin_proto_goTypes = []interface{}{
(PluginPhase)(0), // 0: higress.extensions.v1alpha1.PluginPhase
(PullPolicy)(0), // 1: higress.extensions.v1alpha1.PullPolicy
(EnvValueSource)(0), // 2: higress.extensions.v1alpha1.EnvValueSource
@@ -804,7 +805,7 @@ var file_extensions_v1alpha1_wasm_proto_goTypes = []interface{}{
(*_struct.Struct)(nil), // 8: google.protobuf.Struct
(*wrappers.Int32Value)(nil), // 9: google.protobuf.Int32Value
}
var file_extensions_v1alpha1_wasm_proto_depIdxs = []int32{
var file_extensions_v1alpha1_wasmplugin_proto_depIdxs = []int32{
1, // 0: higress.extensions.v1alpha1.WasmPlugin.image_pull_policy:type_name -> higress.extensions.v1alpha1.PullPolicy
8, // 1: higress.extensions.v1alpha1.WasmPlugin.plugin_config:type_name -> google.protobuf.Struct
0, // 2: higress.extensions.v1alpha1.WasmPlugin.phase:type_name -> higress.extensions.v1alpha1.PluginPhase
@@ -823,13 +824,13 @@ var file_extensions_v1alpha1_wasm_proto_depIdxs = []int32{
0, // [0:11] is the sub-list for field type_name
}
func init() { file_extensions_v1alpha1_wasm_proto_init() }
func file_extensions_v1alpha1_wasm_proto_init() {
if File_extensions_v1alpha1_wasm_proto != nil {
func init() { file_extensions_v1alpha1_wasmplugin_proto_init() }
func file_extensions_v1alpha1_wasmplugin_proto_init() {
if File_extensions_v1alpha1_wasmplugin_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_extensions_v1alpha1_wasm_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
file_extensions_v1alpha1_wasmplugin_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*WasmPlugin); i {
case 0:
return &v.state
@@ -841,7 +842,7 @@ func file_extensions_v1alpha1_wasm_proto_init() {
return nil
}
}
file_extensions_v1alpha1_wasm_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
file_extensions_v1alpha1_wasmplugin_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*MatchRule); i {
case 0:
return &v.state
@@ -853,7 +854,7 @@ func file_extensions_v1alpha1_wasm_proto_init() {
return nil
}
}
file_extensions_v1alpha1_wasm_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
file_extensions_v1alpha1_wasmplugin_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*VmConfig); i {
case 0:
return &v.state
@@ -865,7 +866,7 @@ func file_extensions_v1alpha1_wasm_proto_init() {
return nil
}
}
file_extensions_v1alpha1_wasm_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
file_extensions_v1alpha1_wasmplugin_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*EnvVar); i {
case 0:
return &v.state
@@ -882,19 +883,19 @@ func file_extensions_v1alpha1_wasm_proto_init() {
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_extensions_v1alpha1_wasm_proto_rawDesc,
RawDescriptor: file_extensions_v1alpha1_wasmplugin_proto_rawDesc,
NumEnums: 4,
NumMessages: 4,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_extensions_v1alpha1_wasm_proto_goTypes,
DependencyIndexes: file_extensions_v1alpha1_wasm_proto_depIdxs,
EnumInfos: file_extensions_v1alpha1_wasm_proto_enumTypes,
MessageInfos: file_extensions_v1alpha1_wasm_proto_msgTypes,
GoTypes: file_extensions_v1alpha1_wasmplugin_proto_goTypes,
DependencyIndexes: file_extensions_v1alpha1_wasmplugin_proto_depIdxs,
EnumInfos: file_extensions_v1alpha1_wasmplugin_proto_enumTypes,
MessageInfos: file_extensions_v1alpha1_wasmplugin_proto_msgTypes,
}.Build()
File_extensions_v1alpha1_wasm_proto = out.File
file_extensions_v1alpha1_wasm_proto_rawDesc = nil
file_extensions_v1alpha1_wasm_proto_goTypes = nil
file_extensions_v1alpha1_wasm_proto_depIdxs = nil
File_extensions_v1alpha1_wasmplugin_proto = out.File
file_extensions_v1alpha1_wasmplugin_proto_rawDesc = nil
file_extensions_v1alpha1_wasmplugin_proto_goTypes = nil
file_extensions_v1alpha1_wasmplugin_proto_depIdxs = nil
}

View File

@@ -8,49 +8,49 @@ import (
// MarshalJSON is a custom marshaler for WasmPlugin
func (this *WasmPlugin) MarshalJSON() ([]byte, error) {
str, err := WasmMarshaler.MarshalToString(this)
str, err := WasmpluginMarshaler.MarshalToString(this)
return []byte(str), err
}
// UnmarshalJSON is a custom unmarshaler for WasmPlugin
func (this *WasmPlugin) UnmarshalJSON(b []byte) error {
return WasmUnmarshaler.Unmarshal(bytes.NewReader(b), this)
return WasmpluginUnmarshaler.Unmarshal(bytes.NewReader(b), this)
}
// MarshalJSON is a custom marshaler for MatchRule
func (this *MatchRule) MarshalJSON() ([]byte, error) {
str, err := WasmMarshaler.MarshalToString(this)
str, err := WasmpluginMarshaler.MarshalToString(this)
return []byte(str), err
}
// UnmarshalJSON is a custom unmarshaler for MatchRule
func (this *MatchRule) UnmarshalJSON(b []byte) error {
return WasmUnmarshaler.Unmarshal(bytes.NewReader(b), this)
return WasmpluginUnmarshaler.Unmarshal(bytes.NewReader(b), this)
}
// MarshalJSON is a custom marshaler for VmConfig
func (this *VmConfig) MarshalJSON() ([]byte, error) {
str, err := WasmMarshaler.MarshalToString(this)
str, err := WasmpluginMarshaler.MarshalToString(this)
return []byte(str), err
}
// UnmarshalJSON is a custom unmarshaler for VmConfig
func (this *VmConfig) UnmarshalJSON(b []byte) error {
return WasmUnmarshaler.Unmarshal(bytes.NewReader(b), this)
return WasmpluginUnmarshaler.Unmarshal(bytes.NewReader(b), this)
}
// MarshalJSON is a custom marshaler for EnvVar
func (this *EnvVar) MarshalJSON() ([]byte, error) {
str, err := WasmMarshaler.MarshalToString(this)
str, err := WasmpluginMarshaler.MarshalToString(this)
return []byte(str), err
}
// UnmarshalJSON is a custom unmarshaler for EnvVar
func (this *EnvVar) UnmarshalJSON(b []byte) error {
return WasmUnmarshaler.Unmarshal(bytes.NewReader(b), this)
return WasmpluginUnmarshaler.Unmarshal(bytes.NewReader(b), this)
}
var (
WasmMarshaler = &jsonpb.Marshaler{}
WasmUnmarshaler = &jsonpb.Unmarshaler{AllowUnknownFields: true}
WasmpluginMarshaler = &jsonpb.Marshaler{}
WasmpluginUnmarshaler = &jsonpb.Unmarshaler{AllowUnknownFields: true}
)

View File

@@ -284,6 +284,10 @@ spec:
type: string
port:
type: integer
protocol:
type: string
sni:
type: string
type:
type: string
zkServicesPath:

View File

@@ -126,6 +126,8 @@ type RegistryConfig struct {
ConsulServiceTag string `protobuf:"bytes,15,opt,name=consulServiceTag,proto3" json:"consulServiceTag,omitempty"`
ConsulRefreshInterval int64 `protobuf:"varint,16,opt,name=consulRefreshInterval,proto3" json:"consulRefreshInterval,omitempty"`
AuthSecretName string `protobuf:"bytes,17,opt,name=authSecretName,proto3" json:"authSecretName,omitempty"`
Protocol string `protobuf:"bytes,18,opt,name=protocol,proto3" json:"protocol,omitempty"`
Sni string `protobuf:"bytes,19,opt,name=sni,proto3" json:"sni,omitempty"`
}
func (x *RegistryConfig) Reset() {
@@ -279,6 +281,20 @@ func (x *RegistryConfig) GetAuthSecretName() string {
return ""
}
func (x *RegistryConfig) GetProtocol() string {
if x != nil {
return x.Protocol
}
return ""
}
func (x *RegistryConfig) GetSni() string {
if x != nil {
return x.Sni
}
return ""
}
var File_networking_v1_mcp_bridge_proto protoreflect.FileDescriptor
var file_networking_v1_mcp_bridge_proto_rawDesc = []byte{
@@ -292,7 +308,7 @@ var file_networking_v1_mcp_bridge_proto_rawDesc = []byte{
0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x68, 0x69, 0x67, 0x72,
0x65, 0x73, 0x73, 0x2e, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x2e, 0x76,
0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
0x52, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xa5, 0x05, 0x0a,
0x52, 0x0a, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x69, 0x65, 0x73, 0x22, 0xd3, 0x05, 0x0a,
0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12,
0x17, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x03, 0xe0,
0x41, 0x02, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
@@ -335,10 +351,13 @@ var file_networking_v1_mcp_bridge_proto_rawDesc = []byte{
0x72, 0x65, 0x73, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x26, 0x0a, 0x0e,
0x61, 0x75, 0x74, 0x68, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x18, 0x11,
0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x61, 0x75, 0x74, 0x68, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74,
0x4e, 0x61, 0x6d, 0x65, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,
0x6f, 0x6d, 0x2f, 0x61, 0x6c, 0x69, 0x62, 0x61, 0x62, 0x61, 0x2f, 0x68, 0x69, 0x67, 0x72, 0x65,
0x73, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e,
0x67, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x4e, 0x61, 0x6d, 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c,
0x12, 0x10, 0x0a, 0x03, 0x73, 0x6e, 0x69, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x73,
0x6e, 0x69, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
0x2f, 0x61, 0x6c, 0x69, 0x62, 0x61, 0x62, 0x61, 0x2f, 0x68, 0x69, 0x67, 0x72, 0x65, 0x73, 0x73,
0x2f, 0x61, 0x70, 0x69, 0x2f, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x69, 0x6e, 0x67, 0x2f,
0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@@ -64,4 +64,6 @@ message RegistryConfig {
string consulServiceTag = 15;
int64 consulRefreshInterval = 16;
string authSecretName = 17;
string protocol = 18;
string sni = 19;
}

3
go.mod
View File

@@ -119,7 +119,6 @@ require (
github.com/google/uuid v1.3.1 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.2.5 // indirect
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
@@ -176,13 +175,13 @@ require (
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/prometheus/prometheus v0.45.0 // indirect
github.com/ryanuber/go-glob v1.0.0 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
github.com/shopspring/decimal v1.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spaolacci/murmur3 v1.1.0 // indirect
github.com/spf13/cast v1.5.1 // indirect
github.com/stoewer/go-strcase v1.3.0 // indirect
github.com/tetratelabs/wazero v1.7.3 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.0 // indirect
github.com/toolkits/concurrent v0.0.0-20150624120057-a4371d70e3e3 // indirect

3
go.sum
View File

@@ -1623,7 +1623,6 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w=
github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I=
@@ -1705,6 +1704,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/tebeka/strftime v0.1.3 h1:5HQXOqWKYRFfNyBMNVc9z5+QzuBtIXy03psIhtdJYto=
github.com/tebeka/strftime v0.1.3/go.mod h1:7wJm3dZlpr4l/oVK0t1HYIc4rMzQ2XJlOMIUJUJH6XQ=
github.com/tetratelabs/wazero v1.7.3 h1:PBH5KVahrt3S2AHgEjKu4u+LlDbbk+nsGE3KLucy6Rw=
github.com/tetratelabs/wazero v1.7.3/go.mod h1:ytl6Zuh20R/eROuyDaGPkp82O9C/DJfXAwJfQ3X6/7Y=
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=

View File

@@ -1,5 +1,5 @@
apiVersion: v2
appVersion: 2.0.0-rc.1
appVersion: 2.0.3
description: Helm chart for deploying higress gateways
icon: https://higress.io/img/higress_logo_small.png
home: http://higress.io/
@@ -10,4 +10,4 @@ name: higress-core
sources:
- http://github.com/alibaba/higress
type: application
version: 2.0.0-rc.1
version: 2.0.3

View File

@@ -284,6 +284,10 @@ spec:
type: string
port:
type: integer
protocol:
type: string
sni:
type: string
type:
type: string
zkServicesPath:
@@ -302,3 +306,4 @@ spec:
subresources:
status: {}
---

View File

@@ -1,3 +1,4 @@
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:

View File

@@ -101,3 +101,15 @@ higress: {{ include "controller.name" . }}
true
{{- end }}
{{- end }}
{{- define "gateway.podMonitor.gvk" -}}
{{- if eq .Values.gateway.metrics.provider "monitoring.coreos.com" -}}
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
{{- else if eq .Values.gateway.metrics.provider "operator.victoriametrics.com" -}}
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMPodScrape
{{- else -}}
{{- fail "unexpected gateway.metrics.provider" -}}
{{- end -}}
{{- end -}}

View File

@@ -0,0 +1,326 @@
{{/*
Rendering the pod template of gateway component.
*/}}
{{- define "gateway.podTemplate" -}}
{{- $o11y := .Values.global.o11y -}}
template:
metadata:
annotations:
{{- if .Values.global.enableHigressIstio }}
"enableHigressIstio": "true"
{{- end }}
{{- if .Values.gateway.podAnnotations }}
{{- toYaml .Values.gateway.podAnnotations | nindent 6 }}
{{- end }}
labels:
sidecar.istio.io/inject: "false"
{{- with .Values.gateway.revision }}
istio.io/rev: {{ . }}
{{- end }}
{{- include "gateway.selectorLabels" . | nindent 6 }}
spec:
{{- with .Values.gateway.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 6 }}
{{- end }}
serviceAccountName: {{ include "gateway.serviceAccountName" . }}
{{- if .Values.global.priorityClassName }}
priorityClassName: "{{ .Values.global.priorityClassName }}"
{{- end }}
securityContext:
{{- if .Values.gateway.securityContext }}
{{- toYaml .Values.gateway.securityContext | nindent 6 }}
{{- else if and .Values.gateway.unprivilegedPortSupported (and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion)) }}
# Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326
sysctls:
- name: net.ipv4.ip_unprivileged_port_start
value: "0"
{{- end }}
containers:
- name: higress-gateway
image: "{{ .Values.gateway.hub | default .Values.global.hub }}/{{ .Values.gateway.image | default "gateway" }}:{{ .Values.gateway.tag | default .Chart.AppVersion }}"
args:
- proxy
- router
- --domain
- $(POD_NAMESPACE).svc.cluster.local
- --proxyLogLevel=warning
- --proxyComponentLogLevel=misc:error
- --log_output_level=all:info
- --serviceCluster=higress-gateway
securityContext:
{{- if .Values.gateway.containerSecurityContext }}
{{- toYaml .Values.gateway.containerSecurityContext | nindent 10 }}
{{- else if and .Values.gateway.unprivilegedPortSupported (and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion)) }}
# Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326
capabilities:
drop:
- ALL
allowPrivilegeEscalation: false
privileged: false
# When enabling lite metrics, the configuration template files need to be replaced.
{{- if not .Values.global.liteMetrics }}
readOnlyRootFilesystem: true
{{- end }}
runAsUser: 1337
runAsGroup: 1337
runAsNonRoot: true
{{- else }}
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 0
runAsGroup: 1337
runAsNonRoot: false
allowPrivilegeEscalation: true
{{- end }}
env:
- name: NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: INSTANCE_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: HOST_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.hostIP
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
- name: PROXY_XDS_VIA_AGENT
value: "true"
- name: ENABLE_INGRESS_GATEWAY_SDS
value: "false"
- name: JWT_POLICY
value: {{ include "controller.jwtPolicy" . }}
- name: ISTIO_META_HTTP10
value: "1"
- name: ISTIO_META_CLUSTER_ID
value: "{{ $.Values.clusterName | default `Kubernetes` }}"
- name: INSTANCE_NAME
value: "higress-gateway"
{{- if .Values.global.liteMetrics }}
- name: LITE_METRICS
value: "on"
{{- end }}
{{- if include "skywalking.enabled" . }}
- name: ISTIO_BOOTSTRAP_OVERRIDE
value: /etc/istio/custom-bootstrap/custom_bootstrap.json
{{- end }}
{{- with .Values.gateway.networkGateway }}
- name: ISTIO_META_REQUESTED_NETWORK_VIEW
value: "{{.}}"
{{- end }}
{{- range $key, $val := .Values.env }}
- name: {{ $key }}
value: {{ $val | quote }}
{{- end }}
ports:
- containerPort: 15020
protocol: TCP
name: istio-prom
- containerPort: 15090
protocol: TCP
name: http-envoy-prom
{{- if or .Values.global.local .Values.global.kind }}
- containerPort: {{ .Values.gateway.httpPort }}
hostPort: {{ .Values.gateway.httpPort }}
name: http
protocol: TCP
- containerPort: {{ .Values.gateway.httpsPort }}
hostPort: {{ .Values.gateway.httpsPort }}
name: https
protocol: TCP
{{- end }}
readinessProbe:
failureThreshold: {{ .Values.gateway.readinessFailureThreshold }}
httpGet:
path: /healthz/ready
port: 15021
scheme: HTTP
initialDelaySeconds: {{ .Values.gateway.readinessInitialDelaySeconds }}
periodSeconds: {{ .Values.gateway.readinessPeriodSeconds }}
successThreshold: {{ .Values.gateway.readinessSuccessThreshold }}
timeoutSeconds: {{ .Values.gateway.readinessTimeoutSeconds }}
{{- if not (or .Values.global.local .Values.global.kind) }}
resources:
{{- toYaml .Values.gateway.resources | nindent 10 }}
{{- end }}
volumeMounts:
- mountPath: /var/run/secrets/workload-spiffe-uds
name: workload-socket
- mountPath: /var/run/secrets/credential-uds
name: credential-socket
- mountPath: /var/run/secrets/workload-spiffe-credentials
name: workload-certs
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
- name: istio-token
mountPath: /var/run/secrets/tokens
readOnly: true
{{- end }}
- name: config
mountPath: /etc/istio/config
- name: higress-ca-root-cert
mountPath: /var/run/secrets/istio
- name: istio-data
mountPath: /var/lib/istio/data
- name: podinfo
mountPath: /etc/istio/pod
- name: proxy-socket
mountPath: /etc/istio/proxy
{{- if include "skywalking.enabled" . }}
- mountPath: /etc/istio/custom-bootstrap
name: custom-bootstrap-volume
{{- end }}
{{- if .Values.global.volumeWasmPlugins }}
- mountPath: /opt/plugins
name: local-wasmplugins-volume
{{- end }}
{{- if $o11y.enabled }}
- mountPath: /var/log/proxy
name: log
{{- end }}
{{- if $o11y.enabled }}
{{- $config := $o11y.promtail }}
- name: promtail
image: {{ $config.image.repository }}:{{ $config.image.tag }}
imagePullPolicy: IfNotPresent
args:
- -config.file=/etc/promtail/promtail.yaml
env:
- name: 'HOSTNAME'
valueFrom:
fieldRef:
fieldPath: 'spec.nodeName'
ports:
- containerPort: {{ $config.port }}
name: http-metrics
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /ready
port: {{ $config.port }}
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
volumeMounts:
- name: promtail-config
mountPath: "/etc/promtail"
- name: log
mountPath: /var/log/proxy
- name: tmp
mountPath: /tmp
{{- end }}
{{- if .Values.gateway.hostNetwork }}
hostNetwork: {{ .Values.gateway.hostNetwork }}
dnsPolicy: ClusterFirstWithHostNet
{{- end }}
{{- with .Values.gateway.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.gateway.affinity }}
affinity:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- with .Values.gateway.tolerations }}
tolerations:
{{- toYaml . | nindent 6 }}
{{- end }}
volumes:
- emptyDir: {}
name: workload-socket
- emptyDir: {}
name: credential-socket
- emptyDir: {}
name: workload-certs
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
- name: istio-token
projected:
sources:
- serviceAccountToken:
audience: istio-ca
expirationSeconds: 43200
path: istio-token
{{- end }}
- name: higress-ca-root-cert
configMap:
{{- if .Values.global.enableHigressIstio }}
name: istio-ca-root-cert
{{- else }}
name: higress-ca-root-cert
{{- end }}
- name: config
configMap:
name: higress-config
{{- if include "skywalking.enabled" . }}
- configMap:
defaultMode: 420
name: higress-custom-bootstrap
name: custom-bootstrap-volume
{{- end }}
- name: istio-data
emptyDir: {}
- name: proxy-socket
emptyDir: {}
{{- if $o11y.enabled }}
- name: log
emptyDir: {}
- name: tmp
emptyDir: {}
- name: promtail-config
configMap:
name: higress-promtail
{{- end }}
- name: podinfo
downwardAPI:
defaultMode: 420
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.labels
path: labels
- fieldRef:
apiVersion: v1
fieldPath: metadata.annotations
path: annotations
- path: cpu-request
resourceFieldRef:
containerName: higress-gateway
divisor: 1m
resource: requests.cpu
- path: cpu-limit
resourceFieldRef:
containerName: higress-gateway
divisor: 1m
resource: limits.cpu
{{- if .Values.global.volumeWasmPlugins }}
- name: local-wasmplugins-volume
hostPath:
path: /opt/plugins
type: Directory
{{- end }}
{{- end -}}

View File

@@ -116,6 +116,12 @@ data:
{{- $existingData = index $existingConfig.data "higress" | default "{}" | fromYaml }}
{{- end }}
{{- $newData := dict }}
{{- if hasKey .Values "upstream" }}
{{- $_ := set $newData "upstream" .Values.upstream }}
{{- end }}
{{- if hasKey .Values "downstream" }}
{{- $_ := set $newData "downstream" .Values.downstream }}
{{- end }}
{{- if and (hasKey .Values "tracing") .Values.tracing.enable }}
{{- $_ := set $newData "tracing" .Values.tracing }}
{{- end }}
@@ -155,44 +161,12 @@ data:
"transport_api_version": "V3",
"grpc_service": {
"envoy_grpc": {
"cluster_name": "service_skywalking"
"cluster_name": "outbound|{{ .Values.tracing.skywalking.port }}||{{ .Values.tracing.skywalking.service }}"
}
}
}
}
],
"static_resources": {
"clusters": [
{
"name": "service_skywalking",
"type": "LOGICAL_DNS",
"connect_timeout": "5s",
"http2_protocol_options": {
},
"dns_lookup_family": "V4_ONLY",
"lb_policy": "ROUND_ROBIN",
"load_assignment": {
"cluster_name": "service_skywalking",
"endpoints": [
{
"lb_endpoints": [
{
"endpoint": {
"address": {
"socket_address": {
"address": "{{ .Values.tracing.skywalking.service }}",
"port_value": "{{ .Values.tracing.skywalking.port }}"
}
}
}
}
]
}
]
}
}
]
}
]
}
---
{{- end }}

View File

@@ -129,3 +129,10 @@ rules:
- apiGroups: ["networking.internal.knative.dev"]
resources: ["ingresses/status"]
verbs: ["get","patch","update"]
# gateway api need
- apiGroups: ["apps"]
verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ]
resources: [ "deployments" ]
- apiGroups: [""]
verbs: [ "get", "watch", "list", "update", "patch", "create", "delete" ]
resources: [ "serviceaccounts"]

View File

@@ -69,6 +69,12 @@ spec:
fieldPath: spec.serviceAccountName
- name: DOMAIN_SUFFIX
value: {{ .Values.global.proxy.clusterDomain }}
- name: GATEWAY_NAME
value: {{ include "gateway.name" . }}
- name: PILOT_ENABLE_GATEWAY_API
value: "{{ .Values.global.enableGatewayAPI }}"
- name: PILOT_ENABLE_ALPHA_GATEWAY_API
value: "{{ .Values.global.enableGatewayAPI }}"
{{- if .Values.controller.env }}
{{- range $key, $val := .Values.controller.env }}
- name: {{ $key }}
@@ -133,6 +139,8 @@ spec:
env:
- name: PILOT_ENABLE_QUIC_LISTENERS
value: "true"
- name: VALIDATION_WEBHOOK_CONFIG_NAME
value: ""
- name: ISTIO_DUAL_STACK
value: "{{ .Values.global.enableIPv6 }}"
- name: PILOT_ENABLE_HEADLESS_SERVICE_POD_LISTENERS
@@ -213,14 +221,14 @@ spec:
- name: HIGRESS_ENABLE_ISTIO_API
value: "true"
{{- end }}
{{- if .Values.global.enableGatewayAPI }}
- name: PILOT_ENABLE_GATEWAY_API
value: "true"
value: "false"
- name: PILOT_ENABLE_ALPHA_GATEWAY_API
value: "false"
- name: PILOT_ENABLE_GATEWAY_API_STATUS
value: "true"
value: "false"
- name: PILOT_ENABLE_GATEWAY_API_DEPLOYMENT_CONTROLLER
value: "false"
{{- end }}
{{- if not .Values.global.enableHigressIstio }}
- name: CUSTOM_CA_CERT_NAME
value: "higress-ca-root-cert"

View File

@@ -6,10 +6,12 @@
{{- if $kernelVersion }}
{{- $kernelVersion = regexFind "^(\\d+\\.\\d+\\.\\d+)" $kernelVersion }}
{{- if and $kernelVersion (semverCompare "<4.11.0" $kernelVersion) }}
{{- $unprivilegedPortSupported = false }}
{{- $unprivilegedPortSupported = false }}
{{- end }}
{{- end }}
{{- end -}}
{{- $_ := set .Values.gateway "unprivilegedPortSupported" $unprivilegedPortSupported -}}
apiVersion: apps/v1
kind: DaemonSet
metadata:
@@ -23,310 +25,5 @@ spec:
selector:
matchLabels:
{{- include "gateway.selectorLabels" . | nindent 6 }}
template:
metadata:
annotations:
{{- if .Values.global.enableHigressIstio }}
"enableHigressIstio": "true"
{{- end }}
{{- if .Values.gateway.podAnnotations }}
{{- toYaml .Values.gateway.podAnnotations | nindent 8 }}
{{- end }}
labels:
sidecar.istio.io/inject: "false"
{{- with .Values.gateway.revision }}
istio.io/rev: {{ . }}
{{- end }}
{{- include "gateway.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.gateway.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "gateway.serviceAccountName" . }}
{{- if .Values.global.priorityClassName }}
priorityClassName: "{{ .Values.global.priorityClassName }}"
{{- end }}
securityContext:
{{- if .Values.gateway.securityContext }}
{{- toYaml .Values.gateway.securityContext | nindent 8 }}
{{- else if and $unprivilegedPortSupported (and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion)) }}
# Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326
sysctls:
- name: net.ipv4.ip_unprivileged_port_start
value: "0"
{{- end }}
containers:
{{- if $o11y.enabled }}
{{- $config := $o11y.promtail }}
- name: promtail
image: {{ $config.image.repository }}:{{ $config.image.tag }}
imagePullPolicy: IfNotPresent
args:
- -config.file=/etc/promtail/promtail.yaml
env:
- name: 'HOSTNAME'
valueFrom:
fieldRef:
fieldPath: 'spec.nodeName'
ports:
- containerPort: {{ $config.port }}
name: http-metrics
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /ready
port: {{ $config.port }}
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
volumeMounts:
- name: promtail-config
mountPath: "/etc/promtail"
- name: log
mountPath: /var/log/proxy
- name: tmp
mountPath: /tmp
{{- end }}
- name: higress-gateway
image: "{{ .Values.gateway.hub | default .Values.global.hub }}/{{ .Values.gateway.image | default "gateway" }}:{{ .Values.gateway.tag | default .Chart.AppVersion }}"
args:
- proxy
- router
- --domain
- $(POD_NAMESPACE).svc.cluster.local
- --proxyLogLevel=warning
- --proxyComponentLogLevel=misc:error
- --log_output_level=all:info
- --serviceCluster=higress-gateway
securityContext:
{{- if .Values.gateway.containerSecurityContext }}
{{- toYaml .Values.gateway.containerSecurityContext | nindent 12 }}
{{- else if and $unprivilegedPortSupported (and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion)) }}
# Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326
capabilities:
drop:
- ALL
allowPrivilegeEscalation: false
privileged: false
# When enabling lite metrics, the configuration template files need to be replaced.
{{- if not .Values.global.liteMetrics }}
readOnlyRootFilesystem: true
{{- end }}
runAsUser: 1337
runAsGroup: 1337
runAsNonRoot: true
{{- else }}
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 0
runAsGroup: 1337
runAsNonRoot: false
allowPrivilegeEscalation: true
{{- end }}
env:
- name: NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: INSTANCE_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: HOST_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.hostIP
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
- name: PILOT_XDS_SEND_TIMEOUT
value: 60s
- name: PROXY_XDS_VIA_AGENT
value: "true"
- name: ENABLE_INGRESS_GATEWAY_SDS
value: "false"
- name: JWT_POLICY
value: {{ include "controller.jwtPolicy" . }}
- name: ISTIO_META_HTTP10
value: "1"
- name: ISTIO_META_CLUSTER_ID
value: "{{ $.Values.clusterName | default `Kubernetes` }}"
- name: INSTANCE_NAME
value: "higress-gateway"
{{- if .Values.global.liteMetrics }}
- name: LITE_METRICS
value: "on"
{{- end }}
{{- if include "skywalking.enabled" . }}
- name: ISTIO_BOOTSTRAP_OVERRIDE
value: /etc/istio/custom-bootstrap/custom_bootstrap.json
{{- end }}
{{- with .Values.gateway.networkGateway }}
- name: ISTIO_META_REQUESTED_NETWORK_VIEW
value: "{{.}}"
{{- end }}
{{- range $key, $val := .Values.env }}
- name: {{ $key }}
value: {{ $val | quote }}
{{- end }}
ports:
- containerPort: 15090
protocol: TCP
name: http-envoy-prom
{{- if or .Values.global.local .Values.global.kind }}
- containerPort: {{ .Values.gateway.httpPort }}
hostPort: {{ .Values.gateway.httpPort }}
name: http
protocol: TCP
- containerPort: {{ .Values.gateway.httpsPort }}
hostPort: {{ .Values.gateway.httpsPort }}
name: https
protocol: TCP
{{- end }}
readinessProbe:
failureThreshold: {{ .Values.gateway.readinessFailureThreshold }}
httpGet:
path: /healthz/ready
port: 15021
scheme: HTTP
initialDelaySeconds: {{ .Values.gateway.readinessInitialDelaySeconds }}
periodSeconds: {{ .Values.gateway.readinessPeriodSeconds }}
successThreshold: {{ .Values.gateway.readinessSuccessThreshold }}
timeoutSeconds: {{ .Values.gateway.readinessTimeoutSeconds }}
{{- if not (or .Values.global.local .Values.global.kind) }}
resources:
{{- toYaml .Values.gateway.resources | nindent 12 }}
{{- end }}
volumeMounts:
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
- name: istio-token
mountPath: /var/run/secrets/tokens
readOnly: true
{{- end }}
- name: config
mountPath: /etc/istio/config
- name: istio-ca-root-cert
mountPath: /var/run/secrets/istio
- name: istio-data
mountPath: /var/lib/istio/data
- name: podinfo
mountPath: /etc/istio/pod
- name: proxy-socket
mountPath: /etc/istio/proxy
{{- if include "skywalking.enabled" . }}
- mountPath: /etc/istio/custom-bootstrap
name: custom-bootstrap-volume
{{- end }}
{{- if .Values.global.volumeWasmPlugins }}
- mountPath: /opt/plugins
name: local-wasmplugins-volume
{{- end }}
{{- if $o11y.enabled }}
- mountPath: /var/log/proxy
name: log
{{- end }}
{{- if .Values.gateway.hostNetwork }}
hostNetwork: {{ .Values.gateway.hostNetwork }}
dnsPolicy: ClusterFirstWithHostNet
{{- end }}
{{- with .Values.gateway.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.gateway.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.gateway.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
- name: istio-token
projected:
sources:
- serviceAccountToken:
audience: istio-ca
expirationSeconds: 43200
path: istio-token
{{- end }}
- name: istio-ca-root-cert
configMap:
{{- if .Values.global.enableHigressIstio }}
name: istio-ca-root-cert
{{- else }}
name: higress-ca-root-cert
{{- end }}
- name: config
configMap:
name: higress-config
{{- if include "skywalking.enabled" . }}
- configMap:
defaultMode: 420
name: higress-custom-bootstrap
name: custom-bootstrap-volume
{{- end }}
- name: istio-data
emptyDir: {}
- name: proxy-socket
emptyDir: {}
{{- if $o11y.enabled }}
- name: log
emptyDir: {}
- name: tmp
emptyDir: {}
- name: promtail-config
configMap:
name: higress-promtail
{{- end }}
- name: podinfo
downwardAPI:
defaultMode: 420
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.labels
path: labels
- fieldRef:
apiVersion: v1
fieldPath: metadata.annotations
path: annotations
- path: cpu-request
resourceFieldRef:
containerName: higress-gateway
divisor: 1m
resource: requests.cpu
- path: cpu-limit
resourceFieldRef:
containerName: higress-gateway
divisor: 1m
resource: limits.cpu
{{- if .Values.global.volumeWasmPlugins }}
- name: local-wasmplugins-volume
hostPath:
path: /opt/plugins
type: Directory
{{- end }}
{{- include "gateway.podTemplate" $ | nindent 2 -}}
{{- end }}

View File

@@ -1,15 +1,16 @@
{{- if eq .Values.gateway.kind "Deployment" -}}
{{- $o11y := .Values.global.o11y }}
{{- $unprivilegedPortSupported := true }}
{{- range $index, $node := (lookup "v1" "Node" "default" "").items }}
{{- $kernelVersion := $node.status.nodeInfo.kernelVersion }}
{{- if $kernelVersion }}
{{- $kernelVersion = regexFind "^(\\d+\\.\\d+\\.\\d+)" $kernelVersion }}
{{- if and $kernelVersion (semverCompare "<4.11.0" $kernelVersion) }}
{{- $unprivilegedPortSupported = false }}
{{- $unprivilegedPortSupported = false }}
{{- end }}
{{- end }}
{{- end -}}
{{- $_ := set .Values.gateway "unprivilegedPortSupported" $unprivilegedPortSupported -}}
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -38,311 +39,7 @@ spec:
{{- else }}
maxUnavailable: {{ .Values.gateway.rollingMaxUnavailable }}
{{- end }}
template:
metadata:
annotations:
{{- if .Values.global.enableHigressIstio }}
"enableHigressIstio": "true"
{{- end }}
{{- if .Values.gateway.podAnnotations }}
{{- toYaml .Values.gateway.podAnnotations | nindent 8 }}
{{- end }}
labels:
sidecar.istio.io/inject: "false"
{{- with .Values.gateway.revision }}
istio.io/rev: {{ . }}
{{- end }}
{{- include "gateway.selectorLabels" . | nindent 8 }}
spec:
{{- with .Values.gateway.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 8 }}
{{- end }}
serviceAccountName: {{ include "gateway.serviceAccountName" . }}
{{- if .Values.global.priorityClassName }}
priorityClassName: "{{ .Values.global.priorityClassName }}"
{{- end }}
securityContext:
{{- if .Values.gateway.securityContext }}
{{- toYaml .Values.gateway.securityContext | nindent 8 }}
{{- else if and $unprivilegedPortSupported (and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion)) }}
# Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326
sysctls:
- name: net.ipv4.ip_unprivileged_port_start
value: "0"
{{- end }}
containers:
{{- if $o11y.enabled }}
{{- $config := $o11y.promtail }}
- name: promtail
image: {{ $config.image.repository }}:{{ $config.image.tag }}
imagePullPolicy: IfNotPresent
args:
- -config.file=/etc/promtail/promtail.yaml
env:
- name: 'HOSTNAME'
valueFrom:
fieldRef:
fieldPath: 'spec.nodeName'
ports:
- containerPort: {{ $config.port }}
name: http-metrics
protocol: TCP
readinessProbe:
failureThreshold: 3
httpGet:
path: /ready
port: {{ $config.port }}
scheme: HTTP
initialDelaySeconds: 10
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 1
volumeMounts:
- name: promtail-config
mountPath: "/etc/promtail"
- name: log
mountPath: /var/log/proxy
- name: tmp
mountPath: /tmp
{{- end }}
- name: higress-gateway
image: "{{ .Values.gateway.hub | default .Values.global.hub }}/{{ .Values.gateway.image | default "gateway" }}:{{ .Values.gateway.tag | default .Chart.AppVersion }}"
args:
- proxy
- router
- --domain
- $(POD_NAMESPACE).svc.cluster.local
- --proxyLogLevel=warning
- --proxyComponentLogLevel=misc:error
- --log_output_level=all:info
- --serviceCluster=higress-gateway
securityContext:
{{- if .Values.gateway.containerSecurityContext }}
{{- toYaml .Values.gateway.containerSecurityContext | nindent 12 }}
{{- else if and $unprivilegedPortSupported (and (not .Values.gateway.hostNetwork) (semverCompare ">=1.22-0" .Capabilities.KubeVersion.GitVersion)) }}
# Safe since 1.22: https://github.com/kubernetes/kubernetes/pull/103326
capabilities:
drop:
- ALL
allowPrivilegeEscalation: false
privileged: false
# When enabling lite metrics, the configuration template files need to be replaced.
{{- if not .Values.global.liteMetrics }}
readOnlyRootFilesystem: true
{{- end }}
runAsUser: 1337
runAsGroup: 1337
runAsNonRoot: true
{{- else }}
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
runAsUser: 0
runAsGroup: 1337
runAsNonRoot: false
allowPrivilegeEscalation: true
{{- end }}
env:
- name: NODE_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: spec.nodeName
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: INSTANCE_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.podIP
- name: HOST_IP
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: status.hostIP
- name: SERVICE_ACCOUNT
valueFrom:
fieldRef:
fieldPath: spec.serviceAccountName
- name: PROXY_XDS_VIA_AGENT
value: "true"
- name: ENABLE_INGRESS_GATEWAY_SDS
value: "false"
- name: JWT_POLICY
value: {{ include "controller.jwtPolicy" . }}
- name: ISTIO_META_HTTP10
value: "1"
- name: ISTIO_META_CLUSTER_ID
value: "{{ $.Values.clusterName | default `Kubernetes` }}"
- name: INSTANCE_NAME
value: "higress-gateway"
{{- if .Values.global.liteMetrics }}
- name: LITE_METRICS
value: "on"
{{- end }}
{{- if include "skywalking.enabled" . }}
- name: ISTIO_BOOTSTRAP_OVERRIDE
value: /etc/istio/custom-bootstrap/custom_bootstrap.json
{{- end }}
{{- with .Values.gateway.networkGateway }}
- name: ISTIO_META_REQUESTED_NETWORK_VIEW
value: "{{.}}"
{{- end }}
{{- range $key, $val := .Values.env }}
- name: {{ $key }}
value: {{ $val | quote }}
{{- end }}
ports:
- containerPort: 15020
protocol: TCP
name: istio-prom
- containerPort: 15090
protocol: TCP
name: http-envoy-prom
{{- if or .Values.global.local .Values.global.kind }}
- containerPort: {{ .Values.gateway.httpPort }}
hostPort: {{ .Values.gateway.httpPort }}
name: http
protocol: TCP
- containerPort: {{ .Values.gateway.httpsPort }}
hostPort: {{ .Values.gateway.httpsPort }}
name: https
protocol: TCP
{{- end }}
readinessProbe:
failureThreshold: {{ .Values.gateway.readinessFailureThreshold }}
httpGet:
path: /healthz/ready
port: 15021
scheme: HTTP
initialDelaySeconds: {{ .Values.gateway.readinessInitialDelaySeconds }}
periodSeconds: {{ .Values.gateway.readinessPeriodSeconds }}
successThreshold: {{ .Values.gateway.readinessSuccessThreshold }}
timeoutSeconds: {{ .Values.gateway.readinessTimeoutSeconds }}
{{- if not (or .Values.global.local .Values.global.kind) }}
resources:
{{- toYaml .Values.gateway.resources | nindent 12 }}
{{- end }}
volumeMounts:
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
- name: istio-token
mountPath: /var/run/secrets/tokens
readOnly: true
{{- end }}
- name: config
mountPath: /etc/istio/config
- name: istio-ca-root-cert
mountPath: /var/run/secrets/istio
- name: istio-data
mountPath: /var/lib/istio/data
- name: podinfo
mountPath: /etc/istio/pod
- name: proxy-socket
mountPath: /etc/istio/proxy
{{- if include "skywalking.enabled" . }}
- mountPath: /etc/istio/custom-bootstrap
name: custom-bootstrap-volume
{{- end }}
{{- if .Values.global.volumeWasmPlugins }}
- mountPath: /opt/plugins
name: local-wasmplugins-volume
{{- end }}
{{- if $o11y.enabled }}
- mountPath: /var/log/proxy
name: log
{{- end }}
{{- if .Values.gateway.hostNetwork }}
hostNetwork: {{ .Values.gateway.hostNetwork }}
dnsPolicy: ClusterFirstWithHostNet
{{- end }}
{{- with .Values.gateway.nodeSelector }}
nodeSelector:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.gateway.affinity }}
affinity:
{{- toYaml . | nindent 8 }}
{{- end }}
{{- with .Values.gateway.tolerations }}
tolerations:
{{- toYaml . | nindent 8 }}
{{- end }}
volumes:
{{- if eq (include "controller.jwtPolicy" .) "third-party-jwt" }}
- name: istio-token
projected:
sources:
- serviceAccountToken:
audience: istio-ca
expirationSeconds: 43200
path: istio-token
{{- end }}
- name: istio-ca-root-cert
configMap:
{{- if .Values.global.enableHigressIstio }}
name: istio-ca-root-cert
{{- else }}
name: higress-ca-root-cert
{{- end }}
- name: config
configMap:
name: higress-config
{{- if include "skywalking.enabled" . }}
- configMap:
defaultMode: 420
name: higress-custom-bootstrap
name: custom-bootstrap-volume
{{- end }}
- name: istio-data
emptyDir: {}
- name: proxy-socket
emptyDir: {}
{{- if $o11y.enabled }}
- name: log
emptyDir: {}
- name: tmp
emptyDir: {}
- name: promtail-config
configMap:
name: higress-promtail
{{- end }}
- name: podinfo
downwardAPI:
defaultMode: 420
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.labels
path: labels
- fieldRef:
apiVersion: v1
fieldPath: metadata.annotations
path: annotations
- path: cpu-request
resourceFieldRef:
containerName: higress-gateway
divisor: 1m
resource: requests.cpu
- path: cpu-limit
resourceFieldRef:
containerName: higress-gateway
divisor: 1m
resource: limits.cpu
{{- if .Values.global.volumeWasmPlugins }}
- name: local-wasmplugins-volume
hostPath:
path: /opt/plugins
type: Directory
{{- end }}
{{- include "gateway.podTemplate" $ | nindent 2 -}}
{{- end }}

View File

@@ -0,0 +1,22 @@
apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
name: {{ include "gateway.name" . }}-global-custom-response
namespace: {{ .Release.Namespace }}
labels:
{{- include "gateway.labels" . | nindent 4}}
spec:
configPatches:
- applyTo: HTTP_FILTER
match:
context: GATEWAY
listener:
filterChain:
filter:
name: envoy.filters.network.http_connection_manager
patch:
operation: INSERT_FIRST
value:
name: envoy.filters.http.custom_response
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.custom_response.v3.CustomResponse

View File

@@ -1,6 +1,8 @@
{{- if .Values.global.ingressClass }}
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
name: {{ .Values.global.ingressClass }}
spec:
controller: higress.io/higress-controller
controller: higress.io/higress-controller
{{- end }}

View File

@@ -0,0 +1,45 @@
{{- if .Values.gateway.metrics.enabled }}
{{- include "gateway.podMonitor.gvk" . }}
metadata:
name: {{ printf "%s-metrics" (include "gateway.name" .) | trunc 63 | trimSuffix "-" }}
namespace: {{ .Release.Namespace }}
labels:
{{- include "gateway.labels" . | nindent 4}}
annotations:
{{- .Values.gateway.annotations | toYaml | nindent 4 }}
spec:
jobLabel: "app.kubernetes.io/name"
selector:
matchLabels:
{{- include "gateway.selectorLabels" . | nindent 6 }}
namespaceSelector:
matchNames:
- {{ .Release.Namespace }}
podMetricsEndpoints:
- port: istio-prom
path: /stats/prometheus
{{- if .Values.gateway.metrics.interval }}
interval: {{ .Values.gateway.metrics.interval }}
{{- end }}
{{- if .Values.gateway.metrics.scrapeTimeout }}
scrapeTimeout: {{ .Values.gateway.metrics.scrapeTimeout }}
{{- end }}
{{- if .Values.gateway.metrics.honorLabels }}
honorLabels: {{ .Values.gateway.metrics.honorLabels }}
{{- end }}
{{- if .Values.gateway.metrics.metricRelabelings }}
metricRelabelings: {{ toYaml .Values.gateway.metrics.metricRelabelings | nindent 8 }}
{{- end }}
{{- if .Values.gateway.metrics.relabelings }}
relabelings: {{ toYaml .Values.gateway.metrics.relabelings | nindent 8 }}
{{- end }}
{{- if .Values.gateway.metrics.metricRelabelConfigs }}
metricRelabelings: {{ toYaml .Values.gateway.metrics.metricRelabelConfigs | nindent 8 }}
{{- end }}
{{- if .Values.gateway.metrics.relabelConfigs }}
relabelings: {{ toYaml .Values.gateway.metrics.relabelConfigs | nindent 8 }}
{{- end }}
{{- if $.Values.gateway.metrics.rawSpec }}
{{- $.Values.gateway.metrics.rawSpec | toYaml | nindent 6 }}
{{- end }}
{{- end }}

View File

@@ -24,6 +24,9 @@ spec:
{{- end }}
{{- with .Values.gateway.service.externalTrafficPolicy }}
externalTrafficPolicy: "{{ . }}"
{{- end }}
{{- with .Values.gateway.service.loadBalancerClass}}
loadBalancerClass: "{{ . }}"
{{- end }}
type: {{ .Values.gateway.service.type }}
ports:

View File

@@ -26,7 +26,7 @@ global:
autoscalingv2API: true
local: false # When deploying to a local cluster (e.g.: kind cluster), set this to true.
kind: false # Deprecated. Please use "global.local" instead. Will be removed later.
enableIstioAPI: false
enableIstioAPI: true
enableGatewayAPI: false
# Deprecated
enableHigressIstio: false
@@ -136,7 +136,6 @@ global:
excludeInboundPorts: ""
includeInboundPorts: "*"
# istio egress capture allowlist
# https://istio.io/docs/tasks/traffic-management/egress.html#calling-external-services-directly
# example: includeIPRanges: "172.30.0.0/16,172.20.0.0/16"
@@ -322,8 +321,8 @@ global:
# Host:Port for submitting traces to the Datadog agent.
address: "$(HOST_IP):8126"
lightstep:
address: "" # example: lightstep-satellite:443
accessToken: "" # example: abcdefg1234567
address: "" # example: lightstep-satellite:443
accessToken: "" # example: abcdefg1234567
stackdriver:
# enables trace output to stdout.
debug: false
@@ -449,25 +448,25 @@ gateway:
prometheus.io/scrape: "true"
prometheus.io/path: "/stats/prometheus"
sidecar.istio.io/inject: "false"
# Define the security context for the pod.
# If unset, this will be automatically set to the minimum privileges required to bind to port 80 and 443.
# On Kubernetes 1.22+, this only requires the `net.ipv4.ip_unprivileged_port_start` sysctl.
securityContext: ~
containerSecurityContext: ~
service:
# Type of service. Set to "None" to disable the service entirely
type: LoadBalancer
ports:
- name: http2
port: 80
protocol: TCP
targetPort: 80
- name: https
port: 443
protocol: TCP
targetPort: 443
- name: http2
port: 80
protocol: TCP
targetPort: 80
- name: https
port: 443
protocol: TCP
targetPort: 443
annotations: {}
loadBalancerIP: ""
loadBalancerClass: ""
@@ -476,7 +475,7 @@ gateway:
rollingMaxSurge: 100%
rollingMaxUnavailable: 25%
resources:
requests:
cpu: 2000m
@@ -484,22 +483,39 @@ gateway:
limits:
cpu: 2000m
memory: 2048Mi
autoscaling:
enabled: false
minReplicas: 1
maxReplicas: 5
targetCPUUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
# If specified, the gateway will act as a network gateway for the given network.
networkGateway: ""
metrics:
# If true, create PodMonitor or VMPodScrape for gateway
enabled: false
# provider group name for CustomResourceDefinition, can be monitoring.coreos.com or operator.victoriametrics.com
provider: monitoring.coreos.com
interval: ""
scrapeTimeout: ""
honorLabels: false
# for monitoring.coreos.com/v1.PodMonitor
metricRelabelings: []
relabelings: []
# for operator.victoriametrics.com/v1beta1.VMPodScrape
metricRelabelConfigs: []
relabelConfigs: []
# some more raw podMetricsEndpoints spec
rawSpec: {}
controller:
name: "higress-controller"
replicas: 1
@@ -510,22 +526,20 @@ controller:
env: {}
labels: {}
probe: {
httpGet: {
path: /ready,
port: 8888,
},
initialDelaySeconds: 1,
periodSeconds: 3,
timeoutSeconds: 5
}
probe:
{
httpGet: { path: /ready, port: 8888 },
initialDelaySeconds: 1,
periodSeconds: 3,
timeoutSeconds: 5,
}
imagePullSecrets: []
rbac:
create: true
serviceAccount:
# Specifies whether a service account should be created
create: true
@@ -534,37 +548,30 @@ controller:
# The name of the service account to use.
# If not set and create is true, a name is generated using the fullname template
name: ""
podAnnotations: {}
podSecurityContext: {}
podSecurityContext:
{}
# fsGroup: 2000
ports: [
{
"name": "http",
"protocol": "TCP",
"port": 8888,
"targetPort": 8888,
},
{
"name": "http-solver",
"protocol": "TCP",
"port": 8889,
"targetPort": 8889,
},
{
"name": "grpc",
"protocol": "TCP",
"port": 15051,
"targetPort": 15051,
}
]
ports:
[
{ "name": "http", "protocol": "TCP", "port": 8888, "targetPort": 8888 },
{
"name": "http-solver",
"protocol": "TCP",
"port": 8889,
"targetPort": 8889,
},
{ "name": "grpc", "protocol": "TCP", "port": 15051, "targetPort": 15051 },
]
service:
type: ClusterIP
securityContext: {}
securityContext:
{}
# capabilities:
# drop:
# - ALL
@@ -579,11 +586,11 @@ controller:
limits:
cpu: 1000m
memory: 2048Mi
nodeSelector: {}
tolerations: []
affinity: {}
autoscaling:
@@ -594,7 +601,7 @@ controller:
automaticHttps:
enabled: true
email: ""
## Discovery Settings
pilot:
autoscaleEnabled: false
@@ -656,7 +663,6 @@ pilot:
# Additional labels to apply to the deployment.
deploymentLabels: {}
## Mesh config settings
# Install the mesh config map, generated from values.yaml.
@@ -666,16 +672,31 @@ pilot:
# Additional labels to apply on the pod level for monitoring and logging configuration.
podLabels: {}
# Tracing config settings
tracing:
enable: false
sampling: 100
timeout: 500
skywalking:
# access_token: ""
service: ""
port: 11800
# access_token: ""
service: ""
port: 11800
# zipkin:
# service: ""
# port: 9411
# service: ""
# port: 9411
# Downstream config settings
downstream:
idleTimeout: 180
maxRequestHeadersKb: 60
connectionBufferLimits: 32768
http2:
maxConcurrentStreams: 100
initialStreamWindowSize: 65535
initialConnectionWindowSize: 1048576
routeTimeout: 0
# Upstream config settings
upstream:
idleTimeout: 10
connectionBufferLimits: 10485760

View File

@@ -1,9 +1,9 @@
dependencies:
- name: higress-core
repository: file://../core
version: 2.0.0-rc.1
version: 2.0.3
- name: higress-console
repository: https://higress.io/helm-charts/
version: 1.4.2
digest: sha256:10375e19aff1cc31e4d450ee5c3124ed684c8bcd2f4c019ea88abf7e3d381d76
generated: "2024-08-15T19:39:04.526398+08:00"
version: 1.4.5
digest: sha256:74b772113264168483961f5d0424459fd7359adc509a4b50400229581d7cddbf
generated: "2024-11-08T14:06:51.871719+08:00"

View File

@@ -1,5 +1,5 @@
apiVersion: v2
appVersion: 2.0.0-rc.1
appVersion: 2.0.3
description: Helm chart for deploying Higress gateways
icon: https://higress.io/img/higress_logo_small.png
home: http://higress.io/
@@ -12,9 +12,9 @@ sources:
dependencies:
- name: higress-core
repository: "file://../core"
version: 2.0.0-rc.1
version: 2.0.3
- name: higress-console
repository: "https://higress.io/helm-charts/"
version: 1.4.2
version: 1.4.5
type: application
version: 2.0.0-rc.1
version: 2.0.3

View File

@@ -114,6 +114,8 @@ static_resources:
value: |
{{ .JSONExample }}
- name: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
clusters:
- name: httpbin
connect_timeout: 30s

View File

@@ -21,7 +21,10 @@ type Protocol string
const (
TCP Protocol = "TCP"
HTTP Protocol = "HTTP"
HTTP2 Protocol = "HTTP2"
HTTPS Protocol = "HTTPS"
GRPC Protocol = "GRPC"
GRPCS Protocol = "GRPCS"
Dubbo Protocol = "Dubbo"
Unsupported Protocol = "UnsupportedProtocol"
)
@@ -32,8 +35,14 @@ func ParseProtocol(s string) Protocol {
return TCP
case "http":
return HTTP
case "https":
return HTTPS
case "http2":
return HTTP2
case "grpc", "triple", "tri":
return GRPC
case "grpcs":
return GRPCS
case "dubbo":
return Dubbo
}
@@ -51,7 +60,7 @@ func (p Protocol) IsTCP() bool {
func (p Protocol) IsHTTP() bool {
switch p {
case HTTP, GRPC:
case HTTP, GRPC, GRPCS, HTTP2, HTTPS:
return true
default:
return false
@@ -60,7 +69,16 @@ func (p Protocol) IsHTTP() bool {
func (p Protocol) IsGRPC() bool {
switch p {
case GRPC:
case GRPC, GRPCS:
return true
default:
return false
}
}
func (i Protocol) IsHTTPS() bool {
switch i {
case HTTPS, GRPCS:
return true
default:
return false

View File

@@ -23,3 +23,7 @@ const KnativeIngressCRDName = "ingresses.networking.internal.knative.dev"
const KnativeServicesCRDName = "services.serving.knative.dev"
const ManagedGatewayController = "higress.io/gateway-controller"
const RegistryTypeLabelKey = "higress-registry-type"
const RegistryNameLabelKey = "higress-registry-name"

View File

@@ -19,6 +19,7 @@ import "istio.io/pkg/env"
var (
PodNamespace = env.RegisterStringVar("POD_NAMESPACE", "higress-system", "").Get()
PodName = env.RegisterStringVar("POD_NAME", "", "").Get()
GatewayName = env.RegisterStringVar("GATEWAY_NAME", "higress-gateway", "").Get()
// Revision is the value of the Istio control plane revision, e.g. "canary",
// and is the value used by the "istio.io/rev" label.
Revision = env.Register("REVISION", "", "").Get()

View File

@@ -34,6 +34,7 @@ import (
extensions "istio.io/api/extensions/v1alpha1"
networking "istio.io/api/networking/v1alpha3"
istiotype "istio.io/api/type/v1beta1"
"istio.io/istio/pilot/pkg/features"
istiomodel "istio.io/istio/pilot/pkg/model"
"istio.io/istio/pilot/pkg/util/protoconv"
"istio.io/istio/pkg/cluster"
@@ -53,6 +54,7 @@ import (
extlisterv1 "github.com/alibaba/higress/client/pkg/listers/extensions/v1alpha1"
netlisterv1 "github.com/alibaba/higress/client/pkg/listers/networking/v1"
"github.com/alibaba/higress/pkg/cert"
higressconst "github.com/alibaba/higress/pkg/config/constants"
"github.com/alibaba/higress/pkg/ingress/kube/annotations"
"github.com/alibaba/higress/pkg/ingress/kube/common"
"github.com/alibaba/higress/pkg/ingress/kube/configmap"
@@ -234,8 +236,9 @@ func (m *IngressConfig) AddLocalCluster(options common.Options) {
ingressController = ingressv1.NewController(m.localKubeClient, m.localKubeClient, options, secretController)
}
m.remoteIngressControllers[options.ClusterId] = ingressController
m.remoteGatewayControllers[options.ClusterId] = gateway.NewController(m.localKubeClient, options)
if features.EnableGatewayAPI {
m.remoteGatewayControllers[options.ClusterId] = gateway.NewController(m.localKubeClient, options)
}
}
func (m *IngressConfig) List(typ config.GroupVersionKind, namespace string) []config.Config {
@@ -628,8 +631,8 @@ func (m *IngressConfig) convertServiceEntry([]common.WrapperConfig) []config.Con
if m.RegistryReconciler == nil {
return nil
}
serviceEntries := m.RegistryReconciler.GetAllServiceEntryWrapper()
IngressLog.Infof("Found http2rpc serviceEntries %s", serviceEntries)
serviceEntries := m.RegistryReconciler.GetAllServiceWrapper()
IngressLog.Infof("Found mcp serviceEntries %v", serviceEntries)
out := make([]config.Config, 0, len(serviceEntries))
for _, se := range serviceEntries {
out = append(out, config.Config{
@@ -638,6 +641,10 @@ func (m *IngressConfig) convertServiceEntry([]common.WrapperConfig) []config.Con
Name: se.ServiceEntry.Hosts[0],
Namespace: "mcp",
CreationTimestamp: se.GetCreateTime(),
Labels: map[string]string{
higressconst.RegistryTypeLabelKey: se.RegistryType,
higressconst.RegistryNameLabelKey: se.RegistryName,
},
},
Spec: se.ServiceEntry,
})
@@ -703,6 +710,32 @@ func (m *IngressConfig) convertDestinationRule(configs []common.WrapperConfig) [
destinationRules[serviceName] = dr
}
if m.RegistryReconciler != nil {
drws := m.RegistryReconciler.GetAllDestinationRuleWrapper()
IngressLog.Infof("Found mcp destinationRules: %v", drws)
for _, destinationRuleWrapper := range drws {
serviceName := destinationRuleWrapper.ServiceKey.ServiceFQDN
dr, exist := destinationRules[serviceName]
if !exist {
destinationRules[serviceName] = destinationRuleWrapper
} else if dr.DestinationRule.TrafficPolicy != nil {
portTrafficPolicy := destinationRuleWrapper.DestinationRule.TrafficPolicy.PortLevelSettings[0]
portUpdated := false
for _, policy := range dr.DestinationRule.TrafficPolicy.PortLevelSettings {
if policy.Port.Number == portTrafficPolicy.Port.Number {
policy.Tls = portTrafficPolicy.Tls
portUpdated = true
break
}
}
if portUpdated {
continue
}
dr.DestinationRule.TrafficPolicy.PortLevelSettings = append(dr.DestinationRule.TrafficPolicy.PortLevelSettings, portTrafficPolicy)
}
}
}
out := make([]config.Config, 0, len(destinationRules))
for _, dr := range destinationRules {
sort.SliceStable(dr.DestinationRule.TrafficPolicy.PortLevelSettings, func(i, j int) bool {
@@ -727,6 +760,7 @@ func (m *IngressConfig) convertDestinationRule(configs []common.WrapperConfig) [
Spec: dr.DestinationRule,
})
}
return out
}
@@ -1034,16 +1068,27 @@ func (m *IngressConfig) AddOrUpdateMcpBridge(clusterNamespacedName util.ClusterN
}
if m.RegistryReconciler == nil {
m.RegistryReconciler = reconcile.NewReconciler(func() {
metadata := config.Meta{
seMetadata := config.Meta{
Name: "mcpbridge-serviceentry",
Namespace: m.namespace,
GroupVersionKind: gvk.ServiceEntry,
// Set this label so that we do not compare configs and just push.
Labels: map[string]string{constants.AlwaysPushLabel: "true"},
}
drMetadata := config.Meta{
Name: "mcpbridge-destinationrule",
Namespace: m.namespace,
GroupVersionKind: gvk.DestinationRule,
// Set this label so that we do not compare configs and just push.
Labels: map[string]string{constants.AlwaysPushLabel: "true"},
}
for _, f := range m.serviceEntryHandlers {
IngressLog.Debug("McpBridge triggerd serviceEntry update")
f(config.Config{Meta: metadata}, config.Config{Meta: metadata}, istiomodel.EventUpdate)
f(config.Config{Meta: seMetadata}, config.Config{Meta: seMetadata}, istiomodel.EventUpdate)
}
for _, f := range m.destinationRuleHandlers {
IngressLog.Debug("McpBridge triggerd destinationRule update")
f(config.Config{Meta: drMetadata}, config.Config{Meta: drMetadata}, istiomodel.EventUpdate)
}
}, m.localKubeClient, m.namespace)
}
@@ -1489,7 +1534,7 @@ func constructBasicAuthEnvoyFilter(rules *common.BasicAuthRules, namespace strin
}, nil
}
func QueryByName(serviceEntries []*memory.ServiceEntryWrapper, serviceName string) (*memory.ServiceEntryWrapper, error) {
func QueryByName(serviceEntries []*memory.ServiceWrapper, serviceName string) (*memory.ServiceWrapper, error) {
IngressLog.Infof("Found http2rpc serviceEntries %s", serviceEntries)
for _, se := range serviceEntries {
if se.ServiceName == serviceName {
@@ -1499,7 +1544,7 @@ func QueryByName(serviceEntries []*memory.ServiceEntryWrapper, serviceName strin
return nil, fmt.Errorf("can't find ServiceEntry by serviceName:%v", serviceName)
}
func QueryRpcServiceVersion(serviceEntry *memory.ServiceEntryWrapper, serviceName string) (string, error) {
func QueryRpcServiceVersion(serviceEntry *memory.ServiceWrapper, serviceName string) (string, error) {
IngressLog.Infof("Found http2rpc serviceEntry %s", serviceEntry)
IngressLog.Infof("Found http2rpc ServiceEntry %s", serviceEntry.ServiceEntry)
IngressLog.Infof("Found http2rpc WorkloadSelector %s", serviceEntry.ServiceEntry.WorkloadSelector)

View File

@@ -69,6 +69,8 @@ type Ingress struct {
Auth *AuthConfig
Mirror *MirrorConfig
Destination *DestinationConfig
IgnoreCase *IgnoreCaseConfig
@@ -161,6 +163,7 @@ func NewAnnotationHandlerManager() AnnotationHandler {
localRateLimit{},
fallback{},
auth{},
mirror{},
destination{},
ignoreCaseMatching{},
match{},
@@ -182,6 +185,7 @@ func NewAnnotationHandlerManager() AnnotationHandler {
retry{},
localRateLimit{},
fallback{},
mirror{},
ignoreCaseMatching{},
match{},
headerControl{},

View File

@@ -0,0 +1,118 @@
// Copyright (c) 2023 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 annotations
import (
"github.com/alibaba/higress/pkg/ingress/kube/util"
. "github.com/alibaba/higress/pkg/ingress/log"
wrappers "google.golang.org/protobuf/types/known/wrapperspb"
networking "istio.io/api/networking/v1alpha3"
)
const (
mirrorTargetService = "mirror-target-service"
mirrorPercentage = "mirror-percentage"
)
var (
_ Parser = &mirror{}
_ RouteHandler = &mirror{}
)
type MirrorConfig struct {
util.ServiceInfo
Percentage *wrappers.DoubleValue
}
type mirror struct{}
func (m mirror) Parse(annotations Annotations, config *Ingress, globalContext *GlobalContext) error {
if !needMirror(annotations) {
return nil
}
target, err := annotations.ParseStringASAP(mirrorTargetService)
if err != nil {
IngressLog.Errorf("Get mirror target service fail, err: %v", err)
return nil
}
serviceInfo, err := util.ParseServiceInfo(target, config.Namespace)
if err != nil {
IngressLog.Errorf("Get mirror target service fail, err: %v", err)
return nil
}
serviceLister, exist := globalContext.ClusterServiceList[config.ClusterId]
if !exist {
IngressLog.Errorf("service lister of cluster %s doesn't exist", config.ClusterId)
return nil
}
service, err := serviceLister.Services(serviceInfo.Namespace).Get(serviceInfo.Name)
if err != nil {
IngressLog.Errorf("Mirror service %s/%s within ingress %s/%s is not found, with err: %v",
serviceInfo.Namespace, serviceInfo.Name, config.Namespace, config.Name, err)
return nil
}
if service == nil {
IngressLog.Errorf("service %s/%s within ingress %s/%s is empty value",
serviceInfo.Namespace, serviceInfo.Name, config.Namespace, config.Name)
return nil
}
if serviceInfo.Port == 0 {
// Use the first port
serviceInfo.Port = uint32(service.Spec.Ports[0].Port)
}
var percentage *wrappers.DoubleValue
if value, err := annotations.ParseIntASAP(mirrorPercentage); err == nil {
if value < 100 {
percentage = &wrappers.DoubleValue{
Value: float64(value),
}
}
}
config.Mirror = &MirrorConfig{
ServiceInfo: serviceInfo,
Percentage: percentage,
}
return nil
}
func (m mirror) ApplyRoute(route *networking.HTTPRoute, config *Ingress) {
if config.Mirror == nil {
return
}
route.Mirror = &networking.Destination{
Host: util.CreateServiceFQDN(config.Mirror.Namespace, config.Mirror.Name),
Port: &networking.PortSelector{
Number: config.Mirror.Port,
},
}
if config.Mirror.Percentage != nil {
route.MirrorPercentage = &networking.Percent{
Value: config.Mirror.Percentage.GetValue(),
}
}
}
func needMirror(annotations Annotations) bool {
return annotations.HasASAP(mirrorTargetService)
}

View File

@@ -0,0 +1,163 @@
// 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 annotations
import (
"github.com/alibaba/higress/pkg/ingress/kube/util"
"github.com/golang/protobuf/proto"
networking "istio.io/api/networking/v1alpha3"
"istio.io/istio/pilot/pkg/model"
"reflect"
"testing"
)
func TestParseMirror(t *testing.T) {
testCases := []struct {
input []map[string]string
expect *MirrorConfig
}{
{},
{
input: []map[string]string{
{buildHigressAnnotationKey(mirrorTargetService): "test/app"},
{buildNginxAnnotationKey(mirrorTargetService): "test/app"},
},
expect: &MirrorConfig{
ServiceInfo: util.ServiceInfo{
NamespacedName: model.NamespacedName{
Namespace: "test",
Name: "app",
},
Port: 80,
},
},
},
{
input: []map[string]string{
{buildHigressAnnotationKey(mirrorTargetService): "test/app:8080"},
{buildNginxAnnotationKey(mirrorTargetService): "test/app:8080"},
},
expect: &MirrorConfig{
ServiceInfo: util.ServiceInfo{
NamespacedName: model.NamespacedName{
Namespace: "test",
Name: "app",
},
Port: 8080,
},
},
},
{
input: []map[string]string{
{buildHigressAnnotationKey(mirrorTargetService): "test/app:hi"},
{buildNginxAnnotationKey(mirrorTargetService): "test/app:hi"},
},
expect: &MirrorConfig{
ServiceInfo: util.ServiceInfo{
NamespacedName: model.NamespacedName{
Namespace: "test",
Name: "app",
},
Port: 80,
},
},
},
{
input: []map[string]string{
{buildHigressAnnotationKey(mirrorTargetService): "test/app"},
{buildNginxAnnotationKey(mirrorTargetService): "test/app"},
},
expect: &MirrorConfig{
ServiceInfo: util.ServiceInfo{
NamespacedName: model.NamespacedName{
Namespace: "test",
Name: "app",
},
Port: 80,
},
},
},
}
mirror := mirror{}
for _, testCase := range testCases {
t.Run("", func(t *testing.T) {
config := &Ingress{
Meta: Meta{
Namespace: "test",
ClusterId: "cluster",
},
}
globalContext, cancel := initGlobalContextForService()
defer cancel()
for _, in := range testCase.input {
_ = mirror.Parse(in, config, globalContext)
if !reflect.DeepEqual(testCase.expect, config.Mirror) {
t.Log("expect:", *testCase.expect)
t.Log("actual:", *config.Mirror)
t.Fatal("Should be equal")
}
}
})
}
}
func TestMirror_ApplyRoute(t *testing.T) {
testCases := []struct {
config *Ingress
input *networking.HTTPRoute
expect *networking.HTTPRoute
}{
{
config: &Ingress{},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{},
},
{
config: &Ingress{
Mirror: &MirrorConfig{
ServiceInfo: util.ServiceInfo{
NamespacedName: model.NamespacedName{
Namespace: "default",
Name: "test",
},
Port: 8080,
},
},
},
input: &networking.HTTPRoute{},
expect: &networking.HTTPRoute{
Mirror: &networking.Destination{
Host: "test.default.svc.cluster.local",
Port: &networking.PortSelector{
Number: 8080,
},
},
},
},
}
mirror := mirror{}
for _, testCase := range testCases {
t.Run("", func(t *testing.T) {
mirror.ApplyRoute(testCase.input, testCase.config)
if !proto.Equal(testCase.input, testCase.expect) {
t.Fatal("Must be equal.")
}
})
}
}

View File

@@ -52,6 +52,15 @@ type WrapperGateway struct {
Host string
}
func CreateMcpServiceKey(host string, portNumber int32) ServiceKey {
return ServiceKey{
Namespace: "mcp",
Name: host,
ServiceFQDN: host,
Port: portNumber,
}
}
func (w *WrapperGateway) IsHTTPS() bool {
if w.Gateway == nil || len(w.Gateway.Servers) == 0 {
return false

View File

@@ -255,6 +255,59 @@ func (t *TracingController) ConstructEnvoyFilters() ([]*config.Config, error) {
return configs, nil
}
configPatches := []*networking.EnvoyFilter_EnvoyConfigObjectPatch{
{
ApplyTo: networking.EnvoyFilter_NETWORK_FILTER,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{
Listener: &networking.EnvoyFilter_ListenerMatch{
FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{
Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{
Name: "envoy.filters.network.http_connection_manager",
},
},
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_MERGE,
Value: util.BuildPatchStruct(tracingConfig),
},
},
{
ApplyTo: networking.EnvoyFilter_HTTP_FILTER,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{
Listener: &networking.EnvoyFilter_ListenerMatch{
FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{
Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{
Name: "envoy.filters.network.http_connection_manager",
SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{
Name: "envoy.filters.http.router",
},
},
},
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_MERGE,
Value: util.BuildPatchStruct(`{
"name":"envoy.filters.http.router",
"typed_config":{
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router",
"start_child_span": true
}
}`),
},
},
}
patches := t.constructTracingExtendPatches(tracing)
configPatches = append(configPatches, patches...)
config := &config.Config{
Meta: config.Meta{
GroupVersionKind: gvk.EnvoyFilter,
@@ -262,55 +315,7 @@ func (t *TracingController) ConstructEnvoyFilters() ([]*config.Config, error) {
Namespace: namespace,
},
Spec: &networking.EnvoyFilter{
ConfigPatches: []*networking.EnvoyFilter_EnvoyConfigObjectPatch{
{
ApplyTo: networking.EnvoyFilter_NETWORK_FILTER,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{
Listener: &networking.EnvoyFilter_ListenerMatch{
FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{
Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{
Name: "envoy.filters.network.http_connection_manager",
},
},
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_MERGE,
Value: util.BuildPatchStruct(tracingConfig),
},
},
{
ApplyTo: networking.EnvoyFilter_HTTP_FILTER,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Listener{
Listener: &networking.EnvoyFilter_ListenerMatch{
FilterChain: &networking.EnvoyFilter_ListenerMatch_FilterChainMatch{
Filter: &networking.EnvoyFilter_ListenerMatch_FilterMatch{
Name: "envoy.filters.network.http_connection_manager",
SubFilter: &networking.EnvoyFilter_ListenerMatch_SubFilterMatch{
Name: "envoy.filters.http.router",
},
},
},
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_MERGE,
Value: util.BuildPatchStruct(`{
"name":"envoy.filters.http.router",
"typed_config":{
"@type": "type.googleapis.com/envoy.extensions.filters.http.router.v3.Router",
"start_child_span": true
}
}`),
},
},
},
ConfigPatches: configPatches,
},
}
@@ -318,6 +323,52 @@ func (t *TracingController) ConstructEnvoyFilters() ([]*config.Config, error) {
return configs, nil
}
func tracingClusterName(port, service string) string {
return fmt.Sprintf("outbound|%s||%s", port, service)
}
func (t *TracingController) constructHTTP2ProtocolOptionsPatch(port, service string) *networking.EnvoyFilter_EnvoyConfigObjectPatch {
http2ProtocolOptions := `{"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": {}
}
}
}}`
return &networking.EnvoyFilter_EnvoyConfigObjectPatch{
ApplyTo: networking.EnvoyFilter_CLUSTER,
Match: &networking.EnvoyFilter_EnvoyConfigObjectMatch{
Context: networking.EnvoyFilter_GATEWAY,
ObjectTypes: &networking.EnvoyFilter_EnvoyConfigObjectMatch_Cluster{
Cluster: &networking.EnvoyFilter_ClusterMatch{
Name: tracingClusterName(port, service),
},
},
},
Patch: &networking.EnvoyFilter_Patch{
Operation: networking.EnvoyFilter_Patch_MERGE,
Value: util.BuildPatchStruct(http2ProtocolOptions),
},
}
}
func (t *TracingController) constructTracingExtendPatches(tracing *Tracing) []*networking.EnvoyFilter_EnvoyConfigObjectPatch {
if tracing == nil {
return nil
}
var patches []*networking.EnvoyFilter_EnvoyConfigObjectPatch
if skywalking := tracing.Skywalking; skywalking != nil {
patches = append(patches, t.constructHTTP2ProtocolOptionsPatch(skywalking.Port, skywalking.Service))
}
if otel := tracing.OpenTelemetry; otel != nil {
patches = append(patches, t.constructHTTP2ProtocolOptionsPatch(otel.Port, otel.Service))
}
return patches
}
func (t *TracingController) constructTracingTracer(tracing *Tracing, namespace string) string {
tracingConfig := ""
timeout := float32(tracing.Timeout) / 1000
@@ -338,7 +389,7 @@ func (t *TracingController) constructTracingTracer(tracing *Tracing, namespace s
},
"grpc_service": {
"envoy_grpc": {
"cluster_name": "outbound|%s||%s"
"cluster_name": "%s"
},
"timeout": "%.3fs"
}
@@ -349,7 +400,7 @@ func (t *TracingController) constructTracingTracer(tracing *Tracing, namespace s
}
}
}
}`, namespace, skywalking.AccessToken, skywalking.Port, skywalking.Service, timeout, tracing.Sampling)
}`, namespace, skywalking.AccessToken, tracingClusterName(skywalking.Port, skywalking.Service), timeout, tracing.Sampling)
}
if tracing.Zipkin != nil {
@@ -363,7 +414,7 @@ func (t *TracingController) constructTracingTracer(tracing *Tracing, namespace s
"name": "envoy.tracers.zipkin",
"typed_config": {
"@type": "type.googleapis.com/envoy.config.trace.v3.ZipkinConfig",
"collector_cluster": "outbound|%s||%s",
"collector_cluster": "%s",
"collector_endpoint": "/api/v2/spans",
"collector_hostname": "higress-gateway",
"collector_endpoint_version": "HTTP_JSON",
@@ -375,7 +426,7 @@ func (t *TracingController) constructTracingTracer(tracing *Tracing, namespace s
}
}
}
}`, zipkin.Port, zipkin.Service, tracing.Sampling)
}`, tracingClusterName(zipkin.Port, zipkin.Service), tracing.Sampling)
}
if tracing.OpenTelemetry != nil {
@@ -392,7 +443,7 @@ func (t *TracingController) constructTracingTracer(tracing *Tracing, namespace s
"service_name": "higress-gateway.%s",
"grpc_service": {
"envoy_grpc": {
"cluster_name": "outbound|%s||%s"
"cluster_name": "%s"
},
"timeout": "%.3fs"
}
@@ -403,7 +454,7 @@ func (t *TracingController) constructTracingTracer(tracing *Tracing, namespace s
}
}
}
}`, namespace, opentelemetry.Port, opentelemetry.Service, timeout, tracing.Sampling)
}`, namespace, tracingClusterName(opentelemetry.Port, opentelemetry.Service), timeout, tracing.Sampling)
}
return tracingConfig
}

View File

@@ -22,6 +22,7 @@ import (
kubecredentials "istio.io/istio/pilot/pkg/credentials/kube"
"istio.io/istio/pilot/pkg/model"
kubecontroller "istio.io/istio/pilot/pkg/serviceregistry/kube/controller"
"istio.io/istio/pilot/pkg/status"
"istio.io/istio/pkg/config"
"istio.io/istio/pkg/config/constants"
"istio.io/istio/pkg/config/schema/collection"
@@ -48,6 +49,7 @@ type gatewayController struct {
store model.ConfigStoreController
credsController credentials.MulticlusterController
istioController *istiogateway.Controller
statusManager *status.Manager
resourceUpToDate atomic.Bool
}
@@ -76,9 +78,10 @@ func NewController(client kube.Client, options common.Options) common.GatewayCon
istioController.DefaultGatewaySelector = map[string]string{options.GatewaySelectorKey: options.GatewaySelectorValue}
}
var statusManager *status.Manager = nil
if options.EnableStatus {
// TODO: Add status sync support
//istioController.SetStatusWrite(true,)
statusManager = status.NewManager(store)
istioController.SetStatusWrite(true, statusManager)
} else {
IngressLog.Infof("Disable status update for cluster %s", clusterId)
}
@@ -87,6 +90,7 @@ func NewController(client kube.Client, options common.Options) common.GatewayCon
store: store,
credsController: credsController,
istioController: istioController,
statusManager: statusManager,
}
}
@@ -148,6 +152,9 @@ func (g *gatewayController) Run(stop <-chan struct{}) {
})
go g.store.Run(stop)
go g.istioController.Run(stop)
if g.statusManager != nil {
g.statusManager.Start(stop)
}
}
func (g *gatewayController) SetWatchErrorHandler(f func(r *cache.Reflector, err error)) error {

View File

@@ -15,26 +15,37 @@
package istio
import (
"context"
"fmt"
"sort"
"strconv"
"strings"
networking "istio.io/api/networking/v1alpha3"
"istio.io/istio/pilot/pkg/model"
serviceRegistryKube "istio.io/istio/pilot/pkg/serviceregistry/kube"
"istio.io/istio/pkg/cluster"
"istio.io/istio/pkg/config/host"
"istio.io/istio/pkg/config/schema/gvk"
"istio.io/istio/pkg/kube"
"istio.io/istio/pkg/util/sets"
corev1 "k8s.io/api/core/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
// GatewayContext contains a minimal subset of push context functionality to be exposed to GatewayAPIControllers
type GatewayContext struct {
ps *model.PushContext
// Start - Updated by Higress
client kube.Client
domainSuffix string
clusterID cluster.ID
// End - Updated by Higress
}
func NewGatewayContext(ps *model.PushContext) GatewayContext {
return GatewayContext{ps}
// Start - Updated by Higress
func NewGatewayContext(ps *model.PushContext, client kube.Client, domainSuffix string, clusterID cluster.ID) GatewayContext {
return GatewayContext{ps, client, domainSuffix, clusterID}
}
// ResolveGatewayInstances attempts to resolve all instances that a gateway will be exposed on.
@@ -59,26 +70,20 @@ func (gc GatewayContext) ResolveGatewayInstances(
foundExternal := sets.New[string]()
foundPending := sets.New[string]()
warnings := []string{}
// Cache endpoints to reduce redundant queries
endpointsCache := make(map[string]*corev1.Endpoints)
for _, g := range gwsvcs {
svc, f := gc.ps.ServiceIndex.HostnameAndNamespace[host.Name(g)][namespace]
if !f {
otherNamespaces := []string{}
for ns := range gc.ps.ServiceIndex.HostnameAndNamespace[host.Name(g)] {
otherNamespaces = append(otherNamespaces, `"`+ns+`"`) // Wrap in quotes for output
}
if len(otherNamespaces) > 0 {
sort.Strings(otherNamespaces)
warnings = append(warnings, fmt.Sprintf("hostname %q not found in namespace %q, but it was found in namespace(s) %v",
g, namespace, strings.Join(otherNamespaces, ", ")))
} else {
warnings = append(warnings, fmt.Sprintf("hostname %q not found", g))
}
svc := gc.GetService(g, namespace, gvk.Service.Kind)
if svc == nil {
warnings = append(warnings, fmt.Sprintf("hostname %q not found", g))
continue
}
svcKey := svc.Key()
for port := range ports {
instances := gc.ps.ServiceInstancesByPort(svc, port, nil)
if len(instances) > 0 {
exists := checkServicePortExists(svc, port)
if exists {
foundInternal.Insert(fmt.Sprintf("%s:%d", g, port))
if svc.Attributes.ClusterExternalAddresses.Len() > 0 {
// Fetch external IPs from all clusters
@@ -92,22 +97,30 @@ func (gc GatewayContext) ResolveGatewayInstances(
}
}
} else {
instancesByPort := gc.ps.ServiceInstances(svcKey)
if instancesEmpty(instancesByPort) {
endpoints, ok := endpointsCache[g]
if !ok {
endpoints = gc.GetEndpoints(g, namespace)
endpointsCache[g] = endpoints
}
if endpoints == nil {
warnings = append(warnings, fmt.Sprintf("no instances found for hostname %q", g))
} else {
hintPort := sets.New[string]()
for _, instances := range instancesByPort {
for _, i := range instances {
if i.Endpoint.EndpointPort == uint32(port) {
hintPort.Insert(strconv.Itoa(i.ServicePort.Port))
hintWorkloadPort := false
for _, subset := range endpoints.Subsets {
for _, subSetPort := range subset.Ports {
if subSetPort.Port == int32(port) {
hintWorkloadPort = true
break
}
}
if hintWorkloadPort {
break
}
}
if hintPort.Len() > 0 {
if hintWorkloadPort {
warnings = append(warnings, fmt.Sprintf(
"port %d not found for hostname %q (hint: the service port should be specified, not the workload port. Did you mean one of these ports: %v?)",
port, g, sets.SortedList(hintPort)))
"port %d not found for hostname %q (hint: the service port should be specified, not the workload port", port, g))
} else {
warnings = append(warnings, fmt.Sprintf("port %d not found for hostname %q", port, g))
}
@@ -119,15 +132,60 @@ func (gc GatewayContext) ResolveGatewayInstances(
return sets.SortedList(foundInternal), sets.SortedList(foundExternal), sets.SortedList(foundPending), warnings
}
func (gc GatewayContext) GetService(hostname, namespace string) *model.Service {
return gc.ps.ServiceIndex.HostnameAndNamespace[host.Name(hostname)][namespace]
func (gc GatewayContext) GetService(hostname, namespace, kind string) *model.Service {
// Currently only supports type Kubernetes Service
if kind != gvk.Service.Kind {
log.Warnf("Unsupported kind: expected 'Service', but got '%s'", kind)
return nil
}
serviceName := extractServiceName(hostname)
svc, err := gc.client.Kube().CoreV1().Services(namespace).Get(context.TODO(), serviceName, metav1.GetOptions{})
if err != nil {
if kerrors.IsNotFound(err) {
return nil
}
log.Errorf("failed to get service (serviceName: %s, namespace: %s): %v", serviceName, namespace, err)
return nil
}
return serviceRegistryKube.ConvertService(*svc, gc.domainSuffix, gc.clusterID)
}
func instancesEmpty(m map[int][]*model.ServiceInstance) bool {
for _, instances := range m {
if len(instances) > 0 {
return false
func (gc GatewayContext) GetEndpoints(hostname, namespace string) *corev1.Endpoints {
serviceName := extractServiceName(hostname)
endpoints, err := gc.client.Kube().CoreV1().Endpoints(namespace).Get(context.TODO(), serviceName, metav1.GetOptions{})
if err != nil {
if kerrors.IsNotFound(err) {
return nil
}
log.Errorf("failed to get endpoints (serviceName: %s, namespace: %s): %v", serviceName, namespace, err)
return nil
}
return endpoints
}
func checkServicePortExists(svc *model.Service, port int) bool {
if svc == nil {
return false
}
for _, svcPort := range svc.Ports {
if port == svcPort.Port {
return true
}
}
return true
return false
}
func extractServiceName(hostName string) string {
parts := strings.Split(hostName, ".")
if len(parts) >= 4 {
return parts[0]
}
return ""
}
// End - Updated by Higress

View File

@@ -201,7 +201,9 @@ func (c *Controller) Reconcile(ps *model.PushContext) error {
ReferenceGrant: referenceGrant,
DefaultGatewaySelector: c.DefaultGatewaySelector,
Domain: c.domain,
Context: NewGatewayContext(ps),
// Start - Updated by Higress
Context: NewGatewayContext(ps, c.client, c.domain, c.cluster),
// End - Updated by Higress
}
if !input.hasResources() {

View File

@@ -25,6 +25,7 @@ import (
"strings"
higressconfig "github.com/alibaba/higress/pkg/config"
"github.com/alibaba/higress/pkg/ingress/kube/util"
istio "istio.io/api/networking/v1alpha3"
"istio.io/istio/pilot/pkg/features"
"istio.io/istio/pilot/pkg/model"
@@ -1168,7 +1169,7 @@ func buildDestination(ctx configContext, to k8s.BackendRef, ns string, enforceRe
return nil, &ConfigError{Reason: InvalidDestination, Message: "serviceName invalid; the name of the Service must be used, not the hostname."}
}
hostname := fmt.Sprintf("%s.%s.svc.%s", to.Name, namespace, ctx.Domain)
if ctx.Context.GetService(hostname, namespace) == nil {
if ctx.Context.GetService(hostname, namespace, gvk.Service.Kind) == nil {
invalidBackendErr = &ConfigError{Reason: InvalidDestinationNotFound, Message: fmt.Sprintf("backend(%s) not found", hostname)}
}
return &istio.Destination{
@@ -1192,7 +1193,7 @@ func buildDestination(ctx configContext, to k8s.BackendRef, ns string, enforceRe
if strings.Contains(string(to.Name), ".") {
return nil, &ConfigError{Reason: InvalidDestination, Message: "serviceName invalid; the name of the Service must be used, not the hostname."}
}
if ctx.Context.GetService(hostname, namespace) == nil {
if ctx.Context.GetService(hostname, namespace, "ServiceImport") == nil {
invalidBackendErr = &ConfigError{Reason: InvalidDestinationNotFound, Message: fmt.Sprintf("backend(%s) not found", hostname)}
}
return &istio.Destination{
@@ -1210,7 +1211,7 @@ func buildDestination(ctx configContext, to k8s.BackendRef, ns string, enforceRe
return nil, &ConfigError{Reason: InvalidDestination, Message: "namespace may not be set with Hostname type"}
}
hostname := string(to.Name)
if ctx.Context.GetService(hostname, namespace) == nil {
if ctx.Context.GetService(hostname, namespace, "Hostname") == nil {
invalidBackendErr = &ConfigError{Reason: InvalidDestinationNotFound, Message: fmt.Sprintf("backend(%s) not found", hostname)}
}
return &istio.Destination{
@@ -1880,7 +1881,7 @@ func extractGatewayServices(r GatewayResources, kgw *k8s.GatewaySpec, obj config
if len(name) > 0 {
return []string{fmt.Sprintf("%s.%s.svc.%v", name, obj.Namespace, r.Domain)}, false, nil
}
return []string{}, true, nil
return []string{fmt.Sprintf("%s.%s.svc.%s", higressconfig.GatewayName, higressconfig.PodNamespace, util.GetDomainSuffix())}, true, nil
}
gatewayServices := []string{}
skippedAddresses := []string{}

View File

@@ -17,6 +17,7 @@
package istio
import (
"context"
"fmt"
"os"
"reflect"
@@ -25,6 +26,7 @@ import (
"strings"
"testing"
"github.com/google/go-cmp/cmp"
"istio.io/istio/pilot/pkg/config/kube/crd"
credentials "istio.io/istio/pilot/pkg/credentials/kube"
"istio.io/istio/pilot/pkg/features"
@@ -47,7 +49,8 @@ import (
"sigs.k8s.io/yaml"
)
var ports = []*model.Port{
// Start - Updated by Higress
var ports = []corev1.ServicePort{
{
Name: "http",
Port: 80,
@@ -64,232 +67,291 @@ var defaultGatewaySelector = map[string]string{
"higress": "higress-system-higress-gateway",
}
var services = []*model.Service{
var services = []corev1.Service{
{
Attributes: model.ServiceAttributes{
ObjectMeta: metav1.ObjectMeta{
Name: "higress-gateway",
Namespace: "higress-system",
ClusterExternalAddresses: &model.AddressMap{
Addresses: map[cluster.ID][]string{
"Kubernetes": {"1.2.3.4"},
},
Spec: corev1.ServiceSpec{
Ports: ports,
ExternalIPs: []string{"1.2.3.4"},
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "example.com",
Namespace: "higress-system",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin-apple",
Namespace: "apple",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin-banana",
Namespace: "banana",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin-second",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin-wildcard",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "foo-svc",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin-other",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "example",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "echo",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin",
Namespace: "cert",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "my-svc",
Namespace: "service",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "google.com",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc2",
Namespace: "allowed-1",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc2",
Namespace: "allowed-2",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc1",
Namespace: "allowed-1",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc3",
Namespace: "allowed-2",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "svc4",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin",
Namespace: "group-namespace1",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin",
Namespace: "group-namespace2",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin-zero",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin",
Namespace: "higress-system",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin-mirror",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin-foo",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin-alt",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "higress-controller",
Namespace: "higress-system",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "echo",
Namespace: "higress-system",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "httpbin-bad",
Namespace: "default",
},
Spec: corev1.ServiceSpec{
Ports: ports,
},
},
}
var endpoints = []corev1.Endpoints{
{
ObjectMeta: metav1.ObjectMeta{
Name: "higress-gateway",
Namespace: "higress-system",
},
Subsets: []corev1.EndpointSubset{
{
Ports: []corev1.EndpointPort{
{
Port: 8080,
},
},
},
},
Ports: ports,
Hostname: "higress-gateway.higress-system.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "higress-system",
},
Ports: ports,
Hostname: "example.com",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "httpbin.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "apple",
},
Ports: ports,
Hostname: "httpbin-apple.apple.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "banana",
},
Ports: ports,
Hostname: "httpbin-banana.banana.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "httpbin-second.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "httpbin-wildcard.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "foo-svc.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "httpbin-other.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "example.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "echo.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "echo.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "cert",
},
Ports: ports,
Hostname: "httpbin.cert.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "service",
},
Ports: ports,
Hostname: "my-svc.service.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "google.com",
},
{
Attributes: model.ServiceAttributes{
Namespace: "allowed-1",
},
Ports: ports,
Hostname: "svc2.allowed-1.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "allowed-2",
},
Ports: ports,
Hostname: "svc2.allowed-2.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "allowed-1",
},
Ports: ports,
Hostname: "svc1.allowed-1.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "allowed-2",
},
Ports: ports,
Hostname: "svc3.allowed-2.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "svc4.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "group-namespace1",
},
Ports: ports,
Hostname: "httpbin.group-namespace1.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "group-namespace2",
},
Ports: ports,
Hostname: "httpbin.group-namespace2.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "httpbin-zero.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "higress-system",
},
Ports: ports,
Hostname: "httpbin.higress-system.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "httpbin-mirror.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "httpbin-foo.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "httpbin-alt.default.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "higress-system",
},
Ports: ports,
Hostname: "higress-controller.higress-system.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "higress-system",
},
Ports: ports,
Hostname: "higress-controller.higress-system.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "higress-system",
},
Ports: ports,
Hostname: "echo.higress-system.svc.domain.suffix",
},
{
Attributes: model.ServiceAttributes{
Namespace: "default",
},
Ports: ports,
Hostname: "httpbin-bad.default.svc.domain.suffix",
},
}
// End - Updated by Higress
var (
// https://github.com/kubernetes/kubernetes/blob/v1.25.4/staging/src/k8s.io/kubectl/pkg/cmd/create/create_secret_tls_test.go#L31
rsaCertPEM = `-----BEGIN CERTIFICATE-----
@@ -364,6 +426,21 @@ func init() {
func TestConvertResources(t *testing.T) {
validator := crdvalidation.NewIstioValidator(t)
// Start - Updated by Higress
client := kube.NewFakeClient()
for _, svc := range services {
if _, err := client.Kube().CoreV1().Services(svc.Namespace).Create(context.TODO(), &svc, metav1.CreateOptions{}); err != nil {
t.Fatal(err)
}
}
for _, endpoint := range endpoints {
if _, err := client.Kube().CoreV1().Endpoints(endpoint.Namespace).Create(context.TODO(), &endpoint, metav1.CreateOptions{}); err != nil {
t.Fatal(err)
}
}
// End - Updated by Higress
cases := []struct {
name string
}{
@@ -374,38 +451,23 @@ func TestConvertResources(t *testing.T) {
{"weighted"},
{"zero"},
{"invalid"},
{"multi-gateway"},
// 目前仅支持 type 为 Hostname 和 ServiceImport
//{"multi-gateway"},
{"delegated"},
{"route-binding"},
{"reference-policy-tls"},
{"reference-policy-service"},
{"serviceentry"},
//{"serviceentry"},
{"alias"},
{"mcs"},
//{"mcs"},
{"route-precedence"},
}
for _, tt := range cases {
t.Run(tt.name, func(t *testing.T) {
input := readConfig(t, fmt.Sprintf("testdata/%s.yaml", tt.name), validator)
// Setup a few preconfigured services
instances := []*model.ServiceInstance{}
for _, svc := range services {
instances = append(instances, &model.ServiceInstance{
Service: svc,
ServicePort: ports[0],
Endpoint: &model.IstioEndpoint{EndpointPort: 8080},
}, &model.ServiceInstance{
Service: svc,
ServicePort: ports[1],
Endpoint: &model.IstioEndpoint{},
})
}
cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{
Services: services,
Instances: instances,
})
cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{})
kr := splitInput(t, input)
kr.Context = NewGatewayContext(cg.PushContext())
kr.Context = NewGatewayContext(cg.PushContext(), client, "domain.suffix", "")
output := convertResources(kr)
output.AllowedReferences = AllowedReferences{} // Not tested here
output.ReferencedNamespaceKeys = nil // Not tested here
@@ -427,20 +489,20 @@ func TestConvertResources(t *testing.T) {
assert.Equal(t, golden, output)
//outputStatus := getStatus(t, kr.GatewayClass, kr.Gateway, kr.HTTPRoute, kr.TLSRoute, kr.TCPRoute)
//goldenStatusFile := fmt.Sprintf("testdata/%s.status.yaml.golden", tt.name)
//if util.Refresh() {
// if err := os.WriteFile(goldenStatusFile, outputStatus, 0o644); err != nil {
// t.Fatal(err)
// }
//}
//goldenStatus, err := os.ReadFile(goldenStatusFile)
//if err != nil {
// t.Fatal(err)
//}
//if diff := cmp.Diff(string(goldenStatus), string(outputStatus)); diff != "" {
// t.Fatalf("Diff:\n%s", diff)
//}
outputStatus := getStatus(t, kr.GatewayClass, kr.Gateway, kr.HTTPRoute, kr.TLSRoute, kr.TCPRoute)
goldenStatusFile := fmt.Sprintf("testdata/%s.status.yaml.golden", tt.name)
if util.Refresh() {
if err := os.WriteFile(goldenStatusFile, outputStatus, 0o644); err != nil {
t.Fatal(err)
}
}
goldenStatus, err := os.ReadFile(goldenStatusFile)
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(string(goldenStatus), string(outputStatus)); diff != "" {
t.Fatalf("Diff:\n%s", diff)
}
})
}
}
@@ -593,7 +655,7 @@ spec:
input := readConfigString(t, tt.config, validator)
cg := v1alpha3.NewConfigGenTest(t, v1alpha3.TestOptions{})
kr := splitInput(t, input)
kr.Context = NewGatewayContext(cg.PushContext())
kr.Context = NewGatewayContext(cg.PushContext(), nil, "", "")
output := convertResources(kr)
c := &Controller{
state: output,
@@ -814,7 +876,7 @@ func BenchmarkBuildHTTPVirtualServices(b *testing.B) {
validator := crdvalidation.NewIstioValidator(b)
input := readConfig(b, "testdata/benchmark-httproute.yaml", validator)
kr := splitInput(b, input)
kr.Context = NewGatewayContext(cg.PushContext())
kr.Context = NewGatewayContext(cg.PushContext(), nil, "", "")
ctx := configContext{
GatewayResources: kr,
AllowedReferences: convertReferencePolicies(kr),
@@ -857,7 +919,7 @@ func TestExtractGatewayServices(t *testing.T) {
Namespace: "default",
},
},
gatewayServices: []string{},
gatewayServices: []string{"higress-gateway.higress-system.svc.cluster.local"},
useDefaultService: true,
},
{
@@ -977,7 +1039,7 @@ func TestExtractGatewayServices(t *testing.T) {
Namespace: "default",
},
},
gatewayServices: []string{},
gatewayServices: []string{"higress-gateway.higress-system.svc.cluster.local"},
useDefaultService: true,
},
}

View File

@@ -128,8 +128,7 @@ status:
- lastTransitionTime: fake
message: 'Failed to assign to any requested addresses: port 8080 not found for
hostname "higress-gateway.higress-system.svc.domain.suffix" (hint: the service
port should be specified, not the workload port. Did you mean one of these ports:
[80]?)'
port should be specified, not the workload port'
reason: Invalid
status: "False"
type: Programmed
@@ -163,26 +162,6 @@ status:
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
creationTimestamp: null
name: invalid-gateway-address
namespace: invalid-gateway-address
spec: null
status:
conditions:
- lastTransitionTime: fake
message: only Hostname is supported, ignoring [1.2.3.4]
reason: UnsupportedAddress
status: "False"
type: Accepted
- lastTransitionTime: fake
message: Failed to assign to any requested addresses
reason: UnsupportedAddress
status: "False"
type: Programmed
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: Gateway
metadata:
creationTimestamp: null
name: invalid-cert-kind
@@ -477,4 +456,29 @@ status:
namespace: higress-system
sectionName: fake
---
apiVersion: gateway.networking.k8s.io/v1beta1
kind: HTTPRoute
metadata:
creationTimestamp: null
name: no-backend
namespace: default
spec: null
status:
parents:
- conditions:
- lastTransitionTime: fake
message: Route was valid
reason: Accepted
status: "True"
type: Accepted
- lastTransitionTime: fake
message: All references resolved
reason: ResolvedRefs
status: "True"
type: ResolvedRefs
controllerName: higress.io/gateway-controller
parentRef:
group: ""
kind: Service
name: httpbin
---

View File

@@ -55,22 +55,23 @@ spec:
hostname: "*.example"
port: 8080 # Test service has port 80 with targetPort 8080
protocol: HTTP
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: Gateway
metadata:
name: invalid-gateway-address
namespace: invalid-gateway-address
spec:
gatewayClassName: higress
addresses:
- value: 1.2.3.4
type: istio.io/FakeType
listeners:
- name: default
hostname: "*.domain.example"
port: 80
protocol: HTTP
#---
# Higress 仅支持 addresses type 为 Hostname
#apiVersion: gateway.networking.k8s.io/v1alpha2
#kind: Gateway
#metadata:
# name: invalid-gateway-address
# namespace: invalid-gateway-address
#spec:
# gatewayClassName: higress
# addresses:
# - value: 1.2.3.4
# type: istio.io/FakeType
# listeners:
# - name: default
# hostname: "*.domain.example"
# port: 80
# protocol: HTTP
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: Gateway

View File

@@ -53,25 +53,6 @@ spec:
protocol: HTTP
---
apiVersion: networking.istio.io/v1alpha3
kind: Gateway
metadata:
annotations:
internal.istio.io/parents: Gateway/invalid-gateway-address/default.invalid-gateway-address
creationTimestamp: null
name: invalid-gateway-address-istio-autogenerated-k8s-gateway-default
namespace: invalid-gateway-address
spec:
selector:
higress: higress-system-higress-gateway
servers:
- hosts:
- invalid-gateway-address/*.domain.example
port:
name: default
number: 80
protocol: HTTP
---
apiVersion: networking.istio.io/v1alpha3
kind: VirtualService
metadata:
annotations:

View File

@@ -920,12 +920,7 @@ func (c *controller) storeBackendTrafficPolicy(wrapper *common.WrapperConfig, ba
if common.ValidateBackendResource(backend.Resource) && wrapper.AnnotationsConfig.Destination != nil {
for _, dest := range wrapper.AnnotationsConfig.Destination.McpDestination {
portNumber := dest.Destination.GetPort().GetNumber()
serviceKey := common.ServiceKey{
Namespace: "mcp",
Name: dest.Destination.Host,
Port: int32(portNumber),
ServiceFQDN: dest.Destination.Host,
}
serviceKey := common.CreateMcpServiceKey(dest.Destination.Host, int32(portNumber))
if _, exist := store[serviceKey]; !exist {
if serviceKey.Port != 0 {
store[serviceKey] = &common.WrapperTrafficPolicy{

View File

@@ -900,12 +900,7 @@ func (c *controller) storeBackendTrafficPolicy(wrapper *common.WrapperConfig, ba
if common.ValidateBackendResource(backend.Resource) && wrapper.AnnotationsConfig.Destination != nil {
for _, dest := range wrapper.AnnotationsConfig.Destination.McpDestination {
portNumber := dest.Destination.GetPort().GetNumber()
serviceKey := common.ServiceKey{
Namespace: "mcp",
Name: dest.Destination.Host,
Port: int32(portNumber),
ServiceFQDN: dest.Destination.Host,
}
serviceKey := common.CreateMcpServiceKey(dest.Destination.Host, int32(portNumber))
if _, exist := store[serviceKey]; !exist {
if serviceKey.Port != 0 {
store[serviceKey] = &common.WrapperTrafficPolicy{

View File

@@ -20,8 +20,10 @@ import (
"encoding/hex"
"errors"
"fmt"
"istio.io/istio/pilot/pkg/model"
"os"
"path"
"strconv"
"strings"
"github.com/golang/protobuf/jsonpb"
@@ -113,3 +115,44 @@ func BuildPatchStruct(config string) *_struct.Struct {
}
return val
}
type ServiceInfo struct {
model.NamespacedName
Port uint32
}
// convertToPort converts a port string to a uint32.
func convertToPort(v string) (uint32, error) {
p, err := strconv.ParseUint(v, 10, 32)
if err != nil || p > 65535 {
return 0, fmt.Errorf("invalid port %s: %v", v, err)
}
return uint32(p), nil
}
func ParseServiceInfo(service string, ingressNamespace string) (ServiceInfo, error) {
parts := strings.Split(service, ":")
namespacedName := SplitNamespacedName(parts[0])
if namespacedName.Name == "" {
return ServiceInfo{}, errors.New("service name can not be empty")
}
if namespacedName.Namespace == "" {
namespacedName.Namespace = ingressNamespace
}
var port uint32
if len(parts) == 2 {
// If port parse fail, we ignore port and pick the first one.
port, _ = convertToPort(parts[1])
}
return ServiceInfo{
NamespacedName: model.NamespacedName{
Name: namespacedName.Name,
Namespace: namespacedName.Namespace,
},
Port: port,
}, nil
}

View File

@@ -64,7 +64,7 @@ func (c ServiceEntryGenerator) Generate(proxy *model.Proxy, w *model.WatchedReso
return serviceEntries[i].CreationTimestamp.Before(serviceEntries[j].CreationTimestamp)
})
}
return generate(proxy, serviceEntries, w, updates, false, false)
return generate(proxy, serviceEntries, w, updates, c.GeneratorOptions.KeepConfigLabels, c.GeneratorOptions.KeepConfigAnnotations)
}
func (c ServiceEntryGenerator) GenerateDeltas(proxy *model.Proxy, updates *model.PushRequest,
@@ -82,7 +82,7 @@ type VirtualServiceGenerator struct {
func (c VirtualServiceGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource,
updates *model.PushRequest) (model.Resources, model.XdsLogDetails, error) {
virtualServices := c.Environment.List(gvk.VirtualService, model.NamespaceAll)
return generate(proxy, virtualServices, w, updates, false, false)
return generate(proxy, virtualServices, w, updates, c.GeneratorOptions.KeepConfigLabels, c.GeneratorOptions.KeepConfigAnnotations)
}
func (c VirtualServiceGenerator) GenerateDeltas(proxy *model.Proxy, updates *model.PushRequest,
@@ -100,7 +100,7 @@ type DestinationRuleGenerator struct {
func (c DestinationRuleGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource,
updates *model.PushRequest) (model.Resources, model.XdsLogDetails, error) {
rules := c.Environment.List(gvk.DestinationRule, model.NamespaceAll)
return generate(proxy, rules, w, updates, false, false)
return generate(proxy, rules, w, updates, c.GeneratorOptions.KeepConfigLabels, c.GeneratorOptions.KeepConfigAnnotations)
}
func (c DestinationRuleGenerator) GenerateDeltas(proxy *model.Proxy, updates *model.PushRequest,
@@ -118,7 +118,7 @@ type EnvoyFilterGenerator struct {
func (c EnvoyFilterGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource,
updates *model.PushRequest) (model.Resources, model.XdsLogDetails, error) {
filters := c.Environment.List(gvk.EnvoyFilter, model.NamespaceAll)
return generate(proxy, filters, w, updates, false, false)
return generate(proxy, filters, w, updates, c.GeneratorOptions.KeepConfigLabels, c.GeneratorOptions.KeepConfigAnnotations)
}
func (c EnvoyFilterGenerator) GenerateDeltas(proxy *model.Proxy, updates *model.PushRequest,
@@ -154,7 +154,7 @@ type WasmPluginGenerator struct {
func (c WasmPluginGenerator) Generate(proxy *model.Proxy, w *model.WatchedResource,
updates *model.PushRequest) (model.Resources, model.XdsLogDetails, error) {
wasmPlugins := c.Environment.List(gvk.WasmPlugin, model.NamespaceAll)
return generate(proxy, wasmPlugins, w, updates, false, false)
return generate(proxy, wasmPlugins, w, updates, c.GeneratorOptions.KeepConfigLabels, c.GeneratorOptions.KeepConfigAnnotations)
}
func (c WasmPluginGenerator) GenerateDeltas(proxy *model.Proxy, push *model.PushContext, updates *model.PushRequest,

View File

@@ -187,10 +187,9 @@ func (m *IngressTranslation) List(typ config.GroupVersionKind, namespace string)
higressConfig = append(higressConfig, ingressConfig...)
if m.kingressConfig != nil {
kingressConfig := m.kingressConfig.List(typ, namespace)
if kingressConfig == nil {
return nil
if kingressConfig != nil {
higressConfig = append(higressConfig, kingressConfig...)
}
higressConfig = append(higressConfig, kingressConfig...)
}
return higressConfig
}

View File

@@ -1,5 +1,6 @@
## Wasm 插件
目前 Higress 提供了 c++ 和 golang 两种 Wasm 插件开发框架,支持 Wasm 插件路由&域名级匹配生效。
同时提供了多个内置插件,用户可以基于 Higress 提供的官方镜像仓库直接使用这些插件(以 c++ 版本举例):

View File

@@ -16,15 +16,15 @@ load("@io_bazel_rules_docker//repositories:deps.bzl", container_deps = "deps")
container_deps()
PROXY_WASM_CPP_SDK_SHA = "fd0be8405db25de0264bdb78fae3a82668c03782"
PROXY_WASM_CPP_SDK_SHA = "eaec483b5b3c7bcb89fd208b5a1fa5d79d626f61"
PROXY_WASM_CPP_SDK_SHA256 = "c57de2425b5c61d7f630c5061e319b4557ae1f1c7526e5a51c33dc1299471b08"
PROXY_WASM_CPP_SDK_SHA256 = "1140bc8114d75db56a6ca6b18423d4df50d988d40b4cec929a1eb246cf5a4a3d"
http_archive(
name = "proxy_wasm_cpp_sdk",
sha256 = PROXY_WASM_CPP_SDK_SHA256,
strip_prefix = "proxy-wasm-cpp-sdk-" + PROXY_WASM_CPP_SDK_SHA,
url = "https://github.com/proxy-wasm/proxy-wasm-cpp-sdk/archive/" + PROXY_WASM_CPP_SDK_SHA + ".tar.gz",
url = "https://github.com/higress-group/proxy-wasm-cpp-sdk/archive/" + PROXY_WASM_CPP_SDK_SHA + ".tar.gz",
)
load("@proxy_wasm_cpp_sdk//bazel/dep:deps.bzl", "wasm_dependencies")

View File

@@ -33,14 +33,14 @@ def wasm_libraries():
urls = ["https://github.com/google/googletest/archive/release-1.10.0.tar.gz"],
)
PROXY_WASM_CPP_HOST_SHA = "f38347360feaaf5b2a733f219c4d8c9660d626f0"
PROXY_WASM_CPP_HOST_SHA256 = "bf10de946eb5785813895c2bf16504afc0cd590b9655d9ee52fb1074d0825ea3"
PROXY_WASM_CPP_HOST_SHA = "7850d1721fe3dd2ccfb86a06116f76c23b1f1bf8"
PROXY_WASM_CPP_HOST_SHA256 = "740690fc1d749849f6e24b5bc48a07dabc0565a7d03b6cd13425dba693956c57"
http_archive(
name = "proxy_wasm_cpp_host",
sha256 = PROXY_WASM_CPP_HOST_SHA256,
strip_prefix = "proxy-wasm-cpp-host-" + PROXY_WASM_CPP_HOST_SHA,
url = "https://github.com/proxy-wasm/proxy-wasm-cpp-host/archive/" + PROXY_WASM_CPP_HOST_SHA +".tar.gz",
url = "https://github.com/higress-group/proxy-wasm-cpp-host/archive/" + PROXY_WASM_CPP_HOST_SHA +".tar.gz",
)
http_archive(

View File

@@ -1,16 +1,30 @@
<p>
<a href="README_EN.md">English</a> | 中文
</p>
---
title: Basic 认证
keywords: [higress,basic auth]
description: Basic 认证插件配置参考
---
# 功能说明
## 功能说明
`basic-auth`插件实现了基于 HTTP Basic Auth 标准进行认证鉴权的功能
# 配置字段
## 运行属性
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | -------- | ------ | ---------------------------------------------------- |
| `consumers` | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
| `_rules_` | array of object | 选填 | - | 配置特定路由或域名的访问权限列表,用于对请求进行鉴权 |
插件执行阶段:`认证阶段`
插件执行优先级:`320`
## 配置字段
**注意:**
- 在一个规则里,鉴权配置和认证配置不可同时存在
- 对于通过认证鉴权的请求请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
### 认证配置
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | -------- | ------ | ---------------------------------------------------- |
| `global_auth` | bool | 选填(**仅实例级别配置** | - | 只能在实例级别配置若配置为true则全局生效认证机制; 若配置为false则只对做了配置的域名和路由生效认证机制若不配置则仅当没有域名和路由配置时全局生效兼容老用户使用习惯。 |
| `consumers` | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
`consumers`中每一项的配置字段说明如下:
@@ -19,51 +33,49 @@
| `credential` | string | 必填 | - | 配置该consumer的访问凭证 |
| `name` | string | 必填 | - | 配置该consumer的名称 |
`_rules_` 中每一项的配置字段说明如下:
### 鉴权配置(非必需)
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ---------------- | --------------- | ------------------------------------------------- | ------ | -------------------------------------------------- |
| `_match_route_` | array of string | 选填,`_match_route_``_match_domain_`中选填一项 | - | 配置要匹配的路由名称 |
| `_match_domain_` | array of string | 选填,`_match_route_``_match_domain_`中选填一项 | - | 配置要匹配的域名 |
| `allow` | array of string | 必填 | - | 对于符合匹配条件的请求配置允许访问的consumer名称 |
**注意:**
- 若不配置`_rules_`字段,则默认对当前网关实例的所有路由开启认证;
- 对于通过认证鉴权的请求请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
## 配置示例
# 配置示例
## 对特定路由或域名开启认证和鉴权
### 全局配置认证和路由粒度进行鉴权
以下配置将对网关特定路由或域名开启 Basic Auth 认证和鉴权,注意凭证信息中的用户名和密码之间使用":"分隔,`credential`字段不能重复
在实例级别做如下插件配置:
```yaml
# 使用 _rules_ 字段进行细粒度规则配置
consumers:
- credential: 'admin:123456'
name: consumer1
- credential: 'guest:abc'
name: consumer2
_rules_:
# 规则一:按路由名称匹配生效
- _match_route_:
- route-a
- route-b
allow:
- consumer1
# 规则二:按域名匹配生效
- _match_domain_:
- "*.example.com"
- test.com
allow:
- consumer2
global_auth: false
```
此例 `_match_route_` 中指定的 `route-a``route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将允许`name``consumer1`的调用者访问,其他调用者不允许访问;
route-a 和 route-b 这两个路由做如下配置:
此例 `_match_domain_` 中指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将允许`name``consumer2`的调用者访问,其他调用者不允许访问。
```yaml
allow:
- consumer1
```
### 根据该配置,下列请求可以允许访问:
对 *.example.com 和 test.com 在这两个域名做如下配置:
```yaml
allow:
- consumer2
```
若是在控制台进行配置,此例指定的 `route-a``route-b` 即在控制台创建路由时填写的路由名称,当匹配到这两个路由时,将允许`name``consumer1`的调用者访问,其他调用者不允许访问;
此例指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将允许`name``consumer2`的调用者访问,其他调用者不允许访问。
根据该配置,下列请求可以允许访问:
**请求指定用户名密码**
@@ -77,7 +89,7 @@ curl -H 'Authorization: Basic YWRtaW46MTIzNDU2' http://xxx.hello.com/test
认证鉴权通过后请求的header中会被添加一个`X-Mse-Consumer`字段,在此例中其值为`consumer1`,用以标识调用方的名称
### 下列请求将拒绝访问:
下列请求将拒绝访问:
**请求未提供用户名密码返回401**
```bash
@@ -93,22 +105,10 @@ curl -u admin:abc http://xxx.hello.com/test
curl -u guest:abc http://xxx.hello.com/test
```
## 网关实例级别开启
## 相关错误码
以下配置未指定`_rules_`字段,因此将对网关实例级别开启 Basic Auth 认证
```yaml
consumers:
- credential: 'admin:123456'
name: consumer1
- credential: 'guest:abc'
name: consumer2
```
# 相关错误码
| HTTP 状态码 | 出错信息 | 原因说明 |
| ----------- | ------------------------------------------------------------------------------ | ---------------------- |
| HTTP 状态码 | 出错信息 | 原因说明 |
| ----------- |--------------------------------------------------------------------------------| ---------------------- |
| 401 | Request denied by Basic Auth check. No Basic Authentication information found. | 请求未提供凭证 |
| 401 | Request denied by Basic Auth check. Invalid username and/or password | 请求凭证无效 |
| 403 | Request denied by Basic Auth check. Unauthorized consumer | 请求的调用方无访问权限 |
| 401 | Request denied by Basic Auth check. Invalid username and/or password. | 请求凭证无效 |
| 403 | Request denied by Basic Auth check. Unauthorized consumer. | 请求的调用方无访问权限 |

View File

@@ -1,117 +1,99 @@
<p>
English | <a href="README.md">中文</a>
</p>
---
title: Basic Authentication
keywords: [higress,basic auth]
description: Basic authentication plugin configuration reference
---
## Function Description
The `basic-auth` plugin implements authentication and authorization based on the HTTP Basic Auth standard.
# Description
`basic-auth` plugin implements the function of authentication based on the HTTP Basic Auth standard.
# Configuration Fields
| Name | Type | Requirement | Default Value | Description |
| ----------- | --------------- | -------- | ------ | ---------------------------------------------------- |
| `consumers` | array of object | Required | - | Caller of the service for authentication of requests |
| `_rules_` | array of object | Optional | - | Configure access permission list for specific routes or domains to authenticate requests |
Filed descriptions of `consumers` items:
| Name | Type | Requirement | Default Value | Description |
| ------------ | ------ | ----------- | ------------- | ------------------------------------- |
| `credential` | string | Required | - | Credential for this consumer's access |
| `name` | string | Required | - | Name of this consumer |
Configuration field descriptions for each item in `_rules_` are as follows:
| Field Name | Data Type | Requirement | Default | Description |
| ---------------- | --------------- | ------------------------------------------------- | ------ | -------------------------------------------------- |
| `_match_route_` | array of string | One of `_match_route_` or `_match_domain_` | - | Configure the routes to match for request authorization |
| `_match_domain_` | array of string | One of `_match_route_` , `_match_domain_` | - | Configure the domains to match for request authorization |
| `allow` | array of string | Required | - | Configure the consumer names allowed to access requests that match the match condition |
## Operation Attributes
Plugin execution stage: `Authentication Phase`
Plugin execution priority: `320`
## Configuration Fields
**Note:**
- In one rule, authentication configurations and authorization configurations cannot coexist.
- For requests that pass authentication, the request header will include an `X-Mse-Consumer` field to identify the caller's name.
- If the `_rules_` field is not configured, authentication is enabled for all routes of the current gateway instance by default;
- For authenticated requests, `X-Mse-Consumer` field will be added to the request header to identify the name of the caller.
### Authentication Configuration
| Name | Data Type | Requirements | Default Value | Description |
| ------------- | ---------------- | ------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `global_auth` | bool | Optional (**instance-level only**) | - | Can only be configured at the instance level. If set to true, the authentication mechanism will take effect globally; if set to false, it will only take effect for the configured domains and routes. If not configured, it will only take effect globally when there are no domain and route configurations (compatible with old user habits). |
| `consumers` | array of object | Required | - | Configures the service callers for request authentication. |
# Configuration Samples
## Enable Authentication and Authorization for specific routes or domains
The following configuration will enable Basic Auth authentication and authorization for specific routes or domains of the gateway. Note that the username and password in the credential information are separated by a ":", and the `credential` field cannot be repeated.
Each configuration field in `consumers` is described as follows:
| Name | Data Type | Requirements | Default Value | Description |
| ------------ | --------- | ------------ | ------------- | ------------------------------- |
| `credential` | string | Required | - | Configures the access credentials for this consumer. |
| `name` | string | Required | - | Configures the name of this consumer. |
### Authorization Configuration (Optional)
| Name | Data Type | Requirements | Default Value | Description |
| ---------------- | ---------------- | ---------------------------------------------------- | -------------- | -------------------------------------------------------- |
| `allow` | array of string | Required | - | Configures the consumer names allowed to access for matching requests. |
## Configuration Example
### Global Authentication and Route Granularity Authorization
The following configuration will enable Basic Auth authentication and authorization for specific routes or domains of the gateway. Note that the username and password in the credential information are separated by ":", and the `credential` field cannot be duplicated.
Make the following plugin configuration at the instance level:
```yaml
# use the _rules_ field for fine-grained rule configuration.
consumers:
- credential: 'admin:123456'
name: consumer1
- credential: 'guest:abc'
name: consumer2
_rules_:
# rule 1: match by the route name.
- _match_route_:
- route-a
- route-b
allow:
- consumer1
# rule 2: match by the domain.
- _match_domain_:
- "*.example.com"
- test.com
allow:
- consumer2
global_auth: false
```
In this sample, `route-a` and `route-b` specified in `_match_route_` are the route names filled in when creating gateway routes. When these two routes are matched, the caller with `name` as `consumer1` is allowed to access, and other callers are not allowed to access.
The `*.example.com` and `test.com` specified in `_match_domain_` are used to match the domain name of the request. When the domain name is matched, the caller with `name` as `consumer2` is allowed to access, and other callers are not allowed to access.
For routes `route-a` and `route-b`, configure as follows:
```yaml
allow:
- consumer1
```
For the domains `*.example.com` and `test.com`, configure as follows:
```yaml
allow:
- consumer2
```
### According to this configuration, the following requests are allowed:
If configured in the console, the specified `route-a` and `route-b` refer to the route names filled in when creating the routes in the console. When matching these two routes, callers with the name `consumer1` will be allowed access, while other callers will not.
**Requests with specified username and password**
The specified `*.example.com` and `test.com` are used to match the request domain. When a match is found, callers with the name `consumer2` will be allowed access, while other callers will not.
Based on this configuration, the following requests may be allowed access:
**Request with specified username and password**
```bash
# Assuming the following request will match with route-a
# Use -u option of curl to specify the credentials
# Assuming the following request matches the route-a route
# Using curl's -u parameter to specify
curl -u admin:123456 http://xxx.hello.com/test
# Or specify the Authorization request header directly with the credentials in base64 encoding
# Or directly specify the Authorization request header with the username and password encoded in base64
curl -H 'Authorization: Basic YWRtaW46MTIzNDU2' http://xxx.hello.com/test
```
A `X-Mse-Consumer` field will be added to the headers of the request, and its value in this example is `consumer1`, used to identify the name of the caller when passed authentication and authorization.
After successful authentication, the request header will have an added `X-Mse-Consumer` field, which in this case is `consumer1` to identify the caller's name.
### The following requests will be denied:
**Requests without providing username and password, returning 401**
The following requests will be denied access:
**Request without username and password, returns 401**
```bash
curl http://xxx.hello.com/test
```
**Requests with incorrect username or password, returning 401**
**Request with incorrect username and password, returns 401**
```bash
curl -u admin:abc http://xxx.hello.com/test
```
**Requests matched with a caller who has no access permission, returning 403**
**Caller matched by username and password has no access, returns 403**
```bash
# consumer2 is not in the allow list of route-a
# consumer2 is not in the allow list for route-a
curl -u guest:abc http://xxx.hello.com/test
```
## Enable basic auth for gateway instance
The following configuration does not specify the `_rules_` field, so Basic Auth authentication will be effective for the whole gateway instance.
```yaml
consumers:
- credential: 'admin:123456'
name: consumer1
- credential: 'guest:abc'
name: consumer2
```
# Error Codes
| HTTP Status Code | Error Info | Reason |
| ----------- | ------------------------------------------------------------------------------ | ---------------------- |
| 401 | Request denied by Basic Auth check. No Basic Authentication information found. | Credentials not provided in the request |
| 401 | Request denied by Basic Auth check. Invalid username and/or password | Invalid username and/or password |
| 403 | Request denied by Basic Auth check. Unauthorized consumer | Unauthorized consumer |
## Related Error Codes
| HTTP Status Code | Error Message | Reason Description |
| ---------------- | ------------------------------------------------------------------------------------- | -------------------------------- |
| 401 | Request denied by Basic Auth check. No Basic Authentication information found. | Request did not provide credentials. |
| 401 | Request denied by Basic Auth check. Invalid username and/or password. | Request credentials are invalid. |
| 403 | Request denied by Basic Auth check. Unauthorized consumer. | The caller making the request does not have access. |

View File

@@ -1,11 +1,21 @@
<p>
<a href="README_EN.md"> English </a> | 中文
</p>
---
title: Bot 拦截
keywords: [higress,bot detect]
description: Bot 拦截插件配置参考
---
## 功能说明
# 功能说明
`bot-detect`插件可以用于识别并阻止互联网爬虫对站点资源的爬取
# 配置字段
## 运行属性
插件执行阶段:`授权阶段`
插件执行优先级:`310`
## 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| -------- | -------- | -------- | -------- | -------- |
@@ -33,9 +43,9 @@
(CSimpleSpider|Cityreview Robot|CrawlDaddy|CrawlFire|Finderbots|Index crawler|Job Roboter|KiwiStatus Spider|Lijit Crawler|QuerySeekerSpider|ScollSpider|Trends Crawler|USyd-NLP-Spider|SiteCat Webbot|BotName\/\$BotVersion|123metaspider-Bot|1470\.net crawler|50\.nu|8bo Crawler Bot|Aboundex|Accoona-[A-z]{1,30}-Agent|AdsBot-Google(?:-[a-z]{1,30}|)|altavista|AppEngine-Google|archive.{0,30}\.org_bot|archiver|Ask Jeeves|[Bb]ai[Dd]u[Ss]pider(?:-[A-Za-z]{1,30})(?:-[A-Za-z]{1,30}|)|bingbot|BingPreview|blitzbot|BlogBridge|Bloglovin|BoardReader Blog Indexer|BoardReader Favicon Fetcher|boitho.com-dc|BotSeer|BUbiNG|\b\w{0,30}favicon\w{0,30}\b|\bYeti(?:-[a-z]{1,30}|)|Catchpoint(?: bot|)|[Cc]harlotte|Checklinks|clumboot|Comodo HTTP\(S\) Crawler|Comodo-Webinspector-Crawler|ConveraCrawler|CRAWL-E|CrawlConvera|Daumoa(?:-feedfetcher|)|Feed Seeker Bot|Feedbin|findlinks|Flamingo_SearchEngine|FollowSite Bot|furlbot|Genieo|gigabot|GomezAgent|gonzo1|(?:[a-zA-Z]{1,30}-|)Googlebot(?:-[a-zA-Z]{1,30}|)|Google SketchUp|grub-client|gsa-crawler|heritrix|HiddenMarket|holmes|HooWWWer|htdig|ia_archiver|ICC-Crawler|Icarus6j|ichiro(?:/mobile|)|IconSurf|IlTrovatore(?:-Setaccio|)|InfuzApp|Innovazion Crawler|InternetArchive|IP2[a-z]{1,30}Bot|jbot\b|KaloogaBot|Kraken|Kurzor|larbin|LEIA|LesnikBot|Linguee Bot|LinkAider|LinkedInBot|Lite Bot|Llaut|lycos|Mail\.RU_Bot|masscan|masidani_bot|Mediapartners-Google|Microsoft .{0,30} Bot|mogimogi|mozDex|MJ12bot|msnbot(?:-media {0,2}|)|msrbot|Mtps Feed Aggregation System|netresearch|Netvibes|NewsGator[^/]{0,30}|^NING|Nutch[^/]{0,30}|Nymesis|ObjectsSearch|OgScrper|Orbiter|OOZBOT|PagePeeker|PagesInventory|PaxleFramework|Peeplo Screenshot Bot|PlantyNet_WebRobot|Pompos|Qwantify|Read%20Later|Reaper|RedCarpet|Retreiver|Riddler|Rival IQ|scooter|Scrapy|Scrubby|searchsight|seekbot|semanticdiscovery|SemrushBot|Simpy|SimplePie|SEOstats|SimpleRSS|SiteCon|Slackbot-LinkExpanding|Slack-ImgProxy|Slurp|snappy|Speedy Spider|Squrl Java|Stringer|TheUsefulbot|ThumbShotsBot|Thumbshots\.ru|Tiny Tiny RSS|Twitterbot|WhatsApp|URL2PNG|Vagabondo|VoilaBot|^vortex|Votay bot|^voyager|WASALive.Bot|Web-sniffer|WebThumb|WeSEE:[A-z]{1,30}|WhatWeb|WIRE|WordPress|Wotbox|www\.almaden\.ibm\.com|Xenu(?:.s|) Link Sleuth|Xerka [A-z]{1,30}Bot|yacy(?:bot|)|YahooSeeker|Yahoo! Slurp|Yandex\w{1,30}|YodaoBot(?:-[A-z]{1,30}|)|YottaaMonitor|Yowedo|^Zao|^Zao-Crawler|ZeBot_www\.ze\.bz|ZooShot|ZyBorg)(?:[ /]v?(\d+)(?:\.(\d+)(?:\.(\d+)|)|)|)
```
# 配置示例
## 配置示例
## 放行原本命中爬虫规则的请求
### 放行原本命中爬虫规则的请求
```yaml
allow:
- ".*Go-http-client.*"
@@ -44,7 +54,7 @@ allow:
若不作该配置,默认的 Golang 网络库请求会被视做爬虫,被禁止访问
## 增加爬虫判断
### 增加爬虫判断
```yaml
deny:
- "spd-tools.*"
@@ -56,24 +66,3 @@ deny:
curl http://example.com -H 'User-Agent: spd-tools/1.1'
curl http://exmaple.com -H 'User-Agent: spd-tools'
```
## 对特定路由或域名开启
```yaml
# 使用 _rules_ 字段进行细粒度规则配置
_rules_:
# 规则一:按路由名称匹配生效
- _match_route_:
- route-a
- route-b
# 规则二:按域名匹配生效
- _match_domain_:
- "*.example.com"
- test.com
allow:
- ".*Go-http-client.*"
```
此例 `_match_route_` 中指定的 `route-a``route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将使用此段配置;
此例 `_match_domain_` 中指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将使用此段配置;
配置的匹配生效顺序,将按照 `_rules_` 下规则的排列顺序,匹配第一个规则后生效对应配置,后续规则将被忽略。

View File

@@ -1,22 +1,26 @@
<p>
English | <a href="README.md">中文</a>
</p>
---
title: Bot Detect
keywords: [higress, bot detect]
description: Bot detect plugin configuration reference
---
## Function Description
The `bot-detect` plugin can be used to identify and block internet crawlers from accessing site resources.
# Description
`bot-detect` plugin can be used to identify and prevent web crawlers from crawling websites.
## Running Properties
Plugin Execution Phase: `Authorization Phase`
Plugin Execution Priority: `310`
# Configuration Fields
## Configuration Fields
| Name | Data Type | Required | Default Value | Description |
| ----------------- | ------------------- | --------------| --------------| ---------------------------------------------------------- |
| allow | array of string | Optional | - | Regular expressions to match the User-Agent request header; requests matching will be allowed to access. |
| deny | array of string | Optional | - | Regular expressions to match the User-Agent request header; requests matching will be blocked. |
| blocked_code | number | Optional | 403 | HTTP status code returned when a request is blocked. |
| blocked_message | string | Optional | - | HTTP response body returned when a request is blocked. |
| Name | Type | Requirement | Default Value | Description |
| -------- | -------- | -------- | -------- | -------- |
| allow | array of string | Optional | - | A regular expression to match the User-Agent request header and will allow access if the match hits |
| deny | array of string | Optional | - | A regular expression to match the User-Agent request header and will block the request if the match hits |
| blocked_code | number | Optional | 403 | The HTTP status code returned when a request is blocked |
| blocked_message | string | Optional | - | The HTTP response Body returned when a request is blocked |
The `allow` and `deny` fields can both be left unconfigured, in which case the default crawler identification logic will be executed. Configuring the `allow` field can allow requests that would otherwise hit the default crawler identification logic. Configuring the `deny` field can add additional crawler identification logic.
If field `allow` and field `deny` are not configured at the same time, the default logic to identify crawlers will be executed. By configuring the `allow` field, requests that would otherwise hit the default logic can be allowed. The judgement can be extended by configuring the `deny` field
The default set of crawler judgment regular expressions is as follows
The default crawler identification regular expression set is as follows:
```bash
# Bots General matcher 'name/0.0'
@@ -33,45 +37,23 @@ The default set of crawler judgment regular expressions is as follows
(CSimpleSpider|Cityreview Robot|CrawlDaddy|CrawlFire|Finderbots|Index crawler|Job Roboter|KiwiStatus Spider|Lijit Crawler|QuerySeekerSpider|ScollSpider|Trends Crawler|USyd-NLP-Spider|SiteCat Webbot|BotName\/\$BotVersion|123metaspider-Bot|1470\.net crawler|50\.nu|8bo Crawler Bot|Aboundex|Accoona-[A-z]{1,30}-Agent|AdsBot-Google(?:-[a-z]{1,30}|)|altavista|AppEngine-Google|archive.{0,30}\.org_bot|archiver|Ask Jeeves|[Bb]ai[Dd]u[Ss]pider(?:-[A-Za-z]{1,30})(?:-[A-Za-z]{1,30}|)|bingbot|BingPreview|blitzbot|BlogBridge|Bloglovin|BoardReader Blog Indexer|BoardReader Favicon Fetcher|boitho.com-dc|BotSeer|BUbiNG|\b\w{0,30}favicon\w{0,30}\b|\bYeti(?:-[a-z]{1,30}|)|Catchpoint(?: bot|)|[Cc]harlotte|Checklinks|clumboot|Comodo HTTP\(S\) Crawler|Comodo-Webinspector-Crawler|ConveraCrawler|CRAWL-E|CrawlConvera|Daumoa(?:-feedfetcher|)|Feed Seeker Bot|Feedbin|findlinks|Flamingo_SearchEngine|FollowSite Bot|furlbot|Genieo|gigabot|GomezAgent|gonzo1|(?:[a-zA-Z]{1,30}-|)Googlebot(?:-[a-zA-Z]{1,30}|)|Google SketchUp|grub-client|gsa-crawler|heritrix|HiddenMarket|holmes|HooWWWer|htdig|ia_archiver|ICC-Crawler|Icarus6j|ichiro(?:/mobile|)|IconSurf|IlTrovatore(?:-Setaccio|)|InfuzApp|Innovazion Crawler|InternetArchive|IP2[a-z]{1,30}Bot|jbot\b|KaloogaBot|Kraken|Kurzor|larbin|LEIA|LesnikBot|Linguee Bot|LinkAider|LinkedInBot|Lite Bot|Llaut|lycos|Mail\.RU_Bot|masscan|masidani_bot|Mediapartners-Google|Microsoft .{0,30} Bot|mogimogi|mozDex|MJ12bot|msnbot(?:-media {0,2}|)|msrbot|Mtps Feed Aggregation System|netresearch|Netvibes|NewsGator[^/]{0,30}|^NING|Nutch[^/]{0,30}|Nymesis|ObjectsSearch|OgScrper|Orbiter|OOZBOT|PagePeeker|PagesInventory|PaxleFramework|Peeplo Screenshot Bot|PlantyNet_WebRobot|Pompos|Qwantify|Read%20Later|Reaper|RedCarpet|Retreiver|Riddler|Rival IQ|scooter|Scrapy|Scrubby|searchsight|seekbot|semanticdiscovery|SemrushBot|Simpy|SimplePie|SEOstats|SimpleRSS|SiteCon|Slackbot-LinkExpanding|Slack-ImgProxy|Slurp|snappy|Speedy Spider|Squrl Java|Stringer|TheUsefulbot|ThumbShotsBot|Thumbshots\.ru|Tiny Tiny RSS|Twitterbot|WhatsApp|URL2PNG|Vagabondo|VoilaBot|^vortex|Votay bot|^voyager|WASALive.Bot|Web-sniffer|WebThumb|WeSEE:[A-z]{1,30}|WhatWeb|WIRE|WordPress|Wotbox|www\.almaden\.ibm\.com|Xenu(?:.s|) Link Sleuth|Xerka [A-z]{1,30}Bot|yacy(?:bot|)|YahooSeeker|Yahoo! Slurp|Yandex\w{1,30}|YodaoBot(?:-[A-z]{1,30}|)|YottaaMonitor|Yowedo|^Zao|^Zao-Crawler|ZeBot_www\.ze\.bz|ZooShot|ZyBorg)(?:[ /]v?(\d+)(?:\.(\d+)(?:\.(\d+)|)|)|)
```
# Configuration Samples
## Release Requests that would otherwise Hit the Crawler Rules
## Configuration Example
### Allowing Requests That Hit the Crawler Rules
```yaml
allow:
- ".*Go-http-client.*"
```
Without this configuration, the default Golang web library request will be treated as a crawler and access will be denied.
If this configuration is not made, requests from the default Golang network library will be treated as crawlers and blocked.
## Add Crawler Judgement
### Adding Crawler Identification
```yaml
deny:
- "spd-tools.*"
```
According to this configuration, the following requests will be denied:
With this configuration, the following requests will be blocked:
```bash
curl http://example.com -H 'User-Agent: spd-tools/1.1'
curl http://exmaple.com -H 'User-Agent: spd-tools'
```
## Only Enabled for Specific Routes or Domains
```yaml
# Use _rules_ field for fine-grained rule configurations
_rules_:
# Rule 1: Match by route name
- _match_route_:
- route-a
- route-b
# Rule 2: Match by domain
- _match_domain_:
- "*.example.com"
- test.com
allow:
- ".*Go-http-client.*"
```
In the rule sample of `_match_route_`, `route-a` and `route-b` are the route names provided when creating a new gateway route. When the current route names matches the configuration, the rule following shall be applied.
In the rule sample of `_match_domain_`, `*.example.com` and `test.com` are the domain names used for request matching. When the current domain name matches the configuration, the rule following shall be applied.
All rules shall be checked following the order of items in the `_rules_` field, The first matched rule will be applied. All remained will be ignored.

View File

@@ -1,11 +1,19 @@
<p>
<a href="README_EN.md"> English </a> | 中文
</p>
---
title: 自定义应答
keywords: [higress,customn response]
description: 自定义应答插件配置参考
---
# 功能说明
## 功能说明
`custom-response`插件支持配置自定义的响应,包括自定义 HTTP 应答状态码、HTTP 应答头,以及 HTTP 应答 Body。可以用于 Mock 响应,也可以用于判断特定状态码后给出自定义应答,例如在触发网关限流策略时实现自定义响应。
# 配置字段
## 运行属性
插件执行阶段:`认证阶段`
插件执行优先级:`910`
## 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| -------- | -------- | -------- | -------- | -------- |
@@ -14,9 +22,9 @@
| body | string | 选填 | - | 自定义 HTTP 应答 Body |
| enable_on_status | array of number | 选填 | - | 匹配原始状态码,生成自定义响应,不填写时,不判断原始状态码 |
# 配置示例
## 配置示例
## Mock 应答场景
### Mock 应答场景
```yaml
status_code: 200
@@ -38,7 +46,7 @@ Content-Length: 17
{"hello":"world"}
```
## 触发限流时自定义响应
### 触发限流时自定义响应
```yaml
enable_on_status:
@@ -58,27 +66,3 @@ Location: https://example.com
从而实现基于浏览器 302 重定向机制,将限流后的用户引导到其他页面,比如可以是一个 CDN 上的静态页面。
如果希望触发限流时,正常返回其他应答,参考 Mock 应答场景配置相应的字段即可。
## 对特定路由或域名开启
```yaml
# 使用 _rules_ 字段进行细粒度规则配置
_rules_:
# 规则一:按路由名称匹配生效
- _match_route_:
- route-a
- route-b
body: "{\"hello\":\"world\"}"
# 规则二:按域名匹配生效
- _match_domain_:
- "*.example.com"
- test.com
enable_on_status:
- 429
status_code: 200
headers:
- Content-Type=application/json
body: "{\"errmsg\": \"rate limited\"}"
```
此例 `_match_route_` 中指定的 `route-a``route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将使用此段配置;
此例 `_match_domain_` 中指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将使用此段配置;
配置的匹配生效顺序,将按照 `_rules_` 下规则的排列顺序,匹配第一个规则后生效对应配置,后续规则将被忽略。

View File

@@ -1,84 +1,54 @@
<p>
English | <a href="README.md">中文</a>
</p>
---
title: Custom Response
keywords: [higress, custom response]
description: Custom response plugin configuration reference
---
## Function Description
The `custom-response` plugin supports the configuration of custom responses, including custom HTTP response status codes, HTTP response headers, and HTTP response bodies. It can be used for Mock responses or for providing custom responses based on specific status codes, such as implementing custom responses when triggering the gateway rate-limiting policy.
# Description
`custom-response` plugin implements a function of sending custom responses, including custom HTTP response status codes, HTTP response headers and HTTP response body, which can be used in the scenarios of response mocking and sending a custom response for specific status codes, such as customizing the response for rate-limited requests.
## Running Attributes
Plugin Execution Phase: `Authentication Phase`
# Configuration Fields
Plugin Execution Priority: `910`
| Name | Type | Requirement | Default Value | Description |
## Configuration Fields
| Name | Data Type | Requirements | Default Value | Description |
| -------- | -------- | -------- | -------- | -------- |
| status_code | number | Optional | 200 | Custom HTTP response status code |
| headers | array of string | Optional | - | Custom HTTP response header. Key and value shall be separated using `=`. |
| headers | array of string | Optional | - | Custom HTTP response headers, keys and values separated by `=` |
| body | string | Optional | - | Custom HTTP response body |
| enable_on_status | array of number | Optional | - | The original response status code to match. Generate the custom response only the actual status code matches the configuration. Ignore the status code match if left unconfigured. |
# Configuration Samples
## Mock Responses
| enable_on_status | array of number | Optional | - | Match original status codes to generate custom responses; if not specified, the original status code is not checked |
## Configuration Example
### Mock Response Scenario
```yaml
status_code: 200
headers:
- Content-Type=application/json
- Hello=World
body: "{\"hello\":\"world\"}"
```
According to the configuration above, all the requests will get the following custom response:
With this configuration, the request will return the following custom response:
```text
HTTP/1.1 200 OK
Content-Type: application/json
Hello: World
Content-Length: 17
{"hello":"world"}
```
## Send a Custom Response when Rate-Limited
### Custom Response on Rate Limiting
```yaml
enable_on_status:
enable_on_status:
- 429
status_code: 302
headers:
- Location=https://example.com
```
When rate-limited, normally gateway will return a status code of `429` . Now, rate-limited requests will get the following custom response:
When the gateway rate limiting is triggered, it generally returns the `429` status code, and the request will return the following custom response:
```text
HTTP/1.1 302 Found
Location: https://example.com
```
This achieves the goal of redirecting users who have been rate-limited to another page based on the browser's 302 redirect mechanism, which could be a static page on a CDN.
So based on the 302 redirecting mechanism provided by browsers, this can redirect rate-limited users to other pages, for example, a static page hosted on CDN.
If you'd like to send other responses when rate-limited, please add other fields into the configuration, referring to the Mock Responses scenario.
## Only Enabled for Specific Routes or Domains
```yaml
# Use _rules_ field for fine-grained rule configurations
_rules_:
# Rule 1: Match by route name
- _match_route_:
- route-a
- route-b
body: "{\"hello\":\"world\"}"
# Rule 2: Match by domain
- _match_domain_:
- "*.example.com"
- test.com
enable_on_status:
- 429
status_code: 200
headers:
- Content-Type=application/json
body: "{\"errmsg\": \"rate limited\"}"
```
In the rule sample of `_match_route_`, `route-a` and `route-b` are the route names provided when creating a new gateway route. When the current route names matches the configuration, the rule following shall be applied.
In the rule sample of `_match_domain_`, `*.example.com` and `test.com` are the domain names used for request matching. When the current domain name matches the configuration, the rule following shall be applied.
All rules shall be checked following the order of items in the `_rules_` field, The first matched rule will be applied. All remained will be ignored.
If you wish to return other responses normally when rate limiting is triggered, just refer to the Mock response scenario to configure the relevant fields accordingly.

View File

@@ -1,13 +1,32 @@
# 功能说明
---
title: HMAC 认证
keywords: [higress,hmac auth]
description: HMAC 认证插件配置参考
---
## 功能说明
`hmac-auth`插件实现了基于 HMAC 算法为 HTTP 请求生成不可伪造的签名,并基于签名实现身份认证和鉴权
# 配置字段
## 运行属性
插件执行阶段:`认证阶段`
插件执行优先级:`330`
## 配置字段
**注意:**
- 在一个规则里,鉴权配置和认证配置不可同时存在
- 对于通过认证鉴权的请求请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
### 认证配置
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ------------- | --------------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `global_auth` | bool | 选填(**仅实例级别配置** | - | 只能在实例级别配置若配置为true则全局生效认证机制; 若配置为false则只对做了配置的域名和路由生效认证机制若不配置则仅当没有域名和路由配置时全局生效兼容老用户使用习惯。 |
| `consumers` | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
| `date_offset` | number | 选填 | - | 配置允许的客户端最大时间偏移,单位为秒,根据请求头`Date`解析客户端 UTC 时间,可用于避免请求重放;未配置时,不做校验 |
| `_rules_` | array of object | 选填 | - | 配置特定路由或域名的访问权限列表,用于对请求进行鉴权 |
`consumers`中每一项的配置字段说明如下:
@@ -17,24 +36,21 @@
| `secret` | string | 必填 | - | 配置用于生成签名的secret |
| `name` | string | 必填 | - | 配置该consumer的名称 |
`_rules_` 中每一项的配置字段说明如下:
### 鉴权配置(非必需)
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ---------------- | --------------- | ------------------------------------------------- | ------ | -------------------------------------------------- |
| `_match_route_` | array of string | 选填`_match_route_``_match_domain_`中选填一项 | - | 配置要匹配的路由名称 |
| `_match_domain_` | array of string | 选填,`_match_route_``_match_domain_`中选填一项 | - | 配置要匹配的域名 |
| `allow` | array of string | 必填 | - | 对于符合匹配条件的请求配置允许访问的consumer名称 |
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `allow` | array of string | 选填(**非实例级别配置**) | - | 只能在路由或域名等细粒度规则上配置,对于符合匹配条件的请求,配置允许访问的 consumer从而实现细粒度的权限控制 |
**注意:**
- 若不配置`_rules_`字段,则默认对当前网关实例的所有路由开启认证;
- 对于通过认证鉴权的请求请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
# 配置示例
## 配置示例
以下配置将对网关特定路由或域名开启 Hmac Auth 认证和鉴权,注意`key`字段不能重复
### 全局配置认证和路由粒度进行鉴权
在实例级别做如下插件配置, 注意`key`字段不能重复:
## 对特定路由或域名开启
```yaml
global_auth: false
consumers:
- key: appKey-example-1
secret: appSecret-example-1
@@ -42,34 +58,33 @@ consumers:
- key: appKey-example-2
secret: appSecret-example-2
name: consumer-2
# 使用 _rules_ 字段进行细粒度规则配置
_rules_:
# 规则一:按路由名称匹配生效
- _match_route_:
- route-a
- route-b
allow:
- consumer-1
# 规则二:按域名匹配生效
- _match_domain_:
- "*.example.com"
- test.com
allow:
- consumer-2
```
每条匹配规则下的`allow`字段用于指定该匹配条件下允许访问的调用者列表;
此例 `_match_route_` 中指定的 `route-a``route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将允许`name``consumer-1`的调用者访问,其他调用者不允许访问;
此例 `_match_domain_` 中指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将允许`name``consumer-2`的调用者访问,其他调用者不允许访问;
认证成功后请求的header中会被添加一个`X-Mse-Consumer`字段,其值为调用方的名称,例如`consumer-1`
## 网关实例级别开启
以下配置将对网关实例级别开启 Hamc Auth 认证
route-a和route-b两个路由做如下插件配置
```yaml
allow:
- consumer1
```
在*.example.com和test.com两个域名做如下插件配置
```yaml
allow:
- consumer2
```
若是在控制台进行配置此例指定的route-a和route-b即在创建网关路由时填写的路由名称当匹配到这两个路由时将允许name为consumer1的调用者访问其他调用者不允许访问。
此例指定的*.example.com和test.com用于匹配请求的域名当发现域名匹配时将允许name为consumer2的调用者访问其他调用者不被允许访问。
### 网关实例级别开启
以下配置将对网关实例级别开启 Hamc Auth 认证,所有请求均需要经过认证后才能访问。
```yaml
global_auth: true
consumers:
- key: appKey-example-1
secret: appSecret-example-1
@@ -80,18 +95,18 @@ consumers:
```
# 签名机制说明
## 签名机制说明
## 配置准备
### 配置准备
如上指引,在插件配置中配置生成和验证签名需要用的凭证配置
- key: 用于请求头 `x-ca-key` 中设置
- secret: 用于生成请求签名
## 客户端签名生成方式
### 客户端签名生成方式
### 流程简介
#### 流程简介
客户端生成签名一共分三步处理:
@@ -104,7 +119,7 @@ consumers:
如下图所示:
![](https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/1745707061/p188113.png)
### 签名串提取流程
#### 签名串提取流程
客户端需要从Http请求中提取出关键数据组合成一个签名串生成的签名串的格式如下
@@ -160,7 +175,7 @@ Path + "?" + Key1 + "=" + Value1 + "&" + Key2 + "=" + Value2 + ... "&" + KeyN +
4. Query和Form存在数组参数时key相同value不同的参数 取第一个Value参与签名计算
### 签名串提取示例
#### 签名串提取示例
初始的HTTP请求
```text
@@ -190,7 +205,7 @@ x-ca-timestamp:1525872629832
/http2test/test?param1=test&password=123456789&username=xiaoming
```
### 签名计算流程
#### 签名计算流程
客户端从HTTP请求中提取出关键数据组装成签名串后需要对签名串进行加密及编码处理形成最终的签名
@@ -206,7 +221,7 @@ String sign = Base64.encodeBase64String(result);
总结一下,就是将 `stringToSign` 使用UTF-8解码后得到Byte数组然后使用加密算法对Byte数组进行加密然后使用Base64算法进行编码形成最终的签名。
### 添加签名流程
#### 添加签名流程
客户端需要将以下四个Header放在HTTP请求中传输给API网关进行签名校验
@@ -238,9 +253,9 @@ content-length:33
username=xiaoming&password=123456789
```
## 服务端签名验证方式
### 服务端签名验证方式
### 流程简介
#### 流程简介
服务器验证客户端签名一共分四步处理:
@@ -256,7 +271,7 @@ username=xiaoming&password=123456789
![](https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/1745707061/p188116.png)
## 签名排错方法
### 签名排错方法
网关签名校验失败时会将服务端的签名串StringToSign放到HTTP Response的Header中返回到客户端Key为X-Ca-Error-Message用户只需要将本地计算的签名串StringToSign与服务端返回的签名串进行对比即可找到问题
@@ -269,7 +284,7 @@ X-Ca-Error-Message: Server StringToSign:`GET#application/json##application/json
```
# 相关错误码
## 相关错误码
| HTTP 状态码 | 出错信息 | 原因说明 |
| ----------- | ---------------------- | -------------------------------------------------------------------------------- |
@@ -281,5 +296,3 @@ X-Ca-Error-Message: Server StringToSign:`GET#application/json##application/json
| 413 | Request Body Too Large | 请求 Body 超过限制大小32 MB |
| 413 | Payload Too Large | 请求 Body 超过全局配置 DownstreamConnectionBufferLimits |
| 403 | Unauthorized Consumer | 请求的调用方无访问权限 |

View File

@@ -1,76 +1,71 @@
# Function Description
The `hmac-auth` plugin implements the generation of tamper-proof signatures for HTTP requests based on HMAC algorithm, and uses the signature for identity authentication and authorization.
---
title: HMAC Authentication
keywords: [higress,hmac auth]
description: HMAC Authentication plugin configuration reference
---
## Function Description
The `hmac-auth` plugin implements the generation of tamper-proof signatures for HTTP requests based on the HMAC algorithm, and performs authentication and authorization based on the signature.
# Configuration Fields
## Running Attributes
Plugin execution phase: `Authentication phase`
Plugin execution priority: `330`
| Name | Data Type | Required | Default | Description |
| ------------- | --------------- | -------------| ------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| `consumers` | array of object | Required | - | Configures the caller of the service to authenticate the request. |
| `date_offset` | number | Optional | - | Configures the maximum allowed time deviation of the client, in seconds. It is used to parse the client's UTC time from `the Date` header of the request, and can be used to prevent replay attacks. If not configured, no validation is performed. |
| `_rules_` | array of object | Optional | - | Configures the access control list for specific routes or domains, used for authorization of requests. |
## Configuration Fields
**Note:**
- In a rule, authentication and authorization configurations cannot coexist.
- For requests that pass authentication and authorization, the request header will be added with an `X-Mse-Consumer` field to identify the caller's name.
The configuration fields for each item in `consumers` are as follows :
### Authentication Configuration
| Name | Data Type | Requirement | Default Value | Description |
| ------------- | ---------------- | ------------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------- |
| `global_auth` | bool | Optional (**Instance level configuration only**) | - | Can only be configured at the instance level. If set to true, it acts globally; if false, only applies to configured domains and routes. If not configured, it will apply globally only when there are no domain and route configurations (to accommodate old user habits). |
| `consumers` | array of object | Mandatory | - | Configures the callers of the service for request authentication. |
| `date_offset` | number | Optional | - | Configures the maximum allowed client time offset, in seconds; parsed based on the request header `Date`; can be used to prevent request replay; no validation is performed if not configured. |
| Name | Data Type| Required | Default| Description |
| -------- | -------- | ------------ | ------ | ----------------------------------------------------------------------- |
| `key` | string | Required | - | Configures the key extracted from the `x-ca-key` header of the request. |
| `secret` | string | Required | - | Configures the secret used to generate the signature. |
| `name` | string | Required | - | Configures the name of the consumer. |
The configuration fields for each item in `consumers` are as follows:
| Name | Data Type | Requirement | Default Value | Description |
| -------- | --------- | ----------- | ------------- | ------------------------------------------- |
| `key` | string | Mandatory | - | Configures the key extracted from the `x-ca-key` header of the request. |
| `secret` | string | Mandatory | - | Configures the secret used to generate the signature. |
| `name` | string | Mandatory | - | Configures the name of the consumer. |
The configuration fields for each item in `_rules_` are as follows:
### Authorization Configuration (Optional)
| Name | Data Type | Requirement | Default Value | Description |
| ----------- | ---------------- | --------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allow` | array of string | Optional (**Non-instance level configuration**) | - | Can only be configured on granular rules such as routes or domains. For requests that match the conditions, configure the allowed consumers to achieve fine-grained permission control. |
| Name | Data Type | Required | Default | Description |
| ---------------- | --------------- | ------------------------------------------------- | ---------------------------- | -------------------------------------------------- |
| `_match_route_` | array of string | Optional, either `_match_route_` or `_match_domain_` must be provided | - | Configures the name of the route to match. |
| `_match_domain_` | array of string | Optional, either `_match_route_` or `_match_domain_` must be provided | - | Configures the name of the domain to match. |
| `allow` | array of string | Required | - | Configures the name of the consumer to allow for requests that match the specified route or domain. |
**Note**
- If `_rules_` is not configured, authentication is enabled for all routes on the current gateway instance by default
- For requests that pass authentication and authorization, a `X-Mse-Consumer` header will be added to the request headers to identify the name of the consumer.
# Configuration Example
The following configuration enables Hmac Auth authentication and authorization for specific routes or domains on the gateway. Note that the `key` field should not be duplicated.
## Enabling for specific routes or domains
## Configuration Example
### Global Configuration Authentication and Route Granular Authorization
Configure the following plugin settings at the instance level. Note that the `key` field cannot be duplicated:
```yaml
consumers:
global_auth: false
consumers:
- key: appKey-example-1
secret: appSecret-example-1
name: consumer-1
- key: appKey-example-2
secret: appSecret-example-2
name: consumer-2
# Configuring Fine-Grained Rules using _rules_ Field
_rules_:
# Rule 1: Matching by route name.
- _match_route_:
- route-a
- route-b
allow:
- consumer-1
# Rule 2: Applies based on domain name matching.
- _match_domain_:
- "*.example.com"
- test.com
allow:
- consumer-2
```
The `allow` field under each matching rule specifies the list of callers allowed to access under that matching condition;
In this example, `route-a` and `route-b` specified in `_match_route_` are the route names filled in when creating the gateway route. When either of these routes is matched, it will allow access to the caller named `consumer-1`, while denying access to other callers
In` _match_domain_`, `*.example.com` and `test.com` are used to match the requested domain name. When a match is found, it will allow access to the caller named `consumer-2`, while denying access to other callers
Upon successful authentication, the `X-Mse-Consumer` field will be added to the request header with the value set to the caller's name, such as `consumer-1`.。
## Enable at the Gateway Instance Level
The following configuration enables HMAC authentication at the gateway instance level.
For route-a and route-b, configure the plugin as follows:
```yaml
consumers:
allow:
- consumer1
```
For the two domains *.example.com and test.com, configure as follows:
```yaml
allow:
- consumer2
```
If configured in the console, the specified route names route-a and route-b correspond to the route names filled in when creating the gateway routes. When matched to these two routes, access will be allowed for the caller named consumer1, while other callers will not be allowed access.
The specified *.example.com and test.com are used to match the domains of the requests. When a domain match is found, access will be allowed for the caller named consumer2, while other callers will not be allowed access.
### Gateway Instance Level Activation
The following configuration will enable HMAC Auth authentication at the gateway instance level, requiring all requests to undergo authentication before access.
```yaml
global_auth: true
consumers:
- key: appKey-example-1
secret: appSecret-example-1
name: consumer-1
@@ -79,34 +74,24 @@ consumers:
name: consumer-2
```
## Signature Mechanism Description
### Configuration Preparation
As mentioned above, configure the credentials required for generating and verifying signatures in the plugin settings.
- key: to be set in the request header `x-ca-key`.
- secret: used for generating request signatures.
# Description of Signing Mechanism
### Client Signature Generation Process
#### Overview
The client generates a signature through three main steps:
1. Extract key data from the original request to create a string for signing.
2. Encrypt the key data signing string using the algorithm and the configured `secret` to obtain the signature.
3. Include all relevant headers for the signature into the original HTTP request to form the final HTTP request.
## Configuration Preparation
As mentioned in the guide above, configure the credential settings required for generating and validating signatures in the plugin configuration.
- key: Used for setting in the request header `x-ca-key`.
- secret: Used for generating the request signature.
## Client Signature Generation Method
### Overview of the Process
The process for generating a signature on the client side consists of three steps:
1. Extracting key data from the original request to obtain a string to be signed.
2. Using encryption algorithms and the configured `secret` to encrypt the key data signing string and obtain a signature.
3. Adding all headers related to the signature to the original HTTP request to obtain the final HTTP request.
As shown below :
As shown in the figure below:
![](https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/1745707061/p188113.png)
### Process for Extracting Signing String
To generate a signature, the client needs to extract key data from the HTTP request and combine it into a signing string. The format of the generated signing string is as follows:
#### Signing String Extraction Process
The client needs to extract key data from the HTTP request, combine it into a signing string, which has the following format:
```text
HTTPMethod
Accept
@@ -116,54 +101,38 @@ Date
Headers
PathAndParameters
```
The signing string consists of the above 7 fields separated by \n. If Headers is empty, no \n is needed. If other fields are empty, the \n should still be retained. The signature is case-sensitive. Below are the rules for extracting each field:
- HTTPMethod: The HTTP method used in the request, in all capital letters, such as POST.
- Accept: The value of the Accept header in the request, which can be empty. It is recommended to explicitly set the Accept header. When Accept is empty, some HTTP clients will set the default value of `*/*`, which may cause signature verification to fail.
- Content-MD5: The value of the Content-MD5 header in the request, which can be empty. It is only calculated when there is a non-form body in the request. The following is a reference calculation method for Content-MD5 values in
The seven fields above constitute the entire signing string, separated by newline characters `\n`. If Headers is empty, no newline is needed; other fields should retain `\n` if empty. The signature is case-sensitive. Below are the extraction rules for each field:
- HTTPMethod: The HTTP method, all uppercase (e.g., POST).
- Accept: The value of the Accept header in the request, can be empty. It is recommended to explicitly set the Accept Header. When Accept is empty, some HTTP clients may set a default value of `*/*`, resulting in a signature verification failure.
- Content-MD5: The value of the Content-MD5 header in the request, can be empty. It is calculated only if there is a Body in the request and it is not in Form format. Heres a reference calculation method for the Content-MD5 value in Java:
```java
String content-MD5 = Base64.encodeBase64(MD5(bodyStream.getbytes("UTF-8")));
String content-MD5 = Base64.encodeBase64(MD5(bodyStream.getBytes("UTF-8")));
```
- Content-Type: The value of the Content-Type header in the request, which can be empty.
- Date: The value of the Date header in the request. When the` date_offset` configuration is not enabled, it can be empty. Otherwise, it will be used for time offset verification.
- Headers: Users can select specific headers to participate in the signature. There are the following rules for concatenating the signature string with headers:
- The keys of the headers participating in the signature calculation are sorted in alphabetical order and concatenated as follows:
- Content-Type: The value of the Content-Type header in the request, can be empty.
- Date: The value of the Date header in the request. If the `date_offset` configuration is not turned on, it can be empty; otherwise, it will be used for time offset verification.
- Headers: Users can select specific headers to participate in the signature. The rules for concatenating the signing header string are as follows:
- The Keys of the headers participating in the signature calculation are concatenated after being sorted lexicographically, as follows:
```text
HeaderKey1 + ":" + HeaderValue1 + "\n"\+
HeaderKey2 + ":" + HeaderValue2 + "\n"\+
HeaderKey1 + ":" + HeaderValue1 + "\n" +
HeaderKey2 + ":" + HeaderValue2 + "\n" +
...
HeaderKeyN + ":" + HeaderValueN + "\n"
```
- If the value of a header is empty, it will participate in the signature with the `HeaderKey+":"+"\n"` only, and the key and english colon should be retained.
- The set of keys for all headers participating in the signature is separated by a comma and placed in the `X-Ca-Signature-Headers header`.
- If the Value of a certain header is empty, use HeaderKey + ":" + "\n" to participate in the signature, retaining the Key and the colon.
- The collection of all participating header Keys is placed in the Header with the key X-Ca-Signature-Headers, separated by commas.
- The following headers are not included in the header signature calculation: X-Ca-Signature, X-Ca-Signature-Headers, Accept, Content-MD5, Content-Type, Date.
- PathAndParameters: This field contains all parameters in the path, query, and form. The specific format is as follows:
- PathAndParameters: This field includes Path, Query, and all parameters in Form, specifically organized as follows:
```text
Path + "?" + Key1 + "=" + Value1 + "&" + Key2 + "=" + Value2 + ... "&" + KeyN + "=" + ValueN
```
Note:
1. The Key of Query and Form parameters should be sorted lexicographically before being concatenated as above.
2. If Query and Form parameters are empty, just use Path without adding `?`.
3. If the Value of parameters is empty, only the Key should be retained in the signature, the equal sign does not need to be added.
4. In the case of array parameters (parameters with the same key but different values), only the first Value should be used for signature calculation.
Notes:
1. The keys of the query and form parameter pairs are sorted alphabetically, and the same format as above is used for concatenation.
2. If there are no query and form parameters, use the path directly without adding `?` .
3. If the value of a parameter is empty, only the key will be included in the signature. The equal sign should not be included in the signature.
4. If there are array parameters in the query or form (parameters with the same key but different values), only the first value should be included in the signature calculation.
### Example of Extracting Signing String
The initial HTTP request :
#### Signing String Extraction Example
Initial HTTP request:
```text
POST /http2test/test?param1=test HTTP/1.1
host:api.aliyun.com
@@ -177,8 +146,7 @@ x-ca-nonce:c9f15cbf-f4ac-4a6c-b54d-f51abf4b5b44
content-length:33
username=xiaoming&password=123456789
```
The correct generated signature string is :
The generated correct signing string is:
```text
POST
application/json; charset=utf-8
@@ -190,13 +158,10 @@ x-ca-signature-method:HmacSHA256
x-ca-timestamp:1525872629832
/http2test/test?param1=test&password=123456789&username=xiaoming
```
#### Signature Calculation Process
After the client assembles the key data extracted from the HTTP request into a signing string, it needs to encrypt the signing string and encode it to form the final signature.
### Signature Calculation Process
After extracting the key data from the HTTP request and assembling it into a signature string, the client needs to encrypt and encode the signature string to form the final signature.
The specific encryption format is as follows, where `stringToSign` is the extracted signature string, `secret` is the one filled in the plugin configuration, and `sign` is the final generated signature:
The specific encryption form is as follows, where `stringToSign` is the extracted signing string, `secret` is the one filled in the plugin configuration, and `sign` is the final generated signature:
```java
Mac hmacSha256 = Mac.getInstance("HmacSHA256");
byte[] secretBytes = secret.getBytes("UTF-8");
@@ -204,23 +169,16 @@ hmacSha256.init(new SecretKeySpec(secretBytes, 0, secretBytes.length, "HmacSHA25
byte[] result = hmacSha256.doFinal(stringToSign.getBytes("UTF-8"));
String sign = Base64.encodeBase64String(result);
```
To summarize, the `stringToSign` is decoded using UTF-8 to obtain a Byte array, then the encryption algorithm is applied to the Byte array, and finally, the Base64 algorithm is used for encoding, forming the final signature.
In summary, the `stringToSign` is decoded using UTF-8 to obtain a Byte array. Then, an encryption algorithm is used to encrypt the Byte array, and finally, the Base64 algorithm is used to encode the encrypted data, resulting in the final signature.
### The Process of Adding a Signature
The client needs to include the following four headers in the HTTP request to be transmitted to the API gateway for signature verification:
- x-ca-key: The value is the APP Key and is required.
- x-ca-signature-method: The signature algorithm, the value can be HmacSHA256 or HmacSHA1, optional. The default value is HmacSHA256.
- x-ca-signature-headers: The collection of keys for all signature headers, separated by commas. Optional.
- x-ca-signature: The signature and it is required.
Here is an example of a complete HTTP request with a signature :
#### Adding the Signature Process
The client needs to include the following four headers in the HTTP request to transmit to the API gateway for signature verification:
- x-ca-key: The APP Key, mandatory.
- x-ca-signature-method: The signature algorithm, can be HmacSHA256 or HmacSHA1, optional, default is HmacSHA256.
- x-ca-signature-headers: The collection of all signature header Keys, separated by commas, optional.
- x-ca-signature: The signature, mandatory.
Below is an example of the entire HTTP request carrying the signature:
```text
POST /http2test/test?param1=test HTTP/1.1
host:api.aliyun.com
@@ -239,48 +197,35 @@ content-length:33
username=xiaoming&password=123456789
```
## Server-side Signature Verification Method
### Server-side Signature Verification Method
#### Overview
The server verifies the client signature through four main steps:
1. Extract key data from the received request to create a signing string.
2. Read the `key` from the received request and query the corresponding `secret`.
3. Encrypt the key data signing string using the algorithm and the `secret` to obtain the signature.
4. Read the client signature from the received request and compare the server-side signature with the client-side signature for consistency.
### Overview of the Process
The server-side signature verification of the client's request involves four steps :
1. Extract crucial data from the received request to obtain a string for signing.
2. Retrieve the `key` from the received request and use it to query its corresponding `secret`.
3. Encrypt the string for signing using the encryption algorithm and `secret`.
4. Retrieve the client's signature from the received request, and compare the consistency of the server-side signature with the client's signature.
As shown below :
As shown in the figure below:
![](https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/1745707061/p188116.png)
### Signature Troubleshooting Method
When the gateway signature verification fails, the server's signing string (StringToSign) will be returned in the HTTP Response header to the client, with the key: X-Ca-Error-Message. The user only needs to compare the locally computed signing string (StringToSign) with the signing string returned by the server to find the issue.
## Troubleshooting Signature Errors
When the gateway signature verification fails, the server-side signing string (StringToSign) will be returned to the client in the HTTP Response Header. The key is X-Ca-Error-Message. Users only need to compare the locally calculated signing string with the server-side signing string returned to locate the problem;
If the StringToSign on the server side is consistent with that on the client side, please check whether the APP Secret used for signature calculation is correct
Because line breaks cannot be represented in HTTP headers, all line breaks in the StringToSign are replaced with #, as shown below:
If the StringToSign from the server and the client are consistent, please check whether the APP Secret used for signature calculation is correct.
Since HTTP headers cannot express line breaks, the line breaks in the StringToSign have been replaced with `#`, as shown below:
```text
X-Ca-Error-Message: Server StringToSign:`GET#application/json##application/json##X-Ca-Key:200000#X-Ca-Timestamp:1589458000000#/app/v1/config/keys?keys=TEST`
```
# Related Error Codes
| HTTP Status Code | Error Message | Reason |
| ----------- | ---------------------- | -------------------------------------------------------------------------------- |
| 401 | Invalid Key | The x-ca-key request header is not provided or is invalid. |
| 401 | Empty Signature | The x-ca-signature request header does not contain a signature. |
| 400 | Invalid Signature | The x-ca-signature request header contains a signature that does not match the server-calculated signature. |
| 400 | Invalid Content-MD5 | The content-md5 request header is incorrect. |
| 400 | Invalid Date | The time offset calculated based on the date request header exceeds the configured date_offset. |
| 413 | Request Body Too Large | The request body exceeds the size limit of 32 MB. |
| 413 | Payload Too Large | The request body exceeds the DownstreamConnectionBufferLimits global configuration. |
| 403 | Unauthorized Consumer | The requesting party does not have access permission. |
## Related Error Codes
| HTTP Status Code | Error Message | Reasoning |
| ---------------- | --------------------- | --------------------------------------------- |
| 401 | Invalid Key | The request header did not provide x-ca-key, or x-ca-key is invalid. |
| 401 | Empty Signature | The request header did not provide the x-ca-signature signing string. |
| 400 | Invalid Signature | The x-ca-signature signing string in the request header does not match the signature calculated by the server. |
| 400 | Invalid Content-MD5 | The Content-MD5 header in the request is incorrect. |
| 400 | Invalid Date | The time offset calculated based on the Date header in the request exceeds the configured date_offset. |
| 413 | Request Body Too Large| The request Body exceeds the maximum size of 32 MB. |
| 413 | Payload Too Large | The request Body exceeds the global configured DownstreamConnectionBufferLimits. |
| 403 | Unauthorized Consumer | The calling party does not have access permissions for the request. |

View File

@@ -1,17 +1,216 @@
# 功能说明
---
title: JWT 认证
keywords: [higress,jwt auth]
description: JWT 认证插件配置参考
---
## 功能说明
`jwt-auth`插件实现了基于JWT(JSON Web Tokens)进行认证鉴权的功能支持从HTTP请求的URL参数、请求头、Cookie字段解析JWT同时验证该Token是否有权限访问。
本插件和`安全能力->认证鉴权`中JWT认证的区别是额外提供了调用方身份识别的能力支持对不同调用方配置不同的JWT凭证。
# 详细说明
## 运行属性
## 1、基于token的认证
插件执行阶段:`认证阶段`
插件执行优先级:`340`
### 1.1 简介
## 配置字段
**注意:**
- 在一个规则里,鉴权配置和认证配置不可同时存在
- 对于通过认证鉴权的请求请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
### 认证配置
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `global_auth` | bool | 选填(**仅实例级别配置** | - | 只能在实例级别配置若配置为true则全局生效认证机制; 若配置为false则只对做了配置的域名和路由生效认证机制若不配置则仅当没有域名和路由配置时全局生效兼容老用户使用习惯。 |
| `consumers` | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
`consumers`中每一项的配置字段说明如下:
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------------------- | ----------------- | -------- | ------------------------------------------------- | ------------------------ |
| `name` | string | 必填 | - | 配置该consumer的名称 |
| `jwks` | string | 必填 | - | https://www.rfc-editor.org/rfc/rfc7517 指定的json格式字符串是由验证JWT中签名的公钥或对称密钥组成的Json Web Key Set |
| `issuer` | string | 必填 | - | JWT的签发者需要和payload中的iss字段保持一致 |
| `claims_to_headers` | array of object | 选填 | - | 抽取JWT的payload中指定字段设置到指定的请求头中转发给后端 |
| `from_headers` | array of object | 选填 | {"name":"Authorization","value_prefix":"Bearer "} | 从指定的请求头中抽取JWT |
| `from_params` | array of string | 选填 | access_token | 从指定的URL参数中抽取JWT |
| `from_cookies` | array of string | 选填 | - | 从指定的cookie中抽取JWT |
| `clock_skew_seconds` | number | 选填 | 60 | 校验JWT的exp和iat字段时允许的时钟偏移量单位为秒 |
| `keep_token` | bool | 选填 | ture | 转发给后端时是否保留JWT |
**注意:**
- 只有当`from_headers`,`from_params`,`from_cookies`均未配置时,才会使用默认值
`from_headers` 中每一项的配置字段说明如下:
| 名称 | 数据类型 | 填写要求| 默认值 | 描述 |
| ---------------- | --------------- | ------- | ------ | --------------------------------------------------------- |
| `name` | string | 必填 | - | 抽取JWT的请求header |
| `value_prefix` | string | 必填 | - | 对请求header的value去除此前缀剩余部分作为JWT |
`claims_to_headers` 中每一项的配置字段说明如下:
| 名称 | 数据类型 | 填写要求| 默认值 | 描述 |
| ---------------- | --------------- | ------- | ------ | --------------------------------------------------------- |
| `claim` | string | 必填 | - | JWT payload中的指定字段要求必须是字符串或无符号整数类型 |
| `header` | string | 必填 | - | 从payload取出字段的值设置到这个请求头中转发给后端 |
| `override` | bool | 选填 | true | true时存在同名请求头会进行覆盖false时追加同名请求头 |
### 鉴权配置(非必需)
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `allow` | array of string | 选填(**非实例级别配置**) | - | 只能在路由或域名等细粒度规则上配置,对于符合匹配条件的请求,配置允许访问的 consumer从而实现细粒度的权限控制 |
## 配置示例
### 全局配置认证和路由粒度进行鉴权
注意如果一个JWT能匹配多个`jwks`,则按照配置顺序命中第一个匹配的`consumer`
在实例级别做如下插件配置:
```yaml
global_auth: false
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
```
对 route-a 和 route-b 这两个路由做如下配置:
```yaml
allow:
- consumer1
```
对 *.example.com 和 test.com 在这两个域名做如下配置:
```yaml
allow:
- consumer2
```
**说明:**
此例指定的route-a和route-b即在创建网关路由时填写的路由名称当匹配到这两个路由时将允许name为consumer1的调用者访问其他调用者不允许访问。
此例指定的*.example.com和test.com用于匹配请求的域名当发现域名匹配时将允许name为consumer2的调用者访问其他调用者不被允许访问。
根据该配置,下列请求可以允许访问:
假设以下请求会匹配到route-a这条路由
**将 JWT 设置在 url 参数中**
```bash
curl 'http://xxx.hello.com/test?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
**将 JWT 设置在 http 请求头中**
```bash
curl http://xxx.hello.com/test -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
认证鉴权通过后请求的header中会被添加一个`X-Mse-Consumer`字段,在此例中其值为`consumer1`,用以标识调用方的名称
下列请求将拒绝访问:
**请求未提供JWT返回401**
```bash
curl http://xxx.hello.com/test
```
**根据请求提供的JWT匹配到的调用者无访问权限返回403**
```bash
# consumer1不在*.example.com的allow列表里
curl 'http://xxx.example.com/test' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
#### 网关实例级别开启
以下配置将对网关实例级别开启 JWT Auth 认证,所有请求均需要经过认证后才能访问。
```yaml
global_auth: true
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
```
## 常见错误码说明
| HTTP 状态码 | 出错信息 | 原因说明 |
| ----------- | ---------------------- | -------------------------------------------------------------------------------- |
| 401 | Jwt missing | 请求头未提供JWT |
| 401 | Jwt expired | JWT已经过期 |
| 401 | Jwt verification fails | JWT payload校验失败如iss不匹配 |
| 403 | Access Denied | 无权限访问当前路由 |
## 详细说明
### 1、基于token的认证
#### 1.1 简介
很多对外开放的API需要识别请求者的身份并据此判断所请求的资源是否可以返回给请求者。token就是一种用于身份验证的机制基于这种机制应用不需要在服务端保留用户的认证信息或者会话信息可实现无状态、分布式的Web应用授权为应用的扩展提供了便利。
### 1.2 流程描述
#### 1.2 流程描述
![](https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/2336348951/p135822.png)
@@ -35,13 +234,13 @@
在这个整个过程中, 网关利用token认证机制实现了用户使用自己的用户体系对自己API进行授权的能力。下面我们就要介绍网关实现token认证所使用的结构化令牌Json Web Token(JWT)。
### 1.3 JWT
#### 1.3 JWT
#### 1.3.1 简介
##### 1.3.1 简介
Json Web TokeJWT是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准RFC7519。JWT一般可以用作独立的身份验证令牌可以包含用户标识、用户角色和权限等信息以便于从资源服务器获取资源也可以增加一些额外的其它业务逻辑所必须的声明信息特别适用于分布式站点的登录场景。
#### 1.3.2 JWT的构成
##### 1.3.2 JWT的构成
`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ`
@@ -119,11 +318,11 @@ var signature = HMACSHA256(encodedString, '$secret');
将这三部分用 . 连接成一个完整的字符串,就构成了 1.3.2 节最开始的JWT示例。
#### 1.3.3 时效
##### 1.3.3 时效
网关会验证token中的exp字段一旦这个字段过期了网关会认为这个token无效而将请求直接打回。过期时间这个值必须设置。
#### 1.3.4 JWT的几个特点
##### 1.3.4 JWT的几个特点
1. JWT 默认是不加密,不能将秘密数据写入 JWT。
2. JWT 不仅可以用于认证,也可以用于交换信息。有效使用 JWT可以降低服务器查询数据库的次数。
@@ -131,9 +330,9 @@ var signature = HMACSHA256(encodedString, '$secret');
4. JWT 本身包含了认证信息一旦泄露任何人都可以获得该令牌的所有权限。为了减少盗用JWT 的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
5. 为了减少盗用JWT 不应该使用 HTTP 协议明码传输要使用HTTPS 协议传输。
## 2、用户系统如何应用JWT插件保护API
### 2、用户系统如何应用JWT插件保护API
### 2.1 生成一对JWKJSON Web 密钥)
#### 2.1 生成一对JWKJSON Web 密钥)
**方法一、在线生成:**
@@ -162,7 +361,7 @@ final String publicKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLeve
final String privateKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
```
### 2.2 使用JWK中的私钥实现颁发token 的认证服务
#### 2.2 使用JWK中的私钥实现颁发token 的认证服务
需要使用2.1节中在线生成的 Keypair JSON字符串三个方框内的第一个或者本地生成的 privateKeyString JSON字符串作为私钥来颁发token用于授权可信的用户访问受保护的API具体实现可以参考下方示例。 向客户颁发token的形式由用户根据具体的业务场景决定可以将颁发token的功能部署到生产环境配置成普通API后由访问者通过用户名密码获得也可以直接在本地环境生成token 后,直接拷贝给指定用户使用。
@@ -216,187 +415,3 @@ public class GenerateJwtDemo {
}
}
```
# 插件配置说明
## 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `consumers` | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
| `_rules_` | array of object | 选填 | - | 配置特定路由或域名的访问权限列表,用于对请求进行鉴权 |
`consumers`中每一项的配置字段说明如下:
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------------------- | ----------------- | -------- | ------------------------------------------------- | ------------------------ |
| `name` | string | 必填 | - | 配置该consumer的名称 |
| `jwks` | string | 必填 | - | https://www.rfc-editor.org/rfc/rfc7517 指定的json格式字符串是由验证JWT中签名的公钥或对称密钥组成的Json Web Key Set |
| `issuer` | string | 必填 | - | JWT的签发者需要和payload中的iss字段保持一致 |
| `claims_to_headers` | array of object | 选填 | - | 抽取JWT的payload中指定字段设置到指定的请求头中转发给后端 |
| `from_headers` | array of object | 选填 | {"name":"Authorization","value_prefix":"Bearer "} | 从指定的请求头中抽取JWT |
| `from_params` | array of string | 选填 | access_token | 从指定的URL参数中抽取JWT |
| `from_cookies` | array of string | 选填 | - | 从指定的cookie中抽取JWT |
| `clock_skew_seconds` | number | 选填 | 60 | 校验JWT的exp和iat字段时允许的时钟偏移量单位为秒 |
| `keep_token` | bool | 选填 | ture | 转发给后端时是否保留JWT |
**注意:**
- 只有当`from_headers`,`from_params`,`from_cookies`均未配置时,才会使用默认值
`from_headers` 中每一项的配置字段说明如下:
| 名称 | 数据类型 | 填写要求| 默认值 | 描述 |
| ---------------- | --------------- | ------- | ------ | --------------------------------------------------------- |
| `name` | string | 必填 | - | 抽取JWT的请求header |
| `value_prefix` | string | 必填 | - | 对请求header的value去除此前缀剩余部分作为JWT |
`claims_to_headers` 中每一项的配置字段说明如下:
| 名称 | 数据类型 | 填写要求| 默认值 | 描述 |
| ---------------- | --------------- | ------- | ------ | --------------------------------------------------------- |
| `claim` | string | 必填 | - | JWT payload中的指定字段要求必须是字符串或无符号整数类型 |
| `header` | string | 必填 | - | 从payload取出字段的值设置到这个请求头中转发给后端 |
| `override` | bool | 选填 | true | true时存在同名请求头会进行覆盖false时追加同名请求头 |
`_rules_` 中每一项的配置字段说明如下:
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ---------------- | --------------- | ------------------------------------------------- | ------ | -------------------------------------------------- |
| `_match_route_` | array of string | 选填,`_match_route_``_match_domain_`中选填一项 | - | 配置要匹配的路由名称 |
| `_match_domain_` | array of string | 选填,`_match_route_``_match_domain_`中选填一项 | - | 配置要匹配的域名 |
| `allow` | array of string | 必填 | - | 对于符合匹配条件的请求配置允许访问的consumer名称 |
**注意:**
- 若不配置`_rules_`字段,则默认对当前网关实例的所有路由开启认证;
- 对于通过认证鉴权的请求请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
## 配置示例
### 对特定路由或域名开启
以下配置将对网关特定路由或域名开启 Jwt Auth 认证和鉴权注意如果一个JWT能匹配多个`jwks`,则按照配置顺序命中第一个匹配的`consumer`
```yaml
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
# 使用 _rules_ 字段进行细粒度规则配置
_rules_:
# 规则一:按路由名称匹配生效
- _match_route_:
- route-a
- route-b
allow:
- consumer1
# 规则二:按域名匹配生效
- _match_domain_:
- "*.example.com"
- test.com
allow:
- consumer2
```
此例 `_match_route_` 中指定的 `route-a``route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将允许`name``consumer1`的调用者访问,其他调用者不允许访问;
此例 `_match_domain_` 中指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将允许`name``consumer2`的调用者访问,其他调用者不允许访问。
#### 根据该配置,下列请求可以允许访问:
假设以下请求会匹配到route-a这条路由
**将 JWT 设置在 url 参数中**
```bash
curl 'http://xxx.hello.com/test?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
**将 JWT 设置在 http 请求头中**
```bash
curl http://xxx.hello.com/test -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
认证鉴权通过后请求的header中会被添加一个`X-Mse-Consumer`字段,在此例中其值为`consumer1`,用以标识调用方的名称
#### 下列请求将拒绝访问:
**请求未提供JWT返回401**
```bash
curl http://xxx.hello.com/test
```
**根据请求提供的JWT匹配到的调用者无访问权限返回403**
```bash
# consumer1不在*.example.com的allow列表里
curl 'http://xxx.example.com/test' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
### 网关实例级别开启
以下配置未指定`_rules_`字段,因此将对网关实例级别开启 JWT Auth 认证
```yaml
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
```
# 常见错误码说明
| HTTP 状态码 | 出错信息 | 原因说明 |
| ----------- | ---------------------- | -------------------------------------------------------------------------------- |
| 401 | Jwt missing | 请求头未提供JWT |
| 401 | Jwt expired | JWT已经过期 |
| 401 | Jwt verification fails | JWT payload校验失败如iss不匹配 |
| 403 | Access Denied | 无权限访问当前路由 |

View File

@@ -1,170 +1,306 @@
# Description
The `jwt-auth` plugin implements authentication and authorization based on JWT (JSON Web Token), supports parsing JWTs from URL parameters, request headers, and Cookie fields from HTTP requests, and verifies whether the token has permission to access.
---
title: JWT Authentication
keywords: [higress,jwt auth]
description: JWT Authentication plugin configuration reference
---
The difference between this plugin and the JWT authentication in `Security Capabilities->Authentication and Authorization` is that it provides additional capabilities for identifying the caller's identity, supporting the configuration of different JWT credentials for different callers.
## Function Description
`jwt-auth` plugin implements authentication and authorization based on JWT (JSON Web Tokens). It supports extracting JWT from HTTP request URL parameters, request headers, and Cookie fields, while verifying whether the Token has the necessary permissions to access the resource.
# Detailed Description
The difference between this plugin and the JWT authentication in `Security Capability -> Authentication and Authorization` is that it additionally provides the capability of identifying the caller's identity, supporting different JWT credentials for different callers.
## 1. Token-based authentication
## Runtime Properties
Plugin execution phase: `Authentication Phase`
Plugin execution priority: `340`
### 1.1 Introduction
Many open APIs need to identify the identity of the caller and determine whether the requested resource can be returned to the caller based on this identity. Token is a mechanism used for identity verification. Based on this mechanism, the application does not need to retain the user's authentication information or session information on the server, which can realize stateless and distributed web application authorization and provide convenience for application extension.
## Configuration Fields
**Note:**
- In one rule, authentication configuration and authorization configuration cannot coexist.
- For requests authenticated through authentication and authorization, the request header will be augmented with an `X-Mse-Consumer` field to identify the caller's name.
### 1.2 Process Description
### Authentication Configuration
| Name | Data Type | Requirements | Default Value | Description |
| ----------- | --------------- | ---------------------------------------------- | ------------- | ----------------------------------------------------------- |
| `global_auth` | bool | Optional (**instance-level configuration only**) | - | Can only be configured at the instance level. If set to true, it will globally enable the authentication mechanism; if set to false, it will only apply to the configured domain names and routes. If not configured, it will only globally take effect when no domain names and routes are configured (to be compatible with old user habits). |
| `consumers` | array of object | Required | - | Configure service consumers for request authentication |
![](process.png)
The configuration fields for each item in `consumers` are as follows:
| Name | Data Type | Requirements | Default Value | Description |
| ----------------------- | ------------------ | ------------ | -------------------------------------------------- | ------------------------------- |
| `name` | string | Required | - | The name of the consumer |
| `jwks` | string | Required | - | JSON format string specified by https://www.rfc-editor.org/rfc/rfc7517, consisting of the public key (or symmetric key) used to verify the JWT signature. |
| `issuer` | string | Required | - | The issuer of the JWT, must match the `iss` field in the payload. |
| `claims_to_headers` | array of object | Optional | - | Extract the specified fields from the JWT payload and set them in the specified request headers to forward to the backend. |
| `from_headers` | array of object | Optional | {"name":"Authorization","value_prefix":"Bearer "} | Extract JWT from the specified request headers. |
| `from_params` | array of string | Optional | access_token | Extract JWT from the specified URL parameters. |
| `from_cookies` | array of string | Optional | - | Extract JWT from the specified cookies. |
| `clock_skew_seconds` | number | Optional | 60 | The allowed clock skew when validating the `exp` and `iat` fields of the JWT, measured in seconds. |
| `keep_token` | bool | Optional | true | Whether to retain the JWT when forwarding to the backend. |
The above figure is the business process sequence diagram when the gateway uses JWT for authentication, and the following we will describe the steps marked in the figure in detail in words:
**Note:**
- The default values will only be used when `from_headers`, `from_params`, and `from_cookies` are not all configured.
The configuration fields for each item in `from_headers` are as follows:
| Name | Data Type | Requirements | Default Value | Description |
| --------------- | ---------------- | ------------ | ------------- | ----------------------------------------------- |
| `name` | string | Required | - | Extract JWT from the request header. |
| `value_prefix` | string | Required | - | Remove the prefix from the request header value, with the remaining part serving as the JWT. |
1. The client initiates an authentication request to the API gateway, usually carrying the end user's username and password in the request;
The configuration fields for each item in `claims_to_headers` are as follows:
| Name | Data Type | Requirements | Default Value | Description |
| --------------- | ---------------- | ------------ | ------------- | --------------------------------------------- |
| `claim` | string | Required | - | The specified field in the JWT payload, must be a string or unsigned integer type. |
| `header` | string | Required | - | The value of the field extracted from the payload is set to this request header and forwarded to the backend. |
| `override` | bool | Optional | true | If true, existing headers with the same name will be overridden; if false, they will be appended. |
2. The gateway forwards the request directly to the backend service;
### Authorization Configuration (Optional)
| Name | Data Type | Requirements | Default Value | Description |
| ----------- | --------------- | ---------------------------------------------- | ------------- | ----------------------------------------------------------------------------------------------------- |
| `allow` | array of string | Optional (**not instance-level configuration**) | - | Can only be configured on fine-grained rules such as routes or domain names, allowing specified consumers to access matching requests for fine-grained permission control. |
3. The backend service reads the authentication information (such as the username and password) in the request for verification. After the verification is passed, it uses the private key to generate a standard token and returns it to the gateway;
## Configuration Examples
### Global Configuration for Authentication and Route-Level Authorization
Note: If a JWT matches multiple `jwks`, the first matching consumer will be applied according to the configuration order.
4. The gateway returns the response with the token to the client, and the client needs to cache this token locally;
5. The client sends a business request to the API gateway, carrying the token in the request;
6. The gateway uses the public key set by the user to verify the token in the request. After the verification is passed, the request is passed through to the backend service;
7. The backend service processes the business and responds;
8. The gateway returns the business response to the client.
In this entire process, the gateway uses the token authentication mechanism to realize the ability of users to use their own user system to authorize their API. Next, we will introduce the structured token JSON Web Token (JWT) used by the gateway to implement token authentication.
### 1.3 JWT
#### 1.3.1 Introduction
JSON Web Token (JWT) is an open standard RFC7519 based on JSON for executing a type of claim to pass between network applications. JWT can generally be used as an independent identity verification token, which can contain user identification, user roles, and permissions information, making it easier to obtain resources from the resource server, and can also add some other necessary declarative information for other business logic, especially suitable for the login scenario of distributed sites.
#### 1.3.2 Composition of JWT
`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ`
As shown in the example above, JWT is a string consisting of three parts:
- Header
- Payload
- Signature
**Header**
The header of the JWT carries two pieces of information:
- The type of the token, which is JWT
- The algorithm used for encryption
The gateway supports the following encryption algorithms:
```text
ES256, ES384, ES512,
HS256, HS384, HS512,
RS256, RS384, RS512,
PS256, PS384, PS512,
EdDSA
Configure the plugin at the instance level as follows:
```yaml
global_auth: false
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
```
The complete header looks like the following JSON:
Configure the following for routes `route-a` and `route-b`:
```yaml
allow:
- consumer1
```
Configure the following for domain names `*.example.com` and `test.com`:
```yaml
allow:
- consumer2
```
**Explanation:**
The specified `route-a` and `route-b` refer to the routing names filled in when creating the gateway route. When these two routes are matched, the caller with the name consumer1 will be allowed access, while others will not be permitted.
The specified `*.example.com` and `test.com` are used to match the request domain names. When a matching domain name is found, the caller with the name consumer2 will be allowed access, while others will not.
Based on this configuration, the following requests will be allowed:
Suppose the following request matches the route-a.
**Setting the JWT in URL parameters**
```bash
curl 'http://xxx.hello.com/test?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
**Setting the JWT in HTTP request headers**
```bash
curl http://xxx.hello.com/test -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
After successful authentication and authorization, the request's header will include an `X-Mse-Consumer` field, in this example with the value `consumer1`, to identify the caller's name.
The following requests will be denied:
**Request without JWT returns 401**
```bash
curl http://xxx.hello.com/test
```
**Caller matching from the provided JWT has no access permission, returning 403**
```bash
# consumer1 is not in the allow list for *.example.com
curl 'http://xxx.example.com/test' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
#### Enable at Gateway Instance Level
The following configuration will enable JWT Auth authentication at the instance level, requiring all requests to be authenticated before accessing.
```yaml
global_auth: true
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
```
## Common Error Codes
| HTTP Status Code | Error Message | Reason Description |
| ---------------- | ----------------------------- | ---------------------------------------------------------------------------- |
| 401 | Jwt missing | The request header did not provide a JWT |
| 401 | Jwt expired | The JWT has expired |
| 401 | Jwt verification fails | JWT payload verification failed, such as mismatched `iss` |
| 403 | Access Denied | No permission to access the current route |
## Detailed Description
### 1. Token-based Authentication
#### 1.1 Introduction
Many open APIs need to identify the requester's identity and determine whether the requested resource can be returned. A token is a mechanism used for identity verification. With this mechanism, applications do not need to retain user authentication information or session information on the server, allowing for stateless, distributed web application authorization, facilitating application scaling.
#### 1.2 Process Description
![](https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/2336348951/p135822.png)
The above image shows the entire business process sequence diagram for gateway authentication using JWT. Below, we will detail the steps indicated in the diagram:
1. The client initiates an authentication request to the API gateway, generally carrying the terminal user's username and password.
2. The gateway forwards the request directly to the backend service.
3. The backend service reads the verification information in the request (such as username and password) for validation. Upon successful verification, it generates a standard token using a private key and returns it to the gateway.
4. The gateway returns a response containing the token to the client, who must cache this token locally.
5. The client sends a business request to the API gateway, carrying the token in the request.
6. The gateway validates the token using the user's set public key. Upon successful validation, it forwards the request to the backend service.
7. The backend service processes the business and responds.
8. The gateway returns the business response to the client.
Throughout this process, the gateway utilizes the token authentication mechanism, enabling the user to authorize their API using their user system. Next, we will introduce the structured token used by the gateway for token authentication: JSON Web Token (JWT).
#### 1.3 JWT
##### 1.3.1 Introduction
JSON Web Token (JWT) is an open standard (RFC7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. JWT can be used as a stand-alone authentication token, capable of containing user identity, user roles, permissions, and other information, aiding in resource retrieval from resource servers and adding any additional claims required by business logic, particularly suitable for login scenarios for distributed sites.
##### 1.3.2 JWT Structure
`eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWV9.TJVA95OrM7E2cBab30RMHrHDcEfxjoYZgeFONFh7HgQ`
As shown above, JWT is a string composed of three parts:
- Header
- Payload
- Signature
**Header**
The header of the JWT carries two pieces of information:
- Token type, which is JWT
- Signing algorithm
The supported signing algorithms by the gateway are as follows:
```text
ES256, ES384, ES512,
HS256, HS384, HS512,
RS256, RS384, RS512,
PS256, PS384, PS512,
EdDSA
```
The complete header looks like the following JSON:
```js
{
'typ': 'JWT',
'alg': 'HS256'
}
```
The header is then Base64-encoded (this encoding can be symmetrically decoded), forming the first part:
`eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9`
**Payload**
The payload is where the actual information is stored. The details are defined below:
```
Then the header is Base64 encoded (this encoding is symmetrically decodable), forming the first part.
`eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9`
**Payload**
The payload is where valid information is stored. Its details are defined as follows:
```text
iss: The issuer of the token. This indicates who created the token and is a string value.
sub: The subject identifier. This is the unique identifier for the end user provided by the issuer, and is no longer than 255 ASCII characters, and is case-sensitive within the issuer's scope.
aud: The audience(s) of the token, which is an array of case-sensitive strings.
exp: The expiration time of the token, after which the token will be invalidated, is an integer declaration representing the Unix timestamp in seconds.
iat: The issuance time of the token, is an integer declaration representing the Unix timestamp in seconds.
jti: The unique identifier of the token, and the value is unique for every token created by the issuer. It is usually a cryptographically random value to prevent conflicts. This component adds a random entropy that an attacker cannot obtain to the structured token, making it more difficult for the token to be guessed or replayed.
```
Custom fields for a user feature can also be added, such as the example below adding a "name" field for the user's nickname:
iss: Token issuer. Indicates who created the token, this claim is a string.
sub: Subject Identifier, an identifier provided by the issuer for its end users, unique within the issuer's scope, up to 255 ASCII characters, case sensitive.
aud: Audience(s), the audience of the token, an array of strings that are case-sensitive.
exp: Expiration time, a timestamp of the token's expiration. Tokens expired beyond this time will be invalid. This claim is an integer, representing the number of seconds since January 1, 1970.
iat: Issuance time of the token, this claim is an integer, representing the number of seconds since January 1, 1970.
jti: Unique identifier for the token, the value of this claim must be unique for each token created by the token issuer to prevent collisions. It is typically a cryptographically random value. This value adds a random entropy component to the structured token that is not accessible to an attacker, helping prevent token guess and replay attacks.
```
Custom fields necessary for the user system can also be added, for example, the following example adds a nickname `name`:
```js
{
"sub": "1234567890",
"name": "John Doe"
}
```
The payload is then Base64-encoded, forming the second part of the JWT:
`JTdCJTBBJTIwJTIwJTIyc3ViJTIyJTNBJTIwJTIyMTIzNDU2Nzg5MCUyMiUyQyUwQSUyMCUyMCUyMm5hbWUlMjIlM0ElMjAlMjJKb2huJTIwRG9lJTIyJTBBJTdE`
**Signature**
This part is a string that consists of the Base64-encoded header and Base64-encoded payload concatenated with a period, followed by the encryption of the resulting string using the algorithm specified in the header (where $secret represents the user's private key).
```
Then encode it in Base64 to obtain the second part of the JWT:
`JTdCJTBBJTIwJTIwJTIyc3ViJTIyJTNBJTIwJTIyMTIzNDU2Nzg5MCUyMiUyQyUwQSUyMCUyMCUyMm5hbWUlMjIlM0ElMjAlMjJKb2huJTIwRG9lJTIyJTBBJTdE`
**Signature**
This part requires the Base64-encoded Header and Base64-encoded Payload to be connected by a period, and then encrypted using the signing method declared in the Header (where `$secret` represents the user's private key) to form the third part of the JWT.
```js
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, '$secret');
```
```
Connecting these three parts with a period creates a complete string that forms the JWT example at the beginning of section 1.3.2.
These three parts are then concatenated using periods to form the complete JWT string as shown in the example at the beginning of this section.
##### 1.3.3 Validity Period
The gateway will validate the `exp` field in the token. Once this field expires, the gateway will consider this token invalid and directly reject the request. The expiration time must be set.
#### 1.3.3 Time validity
##### 1.3.4 Characteristics of JWT
1. JWT is not encrypted by default; do not write secret data into JWT.
2. JWT can be used for both authentication and information exchange. Effectively using JWT can reduce the number of queries to the database on the server.
3. The biggest drawback of JWT is that since the server does not maintain session state, it cannot revoke a token during use or change the permissions of said token. In other words, once a JWT is issued, it will remain valid until expiration, unless the server implements additional logic.
4. JWT itself contains authentication information, and once leaked, anyone can gain all permissions of that token. To minimize theft, the validity period of JWT should be set to be relatively short. For some critical permissions, users should be re-authenticated.
5. To reduce theft, JWT should not be transmitted in plain text over HTTP but should use HTTPS for transmission.
The gateway will verify the exp field in the token. Once this field has expired, the gateway will consider the token invalid and reject the request directly. The expiration time value must be set.
#### 1.3.4 Several Characteristics of JWT
1. By default, JWT is not encrypted and cannot write secret data into JWT.
2. JWT can not only be used for authentication but also for exchanging information. Using JWT effectively can reduce the number of times servers query the database.
3. The biggest drawback of JWT is that the server cannot invalidate a token during use, or change the token's permission, because the server does not keep the session state. That is, once JWT is issued, it will always be valid before it expires, unless the server deploys additional logic.
4. JWT contains authentication information itself. Once leaked, anyone can obtain all permissions of the token. To reduce theft, the expiration time of JWT should be set relatively short. For some more important permissions, users should be authenticated again when using them.
5. To reduce theft, JWT should not be transmitted in plaintext using the HTTP protocol, and the HTTPS protocol should be used for transmission.
## 2. How to apply the JWT plugin to protect the API of the user system
### 2.1 Generate a pair of JWK (JSON Web Key)
**Method 1: Generate online**
Users can generate the private and public keys used for token generation and verification on this website https://mkjwk.org. The private key is used for the authorization service to issue JWT, and the public key is configured into the JWT plugin for the gateway to verify the signature of the request. Note that the JWKs format configuration used by the gateway requires the public key in the figure below to be placed in the keys structure, such as: `{"keys":[{"kty":"RSA","e":"AQAB",...}]}`
<img src="https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/2336348951/p135823.png" style="zoom:50%" />
**Method 2: Generate locally**
This article uses a Java example to illustrate, and users of other languages can also find relevant tools to generate key pairs. Create a new Maven project and add the following dependencies:
### 2. How User Systems Apply the JWT Plugin to Protect APIs
#### 2.1 Generating a Pair of JWKs (JSON Web Keys)
**Method 1: Online Generation:**
Users can generate the private and public keys used for token generation and verification at this site https://mkjwk.org. The private key is used by the authorization service to issue JWTs, and the public key is configured into the JWT plugin for the gateway to verify requests. Pay attention to the jwks format configuration used by the gateway. In the image below, the Public Key should be placed into the keys structure, for example: `{"keys":[{"kty":"RSA","e":"AQAB",...}]}`
<img src="https://help-static-aliyun-doc.aliyuncs.com/assets/img/zh-CN/2336348951/p135823.png" style="zoom:50%" />
**Method 2: Local Generation:**
This article demonstrates using Java; users of other languages can find related tools to generate key pairs. Create a new Maven project and include the following dependency:
```xml
<dependency>
<groupId>org.bitbucket.b_c</groupId>
<artifactId>jose4j</artifactId>
<version>0.7.0</version>
</dependency>
```
Use the following code to generate a pair of RSA keys:
```
Use the following code to generate a pair of RSA keys:
```java
RsaJsonWebKey rsaJsonWebKey = RsaJwkGenerator.generateJwk(2048);
final String publicKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.PUBLIC_ONLY);
final String privateKeyString = rsaJsonWebKey.toJson(JsonWebKey.OutputControlLevel.INCLUDE_PRIVATE);
```
### 2.2 Implement the token issuance authentication service using the private key in JWK
The Keypair JSON string generated online in Section 2.1 (the first of the three boxes) or privateKeyString JSON string generated locally needs to be used as the private key to issue tokens for trusted users to access protected APIs. The specific implementation can refer to the following example. The form of issuing tokens to customers is determined by the user according to the specific business scenario. The function of issuing tokens can be deployed in the production environment and configured as a normal API for visitors to obtain through username and password, or tokens can be generated directly in the local environment and copied to designated users for use.
```
#### 2.2 Using the Private Key in JWK to Implement the Token Issuance Authentication Service
You will need to use the Keypair JSON string (the first inside the three boxes) generated online in section 2.1 or the locally generated privateKeyString JSON string as the private key to issue tokens for authorizing trusted users to access protected APIs. The specific implementation can refer to the example below. The form of issuing tokens to customers can be determined by the user based on the specific business scenario; it can be deployed in the production environment, configured to be a normal API that visitors can access through username and password, or tokens can be generated locally and directly copied for specified users to use.
```java
import java.security.PrivateKey;
import java.security.PrivateKey;
import org.jose4j.json.JsonUtil;
import org.jose4j.jwk.RsaJsonWebKey;
import org.jose4j.jwk.RsaJwkGenerator;
@@ -173,32 +309,29 @@ import org.jose4j.jws.JsonWebSignature;
import org.jose4j.jwt.JwtClaims;
import org.jose4j.jwt.NumericDate;
import org.jose4j.lang.JoseException;
public class GenerateJwtDemo {
public static void main(String[] args) throws JoseException {
String keyId = "uniq_key";
//Use the Keypair generated in section 2.1 of this article
// Use the Keypair generated in section 2.1
String privateKeyJson = "{\n"
+ " \"kty\": \"RSA\",\n"
+ " \"d\": "
+
"\"O9MJSOgcjjiVMNJ4jmBAh0mRHF_TlaVva70Imghtlgwxl8BLfcf1S8ueN1PD7xV6Cnq8YenSKsfiNOhC6yZ_fjW1syn5raWfj68eR7cjHWjLOvKjwVY33GBPNOvspNhVAFzeqfWneRTBbga53Agb6jjN0SUcZdJgnelzz5JNdOGaLzhacjH6YPJKpbuzCQYPkWtoZHDqWTzCSb4mJ3n0NRTsWy7Pm8LwG_Fd3pACl7JIY38IanPQDLoighFfo-Lriv5z3IdlhwbPnx0tk9sBwQBTRdZ8JkqqYkxUiB06phwr7mAnKEpQJ6HvhZBQ1cCnYZ_nIlrX9-I7qomrlE1UoQ\",\n"
+ " \"d\": \"O9MJSOgcjjiVMNJ4jmBAh0mRHF_TlaVva70Imghtlgwxl8BLfcf1S8ueN1PD7xV6Cnq8YenSKsfiNOhC6yZ_fjW1syn5raWfj68eR7cjHWjLOvKjwVY33GBPNOvspNhVAFzeqfWneRTBbga53Agb6jjN0SUcZdJgnelzz5JNdOGaLzhacjH6YPJKpbuzCQYPkWtoZHDqWTzCSb4mJ3n0NRTsWy7Pm8LwG_Fd3pACl7JIY38IanPQDLoighFfo-Lriv5z3IdlhwbPnx0tk9sBwQBTRdZ8JkqqYkxUiB06phwr7mAnKEpQJ6HvhZBQ1cCnYZ_nIlrX9-I7qomrlE1UoQ\",\n"
+ " \"e\": \"AQAB\",\n"
+ " \"alg\": \"RS256\",\n"
+ " \"n\": \"vCuB8MgwPZfziMSytEbBoOEwxsG7XI3MaVMoocziP4SjzU4IuWuE_DodbOHQwb_thUru57_Efe"
+
"--sfATHEa0Odv5ny3QbByqsvjyeHk6ZE4mSAV9BsHYa6GWAgEZtnDceeeDc0y76utXK2XHhC1Pysi2KG8KAzqDa099Yh7s31AyoueoMnrYTmWfEyDsQL_OAIiwgXakkS5U8QyXmWicCwXntDzkIMh8MjfPskesyli0XQD1AmCXVV3h2Opm1Amx0ggSOOiINUR5YRD6mKo49_cN-nrJWjtwSouqDdxHYP-4c7epuTcdS6kQHiQERBd1ejdpAxV4c0t0FHF7MOy9kw\"\n"
+ " \"n\": \"vCuB8MgwPZfziMSytEbBoOEwxsG7XI3MaVMoocziP4SjzU4IuWuE_DodbOHQwb_thUru57_Efe--sfATHEa0Odv5ny3QbByqsvjyeHk6ZE4mSAV9BsHYa6GWAgEZtnDceeeDc0y76utXK2XHhC1Pysi2KG8KAzqDa099Yh7s31AyoueoMnrYTmWfEyDsQL_OAIiwgXakkS5U8QyXmWicCwXntDzkIMh8MjfPskesyli0XQD1AmCXVV3h2Opm1Amx0ggSOOiINUR5YRD6mKo49_cN-nrJWjtwSouqDdxHYP-4c7epuTcdS6kQHiQERBd1ejdpAxV4c0t0FHF7MOy9kw\"\n"
+ "}";
JwtClaims claims = new JwtClaims();
claims.setGeneratedJwtId();
claims.setIssuedAtToNow();
//Expiration time must be set
// Expiration time must be set
NumericDate date = NumericDate.now();
date.addSeconds(120*60);
claims.setExpirationTime(date);
claims.setNotBeforeMinutesInThePast(1);
claims.setSubject("YOUR_SUBJECT");
claims.setAudience("YOUR_AUDIENCE");
//Add custom parameters, use String type for all values
// Add custom parameters, all values should be String type
claims.setClaim("userId", "1213234");
claims.setClaim("email", "userEmail@youapp.com");
JsonWebSignature jws = new JsonWebSignature();
@@ -206,194 +339,9 @@ public class GenerateJwtDemo {
jws.setKeyIdHeaderValue(keyId);
jws.setPayload(claims.toJson());
PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyJson)).getPrivateKey();
jws.setKey(privateKey);
String jwtResult = jws.getCompactSerialization();
System.out.println("Generate Json Web token , result is " + jwtResult);
}
}
```
# Plugin Configuration Guide
## Configuration Fields
| Name | Data Type | Required | Default | Description |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `consumers` | array of object | Yes | - | Configures callers of the service for authenticating requests |
| `_rules_` | array of object | Optional | - | Configures access control lists for specific routes or domains for authorizing requests |
The configuration field descriptions for each item in consumers are as follows:
| Name | Data Type | Required | Default | Description|
| ----------------------- | ----------------- | -------- | ------------------------------------------------- | ------------------------ |
| `name` | string | Yes | - | Configures the name of this consumer |
| `jwks` | string | Yes | - | Specifies a JSON Web Key Set, as defined in https://www.rfc-editor.org/rfc/rfc7517, consisting of public keys (or symmetric keys) used to verify the signature of JWT |
| `issuer` | string | Yes | - | The issuer of the JWT, which should be consistent with the iss field in the payload |
| `claims_to_headers` | array of object | Optional | - | Extracts the specified fields from the JWT's payload and sets them to the specified request headers for forwarding to the backend |
| `from_headers` | array of object | Optional | {"name":"Authorization","value_prefix":"Bearer "} | Extracts the JWT from the specified request headers |
| `from_params` | array of string | Optional | access_token | Extracts the JWT from the specified URL parameters |
| `from_cookies` | array of string | Optional | - | Extracts the JWT from the specified cookie(s) |
| `clock_skew_seconds` | number | Optional | 60 | The amount of clock skew, in seconds, that is allowed when verifying the exp and iat fields of the JWT |
| `keep_token` | bool | Optional | ture | Whether to keep the JWT when forwarding it to the backend |
**Note:**
- The default value is used only if neither `from_headers`, `from_params`, nor `from_cookies` are configured.
The configuration field descriptions for each item in `from_headers` are as follows:
| Name | Data Type | Required| Default | Description |
| ---------------- | --------------- | ------- | ------ | --------------------------------------------------------- |
| `name` | string | Yes | - | Specifies the request header to extract the JWT from |
| `value_prefix` | string | Yes | - | Removes the specified prefix from the request header's value, leaving the rest as the JWT |
The configuration field descriptions for each item in `claims_to_headers` are as follows:
| Name | Data Type | Required| Default | Description |
| ---------------- | --------------- | ------- | ------ | --------------------------------------------------------- |
| `claim` | string | Yes | - | The name of the field in the JWT payload, which must be a string or unsigned integer |
| `header` | string | Yes | - | Sets the value of the specified request header to the value of the specified field in the payload, for forwarding to the backend |
| `override` | bool | Optional | true | If true, overrides an existing header with the same name; if false, appends the header to the existing headers |
The configuration field descriptions for each item in `_rules_` are as follows:
| Name | Data Type | Required| Default | Description |
| ---------------- | --------------- | ------------------------------------------------- | ------ | -------------------------------------------------- |
| `_match_route_` | array of string | Optional, select either `_match_route_` or `_match_domain_` | - | Configures the route names to match|
| `_match_domain_` | array of string | Optional, select either `_match_route_` or `_match_domain_` | - | Configures the domains to match |
| `allow` | array of string | Required | - | Configures the consumer names allowed to access the matched requests |
**Note:**
- If the `_rules_` field is not configured, authentication and authorization are enabled for all routes of the current gateway instance by default;
- For authenticated and authorized requests, a `X-Mse-Consumer` field is added to the request header to identify the caller's name.
## Configuration Example
### Enable for Specific Routes or Domains
The following configuration enables Jwt Auth authentication and authorization for specific routes or domains of the gateway. If a JWT can match multiple `jwks`, the first matching `consumer` is hit according to the configuration order.
```yaml
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
# Use the _rules_ field for fine-grained rule configuration
_rules_:
# Rule 1: Effective when matched by route name
- _match_route_:
- route-a
- route-b
allow:
- consumer1
# Rule 2: Effective when matched by domain name
- _match_domain_:
- "*.example.com"
- test.com
allow:
- consumer2
```
In this example, the `route-a` and `route-b` specified in `_match_route_` are the names of the routes filled in when creating the gateway route. When these two routes are matched, access will be allowed for the caller with the `name` of `consumer1`, and other callers will not be allowed to access.
The `*.example.com` and `test.com` specified in `_match_domain_` are used to match the domain names of the requests. When a domain name match is found, access will be allowed for the caller with the `name` of `consumer2`, and other callers will not be allowed to access.
#### According to this configuration, the following requests are allowed:
Assuming the following requests will match the route-a route:
**JWT is set in URL parameter**
```bash
curl 'http://xxx.hello.com/test?access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
**JWT is set in HTTP request header**
```bash
curl http://xxx.hello.com/test -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
After authentication and authorization, a `X-Mse-Consumer` field will be added in the request header with a value of `consumer1` in this example, to identify the name of the caller.
#### The following requests will be denied:
**Request without JWT provided, returns 401**
```bash
curl http://xxx.hello.com/test
```
**The consumer matched by the provided JWT in the request does not have access, returns 403**
```bash
# consumer1 is not in the allow list of *.example.com
curl 'http://xxx.example.com/test' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxODY1NjczODE5fQ.-vBSV0bKeDwQcuS6eeSZN9dLTUnSnZVk8eVCXdooCQ4'
```
### Enabling at Gateway Instance Level
The following configuration does not specify the `_rules_` field, so JWT authentication will be enabled at the gateway instance level:
```yaml
consumers:
- name: consumer1
issuer: abcd
jwks: |
{
"keys": [
{
"kty": "oct",
"kid": "123",
"k": "hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew",
"alg": "HS256"
}
]
}
- name: consumer2
issuer: abc
jwks: |
{
"keys": [
{
"kty": "RSA",
"e": "AQAB",
"use": "sig",
"kid": "123",
"alg": "RS256",
"n": "i0B67f1jggT9QJlZ_8QL9QQ56LfurrqDhpuu8BxtVcfxrYmaXaCtqTn7OfCuca7cGHdrJIjq99rz890NmYFZuvhaZ-LMt2iyiSb9LZJAeJmHf7ecguXS_-4x3hvbsrgUDi9tlg7xxbqGYcrco3anmalAFxsbswtu2PAXLtTnUo6aYwZsWA6ksq4FL3-anPNL5oZUgIp3HGyhhLTLdlQcC83jzxbguOim-0OEz-N4fniTYRivK7MlibHKrJfO3xa_6whBS07HW4Ydc37ZN3Rx9Ov3ZyV0idFblU519nUdqp_inXj1eEpynlxH60Ys_aTU2POGZh_25KXGdF_ZC_MSRw"
}
]
}
```
# Common Error Codes
| HTTP Status Code | Error Message | Reason Description|
|------------------| ---------------------- | -------------------------------------------------------------------------------- |
| 401 | JWT missing | The JWT is not provided in the request header. |
| 401 | JWT expired | The JWT has expired. |
| 401 | JWT verification fails | The JWT payload verification failed, such as the iss mismatch. |
| 403 | Access denied | Access to the current route is denied. |

View File

@@ -1,15 +1,32 @@
# 功能说明
---
title: Key 认证
keywords: [higress,key auth]
description: Key 认证插件配置参考
---
## 功能说明
`key-auth`插件实现了基于 API Key 进行认证鉴权的功能,支持从 HTTP 请求的 URL 参数或者请求头解析 API Key同时验证该 API Key 是否有权限访问。
# 配置字段
## 运行属性
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `consumers` | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
| `keys` | array of string | 必填 | - | API Key 的来源字段名称,可以是 URL 参数或者 HTTP 请求头名称 |
| `in_query` | bool | `in_query``in_header` 至少有一个为 true | true | 配置 true 时,网关会尝试从 URL 参数中解析 API Key |
| `in_header` | bool | `in_query``in_header` 至少有一个为 true | true | 配置 true 时,网关会尝试从 HTTP 请求头中解析 API Key |
| `_rules_` | array of object | 选填 | - | 配置特定路由或域名的访问权限列表,用于对请求进行鉴权 |
插件执行阶段:`认证阶段`
插件执行优先级:`310`
## 配置字段
**注意:**
- 在一个规则里,鉴权配置和认证配置不可同时存在
- 对于通过认证鉴权的请求请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
### 认证配置
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `global_auth` | bool | 选填(**仅实例级别配置** | - | 只能在实例级别配置若配置为true则全局生效认证机制; 若配置为false则只对做了配置的域名和路由生效认证机制若不配置则仅当没有域名和路由配置时全局生效兼容老用户使用习惯。 |
| `consumers` | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
| `keys` | array of string | 必填 | - | API Key 的来源字段名称,可以是 URL 参数或者 HTTP 请求头名称 |
| `in_query` | bool | `in_query``in_header` 至少有一个为 true | true | 配置 true 时,网关会尝试从 URL 参数中解析 API Key |
| `in_header` | bool | `in_query``in_header` 至少有一个为 true | true | 配置 true 时,网关会尝试从 HTTP 请求头中解析 API Key |
`consumers`中每一项的配置字段说明如下:
@@ -18,25 +35,22 @@
| `credential` | string | 必填 | - | 配置该consumer的访问凭证 |
| `name` | string | 必填 | - | 配置该consumer的名称 |
`_rules_` 中每一项的配置字段说明如下:
### 鉴权配置(非必需)
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ---------------- | --------------- | ------------------------------------------------- | ------ | -------------------------------------------------- |
| `_match_route_` | array of string | 选填`_match_route_``_match_domain_`中选填一项 | - | 配置要匹配的路由名称 |
| `_match_domain_` | array of string | 选填,`_match_route_``_match_domain_`中选填一项 | - | 配置要匹配的域名 |
| `allow` | array of string | 必填 | - | 对于符合匹配条件的请求配置允许访问的consumer名称 |
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `allow` | array of string | 选填(**非实例级别配置**) | - | 只能在路由或域名等细粒度规则上配置,对于符合匹配条件的请求,配置允许访问的 consumer从而实现细粒度的权限控制 |
**注意:**
- 若不配置`_rules_`字段,则默认对当前网关实例的所有路由开启认证;
- 对于通过认证鉴权的请求请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
## 配置示例
# 配置示例
### 全局配置认证和路由粒度进行鉴权
## 对特定路由或域名开启
以下配置将对网关特定路由或域名开启Key Auth认证和鉴权。credential字段不能重复。
以下配置将对网关特定路由或域名开启 Key Auth 认证和鉴权,注意`credential`字段不能重复
在实例级别做如下插件配置:
```yaml
global_auth: false
consumers:
- credential: 2bda943c-ba2b-11ec-ba07-00163e1250b5
name: consumer1
@@ -44,31 +58,33 @@ consumers:
name: consumer2
keys:
- apikey
in_query: true
# 使用 _rules_ 字段进行细粒度规则配置
_rules_:
# 规则一:按路由名称匹配生效
- _match_route_:
- route-a
- route-b
allow:
- consumer1
# 规则二:按域名匹配生效
- _match_domain_:
- "*.example.com"
- test.com
allow:
- consumer2
- x-api-key
```
此例 `_match_route_` 中指定的 `route-a``route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将允许`name``consumer1`的调用者访问,其他调用者不允许访问;
route-a 和 route-b 这两个路由做如下配置:
此例 `_match_domain_` 中指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将允许`name``consumer2`的调用者访问,其他调用者不允许访问。
```yaml
allow:
- consumer1
```
### 根据该配置,下列请求可以允许访问:
对 *.example.com 和 test.com 在这两个域名做如下配置:
```yaml
allow:
- consumer2
```
**说明:**
此例指定的route-a和route-b即在创建网关路由时填写的路由名称当匹配到这两个路由时将允许name为consumer1的调用者访问其他调用者不允许访问。
此例指定的*.example.com和test.com用于匹配请求的域名当发现域名匹配时将允许name为consumer2的调用者访问其他调用者不被允许访问。
根据该配置,下列请求可以允许访问:
假设以下请求会匹配到route-a这条路由
n
**将 API Key 设置在 url 参数中**
```bash
curl http://xxx.hello.com/test?apikey=2bda943c-ba2b-11ec-ba07-00163e1250b5
@@ -80,7 +96,7 @@ curl http://xxx.hello.com/test -H 'x-api-key: 2bda943c-ba2b-11ec-ba07-00163e125
认证鉴权通过后请求的header中会被添加一个`X-Mse-Consumer`字段,在此例中其值为`consumer1`,用以标识调用方的名称
### 下列请求将拒绝访问:
下列请求将拒绝访问:
**请求未提供 API Key返回401**
```bash
@@ -97,11 +113,12 @@ curl http://xxx.hello.com/test?apikey=926d90ac-ba2e-11ec-ab68-00163e1250b5
curl http://xxx.hello.com/test?apikey=c8c8e9ca-558e-4a2d-bb62-e700dcc40e35
```
## 网关实例级别开启
### 网关实例级别开启
以下配置未指定`_rules_`字段,因此将对网关实例级别开启 Key Auth 认证
以下配置将对网关实例级别开启 Basic Auth 认证,所有请求均需要经过认证后才能访问。
```yaml
global_auth: true
consumers:
- credential: 2bda943c-ba2b-11ec-ba07-00163e1250b5
name: consumer1
@@ -109,13 +126,15 @@ consumers:
name: consumer2
keys:
- apikey
in_query: true
- x-api-key
```
# 相关错误码
## 相关错误码
| HTTP 状态码 | 出错信息 | 原因说明 |
| ----------- | --------------------------------------------------------- | ----------------------- |
| 401 | No API key found in request | 请求提供 API Key |
| 401 | Request denied by Key Auth check. Muti API key found in request | 请求提供多个 API Key |
| 401 | Request denied by Key Auth check. No API key found in request | 请求未提供 API Key |
| 401 | Request denied by Key Auth check. Invalid API key | 不允许当前 API Key 访问 |
| 403 | Request denied by Basic Auth check. Unauthorized consumer | 请求的调用方无访问权限 |
| 403 | Request denied by Key Auth check. Unauthorized consumer | 请求的调用方无访问权限 |

View File

@@ -1,43 +1,47 @@
# Features
The `key-auth` plug-in implements the authentication function based on the API Key, supports parsing the API Key from the URL parameter or request header of the HTTP request, and verifies whether the API Key has permission to access.
---
title: Key Authentication
keywords: [higress,key auth]
description: Key Authentication Plugin Configuration Reference
---
## Function Description
The `key-auth` plugin implements authentication based on API Key, supporting the parsing of the API Key from HTTP request URL parameters or request headers, while also verifying whether the API Key has permission to access the resource.
# Configuration field
## Runtime Properties
Plugin Execution Phase: `Authentication Phase`
Plugin Execution Priority: `310`
| Name | Data Type | Parameter requirements | Default| Description |
| ----------- | --------------- | -------------------------------------------------------- | ------ | --------------------------------------------------------------------------------------------------------- |
| `consumers` | array of object | Required | - | Configure the caller of the service to authenticate the request. |
| `keys` | array of string | Required | - | The name of the source field of the API Key, which can be a URL parameter or an HTTP request header name. |
| `in_query` | bool | At least one of `in_query` and `in_header` must be true. | true | When configured true, the gateway will try to parse the API Key from the URL parameters. |
| `in_header` | bool | The same as above. | true | The same as above. |
| `_rules_` | array of object | Optional | - | Configure the access list of a specific route or domain name for authenticating requests. |
## Configuration Fields
**Note:**
- Authentication and authorization configurations cannot coexist within a single rule.
- For requests that are authenticated, a header field `X-Mse-Consumer` will be added to identify the caller's name.
### Authentication Configuration
| Name | Data Type | Requirements | Default Value | Description |
| ------------- | ---------------- | ----------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `global_auth` | bool | Optional (**Instance-Level Configuration Only**) | - | Can only be configured at the instance level; if set to true, the authentication mechanism takes effect globally; if set to false, it only applies to the configured hostnames and routes. If not configured, it will only take effect globally when no hostname and route configurations are present (to maintain compatibility with older user habits). |
| `consumers` | array of object | Required | - | Configures the service callers for request authentication. |
| `keys` | array of string | Required | - | Source field names for the API Key, which can be URL parameters or HTTP request header names. |
| `in_query` | bool | At least one of `in_query` and `in_header` must be true | true | When configured as true, the gateway will attempt to parse the API Key from URL parameters. |
| `in_header` | bool | At least one of `in_query` and `in_header` must be true | true | When configured as true, the gateway will attempt to parse the API Key from HTTP request headers. |
The configuration fields of each item in `consumers` are described as follows:
The configuration field descriptions for each item in `consumers` are as follows:
| Name | Data Type | Requirements | Default Value | Description |
| ------------ | --------- | ------------ | ------------- | ------------------------------ |
| `credential` | string | Required | - | Configures the access credential for this consumer. |
| `name` | string | Required | - | Configures the name for this consumer. |
| Name | Data Type | Parameter requirements | Default | Description |
| ------------ | --------- | -----------------------| ------ | ------------------------------------------- |
| `credential` | string | Required | - | Configure the consumer's access credentials. |
| `name` | string | Required | - | Configure the name of the consumer. |
### Authorization Configuration (Optional)
| Name | Data Type | Requirements | Default Value | Description |
| ----------- | ---------------- | ----------------------------------------------- | ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allow` | array of string | Optional (**Non-Instance Level Configuration**) | - | Can only be configured on fine-grained rules such as routes or hostnames; specifies the allowed consumers for matching requests, allowing for fine-grained permission control. |
The configuration fields of each item in `_rules_` are described as follows:
| Name | Data Type | Parameter requirements | Default| Description |
| ---------------- | --------------- | --------------------------------------------------------------------- | ------ | -------------------------------------------------- |
| `_match_route_` | array of string | OptionalOptionally fill in one of `_match_route_`, `_match_domain_`. | - | Configure the route name to match. |
| `_match_domain_` | array of string | OptionalOptionally fill in one of `_match_route_`, `_match_domain_`. | - | Configure the domain name to match. |
| `allow` | array of string | Required | - | For requests that meet the matching conditions, configure the name of the consumer that is allowed to access. |
**Warning**
- If the `_rules_` field is not configured, authentication will be enabled for all routes of the current gateway instance by default;
- For a request that passes authentication, an `X-Mse-Consumer` field will be added to the request header to identify the name of the caller.
# Example configuration
## Enabled for specific routes or domains
The following configuration will enable Key Auth authentication and authentication for gateway-specific routes or domain names. Note that the `credential` field can not be repeated.
## Configuration Example
### Global Configuration for Authentication and Granular Route Authorization
The following configuration will enable Key Auth authentication and authorization for specific routes or hostnames in the gateway. The `credential` field must not repeat.
At the instance level, do the following plugin configuration:
```yaml
global_auth: false
consumers:
- credential: 2bda943c-ba2b-11ec-ba07-00163e1250b5
name: consumer1
@@ -45,64 +49,63 @@ consumers:
name: consumer2
keys:
- apikey
in_query: true
# Use the _rules_ field for fine-grained rule configuration
_rules_:
# Rule 1: Match by route name to take effect
- _match_route_:
- route-a
- route-b
allow:
- consumer1
# Rule 2: Take effect by domain name matching
- _match_domain_:
- "*.example.com"
- test.com
allow:
- consumer2
- x-api-key
```
The `route-a` and `route-b` specified in `_match_route_` in this example are the route names filled in when creating the gateway route. When these two routes are matched, calls whose `name` is `consumer1` will be allowed Access by callers, other callers are not allowed to access;
For routes route-a and route-b, do the following configuration:
```yaml
allow:
- consumer1
```
`*.example.com` and `test.com` specified in `_match_domain_` in this example are used to match the domain name of the request. When the domain name matches, the caller whose `name` is `consumer2` will be allowed to access, and other calls access is not allowed.
For the hostnames *.example.com and test.com, do the following configuration:
```yaml
allow:
- consumer2
```
### Depending on this configuration, the following requests would allow access
**Note:**
The routes route-a and route-b specified in this example refer to the route names filled in when creating the gateway routes. When matched with these two routes, requests from the caller named consumer1 will be allowed while others will be denied.
Assume that the following request will match the route-a route:
The specified hostnames *.example.com and test.com are used to match the request's domain name. When a domain name is matched, callers named consumer2 will be allowed while others will be denied.
**Set the API Key in the url parameter**
Based on this configuration, the following requests will be allowed:
Assuming the following request matches route-a:
**Setting API Key in URL Parameters**
```bash
curl http://xxx.hello.com/test?apikey=2bda943c-ba2b-11ec-ba07-00163e1250b5
```
**Set the API Key in the http request header**
**Setting API Key in HTTP Request Headers**
```bash
curl http://xxx.hello.com/test -H 'x-api-key: 2bda943c-ba2b-11ec-ba07-00163e1250b5'
```
After the authentication is passed, an `X-Mse-Consumer` field will be added to the header of the request. In this example, its value is `consumer1`, which is used to identify the name of the caller.
After successful authentication and authorization, the request's header will have an added `X-Mse-Consumer` field with the value `consumer1`, to identify the name of the caller.
### The following requests will deny access
**The request does not provide an API Key, return 401**
The following requests will be denied access:
**Request without an API Key returns 401**
```bash
curl http://xxx.hello.com/test
```
**The API Key provided by the request is not authorized to access, return 401**
**Request with an invalid API Key returns 401**
```bash
curl http://xxx.hello.com/test?apikey=926d90ac-ba2e-11ec-ab68-00163e1250b5
```
**The caller matched according to the API Key provided in the request has no access rights, return 403**
**Caller matched with provided API Key has no access rights, returns 403**
```bash
# consumer2 is not in the allow list of route-a
curl http://xxx.hello.com/test?apikey=c8c8e9ca-558e-4a2d-bb62-e700dcc40e35
```
## Gateway instance level enabled
The following configuration does not specify the `_rules_` field, so Key Auth authentication will be enabled at the gateway instance level.
### Enabling at the Instance Level
The following configuration will enable Basic Auth authentication at the instance level for the gateway, requiring all requests to pass authentication before accessing.
```yaml
global_auth: true
consumers:
- credential: 2bda943c-ba2b-11ec-ba07-00163e1250b5
name: consumer1
@@ -110,14 +113,13 @@ consumers:
name: consumer2
keys:
- apikey
in_query: true
- x-api-key
```
# Error code
| HTTP status code | Error information | Reason |
| ---------------- | --------------------------------------------------------- | -------------------------------------------- |
| 401 | No API key found in request. | API not provided by request Key. |
| 401 | Request denied by Key Auth check. Invalid API key. | Current API Key access is not allowed. |
| 403 | Request denied by Basic Auth check. Unauthorized consumer. | The requested caller does not have access. |
## Related Error Codes
| HTTP Status Code | Error Message | Reason Explanation |
| ---------------- | ---------------------------------------------------------- | --------------------------------- |
| 401 | Request denied by Key Auth check. Multiple API keys found in request | Multiple API Keys provided in the request. |
| 401 | Request denied by Key Auth check. No API key found in request | API Key not provided in the request. |
| 401 | Request denied by Key Auth check. Invalid API key | The current API Key is not authorized for access. |
| 403 | Request denied by Key Auth check. Unauthorized consumer | The caller does not have access permissions. |

View File

@@ -42,10 +42,7 @@ static RegisterContextFactory register_KeyAuth(CONTEXT_FACTORY(PluginContext),
namespace {
void deniedNoKeyAuthData(const std::string& realm) {
sendLocalResponse(401, "No API key found in request", "",
{{"WWW-Authenticate", absl::StrCat("Key realm=", realm)}});
}
const std::string OriginalAuthKey("X-HI-ORIGINAL-AUTH");
void deniedInvalidCredentials(const std::string& realm) {
sendLocalResponse(401, "Request denied by Key Auth check. Invalid API key",
@@ -84,6 +81,7 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
}
if (!JsonArrayIterate(
configuration, "consumers", [&](const json& consumer) -> bool {
Consumer c;
auto item = consumer.find("name");
if (item == consumer.end()) {
LOG_WARN("can't find 'name' field in consumer.");
@@ -94,6 +92,7 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
!name.first) {
return false;
}
c.name = name.first.value();
item = consumer.find("credential");
if (item == consumer.end()) {
LOG_WARN("can't find 'credential' field in consumer.");
@@ -104,6 +103,7 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
!credential.first) {
return false;
}
c.credential = credential.first.value();
if (rule.credential_to_name.find(credential.first.value()) !=
rule.credential_to_name.end()) {
LOG_WARN(absl::StrCat("duplicate consumer credential: ",
@@ -113,15 +113,59 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
rule.credentials.insert(credential.first.value());
rule.credential_to_name.emplace(
std::make_pair(credential.first.value(), name.first.value()));
item = consumer.find("keys");
if (item != consumer.end()) {
c.keys = std::vector<std::string>{OriginalAuthKey};
if (!JsonArrayIterate(
consumer, "keys", [&](const json& key_json) -> bool {
auto key = JsonValueAs<std::string>(key_json);
if (key.second !=
Wasm::Common::JsonParserResultDetail::OK) {
return false;
}
c.keys->push_back(key.first.value());
return true;
})) {
LOG_WARN("failed to parse configuration for consumer keys.");
return false;
}
item = consumer.find("in_query");
if (item != consumer.end()) {
auto in_query = JsonValueAs<bool>(item.value());
if (in_query.second !=
Wasm::Common::JsonParserResultDetail::OK ||
!in_query.first) {
LOG_WARN(
"failed to parse 'in_query' field in consumer "
"configuration.");
return false;
}
c.in_query = in_query.first;
}
item = consumer.find("in_header");
if (item != consumer.end()) {
auto in_header = JsonValueAs<bool>(item.value());
if (in_header.second !=
Wasm::Common::JsonParserResultDetail::OK ||
!in_header.first) {
LOG_WARN(
"failed to parse 'in_header' field in consumer "
"configuration.");
return false;
}
c.in_header = in_header.first;
}
}
rule.consumers.push_back(std::move(c));
return true;
})) {
LOG_WARN("failed to parse configuration for credentials.");
return false;
}
if (rule.credentials.empty()) {
LOG_INFO("at least one credential has to be configured for a rule.");
return false;
}
// if (rule.credentials.empty()) {
// LOG_INFO("at least one credential has to be configured for a rule.");
// return false;
// }
if (!JsonArrayIterate(configuration, "keys", [&](const json& item) -> bool {
auto key = JsonValueAs<std::string>(item);
if (key.second != Wasm::Common::JsonParserResultDetail::OK) {
@@ -137,6 +181,7 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
LOG_WARN("at least one key has to be configured for a rule.");
return false;
}
rule.keys.push_back(OriginalAuthKey);
auto it = configuration.find("realm");
if (it != configuration.end()) {
auto realm_string = JsonValueAs<std::string>(it.value());
@@ -175,36 +220,102 @@ bool PluginRootContext::parsePluginConfig(const json& configuration,
bool PluginRootContext::checkPlugin(
const KeyAuthConfigRule& rule,
const std::optional<std::unordered_set<std::string>>& allow_set) {
auto credential = extractCredential(rule);
if (credential.empty()) {
LOG_DEBUG("empty credential");
deniedNoKeyAuthData(rule.realm);
return false;
}
auto auth_credential_iter = rule.credentials.find(std::string(credential));
// Check if the credential is part of the credentials
// set from our container to grant or deny access.
if (auth_credential_iter == rule.credentials.end()) {
LOG_DEBUG(absl::StrCat("api key not found: ", credential));
deniedInvalidCredentials(rule.realm);
return false;
}
// Check if this credential has a consumer name. If so, check if this
// consumer is allowed to access. If allow_set is empty, allow all consumers.
auto credential_to_name_iter =
rule.credential_to_name.find(std::string(std::string(credential)));
if (credential_to_name_iter != rule.credential_to_name.end()) {
if (allow_set && !allow_set.value().empty()) {
if (allow_set.value().find(credential_to_name_iter->second) ==
allow_set.value().end()) {
deniedUnauthorizedConsumer(rule.realm);
LOG_DEBUG(credential_to_name_iter->second);
return false;
if (rule.consumers.empty()) {
for (const auto& key : rule.keys) {
auto credential = extractCredential(rule.in_header, rule.in_query, key);
if (credential.empty()) {
LOG_DEBUG("empty credential for key: " + key);
continue;
}
auto auth_credential_iter = rule.credentials.find(credential);
if (auth_credential_iter == rule.credentials.end()) {
LOG_DEBUG("api key not found: " + credential);
continue;
}
auto credential_to_name_iter = rule.credential_to_name.find(credential);
if (credential_to_name_iter != rule.credential_to_name.end()) {
if (allow_set && !allow_set->empty()) {
if (allow_set->find(credential_to_name_iter->second) ==
allow_set->end()) {
deniedUnauthorizedConsumer(rule.realm);
LOG_DEBUG("unauthorized consumer: " +
credential_to_name_iter->second);
return false;
}
}
addRequestHeader("X-Mse-Consumer", credential_to_name_iter->second);
}
return true;
}
} else {
for (const auto& consumer : rule.consumers) {
std::vector<std::string> keys_to_check =
consumer.keys.value_or(rule.keys);
bool in_query = consumer.in_query.value_or(rule.in_query);
bool in_header = consumer.in_header.value_or(rule.in_header);
for (const auto& key : keys_to_check) {
auto credential = extractCredential(in_header, in_query, key);
if (credential.empty()) {
LOG_DEBUG("empty credential for key: " + key);
continue;
}
if (credential != consumer.credential) {
LOG_DEBUG("credential does not match the consumer's credential: " +
credential);
continue;
}
auto auth_credential_iter = rule.credentials.find(credential);
if (auth_credential_iter == rule.credentials.end()) {
LOG_DEBUG("api key not found: " + credential);
continue;
}
auto credential_to_name_iter = rule.credential_to_name.find(credential);
if (credential_to_name_iter != rule.credential_to_name.end()) {
if (allow_set && !allow_set->empty()) {
if (allow_set->find(credential_to_name_iter->second) ==
allow_set->end()) {
deniedUnauthorizedConsumer(rule.realm);
LOG_DEBUG("unauthorized consumer: " +
credential_to_name_iter->second);
return false;
}
}
addRequestHeader("X-Mse-Consumer", credential_to_name_iter->second);
}
return true;
}
}
addRequestHeader("X-Mse-Consumer", credential_to_name_iter->second);
}
return true;
LOG_DEBUG("No valid credentials were found after checking all consumers.");
deniedInvalidCredentials(rule.realm);
return false;
}
std::string PluginRootContext::extractCredential(bool in_header, bool in_query,
const std::string& key) {
if (in_header) {
auto header = getRequestHeader(key);
if (header->size() != 0) {
return header->toString();
}
}
if (in_query) {
auto request_path_header = getRequestHeader(":path");
auto path = request_path_header->view();
auto params = Wasm::Common::Http::parseAndDecodeQueryString(path);
auto it = params.find(key);
if (it != params.end()) {
return it->second;
}
}
return "";
}
bool PluginRootContext::onConfigure(size_t size) {
@@ -234,31 +345,6 @@ bool PluginRootContext::configure(size_t configuration_size) {
return true;
}
std::string PluginRootContext::extractCredential(
const KeyAuthConfigRule& rule) {
auto request_path_header = getRequestHeader(":path");
auto path = request_path_header->view();
LOG_DEBUG(std::string(path));
if (rule.in_query) {
auto params = Wasm::Common::Http::parseAndDecodeQueryString(path);
for (const auto& key : rule.keys) {
auto it = params.find(key);
if (it != params.end()) {
return it->second;
}
}
}
if (rule.in_header) {
for (const auto& key : rule.keys) {
auto header = getRequestHeader(key);
if (header->size() != 0) {
return header->toString();
}
}
}
return "";
}
FilterHeadersStatus PluginContext::onRequestHeaders(uint32_t, bool) {
auto* rootCtx = rootContext();
return rootCtx->checkAuthRule(

View File

@@ -36,7 +36,16 @@ namespace key_auth {
#endif
struct Consumer {
std::string name;
std::string credential;
std::optional<std::vector<std::string>> keys;
std::optional<bool> in_query = std::nullopt;
std::optional<bool> in_header = std::nullopt;
};
struct KeyAuthConfigRule {
std::vector<Consumer> consumers;
std::unordered_set<std::string> credentials;
std::unordered_map<std::string, std::string> credential_to_name;
std::string realm = "MSE Gateway";
@@ -61,7 +70,8 @@ class PluginRootContext : public RootContext,
private:
bool parsePluginConfig(const json&, KeyAuthConfigRule&) override;
std::string extractCredential(const KeyAuthConfigRule&);
std::string extractCredential(bool in_header, bool in_query,
const std::string& key);
};
// Per-stream context.

View File

@@ -121,7 +121,7 @@ TEST_F(KeyAuthTest, InQuery) {
"_rules_": [
{
"_match_route_": ["test"],
"credentials":["abc"],
"credentials":["abc","def"],
"keys": ["apiKey", "x-api-key"]
}
]
@@ -144,6 +144,10 @@ TEST_F(KeyAuthTest, InQuery) {
path_ = "/test?hello=123&apiKey=123";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
path_ = "/test?hello=123&apiKey=123&x-api-key=def";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
}
TEST_F(KeyAuthTest, InQueryWithConsumer) {
@@ -173,6 +177,29 @@ TEST_F(KeyAuthTest, InQueryWithConsumer) {
FilterHeadersStatus::StopIteration);
}
TEST_F(KeyAuthTest, EmptyConsumer) {
std::string configuration = R"(
{
"consumers" : [],
"keys" : [ "apiKey", "x-api-key" ],
"_rules_" : [ {"_match_route_" : ["test"], "allow" : []} ]
})";
BufferBase buffer;
buffer.set(configuration);
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
route_name_ = "test";
path_ = "/test?hello=1&apiKey=abc";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
route_name_ = "test2";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
}
TEST_F(KeyAuthTest, InHeader) {
std::string configuration = R"(
{
@@ -240,6 +267,40 @@ TEST_F(KeyAuthTest, InHeaderWithConsumer) {
FilterHeadersStatus::StopIteration);
}
TEST_F(KeyAuthTest, ConsumerDifferentKey) {
std::string configuration = R"(
{
"consumers" : [ {"credential" : "abc", "name" : "consumer1", "keys" : [ "apiKey" ]}, {"credential" : "123", "name" : "consumer2"} ],
"keys" : [ "apiKey2" ],
"_rules_" : [ {"_match_route_" : ["test"], "allow" : ["consumer1"]}, {"_match_route_" : ["test2"], "allow" : ["consumer2"]} ]
})";
BufferBase buffer;
buffer.set(configuration);
EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration))
.WillOnce([&buffer](WasmBufferType) { return &buffer; });
EXPECT_TRUE(root_context_->configure(configuration.size()));
route_name_ = "test";
path_ = "/test?hello=1&apiKey=abc";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
route_name_ = "test";
path_ = "/test?hello=1&apiKey2=abc";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
route_name_ = "test";
path_ = "/test?hello=123&apiKey2=123";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
route_name_ = "test2";
path_ = "/test?hello=123&apiKey2=123";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::Continue);
}
} // namespace key_auth
} // namespace null_plugin
} // namespace proxy_wasm

View File

@@ -1,11 +1,18 @@
<p>
<a href="README_EN.md"> English </a> | 中文
</p>
---
title: 基于 Key 的本地限流
keywords: [higress,key rate limit]
description: Key 本地限流插件配置参考
---
# 功能说明
## 功能说明
`key-rate-limit`插件实现了基于特定键值实现限流,键值来源可以是 URL 参数、HTTP 请求头
# 配置字段
## 运行属性
插件执行阶段:`默认阶段`
插件执行优先级:`10`
## 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| -------- | -------- | -------- | -------- | -------- |
@@ -23,9 +30,9 @@
| query_per_hour | number | 选填,`query_per_second`,`query_per_minute`,`query_per_hour`,`query_per_day` 中选填一项 | - | 允许每小时请求次数 |
| query_per_day | number | 选填,`query_per_second`,`query_per_minute`,`query_per_hour`,`query_per_day` 中选填一项 | - | 允许每天请求次数 |
# 配置示例
## 配置示例
## 识别请求参数 apikey进行区别限流
### 识别请求参数 apikey进行区别限流
```yaml
limit_by_param: apikey
limit_keys:
@@ -35,7 +42,7 @@ limit_keys:
query_per_minute: 100
```
## 识别请求头 x-ca-key进行区别限流
### 识别请求头 x-ca-key进行区别限流
```yaml
limit_by_header: x-ca-key
limit_keys:
@@ -45,29 +52,3 @@ limit_keys:
query_per_hour: 10
```
## 对特定路由或域名开启
```yaml
# 使用 _rules_ 字段进行细粒度规则配置
_rules_:
# 规则一:按路由名称匹配生效
- _match_route_:
- route-a
- route-b
limit_by_header: x-ca-key
limit_keys:
- key: 102234
query_per_second: 10
# 规则二:按域名匹配生效
- _match_domain_:
- "*.example.com"
- test.com
limit_by_header: x-ca-key
limit_keys:
- key: 102234
query_per_second: 100
```
此例 `_match_route_` 中指定的 `route-a``route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将使用此段配置;
此例 `_match_domain_` 中指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将使用此段配置;
配置的匹配生效顺序,将按照 `_rules_` 下规则的排列顺序,匹配第一个规则后生效对应配置,后续规则将被忽略。

View File

@@ -1,31 +1,36 @@
<p>
English | <a href="README.md">中文</a>
</p>
---
title: Key-based Local Rate Limiting
keywords: [higress,key rate limit]
description: Configuration reference for Key local rate limiting plugin
---
# Description
`key-rate-limit` plugin implements a rate-limiting function based on specific key-values. The key-values may come from URL parameters or HTTP headers.
## Functional Description
The `key-rate-limit` plugin implements rate limiting based on specific key values, which can originate from URL parameters or HTTP request headers.
# Configuration Fields
## Running Properties
Plugin execution phase: `default phase`
Plugin execution priority: `10`
| Name | Type | Requirement | Default Value | Description |
| -------- | -------- | -------- | -------- | -------- |
| limit_by_header | string | Optional. Choose one from following: `limit_by_header`, `limit_by_param`. | - | The name of HTTP header used to obtain key-value used in rate-limiting. |
| limit_by_param | string | Optional. Choose one from following: `limit_by_header`, `limit_by_param`. | - | The name of URL parameter used to obtain key-value used in rate-limiting. |
| limit_keys | array of object | Required | - | Rate-limiting thresholds when matching specific key-values |
## Configuration Fields
Field descriptions of `limit_keys` items:
| Name | Data Type | Required | Default Value | Description |
|-----------------|-----------------|---------------------------------------------------------------|---------------|----------------------------------------------------------------------------------------|
| limit_by_header | string | Optional, choose one from `limit_by_header`, `limit_by_param` | - | Configuration for the source of the rate limiting key value (HTTP request header name) |
| limit_by_param | string | Optional, choose one from `limit_by_header`, `limit_by_param` | - | Configuration for the source of the rate limiting key value (URL parameter name) |
| limit_keys | array of object | Required | - | Configuration for the rate limiting frequency based on matched key values |
| Name | Type | Requirement | Default Value | Description |
| -------- | -------- | -------- | -------- | -------- |
| key | string | Required | - | Value to match of the specific key |
| query_per_second | number | Optional. Choose one from following: `query_per_second`, `query_per_minute`, `query_per_hour`, `query_per_day`. | - | Number of requests allowed per second |
| query_per_minute | number | Optional. Choose one from following: `query_per_second`, `query_per_minute`, `query_per_hour`, `query_per_day`. | - | Number of requests allowed per minute |
| query_per_hour | number | Optional. Choose one from following: `query_per_second`, `query_per_minute`, `query_per_hour`, `query_per_day`. | - | Number of requests allowed per hour |
| query_per_day | number | Optional. Choose one from following: `query_per_second`, `query_per_minute`, `query_per_hour`, `query_per_day`. | - | Number of requests allowed per day |
Explanation of each configuration field in `limit_keys`
# Configuration Samples
| Name | Data Type | Required | Default Value | Description |
|------------------|-----------|-----------------------------------------------------------------------------------------------------|---------------|---------------------------------------|
| key | string | Required | - | Matched key value |
| query_per_second | number | Optional, choose one from `query_per_second`, `query_per_minute`, `query_per_hour`, `query_per_day` | - | Allowed number of requests per second |
| query_per_minute | number | Optional, choose one from `query_per_second`, `query_per_minute`, `query_per_hour`, `query_per_day` | - | Allowed number of requests per minute |
| query_per_hour | number | Optional, choose one from `query_per_second`, `query_per_minute`, `query_per_hour`, `query_per_day` | - | Allowed number of requests per hour |
| query_per_day | number | Optional, choose one from `query_per_second`, `query_per_minute`, `query_per_hour`, `query_per_day` | - | Allowed number of requests per day |
## Use query parameter `apikey` for rate-limiting
## Configuration Examples
### Identify request parameter apikey for differentiated rate limiting
```yaml
limit_by_param: apikey
limit_keys:
@@ -35,7 +40,7 @@ limit_keys:
query_per_minute: 100
```
## Use HTTP header parameter `x-ca-key` for rate-limiting
### Identify request header x-ca-key for differentiated rate limiting
```yaml
limit_by_header: x-ca-key
limit_keys:
@@ -44,28 +49,3 @@ limit_keys:
- key: 308239
query_per_hour: 10
```
## Enable rate-limiting for specific routes or domains
```yaml
# Use _rules_ field for fine-grained rule configurations
_rules_:
# Rule 1: Match by route name
- _match_route_:
- route-a
- route-b
limit_by_header: x-ca-key
limit_keys:
- key: 102234
query_per_second: 10
# Rule 2: Match by domain
- _match_domain_:
- "*.example.com"
- test.com
limit_by_header: x-ca-key
limit_keys:
- key: 102234
query_per_second: 100
```
In the rule sample of `_match_route_`, `route-a` and `route-b` are the route names provided when creating a new gateway route. When the current route names matches the configuration, the rule following shall be applied.
In the rule sample of `_match_domain_`, `*.example.com` and `test.com` are the domain names used for request matching. When the current domain name matches the configuration, the rule following shall be applied.
All rules shall be checked following the order of items in the `_rules_` field, The first matched rule will be applied. All remained will be ignored.

View File

@@ -0,0 +1,70 @@
# 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.
load("@proxy_wasm_cpp_sdk//bazel/wasm:wasm.bzl", "wasm_cc_binary")
load("//bazel:wasm.bzl", "declare_wasm_image_targets")
wasm_cc_binary(
name = "model_router.wasm",
srcs = [
"plugin.cc",
"plugin.h",
],
deps = [
"@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics_higress",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"//common:json_util",
"//common:http_util",
"//common:rule_util",
],
)
cc_library(
name = "model_router_lib",
srcs = [
"plugin.cc",
],
hdrs = [
"plugin.h",
],
copts = ["-DNULL_PLUGIN"],
deps = [
"@com_google_absl//absl/strings",
"@com_google_absl//absl/time",
"//common:json_util",
"@proxy_wasm_cpp_host//:lib",
"//common:http_util_nullvm",
"//common:rule_util_nullvm",
],
)
cc_test(
name = "model_router_test",
srcs = [
"plugin_test.cc",
],
copts = ["-DNULL_PLUGIN"],
deps = [
":model_router_lib",
"@com_google_googletest//:gtest",
"@com_google_googletest//:gtest_main",
"@proxy_wasm_cpp_host//:lib",
],
)
declare_wasm_image_targets(
name = "model_router",
wasm_file = ":model_router.wasm",
)

View File

@@ -0,0 +1,64 @@
## 功能说明
`model-router`插件实现了基于LLM协议中的model参数路由的功能
## 运行属性
插件执行阶段:`默认阶段`
插件执行优先级:`260`
## 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ----------------------- | ------ | ------------------------------------------- |
| `enable` | bool | 选填 | false | 是否开启基于model参数路由 |
| `model_key` | string | 选填 | model | 请求body中model参数的位置 |
| `add_header_key` | string | 选填 | x-higress-llm-provider | 从model参数中解析出的provider名字放到哪个请求header中 |
## 效果说明
如下开启基于model参数路由的功能
```yaml
enable: true
```
开启后,插件将请求中 model 参数的 provider 部分(如果有)提取出来,设置到 x-higress-llm-provider 这个请求 header 中,用于后续路由,并将 model 参数重写为模型名称部分。举例来说,原生的 LLM 请求体是:
```json
{
"model": "qwen/qwen-long",
"frequency_penalty": 0,
"max_tokens": 800,
"stream": false,
"messages": [{
"role": "user",
"content": "higress项目主仓库的github地址是什么"
}],
"presence_penalty": 0,
"temperature": 0.7,
"top_p": 0.95
}
```
经过这个插件后,将添加下面这个请求头(可以用于路由匹配)
x-higress-llm-provider: qwen
原始的 LLM 请求体将被改成:
```json
{
"model": "qwen-long",
"frequency_penalty": 0,
"max_tokens": 800,
"stream": false,
"messages": [{
"role": "user",
"content": "higress项目主仓库的github地址是什么"
}],
"presence_penalty": 0,
"temperature": 0.7,
"top_p": 0.95
}
```

View File

@@ -0,0 +1,63 @@
## Function Description
The `model-router` plugin implements the functionality of routing based on the `model` parameter in the LLM protocol.
## Runtime Properties
Plugin Execution Phase: `Default Phase`
Plugin Execution Priority: `260`
## Configuration Fields
| Name | Data Type | Filling Requirement | Default Value | Description |
| -------------------- | ------------- | --------------------- | ---------------------- | ----------------------------------------------------- |
| `enable` | bool | Optional | false | Whether to enable routing based on the `model` parameter |
| `model_key` | string | Optional | model | The location of the `model` parameter in the request body |
| `add_header_key` | string | Optional | x-higress-llm-provider | The header where the parsed provider name from the `model` parameter will be placed |
## Effect Description
To enable routing based on the `model` parameter, use the following configuration:
```yaml
enable: true
```
After enabling, the plugin extracts the provider part (if any) from the `model` parameter in the request, and sets it in the `x-higress-llm-provider` request header for subsequent routing. It also rewrites the `model` parameter to the model name part. For example, the original LLM request body is:
```json
{
"model": "openai/gpt-4o",
"frequency_penalty": 0,
"max_tokens": 800,
"stream": false,
"messages": [{
"role": "user",
"content": "What is the GitHub address for the main repository of the Higress project?"
}],
"presence_penalty": 0,
"temperature": 0.7,
"top_p": 0.95
}
```
After processing by the plugin, the following request header (which can be used for routing matching) will be added:
`x-higress-llm-provider: openai`
The original LLM request body will be modified to:
```json
{
"model": "gpt-4o",
"frequency_penalty": 0,
"max_tokens": 800,
"stream": false,
"messages": [{
"role": "user",
"content": "What is the GitHub address for the main repository of the Higress project?"
}],
"presence_penalty": 0,
"temperature": 0.7,
"top_p": 0.95
}
```

View File

@@ -0,0 +1,189 @@
// 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.
#include "extensions/model_router/plugin.h"
#include <array>
#include <limits>
#include "absl/strings/str_cat.h"
#include "absl/strings/str_split.h"
#include "common/http_util.h"
#include "common/json_util.h"
using ::nlohmann::json;
using ::Wasm::Common::JsonArrayIterate;
using ::Wasm::Common::JsonGetField;
using ::Wasm::Common::JsonObjectIterate;
using ::Wasm::Common::JsonValueAs;
#ifdef NULL_PLUGIN
namespace proxy_wasm {
namespace null_plugin {
namespace model_router {
PROXY_WASM_NULL_PLUGIN_REGISTRY
#endif
static RegisterContextFactory register_ModelRouter(
CONTEXT_FACTORY(PluginContext), ROOT_FACTORY(PluginRootContext));
namespace {
constexpr std::string_view SetDecoderBufferLimitKey =
"SetRequestBodyBufferLimit";
constexpr std::string_view DefaultMaxBodyBytes = "10485760";
} // namespace
bool PluginRootContext::parsePluginConfig(const json& configuration,
ModelRouterConfigRule& rule) {
if (auto it = configuration.find("enable"); it != configuration.end()) {
if (it->is_boolean()) {
rule.enable_ = it->get<bool>();
} else {
LOG_WARN("Invalid type for enable. Expected boolean.");
return false;
}
}
if (auto it = configuration.find("model_key"); it != configuration.end()) {
if (it->is_string()) {
rule.model_key_ = it->get<std::string>();
} else {
LOG_WARN("Invalid type for model_key. Expected string.");
return false;
}
}
if (auto it = configuration.find("add_header_key");
it != configuration.end()) {
if (it->is_string()) {
rule.add_header_key_ = it->get<std::string>();
} else {
LOG_WARN("Invalid type for add_header_key. Expected string.");
return false;
}
}
return true;
}
bool PluginRootContext::onConfigure(size_t size) {
// Parse configuration JSON string.
if (size > 0 && !configure(size)) {
LOG_WARN("configuration has errors initialization will not continue.");
return false;
}
return true;
}
bool PluginRootContext::configure(size_t configuration_size) {
auto configuration_data = getBufferBytes(WasmBufferType::PluginConfiguration,
0, configuration_size);
// Parse configuration JSON string.
auto result = ::Wasm::Common::JsonParse(configuration_data->view());
if (!result) {
LOG_WARN(absl::StrCat("cannot parse plugin configuration JSON string: ",
configuration_data->view()));
return false;
}
if (!parseRuleConfig(result.value())) {
LOG_WARN(absl::StrCat("cannot parse plugin configuration JSON string: ",
configuration_data->view()));
return false;
}
return true;
}
FilterHeadersStatus PluginRootContext::onHeader(
const ModelRouterConfigRule& rule) {
if (!rule.enable_ || !Wasm::Common::Http::hasRequestBody()) {
return FilterHeadersStatus::Continue;
}
auto content_type_value =
getRequestHeader(Wasm::Common::Http::Header::ContentType);
if (!absl::StrContains(content_type_value->view(),
Wasm::Common::Http::ContentTypeValues::Json)) {
return FilterHeadersStatus::Continue;
}
removeRequestHeader(Wasm::Common::Http::Header::ContentLength);
setFilterState(SetDecoderBufferLimitKey, DefaultMaxBodyBytes);
return FilterHeadersStatus::StopIteration;
}
FilterDataStatus PluginRootContext::onBody(const ModelRouterConfigRule& rule,
std::string_view body) {
const auto& model_key = rule.model_key_;
const auto& add_header_key = rule.add_header_key_;
auto body_json_opt = ::Wasm::Common::JsonParse(body);
if (!body_json_opt) {
LOG_WARN(absl::StrCat("cannot parse body to JSON string: ", body));
return FilterDataStatus::Continue;
}
auto body_json = body_json_opt.value();
if (body_json.contains(model_key)) {
std::string model_value = body_json[model_key];
auto pos = model_value.find('/');
if (pos != std::string::npos) {
const auto& provider = model_value.substr(0, pos);
const auto& model = model_value.substr(pos + 1);
replaceRequestHeader(add_header_key, provider);
body_json[model_key] = model;
setBuffer(WasmBufferType::HttpRequestBody, 0,
std::numeric_limits<size_t>::max(), body_json.dump());
LOG_DEBUG(absl::StrCat("model route to provider:", provider,
", model:", model));
} else {
LOG_DEBUG(absl::StrCat("model route not work, model:", model_value));
}
}
return FilterDataStatus::Continue;
}
FilterHeadersStatus PluginContext::onRequestHeaders(uint32_t, bool) {
auto* rootCtx = rootContext();
return rootCtx->onHeaders([rootCtx, this](const auto& config) {
auto ret = rootCtx->onHeader(config);
if (ret == FilterHeadersStatus::StopIteration) {
this->config_ = &config;
}
return ret;
});
}
FilterDataStatus PluginContext::onRequestBody(size_t body_size,
bool end_stream) {
if (config_ == nullptr) {
return FilterDataStatus::Continue;
}
body_total_size_ += body_size;
if (!end_stream) {
return FilterDataStatus::StopIterationAndBuffer;
}
auto body =
getBufferBytes(WasmBufferType::HttpRequestBody, 0, body_total_size_);
auto* rootCtx = rootContext();
return rootCtx->onBody(*config_, body->view());
}
#ifdef NULL_PLUGIN
} // namespace model_router
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@@ -0,0 +1,85 @@
/*
* 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.
*/
#include <assert.h>
#include <string>
#include <unordered_set>
#include "common/route_rule_matcher.h"
#define ASSERT(_X) assert(_X)
#ifndef NULL_PLUGIN
#include "proxy_wasm_intrinsics.h"
#else
#include "include/proxy-wasm/null_plugin.h"
namespace proxy_wasm {
namespace null_plugin {
namespace model_router {
#endif
struct ModelRouterConfigRule {
bool enable_ = false;
std::string model_key_ = "model";
std::string add_header_key_ = "x-higress-llm-provider";
};
// PluginRootContext is the root context for all streams processed by the
// thread. It has the same lifetime as the worker thread and acts as target for
// interactions that outlives individual stream, e.g. timer, async calls.
class PluginRootContext : public RootContext,
public RouteRuleMatcher<ModelRouterConfigRule> {
public:
PluginRootContext(uint32_t id, std::string_view root_id)
: RootContext(id, root_id) {}
~PluginRootContext() {}
bool onConfigure(size_t) override;
FilterHeadersStatus onHeader(const ModelRouterConfigRule&);
FilterDataStatus onBody(const ModelRouterConfigRule&, std::string_view);
bool configure(size_t);
private:
bool parsePluginConfig(const json&, ModelRouterConfigRule&) override;
};
// Per-stream context.
class PluginContext : public Context {
public:
explicit PluginContext(uint32_t id, RootContext* root) : Context(id, root) {}
FilterHeadersStatus onRequestHeaders(uint32_t, bool) override;
FilterDataStatus onRequestBody(size_t, bool) override;
private:
inline PluginRootContext* rootContext() {
return dynamic_cast<PluginRootContext*>(this->root());
}
size_t body_total_size_ = 0;
const ModelRouterConfigRule* config_ = nullptr;
};
#ifdef NULL_PLUGIN
} // namespace model_router
} // namespace null_plugin
} // namespace proxy_wasm
#endif

View File

@@ -0,0 +1,192 @@
// 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.
#include "extensions/model_router/plugin.h"
#include <cstddef>
#include "gmock/gmock.h"
#include "gtest/gtest.h"
#include "include/proxy-wasm/context.h"
#include "include/proxy-wasm/null.h"
namespace proxy_wasm {
namespace null_plugin {
namespace model_router {
NullPluginRegistry* context_registry_;
RegisterNullVmPluginFactory register_model_router_plugin("model_router", []() {
return std::make_unique<NullPlugin>(model_router::context_registry_);
});
class MockContext : public proxy_wasm::ContextBase {
public:
MockContext(WasmBase* wasm) : ContextBase(wasm) {}
MOCK_METHOD(BufferInterface*, getBuffer, (WasmBufferType));
MOCK_METHOD(WasmResult, log, (uint32_t, std::string_view));
MOCK_METHOD(WasmResult, setBuffer,
(WasmBufferType, size_t, size_t, std::string_view));
MOCK_METHOD(WasmResult, getHeaderMapValue,
(WasmHeaderMapType /* type */, std::string_view /* key */,
std::string_view* /*result */));
MOCK_METHOD(WasmResult, replaceHeaderMapValue,
(WasmHeaderMapType /* type */, std::string_view /* key */,
std::string_view /* value */));
MOCK_METHOD(WasmResult, removeHeaderMapValue,
(WasmHeaderMapType /* type */, std::string_view /* key */));
MOCK_METHOD(WasmResult, addHeaderMapValue,
(WasmHeaderMapType, std::string_view, std::string_view));
MOCK_METHOD(WasmResult, getProperty, (std::string_view, std::string*));
MOCK_METHOD(WasmResult, setProperty, (std::string_view, std::string_view));
};
class ModelRouterTest : public ::testing::Test {
protected:
ModelRouterTest() {
// Initialize test VM
test_vm_ = createNullVm();
wasm_base_ = std::make_unique<WasmBase>(
std::move(test_vm_), "test-vm", "", "",
std::unordered_map<std::string, std::string>{},
AllowedCapabilitiesMap{});
wasm_base_->load("model_router");
wasm_base_->initialize();
// Initialize host side context
mock_context_ = std::make_unique<MockContext>(wasm_base_.get());
current_context_ = mock_context_.get();
// Initialize Wasm sandbox context
root_context_ = std::make_unique<PluginRootContext>(0, "");
context_ = std::make_unique<PluginContext>(1, root_context_.get());
ON_CALL(*mock_context_, log(testing::_, testing::_))
.WillByDefault([](uint32_t, std::string_view m) {
std::cerr << m << "\n";
return WasmResult::Ok;
});
ON_CALL(*mock_context_, getBuffer(testing::_))
.WillByDefault([&](WasmBufferType type) {
if (type == WasmBufferType::HttpRequestBody) {
return &body_;
}
return &config_;
});
ON_CALL(*mock_context_, getHeaderMapValue(WasmHeaderMapType::RequestHeaders,
testing::_, testing::_))
.WillByDefault([&](WasmHeaderMapType, std::string_view header,
std::string_view* result) {
if (header == "content-type") {
*result = "application/json";
} else if (header == "content-length") {
*result = "1024";
}
return WasmResult::Ok;
});
ON_CALL(*mock_context_,
replaceHeaderMapValue(WasmHeaderMapType::RequestHeaders, testing::_,
testing::_))
.WillByDefault([&](WasmHeaderMapType, std::string_view key,
std::string_view value) { return WasmResult::Ok; });
ON_CALL(*mock_context_,
removeHeaderMapValue(WasmHeaderMapType::RequestHeaders, testing::_))
.WillByDefault([&](WasmHeaderMapType, std::string_view key) {
return WasmResult::Ok;
});
ON_CALL(*mock_context_, addHeaderMapValue(WasmHeaderMapType::RequestHeaders,
testing::_, testing::_))
.WillByDefault([&](WasmHeaderMapType, std::string_view header,
std::string_view value) { return WasmResult::Ok; });
ON_CALL(*mock_context_, getProperty(testing::_, testing::_))
.WillByDefault([&](std::string_view path, std::string* result) {
*result = route_name_;
return WasmResult::Ok;
});
ON_CALL(*mock_context_, setProperty(testing::_, testing::_))
.WillByDefault(
[&](std::string_view, std::string_view) { return WasmResult::Ok; });
}
~ModelRouterTest() override {}
std::unique_ptr<WasmBase> wasm_base_;
std::unique_ptr<WasmVm> test_vm_;
std::unique_ptr<MockContext> mock_context_;
std::unique_ptr<PluginRootContext> root_context_;
std::unique_ptr<PluginContext> context_;
std::string route_name_;
BufferBase body_;
BufferBase config_;
};
TEST_F(ModelRouterTest, RewriteModelAndHeader) {
std::string configuration = R"(
{
"enable": true
})";
config_.set(configuration);
EXPECT_TRUE(root_context_->configure(configuration.size()));
std::string request_json = R"({"model": "qwen/qwen-long"})";
EXPECT_CALL(*mock_context_,
setBuffer(testing::_, testing::_, testing::_, testing::_))
.WillOnce([&](WasmBufferType, size_t, size_t, std::string_view body) {
EXPECT_EQ(body, R"({"model":"qwen-long"})");
return WasmResult::Ok;
});
EXPECT_CALL(*mock_context_,
replaceHeaderMapValue(testing::_,
std::string_view("x-higress-llm-provider"),
std::string_view("qwen")));
body_.set(request_json);
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
EXPECT_EQ(context_->onRequestBody(28, true), FilterDataStatus::Continue);
}
TEST_F(ModelRouterTest, RouteLevelRewriteModelAndHeader) {
std::string configuration = R"(
{
"_rules_": [
{
"_match_route_": ["route-a"],
"enable": true
}
]})";
config_.set(configuration);
EXPECT_TRUE(root_context_->configure(configuration.size()));
std::string request_json = R"({"model": "qwen/qwen-long"})";
EXPECT_CALL(*mock_context_,
setBuffer(testing::_, testing::_, testing::_, testing::_))
.WillOnce([&](WasmBufferType, size_t, size_t, std::string_view body) {
EXPECT_EQ(body, R"({"model":"qwen-long"})");
return WasmResult::Ok;
});
EXPECT_CALL(*mock_context_,
replaceHeaderMapValue(testing::_,
std::string_view("x-higress-llm-provider"),
std::string_view("qwen")));
body_.set(request_json);
route_name_ = "route-a";
EXPECT_EQ(context_->onRequestHeaders(0, false),
FilterHeadersStatus::StopIteration);
EXPECT_EQ(context_->onRequestBody(28, true), FilterDataStatus::Continue);
}
} // namespace model_router
} // namespace null_plugin
} // namespace proxy_wasm

View File

@@ -1,21 +1,32 @@
# 功能说明
---
title: OAuth2 认证
keywords: [higress,oauth2]
description: OAuth2 认证插件配置参考
---
## 功能说明
`OAuth2`插件实现了基于JWT(JSON Web Tokens)进行OAuth2 Access Token签发的能力, 遵循[RFC9068](https://datatracker.ietf.org/doc/html/rfc9068)规范
# 插件配置说明
## 运行属性
插件执行阶段:`认证阶段`
插件执行优先级:`350`
## 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `consumers` | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
| `_rules_` | array of object | 选填 | - | 配置特定路由或域名的访问权限列表,用于对请求进行鉴权 |
| `issuer` | string | 选填 | Higress-Gateway | 用于填充JWT中的issuer |
| `auth_path` | string | 选填 | /oauth2/token | 指定路径后缀用于签发Token路由级配置时要确保首先能匹配对应的路由 |
| `global_credentials` | bool | 选填 | ture | 是否开启全局凭证即允许路由A下的auth_path签发Token可以用于访问路由B |
| `auth_header_name` | string | 选填 | Authorization | 用于指定从哪个请求头获取JWT |
| `token_ttl` | number | 选填 | 7200 | token从签发后多久内有效单位为秒 |
| `clock_skew_seconds` | number | 选填 | 60 | 校验JWT的exp和iat字段时允许的时钟偏移量单位为秒 |
| `keep_token` | bool | 选填 | ture | 转发给后端时是否保留JWT |
### 认证配置
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `consumers` | array of object | 必填 | - | 配置服务的调用者,用于对请求进行认证 |
| `issuer` | string | 选填 | Higress-Gateway | 用于填充JWT中的issuer |
| `auth_path` | string | 选填 | /oauth2/token | 指定路径后缀用于签发Token,路由级配置时,要确保首先能匹配对应的路由, 使用 API 管理时,需要创建相同路径的接口 |
| `global_credentials` | bool | 选填 | ture | 在通过 consumer 认证的前提下,允许任意路由签发的凭证访问 |
| `auth_header_name` | string | 选填 | Authorization | 用于指定从哪个请求头获取JWT |
| `token_ttl` | number | 选填 | 7200 | token从签发后多久内有效单位为秒 |
| `clock_skew_seconds` | number | 选填 | 60 | 校验JWT的exp和iat字段时允许的时钟偏移量单位为秒 |
| `keep_token` | bool | 选填 | ture | 转发给后端时是否保留JWT |
| `global_auth` | array of string | 选填(**仅实例级别配置**) | - | 只能在实例级别配置若配置为true则全局生效认证机制; 若配置为false则只对做了配置的域名和路由生效认证机制; 若不配置则仅当没有域名和路由配置时全局 生效(兼容老用户使用习惯) |
`consumers`中每一项的配置字段说明如下:
@@ -25,27 +36,53 @@
| `client_id` | string | 必填 | - | OAuth2 client id |
| `client_secret` | string | 必填 | - | OAuth2 client secret |
`_rules_` 中每一项的配置字段说明如下:
**注意:**
- 对于开启该配置的路由,如果路径后缀和`auth_path`匹配则该路由不会到原目标服务而是用于生成Token
- 如果关闭`global_credentials`,请确保启用此插件的路由不是精确匹配路由,此时若存在另一条前缀匹配路由,则可能导致预期外行为
- 对于通过认证鉴权的请求请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ---------------- | --------------- | ------------------------------------------------- | ------ | -------------------------------------------------- |
| `_match_route_` | array of string | 选填,`_match_route_``_match_domain_`中选填一项 | - | 配置要匹配的路由名称 |
| `_match_domain_` | array of string | 选填,`_match_route_``_match_domain_`中选填一项 | - | 配置要匹配的域名 |
| `allow` | array of string | 必填 | - | 对于符合匹配条件的请求配置允许访问的consumer名称 |
### 鉴权配置(非必需)
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| ----------- | --------------- | ------------------------------------------- | ------ | ----------------------------------------------------------- |
| `allow` | array of string | 选填(**非实例级别配置**) | - | 只能在路由或域名等细粒度规则上配置,对于符合匹配条件的请求,配置允许访问的 consumer,从而实现细粒度的权限控制 |
**注意:**
- 对于开启该配置的路由,如果路径后缀和`auth_path`匹配则该路由到原目标服务而是用于生成Token
- 如果关闭`global_credentials`,请确保启用此插件的路由不是精确匹配路由,此时若存在另一条前缀匹配路由,则可能导致预期外行为
- 若不配置`_rules_`字段,则默认对当前网关实例的所有路由开启认证;
- 对于通过认证鉴权的请求请求的header会被添加一个`X-Mse-Consumer`字段,用以标识调用者的名称。
- 在一个规则里,鉴权配置和认证配置不可同时存在
## 配置示例
### 对特定路由或域名开启
### 路由粒度配置认证
`route-a``route-b`两个路由做如下插件配置:
```yaml
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```
此时虽然使用同一份配置,但`route-a` 下签发的凭证无法用于访问 `route-b`,反之亦然。
如果希望同一份配置共享凭证访问权限,可以做如下配置:
```yaml
global_credentials: true
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```
### 全局配置认证,路由粒度进行鉴权
以下配置将对网关特定路由或域名开启 Jwt Auth 认证和鉴权注意如果一个JWT能匹配多个`jwks`,则按照配置顺序命中第一个匹配的`consumer`
在实例级别做如下插件配置:
```yaml
global_auth: false
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
@@ -53,37 +90,54 @@ consumers:
- name: consumer2
client_id: 87654321-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: hgfedcba-xxxx-xxxx-xxxx-xxxxxxxxxxxx
# 使用 _rules_ 字段进行细粒度规则配置
_rules_:
# 规则一:按路由名称匹配生效
- _match_route_:
- route-a
- route-b
allow:
- consumer1
# 规则二:按域名匹配生效
- _match_domain_:
- "*.example.com"
- test.com
allow:
- consumer2
```
此例 `_match_route_` 中指定的 `route-a``route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将允许`name``consumer1`的调用者访问,其他调用者不允许访问;
`route-a``route-b`两个路由做如下插件配置:
此例 `_match_domain_` 中指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将允许`name``consumer2`的调用者访问,其他调用者不允许访问。
```yaml
allow:
- consumer1
```
#### 使用 Client Credential 授权模式
`*.exmaple.com``test.com`两个域名做如下插件配置:
**获取 AccessToken**
```yaml
allow:
- consumer2
```
此例指定的 `route-a``route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将允许`name``consumer1`的调用者访问,其他调用者不允许访问;
此例指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将允许`name``consumer2`的调用者访问,其他调用者不允许访问。
### 网关实例级别开启
以下配置将对网关实例级别开启 OAuth2 认证,所有请求均需要经过认证后才能访问
```yaml
global_auth: true
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: consumer2
client_id: 87654321-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: hgfedcba-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```
# 请求示例
## 使用 Client Credential 授权模式
### 获取 AccessToken
```bash
# 通过 GET 方法获取
# 通过 GET 方法获取(推荐)
curl 'http://test.com/oauth2/token?grant_type=client_credentials&client_id=12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx&client_secret=abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# 通过 POST 方法获取 (需要先匹配到有真实目标服务的路由)
# 通过 POST 方法获取需要先匹配到有真实目标服务的路由,否则网关不会读取请求 Body
curl 'http://test.com/oauth2/token' -H 'content-type: application/x-www-form-urlencoded' -d 'grant_type=client_credentials&client_id=12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx&client_secret=abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
@@ -96,29 +150,13 @@ curl 'http://test.com/oauth2/token' -H 'content-type: application/x-www-form-url
```
**使用 AccessToken 请求**
### 使用 AccessToken 请求
```bash
curl 'http://test.com' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6ImFwcGxpY2F0aW9uXC9hdCtqd3QifQ.eyJhdWQiOiJkZWZhdWx0IiwiY2xpZW50X2lkIjoiMTIzNDU2NzgteHh4eC14eHh4LXh4eHgteHh4eHh4eHh4eHh4IiwiZXhwIjoxNjg3OTUxNDYzLCJpYXQiOjE2ODc5NDQyNjMsImlzcyI6IkhpZ3Jlc3MtR2F0ZXdheSIsImp0aSI6IjEwOTU5ZDFiLThkNjEtNGRlYy1iZWE3LTk0ODEwMzc1YjYzYyIsInN1YiI6ImNvbnN1bWVyMSJ9.NkT_rG3DcV9543vBQgneVqoGfIhVeOuUBwLJJ4Wycb0'
```
因为 test.com 仅授权了 consumer2但这个 Access Token 是基于 consumer1 的 `client_id``client_secret` 获取的,因此将返回 `403 Access Denied`
### 网关实例级别开启
以下配置未指定`_rules_`字段,因此将对网关实例级别开启 OAuth2 认证
```yaml
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: consumer2
client_id: 87654321-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: hgfedcba-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```
# 常见错误码说明
@@ -126,4 +164,3 @@ consumers:
| ----------- | ---------------------- | -------------------------------------------------------------------------------- |
| 401 | Invalid Jwt token | 请求头未提供JWT, 或者JWT格式错误或过期等原因 |
| 403 | Access Denied | 无权限访问当前路由 |

View File

@@ -0,0 +1,138 @@
---
title: OAuth2 Authentication
keywords: [higress,oauth2]
description: OAuth2 authentication plugin configuration reference
---
## Function Description
`OAuth2` plugin implements the capability of issuing OAuth2 Access Tokens based on JWT (JSON Web Tokens), complying with the [RFC9068](https://datatracker.ietf.org/doc/html/rfc9068) specification.
## Runtime Properties
Plugin execution phase: `Authentication Phase`
Plugin execution priority: `350`
## Configuration Fields
### Authentication Configuration
| Name | Data Type | Requirement | Default Value | Description |
| -------------------- | ---------------- | ------------------------------------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `consumers` | array of object | Required | - | Configures the callers of the service for request authentication |
| `issuer` | string | Optional | Higress-Gateway | Used to fill the issuer in the JWT |
| `auth_path` | string | Optional | /oauth2/token | Specifies the path suffix for issuing Tokens. When configured at the routing level, ensure it matches the corresponding route first. When using API management, create an interface with the same path. |
| `global_credentials` | bool | Optional | true | Allows any route to issue credentials for access under the condition of authentication through the consumer. |
| `auth_header_name` | string | Optional | Authorization | Specifies which request header to retrieve the JWT from |
| `token_ttl` | number | Optional | 7200 | The time duration in seconds for which the token is valid after issuance. |
| `clock_skew_seconds` | number | Optional | 60 | Allowed clock skew when verifying the exp and iat fields of the JWT, in seconds. |
| `keep_token` | bool | Optional | true | Indicates whether to keep the JWT when forwarding to the backend. |
| `global_auth` | array of string | Optional (**Instance-level configuration only**) | - | Can only be configured at the instance level. If set to true, the global authentication mechanism takes effect; if false, the authentication mechanism only takes effect for configured domains and routes; if not configured, global effect occurs only when there are no domain and route configurations (compatible with legacy user habits). |
The configuration fields for each item in `consumers` are as follows:
| Name | Data Type | Requirement | Default Value | Description |
| ----------------------- | ------------------| ----------- | ------------------------------------------------- | ---------------------------------- |
| `name` | string | Required | - | Configures the name of the consumer. |
| `client_id` | string | Required | - | OAuth2 client id |
| `client_secret` | string | Required | - | OAuth2 client secret |
**Note:**
- For routes with this configuration enabled, if the path suffix matches `auth_path`, the route will not forward to the original target service but will be used to generate a Token.
- If `global_credentials` is disabled, please ensure that the routes enabling this plugin do not precisely match routes. If there is another prefix-matching route, it may lead to unexpected behavior.
- For requests authenticated and authorized, the request header will have an `X-Mse-Consumer` field added to identify the caller's name.
### Authorization Configuration (Optional)
| Name | Data Type | Requirement | Default Value | Description |
| ----------- | ---------------- | ---------------------------------------------- | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `allow` | array of string | Optional (**Non-instance-level configuration**) | - | Can only be configured on fine-grained rules such as routes or domains, allowing specified consumers to access requests that meet the matching conditions for fine-grained permission control. |
**Note:**
- Authentication and authorization configurations cannot coexist in one rule.
## Configuration Example
### Route Granularity Configuration Authentication
For the two routes `route-a` and `route-b`, do the following plugin configuration:
```yaml
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```
At this time, although using the same configuration, the credentials issued under `route-a` cannot be used to access `route-b`, and vice versa.
If you want the same configuration to share credential access permissions, you can configure as follows:
```yaml
global_credentials: true
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```
### Global Configuration Authentication, Route Granularity Authorization
The following configuration will enable Jwt Auth for specific routes or domains on the gateway. Note that if a JWT matches multiple `jwks`, it will hit the first matching `consumer` in the order of configuration.
At the instance level, do the following plugin configuration:
```yaml
global_auth: false
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: consumer2
client_id: 87654321-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: hgfedcba-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```
For the routes `route-a` and `route-b`, do the following plugin configuration:
```yaml
allow:
- consumer1
```
For the domains `*.example.com` and `test.com`, do the following plugin configuration:
```yaml
allow:
- consumer2
```
In this example, route names `route-a` and `route-b` refer to the route names filled in when creating the gateway route. When these two routes are matched, it will allow access for the caller with `name` as `consumer1`, and other callers will not be allowed to access.
In this example, the domains `*.example.com` and `test.com` are used to match request domains. When a matching domain is found, it will allow access for the caller with `name` as `consumer2`, while other callers will not be allowed to access.
### Enable at Gateway Instance Level
The following configuration will enable OAuth2 authentication at the gateway instance level, requiring all requests to be authenticated before access.
```yaml
global_auth: true
consumers:
- name: consumer1
client_id: 12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx
- name: consumer2
client_id: 87654321-xxxx-xxxx-xxxx-xxxxxxxxxxxx
client_secret: hgfedcba-xxxx-xxxx-xxxx-xxxxxxxxxxxx
```
# Request Example
## Using Client Credential Authorization Mode
### Get AccessToken
```bash
# Get via GET method (recommended)
curl 'http://test.com/oauth2/token?grant_type=client_credentials&client_id=12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx&client_secret=abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# Get via POST method (requires matching a route with a real target service first, or the gateway will not read the request Body)
curl 'http://test.com/oauth2/token' -H 'content-type: application/x-www-form-urlencoded' -d 'grant_type=client_credentials&client_id=12345678-xxxx-xxxx-xxxx-xxxxxxxxxxxx&client_secret=abcdefgh-xxxx-xxxx-xxxx-xxxxxxxxxxxx'
# Simply get the access_token field from the response:
{
"token_type": "bearer",
"access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6ImFwcGxpY2F0aW9uXC9hdCtqd3QifQ.eyJhdWQiOiJkZWZhdWx0IiwiY2xpZW50X2lkIjoiMTIzNDU2NzgteHh4eC14eHh4LXh4eHgteHh4eHh4eHh4eHh4IiwiZXhwIjoxNjg3OTUxNDYzLCJpYXQiOjE2ODc5NDQyNjMsImlzcyI6IkhpZ3Jlc3MtR2F0ZXdheSIsImp0aSI6IjEwOTU5ZDFiLThkNjEtNGRlYy1iZWE3LTk0ODEwMzc1YjYzYyIsInN1YiI6ImNvbnN1bWVyMSJ9.NkT_rG3DcV9543vBQgneVqoGfIhVeOuUBwLJJ4Wycb0",
"expires_in": 7200
}
```
### AccessToken Request
```bash
curl 'http://test.com' -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6ImFwcGxpY2F0aW9uXC9hdCtqd3QifQ.eyJhdWQiOiJkZWZhdWx0IiwiY2xpZW50X2lkIjoiMTIzNDU2NzgteHh4eC14eHh4LXh4eHgteHh4eHh4eHh4eHh4IiwiZXhwIjoxNjg3OTUxNDYzLCJpYXQiOjE2ODc5NDQyNjMsImlzcyI6IkhpZ3Jlc3MtR2F0ZXdheSIsImp0aSI6IjEwOTU5ZDFiLThkNjEtNGRlYy1iZWE3LTk0ODEwMzc1YjYzYyIsInN1YiI6ImNvbnN1bWVyMSJ9.NkT_rG3DcV9543vBQgneVqoGfIhVeOuUBwLJJ4Wycb0'
```
# Common Error Code Description
| HTTP Status Code | Error Message | Explanation |
| ---------------- | ----------------------| --------------------------------------------------------------------------- |
| 401 | Invalid Jwt token | JWT not provided in request header, or JWT format is incorrect, or expired, etc. |
| 403 | Access Denied | No permission to access the current route. |

View File

@@ -1,11 +1,18 @@
<p>
<a href="README_EN.md"> English </a> | 中文
</p>
---
title: 请求屏蔽
keywords: [higress,request block]
description: 请求屏蔽插件配置参考
---
# 功能说明
## 功能说明
`request-block`插件实现了基于 URL、请求头等特征屏蔽 HTTP 请求,可以用于防护部分站点资源不对外部暴露
# 配置字段
## 运行属性
插件执行阶段:`鉴权阶段`
插件执行优先级:`320`
## 配置字段
| 名称 | 数据类型 | 填写要求 | 默认值 | 描述 |
| -------- | -------- | -------- | -------- | -------- |
@@ -16,9 +23,9 @@
| blocked_message | string | 选填 | - | 配置请求被屏蔽时返回的 HTTP 应答 Body |
| case_sensitive | bool | 选填 | true | 配置匹配时是否区分大小写,默认区分 |
# 配置示例
## 配置示例
## 屏蔽请求 url 路径
### 屏蔽请求 url 路径
```yaml
block_urls:
- swagger.html
@@ -33,7 +40,7 @@ curl http://example.com?foo=Bar
curl http://exmaple.com/Swagger.html
```
## 屏蔽请求 header
### 屏蔽请求 header
```yaml
block_headers:
- example-key
@@ -47,7 +54,7 @@ curl http://example.com -H 'example-key: 123'
curl http://exmaple.com -H 'my-header: example-value'
```
## 屏蔽请求 body
### 屏蔽请求 body
```yaml
block_bodies:
- "hello world"
@@ -61,30 +68,8 @@ curl http://example.com -d 'Hello World'
curl http://exmaple.com -d 'hello world'
```
## 对特定路由或域名开启
```yaml
# 使用 _rules_ 字段进行细粒度规则配置
_rules_:
# 规则一:按路由名称匹配生效
- _match_route_:
- route-a
- route-b
block_bodies:
- "hello world"
# 规则二:按域名匹配生效
- _match_domain_:
- "*.example.com"
- test.com
block_urls:
- "swagger.html"
block_bodies:
- "hello world"
```
此例 `_match_route_` 中指定的 `route-a``route-b` 即在创建网关路由时填写的路由名称,当匹配到这两个路由时,将使用此段配置;
此例 `_match_domain_` 中指定的 `*.example.com``test.com` 用于匹配请求的域名,当发现域名匹配时,将使用此段配置;
配置的匹配生效顺序,将按照 `_rules_` 下规则的排列顺序,匹配第一个规则后生效对应配置,后续规则将被忽略。
# 请求 Body 大小限制
## 请求 Body 大小限制
当配置了 `block_bodies` 时,仅支持小于 32 MB 的请求 Body 进行匹配。若请求 Body 大于此限制,并且不存在匹配到的 `block_urls``block_headers` 项时,不会对该请求执行屏蔽操作
当配置了 `block_bodies` 时,若请求 Body 超过全局配置 DownstreamConnectionBufferLimits将返回 `413 Payload Too Large`

View File

@@ -1,24 +1,28 @@
<p>
English | <a href="README.md">中文</a>
</p>
---
title: Request Blocking
keywords: [higress,request block]
description: Request blocking plugin configuration reference
---
## Function Description
The `request-block` plugin implements HTTP request blocking based on features such as URL, request headers, etc. It can be used to protect certain site resources from being exposed to the outside.
# Description
`request-block` plugin implements a request blocking function based on request characteristics such as URL and request header. It can be used to protect internal resources from unauthorized access.
## Running Attributes
Plugin Execution Stage: `Authentication Stage`
# Configuration Fields
Plugin Execution Priority: `320`
| Name | Type | Requirement | Default Value | Description |
| -------- | -------- | -------- | -------- | -------- |
| block_urls | array of string | Optional. Choose one from following: `block_urls`, `block_headers`, `block_bodies` | - | HTTP URLs to be blocked. |
| block_headers | array of string | Optional. Choose one from following: `block_urls`, `block_headers`, `block_bodies` | - | HTTP request headers to be blocked. |
| block_bodies | array of string | Optional. Choose one from following: `block_urls` ,`block_headers`, `block_bodies` | - | HTTP request bodies to be blocked. |
| blocked_code | number | Optional | 403 | HTTP response status code to be sent when corresponding request is blocked. |
| blocked_message | string | Optional | - | HTTP response body to be sent when corresponding request is blocked. |
| case_sensitive | bool | Optional | true | Whether to use case-senstive comparison when matching. Enabled by default. |
## Configuration Fields
| Name | Data Type | Fill Requirement | Default Value | Description |
|--------------------|--------------------|---------------------------------------------------------|---------------|------------------------------------------------------------|
| block_urls | array of string | Optional, at least one of `block_urls`, `block_headers`, `block_bodies` must be filled | - | Configure strings for matching URLs that need to be blocked |
| block_headers | array of string | Optional, at least one of `block_urls`, `block_headers`, `block_bodies` must be filled | - | Configure strings for matching request headers that need to be blocked |
| block_bodies | array of string | Optional, at least one of `block_urls`, `block_headers`, `block_bodies` must be filled | - | Configure strings for matching request bodies that need to be blocked |
| blocked_code | number | Optional | 403 | Configure the HTTP status code returned when a request is blocked |
| blocked_message | string | Optional | - | Configure the HTTP response body returned when a request is blocked |
| case_sensitive | bool | Optional | true | Configure whether matching is case-sensitive, default is case-sensitive |
# Configuration Samples
## Block Specific Request URLs
## Configuration Example
### Blocking Request URL Paths
```yaml
block_urls:
- swagger.html
@@ -26,65 +30,39 @@ block_urls:
case_sensitive: false
```
According to the configuration above, following requests will be blocked:
Based on this configuration, the following requests will be denied access:
```bash
curl http://example.com?foo=Bar
curl http://exmaple.com/Swagger.html
```
## Block Specific Request Headers
### Blocking Request Headers
```yaml
block_headers:
- example-key
- example-value
```
According to the configuration above, following requests will be blocked:
Based on this configuration, the following requests will be denied access:
```bash
curl http://example.com -H 'example-key: 123'
curl http://exmaple.com -H 'my-header: example-value'
```
## Block Specific Request Bodies
### Blocking Request Bodies
```yaml
block_bodies:
- "hello world"
case_sensitive: false
```
According to the configuration above, following requests will be blocked:
Based on this configuration, the following requests will be denied access:
```bash
curl http://example.com -d 'Hello World'
curl http://exmaple.com -d 'hello world'
```
## Only Enable for Specific Routes or Domains
```yaml
# Use _rules_ field for fine-grained rule configurations
_rules_:
# Rule 1: Match by route name
- _match_route_:
- route-a
- route-b
block_bodies:
- "hello world"
# Rule 2: Match by domain
- _match_domain_:
- "*.example.com"
- test.com
block_urls:
- "swagger.html"
block_bodies:
- "hello world"
```
In the rule sample of `_match_route_`, `route-a` and `route-b` are the route names provided when creating a new gateway route. When the current route names matches the configuration, the rule following shall be applied.
In the rule sample of `_match_domain_`, `*.example.com` and `test.com` are the domain names used for request matching. When the current domain name matches the configuration, the rule following shall be applied.
All rules shall be checked following the order of items in the `_rules_` field, The first matched rule will be applied. All remained will be ignored.
## Request Body Size Limit
When `block_bodies` is configured, only request bodies smaller than 32 MB are supported for matching. If the request body exceeds this limit and there are no matching `block_urls` or `block_headers`, the blocking operation will not be executed for that request.
# Maximum Request Body Size Limitation
When `block_bodies` is configured, body matching shall only be performed when its size is smaller than 32MB. If not, and no `block_urls` or `block_headers` configuration is matched, the request won't be blocked.
When `block_bodies` is configured, if the size of request body exceeds the global configuration of DownstreamConnectionBufferLimits, a ``413 Payload Too Large`` response will be returned.
When `block_bodies` is configured and the request body exceeds the global configuration DownstreamConnectionBufferLimits, it will return `413 Payload Too Large`.

View File

@@ -1,11 +1,14 @@
# 功能说明
## 功能说明
**此插件已经废弃从1.4版本开始Higress 从根本上解决了这一问题,不再需要此插件**
`http2-misdirect`插件用于解决网关开启 HTTP2 时,因为浏览器复用连接导致访问出现 404 等问题。
# 插件原理
## 插件原理
HTTP2 协议允许两个不同域名的请求,在域名解析到相同 IP并且使用了相同证书的情况下复用同一条连接。这在一些特殊场景会导致复用连接的请求发送给了错误的 Virtual Host 进行处理,从而导致出现 404 等问题。
本插件基于`HTTP/2 RFC 7540``9.1.1``9.1.2`章节描述,在发现请求 SNI 与当前 Virtual Host 不匹配时,发送 HTTP 421 状态码,强制浏览器新建连接,并根据当前请求域名生成匹配的 SNI从而让网关能正确处理路由。
# 浏览器兼容性
## 浏览器兼容性
`Safari` 浏览器 `15.1` 版本以下不支持 HTTP 421 状态码,若有此类客户端访问场景,建议对相应域名关闭 HTTP2 的 ALPN

View File

@@ -1,4 +1,4 @@
ARG BUILDER=higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.19-tinygo0.28.1-oras1.0.0
ARG BUILDER=higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/wasm-go-builder:go1.20.14-tinygo0.29.0-oras1.0.0
FROM $BUILDER as builder

View File

@@ -1,8 +1,8 @@
PLUGIN_NAME ?= hello-world
BUILDER_REGISTRY ?= higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/
REGISTRY ?= higress-registry.cn-hangzhou.cr.aliyuncs.com/plugins/
GO_VERSION ?= 1.19
TINYGO_VERSION ?= 0.28.1
GO_VERSION ?= 1.20.14
TINYGO_VERSION ?= 0.29.0
ORAS_VERSION ?= 1.0.0
HIGRESS_VERSION ?= 1.0.0-rc
USE_HIGRESS_TINYGO ?= false

Some files were not shown because too many files have changed in this diff Show More