diff --git a/Makefile.core.mk b/Makefile.core.mk index 91d50b0c7..629152503 100644 --- a/Makefile.core.mk +++ b/Makefile.core.mk @@ -139,8 +139,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 ?= 0.7.0 -ISTIO_LATEST_IMAGE_TAG ?= 0.7.0 +ENVOY_LATEST_IMAGE_TAG ?= 1.0.0-rc +ISTIO_LATEST_IMAGE_TAG ?= 1.0.0-rc install-dev: pre-install helm install higress helm/core -n higress-system --create-namespace --set 'controller.tag=$(TAG)' --set 'gateway.replicas=1' --set 'gateway.tag=$(ENVOY_LATEST_IMAGE_TAG)' --set 'global.local=true' diff --git a/VERSION b/VERSION index 63f2359f6..ff0ad4c24 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v0.7.1 +v1.0.0-rc diff --git a/envoy/1.20/patches/envoy/20230408-basic-auth.patch b/envoy/1.20/patches/envoy/20230408-basic-auth.patch new file mode 100644 index 000000000..75a68322d --- /dev/null +++ b/envoy/1.20/patches/envoy/20230408-basic-auth.patch @@ -0,0 +1,55 @@ +diff -Naur envoy/bazel/envoy_binary.bzl envoy-new/bazel/envoy_binary.bzl +--- envoy/bazel/envoy_binary.bzl 2023-04-08 20:52:57.041729111 +0800 ++++ envoy-new/bazel/envoy_binary.bzl 2023-04-08 20:50:53.657603065 +0800 +@@ -80,7 +80,7 @@ + "@envoy//bazel:boringssl_fips": [], + "@envoy//bazel:windows_x86_64": [], + "//conditions:default": ["-pie"], +- }) + _envoy_select_exported_symbols(["-Wl,-E"]) ++ }) + _envoy_select_exported_symbols(["-Wl,-E"]) + envoy_select_alimesh(["-lcrypt"]) + + def _envoy_stamped_deps(): + return select({ +diff -Naur envoy/bazel/repositories.bzl envoy-new/bazel/repositories.bzl +--- envoy/bazel/repositories.bzl 2023-04-08 20:52:57.085730582 +0800 ++++ envoy-new/bazel/repositories.bzl 2023-04-08 20:27:20.110335884 +0800 +@@ -272,6 +272,8 @@ + actual = "@bazel_tools//tools/cpp/runfiles", + ) + ++ _com_github_higress_wasm_extensions() ++ + def _boringssl(): + external_http_archive( + name = "boringssl", +@@ -1066,6 +1068,17 @@ + actual = "@com_github_wasm_c_api//:wasmtime_lib", + ) + ++def _com_github_higress_wasm_extensions(): ++ native.local_repository( ++ name = "com_github_higress_wasm_extensions", ++ path = "../../wasm-cpp", ++ ) ++ ++ native.bind( ++ name = "basic_auth_lib", ++ actual = "@com_github_higress_wasm_extensions//extensions/basic_auth:basic_auth_lib", ++ ) ++ + def _rules_fuzzing(): + external_http_archive( + name = "rules_fuzzing", +diff -Naur envoy/source/exe/BUILD envoy-new/source/exe/BUILD +--- envoy/source/exe/BUILD 2023-04-08 20:52:57.053729512 +0800 ++++ envoy-new/source/exe/BUILD 2023-04-08 19:48:37.420667254 +0800 +@@ -43,6 +43,9 @@ + "//bazel:darwin": envoy_all_extensions(DARWIN_SKIP_TARGETS), + "//conditions:default": envoy_all_extensions(), + }), ++ alimesh_deps = [ ++ "//external:basic_auth_lib", ++ ], + ) + + envoy_cc_library( diff --git a/helm/core/Chart.yaml b/helm/core/Chart.yaml index d12920797..17ab3356e 100644 --- a/helm/core/Chart.yaml +++ b/helm/core/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -appVersion: 0.7.1 +appVersion: 1.0.0-rc description: Helm chart for deploying higress gateways icon: https://higress.io/img/higress_logo_small.png keywords: @@ -9,4 +9,4 @@ name: higress-core sources: - http://github.com/alibaba/higress type: application -version: 0.7.1 +version: 1.0.0-rc diff --git a/helm/core/templates/configmap.yaml b/helm/core/templates/configmap.yaml index 8afa0361c..1eb9775e7 100644 --- a/helm/core/templates/configmap.yaml +++ b/helm/core/templates/configmap.yaml @@ -29,6 +29,9 @@ {{- end }} defaultConfig: + {{- if .Values.global.disableAlpnH2 }} + disableAlpnH2: true + {{- end }} {{- if .Values.global.meshID }} meshId: {{ .Values.global.meshID }} {{- end }} diff --git a/helm/core/values.yaml b/helm/core/values.yaml index 24f344afe..42e0cb722 100644 --- a/helm/core/values.yaml +++ b/helm/core/values.yaml @@ -9,6 +9,7 @@ global: # resources in the k8s cluster. ingressClass: "higress" watchNamespace: "" + disableAlpnH2: true enableStatus: true # whether to use autoscaling/v2 template for HPA settings # for internal usage only, not to be configured by users. @@ -44,7 +45,7 @@ global: # Dev builds from prow are on gcr.io hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress # Default tag for Istio images. - tag: 0.7.0 + tag: 1.0.0-rc # Specify image pull policy if default behavior isn't desired. # Default behavior: latest images will be Always else IfNotPresent. @@ -368,7 +369,7 @@ gateway: name: "higress-gateway" replicas: 2 image: gateway - tag: "0.7.0" + tag: "1.0.0-rc" # revision declares which revision this gateway is a part of revision: "" @@ -456,7 +457,7 @@ controller: name: "higress-controller" replicas: 1 image: higress - tag: "0.7.1" + tag: "1.0.0-rc" env: {} labels: {} @@ -546,7 +547,7 @@ pilot: rollingMaxUnavailable: 25% hub: higress-registry.cn-hangzhou.cr.aliyuncs.com/higress - tag: 0.7.0 + tag: 1.0.0-rc # Can be a full hub/image:tag image: pilot diff --git a/helm/higress/Chart.lock b/helm/higress/Chart.lock index 7f66af73d..7270dfa18 100644 --- a/helm/higress/Chart.lock +++ b/helm/higress/Chart.lock @@ -1,9 +1,9 @@ dependencies: - name: higress-core repository: file://../core - version: 0.7.1 + version: 1.0.0-rc - name: higress-console repository: https://higress.io/helm-charts/ - version: 0.1.1 -digest: sha256:051fbd7b2916d1d0c26839d0e27653f6e42d20e9294bd9eed9628f24c5a7b228 -generated: "2023-04-03T13:42:23.705379+08:00" + version: 0.2.0 +digest: sha256:0a34765ab2125ccf397e81566b4d81a8dc0742a2477d225aad77d9450e4add94 +generated: "2023-04-08T23:17:37.193119+08:00" diff --git a/helm/higress/Chart.yaml b/helm/higress/Chart.yaml index 798d204c6..05bff7ae3 100644 --- a/helm/higress/Chart.yaml +++ b/helm/higress/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -appVersion: 0.7.1 +appVersion: 1.0.0-rc description: Helm chart for deploying higress gateways icon: https://higress.io/img/higress_logo_small.png keywords: @@ -11,9 +11,9 @@ sources: dependencies: - name: higress-core repository: "file://../core" - version: 0.7.1 + version: 1.0.0-rc - name: higress-console repository: "https://higress.io/helm-charts/" - version: 0.1.1 + version: 0.2.0 type: application -version: 0.7.1 +version: 1.0.0-rc diff --git a/istio/1.12/patches/proxy/20230408-build-extensions.patch b/istio/1.12/patches/proxy/20230408-build-extensions.patch new file mode 100644 index 000000000..10f199eec --- /dev/null +++ b/istio/1.12/patches/proxy/20230408-build-extensions.patch @@ -0,0 +1,19 @@ +diff -Naur proxy/common/scripts/run.sh proxy-new/common/scripts/run.sh +--- proxy/common/scripts/run.sh 2023-04-08 21:12:05.896147208 +0800 ++++ proxy-new/common/scripts/run.sh 2023-04-08 20:33:51.935437889 +0800 +@@ -40,6 +40,7 @@ + MOUNT_ENVOY_SOURCE="${MOUNT_ENVOY_SOURCE:-`cd $MOUNT_SOURCE/../envoy;pwd`}" + MOUNT_PACKAGE_SOURCE="${MOUNT_PACKAGE_SOURCE:-`cd $MOUNT_SOURCE/../package;pwd`}" + MOUNT_ROOT_SOURCE="${MOUNT_ROOT_SOURCE:-`cd $MOUNT_SOURCE/..;pwd`}" ++MOUNT_PLUGINS_SOURCE="${MOUNT_PLUGINS_SOURCE:-`cd $MOUNT_SOURCE/../../plugins/wasm-cpp;pwd`}" + + read -ra DOCKER_RUN_OPTIONS <<< "${DOCKER_RUN_OPTIONS:-}" + +@@ -64,6 +65,7 @@ + --mount "type=bind,source=${MOUNT_SOURCE},destination=/work" \ + --mount "type=bind,source=${MOUNT_ROOT_SOURCE}/..,destination=/parent" \ + --mount "type=bind,source=${MOUNT_ENVOY_SOURCE},destination=/envoy" \ ++ --mount "type=bind,source=${MOUNT_PLUGINS_SOURCE},destination=/wasm-cpp" \ + --mount "type=volume,source=go,destination=/go" \ + --mount "type=volume,source=gocache,destination=/gocache" \ + --mount "type=volume,source=cache,destination=/home/.cache" \ diff --git a/pkg/ingress/kube/annotations/auth.go b/pkg/ingress/kube/annotations/auth.go index ccaea574a..f9d0308fd 100644 --- a/pkg/ingress/kube/annotations/auth.go +++ b/pkg/ingress/kube/annotations/auth.go @@ -135,6 +135,9 @@ func convertCredentials(secretType authSecretType, secret *corev1.Secret) ([]str } userList := strings.Split(string(users), "\n") for _, item := range userList { + if !strings.Contains(item, ":") { + continue + } result = append(result, item) } case authMapAuthSecretType: diff --git a/plugins/wasm-cpp/.bazelrc b/plugins/wasm-cpp/.bazelrc new file mode 100644 index 000000000..1a351ed7b --- /dev/null +++ b/plugins/wasm-cpp/.bazelrc @@ -0,0 +1,7 @@ +build --config=clang +build:gcc --cxxopt=-std=c++17 + +build:clang --action_env=CC=clang --action_env=CXX=clang++ +build:clang --action_env=BAZEL_COMPILER=clang +build:clang --linkopt=-fuse-ld=lld +build:clang --cxxopt=-std=c++17 \ No newline at end of file diff --git a/plugins/wasm-cpp/common/BUILD b/plugins/wasm-cpp/common/BUILD index 8394d923f..cccc72b1b 100644 --- a/plugins/wasm-cpp/common/BUILD +++ b/plugins/wasm-cpp/common/BUILD @@ -1,3 +1,17 @@ +# 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. + cc_library( name = "common_util", hdrs = [ @@ -38,7 +52,7 @@ cc_library( "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "@com_google_absl//absl/strings:str_format", - "@proxy_wasm_cpp_host//:lib", + "@proxy_wasm_cpp_host//:null_lib", ], ) diff --git a/plugins/wasm-cpp/common/http_util.cc b/plugins/wasm-cpp/common/http_util.cc index be12a3b1b..320ed916c 100644 --- a/plugins/wasm-cpp/common/http_util.cc +++ b/plugins/wasm-cpp/common/http_util.cc @@ -20,6 +20,8 @@ #include "absl/strings/str_format.h" #include "absl/strings/str_split.h" +#include "common/common_util.h" + namespace Wasm::Common::Http { std::string_view stripPortFromHost(std::string_view request_host) { @@ -188,7 +190,7 @@ std::vector getAllOfHeader(std::string_view key) { std::vector result; auto headers = getRequestHeaderPairs()->pairs(); for (auto& header : headers) { - if (absl::EqualsIgnoreCase(header.first, key)) { + if (absl::EqualsIgnoreCase(Wasm::Common::stdToAbsl(header.first), Wasm::Common::stdToAbsl(key))) { result.push_back(std::string(header.second)); } } @@ -197,7 +199,7 @@ std::vector getAllOfHeader(std::string_view key) { void forEachCookie( std::string_view cookie_header, - const std::function& + const std::function& cookie_consumer) { auto cookie_headers = getAllOfHeader(cookie_header); @@ -223,7 +225,7 @@ void forEachCookie( v = v.substr(1, v.size() - 2); } - if (!cookie_consumer(k, v)) { + if (!cookie_consumer(Wasm::Common::abslToStd(k), Wasm::Common::abslToStd(v))) { return; } } @@ -263,7 +265,7 @@ std::string buildOriginalUri(std::optional max_path_length) { auto scheme = scheme_ptr->view(); auto host_ptr = getRequestHeader(Header::Host); auto host = host_ptr->view(); - return absl::StrCat(scheme, "://", host, final_path); + return absl::StrCat(Wasm::Common::stdToAbsl(scheme), "://", Wasm::Common::stdToAbsl(host), Wasm::Common::stdToAbsl(final_path)); } } // namespace Wasm::Common::Http diff --git a/plugins/wasm-cpp/common/regex.h b/plugins/wasm-cpp/common/regex.h index 0c10a0705..5071946d0 100644 --- a/plugins/wasm-cpp/common/regex.h +++ b/plugins/wasm-cpp/common/regex.h @@ -29,17 +29,20 @@ class CompiledGoogleReMatcher { bool do_program_size_check = true) : regex_(regex, re2::RE2::Quiet) { if (!regex_.ok()) { - throw std::runtime_error(regex_.error()); + error_ = regex_.error(); + return; } if (do_program_size_check) { const auto regex_program_size = static_cast(regex_.ProgramSize()); if (regex_program_size > 100) { - throw std::runtime_error("too complex regex: " + regex); + error_ = "too complex regex: " + regex; } } } + const std::string& error() const { return error_; } + bool match(std::string_view value) const { return re2::RE2::FullMatch(re2::StringPiece(value.data(), value.size()), regex_); @@ -56,6 +59,7 @@ class CompiledGoogleReMatcher { private: const re2::RE2 regex_; + std::string error_; }; } // namespace Wasm::Common::Regex diff --git a/plugins/wasm-cpp/common/route_rule_matcher.h b/plugins/wasm-cpp/common/route_rule_matcher.h index 61e5f9e13..506af4b4b 100644 --- a/plugins/wasm-cpp/common/route_rule_matcher.h +++ b/plugins/wasm-cpp/common/route_rule_matcher.h @@ -126,6 +126,12 @@ class RouteRuleMatcher { LOG_DEBUG("no match config"); return true; } + if (!config.second && global_auth_ && !global_auth_.value()) { + // No allow set, means no need to check auth if global_auth is false + LOG_DEBUG( + "no allow set found, and global auth is false, no need to auth"); + return true; + } return checkPlugin(config.first.value(), config.second); } @@ -220,8 +226,9 @@ class RouteRuleMatcher { break; } } - return is_matched ? std::make_pair(match_config, allow_set) - : std::make_pair(std::nullopt, std::nullopt); + return is_matched || (global_auth_ && global_auth_.value()) + ? std::make_pair(match_config, allow_set) + : std::make_pair(std::nullopt, std::nullopt); } void setEmptyGlobalConfig() { global_config_ = PluginConfig{}; } @@ -288,6 +295,18 @@ class RouteRuleMatcher { has_rules = true; key_count--; } + auto auth_it = config.find("global_auth"); + if (auth_it != config.end()) { + auto global_auth_value = JsonValueAs(auth_it.value()); + if (global_auth_value.second != + Wasm::Common::JsonParserResultDetail::OK || + !global_auth_value.first) { + LOG_WARN( + "failed to parse 'global_auth' field in filter configuration."); + return false; + } + global_auth_ = global_auth_value.first.value(); + } PluginConfig plugin_config; // has other config fields if (key_count > 0 && parsePluginConfig(config, plugin_config)) { @@ -455,6 +474,7 @@ class RouteRuleMatcher { } bool invalid_config_ = false; + std::optional global_auth_ = std::nullopt; std::vector rule_config_; std::vector auth_rule_config_; std::optional global_config_ = std::nullopt; diff --git a/plugins/wasm-cpp/extensions/basic_auth/BUILD b/plugins/wasm-cpp/extensions/basic_auth/BUILD index 79b6ff000..d36baab02 100644 --- a/plugins/wasm-cpp/extensions/basic_auth/BUILD +++ b/plugins/wasm-cpp/extensions/basic_auth/BUILD @@ -1,3 +1,17 @@ +# 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") @@ -31,7 +45,7 @@ cc_library( visibility = ["//visibility:public"], alwayslink = 1, deps = [ - "//common:rule_util", + "//common:rule_util_nullvm", "//common:json_util", "//common:crypto_util", "@com_google_absl//absl/strings", diff --git a/plugins/wasm-cpp/extensions/basic_auth/plugin.cc b/plugins/wasm-cpp/extensions/basic_auth/plugin.cc index 86baf75f7..4db843f27 100644 --- a/plugins/wasm-cpp/extensions/basic_auth/plugin.cc +++ b/plugins/wasm-cpp/extensions/basic_auth/plugin.cc @@ -308,8 +308,7 @@ bool PluginRootContext::onConfigure(size_t size) { // Parse configuration JSON string. if (size > 0 && !configure(size)) { LOG_WARN("configuration has errors initialization will not continue."); - setInvalidConfig(); - return true; + return false; } return true; } diff --git a/plugins/wasm-cpp/extensions/basic_auth/plugin_test.cc b/plugins/wasm-cpp/extensions/basic_auth/plugin_test.cc index b6ee7d415..def1fdb2b 100644 --- a/plugins/wasm-cpp/extensions/basic_auth/plugin_test.cc +++ b/plugins/wasm-cpp/extensions/basic_auth/plugin_test.cc @@ -909,7 +909,8 @@ TEST_F(BasicAuthTest, GlobalDeny) { } TEST_F(BasicAuthTest, GlobalWithConsumerDeny) { - std::string configuration = R"( + { + std::string configuration = R"( { "consumers" : [ {"credential" : "ok:test", "name" : "consumer_ok"}, @@ -932,36 +933,171 @@ TEST_F(BasicAuthTest, GlobalWithConsumerDeny) { } ] })"; - BufferBase buffer; - buffer.set({configuration.data(), configuration.size()}); + BufferBase buffer; + buffer.set({configuration.data(), configuration.size()}); - EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) - .WillOnce([&buffer](WasmBufferType) { return &buffer; }); - EXPECT_TRUE(root_context_->configure(configuration.size())); + EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) + .WillOnce([&buffer](WasmBufferType) { return &buffer; }); + EXPECT_TRUE(root_context_->configure(configuration.size())); - cred_ = "wrong-cred"; - route_name_ = "config"; - authorization_header_ = "Basic " + Base64::encode(cred_.data(), cred_.size()); - EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, - testing::_, testing::_)); - EXPECT_EQ(context_->onRequestHeaders(0, false), - FilterHeadersStatus::StopIteration); + cred_ = "wrong-cred"; + route_name_ = "not match"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::Continue); - authority_ = "www.example.com"; - cred_ = "admin2:admin2"; - authorization_header_ = "Basic " + Base64::encode(cred_.data(), cred_.size()); - EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, - testing::_, testing::_)); - EXPECT_EQ(context_->onRequestHeaders(0, false), - FilterHeadersStatus::StopIteration); + cred_ = "wrong-cred"; + route_name_ = "config"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, + testing::_, testing::_)); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); - route_name_ = "config"; - cred_ = "admin4:admin4"; - authorization_header_ = "Basic " + Base64::encode(cred_.data(), cred_.size()); - EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, - testing::_, testing::_)); - EXPECT_EQ(context_->onRequestHeaders(0, false), - FilterHeadersStatus::StopIteration); + authority_ = "www.example.com"; + cred_ = "admin2:admin2"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, + testing::_, testing::_)); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); + + route_name_ = "config"; + cred_ = "admin4:admin4"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, + testing::_, testing::_)); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); + } + { + std::string configuration = R"( +{ + "global_auth": true, + "consumers" : [ + {"credential" : "ok:test", "name" : "consumer_ok"}, + {"credential" : "admin2:admin2", "name" : "consumer2"}, + {"credential" : "admin:admin", "name" : "consumer"} + ], + "_rules_" : [ + { + "_match_route_" : ["test", "config"], + "consumers" : [ + {"credential" : "admin3:admin3", "name" : "consumer3"}, + {"credential" : "YWRtaW41OmFkbWluNQ==", "name" : "consumer5"} + ] + }, + { + "_match_domain_" : ["test.com", "*.example.com"], + "consumers" : [ + {"credential" : "admin4:admin4", "name" : "consumer4"} + ] + } + ] +})"; + BufferBase buffer; + buffer.set({configuration.data(), configuration.size()}); + + EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) + .WillOnce([&buffer](WasmBufferType) { return &buffer; }); + EXPECT_TRUE(root_context_->configure(configuration.size())); + + cred_ = "wrong-cred"; + route_name_ = "not match"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, + testing::_, testing::_)); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); + + cred_ = "wrong-cred"; + route_name_ = "config"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, + testing::_, testing::_)); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); + + authority_ = "www.example.com"; + cred_ = "admin2:admin2"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, + testing::_, testing::_)); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); + + route_name_ = "config"; + cred_ = "admin4:admin4"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, + testing::_, testing::_)); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); + } +} + +TEST_F(BasicAuthTest, OnConfigureNoRulesAuth) { + // enable global auth + { + std::string configuration = R"( +{ + "consumers" : [ + {"credential" : "getuser1:123456", "name" : "consumer1"} + ] +})"; + BufferBase buffer; + buffer.set({configuration.data(), configuration.size()}); + + EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) + .WillOnce([&buffer](WasmBufferType) { return &buffer; }); + EXPECT_TRUE(root_context_->configure(configuration.size())); + cred_ = "admin:admin"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_CALL(*mock_context_, sendLocalResponse(401, testing::_, testing::_, + testing::_, testing::_)); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); + cred_ = "getuser1:123456"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::Continue); + } + // disable global auth + { + std::string configuration = R"( +{ + "consumers" : [ + {"credential" : "getuser1:123456", "name" : "consumer1"} + ], + "global_auth": false +})"; + BufferBase buffer; + buffer.set({configuration.data(), configuration.size()}); + + EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) + .WillOnce([&buffer](WasmBufferType) { return &buffer; }); + EXPECT_TRUE(root_context_->configure(configuration.size())); + cred_ = "admin:admin"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::Continue); + cred_ = "getuser1:123456"; + authorization_header_ = + "Basic " + Base64::encode(cred_.data(), cred_.size()); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::Continue); + } } } // namespace basic_auth diff --git a/plugins/wasm-cpp/extensions/bot_detect/BUILD b/plugins/wasm-cpp/extensions/bot_detect/BUILD index baee11c97..e5ba81c26 100644 --- a/plugins/wasm-cpp/extensions/bot_detect/BUILD +++ b/plugins/wasm-cpp/extensions/bot_detect/BUILD @@ -1,3 +1,17 @@ +# 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") @@ -11,10 +25,10 @@ wasm_cc_binary( "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "//common:json_util", - "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", "//common:http_util", "//common:regex_util", "//common:rule_util", + "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", ], ) @@ -31,9 +45,9 @@ cc_library( "@com_google_absl//absl/strings", "//common:json_util", "@proxy_wasm_cpp_host//:lib", - "//common:http_util", + "//common:http_util_nullvm", "//common:regex_util", - "//common:rule_util", + "//common:rule_util_nullvm", ], ) diff --git a/plugins/wasm-cpp/extensions/bot_detect/plugin.cc b/plugins/wasm-cpp/extensions/bot_detect/plugin.cc index 3b011f4bb..0d4efdd42 100644 --- a/plugins/wasm-cpp/extensions/bot_detect/plugin.cc +++ b/plugins/wasm-cpp/extensions/bot_detect/plugin.cc @@ -78,13 +78,13 @@ bool PluginRootContext::parsePluginConfig(const json& configuration, LOG_WARN("cannot parse allow"); return false; } - try { - rule.allow.push_back( - std::make_unique(regex.first.value())); - } catch (const std::runtime_error& e) { - LOG_WARN(e.what()); + auto re = std::make_unique(regex.first.value()); + if (!re->error().empty()) { + LOG_WARN(re->error()); return false; } + rule.allow.push_back(std::move(re)); + return true; })) { LOG_WARN("failed to parse configuration for allow."); @@ -96,12 +96,13 @@ bool PluginRootContext::parsePluginConfig(const json& configuration, LOG_WARN("cannot parse deny"); return false; } - try { - rule.deny.push_back(std::make_unique(regex.first.value())); - } catch (const std::runtime_error& e) { - LOG_WARN(e.what()); + auto re = std::make_unique(regex.first.value()); + if (!re->error().empty()) { + LOG_WARN(re->error()); return false; } + rule.deny.push_back(std::move(re)); + return true; })) { LOG_WARN("failed to parse configuration for deny."); @@ -114,8 +115,7 @@ bool PluginRootContext::onConfigure(size_t size) { // Parse configuration JSON string. if (size > 0 && !configure(size)) { LOG_WARN("configuration has errors initialization will not continue."); - setInvalidConfig(); - return true; + return false; } if (size == 0) { // support empty config diff --git a/plugins/wasm-cpp/extensions/custom_response/BUILD b/plugins/wasm-cpp/extensions/custom_response/BUILD index 162eaf125..d6202d7b0 100644 --- a/plugins/wasm-cpp/extensions/custom_response/BUILD +++ b/plugins/wasm-cpp/extensions/custom_response/BUILD @@ -1,3 +1,17 @@ +# 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") @@ -11,9 +25,9 @@ wasm_cc_binary( "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "//common:json_util", - "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", "//common:http_util", "//common:rule_util", + "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", ], ) diff --git a/plugins/wasm-cpp/extensions/custom_response/plugin.cc b/plugins/wasm-cpp/extensions/custom_response/plugin.cc index dbe0259a2..fc62034e3 100644 --- a/plugins/wasm-cpp/extensions/custom_response/plugin.cc +++ b/plugins/wasm-cpp/extensions/custom_response/plugin.cc @@ -149,8 +149,7 @@ bool PluginRootContext::onConfigure(size_t size) { // Parse configuration JSON string. if (size > 0 && !configure(size)) { LOG_WARN("configuration has errors initialization will not continue."); - setInvalidConfig(); - return true; + return false; } return true; } diff --git a/plugins/wasm-cpp/extensions/hmac_auth/BUILD b/plugins/wasm-cpp/extensions/hmac_auth/BUILD index a37fe21ec..9fd9ad93b 100644 --- a/plugins/wasm-cpp/extensions/hmac_auth/BUILD +++ b/plugins/wasm-cpp/extensions/hmac_auth/BUILD @@ -1,3 +1,17 @@ +# 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") @@ -13,10 +27,10 @@ wasm_cc_binary( "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", "//common:json_util", - "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", "//common:crypto_util", "//common:http_util", "//common:rule_util", + "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", ], ) @@ -37,8 +51,8 @@ cc_library( "//common:json_util", "@proxy_wasm_cpp_host//:lib", "//common:crypto_util", - "//common:http_util", - "//common:rule_util", + "//common:http_util_nullvm", + "//common:rule_util_nullvm", ], ) diff --git a/plugins/wasm-cpp/extensions/hmac_auth/plugin.cc b/plugins/wasm-cpp/extensions/hmac_auth/plugin.cc index 850575425..b72290114 100644 --- a/plugins/wasm-cpp/extensions/hmac_auth/plugin.cc +++ b/plugins/wasm-cpp/extensions/hmac_auth/plugin.cc @@ -389,8 +389,7 @@ bool PluginRootContext::onConfigure(size_t size) { // Parse configuration JSON string. if (size > 0 && !configure(size)) { LOG_WARN("configuration has errors initialization will not continue."); - setInvalidConfig(); - return true; + return false; } return true; } diff --git a/plugins/wasm-cpp/extensions/jwt_auth/BUILD b/plugins/wasm-cpp/extensions/jwt_auth/BUILD index 729bc5289..c960158a1 100644 --- a/plugins/wasm-cpp/extensions/jwt_auth/BUILD +++ b/plugins/wasm-cpp/extensions/jwt_auth/BUILD @@ -1,3 +1,17 @@ +# 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") @@ -17,9 +31,9 @@ wasm_cc_binary( "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/time", "//common:json_util", - "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", "//common:http_util", "//common:rule_util", + "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", ], ) diff --git a/plugins/wasm-cpp/extensions/jwt_auth/plugin.cc b/plugins/wasm-cpp/extensions/jwt_auth/plugin.cc index 93fd8e3c5..d79d272c7 100644 --- a/plugins/wasm-cpp/extensions/jwt_auth/plugin.cc +++ b/plugins/wasm-cpp/extensions/jwt_auth/plugin.cc @@ -135,11 +135,16 @@ bool PluginRootContext::parsePluginConfig(const json& configuration, [&](const json& from_header) -> bool { JSON_FIND_FIELD(from_header, name); JSON_FIELD_VALUE_AS(std::string, from_header, name); - JSON_FIND_FIELD(from_header, value_prefix); - JSON_FIELD_VALUE_AS(std::string, from_header, - value_prefix); - from_headers.push_back(FromHeader{ - from_header_name, from_header_value_prefix}); + std::string header_value_prefix; + auto from_header_value_prefix_json = + from_header.find("value_prefix"); + if (from_header_value_prefix_json != from_header.end()) { + JSON_FIELD_VALUE_AS(std::string, from_header, + value_prefix); + header_value_prefix = from_header_value_prefix; + } + from_headers.push_back( + FromHeader{from_header_name, header_value_prefix}); return true; })) { LOG_WARN("failed to parse 'from_headers' in consumer: " + @@ -229,6 +234,18 @@ bool PluginRootContext::parsePluginConfig(const json& configuration, LOG_INFO("at least one consumer has to be configured for a rule."); return false; } + std::vector enable_headers; + if (!JsonArrayIterate(configuration, "enable_headers", + [&](const json& enable_header_json) -> bool { + JSON_VALUE_AS(std::string, enable_header_json, + enable_header, "invalid item"); + enable_headers.push_back(enable_header); + return true; + })) { + LOG_WARN("failed to parse 'enable_headers'"); + return false; + } + rule.enable_headers = std::move(enable_headers); return true; } @@ -307,6 +324,20 @@ Status PluginRootContext::consumerVerify( bool PluginRootContext::checkPlugin( const JwtAuthConfigRule& rule, const std::optional>& allow_set) { + if (!rule.enable_headers.empty()) { + bool skip_auth = true; + for (const auto& enable_header : rule.enable_headers) { + auto header_ptr = getRequestHeader(enable_header); + if (header_ptr->size() > 0) { + LOG_DEBUG("enable by header: " + header_ptr->toString()); + skip_auth = false; + break; + } + } + if (skip_auth) { + return true; + } + } std::optional err_status; bool verified = false; uint64_t now = getCurrentTimeNanoseconds() / 1e9; @@ -354,8 +385,7 @@ bool PluginRootContext::onConfigure(size_t size) { // Parse configuration JSON string. if (size > 0 && !configure(size)) { LOG_WARN("configuration has errors initialization will not continue."); - setInvalidConfig(); - return true; + return false; } return true; } diff --git a/plugins/wasm-cpp/extensions/jwt_auth/plugin.h b/plugins/wasm-cpp/extensions/jwt_auth/plugin.h index aff839cbe..f81475d30 100644 --- a/plugins/wasm-cpp/extensions/jwt_auth/plugin.h +++ b/plugins/wasm-cpp/extensions/jwt_auth/plugin.h @@ -73,6 +73,7 @@ struct Consumer { struct JwtAuthConfigRule { std::vector consumers; + std::vector enable_headers; }; // PluginRootContext is the root context for all streams processed by the diff --git a/plugins/wasm-cpp/extensions/jwt_auth/plugin_test.cc b/plugins/wasm-cpp/extensions/jwt_auth/plugin_test.cc index 5b64a920a..4fdfd9e3a 100644 --- a/plugins/wasm-cpp/extensions/jwt_auth/plugin_test.cc +++ b/plugins/wasm-cpp/extensions/jwt_auth/plugin_test.cc @@ -84,6 +84,9 @@ class JwtAuthTest : public ::testing::Test { if (header == "Authorization") { *result = jwt_header_; } + if (header == "x-custom-header") { + *result = custom_header_; + } return WasmResult::Ok; }); ON_CALL(*mock_context_, addHeaderMapValue(WasmHeaderMapType::RequestHeaders, @@ -118,6 +121,7 @@ class JwtAuthTest : public ::testing::Test { std::string authority_; std::string route_name_; std::string jwt_header_; + std::string custom_header_; uint64_t current_time_; }; @@ -146,7 +150,8 @@ TEST_F(JwtAuthTest, RSA) { } TEST_F(JwtAuthTest, OCT) { - std::string configuration = R"( + { + std::string configuration = R"( { "consumers": [ { @@ -156,17 +161,65 @@ TEST_F(JwtAuthTest, OCT) { } ] })"; - BufferBase buffer; - buffer.set({configuration.data(), configuration.size()}); + BufferBase buffer; + buffer.set({configuration.data(), configuration.size()}); - EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) - .WillOnce([&buffer](WasmBufferType) { return &buffer; }); - EXPECT_TRUE(root_context_->configure(configuration.size())); - current_time_ = 1665673819 * 1e9; - jwt_header_ = - R"(Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKmc)"; - EXPECT_EQ(context_->onRequestHeaders(0, false), - FilterHeadersStatus::Continue); + EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) + .WillOnce([&buffer](WasmBufferType) { return &buffer; }); + EXPECT_TRUE(root_context_->configure(configuration.size())); + current_time_ = 1665673819 * 1e9; + jwt_header_ = + R"(Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKmc)"; + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::Continue); + } + { + std::string configuration = R"( +{ + "consumers": [ + { + "name": "consumer-2", + "issuer": "abcd", + "jwks": "{\"keys\":[{\"kty\":\"oct\",\"kid\":\"123\",\"k\":\"hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew\",\"alg\":\"HS256\"}]}" + } + ] +})"; + BufferBase buffer; + buffer.set({configuration.data(), configuration.size()}); + + EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) + .WillOnce([&buffer](WasmBufferType) { return &buffer; }); + EXPECT_TRUE(root_context_->configure(configuration.size())); + current_time_ = 1665673819 * 1e9; + jwt_header_ = + R"(Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKm1)"; + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); + } + { + std::string configuration = R"( +{ + "consumers": [ + { + "name": "consumer-2", + "issuer": "abcd", + "jwks": "{\"keys\":[{\"kty\":\"oct\",\"kid\":\"123\",\"k\":\"hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew\",\"alg\":\"HS256\"}]}" + } + ], + "global_auth": false +})"; + BufferBase buffer; + buffer.set({configuration.data(), configuration.size()}); + + EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) + .WillOnce([&buffer](WasmBufferType) { return &buffer; }); + EXPECT_TRUE(root_context_->configure(configuration.size())); + current_time_ = 1665673819 * 1e9; + jwt_header_ = + R"(Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKm1)"; + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::Continue); + } } TEST_F(JwtAuthTest, AuthZ) { @@ -264,6 +317,97 @@ TEST_F(JwtAuthTest, ClaimToHeader) { FilterHeadersStatus::Continue); } +TEST_F(JwtAuthTest, CustomHeader) { + std::string configuration = R"( +{ + "consumers": [ + { + "name": "consumer-2", + "issuer": "abcd", + "from_headers": [ + { + "name": "x-custom-header", + "value_prefix": "token " + }, + { + "name": "Authorization" + } + ], + "jwks": "{\"keys\":[{\"kty\":\"oct\",\"kid\":\"123\",\"k\":\"hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew\",\"alg\":\"HS256\"}]}" + } + ] +})"; + BufferBase buffer; + buffer.set({configuration.data(), configuration.size()}); + + EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) + .WillOnce([&buffer](WasmBufferType) { return &buffer; }); + EXPECT_TRUE(root_context_->configure(configuration.size())); + custom_header_ = + R"(token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKmc)"; + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::Continue); + custom_header_.clear(); + jwt_header_ = + R"(eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKmc)"; + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::Continue); +} + +TEST_F(JwtAuthTest, SkipAuthHeader) { + std::string configuration = R"( +{ + "consumers": [ + { + "name": "consumer-1", + "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\"}]}" + }, + { + "name": "consumer-2", + "issuer": "abcd", + "jwks": "{\"keys\":[{\"kty\":\"oct\",\"kid\":\"123\",\"k\":\"hM0k3AbXBPpKOGg__Ql2Obcq7s60myWDpbHXzgKUQdYo7YCRp0gUqkCnbGSvZ2rGEl4YFkKqIqW7mTHdj-bcqXpNr-NOznEyMpVPOIlqG_NWVC3dydBgcsIZIdD-MR2AQceEaxriPA_VmiUCwfwL2Bhs6_i7eolXoY11EapLQtutz0BV6ZxQQ4dYUmct--7PLNb4BWJyQeWu0QfbIthnvhYllyl2dgeLTEJT58wzFz5HeNMNz8ohY5K0XaKAe5cepryqoXLhA-V-O1OjSG8lCNdKS09OY6O0fkyweKEtuDfien5tHHSsHXoAxYEHPFcSRL4bFPLZ0orTt1_4zpyfew\",\"alg\":\"HS256\"}]}" + } + ], + "enable_headers": ["x-custom-header"], + "_rules_": [{ + "_match_route_": [ + "test1" + ], + "allow": [ + "consumer-1" + ] + }, + { + "_match_route_": [ + "test2" + ], + "allow": [ + "consumer-2" + ] + } + ] +})"; + BufferBase buffer; + buffer.set({configuration.data(), configuration.size()}); + + EXPECT_CALL(*mock_context_, getBuffer(WasmBufferType::PluginConfiguration)) + .WillOnce([&buffer](WasmBufferType) { return &buffer; }); + EXPECT_TRUE(root_context_->configure(configuration.size())); + current_time_ = 1665673819 * 1e9; + jwt_header_ = + R"(Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjEyMyJ9.eyJpc3MiOiJhYmNkIiwic3ViIjoidGVzdCIsImlhdCI6MTY2NTY2MDUyNywiZXhwIjoxNjY1NjczODE5fQ.7BVJOAobz_xYjsenu_CsYhYbgF1gMcqZSpaeQ8HwKmc)"; + route_name_ = "test1"; + custom_header_ = "123"; + EXPECT_CALL(*mock_context_, sendLocalResponse(403, testing::_, testing::_, + testing::_, testing::_)); + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::StopIteration); + custom_header_ = ""; + EXPECT_EQ(context_->onRequestHeaders(0, false), + FilterHeadersStatus::Continue); +} + } // namespace jwt_auth } // namespace null_plugin } // namespace proxy_wasm diff --git a/plugins/wasm-cpp/extensions/key_auth/BUILD b/plugins/wasm-cpp/extensions/key_auth/BUILD index bac6841e6..8668bc5fe 100644 --- a/plugins/wasm-cpp/extensions/key_auth/BUILD +++ b/plugins/wasm-cpp/extensions/key_auth/BUILD @@ -1,3 +1,17 @@ +# 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") @@ -12,9 +26,9 @@ wasm_cc_binary( "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "//common:json_util", - "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", "//common:http_util", "//common:rule_util", + "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", ], ) @@ -33,8 +47,8 @@ cc_library( "@com_google_absl//absl/time", "//common:json_util", "@proxy_wasm_cpp_host//:lib", - "//common:http_util", - "//common:rule_util", + "//common:http_util_nullvm", + "//common:rule_util_nullvm", ], ) diff --git a/plugins/wasm-cpp/extensions/key_auth/plugin.cc b/plugins/wasm-cpp/extensions/key_auth/plugin.cc index 0c48f121a..a88b6bda7 100644 --- a/plugins/wasm-cpp/extensions/key_auth/plugin.cc +++ b/plugins/wasm-cpp/extensions/key_auth/plugin.cc @@ -211,8 +211,7 @@ bool PluginRootContext::onConfigure(size_t size) { // Parse configuration JSON string. if (size > 0 && !configure(size)) { LOG_WARN("configuration has errors initialization will not continue."); - setInvalidConfig(); - return true; + return false; } return true; } diff --git a/plugins/wasm-cpp/extensions/key_rate_limit/BUILD b/plugins/wasm-cpp/extensions/key_rate_limit/BUILD index 9b2d56a9c..7f4cd9249 100644 --- a/plugins/wasm-cpp/extensions/key_rate_limit/BUILD +++ b/plugins/wasm-cpp/extensions/key_rate_limit/BUILD @@ -1,3 +1,17 @@ +# 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") @@ -13,9 +27,9 @@ wasm_cc_binary( "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "//common:json_util", - "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", "//common:http_util", "//common:rule_util", + "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", ], ) @@ -34,8 +48,8 @@ cc_library( "@com_google_absl//absl/strings", "//common:json_util", "@proxy_wasm_cpp_host//:lib", - "//common:http_util", - "//common:rule_util", + "//common:http_util_nullvm", + "//common:rule_util_nullvm", ], ) diff --git a/plugins/wasm-cpp/extensions/key_rate_limit/bucket.cc b/plugins/wasm-cpp/extensions/key_rate_limit/bucket.cc index 2d42a1b14..1e2d6fa0c 100644 --- a/plugins/wasm-cpp/extensions/key_rate_limit/bucket.cc +++ b/plugins/wasm-cpp/extensions/key_rate_limit/bucket.cc @@ -18,6 +18,7 @@ #include #include "absl/strings/str_cat.h" +#include "absl/strings/str_format.h" #include "absl/strings/str_join.h" namespace { @@ -40,11 +41,16 @@ bool getToken(int rule_id, const std::string &key) { for (int i = 0; i < maxGetTokenRetry; i++) { if (WasmResult::Ok != getSharedData(tokenBucketKey, &token_bucket_data, &cas)) { - return false; + continue; } uint64_t token_left = *reinterpret_cast(token_bucket_data->data()); + LOG_DEBUG(absl::StrFormat( + "ratelimit get token: id:%d, tokenBucketKey:%s, token left:%u", rule_id, + tokenBucketKey, token_left)); if (token_left == 0) { + LOG_DEBUG(absl::StrFormat("get token failed, id:%d, tokenBucketKey:%s", + rule_id, tokenBucketKey)); return false; } token_left -= 1; @@ -52,12 +58,18 @@ bool getToken(int rule_id, const std::string &key) { tokenBucketKey, {reinterpret_cast(&token_left), sizeof(token_left)}, cas); if (res == WasmResult::Ok) { + LOG_DEBUG( + absl::StrFormat("ratelimit token update success: id:%d, " + "tokenBucketKey:%s, token left:%u", + rule_id, tokenBucketKey, token_left)); return true; } if (res == WasmResult::CasMismatch) { continue; } - return false; + LOG_WARN(absl::StrFormat("got invalid result:%d, id:%d, tokenBucketKey:%s", + res, rule_id, tokenBucketKey)); + return true; } LOG_WARN("get token failed with cas mismatch"); @@ -86,12 +98,21 @@ void refillToken(const std::vector> &rules) { if (now - last_update < rule.second.refill_interval_nanosec) { continue; } + LOG_DEBUG( + absl::StrFormat("ratelimit rule need refilled, id:%s, " + "lastRefilledKey:%s, now:%u, last_update:%u", + id, lastRefilledKey, now, last_update)); // Otherwise, try set last updated time. If updated failed because of cas // mismatch, the bucket is going to be refilled by other VMs. auto res = setSharedData( lastRefilledKey, {reinterpret_cast(&now), sizeof(now)}, last_update_cas); if (res == WasmResult::CasMismatch) { + LOG_DEBUG( + absl::StrFormat("ratelimit update lastRefilledKey casmismatch, the " + "bucket is going to be refilled by other VMs, id:%s, " + "lastRefilledKey:%s", + id, lastRefilledKey)); continue; } do { @@ -115,6 +136,10 @@ void refillToken(const std::vector> &rules) { last_update_cas)) { continue; } + LOG_DEBUG( + absl::StrFormat("ratelimit token refilled: id:%s, " + "tokenBucketKey:%s, token left:%u", + id, tokenBucketKey, token_left)); break; } while (true); } @@ -138,6 +163,10 @@ bool initializeTokenBucket( setSharedData(tokenBucketKey, {reinterpret_cast(&rule.second.max_tokens), sizeof(uint64_t)}); + LOG_INFO(absl::StrFormat( + "ratelimit rule created: id:%s, lastRefilledKey:%s, " + "tokenBucketKey:%s, max_tokens:%u", + id, lastRefilledKey, tokenBucketKey, rule.second.max_tokens)); continue; } // reconfigure @@ -172,6 +201,10 @@ bool initializeTokenBucket( } break; } while (true); + LOG_INFO(absl::StrFormat( + "ratelimit rule reconfigured: id:%s, lastRefilledKey:%s, " + "tokenBucketKey:%s, max_tokens:%u", + id, lastRefilledKey, tokenBucketKey, rule.second.max_tokens)); } return true; } diff --git a/plugins/wasm-cpp/extensions/key_rate_limit/plugin.cc b/plugins/wasm-cpp/extensions/key_rate_limit/plugin.cc index ab0e076fb..5a042a8c4 100644 --- a/plugins/wasm-cpp/extensions/key_rate_limit/plugin.cc +++ b/plugins/wasm-cpp/extensions/key_rate_limit/plugin.cc @@ -169,6 +169,7 @@ bool PluginRootContext::checkPlugin(int rule_id, return true; } if (!getToken(rule_id, key)) { + LOG_INFO(absl::StrCat("request rate limited by key: ", key)); tooManyRequest(); return false; } @@ -181,8 +182,7 @@ bool PluginRootContext::onConfigure(size_t size) { // Parse configuration JSON string. if (size > 0 && !configure(size)) { LOG_WARN("configuration has errors initialization will not continue."); - setInvalidConfig(); - return true; + return false; } const auto& rules = getRules(); for (const auto& rule : rules) { @@ -191,7 +191,7 @@ bool PluginRootContext::onConfigure(size_t size) { } } initializeTokenBucket(limits_); - proxy_set_tick_period_milliseconds(1000); + proxy_set_tick_period_milliseconds(500); return true; } diff --git a/plugins/wasm-cpp/extensions/request_block/BUILD b/plugins/wasm-cpp/extensions/request_block/BUILD index c6817a4ed..518209139 100644 --- a/plugins/wasm-cpp/extensions/request_block/BUILD +++ b/plugins/wasm-cpp/extensions/request_block/BUILD @@ -1,3 +1,17 @@ +# 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") @@ -11,9 +25,9 @@ wasm_cc_binary( "@com_google_absl//absl/strings", "@com_google_absl//absl/time", "//common:json_util", - "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", "//common:http_util", "//common:rule_util", + "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", ], ) @@ -30,8 +44,8 @@ cc_library( "@com_google_absl//absl/strings", "//common:json_util", "@proxy_wasm_cpp_host//:lib", - "//common:http_util", - "//common:rule_util", + "//common:http_util_nullvm", + "//common:rule_util_nullvm", ], ) diff --git a/plugins/wasm-cpp/extensions/request_block/plugin.cc b/plugins/wasm-cpp/extensions/request_block/plugin.cc index fbc7c5a1c..90c6c92f7 100644 --- a/plugins/wasm-cpp/extensions/request_block/plugin.cc +++ b/plugins/wasm-cpp/extensions/request_block/plugin.cc @@ -108,24 +108,24 @@ bool PluginRootContext::parsePluginConfig(const json& configuration, return false; } if (!JsonArrayIterate( - configuration, "block_bodies", [&](const json& item) -> bool { + configuration, "block_bodys", [&](const json& item) -> bool { auto body = JsonValueAs(item); if (body.second != Wasm::Common::JsonParserResultDetail::OK) { - LOG_WARN("cannot parse block_bodies"); + LOG_WARN("cannot parse block_bodys"); return false; } if (rule.case_sensitive) { - rule.block_bodies.push_back(std::move(body.first.value())); + rule.block_bodys.push_back(std::move(body.first.value())); } else { - rule.block_bodies.push_back( + rule.block_bodys.push_back( absl::AsciiStrToLower(body.first.value())); } return true; })) { - LOG_WARN("failed to parse configuration for block_bodies."); + LOG_WARN("failed to parse configuration for block_bodys."); return false; } - if (rule.block_bodies.empty() && rule.block_headers.empty() && + if (rule.block_bodys.empty() && rule.block_headers.empty() && rule.block_urls.empty()) { LOG_WARN("there is no block rules"); return false; @@ -137,8 +137,7 @@ bool PluginRootContext::onConfigure(size_t size) { // Parse configuration JSON string. if (size > 0 && !configure(size)) { LOG_WARN("configuration has errors initialization will not continue."); - setInvalidConfig(); - return true; + return false; } return true; } @@ -197,7 +196,7 @@ bool PluginRootContext::checkHeader(const RequestBlockConfigRule& rule, } } } - if (!rule.block_bodies.empty()) { + if (!rule.block_bodys.empty()) { check_body = true; } return true; @@ -212,7 +211,7 @@ bool PluginRootContext::checkBody(const RequestBlockConfigRule& rule, bodystr = absl::AsciiStrToLower(request_body); body = bodystr; } - for (const auto& block_body : rule.block_bodies) { + for (const auto& block_body : rule.block_bodys) { if (absl::StrContains(body, block_body)) { sendLocalResponse(rule.blocked_code, "", rule.blocked_message, {}); return false; diff --git a/plugins/wasm-cpp/extensions/request_block/plugin.h b/plugins/wasm-cpp/extensions/request_block/plugin.h index 29f2d2f9a..4c9288f2a 100644 --- a/plugins/wasm-cpp/extensions/request_block/plugin.h +++ b/plugins/wasm-cpp/extensions/request_block/plugin.h @@ -45,7 +45,7 @@ struct RequestBlockConfigRule { bool case_sensitive = true; std::vector block_urls; std::vector block_headers; - std::vector block_bodies; + std::vector block_bodys; }; // PluginRootContext is the root context for all streams processed by the diff --git a/plugins/wasm-cpp/extensions/request_block/plugin_test.cc b/plugins/wasm-cpp/extensions/request_block/plugin_test.cc index 2b6dce5ae..40cc32711 100644 --- a/plugins/wasm-cpp/extensions/request_block/plugin_test.cc +++ b/plugins/wasm-cpp/extensions/request_block/plugin_test.cc @@ -128,7 +128,7 @@ TEST_F(RequestBlockTest, CaseSensitive) { { "block_urls": ["?foo=bar", "swagger.html"], "block_headers": ["headerKey", "headerValue"], - "block_bodies": ["Hello World"] + "block_bodys": ["Hello World"] })"; config_.set({configuration.data(), configuration.size()}); @@ -188,7 +188,7 @@ TEST_F(RequestBlockTest, CaseInsensitive) { "blocked_code": 404, "block_urls": ["?foo=bar", "swagger.html"], "block_headers": ["headerKey", "headerValue"], - "block_bodies": ["Hello World"] + "block_bodys": ["Hello World"] })"; config_.set({configuration.data(), configuration.size()}); diff --git a/plugins/wasm-cpp/extensions/sni_misdirect/BUILD b/plugins/wasm-cpp/extensions/sni_misdirect/BUILD index 0a800a0c0..b4b32b4c1 100644 --- a/plugins/wasm-cpp/extensions/sni_misdirect/BUILD +++ b/plugins/wasm-cpp/extensions/sni_misdirect/BUILD @@ -1,3 +1,17 @@ +# 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") @@ -10,8 +24,8 @@ wasm_cc_binary( deps = [ "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/strings", - "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", "//common:http_util", + "@proxy_wasm_cpp_sdk//:proxy_wasm_intrinsics", ], ) @@ -30,7 +44,7 @@ cc_library( "@com_google_absl//absl/strings:str_format", "@com_google_absl//absl/strings", "@proxy_wasm_cpp_host//:lib", - "//common:http_util", + "//common:http_util_nullvm", ], )