diff --git a/.asf.yaml b/.asf.yaml index ea44bea55f6f..fa106d0cab42 100644 --- a/.asf.yaml +++ b/.asf.yaml @@ -53,6 +53,10 @@ github: dismiss_stale_reviews: true require_code_owner_reviews: true required_approving_review_count: 2 + release/3.4: + required_pull_request_reviews: + require_code_owner_reviews: true + required_approving_review_count: 2 release/3.3: required_pull_request_reviews: require_code_owner_reviews: true diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index efa5b8f631e3..c90a8e90082d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,7 +28,6 @@ jobs: - ubuntu-20.04 os_name: - linux_openresty - - linux_openresty_1_19 test_dir: - t/plugin/[a-k]* - t/plugin/[l-z]* diff --git a/.github/workflows/cli.yml b/.github/workflows/cli.yml index 2f76670a0611..7aa6554095b9 100644 --- a/.github/workflows/cli.yml +++ b/.github/workflows/cli.yml @@ -31,7 +31,7 @@ jobs: - linux_apisix_current_luarocks_in_customed_nginx runs-on: ${{ matrix.platform }} - timeout-minutes: 15 + timeout-minutes: 30 env: SERVER_NAME: ${{ matrix.job_name }} OPENRESTY_VERSION: default diff --git a/.github/workflows/close-unresponded.yml b/.github/workflows/close-unresponded.yml new file mode 100644 index 000000000000..52e81228eba2 --- /dev/null +++ b/.github/workflows/close-unresponded.yml @@ -0,0 +1,38 @@ +name: Check Issues + +on: + workflow_dispatch: + schedule: + - cron: '0 10 * * *' + +permissions: + contents: read + +jobs: + prune_stale: + permissions: + issues: write # for actions/stale to close stale issues + name: Prune Unresponded + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Prune Stale + uses: actions/stale@v8 + with: + days-before-issue-stale: 14 + days-before-issue-close: 3 + stale-issue-message: > + Due to lack of the reporter's response this issue has been labeled with "no response". + It will be close in 3 days if no further activity occurs. If this issue is still + relevant, please simply write any comment. Even if closed, you can still revive the + issue at any time or discuss it on the dev@apisix.apache.org list. + Thank you for your contributions. + close-issue-message: > + This issue has been closed due to lack of activity. If you think that + is incorrect, or the issue requires additional review, you can revive the issue at + any time. + # Issues with these labels will never be considered stale. + only-labels: 'wait for update' + stale-issue-label: 'no response' + ascending: true diff --git a/.github/workflows/doc-lint.yml b/.github/workflows/doc-lint.yml index 1b3bf1c44111..99340f991e2c 100644 --- a/.github/workflows/doc-lint.yml +++ b/.github/workflows/doc-lint.yml @@ -22,7 +22,7 @@ jobs: steps: - uses: actions/checkout@v3.2.0 - name: 🚀 Use Node.js - uses: actions/setup-node@v3.6.0 + uses: actions/setup-node@v3.8.0 with: node-version: "12.x" - run: npm install -g markdownlint-cli@0.25.0 diff --git a/.github/workflows/kubernetes-ci.yml b/.github/workflows/kubernetes-ci.yml index ea72fe57144c..b8d33af7c956 100644 --- a/.github/workflows/kubernetes-ci.yml +++ b/.github/workflows/kubernetes-ci.yml @@ -28,7 +28,6 @@ jobs: - ubuntu-20.04 os_name: - linux_openresty - - linux_openresty_1_19 runs-on: ${{ matrix.platform }} timeout-minutes: 15 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index e624078d586a..10f852db30a3 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -33,7 +33,7 @@ jobs: uses: actions/checkout@v3.2.0 - name: Setup Nodejs env - uses: actions/setup-node@v3.6.0 + uses: actions/setup-node@v3.8.0 with: node-version: '12' diff --git a/.github/workflows/tars-ci.yml b/.github/workflows/tars-ci.yml index aa4c1b6e45af..a646d86ce1fe 100644 --- a/.github/workflows/tars-ci.yml +++ b/.github/workflows/tars-ci.yml @@ -28,7 +28,6 @@ jobs: - ubuntu-20.04 os_name: - linux_openresty - - linux_openresty_1_19 runs-on: ${{ matrix.platform }} timeout-minutes: 15 diff --git a/.github/workflows/update-labels.yml b/.github/workflows/update-labels.yml new file mode 100644 index 000000000000..262604f23cf0 --- /dev/null +++ b/.github/workflows/update-labels.yml @@ -0,0 +1,30 @@ +name: Update label when user responds +permissions: + issues: write + +on: + issue_comment: + types: [created] + +jobs: + run-check: + if: ${{ !github.event.issue.pull_request }} # don't execute for PR comments + runs-on: ubuntu-latest + steps: + - name: update labels when user responds + uses: actions/github-script@v6 + if: ${{ github.event.comment.user.login == github.event.issue.user.login && contains(github.event.issue.labels.*.name, 'wait for update') && !contains(github.event.issue.labels.*.name, 'user responded') }} + with: + script: | + github.rest.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ["user responded"] + }) + github.rest.issues.removeLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: "wait for update" + }) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1a8de59f9d23..78f9cc93a4dd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ title: Changelog ## Table of Contents +- [3.4.0](#340) - [3.3.0](#330) - [3.2.1](#321) - [3.2.0](#320) @@ -70,6 +71,38 @@ title: Changelog - [0.7.0](#070) - [0.6.0](#060) +## 3.4.0 + +### Core + +- :sunrise: Support route-level MTLS [#9322](https://github.com/apache/apisix/pull/9322) +- :sunrise: Support id schema for global_rules [#9517](https://github.com/apache/apisix/pull/9517) +- :sunrise: Support use a single long http connection to watch all resources for etcd [#9456](https://github.com/apache/apisix/pull/9456) +- :sunrise: Support max len 256 for ssl label [#9301](https://github.com/apache/apisix/pull/9301) + +### Plugins + +- :sunrise: Support multiple regex pattern matching for proxy_rewrite plugin [#9194](https://github.com/apache/apisix/pull/9194) +- :sunrise: Add loki-logger plugin [#9399](https://github.com/apache/apisix/pull/9399) +- :sunrise: Allow user configure DEFAULT_BUCKETS for prometheus plugin [#9673](https://github.com/apache/apisix/pull/9673) + +### Bugfixes + +- Fix(body-transformer): xml2lua: replace empty table with empty string [#9669](https://github.com/apache/apisix/pull/9669) +- Fix: opentelemetry and grpc-transcode plugins cannot work together [#9606](https://github.com/apache/apisix/pull/9606) +- Fix(skywalking-logger, error-log-logger): support $hostname in skywalking service_instance_name [#9401](https://github.com/apache/apisix/pull/9401) +- Fix(admin): fix secrets do not support to update attributes by PATCH [#9510](https://github.com/apache/apisix/pull/9510) +- Fix(http-logger): default request path should be '/' [#9472](https://github.com/apache/apisix/pull/9472) +- Fix: syslog plugin doesn't work [#9425](https://github.com/apache/apisix/pull/9425) +- Fix: wrong log format for splunk-hec-logging [#9478](https://github.com/apache/apisix/pull/9478) +- Fix(etcd): reuse cli and enable keepalive [#9420](https://github.com/apache/apisix/pull/9420) +- Fix: upstream key config add mqtt_client_id support [#9450](https://github.com/apache/apisix/pull/9450) +- Fix: body-transformer plugin return raw body anytime [#9446](https://github.com/apache/apisix/pull/9446) +- Fix(wolf-rbac): other plugin in consumer not effective when consumer used wolf-rbac plugin [#9298](https://github.com/apache/apisix/pull/9298) +- Fix: always parse domain when host is domain name [#9332](https://github.com/apache/apisix/pull/9332) +- Fix: response-rewrite plugin can't add only one character [#9372](https://github.com/apache/apisix/pull/9372) +- Fix(consul): support to fetch only health endpoint [#9204](https://github.com/apache/apisix/pull/9204) + ## 3.3.0 **The changes marked with :warning: are not backward compatible.** diff --git a/Makefile b/Makefile index 0e2238f2af27..c6979cd6f906 100644 --- a/Makefile +++ b/Makefile @@ -158,14 +158,14 @@ check-rust: ### deps : Installing dependencies .PHONY: deps -deps: check-rust runtime +deps: runtime $(eval ENV_LUAROCKS_VER := $(shell $(ENV_LUAROCKS) --version | grep -E -o "luarocks [0-9]+.")) @if [ '$(ENV_LUAROCKS_VER)' = 'luarocks 3.' ]; then \ mkdir -p ~/.luarocks; \ $(ENV_LUAROCKS) config $(ENV_LUAROCKS_FLAG_LOCAL) variables.OPENSSL_LIBDIR $(addprefix $(ENV_OPENSSL_PREFIX), /lib); \ $(ENV_LUAROCKS) config $(ENV_LUAROCKS_FLAG_LOCAL) variables.OPENSSL_INCDIR $(addprefix $(ENV_OPENSSL_PREFIX), /include); \ [ '$(ENV_OS_NAME)' == 'darwin' ] && $(ENV_LUAROCKS) config $(ENV_LUAROCKS_FLAG_LOCAL) variables.PCRE_INCDIR $(addprefix $(ENV_PCRE_PREFIX), /include); \ - $(ENV_LUAROCKS) install rockspec/apisix-master-0.rockspec --tree=deps --only-deps --local $(ENV_LUAROCKS_SERVER_OPT); \ + $(ENV_LUAROCKS) install rockspec/apisix-master-0.rockspec --tree deps --only-deps $(ENV_LUAROCKS_SERVER_OPT); \ else \ $(call func_echo_warn_status, "WARNING: You're not using LuaRocks 3.x; please remove the luarocks and reinstall it via https://raw.githubusercontent.com/apache/apisix/master/utils/linux-install-luarocks.sh"); \ exit 1; \ diff --git a/apisix/admin/init.lua b/apisix/admin/init.lua index 412e15447f30..0d4ef932362f 100644 --- a/apisix/admin/init.lua +++ b/apisix/admin/init.lua @@ -16,6 +16,7 @@ -- local require = require local core = require("apisix.core") +local get_uri_args = ngx.req.get_uri_args local route = require("apisix.utils.router") local plugin = require("apisix.plugin") local v3_adapter = require("apisix.admin.v3_adapter") @@ -174,7 +175,8 @@ local function run() if seg_res == "stream_routes" then local local_conf = core.config.local_conf() - if not local_conf.apisix.stream_proxy then + if local_conf.apisix.proxy_mode ~= "stream" and + local_conf.apisix.proxy_mode ~= "http&stream" then core.log.warn("stream mode is disabled, can not add any stream ", "routes") core.response.exit(400, {error_msg = "stream mode is disabled, " .. @@ -253,9 +255,16 @@ end local function get_plugins_list() set_ctx_and_check_token() - - local plugins = resources.plugins.get_plugins_list() - core.response.exit(200, plugins) + local args = get_uri_args() + local subsystem = args["subsystem"] + -- If subsystem is passed then it should be either http or stream. + -- If it is not passed/nil then http will be default. + subsystem = subsystem or "http" + if subsystem == "http" or subsystem == "stream" then + local plugins = resources.plugins.get_plugins_list(subsystem) + core.response.exit(200, plugins) + end + core.response.exit(400,"invalid subsystem passed") end -- Handle unsupported request methods for the virtual "reload" plugin diff --git a/apisix/admin/plugins.lua b/apisix/admin/plugins.lua index 26dcab719332..201f8f3c998e 100644 --- a/apisix/admin/plugins.lua +++ b/apisix/admin/plugins.lua @@ -18,11 +18,12 @@ local require = require local core = require("apisix.core") local check_schema = require("apisix.plugin").check_schema local ipairs = ipairs -local pcall = pcall local table_sort = table.sort local table_insert = table.insert local get_uri_args = ngx.req.get_uri_args local plugin_get_all = require("apisix.plugin").get_all +local plugin_get_http = require("apisix.plugin").get +local plugin_get_stream = require("apisix.plugin").get_stream local encrypt_conf = require("apisix.plugin").encrypt_conf local pairs = pairs @@ -42,7 +43,15 @@ end function _M.get(name) local arg = get_uri_args() - if arg and arg["all"] == "true" then + -- If subsystem is passed inside args then it should be oneOf: http / stream. + local subsystem = arg["subsystem"] or "http" + if subsystem ~= "http" and subsystem ~= "stream" then + return 400, {error_msg = "unsupported subsystem: "..subsystem} + end + + -- arg all to be deprecated + if (arg and arg["all"] == "true") then + core.log.warn("query parameter \"all\" will be deprecated soon.") local http_plugins, stream_plugins = plugin_get_all({ version = true, priority = true, @@ -60,16 +69,18 @@ function _M.get(name) return 200, http_plugins end - if not name then - return 400, {error_msg = "not found plugin name"} - end + local plugin - local plugin_name = "apisix.plugins." .. name + if subsystem == "http" then + plugin = plugin_get_http(name) + else + plugin = plugin_get_stream(name) + end - local ok, plugin = pcall(require, plugin_name) - if not ok then - core.log.warn("failed to load plugin [", name, "] err: ", plugin) - return 400, {error_msg = "failed to load plugin " .. name} + if not plugin then + local err = "plugin not found in subsystem " .. subsystem + core.log.warn(err) + return 404, {error_msg = err} end local json_schema = plugin.schema @@ -85,16 +96,34 @@ function _M.get(name) end -function _M.get_plugins_list() - local plugins = core.config.local_conf().plugins +function _M.get_plugins_list(subsystem) + local http_plugins + local stream_plugins + if subsystem == "http" then + http_plugins = core.config.local_conf().plugins + else + stream_plugins = core.config.local_conf().stream_plugins + end + local priorities = {} local success = {} - for i, name in ipairs(plugins) do - local plugin_name = "apisix.plugins." .. name - local ok, plugin = pcall(require, plugin_name) - if ok and plugin.priority then - priorities[name] = plugin.priority - table_insert(success, name) + if http_plugins then + for i, name in ipairs(http_plugins) do + local plugin = plugin_get_http(name) + if plugin and plugin.priority then + priorities[name] = plugin.priority + table_insert(success, name) + end + end + end + + if stream_plugins then + for i, name in ipairs(stream_plugins) do + local plugin = plugin_get_stream(name) + if plugin and plugin.priority then + priorities[name] = plugin.priority + table_insert(success, name) + end end end diff --git a/apisix/admin/proto.lua b/apisix/admin/proto.lua index de4d24e23e88..f8133cc80b71 100644 --- a/apisix/admin/proto.lua +++ b/apisix/admin/proto.lua @@ -50,7 +50,7 @@ local function check_proto_used(plugins, deleting, ptype, pid) if type(plugins) == "table" and plugins["grpc-transcode"] and plugins["grpc-transcode"].proto_id and tostring(plugins["grpc-transcode"].proto_id) == deleting then - return false, {error_msg = "can not delete this proto," + return false, {error_msg = "can not delete this proto, " .. ptype .. " [" .. pid .. "] is still using it now"} end diff --git a/apisix/admin/resource.lua b/apisix/admin/resource.lua index bfc6789df0fb..35fe3bba2476 100644 --- a/apisix/admin/resource.lua +++ b/apisix/admin/resource.lua @@ -230,7 +230,7 @@ function _M:put(id, conf, sub_path, args) end -- Keep the unused conf to make the args list consistent with other methods -function _M:delete(id, conf, sub_path) +function _M:delete(id, conf, sub_path, uri_args) if core.table.array_find(self.unsupported_methods, "delete") then return 405, {error_msg = "not supported `DELETE` method for " .. self.kind} end @@ -253,7 +253,7 @@ function _M:delete(id, conf, sub_path) key = key .. "/" .. id - if self.delete_checker then + if self.delete_checker and uri_args.force ~= "true" then local code, err = self.delete_checker(id) if err then return code, err diff --git a/apisix/admin/upstreams.lua b/apisix/admin/upstreams.lua index 687e09cd49e5..6c04d9379d52 100644 --- a/apisix/admin/upstreams.lua +++ b/apisix/admin/upstreams.lua @@ -16,12 +16,12 @@ -- local core = require("apisix.core") local config_util = require("apisix.core.config_util") -local router = require("apisix.router") local get_routes = require("apisix.router").http_routes local get_services = require("apisix.http.service").services local get_plugin_configs = require("apisix.plugin_config").plugin_configs local get_consumers = require("apisix.consumer").consumers local get_consumer_groups = require("apisix.consumer_group").consumer_groups +local get_global_rules = require("apisix.global_rules").global_rules local apisix_upstream = require("apisix.upstream") local resource = require("apisix.admin.resource") local tostring = tostring @@ -115,21 +115,10 @@ local function delete_checker(id) return 400, err_msg end - -- TODO: Refactor router.global_rules and then refactor the following code - local global_rules = router.global_rules - if global_rules and global_rules.values - and #global_rules.values > 0 then - - for _, global_rule in config_util.iterate_values(global_rules.values) do - if global_rule and global_rule.value - and global_rule.value.plugins - and up_id_in_plugins(global_rule.value.plugins, id) then - return 400, {error_msg = "can not delete this upstream," - .. " plugin in global_rule [" - .. global_rule.value.id - .. "] is still using it now"} - end - end + local global_rules = get_global_rules() + err_msg = check_resources_reference(global_rules, id, true, "global_rules") + if err_msg then + return 400, err_msg end return nil, nil diff --git a/apisix/cli/file.lua b/apisix/cli/file.lua index 2fe9edd030b3..149c4e913c35 100644 --- a/apisix/cli/file.lua +++ b/apisix/cli/file.lua @@ -54,8 +54,59 @@ local function tab_is_array(t) end +local function var_sub(val) + local err + local var_used = false + -- we use '${{var}}' because '$var' and '${var}' are taken + -- by Nginx + local new_val = val:gsub("%$%{%{%s*([%w_]+[%:%=]?.-)%s*%}%}", function(var) + local i, j = var:find("%:%=") + local default + if i and j then + default = var:sub(i + 2, #var) + default = default:gsub('^%s*(.-)%s*$', '%1') + var = var:sub(1, i - 1) + end + + local v = getenv(var) or default + if v then + if not exported_vars then + exported_vars = {} + end + + exported_vars[var] = v + var_used = true + return v + end + + err = "failed to handle configuration: " .. + "can't find environment variable " .. var + return "" + end) + return new_val, var_used, err +end + + local function resolve_conf_var(conf) + local new_keys = {} for key, val in pairs(conf) do + -- avoid re-iterating the table for already iterated key + if new_keys[key] then + goto continue + end + -- substitute environment variables from conf keys + if type(key) == "string" then + local new_key, _, err = var_sub(key) + if err then + return nil, err + end + if new_key ~= key then + new_keys[new_key] = "dummy" -- we only care about checking the key + conf.key = nil + conf[new_key] = val + key = new_key + end + end if type(val) == "table" then local ok, err = resolve_conf_var(val) if not ok then @@ -63,34 +114,7 @@ local function resolve_conf_var(conf) end elseif type(val) == "string" then - local err - local var_used = false - -- we use '${{var}}' because '$var' and '${var}' are taken - -- by Nginx - local new_val = val:gsub("%$%{%{%s*([%w_]+[%:%=]?.-)%s*%}%}", function(var) - local i, j = var:find("%:%=") - local default - if i and j then - default = var:sub(i + 2, #var) - default = default:gsub('^%s*(.-)%s*$', '%1') - var = var:sub(1, i - 1) - end - - local v = getenv(var) or default - if v then - if not exported_vars then - exported_vars = {} - end - - exported_vars[var] = v - var_used = true - return v - end - - err = "failed to handle configuration: " .. - "can't find environment variable " .. var - return "" - end) + local new_val, var_used, err = var_sub(val) if err then return nil, err @@ -108,6 +132,7 @@ local function resolve_conf_var(conf) conf[key] = new_val end + ::continue:: end return true @@ -223,7 +248,10 @@ function _M.read_yaml_conf(apisix_home) return nil, "invalid config-default.yaml file" end - local_conf_path = profile:yaml_path("config") + local_conf_path = profile:customized_yaml_path() + if not local_conf_path then + local_conf_path = profile:yaml_path("config") + end local user_conf_yaml, err = util.read_file(local_conf_path) if not user_conf_yaml then return nil, err diff --git a/apisix/cli/ngx_tpl.lua b/apisix/cli/ngx_tpl.lua index 74b14302b9bb..ab8407b572ec 100644 --- a/apisix/cli/ngx_tpl.lua +++ b/apisix/cli/ngx_tpl.lua @@ -124,7 +124,7 @@ http { {% end %} -{% if stream_proxy then %} +{% if enable_stream then %} stream { lua_package_path "{*extra_lua_path*}$prefix/deps/share/lua/5.1/?.lua;$prefix/deps/share/lua/5.1/?/init.lua;]=] .. [=[{*apisix_lua_home*}/?.lua;{*apisix_lua_home*}/?/init.lua;;{*lua_path*};"; @@ -653,6 +653,10 @@ http { } {% if ssl.enable then %} + ssl_client_hello_by_lua_block { + apisix.http_ssl_client_hello_phase() + } + ssl_certificate_by_lua_block { apisix.http_ssl_phase() } diff --git a/apisix/cli/ops.lua b/apisix/cli/ops.lua index 5bb87ad85056..8ba08c7fa974 100644 --- a/apisix/cli/ops.lua +++ b/apisix/cli/ops.lua @@ -258,7 +258,7 @@ Please modify "admin_key" in conf/config.yaml . util.die("can not find openresty\n") end - local need_ver = "1.19.3" + local need_ver = "1.21.4" if not version_greater_equal(or_ver, need_ver) then util.die("openresty version must >=", need_ver, " current ", or_ver, "\n") end @@ -269,11 +269,24 @@ Please modify "admin_key" in conf/config.yaml . "your openresty, please check it out.\n") end + --- http is enabled by default local enable_http = true - if not yaml_conf.apisix.enable_admin and yaml_conf.apisix.stream_proxy and - yaml_conf.apisix.stream_proxy.only ~= false - then - enable_http = false + --- stream is disabled by default + local enable_stream = false + if yaml_conf.apisix.proxy_mode then + --- check for "http" + if yaml_conf.apisix.proxy_mode == "http" then + enable_http = true + enable_stream = false + --- check for "stream" + elseif yaml_conf.apisix.proxy_mode == "stream" then + enable_stream = true + enable_http = false + --- check for "http&stream" + elseif yaml_conf.apisix.proxy_mode == "http&stream" then + enable_stream = true + enable_http = true + end end local enabled_discoveries = {} @@ -488,7 +501,7 @@ Please modify "admin_key" in conf/config.yaml . local tcp_enable_ssl -- compatible with the original style which only has the addr - if yaml_conf.apisix.stream_proxy and yaml_conf.apisix.stream_proxy.tcp then + if enable_stream and yaml_conf.apisix.stream_proxy and yaml_conf.apisix.stream_proxy.tcp then local tcp = yaml_conf.apisix.stream_proxy.tcp for i, item in ipairs(tcp) do if type(item) ~= "table" then @@ -545,6 +558,7 @@ Please modify "admin_key" in conf/config.yaml . use_apisix_base = env.use_apisix_base, error_log = {level = "warn"}, enable_http = enable_http, + enable_stream = enable_stream, enabled_discoveries = enabled_discoveries, enabled_plugins = enabled_plugins, enabled_stream_plugins = enabled_stream_plugins, @@ -739,7 +753,22 @@ local function init_etcd(env, args) end +local function cleanup(env) + if env.apisix_home then + profile.apisix_home = env.apisix_home + end + + os_remove(profile:customized_yaml_index()) +end + + local function start(env, ...) + cleanup(env) + + if env.apisix_home then + profile.apisix_home = env.apisix_home + end + -- Because the worker process started by apisix has "nobody" permission, -- it cannot access the `/root` directory. Therefore, it is necessary to -- prohibit APISIX from running in the /root directory. @@ -803,21 +832,25 @@ local function start(env, ...) local customized_yaml = args["config"] if customized_yaml then - profile.apisix_home = env.apisix_home .. "/" - local local_conf_path = profile:yaml_path("config") - local local_conf_path_bak = local_conf_path .. ".bak" + local customized_yaml_path + local idx = str_find(customized_yaml, "/") + if idx and idx == 1 then + customized_yaml_path = customized_yaml + else + local cur_dir, err = lfs.currentdir() + if err then + util.die("failed to get current directory") + end + customized_yaml_path = cur_dir .. "/" .. customized_yaml + end - local ok, err = os_rename(local_conf_path, local_conf_path_bak) - if not ok then - util.die("failed to backup config, error: ", err) + if not util.file_exists(customized_yaml_path) then + util.die("customized config file not exists, path: " .. customized_yaml_path) end - local ok, err1 = lfs.link(customized_yaml, local_conf_path) + + local ok, err = util.write_file(profile:customized_yaml_index(), customized_yaml_path) if not ok then - ok, err = os_rename(local_conf_path_bak, local_conf_path) - if not ok then - util.die("failed to recover original config file, error: ", err) - end - util.die("failed to link customized config, error: ", err1) + util.die("write customized config index failed, err: " .. err) end print("Use customized yaml: ", customized_yaml) @@ -833,22 +866,6 @@ local function start(env, ...) end -local function cleanup() - local local_conf_path = profile:yaml_path("config") - local local_conf_path_bak = local_conf_path .. ".bak" - if pl_path.exists(local_conf_path_bak) then - local ok, err = os_remove(local_conf_path) - if not ok then - print("failed to remove customized config, error: ", err) - end - ok, err = os_rename(local_conf_path_bak, local_conf_path) - if not ok then - util.die("failed to recover original config file, error: ", err) - end - end -end - - local function test(env, backup_ngx_conf) -- backup nginx.conf local ngx_conf_path = env.apisix_home .. "/conf/nginx.conf" @@ -888,7 +905,7 @@ end local function quit(env) - cleanup() + cleanup(env) local cmd = env.openresty_args .. [[ -s quit]] util.execute_cmd(cmd) @@ -896,7 +913,7 @@ end local function stop(env) - cleanup() + cleanup(env) local cmd = env.openresty_args .. [[ -s stop]] util.execute_cmd(cmd) diff --git a/apisix/cli/schema.lua b/apisix/cli/schema.lua index 56d7e2f630cc..3684232f1a7f 100644 --- a/apisix/cli/schema.lua +++ b/apisix/cli/schema.lua @@ -136,6 +136,10 @@ local config_schema = { } } }, + proxy_mode = { + type = "string", + enum = {"http", "stream", "http&stream"}, + }, stream_proxy = { type = "object", properties = { diff --git a/apisix/cli/util.lua b/apisix/cli/util.lua index cc6206a844e1..bcd56a241aa8 100644 --- a/apisix/cli/util.lua +++ b/apisix/cli/util.lua @@ -19,6 +19,7 @@ local require = require local pcall = pcall local open = io.open local popen = io.popen +local close = io.close local exit = os.exit local stderr = io.stderr local str_format = string.format @@ -127,4 +128,9 @@ function _M.write_file(file_path, data) end +function _M.file_exists(file_path) + local f = open(file_path, "r") + return f ~= nil and close(f) +end + return _M diff --git a/apisix/core/config_etcd.lua b/apisix/core/config_etcd.lua index 4cca5a44cde9..e3e40672c95f 100644 --- a/apisix/core/config_etcd.lua +++ b/apisix/core/config_etcd.lua @@ -55,6 +55,9 @@ local constants = require("apisix.constants") local health_check = require("resty.etcd.health_check") local semaphore = require("ngx.semaphore") local tablex = require("pl.tablex") +local ngx_thread_spawn = ngx.thread.spawn +local ngx_thread_kill = ngx.thread.kill +local ngx_thread_wait = ngx.thread.wait local is_http = ngx.config.subsystem == "http" @@ -124,7 +127,7 @@ local function produce_res(res, err) end -local function run_watch(premature) +local function do_run_watch(premature) if premature then return end @@ -257,6 +260,30 @@ local function run_watch(premature) end +local function run_watch(premature) + local run_watch_th = ngx_thread_spawn(do_run_watch, premature) + + ::restart:: + local check_worker_th = ngx_thread_spawn(function () + while not exiting() do + ngx_sleep(0.1) + end + end) + + local ok, err = ngx_thread_wait(check_worker_th) + + if not ok then + log.error("check_worker thread terminates failed, retart checker, error: " .. err) + ngx_thread_kill(check_worker_th) + goto restart + end + + ngx_thread_kill(run_watch_th) + -- notify child watchers + produce_res(nil, "worker exited") +end + + local function init_watch_ctx(key) if not watch_ctx then watch_ctx = { @@ -392,7 +419,7 @@ local function http_waitdir(self, etcd_cli, key, modified_index, timeout) if tonumber(res.result.header.revision) > self.prev_index then local res2 for _, evt in ipairs(res.result.events) do - if evt.kv.key:find(key) == 1 then + if core_str.find(evt.kv.key, key) == 1 then if not res2 then res2 = tablex.deepcopy(res) table.clear(res2.result.events) @@ -851,6 +878,9 @@ local function _automatic_fetch(premature, self) .. backoff_duration .. "s") end end + elseif err == "worker exited" then + log.info("worker exited.") + return elseif err ~= "timeout" and err ~= "Key not found" and self.last_err ~= err then log.error("failed to fetch data from etcd: ", err, ", ", diff --git a/apisix/core/profile.lua b/apisix/core/profile.lua index 389a9d42ccec..a5dcdc81e10b 100644 --- a/apisix/core/profile.lua +++ b/apisix/core/profile.lua @@ -19,6 +19,8 @@ -- -- @module core.profile +local util = require("apisix.cli.util") + local _M = { version = 0.1, profile = os.getenv("APISIX_PROFILE") or "", @@ -48,4 +50,18 @@ function _M.yaml_path(self, file_name) end +function _M.customized_yaml_index(self) + return self.apisix_home .. "/conf/.customized_config_path" +end + + +function _M.customized_yaml_path(self) + local customized_config_index = self:customized_yaml_index() + if util.file_exists(customized_config_index) then + return util.read_file(customized_config_index) + end + return nil +end + + return _M diff --git a/apisix/core/version.lua b/apisix/core/version.lua index b690175cea0a..7ba204811a82 100644 --- a/apisix/core/version.lua +++ b/apisix/core/version.lua @@ -20,5 +20,5 @@ -- @module core.version return { - VERSION = "3.3.0" + VERSION = "3.4.0" } diff --git a/apisix/discovery/kubernetes/init.lua b/apisix/discovery/kubernetes/init.lua index 3f5f275d9aff..d16d4f4fcd31 100644 --- a/apisix/discovery/kubernetes/init.lua +++ b/apisix/discovery/kubernetes/init.lua @@ -26,7 +26,7 @@ local error = error local pcall = pcall local setmetatable = setmetatable local is_http = ngx.config.subsystem == "http" -local support_process, process = pcall(require, "ngx.process") +local process = require("ngx.process") local core = require("apisix.core") local util = require("apisix.cli.util") local local_conf = require("apisix.core.config_local").local_conf() @@ -520,11 +520,6 @@ end function _M.init_worker() - if not support_process then - core.log.error("kubernetes discovery not support in subsystem: ", ngx.config.subsystem, - ", please check if your openresty version >= 1.19.9.1 or not") - return - end local discovery_conf = local_conf.discovery.kubernetes core.log.info("kubernetes discovery conf: ", core.json.delay_encode(discovery_conf)) if #discovery_conf == 0 then diff --git a/apisix/discovery/nacos/init.lua b/apisix/discovery/nacos/init.lua index e12c025c56d5..2e06f5553493 100644 --- a/apisix/discovery/nacos/init.lua +++ b/apisix/discovery/nacos/init.lua @@ -361,7 +361,9 @@ function _M.nodes(service_name, discovery_args) waiting_time = waiting_time - step end - if not applications[namespace_id] or not applications[namespace_id][group_name] then + if not applications or not applications[namespace_id] + or not applications[namespace_id][group_name] + then return nil end return applications[namespace_id][group_name][service_name] diff --git a/apisix/discovery/tars/init.lua b/apisix/discovery/tars/init.lua index 14f658dce5d2..17bb2758b199 100644 --- a/apisix/discovery/tars/init.lua +++ b/apisix/discovery/tars/init.lua @@ -23,7 +23,7 @@ local local_conf = require("apisix.core.config_local").local_conf() local core = require("apisix.core") local mysql = require("resty.mysql") local is_http = ngx.config.subsystem == "http" -local support_process, process = pcall(require, "ngx.process") +local process = require("ngx.process") local endpoint_dict @@ -343,12 +343,6 @@ local function get_endpoint_dict() end function _M.init_worker() - if not support_process then - core.log.error("tars discovery not support in subsystem: ", ngx.config.subsystem, - ", please check if your openresty version >= 1.19.9.1 or not") - return - end - endpoint_dict = get_endpoint_dict() if not endpoint_dict then error("failed to get lua_shared_dict: tars, please check your APISIX version") diff --git a/apisix/global_rules.lua b/apisix/global_rules.lua new file mode 100644 index 000000000000..93fa2890d423 --- /dev/null +++ b/apisix/global_rules.lua @@ -0,0 +1,56 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +local core = require("apisix.core") +local plugin_checker = require("apisix.plugin").plugin_checker +local error = error + + +local _M = {} + +local global_rules + +function _M.init_worker() + local err + global_rules, err = core.config.new("/global_rules", { + automatic = true, + item_schema = core.schema.global_rule, + checker = plugin_checker, + }) + if not global_rules then + error("failed to create etcd instance for fetching /global_rules : " + .. err) + end +end + + +function _M.global_rules() + if not global_rules then + return nil, nil + end + return global_rules.values, global_rules.conf_version +end + + +function _M.get_pre_index() + if not global_rules then + return nil + end + return global_rules.prev_index +end + +return _M diff --git a/apisix/init.lua b/apisix/init.lua index bff68dd5b40d..86b68cf62208 100644 --- a/apisix/init.lua +++ b/apisix/init.lua @@ -40,6 +40,7 @@ local apisix_upstream = require("apisix.upstream") local apisix_secret = require("apisix.secret") local set_upstream = apisix_upstream.set_by_route local apisix_ssl = require("apisix.ssl") +local apisix_global_rules = require("apisix.global_rules") local upstream_util = require("apisix.utils.upstream") local xrpc = require("apisix.stream.xrpc") local ctxdump = require("resty.ctxdump") @@ -155,6 +156,8 @@ function _M.http_init_worker() consumer_group.init_worker() apisix_secret.init_worker() + apisix_global_rules.init_worker() + apisix_upstream.init_worker() require("apisix.plugins.ext-plugin.init").init_worker() @@ -175,11 +178,31 @@ end function _M.http_ssl_phase() + local ok, err = router.router_ssl.set(ngx.ctx.matched_ssl) + if not ok then + if err then + core.log.error("failed to fetch ssl config: ", err) + end + ngx_exit(-1) + end +end + + +function _M.http_ssl_client_hello_phase() + local sni, err = apisix_ssl.server_name(true) + if not sni or type(sni) ~= "string" then + local advise = "please check if the client requests via IP or uses an outdated " .. + "protocol. If you need to report an issue, " .. + "provide a packet capture file of the TLS handshake." + core.log.error("failed to find SNI: " .. (err or advise)) + ngx_exit(-1) + end + local ngx_ctx = ngx.ctx local api_ctx = core.tablepool.fetch("api_ctx", 0, 32) ngx_ctx.api_ctx = api_ctx - local ok, err = router.router_ssl.match_and_set(api_ctx) + local ok, err = router.router_ssl.match_and_set(api_ctx, true, sni) ngx_ctx.matched_ssl = api_ctx.matched_ssl core.tablepool.release("api_ctx", api_ctx) @@ -189,6 +212,13 @@ function _M.http_ssl_phase() if err then core.log.error("failed to fetch ssl config: ", err) end + core.log.error("failed to match any SSL certificate by SNI: ", sni) + ngx_exit(-1) + end + + ok, err = apisix_ssl.set_protocols_by_clienthello(ngx_ctx.matched_ssl.value.ssl_protocols) + if not ok then + core.log.error("failed to set ssl protocols: ", err) ngx_exit(-1) end end @@ -597,7 +627,8 @@ function _M.http_access_phase() local route = api_ctx.matched_route if not route then -- run global rule when there is no matching route - plugin.run_global_rules(api_ctx, router.global_rules, nil) + local global_rules = apisix_global_rules.global_rules() + plugin.run_global_rules(api_ctx, global_rules, nil) core.log.info("not find any matched route") return core.response.exit(404, @@ -649,7 +680,8 @@ function _M.http_access_phase() api_ctx.route_name = route.value.name -- run global rule - plugin.run_global_rules(api_ctx, router.global_rules, nil) + local global_rules = apisix_global_rules.global_rules() + plugin.run_global_rules(api_ctx, global_rules, nil) if route.value.script then script.load(route, api_ctx) diff --git a/apisix/plugin.lua b/apisix/plugin.lua index e5e04dc39832..bde2b89a5393 100644 --- a/apisix/plugin.lua +++ b/apisix/plugin.lua @@ -775,6 +775,11 @@ function _M.get(name) end +function _M.get_stream(name) + return stream_local_plugins_hash and stream_local_plugins_hash[name] +end + + function _M.get_all(attrs) local http_plugins = {} local stream_plugins = {} @@ -1141,8 +1146,7 @@ end function _M.run_global_rules(api_ctx, global_rules, phase_name) - if global_rules and global_rules.values - and #global_rules.values > 0 then + if global_rules and #global_rules > 0 then local orig_conf_type = api_ctx.conf_type local orig_conf_version = api_ctx.conf_version local orig_conf_id = api_ctx.conf_id @@ -1152,7 +1156,7 @@ function _M.run_global_rules(api_ctx, global_rules, phase_name) end local plugins = core.tablepool.fetch("plugins", 32, 0) - local values = global_rules.values + local values = global_rules local route = api_ctx.matched_route for _, global_rule in config_util.iterate_values(values) do api_ctx.conf_type = "global_rule" diff --git a/apisix/plugin_config.lua b/apisix/plugin_config.lua index 828ebf1e2e1d..88b17d4b83ac 100644 --- a/apisix/plugin_config.lua +++ b/apisix/plugin_config.lua @@ -78,8 +78,7 @@ function _M.merge(route_conf, plugin_config) end end - route_conf.update_count = route_conf.update_count + 1 - route_conf.modifiedIndex = route_conf.orig_modifiedIndex .. "#" .. route_conf.update_count + route_conf.modifiedIndex = route_conf.orig_modifiedIndex .. "#" .. plugin_config.modifiedIndex route_conf.prev_plugin_config_ver = plugin_config.modifiedIndex return route_conf diff --git a/apisix/plugins/ai.lua b/apisix/plugins/ai.lua index b46249a9a190..39430c7ad014 100644 --- a/apisix/plugins/ai.lua +++ b/apisix/plugins/ai.lua @@ -18,6 +18,7 @@ local require = require local apisix = require("apisix") local core = require("apisix.core") local router = require("apisix.router") +local get_global_rules = require("apisix.global_rules").global_rules local event = require("apisix.core.event") local balancer = require("ngx.balancer") local ngx = ngx @@ -229,8 +230,8 @@ local function routes_analyze(routes) end end - local global_rules_flag = router.global_rules and router.global_rules.values - and #router.global_rules.values ~= 0 + local global_rules, _ = get_global_rules() + local global_rules_flag = global_rules and #global_rules ~= 0 if route_flags["vars"] or route_flags["filter_func"] or route_flags["remote_addr"] diff --git a/apisix/plugins/aws-lambda.lua b/apisix/plugins/aws-lambda.lua index fe4d7f394cd2..1b172af4135d 100644 --- a/apisix/plugins/aws-lambda.lua +++ b/apisix/plugins/aws-lambda.lua @@ -122,8 +122,10 @@ local function request_processor(conf, ctx, params) -- computing canonical query string local canonical_qs = {} + local canonical_qs_i = 0 for k, v in pairs(params.query) do - canonical_qs[#canonical_qs+1] = ngx.unescape_uri(k) .. "=" .. ngx.unescape_uri(v) + canonical_qs_i = canonical_qs_i + 1 + canonical_qs[canonical_qs_i] = ngx.unescape_uri(k) .. "=" .. ngx.unescape_uri(v) end tab_sort(canonical_qs) @@ -132,10 +134,12 @@ local function request_processor(conf, ctx, params) -- computing canonical and signed headers local canonical_headers, signed_headers = {}, {} + local signed_headers_i = 0 for k, v in pairs(headers) do k = k:lower() if k ~= "connection" then - signed_headers[#signed_headers+1] = k + signed_headers_i = signed_headers_i + 1 + signed_headers[signed_headers_i] = k -- strip starting and trailing spaces including strip multiple spaces into single space canonical_headers[k] = str_strip(v) end diff --git a/apisix/plugins/chaitin-waf.lua b/apisix/plugins/chaitin-waf.lua new file mode 100644 index 000000000000..afb4c108b3fb --- /dev/null +++ b/apisix/plugins/chaitin-waf.lua @@ -0,0 +1,373 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +local require = require +local core = require("apisix.core") +local rr_balancer = require("apisix.balancer.roundrobin") +local plugin = require("apisix.plugin") +local t1k = require "resty.t1k" +local expr = require("resty.expr.v1") + +local ngx = ngx +local ngx_now = ngx.now +local string = string +local fmt = string.format +local tostring = tostring +local tonumber = tonumber +local ipairs = ipairs + +local plugin_name = "chaitin-waf" + +local vars_schema = { + type = "array", +} + +local match_schema = { + type = "array", + items = { + type = "object", + properties = { + vars = vars_schema + } + }, +} + +local plugin_schema = { + type = "object", + properties = { + -- TODO: we should add a configuration "mode" here + -- It can be one of off, block and monitor + match = match_schema, + append_waf_resp_header = { + type = "boolean", + default = true + }, + append_waf_debug_header = { + type = "boolean", + default = false + }, + config = { + type = "object", + properties = { + connect_timeout = { + type = "integer", + }, + send_timeout = { + type = "integer", + }, + read_timeout = { + type = "integer", + }, + req_body_size = { + type = "integer", + }, + keepalive_size = { + type = "integer", + }, + keepalive_timeout = { + type = "integer", + } + }, + }, + }, +} + +local metadata_schema = { + type = "object", + properties = { + nodes = { + type = "array", + items = { + type = "object", + properties = { + host = { + type = "string", + pattern = "^\\*?[0-9a-zA-Z-._\\[\\]:]+$" + }, + port = { + type = "integer", + minimum = 1, + default = 80 + }, + }, + required = { "host" } + }, + minItems = 1, + }, + config = { + type = "object", + properties = { + connect_timeout = { + type = "integer", + default = 1000 -- milliseconds + }, + send_timeout = { + type = "integer", + default = 1000 -- milliseconds + }, + read_timeout = { + type = "integer", + default = 1000 -- milliseconds + }, + req_body_size = { + type = "integer", + default = 1024 -- milliseconds + }, + -- maximum concurrent idle connections to + -- the SafeLine WAF detection service + keepalive_size = { + type = "integer", + default = 256 + }, + keepalive_timeout = { + type = "integer", + default = 60000 -- milliseconds + }, + -- TODO: we need a configuration to enable/disable the real client ip + -- the real client ip is calculated by APISIX + }, + default = {}, + }, + }, + required = { "nodes" }, +} + +local _M = { + version = 0.1, + priority = 2700, + name = plugin_name, + schema = plugin_schema, + metadata_schema = metadata_schema +} + +local global_server_picker + +local HEADER_CHAITIN_WAF = "X-APISIX-CHAITIN-WAF" +local HEADER_CHAITIN_WAF_ERROR = "X-APISIX-CHAITIN-WAF-ERROR" +local HEADER_CHAITIN_WAF_TIME = "X-APISIX-CHAITIN-WAF-TIME" +local HEADER_CHAITIN_WAF_STATUS = "X-APISIX-CHAITIN-WAF-STATUS" +local HEADER_CHAITIN_WAF_ACTION = "X-APISIX-CHAITIN-WAF-ACTION" +local HEADER_CHAITIN_WAF_SERVER = "X-APISIX-CHAITIN-WAF-SERVER" +local blocked_message = [[{"code": %s, "success":false, ]] .. + [["message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "%s"}]] + + +function _M.check_schema(conf, schema_type) + if schema_type == core.schema.TYPE_METADATA then + return core.schema.check(metadata_schema, conf) + end + + local ok, err = core.schema.check(plugin_schema, conf) + + if not ok then + return false, err + end + + if conf.match then + for _, m in ipairs(conf.match) do + local ok, err = expr.new(m.vars) + if not ok then + return false, "failed to validate the 'vars' expression: " .. err + end + end + end + + return true +end + + +local function get_healthy_chaitin_server_nodes(metadata, checker) + local nodes = metadata.nodes + local new_nodes = core.table.new(0, #nodes) + + for i = 1, #nodes do + local host, port = nodes[i].host, nodes[i].port + new_nodes[host .. ":" .. tostring(port)] = 1 + end + + return new_nodes +end + + +local function get_chaitin_server(metadata, ctx) + if not global_server_picker or global_server_picker.upstream ~= metadata.value.nodes then + local up_nodes = get_healthy_chaitin_server_nodes(metadata.value) + if core.table.nkeys(up_nodes) == 0 then + return nil, nil, "no healthy nodes" + end + core.log.info("chaitin-waf nodes: ", core.json.delay_encode(up_nodes)) + + global_server_picker = rr_balancer.new(up_nodes, metadata.value.nodes) + end + + local server = global_server_picker.get(ctx) + local host, port, err = core.utils.parse_addr(server) + if err then + return nil, nil, err + end + + return host, port, nil +end + + +local function check_match(conf, ctx) + local match_passed = true + + if conf.match then + for _, match in ipairs(conf.match) do + -- todo: use lrucache to cache the result + local exp, err = expr.new(match.vars) + if err then + local msg = "failed to create match expression for " .. + tostring(match.vars) .. ", err: " .. tostring(err) + core.log.error(msg) + return false, msg + end + + match_passed = exp:eval(ctx.var) + if match_passed then + break + end + end + end + + return match_passed, nil +end + + +local function get_conf(conf, metadata) + local t = { + mode = "block", + } + + if metadata.config then + t.connect_timeout = metadata.config.connect_timeout + t.send_timeout = metadata.config.send_timeout + t.read_timeout = metadata.config.read_timeout + t.req_body_size = metadata.config.req_body_size + t.keepalive_size = metadata.config.keepalive_size + t.keepalive_timeout = metadata.config.keepalive_timeout + end + + if conf.config then + t.connect_timeout = conf.config.connect_timeout + t.send_timeout = conf.config.send_timeout + t.read_timeout = conf.config.read_timeout + t.req_body_size = conf.config.req_body_size + t.keepalive_size = conf.config.keepalive_size + t.keepalive_timeout = conf.config.keepalive_timeout + end + + return t +end + + +local function do_access(conf, ctx) + local extra_headers = {} + + local match, err = check_match(conf, ctx) + if not match then + if err then + extra_headers[HEADER_CHAITIN_WAF] = "err" + extra_headers[HEADER_CHAITIN_WAF_ERROR] = tostring(err) + return 500, nil, extra_headers + else + extra_headers[HEADER_CHAITIN_WAF] = "no" + return nil, nil, extra_headers + end + end + + local metadata = plugin.plugin_metadata(plugin_name) + if not core.table.try_read_attr(metadata, "value", "nodes") then + extra_headers[HEADER_CHAITIN_WAF] = "err" + extra_headers[HEADER_CHAITIN_WAF_ERROR] = "missing metadata" + return 500, nil, extra_headers + end + + local host, port, err = get_chaitin_server(metadata, ctx) + if err then + extra_headers[HEADER_CHAITIN_WAF] = "unhealthy" + extra_headers[HEADER_CHAITIN_WAF_ERROR] = tostring(err) + + return 500, nil, extra_headers + end + + core.log.info("picked chaitin-waf server: ", host, ":", port) + + local t = get_conf(conf, metadata.value) + t.host = host + t.port = port + + extra_headers[HEADER_CHAITIN_WAF_SERVER] = host + extra_headers[HEADER_CHAITIN_WAF] = "yes" + + local start_time = ngx_now() * 1000 + local ok, err, result = t1k.do_access(t, false) + if not ok then + extra_headers[HEADER_CHAITIN_WAF] = "waf-err" + local err_msg = tostring(err) + if core.string.find(err_msg, "timeout") then + extra_headers[HEADER_CHAITIN_WAF] = "timeout" + end + extra_headers[HEADER_CHAITIN_WAF_ERROR] = tostring(err) + else + extra_headers[HEADER_CHAITIN_WAF_ACTION] = "pass" + end + extra_headers[HEADER_CHAITIN_WAF_TIME] = ngx_now() * 1000 - start_time + + local code = 200 + extra_headers[HEADER_CHAITIN_WAF_STATUS] = code + if result then + if result.status then + code = result.status + extra_headers[HEADER_CHAITIN_WAF_STATUS] = code + extra_headers[HEADER_CHAITIN_WAF_ACTION] = "reject" + + core.log.error("request rejected by chaitin-waf, event_id: " .. result.event_id) + return tonumber(code), fmt(blocked_message, code, + result.event_id) .. "\n", extra_headers + end + end + if not ok then + extra_headers[HEADER_CHAITIN_WAF_STATUS] = nil + end + + return nil, nil, extra_headers +end + + +function _M.access(conf, ctx) + local code, msg, extra_headers = do_access(conf, ctx) + + if not conf.append_waf_debug_header then + extra_headers[HEADER_CHAITIN_WAF_ERROR] = nil + extra_headers[HEADER_CHAITIN_WAF_SERVER] = nil + end + + if conf.append_waf_resp_header then + core.response.set_header(extra_headers) + end + + return code, msg +end + + +function _M.header_filter(conf, ctx) + t1k.do_header_filter() +end + + +return _M diff --git a/apisix/plugins/consumer-restriction.lua b/apisix/plugins/consumer-restriction.lua index 93f3b9f59f5f..88c2bbd959d6 100644 --- a/apisix/plugins/consumer-restriction.lua +++ b/apisix/plugins/consumer-restriction.lua @@ -128,7 +128,9 @@ function _M.access(conf, ctx) local method = ngx.req.get_method() if not value then - return 401, { message = "Missing authentication or identity verification."} + local err_msg = "The request is rejected, please check the " + .. conf.type .. " for this request" + return 401, { message = err_msg} end core.log.info("value: ", value) diff --git a/apisix/plugins/file-logger.lua b/apisix/plugins/file-logger.lua index 97140abfac91..d07c49270798 100644 --- a/apisix/plugins/file-logger.lua +++ b/apisix/plugins/file-logger.lua @@ -16,6 +16,7 @@ -- local log_util = require("apisix.utils.log-util") local core = require("apisix.core") +local expr = require("resty.expr.v1") local ngx = ngx local io_open = io.open local is_apisix_or, process = pcall(require, "resty.apisix.process") @@ -38,6 +39,13 @@ local schema = { items = { type = "array" } + }, + match = { + type = "array", + maxItems = 20, + items = { + type = "array", + }, } }, required = {"path"} @@ -65,6 +73,12 @@ function _M.check_schema(conf, schema_type) if schema_type == core.schema.TYPE_METADATA then return core.schema.check(metadata_schema, conf) end + if conf.match then + local ok, err = expr.new(conf.match) + if not ok then + return nil, "failed to validate the 'match' expression: " .. err + end + end return core.schema.check(schema, conf) end @@ -150,6 +164,9 @@ end function _M.log(conf, ctx) local entry = log_util.get_log_entry(plugin_name, conf, ctx) + if entry == nil then + return + end write_file_data(conf, entry) end diff --git a/apisix/plugins/google-cloud-logging.lua b/apisix/plugins/google-cloud-logging.lua index 3fb34ab43efe..6e71cc81b300 100644 --- a/apisix/plugins/google-cloud-logging.lua +++ b/apisix/plugins/google-cloud-logging.lua @@ -35,6 +35,7 @@ local schema = { auth_config = { type = "object", properties = { + client_email = { type = "string" }, private_key = { type = "string" }, project_id = { type = "string" }, token_uri = { @@ -62,7 +63,7 @@ local schema = { default = "https://logging.googleapis.com/v2/entries:write" }, }, - required = { "private_key", "project_id", "token_uri" } + required = { "client_email", "private_key", "project_id", "token_uri" } }, ssl_verify = { type = "boolean", diff --git a/apisix/plugins/ldap-auth.lua b/apisix/plugins/ldap-auth.lua index 41156c1bfb6e..11f205c6b8f5 100644 --- a/apisix/plugins/ldap-auth.lua +++ b/apisix/plugins/ldap-auth.lua @@ -18,7 +18,7 @@ local core = require("apisix.core") local ngx = ngx local ngx_re = require("ngx.re") local consumer_mod = require("apisix.consumer") -local ok, ldap_cli = pcall(require, "resty.ldap.client") +local ldap = require("resty.ldap") local schema = { type = "object", @@ -100,11 +100,6 @@ local function extract_auth_header(authorization) end function _M.rewrite(conf, ctx) - if not ok then -- ensure rasn library loaded - core.log.error("failed to load lua-resty-ldap lib: ", ldap_cli) - return 501 - end - core.log.info("plugin rewrite phase, conf: ", core.json.delay_encode(conf)) -- 1. extract authorization from header @@ -115,31 +110,36 @@ function _M.rewrite(conf, ctx) end local user, err = extract_auth_header(auth_header) - if err then - core.log.warn(err) + if err or not user then + if err then + core.log.warn(err) + else + core.log.warn("nil user") + end return 401, { message = "Invalid authorization in request" } end -- 2. try authenticate the user against the ldap server local ldap_host, ldap_port = core.utils.parse_addr(conf.ldap_uri) - local ldap_client = ldap_cli:new(ldap_host, ldap_port, { + local ldapconf = { + timeout = 10000, start_tls = false, + ldap_host = ldap_host, + ldap_port = ldap_port or 389, ldaps = conf.use_tls, - ssl_verify = conf.tls_verify, - socket_timeout = 10000, - keepalive_pool_name = ldap_host .. ":" .. ldap_port .. "_ldapauth" - .. (conf.use_tls and "_tls" or ""), - keepalive_pool_size = 5, - keepalive_timeout = 60000, - }) - - local user_dn = conf.uid .. "=" .. user.username .. "," .. conf.base_dn - local res, err = ldap_client:simple_bind(user_dn, user.password) + tls_verify = conf.tls_verify, + base_dn = conf.base_dn, + attribute = conf.uid, + keepalive = 60000, + } + local res, err = ldap.ldap_authenticate(user.username, user.password, ldapconf) if not res then core.log.warn("ldap-auth failed: ", err) return 401, { message = "Invalid user authorization" } end + local user_dn = conf.uid .. "=" .. user.username .. "," .. conf.base_dn + -- 3. Retrieve consumer for authorization plugin local consumer_conf = consumer_mod.plugin(plugin_name) if not consumer_conf then diff --git a/apisix/plugins/limit-conn/init.lua b/apisix/plugins/limit-conn/init.lua index 3337980fd815..c6ce55f240d0 100644 --- a/apisix/plugins/limit-conn/init.lua +++ b/apisix/plugins/limit-conn/init.lua @@ -16,6 +16,7 @@ -- local limit_conn_new = require("resty.limit.conn").new local core = require("apisix.core") +local is_http = ngx.config.subsystem == "http" local sleep = core.sleep local shdict_name = "plugin-limit-conn" if ngx.config.subsystem == "stream" then @@ -115,11 +116,13 @@ function _M.decrease(conf, ctx) local use_delay = limit_conn[i + 3] local latency - if not use_delay then - if ctx.proxy_passed then - latency = ctx.var.upstream_response_time - else - latency = ctx.var.request_time - delay + if is_http then + if not use_delay then + if ctx.proxy_passed then + latency = ctx.var.upstream_response_time + else + latency = ctx.var.request_time - delay + end end end core.log.debug("request latency is ", latency) -- for test diff --git a/apisix/plugins/limit-count/limit-count-local.lua b/apisix/plugins/limit-count/limit-count-local.lua index 27c1fc454f4b..b6f319ae0106 100644 --- a/apisix/plugins/limit-count/limit-count-local.lua +++ b/apisix/plugins/limit-count/limit-count-local.lua @@ -65,10 +65,7 @@ end function _M.incoming(self, key, commit, conf, cost) local delay, remaining = self.limit_count:incoming(key, commit, cost) - local reset = 0 - if not delay then - return delay, remaining, reset - end + local reset if remaining == conf.count - cost then reset = set_endtime(self, key, conf.time_window) diff --git a/apisix/plugins/log-rotate.lua b/apisix/plugins/log-rotate.lua index 14e4c45c71ee..db3360e448b4 100644 --- a/apisix/plugins/log-rotate.lua +++ b/apisix/plugins/log-rotate.lua @@ -33,6 +33,7 @@ local os_remove = os.remove local os_rename = os.rename local str_sub = string.sub local str_format = string.format +local str_byte = string.byte local ngx_sleep = require("apisix.core.utils").sleep local string_rfind = require("pl.stringx").rfind local local_conf @@ -48,6 +49,7 @@ local default_logs local enable_compression = false local DEFAULT_ACCESS_LOG_FILENAME = "access.log" local DEFAULT_ERROR_LOG_FILENAME = "error.log" +local SLASH_BYTE = str_byte("/") local schema = { type = "object", @@ -88,9 +90,8 @@ local function get_log_path_info(file_type) local prefix = ngx.config.prefix() if conf_path then - local root = str_sub(conf_path, 1, 1) -- relative path - if root ~= "/" then + if str_byte(conf_path) ~= SLASH_BYTE then conf_path = prefix .. conf_path end local n = string_rfind(conf_path, "/") diff --git a/apisix/plugins/loki-logger.lua b/apisix/plugins/loki-logger.lua index 593fc8b18e6c..357ab1978202 100644 --- a/apisix/plugins/loki-logger.lua +++ b/apisix/plugins/loki-logger.lua @@ -192,18 +192,18 @@ function _M.log(conf, ctx) return end - -- generate a function to be executed by the batch processor - local func = function(entries) - local labels = conf.log_labels - - -- parsing possible variables in label value - for key, value in pairs(labels) do - local new_val, err, n_resolved = core.utils.resolve_var(value, ctx.var) - if not err and n_resolved > 0 then - labels[key] = new_val - end + local labels = conf.log_labels + + -- parsing possible variables in label value + for key, value in pairs(labels) do + local new_val, err, n_resolved = core.utils.resolve_var(value, ctx.var) + if not err and n_resolved > 0 then + labels[key] = new_val end + end + -- generate a function to be executed by the batch processor + local func = function(entries) -- build loki request data local data = { streams = { diff --git a/apisix/plugins/mocking.lua b/apisix/plugins/mocking.lua index 134647f71d9f..af5bc75edb2a 100644 --- a/apisix/plugins/mocking.lua +++ b/apisix/plugins/mocking.lua @@ -49,7 +49,19 @@ local schema = { -- specify response json schema, if response_example is not nil, this conf will be ignore. -- generate random response by json schema. response_schema = { type = "object" }, - with_mock_header = { type = "boolean", default = true } + with_mock_header = { type = "boolean", default = true }, + response_headers = { + type = "object", + minProperties = 1, + patternProperties = { + ["^[^:]+$"] = { + oneOf = { + { type = "string" }, + { type = "number" } + } + } + }, + } }, anyOf = { { required = { "response_example" } }, @@ -215,6 +227,12 @@ function _M.access(conf, ctx) ngx.header["x-mock-by"] = "APISIX/" .. core.version.VERSION end + if conf.response_headers then + for key, value in pairs(conf.response_headers) do + core.response.add_header(key, value) + end + end + if conf.delay > 0 then ngx.sleep(conf.delay) end diff --git a/apisix/plugins/opa.lua b/apisix/plugins/opa.lua index 1b7db1b62d73..24bdb5be1a46 100644 --- a/apisix/plugins/opa.lua +++ b/apisix/plugins/opa.lua @@ -19,6 +19,7 @@ local core = require("apisix.core") local http = require("resty.http") local helper = require("apisix.plugins.opa.helper") local type = type +local ipairs = ipairs local schema = { type = "object", @@ -37,6 +38,14 @@ local schema = { description = "timeout in milliseconds", }, keepalive = {type = "boolean", default = true}, + send_headers_upstream = { + type = "array", + minItems = 1, + items = { + type = "string" + }, + description = "list of headers to pass to upstream in request" + }, keepalive_timeout = {type = "integer", minimum = 1000, default = 60000}, keepalive_pool = {type = "integer", minimum = 1, default = 5}, with_route = {type = "boolean", default = false}, @@ -125,6 +134,14 @@ function _M.access(conf, ctx) end return status_code, reason + else if result.headers and conf.send_headers_upstream then + for _, name in ipairs(conf.send_headers_upstream) do + local value = result.headers[name] + if value then + core.request.set_header(ctx, name, value) + end + end + end end end diff --git a/apisix/plugins/openid-connect.lua b/apisix/plugins/openid-connect.lua index 5058eba47d28..927e4ddbd8aa 100644 --- a/apisix/plugins/openid-connect.lua +++ b/apisix/plugins/openid-connect.lua @@ -130,6 +130,32 @@ local schema = { "header to the request for downstream.", type = "boolean", default = false + }, + proxy_opts = { + description = "HTTP proxy server be used to access identity server.", + type = "object", + properties = { + http_proxy = { + type = "string", + description = "HTTP proxy like: http://proxy-server:80.", + }, + https_proxy = { + type = "string", + description = "HTTPS proxy like: http://proxy-server:80.", + }, + http_proxy_authorization = { + type = "string", + description = "Basic [base64 username:password].", + }, + https_proxy_authorization = { + type = "string", + description = "Basic [base64 username:password].", + }, + no_proxy = { + type = "string", + description = "Comma separated list of hosts that should not be proxied.", + } + }, } }, encrypt_fields = {"client_secret"}, diff --git a/apisix/plugins/prometheus/exporter.lua b/apisix/plugins/prometheus/exporter.lua index 8e90326409bc..623a9eddf113 100644 --- a/apisix/plugins/prometheus/exporter.lua +++ b/apisix/plugins/prometheus/exporter.lua @@ -34,6 +34,8 @@ local get_ssls = router.ssls local get_services = require("apisix.http.service").services local get_consumers = require("apisix.consumer").consumers local get_upstreams = require("apisix.upstream").upstreams +local get_global_rules = require("apisix.global_rules").global_rules +local get_global_rules_prev_index = require("apisix.global_rules").get_pre_index local clear_tab = core.table.clear local get_stream_routes = router.stream_routes local get_protos = require("apisix.plugins.grpc-transcode.proto").protos @@ -377,14 +379,15 @@ local function etcd_modify_index() global_max_idx = set_modify_index("consumers", consumers, consumers_ver, global_max_idx) -- global_rules - local global_rules = router.global_rules + local global_rules, global_rules_ver = get_global_rules() if global_rules then - global_max_idx = set_modify_index("global_rules", global_rules.values, - global_rules.conf_version, global_max_idx) + global_max_idx = set_modify_index("global_rules", global_rules, + global_rules_ver, global_max_idx) -- prev_index key_values[1] = "prev_index" - metrics.etcd_modify_indexes:set(global_rules.prev_index, key_values) + local prev_index = get_global_rules_prev_index() + metrics.etcd_modify_indexes:set(prev_index, key_values) else global_max_idx = set_modify_index("global_rules", nil, nil, global_max_idx) diff --git a/apisix/plugins/proxy-rewrite.lua b/apisix/plugins/proxy-rewrite.lua index 0766463fb14e..087686edfbf8 100644 --- a/apisix/plugins/proxy-rewrite.lua +++ b/apisix/plugins/proxy-rewrite.lua @@ -279,9 +279,12 @@ function _M.rewrite(conf, ctx) local separator_escaped = false if conf.use_real_request_uri_unsafe then upstream_uri = ctx.var.real_request_uri - elseif conf.uri ~= nil then + end + + if conf.uri ~= nil then separator_escaped = true upstream_uri = core.utils.resolve_var(conf.uri, ctx.var, escape_separator) + elseif conf.regex_uri ~= nil then if not str_find(upstream_uri, "?") then separator_escaped = true @@ -345,6 +348,8 @@ function _M.rewrite(conf, ctx) else ctx.var.upstream_uri = upstream_uri end + else + ctx.var.upstream_uri = upstream_uri end if conf.headers then diff --git a/apisix/plugins/request-id.lua b/apisix/plugins/request-id.lua index b9f1e94c6715..dac3162db5ed 100644 --- a/apisix/plugins/request-id.lua +++ b/apisix/plugins/request-id.lua @@ -49,7 +49,8 @@ local schema = { minLength = 6, default = "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789" } - } + }, + default = {} } } } diff --git a/apisix/plugins/tencent-cloud-cls/cls-sdk.lua b/apisix/plugins/tencent-cloud-cls/cls-sdk.lua index d2b6e8ad4525..650d4ab0e92b 100644 --- a/apisix/plugins/tencent-cloud-cls/cls-sdk.lua +++ b/apisix/plugins/tencent-cloud-cls/cls-sdk.lua @@ -41,6 +41,7 @@ local type = type local tostring = tostring local setmetatable = setmetatable local pcall = pcall +local unpack = unpack -- api doc https://www.tencentcloud.com/document/product/614/16873 local MAX_SINGLE_VALUE_SIZE = 1 * 1024 * 1024 @@ -62,13 +63,20 @@ local params_cache = { local function get_ip(hostname) local _, resolved = socket.dns.toip(hostname) local ip_list = {} - for _, v in ipairs(resolved.ip) do - insert_tab(ip_list, v) + if not resolved.ip then + -- DNS parsing failure + local err = resolved + core.log.error("resolve ip failed, hostname: " .. hostname .. ", error: " .. err) + return nil, err + else + for _, v in ipairs(resolved.ip) do + insert_tab(ip_list, v) + end end return ip_list end -local host_ip = tostring(unpack(get_ip(core_gethostname()))) +local host_ip local log_group_list = {} local log_group_list_pb = { logGroupList = log_group_list, @@ -273,6 +281,15 @@ function _M.send_to_cls(self, logs) -- sums of all value in all LogGroup should be no more than 5MB -- so send whenever size exceed max size local group_list_start = 1 + + if not host_ip then + local host_ip_list, err = get_ip(core_gethostname()) + if not host_ip_list then + return false, err + end + host_ip = tostring(unpack(host_ip_list)) + end + for i = 1, #logs, 1 do local contents, log_size = normalize_log(logs[i]) if log_size > MAX_LOG_GROUP_VALUE_SIZE then diff --git a/apisix/plugins/traffic-split.lua b/apisix/plugins/traffic-split.lua index 18a6be6d0136..1d621426a137 100644 --- a/apisix/plugins/traffic-split.lua +++ b/apisix/plugins/traffic-split.lua @@ -233,6 +233,20 @@ function _M.access(conf, ctx) local match_passed = true for _, rule in ipairs(conf.rules) do + -- check if all upstream_ids are valid + if rule.weighted_upstreams then + for _, wupstream in ipairs(rule.weighted_upstreams) do + local ups_id = wupstream.upstream_id + if ups_id then + local ups = upstream.get_by_id(ups_id) + if not ups then + return 500, "failed to fetch upstream info by " + .. "upstream id: " .. ups_id + end + end + end + end + if not rule.match then match_passed = true weighted_upstreams = rule.weighted_upstreams diff --git a/apisix/plugins/ua-restriction.lua b/apisix/plugins/ua-restriction.lua index ec74e7592115..577dc2b67cbb 100644 --- a/apisix/plugins/ua-restriction.lua +++ b/apisix/plugins/ua-restriction.lua @@ -22,11 +22,8 @@ local type = type local str_strip = stringx.strip local re_find = ngx.re.find -local MATCH_NONE = 0 -local MATCH_ALLOW = 1 -local MATCH_DENY = 2 - -local lrucache_useragent = core.lrucache.new({ ttl = 300, count = 4096 }) +local lrucache_allow = core.lrucache.new({ ttl = 300, count = 4096 }) +local lrucache_deny = core.lrucache.new({ ttl = 300, count = 4096 }) local schema = { type = "object", @@ -58,6 +55,10 @@ local schema = { default = "Not allowed" }, }, + oneOf = { + {required = {"allowlist"}}, + {required = {"denylist"}} + } } local plugin_name = "ua-restriction" @@ -69,27 +70,56 @@ local _M = { schema = schema, } -local function match_user_agent(user_agent, conf) - user_agent = str_strip(user_agent) - if conf.allowlist then - for _, rule in ipairs(conf.allowlist) do +local function check_with_allow_list(user_agents, allowlist) + local check = function (user_agent) + user_agent = str_strip(user_agent) + + for _, rule in ipairs(allowlist) do if re_find(user_agent, rule, "jo") then - return MATCH_ALLOW + return true end end + return false end - if conf.denylist then - for _, rule in ipairs(conf.denylist) do + if type(user_agents) == "table" then + for _, v in ipairs(user_agents) do + if lrucache_allow(v, allowlist, check, v) then + return true + end + end + return false + else + return lrucache_allow(user_agents, allowlist, check, user_agents) + end +end + + +local function check_with_deny_list(user_agents, denylist) + local check = function (user_agent) + user_agent = str_strip(user_agent) + + for _, rule in ipairs(denylist) do if re_find(user_agent, rule, "jo") then - return MATCH_DENY + return false end end + return true end - return MATCH_NONE + if type(user_agents) == "table" then + for _, v in ipairs(user_agents) do + if lrucache_deny(v, denylist, check, v) then + return false + end + end + return true + else + return lrucache_deny(user_agents, denylist, check, user_agents) + end end + function _M.check_schema(conf) local ok, err = core.schema.check(schema, conf) @@ -118,6 +148,7 @@ function _M.check_schema(conf) return true end + function _M.access(conf, ctx) local user_agent = core.request.header(ctx, "User-Agent") @@ -128,21 +159,16 @@ function _M.access(conf, ctx) return 403, { message = conf.message } end end - local match = MATCH_NONE - if type(user_agent) == "table" then - for _, v in ipairs(user_agent) do - if type(v) == "string" then - match = lrucache_useragent(v, conf, match_user_agent, v, conf) - if match > MATCH_ALLOW then - break - end - end - end + + local is_passed + + if conf.allowlist then + is_passed = check_with_allow_list(user_agent, conf.allowlist) else - match = lrucache_useragent(user_agent, conf, match_user_agent, user_agent, conf) + is_passed = check_with_deny_list(user_agent, conf.denylist) end - if match > MATCH_ALLOW then + if not is_passed then return 403, { message = conf.message } end end diff --git a/apisix/router.lua b/apisix/router.lua index 2fd14917c299..93b123e5b004 100644 --- a/apisix/router.lua +++ b/apisix/router.lua @@ -18,9 +18,7 @@ local require = require local http_route = require("apisix.http.route") local apisix_upstream = require("apisix.upstream") local core = require("apisix.core") -local plugin_checker = require("apisix.plugin").plugin_checker local str_lower = string.lower -local error = error local ipairs = ipairs @@ -29,7 +27,6 @@ local _M = {version = 0.3} local function filter(route) route.orig_modifiedIndex = route.modifiedIndex - route.update_count = 0 route.has_domain = false if not route.value then @@ -91,17 +88,6 @@ function _M.http_init_worker() _M.router_ssl = router_ssl _M.api = require("apisix.api_router") - - local global_rules, err = core.config.new("/global_rules", { - automatic = true, - item_schema = core.schema.global_rule, - checker = plugin_checker, - }) - if not global_rules then - error("failed to create etcd instance for fetching /global_rules : " - .. err) - end - _M.global_rules = global_rules end diff --git a/apisix/schema_def.lua b/apisix/schema_def.lua index f1d4d97efe5d..01e0649e0a91 100644 --- a/apisix/schema_def.lua +++ b/apisix/schema_def.lua @@ -790,6 +790,15 @@ _M.ssl = { enum = {1, 0}, default = 1 }, + ssl_protocols = { + description = "set ssl protocols", + type = "array", + maxItems = 3, + uniqueItems = true, + items = { + enum = {"TLSv1.1", "TLSv1.2", "TLSv1.3"} + }, + }, validity_end = timestamp_def, validity_start = timestamp_def, create_time = timestamp_def, diff --git a/apisix/ssl.lua b/apisix/ssl.lua index 8bcdec0ffa90..e14c92fc3c41 100644 --- a/apisix/ssl.lua +++ b/apisix/ssl.lua @@ -14,9 +14,11 @@ -- See the License for the specific language governing permissions and -- limitations under the License. -- -local core = require("apisix.core") -local ngx_ssl = require("ngx.ssl") -local secret = require("apisix.secret") +local core = require("apisix.core") +local secret = require("apisix.secret") +local ngx_ssl = require("ngx.ssl") +local ngx_ssl_client = require("ngx.ssl.clienthello") + local ngx_encode_base64 = ngx.encode_base64 local ngx_decode_base64 = ngx.decode_base64 local aes = require("resty.aes") @@ -38,8 +40,13 @@ local pkey_cache = core.lrucache.new { local _M = {} -function _M.server_name() - local sni, err = ngx_ssl.server_name() +function _M.server_name(clienthello) + local sni, err + if clienthello then + sni, err = ngx_ssl_client.get_client_hello_server_name() + else + sni, err = ngx_ssl.server_name() + end if err then return nil, err end @@ -57,6 +64,14 @@ function _M.server_name() end +function _M.set_protocols_by_clienthello(ssl_protocols) + if ssl_protocols then + return ngx_ssl_client.set_protocols(ssl_protocols) + end + return true +end + + local function init_iv_tbl(ivs) local _aes_128_cbc_with_iv_tbl = core.table.new(2, 0) local type_ivs = type(ivs) diff --git a/apisix/ssl/router/radixtree_sni.lua b/apisix/ssl/router/radixtree_sni.lua index b0e78a25fdc9..b6824852e0b3 100644 --- a/apisix/ssl/router/radixtree_sni.lua +++ b/apisix/ssl/router/radixtree_sni.lua @@ -185,6 +185,7 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) for _, msni in ipairs(api_ctx.matched_sni) do if sni_rev == msni or not str_find(sni_rev, ".", #msni) then matched = true + break end end if not matched then @@ -205,13 +206,35 @@ function _M.match_and_set(api_ctx, match_only, alt_sni) end end - local matched_ssl = api_ctx.matched_ssl - core.log.info("debug - matched: ", core.json.delay_encode(matched_ssl, true)) + core.log.info("debug - matched: ", core.json.delay_encode(api_ctx.matched_ssl, true)) if match_only then return true end + ok, err = _M.set(api_ctx.matched_ssl, sni) + if not ok then + return false, err + end + + return true +end + + +function _M.set(matched_ssl, sni) + if not matched_ssl then + return false, "failed to match ssl certificate" + end + local ok, err + if not sni then + sni, err = apisix_ssl.server_name() + if type(sni) ~= "string" then + local advise = "please check if the client requests via IP or uses an outdated " .. + "protocol. If you need to report an issue, " .. + "provide a packet capture file of the TLS handshake." + return false, "failed to find SNI: " .. (err or advise) + end + end ngx_ssl.clear_certs() local new_ssl_value = secret.fetch_secrets(matched_ssl.value) or matched_ssl.value diff --git a/apisix/utils/log-util.lua b/apisix/utils/log-util.lua index 4aecf290e4ee..8e9cb9c01361 100644 --- a/apisix/utils/log-util.lua +++ b/apisix/utils/log-util.lua @@ -210,7 +210,27 @@ function _M.inject_get_full_log(f) end +local function is_match(match, ctx) + local match_result + for _, m in pairs(match) do + local expr, _ = expr.new(m) + match_result = expr:eval(ctx.var) + if match_result then + break + end + end + + return match_result +end + + function _M.get_log_entry(plugin_name, conf, ctx) + -- If the "match" configuration is set and the matching conditions are not met, + -- then do not log the message. + if conf.match and not is_match(conf.match, ctx) then + return + end + local metadata = plugin.plugin_metadata(plugin_name) core.log.info("metadata: ", core.json.delay_encode(metadata)) diff --git a/ci/centos7-ci.sh b/ci/centos7-ci.sh index 251dd33639b3..6b6483a4f065 100755 --- a/ci/centos7-ci.sh +++ b/ci/centos7-ci.sh @@ -33,7 +33,7 @@ install_dependencies() { # install openresty to make apisix's rpm test work yum install -y yum-utils && yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo - yum install -y openresty openresty-debug openresty-openssl111-debug-devel pcre pcre-devel + yum install -y openresty-1.21.4.1 openresty-debug-1.21.4.1 openresty-openssl111-debug-devel pcre pcre-devel # install luarocks ./utils/linux-install-luarocks.sh diff --git a/ci/common.sh b/ci/common.sh index 509647b001d2..2840b7d8a711 100644 --- a/ci/common.sh +++ b/ci/common.sh @@ -21,6 +21,7 @@ export_or_prefix() { export OPENRESTY_PREFIX="/usr/local/openresty-debug" export APISIX_MAIN="https://raw.githubusercontent.com/apache/incubator-apisix/master/rockspec/apisix-master-0.rockspec" export PATH=$OPENRESTY_PREFIX/nginx/sbin:$OPENRESTY_PREFIX/luajit/bin:$OPENRESTY_PREFIX/bin:$PATH + export OPENSSL111_BIN=$OPENRESTY_PREFIX/openssl111/bin/openssl } create_lua_deps() { @@ -102,6 +103,9 @@ install_nodejs () { install_rust () { curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sudo sh -s -- -y source "$HOME/.cargo/env" + # 1.69.0 version required to compile lua-resty-ldap + rustup install 1.69.0 + rustup default 1.69.0 } set_coredns() { diff --git a/ci/init-plugin-test-service.sh b/ci/init-plugin-test-service.sh index 39feb410cfd5..d670b7fa39d4 100755 --- a/ci/init-plugin-test-service.sh +++ b/ci/init-plugin-test-service.sh @@ -22,8 +22,8 @@ after() { docker exec -i apache-apisix_kafka-server2_1 /opt/bitnami/kafka/bin/kafka-topics.sh --create --zookeeper zookeeper-server2:2181 --replication-factor 1 --partitions 1 --topic test4 # prepare openwhisk env - docker pull openwhisk/action-nodejs-v14:nightly - docker run --rm -d --name openwhisk -p 3233:3233 -p 3232:3232 -v /var/run/docker.sock:/var/run/docker.sock openwhisk/standalone:nightly + docker pull openwhisk/action-nodejs-v14:1.20.0 + docker run --rm -d --name openwhisk -p 3233:3233 -p 3232:3232 -v /var/run/docker.sock:/var/run/docker.sock openwhisk/standalone:1.0.0 docker exec -i openwhisk waitready docker exec -i openwhisk bash -c "wsk package create pkg" docker exec -i openwhisk bash -c "wsk action update /guest/pkg/testpkg <(echo 'function main(args){return {\"hello\": \"world\"}}') --kind nodejs:14" diff --git a/ci/linux-install-openresty.sh b/ci/linux-install-openresty.sh index 3bc2d18ab35e..8d4b6f87224a 100755 --- a/ci/linux-install-openresty.sh +++ b/ci/linux-install-openresty.sh @@ -38,6 +38,13 @@ OPENSSL3_PREFIX=${OPENSSL3_PREFIX-/home/runner} SSL_LIB_VERSION=${SSL_LIB_VERSION-openssl} if [ "$OPENRESTY_VERSION" == "source" ]; then + export openssl_prefix=/usr/local/openresty/openssl111 + export zlib_prefix=/usr/local/openresty/zlib + export pcre_prefix=/usr/local/openresty/pcre + + export cc_opt="-DNGX_LUA_ABORT_AT_PANIC -I${zlib_prefix}/include -I${pcre_prefix}/include -I${openssl_prefix}/include" + export ld_opt="-L${zlib_prefix}/lib -L${pcre_prefix}/lib -L${openssl_prefix}/lib -Wl,-rpath,${zlib_prefix}/lib:${pcre_prefix}/lib:${openssl_prefix}/lib" + if [ "$COMPILE_OPENSSL3" == "yes" ]; then apt install -y build-essential git clone https://github.com/openssl/openssl @@ -72,7 +79,8 @@ if [ "$OPENRESTY_VERSION" == "source" ]; then chmod +x build-apisix-base.sh ./build-apisix-base.sh latest - sudo apt-get install openresty-openssl111-debug-dev + sudo apt-get install -y openresty-openssl111 openresty-openssl111-debug-dev libldap2-dev openresty-pcre openresty-zlib + exit 0 fi diff --git a/ci/linux_apisix_master_luarocks_runner.sh b/ci/linux_apisix_master_luarocks_runner.sh index 8931ad82c5f5..3e99baf34116 100755 --- a/ci/linux_apisix_master_luarocks_runner.sh +++ b/ci/linux_apisix_master_luarocks_runner.sh @@ -52,6 +52,13 @@ script() { sudo PATH=$PATH apisix init sudo PATH=$PATH apisix start sudo PATH=$PATH apisix quit + for i in {1..10} + do + if [ ! -f /usr/local/apisix/logs/nginx.pid ];then + break + fi + sleep 0.3 + done sudo PATH=$PATH apisix start sudo PATH=$PATH apisix stop diff --git a/ci/linux_openresty_1_19_runner.sh b/ci/linux_openresty_1_19_runner.sh deleted file mode 100755 index ed1751308926..000000000000 --- a/ci/linux_openresty_1_19_runner.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash -# -# Licensed to the Apache Software Foundation (ASF) under one or more -# contributor license agreements. See the NOTICE file distributed with -# this work for additional information regarding copyright ownership. -# The ASF licenses this file to You under the Apache License, Version 2.0 -# (the "License"); you may not use this file except in compliance with -# the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# - - -export OPENRESTY_VERSION=1.19.3.2 -. ./ci/linux_openresty_common_runner.sh diff --git a/ci/pod/opa/example.rego b/ci/pod/opa/example.rego index 2eb912e08c52..a9161042b887 100644 --- a/ci/pod/opa/example.rego +++ b/ci/pod/opa/example.rego @@ -29,6 +29,11 @@ allow { request.query["user"] } +allow { + request.method == "GET" + startswith(request.path, "/echo") +} + reason = users[request.query["user"]].reason { not allow request.query["user"] @@ -39,6 +44,11 @@ headers = users[request.query["user"]].headers { request.query["user"] } +headers = {"user": request.query["user"]} { + allow + request.query["user"] +} + status_code = users[request.query["user"]].status_code { not allow request.query["user"] diff --git a/ci/pod/openfunction/function-example/test-body/go.mod b/ci/pod/openfunction/function-example/test-body/go.mod index c6c3ac1b0ec5..b9e81701913d 100644 --- a/ci/pod/openfunction/function-example/test-body/go.mod +++ b/ci/pod/openfunction/function-example/test-body/go.mod @@ -20,8 +20,8 @@ require ( go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.19.1 // indirect golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect - golang.org/x/sys v0.1.0 // indirect - golang.org/x/text v0.3.8 // indirect + golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 // indirect + golang.org/x/text v0.3.7 // indirect google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2 // indirect google.golang.org/grpc v1.40.0 // indirect google.golang.org/protobuf v1.28.0 // indirect diff --git a/ci/pod/openfunction/function-example/test-body/go.sum b/ci/pod/openfunction/function-example/test-body/go.sum index 2dca89d431b8..1fb1db392365 100644 --- a/ci/pod/openfunction/function-example/test-body/go.sum +++ b/ci/pod/openfunction/function-example/test-body/go.sum @@ -1094,7 +1094,6 @@ github.com/yuin/goldmark v1.1.30/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yuin/gopher-lua v0.0.0-20200603152657-dc2b0ca8b37e/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= @@ -1219,7 +1218,6 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1284,8 +1282,6 @@ golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b h1:PxfKdU9lEEDYjdIzOtC4qFWgkU2rGHdKlKowJSMN9h0= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1311,7 +1307,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180828065106-d99a578cf41b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1403,14 +1398,10 @@ golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210831042530-f4d43177bf5e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1419,9 +1410,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.3.8 h1:nAL+RVCQ9uMn3vJZbV+MRnydTJFPf8qqY42YiA6MrqY= -golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -1513,7 +1503,6 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/ci/pod/openfunction/function-example/test-uri/go.mod b/ci/pod/openfunction/function-example/test-uri/go.mod index 7d7fb3203cc5..94075e599c8f 100644 --- a/ci/pod/openfunction/function-example/test-uri/go.mod +++ b/ci/pod/openfunction/function-example/test-uri/go.mod @@ -20,9 +20,9 @@ require ( go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.7.0 // indirect go.uber.org/zap v1.21.0 // indirect - golang.org/x/net v0.7.0 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/net v0.0.0-20220621193019-9d032be2e588 // indirect + golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a // indirect + golang.org/x/text v0.3.7 // indirect google.golang.org/genproto v0.0.0-20220622171453-ea41d75dfa0f // indirect google.golang.org/grpc v1.47.0 // indirect google.golang.org/protobuf v1.28.0 // indirect diff --git a/ci/pod/openfunction/function-example/test-uri/go.sum b/ci/pod/openfunction/function-example/test-uri/go.sum index 28f9e57c8fbe..d7837ccf355c 100644 --- a/ci/pod/openfunction/function-example/test-uri/go.sum +++ b/ci/pod/openfunction/function-example/test-uri/go.sum @@ -1656,7 +1656,6 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/gopher-lua v0.0.0-20191220021717-ab39c6098bdb/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yuin/gopher-lua v0.0.0-20200603152657-dc2b0ca8b37e/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ= github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= @@ -1856,7 +1855,6 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= -golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/net v0.0.0-20180530234432-1e491301e022/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1941,10 +1939,8 @@ golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220621193019-9d032be2e588 h1:9ubFuySsnAJYGyJrZ3koiEv8FyqofCBdz3G9Mbf2YFc= golang.org/x/net v0.0.0-20220621193019-9d032be2e588/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1977,7 +1973,6 @@ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180828065106-d99a578cf41b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -2129,16 +2124,13 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -2148,9 +2140,8 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -2247,7 +2238,6 @@ golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.6-0.20210820212750-d4cc65f0b2ff/go.mod h1:YD9qOF0M9xpSpdWTBbzEl5e/RnCefISl8E5Noe10jFM= golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= -golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/ci/redhat-ci.sh b/ci/redhat-ci.sh index c1bd6d8f0f0b..4b307e64811a 100755 --- a/ci/redhat-ci.sh +++ b/ci/redhat-ci.sh @@ -32,7 +32,7 @@ install_dependencies() { # install openresty to make apisix's rpm test work yum install -y yum-utils && yum-config-manager --add-repo https://openresty.org/package/centos/openresty.repo - yum install -y openresty openresty-debug openresty-openssl111-debug-devel pcre pcre-devel xz + yum install -y openresty-1.21.4.1 openresty-debug-1.21.4.1 openresty-openssl111-debug-devel pcre pcre-devel xz # install luarocks ./utils/linux-install-luarocks.sh diff --git a/conf/config-default.yaml b/conf/config-default.yaml index e40dc174c908..359e2c2d806e 100755 --- a/conf/config-default.yaml +++ b/conf/config-default.yaml @@ -73,6 +73,8 @@ apisix: # radixtree_uri_with_parameter: similar to radixtree_uri but match URI with parameters. See https://github.com/api7/lua-resty-radixtree/#parameters-in-path for more details. ssl: radixtree_sni # radixtree_sni: match route by SNI + # http is the default proxy mode. proxy_mode can be one of `http`, `stream`, or `http&stream` + proxy_mode: http # stream_proxy: # TCP/UDP L4 proxy # only: true # Enable L4 proxy only without L7 proxy. # tcp: @@ -448,6 +450,7 @@ plugins: # plugin list (sorted by priority) - csrf # priority: 2980 - uri-blocker # priority: 2900 - request-validation # priority: 2800 + - chaitin-waf # priority: 2700 - openid-connect # priority: 2599 - cas-auth # priority: 2597 - authz-casbin # priority: 2560 @@ -609,9 +612,34 @@ plugin_attr: # Plugin attributes # write access to this file for security. deployment: # Deployment configurations - role: traditional # Set deployment mode: traditional, control_plane, data_plane. + role: traditional # Set deployment mode: traditional, control_plane, or data_plane. role_traditional: config_provider: etcd # Set the configuration center. + + # role_data_plane: # Set data plane details if role is data_plane. + # config_provider: control_plane # Set the configuration center: control_plane, or yaml. + # control_plane: # Set control plane details if config_provider is control_plane. + # host: # Set the address of control plane. + # - https://${control_plane_IP}:9280 + # prefix: /apisix # Set etcd prefix. + # timeout: 30 # Set timeout in seconds. + # certs: + # cert: /path/to/client.crt # Set path to the client certificate. + # cert_key: /path/to/client.key # Set path to the client key. + # trusted_ca_cert: /path/to/ca.crt # Set path to the trusted CA certificate. + + # role_control_plane: # Set control plane details if role is control_plane. + # config_provider: etcd # Set the configuration center. + # conf_server: + # listen: 0.0.0.0:9280 # Set the address of the conf server. + # cert: /path/to/server.crt # Set path to the server certificate. + # cert_key: /path/to/server.key # Set path to the server key. + # client_ca_cert: /path/to/ca.crt # Set path to the trusted CA certificate. + # certs: + # cert: /path/to/client.crt # Set path to the client certificate. + # cert_key: /path/to/client.key # Set path to the client key. + # trusted_ca_cert: /path/to/ca.crt # Set path to the trusted CA certificate. + admin: # Admin API admin_key_required: true # Enable Admin API authentication by default for security. admin_key: @@ -644,7 +672,7 @@ deployment: # Deployment configurations etcd: host: # Set etcd address(es) in the same etcd cluster. - "http://127.0.0.1:2379" # If TLS is enabled for etcd, use https://127.0.0.1:2379. - prefix: /apisix # Set prefix in etcd. + prefix: /apisix # Set etcd prefix. use_grpc: false # Use gRPC (experimental) for etcd configuration sync. timeout: 30 # Set timeout in seconds. # Set a higher timeout (e.g. an hour) if `use_grpc` is true. diff --git a/docs/en/latest/admin-api.md b/docs/en/latest/admin-api.md index af2775e7b67b..e34468eacc4b 100644 --- a/docs/en/latest/admin-api.md +++ b/docs/en/latest/admin-api.md @@ -103,6 +103,46 @@ deployment: This will find the environment variable `ADMIN_KEY` first, and if it does not exist, it will use `edd1c9f034335f136f87ad84b625c8f1` as the default value. +You can also specify environment variables in yaml keys. This is specifically useful in the `standalone` [mode](./deployment-modes.md#standalone) where you can specify the upstream nodes as follows: + +```yaml title="./conf/apisix.yaml" +routes: + - + uri: "/test" + upstream: + nodes: + "${{HOST_IP}}:${{PORT}}": 1 + type: roundrobin +#END +``` + +### Force Delete + +By default, the Admin API checks for references between resources and will refuse to delete resources in use. + +You can make a force deletion by adding the request argument `force=true` to the delete request, for example: + +```bash +$ curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{ + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" +}' +$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{ + "uri": "/*", + "upstream_id": 1 +}' +{"value":{"priority":0,"upstream_id":1,"uri":"/*","create_time":1689038794,"id":"1","status":1,"update_time":1689038916},"key":"/apisix/routes/1"} + +$ curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"error_msg":"can not delete this upstream, route [1] is still using it now"} +$ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1?force=anyvalue" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"error_msg":"can not delete this upstream, route [1] is still using it now"} +$ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1?force=true" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"deleted":"1","key":"/apisix/upstreams/1"} +``` + ## V3 new feature The Admin API has made some breaking changes in V3 version, as well as supporting additional features. @@ -237,6 +277,10 @@ curl 'http://127.0.0.1:9180/apisix/admin/routes?name=test&uri=foo&label=' \ Route resource request address: /apisix/admin/routes/{id}?ttl=0 +### Quick Note on ID Syntax + +ID's as a text string must be of a length between 1 and 64 characters and they should only contain uppercase, lowercase, numbers and no special characters apart from dashes ( - ), periods ( . ) and underscores ( _ ). For integer values they simply must have a minimum character count of 1. + ### Request Methods | Method | Request URI | Request Body | Description | @@ -831,6 +875,8 @@ An Upstream configuration can be directly bound to a Route or a Service, but the Upstream resource request address: /apisix/admin/upstreams/{id} +For notes on ID syntax please refer to: [ID Syntax](#quick-note-on-id-syntax) + ### Request Methods | Method | Request URI | Request Body | Description | @@ -1140,6 +1186,8 @@ Currently, the response is returned from etcd. SSL resource request address: /apisix/admin/ssls/{id} +For notes on ID syntax please refer to: [ID Syntax](#quick-note-on-id-syntax) + ### Request Methods | Method | Request URI | Request Body | Description | @@ -1167,6 +1215,7 @@ SSL resource request address: /apisix/admin/ssls/{id} | update_time | False | Auxiliary | Epoch timestamp (in seconds) of the updated time. If missing, this field will be populated automatically. | 1602883670 | | type | False | Auxiliary | Identifies the type of certificate, default `server`. | `client` Indicates that the certificate is a client certificate, which is used when APISIX accesses the upstream; `server` Indicates that the certificate is a server-side certificate, which is used by APISIX when verifying client requests. | | status | False | Auxiliary | Enables the current SSL. Set to `1` (enabled) by default. | `1` to enable, `0` to disable | +| ssl_protocols | False | An array of ssl protocols | It is used to control the SSL/TLS protocol version used between servers and clients. See [SSL Protocol](./ssl-protocol.md) for more examples. | `["TLSv1.2", "TLSv2.3"]` | Example Configuration: @@ -1318,6 +1367,14 @@ Plugin resource request address: /apisix/admin/plugins/{plugin_name} The Plugin ({plugin_name}) of the data structure. +### Request Arguments + +| Name | Description | Default | +| --------- | ----------------------------- | ------- | +| subsystem | The subsystem of the Plugins. | http | + +The plugin can be filtered on subsystem so that the ({plugin_name}) is searched in the subsystem passed through query params. + ### Example API usage: ```shell @@ -1330,7 +1387,7 @@ curl "http://127.0.0.1:9180/apisix/admin/plugins/list" \ ``` ```shell -curl "http://127.0.0.1:9180/apisix/admin/plugins/key-auth" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' +curl "http://127.0.0.1:9180/apisix/admin/plugins/key-auth?subsystem=http" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' ``` ```json @@ -1339,26 +1396,10 @@ curl "http://127.0.0.1:9180/apisix/admin/plugins/key-auth" -H 'X-API-KEY: ed :::tip -You can use the `/apisix/admin/plugins?all=true` API to get all properties of all plugins. - -Each Plugin has the attributes `name`, `priority`, `type`, `schema`, `consumer_schema` and `version`. - -Defaults to only L7 Plugins. If you need to get attributes of L4 / Stream Plugins, use `/apisix/admin/plugins?all=true&subsystem=stream`. +You can use the `/apisix/admin/plugins?all=true` API to get all properties of all plugins. This API will be deprecated soon. ::: -### Request Methods - -| Method | Request URI | Request Body | Description | -| ------ | ------------------------------ | ------------ | ---------------------------------------- | -| GET | /apisix/admin/plugins?all=true | NULL | Fetches all attributes from all Plugins. | - -### Request Arguments - -| Name | Description | Default | -| --------- | ----------------------------- | ------- | -| subsystem | The subsystem of the Plugins. | http | - ## Stream Route Route used in the [Stream Proxy](./stream-proxy.md). diff --git a/docs/en/latest/building-apisix.md b/docs/en/latest/building-apisix.md index ef5b70c019f7..01d4ac331240 100644 --- a/docs/en/latest/building-apisix.md +++ b/docs/en/latest/building-apisix.md @@ -52,7 +52,7 @@ curl https://raw.githubusercontent.com/apache/apisix/master/utils/install-depend Save the APISIX version to an environment variable to be used next: ```shell -APISIX_VERSION='3.3.0' +APISIX_VERSION='3.4.0' ``` Clone the APISIX source code of this version into a new directory `apisix-APISIX_VERSION`: diff --git a/docs/en/latest/certificate.md b/docs/en/latest/certificate.md index 4faf62ceb1ce..146a153fbe25 100644 --- a/docs/en/latest/certificate.md +++ b/docs/en/latest/certificate.md @@ -31,124 +31,121 @@ It is most common for an SSL certificate to contain only one domain. We can crea * `key`: PEM-encoded private key of the SSL key pair. * `snis`: Hostname(s) to associate with this certificate as SNIs. To set this attribute this certificate must have a valid private key associated with it. -We will use the Python script below to simplify the example: - -```python title="create-ssl.py" -#!/usr/bin/env python -# coding: utf-8 -import sys -# sudo pip install requests -import requests - -if len(sys.argv) <= 3: - print("bad argument") - sys.exit(1) -with open(sys.argv[1]) as f: - cert = f.read() -with open(sys.argv[2]) as f: - key = f.read() -sni = sys.argv[3] -api_key = "edd1c9f034335f136f87ad84b625c8f1" -resp = requests.put("http://127.0.0.1:9180/apisix/admin/ssls/1", json={ - "cert": cert, - "key": key, - "snis": [sni], -}, headers={ - "X-API-KEY": api_key, -}) -print(resp.status_code) -print(resp.text) -``` +The following is an example of configuring an SSL certificate with a single SNI in APISIX. + +Create an SSL object with the certificate and key valid for the SNI: ```shell -# create SSL object -./create-ssl.py t.crt t.key test.com +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat t/certs/apisix.crt)"'", + "key": "'"$(cat t/certs/apisix.key)"'", + "snis": ["test.com"] +}' +``` + +Create a Router object: -# create Router object +```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d ' { - "uri": "/hello", + "uri": "/get", "hosts": ["test.com"], "methods": ["GET"], "upstream": { "type": "roundrobin", "nodes": { - "127.0.0.1:1980": 1 + "httpbin.org": 1 } } }' +``` + +Send a request to verify: -# make a test +```shell +curl --resolve 'test.com:9443:127.0.0.1' https://test.com:9443/hello -k -vvv -curl --resolve 'test.com:9443:127.0.0.1' https://test.com:9443/hello -vvv * Added test.com:9443:127.0.0.1 to DNS cache * About to connect() to test.com port 9443 (#0) * Trying 127.0.0.1... * Connected to test.com (127.0.0.1) port 9443 (#0) -* Initializing NSS with certpath: sql:/etc/pki/nssdb -* skipping SSL peer certificate verification -* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 +* ALPN, server accepted to use h2 * Server certificate: -* subject: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN -* start date: Jun 24 22:18:05 2019 GMT -* expire date: May 31 22:18:05 2119 GMT -* common name: test.com -* issuer: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN -> GET /hello HTTP/1.1 -> User-Agent: curl/7.29.0 +* subject: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com +* start date: Jun 24 22:18:05 2019 GMT +* expire date: May 31 22:18:05 2119 GMT +* issuer: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com +* SSL certificate verify result: self-signed certificate (18), continuing anyway. +> GET /get HTTP/2 > Host: test.com:9443 -> Accept: */* +> user-agent: curl/7.81.0 +> accept: */* ``` ### wildcard SNI -Sometimes, one SSL certificate may contain a wildcard domain like `*.test.com`, -that means it can accept more than one domain, eg: `www.test.com` or `mail.test.com`. +An SSL certificate could also be valid for a wildcard domain like `*.test.com`, which means it is valid for any domain of that pattern, including `www.test.com` and `mail.test.com`. + +The following is an example of configuring an SSL certificate with a wildcard SNI in APISIX. -Here is an example, note that the value we pass as `sni` is `*.test.com`. +Create an SSL object with the certificate and key valid for the SNI: ```shell -./create-ssl.py t.crt t.key '*.test.com' +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ + -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' + { + "cert" : "'"$(cat t/certs/apisix.crt)"'", + "key": "'"$(cat t/certs/apisix.key)"'", + "snis": ["*.test.com"] + }' +``` + +Create a Router object: +```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d ' { - "uri": "/hello", + "uri": "/get", "hosts": ["*.test.com"], "methods": ["GET"], "upstream": { "type": "roundrobin", "nodes": { - "127.0.0.1:1980": 1 + "httpbin.org": 1 } } }' +``` -# make a test +Send a request to verify: -curl --resolve 'www.test.com:9443:127.0.0.1' https://www.test.com:9443/hello -vvv -* Added test.com:9443:127.0.0.1 to DNS cache -* About to connect() to test.com port 9443 (#0) -* Trying 127.0.0.1... -* Connected to test.com (127.0.0.1) port 9443 (#0) -* Initializing NSS with certpath: sql:/etc/pki/nssdb -* skipping SSL peer certificate verification -* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +```shell +curl --resolve 'www.test.com:9443:127.0.0.1' https://www.test.com:9443/get -k -vvv + +* Added www.test.com:9443:127.0.0.1 to DNS cache +* Hostname www.test.com was found in DNS cache +* Trying 127.0.0.1:9443... +* Connected to www.test.com (127.0.0.1) port 9443 (#0) +* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 +* ALPN, server accepted to use h2 * Server certificate: -* subject: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN -* start date: Jun 24 22:18:05 2019 GMT -* expire date: May 31 22:18:05 2119 GMT -* common name: test.com -* issuer: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN -> GET /hello HTTP/1.1 -> User-Agent: curl/7.29.0 -> Host: test.com:9443 -> Accept: */* +* subject: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com +* start date: Jun 24 22:18:05 2019 GMT +* expire date: May 31 22:18:05 2119 GMT +* issuer: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com +* SSL certificate verify result: self signed certificate (18), continuing anyway. +> GET /get HTTP/2 +> Host: www.test.com:9443 +> user-agent: curl/7.74.0 +> accept: */* ``` ### multiple domain -If your SSL certificate may contain more than one domain, like `www.test.com` -and `mail.test.com`, then you can add them into the `snis` array. For example: +If your SSL certificate may contain more than one domain, like `www.test.com` and `mail.test.com`, then you can add them into the `snis` array. For example: ```json { diff --git a/docs/en/latest/config.json b/docs/en/latest/config.json index 496a44ff98f1..ee172c33290c 100644 --- a/docs/en/latest/config.json +++ b/docs/en/latest/config.json @@ -1,5 +1,5 @@ { - "version": "3.3.0", + "version": "3.4.0", "sidebar": [ { "type": "category", @@ -26,11 +26,18 @@ "items": [ "tutorials/expose-api", "tutorials/protect-api", - "tutorials/observe-your-api", + { + "type": "category", + "label": "Observability", + "items": [ + "tutorials/observe-your-api", + "tutorials/health-check", + "tutorials/monitor-api-health-check" + ] + }, "tutorials/manage-api-consumers", "tutorials/cache-api-responses", "tutorials/add-multiple-api-versions", - "tutorials/health-check", "tutorials/client-to-apisix-mtls", "tutorials/websocket-authentication" ] @@ -119,7 +126,8 @@ "plugins/consumer-restriction", "plugins/csrf", "plugins/public-api", - "plugins/gm" + "plugins/gm", + "plugins/chaitin-waf" ] }, { @@ -359,6 +367,10 @@ { "type": "doc", "id": "profile" + }, + { + "type": "doc", + "id": "ssl-protocol" } ] }, diff --git a/docs/en/latest/control-api.md b/docs/en/latest/control-api.md index a068d4411fb3..e21ca86d502d 100644 --- a/docs/en/latest/control-api.md +++ b/docs/en/latest/control-api.md @@ -210,7 +210,6 @@ Returns all configured [Routes](./terminology/route.md): ```json [ { - "update_count": 0, "value": { "priority": 0, "uris": [ @@ -249,7 +248,6 @@ Returns the Route with the specified `route_id`: ```json { - "update_count": 0, "value": { "priority": 0, "uris": [ diff --git a/docs/en/latest/debug-mode.md b/docs/en/latest/debug-mode.md index 85be8419c070..b933dfd88868 100644 --- a/docs/en/latest/debug-mode.md +++ b/docs/en/latest/debug-mode.md @@ -46,7 +46,7 @@ For APISIX releases prior to v2.10, basic debug mode is enabled by setting `apis ::: -If you have configured two Plgins `limit-conn` and `limit-count` on the Route `/hello`, you will receive a response with the header `Apisix-Plugins: limit-conn, limit-count` when you enable the basic debug mode. +If you have configured two Plugins `limit-conn` and `limit-count` on the Route `/hello`, you will receive a response with the header `Apisix-Plugins: limit-conn, limit-count` when you enable the basic debug mode. ```shell curl http://127.0.0.1:1984/hello -i diff --git a/docs/en/latest/deployment-modes.md b/docs/en/latest/deployment-modes.md index e5c7b9175cd2..9f75a1d99c06 100644 --- a/docs/en/latest/deployment-modes.md +++ b/docs/en/latest/deployment-modes.md @@ -97,9 +97,9 @@ deployment: prefix: /apisix timeout: 30 certs: - cert: /path/to/ca-cert - cert_key: /path/to/ca-cert - trusted_ca_cert: /path/to/ca-cert + cert: /path/to/client.crt + cert_key: /path/to/client.key + trusted_ca_cert: /path/to/ca.crt #END ``` @@ -117,18 +117,18 @@ deployment: config_provider: etcd conf_server: listen: 0.0.0.0:9280 - cert: /path/to/ca-cert - cert_key: /path/to/ca-cert - client_ca_cert: /path/to/ca-cert + cert: /path/to/server.crt + cert_key: /path/to/server.key + client_ca_cert: /path/to/ca.crt etcd: host: - https://${etcd_IP}:${etcd_Port} prefix: /apisix timeout: 30 certs: - cert: /path/to/ca-cert - cert_key: /path/to/ca-cert - trusted_ca_cert: /path/to/ca-cert + cert: /path/to/client.crt + cert_key: /path/to/client.key + trusted_ca_cert: /path/to/ca.crt #END ``` @@ -143,15 +143,15 @@ deployment: config_provider: etcd conf_server: listen: 0.0.0.0:9280 - cert: /path/to/ca-cert - cert_key: /path/to/ca-cert + cert: /path/to/server.crt + cert_key: /path/to/server.key etcd: host: - https://${etcd_IP}:${etcd_Port} prefix: /apisix timeout: 30 certs: - trusted_ca_cert: /path/to/ca-cert + trusted_ca_cert: /path/to/ca.crt #END ``` @@ -434,3 +434,25 @@ upstreams: id: 1 #END ``` + +### How to configure protos + +```yaml +protos: + - id: helloworld + desc: hello world + content: > + syntax = "proto3"; + package helloworld; + + service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply) {} + } + message HelloRequest { + string name = 1; + } + message HelloReply { + string message = 1; + } +#END +``` diff --git a/docs/en/latest/discovery/kubernetes.md b/docs/en/latest/discovery/kubernetes.md index 0735471b8145..fb11aac676d1 100644 --- a/docs/en/latest/discovery/kubernetes.md +++ b/docs/en/latest/discovery/kubernetes.md @@ -34,12 +34,6 @@ The [_Kubernetes_](https://kubernetes.io/) service discovery [_List-Watch_](http Discovery also provides a node query interface in accordance with the [_APISIX Discovery Specification_](../discovery.md). -:::note - -use kubernetes discovery in L4 require OpenResty version >= 1.19.9.1 - -::: - ## How To Use Kubernetes service discovery both support single-cluster and multi-cluster modes, applicable to the case where the service is distributed in single or multiple Kubernetes clusters. diff --git a/docs/en/latest/plugins/api-breaker.md b/docs/en/latest/plugins/api-breaker.md index c0d840bba72e..64b80c97657d 100644 --- a/docs/en/latest/plugins/api-breaker.md +++ b/docs/en/latest/plugins/api-breaker.md @@ -53,7 +53,7 @@ In an unhealthy state, if the Upstream service responds with a status code from | healthy.http_statuses | array[integer] | False | [200] | [200, ..., 499] | Status codes of Upstream to be considered healthy. | | healthy.successes | integer | False | 3 | >=1 | Number of consecutive healthy requests for the Upstream service to be considered healthy. | -## Enabling the Plugin +## Enable Plugin The example below shows how you can configure the Plugin on a specific Route: @@ -108,9 +108,9 @@ HTTP/1.1 502 Bad Gateway ``` -## Disable Plugin +## Delete Plugin -To disable the `api-breaker` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `api-breaker` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/en/latest/plugins/authz-casbin.md b/docs/en/latest/plugins/authz-casbin.md index c59ee9cd3f6e..b405cb4dd396 100644 --- a/docs/en/latest/plugins/authz-casbin.md +++ b/docs/en/latest/plugins/authz-casbin.md @@ -57,7 +57,7 @@ If you wish to use a global Casbin configuration, you can first specify `model` | model | string | True | Casbin model configuration in text format. | | policy | string | True | Casbin policy in text format. | -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a Route by either using the model/policy file paths or using the model/policy text in Plugin configuration/metadata. @@ -234,9 +234,9 @@ And only users with admin privileges can access the endpoints: curl -i http://127.0.0.1:9080/res -H 'user: alice' -X GET ``` -## Disable Plugin +## Delete Plugin -To disable the `authz-casbin` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `authz-casbin` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/authz-casdoor.md b/docs/en/latest/plugins/authz-casdoor.md index 5de302552ae5..7a8985212f15 100644 --- a/docs/en/latest/plugins/authz-casdoor.md +++ b/docs/en/latest/plugins/authz-casdoor.md @@ -55,7 +55,7 @@ The `callback_url` must belong to the URI of your Route. See the code snippet be ::: -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a specific Route as shown below: @@ -89,9 +89,9 @@ After successfully logging in, Casdoor will redirect this user to the `callback_ Once this is done, the user is redirected to the original URL they wanted to visit. -## Disable Plugin +## Delete Plugin -To disable the `authz-casdoor` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `authz-casdoor` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/authz-keycloak.md b/docs/en/latest/plugins/authz-keycloak.md index 32c6049c6efe..d656e7095ea3 100644 --- a/docs/en/latest/plugins/authz-keycloak.md +++ b/docs/en/latest/plugins/authz-keycloak.md @@ -143,7 +143,7 @@ curl --location --request POST 'http://127.0.0.1:9080/api/token' \ --data-urlencode 'password=' ``` -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the `authz-keycloak` Plugin on a specific Route. `${realm}` represents the realm name in Keycloak. @@ -200,9 +200,9 @@ The image below shows how the policies are configured in the Keycloak server: ![Keycloak policy design](https://raw.githubusercontent.com/apache/apisix/master/docs/assets/images/plugin/authz-keycloak.png) -## Disable Plugin +## Delete Plugin -To disable the `authz-keycloak` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `authz-keycloak` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/5 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/aws-lambda.md b/docs/en/latest/plugins/aws-lambda.md index cc24bbd9347a..0281c8e3189d 100644 --- a/docs/en/latest/plugins/aws-lambda.md +++ b/docs/en/latest/plugins/aws-lambda.md @@ -58,7 +58,7 @@ This Plugin supports authorization via AWS API key and AWS IAM secrets. | aws_region | string | False | "us-east-1" | AWS region where the request is being sent. | | service | string | False | "execute-api" | The service that is receiving the request. For HTTP trigger, it is `"execute-api"`. | -## Enabling the Plugin +## Enable Plugin The example below shows how you can configure the Plugin on a specific Route: @@ -194,9 +194,9 @@ Server: APISIX/2.11.0 "Hello, APISIX!" ``` -## Disable Plugin +## Delete Plugin -To disable the `aws-lambda` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `aws-lambda` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/azure-functions.md b/docs/en/latest/plugins/azure-functions.md index 22a006546e81..22f531ba0c65 100644 --- a/docs/en/latest/plugins/azure-functions.md +++ b/docs/en/latest/plugins/azure-functions.md @@ -71,7 +71,7 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/azure-functions -H 'X-AP }' ``` -## Enabling the Plugin +## Enable Plugin You can configure the Plugin on a specific Route as shown below assuming that you already have your Azure Functions up and running: @@ -171,9 +171,9 @@ Server: APISIX/2.11.0 Hello, APISIX ``` -## Disable Plugin +## Delete Plugin -To disable the `azure-functions` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `azure-functions` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/basic-auth.md b/docs/en/latest/plugins/basic-auth.md index 49658bc2b69a..997bc610a939 100644 --- a/docs/en/latest/plugins/basic-auth.md +++ b/docs/en/latest/plugins/basic-auth.md @@ -51,7 +51,7 @@ For Route: |------------------|---------|----------|---------|------------------------------------------------------------------------| | hide_credentials | boolean | False | false | Set to true will not pass the authorization request headers to the Upstream. | -## Enabling the Plugin +## Enable Plugin To enable the Plugin, you have to create a Consumer object with the authentication configuration: @@ -125,9 +125,9 @@ HTTP/1.1 401 Unauthorized {"message":"Invalid user authorization"} ``` -## Disable Plugin +## Delete Plugin -To disable the `jwt-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `jwt-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/batch-requests.md b/docs/en/latest/plugins/batch-requests.md index 21c28d425243..9c7daba6ea8c 100644 --- a/docs/en/latest/plugins/batch-requests.md +++ b/docs/en/latest/plugins/batch-requests.md @@ -57,7 +57,7 @@ You may need to use the [public-api](public-api.md) plugin to expose this endpoi ::: -## Enabling the Plugin +## Enable Plugin You can enable the `batch-requests` Plugin by adding it to your configuration file (`conf/config.yaml`): @@ -211,6 +211,6 @@ This will give a response: ] ``` -## Disable Plugin +## Delete Plugin You can remove `batch-requests` from your list of Plugins in your configuration file (`conf/config.yaml`). diff --git a/docs/en/latest/plugins/body-transformer.md b/docs/en/latest/plugins/body-transformer.md index 0fed5e01f5fe..650e59adb42c 100644 --- a/docs/en/latest/plugins/body-transformer.md +++ b/docs/en/latest/plugins/body-transformer.md @@ -49,7 +49,7 @@ Use cases: | `response.input_format` | string | False | response body original format, if not specified, it would be determined from `Content-Type` header. | | `response.template` | string | True | response body transformation template | -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a specific Route as shown below: @@ -257,9 +257,9 @@ curl -s http://127.0.0.1:9080/ws -H 'content-type: application/json' -X POST -d } ``` -## Disable Plugin +## Delete Plugin -To disable the `body-transformer` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `body-transformer` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/test_ws \ diff --git a/docs/en/latest/plugins/cas-auth.md b/docs/en/latest/plugins/cas-auth.md index f104d1f623ba..08173ac9716d 100644 --- a/docs/en/latest/plugins/cas-auth.md +++ b/docs/en/latest/plugins/cas-auth.md @@ -41,7 +41,7 @@ to do authentication, from the SP (service provider) perspective. | `cas_callback_uri` | string | True | redirect uri used to callback the SP from IdP after login or logout. | | `logout_uri` | string | True | logout uri to trigger logout. | -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a specific Route as shown below: @@ -88,9 +88,9 @@ or path only (e.g. `/anything/logout`), but it is recommended to be path only to These uris need to be captured by the route where the current APISIX is located. For example, if the `uri` of the current route is `/api/v1/*`, `cas_callback_uri` can be filled in as `/api/v1/cas_callback`. -## Disable Plugin +## Delete Plugin -To disable the `cas-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `cas-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/cas1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/chaitin-waf.md b/docs/en/latest/plugins/chaitin-waf.md new file mode 100644 index 000000000000..14966c4ea558 --- /dev/null +++ b/docs/en/latest/plugins/chaitin-waf.md @@ -0,0 +1,255 @@ +--- +title: chaitin-waf +keywords: + - Apache APISIX + - API Gateway + - Plugin + - WAF +description: This document contains basic information about the Apache APISIX `chaitin-waf` plugin. +--- + + + +## Description + +After enabling the chaitin-waf plugin, the traffic will be forwarded to the Chaitin WAF service for the detection and +prevention of various web application attacks, ensuring the security of the application and user data. + +## Response Headers + +Depending on the plugin configuration, it is optional to add additional response headers. + +The response headers are listed below: + +- **X-APISIX-CHAITIN-WAF**: Whether APISIX forwards the request to the WAF server. + - yes: forwarded + - no: no forwarded + - unhealthy: matches the match variables, but no WAF server is available. + - err: an error occurred during the execution of the plugin. Also with **X-APISIX-CHAITIN-WAF-ERROR** request header + - waf-err: Error while interacting with the WAF server. Also with **X-APISIX-CHAITIN-WAF-ERROR** request header + - timeout: Timeout for request to the WAF server +- **X-APISIX-CHAITIN-WAF-ERROR**: Debug header. WAF error message +- **X-APISIX-CHAITIN-WAF-TIME**: The time in milliseconds that APISIX spent interacting with WAF. +- **X-APISIX-CHAITIN-WAF-STATUS**: The status code returned to APISIX by the WAF server. +- **X-APISIX-CHAITIN-WAF-ACTION**: Processing result returned to APISIX by the WAF server. + - pass: request valid and passed + - reject: request rejected by WAF service +- **X-APISIX-CHAITIN-WAF-SERVER**: Debug header. Picked WAF server. + +## Plugin Metadata + +| Name | Type | Required | Default value | Description | +|--------------------------|---------------|----------|---------------|------------------------------------------------------------------------------------------------------------------------------| +| nodes | array(object) | true | | A list of addresses for the Chaitin SafeLine WAF service. | +| nodes[0].host | string | true | | The address of Chaitin SafeLine WAF service. Supports IPV4, IPV6, Unix Socket, etc. | +| nodes[0].port | string | false | 80 | The port of Chaitin SafeLine WAF service. | +| config | object | false | | Configuration of the Chaitin SafeLine WAF service. The parameters configured here will be used when route is not configured. | +| config.connect_timeout | integer | false | 1000 | connect timeout, in milliseconds | +| config.send_timeout | integer | false | 1000 | send timeout, in milliseconds | +| config.read_timeout | integer | false | 1000 | read timeout, in milliseconds | +| config.req_body_size | integer | false | 1024 | request body size, in KB | +| config.keepalive_size | integer | false | 256 | maximum concurrent idle connections to the SafeLine WAF detection service | +| config.keepalive_timeout | integer | false | 60000 | idle connection timeout, in milliseconds | + +An example configuration is as follows. + +```bash +curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/chaitin-waf -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "nodes":[ + { + "host": "unix:/path/to/safeline/resources/detector/snserver.sock", + "port": 8000 + } + ] +}' +``` + +## Attributes + +| Name | Type | Required | Default value | Description | +|--------------------------|---------------|----------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| match | array[object] | false | | The list of matching rules, default is empty | +| match.vars | array[array] | false | | List of variables to match for filtering requests for conditional traffic split. It is in the format `{variable operator value}`. For example, `{"arg_name", "==", "json"}`. The variables here are consistent with NGINX internal variables. For details on supported operators, [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list). | +| append_waf_resp_header | bool | false | true | Whether to add response headers | +| append_waf_debug_header | bool | false | false | Whether or not to add debugging headers, effective when `add_header` is `true`. | +| config | object | false | | Configuration of the Chaitin SafeLine WAF service. When the route is not configured, the parameters configured in the metadata are used. | +| config.connect_timeout | integer | false | | connect timeout, in milliseconds | +| config.send_timeout | integer | false | | send timeout, in milliseconds | +| config.read_timeout | integer | false | | read timeout, in milliseconds | +| config.req_body_size | integer | false | | request body size, in KB | +| config.keepalive_size | integer | false | | maximum concurrent idle connections to the SafeLine WAF detection service | +| config.keepalive_timeout | integer | false | | idle connection timeout, in milliseconds | + +A sample configuration is shown below, using `httpbun.org` as the example backend, which can be replaced as needed: + +```bash +curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri": "/*", + "plugins": { + "chaitin-waf": { + "match": [ + { + "vars": [ + ["http_waf","==","true"] + ] + } + ] + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "httpbun.org:80": 1 + } + } +}' +``` + +## Test Plugin + +Test the above example configuration. + +If the match condition is not met, the request can be reached normally: + +```bash +curl -H "Host: httpbun.org" http://127.0.0.1:9080/get -i + +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 408 +Connection: keep-alive +X-APISIX-CHAITIN-WAF: no +Date: Wed, 19 Jul 2023 09:30:42 GMT +X-Powered-By: httpbun/3c0dc05883dd9212ac38b04705037d50b02f2596 +Server: APISIX/3.3.0 + +{ + "args": {}, + "headers": { + "Accept": "*/*", + "Connection": "close", + "Host": "httpbun.org", + "User-Agent": "curl/8.1.2", + "X-Forwarded-For": "127.0.0.1", + "X-Forwarded-Host": "httpbun.org", + "X-Forwarded-Port": "9080", + "X-Forwarded-Proto": "http", + "X-Real-Ip": "127.0.0.1" + }, + "method": "GET", + "origin": "127.0.0.1, 122.231.76.178", + "url": "http://httpbun.org/get" +} +``` + +Potential injection requests are also forwarded as is and encounter a 404 error: + +```bash +curl -H "Host: httpbun.org" http://127.0.0.1:9080/getid=1%20AND%201=1 -i + +HTTP/1.1 404 Not Found +Content-Type: text/plain; charset=utf-8 +Content-Length: 19 +Connection: keep-alive +X-APISIX-CHAITIN-WAF: no +Date: Wed, 19 Jul 2023 09:30:28 GMT +X-Content-Type-Options: nosniff +X-Powered-By: httpbun/3c0dc05883dd9212ac38b04705037d50b02f2596 +Server: APISIX/3.3.0 + +404 page not found +``` + +Normal requests are still reachable when the match condition is met: + +```bash +curl -H "Host: httpbun.org" -H "waf: true" http://127.0.0.1:9080/get -i + +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 427 +Connection: keep-alive +X-APISIX-CHAITIN-WAF-TIME: 2 +X-APISIX-CHAITIN-WAF-STATUS: 200 +X-APISIX-CHAITIN-WAF: yes +X-APISIX-CHAITIN-WAF-ACTION: pass +Date: Wed, 19 Jul 2023 09:29:58 GMT +X-Powered-By: httpbun/3c0dc05883dd9212ac38b04705037d50b02f2596 +Server: APISIX/3.3.0 + +{ + "args": {}, + "headers": { + "Accept": "*/*", + "Connection": "close", + "Host": "httpbun.org", + "User-Agent": "curl/8.1.2", + "Waf": "true", + "X-Forwarded-For": "127.0.0.1", + "X-Forwarded-Host": "httpbun.org", + "X-Forwarded-Port": "9080", + "X-Forwarded-Proto": "http", + "X-Real-Ip": "127.0.0.1" + }, + "method": "GET", + "origin": "127.0.0.1, 122.231.76.178", + "url": "http://httpbun.org/get" +} +``` + +Potential attack requests will be intercepted and returned a 403 error: + +```bash +curl -H "Host: httpbun.org" -H "waf: true" http://127.0.0.1:9080/getid=1%20AND%201=1 -i + +HTTP/1.1 403 Forbidden +Date: Wed, 19 Jul 2023 09:29:06 GMT +Content-Type: text/plain; charset=utf-8 +Transfer-Encoding: chunked +Connection: keep-alive +X-APISIX-CHAITIN-WAF: yes +X-APISIX-CHAITIN-WAF-TIME: 2 +X-APISIX-CHAITIN-WAF-ACTION: reject +X-APISIX-CHAITIN-WAF-STATUS: 403 +Server: APISIX/3.3.0 +Set-Cookie: sl-session=UdywdGL+uGS7q8xMfnJlbQ==; Domain=; Path=/; Max-Age=86400 + +{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "51a268653f2c4189bfa3ec66afbcb26d"} +``` + +## Delete Plugin + +To remove the `chaitin-waf` plugin, you can delete the corresponding JSON configuration from the Plugin configuration. +APISIX will automatically reload and you do not have to restart for this to take effect: + +```bash +$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri": "/*", + "upstream": { + "type": "roundrobin", + "nodes": { + "httpbun.org:80": 1 + } + } +}' +``` diff --git a/docs/en/latest/plugins/clickhouse-logger.md b/docs/en/latest/plugins/clickhouse-logger.md index 16a59b2ea73f..2f3a66e0c6fb 100644 --- a/docs/en/latest/plugins/clickhouse-logger.md +++ b/docs/en/latest/plugins/clickhouse-logger.md @@ -93,7 +93,7 @@ Then create a table in your ClickHouse database to store the logs. echo "CREATE TABLE default.test (\`host\` String, \`client_ip\` String, \`route_id\` String, \`service_id\` String, \`@timestamp\` String, PRIMARY KEY(\`@timestamp\`)) ENGINE = MergeTree()" | curl 'http://localhost:8123/' ``` -## Enabling the Plugin +## Enable Plugin If multiple endpoints are configured, they will be written randomly. The example below shows how you can enable the Plugin on a specific Route: @@ -135,9 +135,9 @@ curl 'http://localhost:8123/?query=select%20*%20from%20default.test' 127.0.0.1 127.0.0.1 1 2023-05-08T19:15:53+05:30 ``` -## Disable Plugin +## Delete Plugin -To disable the `clickhouse-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `clickhouse-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/client-control.md b/docs/en/latest/plugins/client-control.md index 6aeb82abb393..fd226f7b2ba1 100644 --- a/docs/en/latest/plugins/client-control.md +++ b/docs/en/latest/plugins/client-control.md @@ -42,7 +42,7 @@ This Plugin requires APISIX to run on APISIX-Base. See [apisix-build-tools](http | ------------- | ------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------ | | max_body_size | integer | False | [0,...] | Dynamically set the [`client_max_body_size`](https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size) directive. | -## Enabling the Plugin +## Enable Plugin The example below enables the Plugin on a specific Route: @@ -85,9 +85,9 @@ HTTP/1.1 413 Request Entity Too Large ``` -## Disable Plugin +## Delete Plugin -To disable the `client-control` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload, and you do not have to restart for this to take effect. +To remove the `client-control` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload, and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/en/latest/plugins/consumer-restriction.md b/docs/en/latest/plugins/consumer-restriction.md index a3e4f5291c55..7df4d0634784 100644 --- a/docs/en/latest/plugins/consumer-restriction.md +++ b/docs/en/latest/plugins/consumer-restriction.md @@ -32,14 +32,16 @@ The `consumer-restriction` Plugin allows users to configure access restrictions ## Attributes -| Name | Type | Required | Default | Valid values | Description | -|--------------------|---------------|----------|---------------|---------------|-------------| -| type | string | False | consumer_name | ["consumer_name", "consumer_group_id", "service_id", "route_id"] | Type of object to base the restriction on. | -| whitelist | array[string] | True | | | List of objects to whitelist. Has a higher priority than `allowed_by_methods`. | -| blacklist | array[string] | True | | | List of objects to blacklist. Has a higher priority than `whitelist`. | -| rejected_code | integer | False | 403 | [200,...] | HTTP status code returned when the request is rejected. | -| rejected_msg | string | False | | | Message returned when the request is rejected. | -| allowed_by_methods | array[object] | False | | ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE", "PURGE"] | List of allowed HTTP methods for a Consumer. | +| Name | Type | Required | Default | Valid values | Description | +| -------------------------- | ------------- | -------- | ------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| type | string | False | consumer_name | ["consumer_name", "consumer_group_id", "service_id", "route_id"] | Type of object to base the restriction on. | +| whitelist | array[string] | True | | | List of objects to whitelist. Has a higher priority than `allowed_by_methods`. | +| blacklist | array[string] | True | | | List of objects to blacklist. Has a higher priority than `whitelist`. | +| rejected_code | integer | False | 403 | [200,...] | HTTP status code returned when the request is rejected. | +| rejected_msg | string | False | | | Message returned when the request is rejected. | +| allowed_by_methods | array[object] | False | | | List of allowed configurations for Consumer settings, including a username of the Consumer and a list of allowed HTTP methods. | +| allowed_by_methods.user | string | False | | | A username for a Consumer. | +| allowed_by_methods.methods | array[string] | False | | ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE", "PURGE"] | List of allowed HTTP methods for a Consumer. | :::note @@ -315,9 +317,9 @@ HTTP/1.1 403 Forbidden {"message":"The service_id is forbidden."} ``` -## Disable Plugin +## Delete Plugin -To disable the `consumer-restriction` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `consumer-restriction` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/cors.md b/docs/en/latest/plugins/cors.md index fc1e3e326835..7d46c7a5a675 100644 --- a/docs/en/latest/plugins/cors.md +++ b/docs/en/latest/plugins/cors.md @@ -56,7 +56,7 @@ The `cors` Plugins lets you enable [CORS](https://developer.mozilla.org/en-US/do |---------------|--------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | allow_origins | object | False | A map with origin reference and allowed origins. The keys in the map are used in the attribute `allow_origins_by_metadata` and the value are equivalent to the `allow_origins` attribute of the Plugin. | -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a specific Route or Service: @@ -95,9 +95,9 @@ curl http://127.0.0.1:9080/hello -v ... ``` -## Disable Plugin +## Delete Plugin -To disable the `cors` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `cors` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/csrf.md b/docs/en/latest/plugins/csrf.md index 92e16a010de9..7211fcd99284 100644 --- a/docs/en/latest/plugins/csrf.md +++ b/docs/en/latest/plugins/csrf.md @@ -44,7 +44,7 @@ This Plugin considers the `GET`, `HEAD` and `OPTIONS` methods to be safe operati NOTE: `encrypt_fields = {"key"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields). -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the Plugin on a specific Route: @@ -127,9 +127,9 @@ curl -i http://127.0.0.1:9080/hello -X POST -H 'apisix-csrf-token: eyJyYW5kb20iO HTTP/1.1 200 OK ``` -## Disable Plugin +## Delete Plugin -To disable the `csrf` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `csrf` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/datadog.md b/docs/en/latest/plugins/datadog.md index 56bce9b98729..d5ca42ccf2c6 100644 --- a/docs/en/latest/plugins/datadog.md +++ b/docs/en/latest/plugins/datadog.md @@ -113,7 +113,7 @@ If there are no suitable values for any particular tag, the tag will be omitted. ::: -## Enabling the Plugin +## Enable Plugin Once you have your Datadog agent running, you can enable the Plugin as shown below: @@ -135,9 +135,9 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 Now, requests to the endpoint `/hello` will generate metrics and push it to the DogStatsD server. -## Disable Plugin +## Delete Plugin -To disable the `datadog` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `datadog` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/degraphql.md b/docs/en/latest/plugins/degraphql.md index b4d15dd23246..b0eaaf83bf05 100644 --- a/docs/en/latest/plugins/degraphql.md +++ b/docs/en/latest/plugins/degraphql.md @@ -57,7 +57,7 @@ After starting the server, the following endpoints are now available: - http://localhost:8080/ - A simple reacter - ws://localhost:8080/subscriptions -### Enabling the Plugin +### Enable Plugin #### Query list @@ -308,9 +308,9 @@ curl 'http://localhost:9080/graphql?name=Niek&githubAccount=npalm' In the GET request, the variables are passed in the query string. -## Disable Plugin +## Delete Plugin -To disable the `degraphql` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `degraphql` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/dubbo-proxy.md b/docs/en/latest/plugins/dubbo-proxy.md index a53e41ac920b..90459040c465 100644 --- a/docs/en/latest/plugins/dubbo-proxy.md +++ b/docs/en/latest/plugins/dubbo-proxy.md @@ -52,7 +52,7 @@ If you are using OpenResty, you need to build it with Dubbo support. See [How do | ------------------------ | ------ | -------- | ------- | ------------ | --------------------------------------------------------------- | | upstream_multiplex_count | number | True | 32 | >= 1 | Maximum number of multiplex requests in an upstream connection. | -## Enabling the Plugin +## Enable Plugin To enable the `dubbo-proxy` Plugin, you have to add it in your configuration file (`conf/config.yaml`): @@ -118,9 +118,9 @@ header2: value2 body of the message ``` -## Disable Plugin +## Delete Plugin -To disable the `dubbo-proxy` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `dubbo-proxy` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/echo.md b/docs/en/latest/plugins/echo.md index f4b872dffe53..f4babdfe4c71 100644 --- a/docs/en/latest/plugins/echo.md +++ b/docs/en/latest/plugins/echo.md @@ -50,7 +50,7 @@ The `echo` Plugin is built as an example. It has missing cases and should **not* At least one of `before_body`, `body`, and `after_body` must be specified. -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the `echo` Plugin for a specific Route: @@ -86,9 +86,9 @@ HTTP/1.1 200 OK before the body modification hello world ``` -## Disable Plugin +## Delete Plugin -To disable the `echo` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `echo` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/en/latest/plugins/elasticsearch-logger.md b/docs/en/latest/plugins/elasticsearch-logger.md index 4242a5cc9682..f6e37671afa3 100644 --- a/docs/en/latest/plugins/elasticsearch-logger.md +++ b/docs/en/latest/plugins/elasticsearch-logger.md @@ -53,7 +53,7 @@ NOTE: `encrypt_fields = {"auth.password"}` is also defined in the schema, which This Plugin supports using batch processors to aggregate and process entries (logs/data) in a batch. This avoids the need for frequently submitting the data. The batch processor submits data every `5` seconds or when the data in the queue reaches `1000`. See [Batch Processor](../batch-processor.md#configuration) for more information or setting your custom configuration. -## Enabling the Plugin +## Enable Plugin ### Full configuration @@ -271,9 +271,9 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/elasticsearch-logger \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE ``` -## Disable Plugin +## Delete Plugin -To disable the `elasticsearch-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `elasticsearch-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/en/latest/plugins/error-log-logger.md b/docs/en/latest/plugins/error-log-logger.md index 1686aa8148f0..889ea7f25d8e 100644 --- a/docs/en/latest/plugins/error-log-logger.md +++ b/docs/en/latest/plugins/error-log-logger.md @@ -69,7 +69,7 @@ NOTE: `encrypt_fields = {"clickhouse.password"}` is also defined in the schema, This Plugin supports using batch processors to aggregate and process entries (logs/data) in a batch. This avoids the need for frequently submitting the data. The batch processor submits data every `5` seconds or when the data in the queue reaches `1000`. See [Batch Processor](../batch-processor.md#configuration) for more information or setting your custom configuration. -## Enabling the Plugin +## Enable Plugin To enable the Plugin, you can add it in your configuration file (`conf/config.yaml`): @@ -153,9 +153,9 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/error-log-logger \ }' ``` -## Disable Plugin +## Delete Plugin -To disable the Plugin, you can remove it from your configuration file (`conf/config.yaml`): +To remove the Plugin, you can remove it from your configuration file (`conf/config.yaml`): ```yaml title="conf/config.yaml" plugins: diff --git a/docs/en/latest/plugins/ext-plugin-post-resp.md b/docs/en/latest/plugins/ext-plugin-post-resp.md index 20dc10f0ca8b..1692eacb79ab 100644 --- a/docs/en/latest/plugins/ext-plugin-post-resp.md +++ b/docs/en/latest/plugins/ext-plugin-post-resp.md @@ -55,7 +55,7 @@ Execution of External Plugins will affect the response of the current request. | conf | array | False | | [{"name": "ext-plugin-A", "value": "{\"enable\":\"feature\"}"}] | List of Plugins and their configurations to be executed on the Plugin Runner. | | allow_degradation | boolean | False | false | | Sets Plugin degradation when the Plugin Runner is not available. When set to `true`, requests are allowed to continue. | -## Enabling the Plugin +## Enable Plugin The example below enables the `ext-plugin-post-resp` Plugin on a specific Route: @@ -89,9 +89,9 @@ curl -i http://127.0.0.1:9080/index.html This will reach the configured Plugin Runner and the `ext-plugin-A` will be executed. -## Disable Plugin +## Delete Plugin -To disable the `ext-plugin-post-resp` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `ext-plugin-post-resp` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/ext-plugin-pre-req.md b/docs/en/latest/plugins/ext-plugin-pre-req.md index 4fc84b93daa1..766713ab33f4 100644 --- a/docs/en/latest/plugins/ext-plugin-pre-req.md +++ b/docs/en/latest/plugins/ext-plugin-pre-req.md @@ -46,7 +46,7 @@ Execution of External Plugins will affect the behavior of the current request. | conf | array | False | | [{"name": "ext-plugin-A", "value": "{\"enable\":\"feature\"}"}] | List of Plugins and their configurations to be executed on the Plugin Runner. | | allow_degradation | boolean | False | false | | Sets Plugin degradation when the Plugin Runner is not available. When set to `true`, requests are allowed to continue. | -## Enabling the Plugin +## Enable Plugin The example below enables the `ext-plugin-pre-req` Plugin on a specific Route: @@ -80,9 +80,9 @@ curl -i http://127.0.0.1:9080/index.html This will reach the configured Plugin Runner and the `ext-plugin-A` will be executed. -## Disable Plugin +## Delete Plugin -To disable the `ext-plugin-pre-req` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `ext-plugin-pre-req` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/fault-injection.md b/docs/en/latest/plugins/fault-injection.md index 58dc53d01bd0..11edcb5afcf4 100644 --- a/docs/en/latest/plugins/fault-injection.md +++ b/docs/en/latest/plugins/fault-injection.md @@ -75,7 +75,7 @@ This means that the relationship between the first two expressions is AND, and t ::: -## Enabling the Plugin +## Enable Plugin You can enable the `fault-injection` Plugin on a specific Route as shown below: @@ -265,9 +265,9 @@ Server: APISIX/2.2 Fault Injection! ``` -## Disable Plugin +## Delete Plugin -To disable the `fault-injection` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `fault-injection` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/file-logger.md b/docs/en/latest/plugins/file-logger.md index 9260cf72afe5..f335a7acb567 100644 --- a/docs/en/latest/plugins/file-logger.md +++ b/docs/en/latest/plugins/file-logger.md @@ -49,6 +49,7 @@ The `file-logger` Plugin is used to push log streams to a specific location. | log_format | object | False | Log format declared as key value pairs in JSON format. Values only support strings. [APISIX](../apisix-variable.md) or [Nginx](http://nginx.org/en/docs/varindex.html) variables can be used by prefixing the string with `$`. | | include_resp_body | boolean | False | When set to `true` includes the response body in the log file. | | include_resp_body_expr | array | False | When the `include_resp_body` attribute is set to `true`, use this to filter based on [lua-resty-expr](https://github.com/api7/lua-resty-expr). If present, only logs the response into file if the expression evaluates to `true`. | +| match | array[] | False | Logs will be recorded when the rule matching is successful if the option is set. See [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list) for a list of available expressions. | ## Metadata @@ -78,7 +79,7 @@ With this configuration, your logs would be formatted as shown below: {"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","route_id":"1"} ``` -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the Plugin on a specific Route: @@ -110,9 +111,49 @@ curl -i http://127.0.0.1:9080/hello You will be able to find the `file.log` file in the configured `logs` directory. -## Disable Plugin +## Filter logs -To disable the `file-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +```shell +curl http://127.0.0.1:9180/apisix/admin/routes/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "plugins": { + "file-logger": { + "path": "logs/file.log", + "match": { + { + { "arg_name","==","jack" } + } + } + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:9001": 1 + } + }, + "uri": "/hello" +}' +``` + +Test: + +```shell +curl -i http://127.0.0.1:9080/hello?name=jack +``` + +Log records can be seen in `logs/file.log`. + +```shell +curl -i http://127.0.0.1:9080/hello?name=rose +``` + +Log records cannot be seen in `logs/file.log`. + +## Delete Plugin + +To remove the `file-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/forward-auth.md b/docs/en/latest/plugins/forward-auth.md index 5cff46bea696..2fe89d39b097 100644 --- a/docs/en/latest/plugins/forward-auth.md +++ b/docs/en/latest/plugins/forward-auth.md @@ -156,9 +156,9 @@ HTTP/1.1 403 Forbidden Location: http://example.com/auth ``` -## Disable Plugin +## Delete Plugin -To disable the `forward-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `forward-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/google-cloud-logging.md b/docs/en/latest/plugins/google-cloud-logging.md index 31d3f1461aad..8b4bb81ad0a1 100644 --- a/docs/en/latest/plugins/google-cloud-logging.md +++ b/docs/en/latest/plugins/google-cloud-logging.md @@ -37,6 +37,7 @@ This plugin also allows to push logs as a batch to your Google Cloud Logging Ser | Name | Required | Default | Description | |-------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------| | auth_config | True | | Either `auth_config` or `auth_file` must be provided. | +| auth_config.client_email | True | | Email address of the Google Cloud service account. | | auth_config.private_key | True | | Private key of the Google Cloud service account. | | auth_config.project_id | True | | Project ID in the Google Cloud service account. | | auth_config.token_uri | True | https://oauth2.googleapis.com/token | Token URI of the Google Cloud service account. | @@ -85,7 +86,7 @@ With this configuration, your logs would be formatted as shown below: {"partialSuccess":false,"entries":[{"jsonPayload":{"client_ip":"127.0.0.1","host":"localhost","@timestamp":"2023-01-09T14:47:25+08:00","route_id":"1"},"resource":{"type":"global"},"insertId":"942e81f60b9157f0d46bc9f5a8f0cc40","logName":"projects/apisix/logs/apisix.apache.org%2Flogs","timestamp":"2023-01-09T14:47:25+08:00","labels":{"source":"apache-apisix-google-cloud-logging"}}]} ``` -## Enabling the Plugin +## Enable Plugin ### Full configuration @@ -98,6 +99,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 "google-cloud-logging": { "auth_config":{ "project_id":"apisix", + "client_email":"your service account email@apisix.iam.gserviceaccount.com", "private_key":"-----BEGIN RSA PRIVATE KEY-----your private key-----END RSA PRIVATE KEY-----", "token_uri":"https://oauth2.googleapis.com/token", "scopes":[ @@ -137,6 +139,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 "google-cloud-logging": { "auth_config":{ "project_id":"apisix", + "client_email":"your service account email@apisix.iam.gserviceaccount.com", "private_key":"-----BEGIN RSA PRIVATE KEY-----your private key-----END RSA PRIVATE KEY-----" } } @@ -161,9 +164,9 @@ curl -i http://127.0.0.1:9080/hello You can then login and view the logs in [Google Cloud Logging Service](https://console.cloud.google.com/logs/viewer). -## Disable Plugin +## Delete Plugin -To disable the `google-cloud-logging` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `google-cloud-logging` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/grpc-transcode.md b/docs/en/latest/plugins/grpc-transcode.md index 198eb1cdff34..56680946dff5 100644 --- a/docs/en/latest/plugins/grpc-transcode.md +++ b/docs/en/latest/plugins/grpc-transcode.md @@ -57,7 +57,7 @@ APISIX takes in an HTTP request, transcodes it and forwards it to a gRPC service | default values | `auto_default_values`, `no_default_values`, `use_default_values`, `use_default_metatable` | | hooks | `enable_hooks`, `disable_hooks` | -## Enabling the Plugin +## Enable Plugin Before enabling the Plugin, you have to add the content of your `.proto` or `.pb` files to APISIX. @@ -238,7 +238,7 @@ If the gRPC service returns an error, there may be a `grpc-status-details-bin` f Upload the proto file: ```shell -curl http://127.0.0.1:9080/apisix/admin/proto/1 \ +curl http://127.0.0.1:9080/apisix/admin/protos/1 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "content" : "syntax = \"proto3\"; @@ -308,7 +308,7 @@ Server: APISIX web server Note that there is an undecoded field in the return body. If you need to decode the field, you need to add the `message type` of the field in the uploaded proto file. ```shell -curl http://127.0.0.1:9080/apisix/admin/proto/1 \ +curl http://127.0.0.1:9080/apisix/admin/protos/1 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "content" : "syntax = \"proto3\"; @@ -375,9 +375,9 @@ Server: APISIX web server {"error":{"details":[{"type":"service","message":"The server is out of service","code":1}],"message":"Out of service","code":14}} ``` -## Disable Plugin +## Delete Plugin -To disable the `grpc-transcode` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `grpc-transcode` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/111 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/grpc-web.md b/docs/en/latest/plugins/grpc-web.md index c8ea77c5c5e4..a834e0e95fd6 100644 --- a/docs/en/latest/plugins/grpc-web.md +++ b/docs/en/latest/plugins/grpc-web.md @@ -32,7 +32,7 @@ description: This document contains information about the Apache APISIX grpc-web The `grpc-web` Plugin is a proxy Plugin that can process [gRPC Web](https://github.com/grpc/grpc-web) requests from JavaScript clients to a gRPC service. -## Enabling the Plugin +## Enable Plugin You can enable the `grpc-web` Plugin on a specific Route as shown below: @@ -75,9 +75,9 @@ The supported `Content-Type` includes `application/grpc-web`, `application/grpc- ::: -## Disable Plugin +## Delete Plugin -To disable the `grpc-web` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `grpc-web` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/gzip.md b/docs/en/latest/plugins/gzip.md index 346354b290ce..b812de4daf82 100644 --- a/docs/en/latest/plugins/gzip.md +++ b/docs/en/latest/plugins/gzip.md @@ -49,7 +49,7 @@ This Plugin requires APISIX to run on [APISIX-Base](../FAQ.md#how-do-i-build-the | buffers.size | integer | False | 4096 | >= 1 | Dynamically sets the `gzip_buffers` directive. | | vary | boolean | False | false | | Dynamically sets the `gzip_vary` directive. | -## Enabling the Plugin +## Enable Plugin The example below enables the `gzip` Plugin on the specified Route: @@ -95,9 +95,9 @@ Warning: curl to output it to your terminal anyway, or consider "--output Warning: " to save to a file. ``` -## Disable Plugin +## Delete Plugin -To disable the `gzip` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `gzip` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/hmac-auth.md b/docs/en/latest/plugins/hmac-auth.md index 3f3951877d6a..5940a52df3da 100644 --- a/docs/en/latest/plugins/hmac-auth.md +++ b/docs/en/latest/plugins/hmac-auth.md @@ -50,7 +50,7 @@ This Plugin works with a [Consumer](../terminology/consumer.md) object and a con NOTE: `encrypt_fields = {"secret_key"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields). -## Enabling the Plugin +## Enable Plugin First we enable the Plugin on a Consumer object as shown below: @@ -357,9 +357,9 @@ Accept-Ranges: bytes ``` -## Disable Plugin +## Delete Plugin -To disable the `hmac-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `hmac-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/http-logger.md b/docs/en/latest/plugins/http-logger.md index f9a8cc4b67bc..0f26bd51e874 100644 --- a/docs/en/latest/plugins/http-logger.md +++ b/docs/en/latest/plugins/http-logger.md @@ -88,7 +88,7 @@ With this configuration, your logs would be formatted as shown below: {"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","route_id":"1"} ``` -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the Plugin on a specific Route: @@ -121,7 +121,7 @@ Now, if you make a request to APISIX, it will be logged in your mockbin server: curl -i http://127.0.0.1:9080/hello ``` -## Disable Plugin +## Delete Plugin To disable this Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. diff --git a/docs/en/latest/plugins/inspect.md b/docs/en/latest/plugins/inspect.md index 18b913ae5c12..8b891b82dc6c 100644 --- a/docs/en/latest/plugins/inspect.md +++ b/docs/en/latest/plugins/inspect.md @@ -85,7 +85,7 @@ The `info` is a hash table which contains below keys: | delay | integer | False | 3 | Time in seconds specifying how often to check the hooks file. | | hooks_file | string | False | "/usr/local/apisix/plugin_inspect_hooks.lua" | Lua file to define hooks, which could be a link file. Ensure only administrator could write this file, otherwise it may be a security risk. | -## Enabling the Plugin +## Enable Plugin Plugin is enabled by default (`conf/config-default.yaml`): @@ -162,7 +162,7 @@ stack traceback: 2022/09/01 00:55:52 [info] 2754534#2754534: *4070 [lua] resty_inspect_hooks.lua:6: conf_key=remote_addr, client: 127.0.0.1, server: _, request: "GET /get HTTP/1.1", host: "127.0.0.1:9080" ``` -## Disable plugin +## Delete Plugin To remove the `inspect` Plugin, you can remove it from your configuration file (`conf/config.yaml`): diff --git a/docs/en/latest/plugins/ip-restriction.md b/docs/en/latest/plugins/ip-restriction.md index 904b316f95b7..0f9e4769f3cf 100644 --- a/docs/en/latest/plugins/ip-restriction.md +++ b/docs/en/latest/plugins/ip-restriction.md @@ -48,7 +48,7 @@ Either one of `whitelist` or `blacklist` attribute must be specified. They canno ::: -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a Route or a Service as shown below: @@ -135,9 +135,9 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 }' ``` -## Disable Plugin +## Delete Plugin -To disable the `ip-restriction` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `ip-restriction` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/jwt-auth.md b/docs/en/latest/plugins/jwt-auth.md index 6243c83d8fd9..41919d4c11a5 100644 --- a/docs/en/latest/plugins/jwt-auth.md +++ b/docs/en/latest/plugins/jwt-auth.md @@ -72,7 +72,7 @@ You may need to use the [public-api](public-api.md) plugin to expose this endpoi ::: -## Enabling the Plugin +## Enable Plugin To enable the Plugin, you have to create a Consumer object with the JWT token and configure your Route to use JWT authentication. @@ -249,9 +249,9 @@ Accept-Ranges: bytes ... ``` -## Disable Plugin +## Delete Plugin -To disable the `jwt-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `jwt-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/kafka-logger.md b/docs/en/latest/plugins/kafka-logger.md index 11f0e8212dcd..5d62fc758f8f 100644 --- a/docs/en/latest/plugins/kafka-logger.md +++ b/docs/en/latest/plugins/kafka-logger.md @@ -164,7 +164,7 @@ With this configuration, your logs would be formatted as shown below: {"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","route_id":"1"} ``` -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the `kafka-logger` Plugin on a specific Route: @@ -218,9 +218,9 @@ Now, if you make a request to APISIX, it will be logged in your Kafka server: curl -i http://127.0.0.1:9080/hello ``` -## Disable Plugin +## Delete Plugin -To disable the `kafka-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `kafka-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/kafka-proxy.md b/docs/en/latest/plugins/kafka-proxy.md index 6345a543da94..303886514c71 100644 --- a/docs/en/latest/plugins/kafka-proxy.md +++ b/docs/en/latest/plugins/kafka-proxy.md @@ -78,6 +78,6 @@ curl -X PUT 'http://127.0.0.1:9180/apisix/admin/routes/r1' \ Now, we can test it by connecting to the `/kafka` endpoint via websocket. -## Disable Plugin +## Delete Plugin -To disable the `kafka-proxy` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `kafka-proxy` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. diff --git a/docs/en/latest/plugins/key-auth.md b/docs/en/latest/plugins/key-auth.md index bf34e93f715a..f8dd177db47c 100644 --- a/docs/en/latest/plugins/key-auth.md +++ b/docs/en/latest/plugins/key-auth.md @@ -52,7 +52,7 @@ For Route: | query | string | optional | apikey | | The query string to get the key from. Lower priority than header. | | hide_credentials | bool | optional | false | | Apache APISIX will pass the request header or query string that contains the authentication information to the Upstream if `hide_credentials` is `false`. Otherwise the authentication information will be removed before proxying.| -## Enabling the Plugin +## Enable Plugin To enable the Plugin, you have to create a Consumer object with an authentication key and configure your Route to authenticate requests. @@ -145,9 +145,9 @@ HTTP/1.1 401 Unauthorized {"message":"Invalid API key in request"} ``` -## Disable Plugin +## Delete Plugin -To disable the `key-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `key-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/ldap-auth.md b/docs/en/latest/plugins/ldap-auth.md index 1e0e4005c00f..dac081dc52fb 100644 --- a/docs/en/latest/plugins/ldap-auth.md +++ b/docs/en/latest/plugins/ldap-auth.md @@ -54,7 +54,7 @@ For Route: | tls_verify| boolean | False | `false` | Whether to verify the server certificate when `use_tls` is enabled; If set to `true`, you must set `ssl_trusted_certificate` in `config.yaml`, and make sure the host of `ldap_uri` matches the host in server certificate. | | uid | string | False | `cn` | uid attribute. | -## Enabling the plugin +## Enable plugin First, you have to create a Consumer and enable the `ldap-auth` Plugin on it: @@ -139,9 +139,9 @@ HTTP/1.1 401 Unauthorized {"message":"Invalid user authorization"} ``` -## Disable Plugin +## Delete Plugin -To disable the `ldap-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `ldap-auth` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/limit-conn.md b/docs/en/latest/plugins/limit-conn.md index 4cd882bac6ab..0f463873cab7 100644 --- a/docs/en/latest/plugins/limit-conn.md +++ b/docs/en/latest/plugins/limit-conn.md @@ -44,7 +44,7 @@ The `limit-con` Plugin limits the number of concurrent requests to your services | rejected_msg | string | False | | non-empty | Body of the response returned when the requests exceeding the threshold are rejected. | | allow_degradation | boolean | False | false | | When set to `true` enables Plugin degradation when the Plugin is temporarily unavailable and allows requests to continue. | -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a Route as shown below: @@ -120,9 +120,9 @@ curl -i http://127.0.0.1:9080/index.html?sleep=20 ``` -## Disable Plugin +## Delete Plugin -To disable the `limit-conn` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `limit-conn` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/en/latest/plugins/limit-count.md b/docs/en/latest/plugins/limit-count.md index c67b0832ddcb..24164a756cd5 100644 --- a/docs/en/latest/plugins/limit-count.md +++ b/docs/en/latest/plugins/limit-count.md @@ -46,8 +46,8 @@ The `limit-count` Plugin limits the number of requests to your service by a give | group | string | False | | non-empty | Group to share the counter with. Routes configured with the same group will share the counter. | | redis_host | string | required when `policy` is `redis` | | | Address of the Redis server. Used when the `policy` attribute is set to `redis`. | | redis_port | integer | False | 6379 | [1,...] | Port of the Redis server. Used when the `policy` attribute is set to `redis`. | -| redis_username | string | False | | | Username use to authenticate to the Redis server for version >= 6.0, use only `redis_password` for the versions prior. Used when the `policy` is set to `redis`. | -| redis_password | string | False | | | Password of the Redis server. Used when the `policy` is set to `redis` or `redis-cluster`. | +| redis_username | string | False | | | Username for Redis authentication if Redis ACL is used (for Redis version >= 6.0). If you use the legacy authentication method `requirepass` to configure Redis password, configure only the `redis_password`. Used when the `policy` is set to `redis`. | +| redis_password | string | False | | | Password for Redis authentication. Used when the `policy` is set to `redis` or `redis-cluster`. | | redis_ssl | boolean | False | false | | If set to `true`, then uses SSL to connect to redis instance. Used when the `policy` attribute is set to `redis`. | | redis_ssl_verify | boolean | False | false | | If set to `true`, then verifies the validity of the server SSL certificate. Used when the `policy` attribute is set to `redis`. See [tcpsock:sslhandshake](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake). | | redis_database | integer | False | 0 | redis_database >= 0 | Selected database of the Redis server (for single instance operation or when using Redis cloud with a single entrypoint). Used when the `policy` attribute is set to `redis`. | @@ -57,7 +57,7 @@ The `limit-count` Plugin limits the number of requests to your service by a give | redis_cluster_ssl | boolean | False | false | | If set to `true`, then uses SSL to connect to redis-cluster. Used when the `policy` attribute is set to `redis-cluster`. | | redis_cluster_ssl_verify | boolean | False | false | | If set to `true`, then verifies the validity of the server SSL certificate. Used when the `policy` attribute is set to `redis-cluster`. | -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a Route as shown below: @@ -304,9 +304,9 @@ Server: APISIX web server {"error_msg":"Requests are too frequent, please try again later."} ``` -## Disable Plugin +## Delete Plugin -To disable the `limit-count` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `limit-count` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/en/latest/plugins/limit-req.md b/docs/en/latest/plugins/limit-req.md index ee41ae96056e..77fd1cf1825b 100644 --- a/docs/en/latest/plugins/limit-req.md +++ b/docs/en/latest/plugins/limit-req.md @@ -44,7 +44,7 @@ The `limit-req` Plugin limits the number of requests to your service using the [ | nodelay | boolean | False | false | | If set to `true`, requests within the burst threshold would not be delayed. | | allow_degradation | boolean | False | false | | When set to `true` enables Plugin degradation when the Plugin is temporarily unavailable and allows requests to continue. | -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a Route as shown below: @@ -186,9 +186,9 @@ Server: APISIX web server {"error_msg":"Requests are too frequent, please try again later."} ``` -## Disable Plugin +## Delete Plugin -To disable the `limit-req` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `limit-req` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/log-rotate.md b/docs/en/latest/plugins/log-rotate.md index 94f08ebd9e3e..4f3fd82b7003 100644 --- a/docs/en/latest/plugins/log-rotate.md +++ b/docs/en/latest/plugins/log-rotate.md @@ -42,7 +42,7 @@ You can configure how often the logs are rotated and how many logs to keep. When | max_size | integer | False | -1 | Max size(Bytes) of log files to be rotated, size check would be skipped with a value less than 0 or time is up specified by interval. | | enable_compression | boolean | False | false | When set to `true`, compresses the log file (gzip). Requires `tar` to be installed. | -## Enabling the Plugin +## Enable Plugin To enable the Plugin, add it in your configuration file (`conf/config.yaml`): @@ -108,7 +108,7 @@ total 10.5K -rw-r--r--. 1 resty resty 1.5K Mar 20 21:31 error.log ``` -## Disable plugin +## Delete Plugin To remove the `log-rotate` Plugin, you can remove it from your configuration file (`conf/config.yaml`): diff --git a/docs/en/latest/plugins/loggly.md b/docs/en/latest/plugins/loggly.md index e7035da03767..cee32c8fc02d 100644 --- a/docs/en/latest/plugins/loggly.md +++ b/docs/en/latest/plugins/loggly.md @@ -79,7 +79,7 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/loggly -H 'X-API-KEY: ed ::: -## Enabling the Plugin +## Enable Plugin ### Full configuration @@ -148,9 +148,9 @@ You can then view the logs on your Loggly Dashboard: ![Loggly Dashboard](../../../assets/images/plugin/loggly-dashboard.png) -## Disable Plugin +## Delete Plugin -To disable the `file-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `file-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/loki-logger.md b/docs/en/latest/plugins/loki-logger.md index 52344eb77a79..75e76c08a31d 100644 --- a/docs/en/latest/plugins/loki-logger.md +++ b/docs/en/latest/plugins/loki-logger.md @@ -89,7 +89,7 @@ With this configuration, your logs would be formatted as shown below: {"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","route_id":"1"} ``` -## Enabling the plugin +## Enable plugin The example below shows how you can enable the `loki-logger` plugin on a specific Route: diff --git a/docs/en/latest/plugins/mocking.md b/docs/en/latest/plugins/mocking.md index de9b20ad22a9..eaadc0b45d63 100644 --- a/docs/en/latest/plugins/mocking.md +++ b/docs/en/latest/plugins/mocking.md @@ -41,6 +41,7 @@ The `mocking` Plugin is used for mocking an API. When executed, it returns rando | response_example | string | False | | Body of the response, support use variables, like `$remote_addr $consumer_name`. | | response_schema | object | False | | The JSON schema object for the response. Works when `response_example` is unspecified. | | with_mock_header | boolean | False | true | When set to `true`, adds a response header `x-mock-by: APISIX/{version}`. | +| response_headers | object | false | | Headers to be added in the mocked response. Example: `{"X-Foo": "bar", "X-Few": "baz"}`| The JSON schema supports the following types in their fields: @@ -121,7 +122,7 @@ This is the response generated by the Plugin from this JSON schema: } ``` -## Enabling the Plugin +## Enable Plugin The example below configures the `mocking` Plugin for a specific Route: @@ -221,9 +222,9 @@ x-mock-by: APISIX/2.10.0 {"a":1,"b":2} ``` -## Disable Plugin +## Delete Plugin -To disable the `mocking` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `mocking` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/mqtt-proxy.md b/docs/en/latest/plugins/mqtt-proxy.md index cafc986251b6..85cdcdfcfc06 100644 --- a/docs/en/latest/plugins/mqtt-proxy.md +++ b/docs/en/latest/plugins/mqtt-proxy.md @@ -40,7 +40,7 @@ This Plugin supports both the protocols [3.1.*](http://docs.oasis-open.org/mqtt/ | protocol_name | string | True | Name of the protocol. Generally `MQTT`. | | protocol_level | integer | True | Level of the protocol. It should be `4` for MQTT `3.1.*` and `5` for MQTT `5.0`. | -## Enabling the Plugin +## Enable Plugin To enable the Plugin, you need to first enable the `stream_proxy` configuration in your configuration file (`conf/config.yaml`). The below configuration represents listening on the `9100` TCP port: @@ -50,7 +50,6 @@ To enable the Plugin, you need to first enable the `stream_proxy` configuration http: 'radixtree_uri' ssl: 'radixtree_sni' stream_proxy: # TCP/UDP proxy - only: false # needed if HTTP and Stream Proxy should be enabled tcp: # TCP proxy port list - 9100 dns_resolver: @@ -152,9 +151,9 @@ curl 127.0.0.1:9180/apisix/admin/stream_routes/1 -H 'X-API-KEY: edd1c9f034335f13 The `sni` name must match one or more of the SNIs provided to the SSL object that you created with the CA and server certificates. -## Disable Plugin +## Delete Plugin -To disable the `mqtt-proxy` Plugin you can remove the corresponding configuration as shown below: +To remove the `mqtt-proxy` Plugin you can remove the corresponding configuration as shown below: ```shell curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE diff --git a/docs/en/latest/plugins/node-status.md b/docs/en/latest/plugins/node-status.md index 0a4f92e36f6e..7ad41f42aa40 100644 --- a/docs/en/latest/plugins/node-status.md +++ b/docs/en/latest/plugins/node-status.md @@ -40,7 +40,7 @@ This Plugin will add the endpoint `/apisix/status` to expose the status of APISI You may need to use the [public-api](public-api.md) Plugin to expose the endpoint. -## Enabling the Plugin +## Enable Plugin To configure the `node-status` Plugin, you have to first enable it in your configuration file (`conf/config.yaml`): @@ -99,7 +99,7 @@ The parameters in the response are described below: | reading | Number of connections where APISIX is reading the request header. | | id | UID of APISIX instance saved in `apisix/conf/apisix.uid`. | -## Disable Plugin +## Delete Plugin To remove the Plugin, you can remove it from your configuration file (`conf/config.yaml`): diff --git a/docs/en/latest/plugins/opa.md b/docs/en/latest/plugins/opa.md index 860219350f73..cddc9bb4f3e8 100644 --- a/docs/en/latest/plugins/opa.md +++ b/docs/en/latest/plugins/opa.md @@ -298,9 +298,9 @@ curl -X GET 127.0.0.1:9080/get } ``` -## Disable Plugin +## Delete Plugin -To disable the `opa` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `opa` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/openfunction.md b/docs/en/latest/plugins/openfunction.md index a211ce1fcda2..098cf238f114 100644 --- a/docs/en/latest/plugins/openfunction.md +++ b/docs/en/latest/plugins/openfunction.md @@ -142,9 +142,9 @@ curl http://127.0.0.1:9080/hello/123 Hello, 123! ``` -## Disable Plugin +## Delete Plugin -To disable the `openfunction` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `openfunction` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/openid-connect.md b/docs/en/latest/plugins/openid-connect.md index a5e015089d40..493370240362 100644 --- a/docs/en/latest/plugins/openid-connect.md +++ b/docs/en/latest/plugins/openid-connect.md @@ -61,6 +61,12 @@ description: OpenID Connect allows the client to obtain user information from th | session | object | False | | | When bearer_only is set to false, openid-connect will use Authorization Code flow to authenticate on the IDP, so you need to set the session-related configuration. | | session.secret | string | True | Automatic generation | 16 or more characters | The key used for session encrypt and HMAC operation. | | unauth_action | string | False | "auth" | | Specify the response type on unauthenticated requests. "auth" redirects to identity provider, "deny" results in a 401 response, "pass" will allow the request without authentication. | +| proxy_opts | object | False | | | HTTP proxy server be used to access identity server. | +| proxy_opts.proxy_opts.http_proxy | string | False | | http://proxy-server:port | HTTP proxy server address. | +| proxy_opts.proxy_opts.https_proxy | string | False | | http://proxy-server:port | HTTPS proxy server address. | +| proxy_opts.http_proxy_authorization | string | False | | Basic [base64 username:password] | Default `Proxy-Authorization` header value to be used with `http_proxy`. | +| proxy_opts.https_proxy_authorization | string | False | | Basic [base64 username:password] | As `http_proxy_authorization` but for use with `https_proxy` (since with HTTPS the authorisation is done when connecting, this one cannot be overridden by passing the `Proxy-Authorization` request header). | +| proxy_opts.no_proxy | string | False | | | Comma separated list of hosts that should not be proxied. | NOTE: `encrypt_fields = {"client_secret"}` is also defined in the schema, which means that the field will be stored encrypted in etcd. See [encrypted storage fields](../plugin-develop.md#encrypted-storage-fields). diff --git a/docs/en/latest/plugins/opentelemetry.md b/docs/en/latest/plugins/opentelemetry.md index 6fe00015633f..eca682a061aa 100644 --- a/docs/en/latest/plugins/opentelemetry.md +++ b/docs/en/latest/plugins/opentelemetry.md @@ -89,7 +89,7 @@ plugin_attr: max_export_batch_size: 2 ``` -## Enabling the Plugin +## Enable Plugin To enable the Plugin, you have to add it to your configuration file (`conf/config.yaml`): @@ -124,9 +124,9 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f1 }' ``` -## Disable Plugin +## Delete Plugin -To disable the `opentelemetry` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `opentelemetry` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/openwhisk.md b/docs/en/latest/plugins/openwhisk.md index 2c2afb24a010..333d0aea2592 100644 --- a/docs/en/latest/plugins/openwhisk.md +++ b/docs/en/latest/plugins/openwhisk.md @@ -56,7 +56,7 @@ OpenWhisk supports timeouts in the range 1ms to 60000ms and it is recommended to ::: -## Enabling the Plugin +## Enable Plugin Before configuring the Plugin, you need to have OpenWhisk running. The example below shows OpenWhisk in standalone mode: @@ -111,9 +111,9 @@ This will give back the response from the action: { "ready": true } ``` -## Disable Plugin +## Delete Plugin -To disable the `openwhisk` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `openwhisk` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/prometheus.md b/docs/en/latest/plugins/prometheus.md index b2120ceafaf0..71927fc3ba91 100644 --- a/docs/en/latest/plugins/prometheus.md +++ b/docs/en/latest/plugins/prometheus.md @@ -131,7 +131,7 @@ This feature requires APISIX to run on [APISIX-Base](../FAQ.md#how-do-i-build-th ::: -## Enabling the Plugin +## Enable Plugin The `prometheus` Plugin can be enabled with an empty table. @@ -359,9 +359,9 @@ apisix_upstream_status{name="/apisix/routes/1",ip="100.24.156.8",port="80"} 0 apisix_upstream_status{name="/apisix/routes/1",ip="52.86.68.46",port="80"} 1 ``` -## Disable Plugin +## Delete Plugin -To disable the `prometheus` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `prometheus` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/proxy-cache.md b/docs/en/latest/plugins/proxy-cache.md index 7120aa54f6ea..885fe2334300 100644 --- a/docs/en/latest/plugins/proxy-cache.md +++ b/docs/en/latest/plugins/proxy-cache.md @@ -55,27 +55,38 @@ The data to be cached can be filtered with response codes, request modes, or mor ::: -## Enabling the Plugin +## Enable Plugin You can add your cache configuration in you APISIX configuration file (`conf/config.yaml`) as shown below: ```yaml title="conf/config.yaml" -proxy_cache: - cache_ttl: 10s # default caching time if the upstream doesn't specify the caching time +apisix: + proxy_cache: + cache_ttl: 10s # 如果上游未指定缓存时间,则为默认磁盘缓存时间 zones: - - name: disk_cache_one # name of the cache. Admin can specify which cache to use in the Admin API by name - memory_size: 50m # size of shared memory, used to store the cache index - disk_size: 1G # size of disk, used to store the cache data - disk_path: "/tmp/disk_cache_one" # path to store the cache data - cache_levels: "1:2" # hierarchy levels of the cache + - name: disk_cache_one + memory_size: 50m + disk_size: 1G + disk_path: /tmp/disk_cache_one + cache_levels: 1:2 + # - name: disk_cache_two + # memory_size: 50m + # disk_size: 1G + # disk_path: "/tmp/disk_cache_two" + # cache_levels: "1:2" + - name: memory_cache + memory_size: 50m ``` -You can enable the Plugin on a specific Route as shown below: +### Use disk-based caching + +You can enable the Plugin on a Route as shown below. The Plugin uses the disk-based `cache_strategy` and `disk_cache_one` as the `cache_zone` by default: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { + "uri": "/ip", "plugins": { "proxy-cache": { "cache_key": ["$uri", "-cache-id"], @@ -88,22 +99,44 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \ }, "upstream": { "nodes": { - "127.0.0.1:1999": 1 + "httpbin.org": 1 }, "type": "roundrobin" - }, - "uri": "/hello" + } }' ``` -In the above configuration, the `cache_zone` attribute defaults to `disk_cache_one`. +### Use memory-based caching + +You can enable the Plugin on a Route with in-memory `cache_strategy` and a corresponding in-memory `cache_zone` as shown below: + +```shell +curl http://127.0.0.1:9180/apisix/admin/routes/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri": "/ip", + "plugins": { + "proxy-cache": { + "cache_strategy": "memory", + "cache_zone": "memory_cache", + "cache_ttl": 10 + } + }, + "upstream": { + "nodes": { + "httpbin.org": 1 + }, + "type": "roundrobin" + } +}' +``` ## Example usage Once you have configured the Plugin as shown above, you can make an initial request: ```shell -curl http://127.0.0.1:9080/hello -i +curl http://127.0.0.1:9080/ip -i ``` ```shell @@ -117,7 +150,7 @@ hello The `Apisix-Cache-Status` in the response shows `MISS` meaning that the response is not cached, as expected. Now, if you make another request, you will see that you get a cached response: ```shell -curl http://127.0.0.1:9080/hello -i +curl http://127.0.0.1:9080/ip -i ``` ```shell @@ -135,7 +168,7 @@ If you set `"cache_zone": "invalid_disk_cache"` attribute to an invalid value (c To clear the cached data, you can send a request with `PURGE` method: ```shell -curl -i http://127.0.0.1:9080/hello -X PURGE +curl -i http://127.0.0.1:9080/ip -X PURGE ``` ```shell @@ -146,20 +179,20 @@ If the response code is `200`, the deletion is successful. If the cached data is ::: -## Disable Plugin +## Delete Plugin -To disable the `proxy-cache` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `proxy-cache` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { - "uri": "/hello", + "uri": "/ip", "plugins": {}, "upstream": { "type": "roundrobin", "nodes": { - "127.0.0.1:1999": 1 + "httpbin.org": 1 } } }' diff --git a/docs/en/latest/plugins/proxy-control.md b/docs/en/latest/plugins/proxy-control.md index a4bbae5a497f..e97d0dc5c084 100644 --- a/docs/en/latest/plugins/proxy-control.md +++ b/docs/en/latest/plugins/proxy-control.md @@ -42,7 +42,7 @@ This Plugin requires APISIX to run on [APISIX-Base](../FAQ.md#how-do-i-build-the | ----------------- | ------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | request_buffering | boolean | False | true | When set to `true`, the Plugin dynamically sets the [`proxy_request_buffering`](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_request_buffering) directive. | -## Enabling the Plugin +## Enable Plugin The example below enables the Plugin on a specific Route: @@ -75,9 +75,9 @@ curl -i http://127.0.0.1:9080/upload -d @very_big_file It's expected to not find a message "a client request body is buffered to a temporary file" in the error log. -## Disable Plugin +## Delete Plugin -To disable the `proxy-control` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `proxy-control` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl -i http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/en/latest/plugins/proxy-mirror.md b/docs/en/latest/plugins/proxy-mirror.md index 50b11e7100bf..73b65f8e19c5 100644 --- a/docs/en/latest/plugins/proxy-mirror.md +++ b/docs/en/latest/plugins/proxy-mirror.md @@ -61,7 +61,7 @@ plugin_attr: | read | string | 60s | Read timeout to the mirrored Upstream. | | send | string | 60s | Send timeout to the mirrored Upstream. | -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a specific Route as shown below: @@ -129,9 +129,9 @@ HTTP/1.1 200 OK hello world ``` -## Disable Plugin +## Delete Plugin -To disable the `proxy-mirror` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `proxy-mirror` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/en/latest/plugins/proxy-rewrite.md b/docs/en/latest/plugins/proxy-rewrite.md index af00d15d46c1..d1b4fdca238e 100644 --- a/docs/en/latest/plugins/proxy-rewrite.md +++ b/docs/en/latest/plugins/proxy-rewrite.md @@ -52,7 +52,7 @@ Header configurations are executed according to the following priorities: `add` > `remove` > `set` -## Enabling the Plugin +## Enable Plugin The example below enables the `proxy-rewrite` Plugin on a specific Route: @@ -103,9 +103,9 @@ Once you send the request, you can check the Upstream `access.log` for its outpu 127.0.0.1 - [26/Sep/2019:10:52:20 +0800] iresty.com GET /test/home.html HTTP/1.1 200 38 - curl/7.29.0 - 0.000 199 107 ``` -## Disable Plugin +## Delete Plugin -To disable the `proxy-rewrite` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `proxy-rewrite` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/public-api.md b/docs/en/latest/plugins/public-api.md index 0098a86e631e..1c275ff23dda 100644 --- a/docs/en/latest/plugins/public-api.md +++ b/docs/en/latest/plugins/public-api.md @@ -134,9 +134,9 @@ curl -i 'http://127.0.0.1:9080/gen_token?key=user-key' HTTP/1.1 401 Unauthorized ``` -## Disable Plugin +## Delete Plugin -To disable the `public-api` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `public-api` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/real-ip.md b/docs/en/latest/plugins/real-ip.md index ce2567da38a9..115fc29b2a9e 100644 --- a/docs/en/latest/plugins/real-ip.md +++ b/docs/en/latest/plugins/real-ip.md @@ -43,7 +43,7 @@ This Plugin requires APISIX to run on [APISIX-Base](../FAQ.md#how-do-i-build-the | Name | Type | Required | Valid values | Description | |-------------------|---------------|----------|-----------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| source | string | True | Any Nginx variable like `arg_realip` or `http_x_forwarded_for`. | Dynamically sets the client's IP address and an optional port from APISIX's view. | +| source | string | True | Any Nginx variable like `arg_realip` or `http_x_forwarded_for`. | Dynamically sets the client's IP address and an optional port, or the client's host name, from APISIX's view. | | trusted_addresses | array[string] | False | List of IPs or CIDR ranges. | Dynamically sets the `set_real_ip_from` field. | | recursive | boolean | False | True to enable, false to disable, default is false | If recursive search is disabled, the original client address that matches one of the trusted addresses is replaced by the last address sent in the configured `source`. If recursive search is enabled, the original client address that matches one of the trusted addresses is replaced by the last non-trusted address sent in the configured `source`. | @@ -53,7 +53,7 @@ If the address specified in `source` is missing or invalid, the Plugin would not ::: -## Enabling the Plugin +## Enable Plugin The example below enables the `real-ip` Plugin on the specified Route: @@ -96,9 +96,9 @@ remote-addr: 1.2.3.4 remote-port: 9080 ``` -## Disable Plugin +## Delete Plugin -To disable the `real-ip` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `real-ip` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/redirect.md b/docs/en/latest/plugins/redirect.md index 7ba47aea8923..0b725451d918 100644 --- a/docs/en/latest/plugins/redirect.md +++ b/docs/en/latest/plugins/redirect.md @@ -53,7 +53,7 @@ The `redirect` Plugin can be used to configure redirects. ::: -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the `redirect` Plugin on a specific Route: @@ -144,9 +144,9 @@ Location: https://127.0.0.1:9443/hello ... ``` -## Disable Plugin +## Delete Plugin -To disable the `redirect` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `redirect` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/referer-restriction.md b/docs/en/latest/plugins/referer-restriction.md index 877896ad1c08..fb3d358a0201 100644 --- a/docs/en/latest/plugins/referer-restriction.md +++ b/docs/en/latest/plugins/referer-restriction.md @@ -45,7 +45,7 @@ Only one of `whitelist` or `blacklist` attribute must be specified. They cannot ::: -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a specific Route or a Service as shown below: @@ -107,9 +107,9 @@ HTTP/1.1 200 OK ... ``` -## Disable Plugin +## Delete Plugin -To disable the `referer-restriction` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `referer-restriction` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/request-id.md b/docs/en/latest/plugins/request-id.md index e653f4d4b971..95dd565b1a55 100644 --- a/docs/en/latest/plugins/request-id.md +++ b/docs/en/latest/plugins/request-id.md @@ -48,7 +48,7 @@ The Plugin will not add a unique ID if the request already has a header with the | range_id.char_set | string | False | "abcdefghijklmnopqrstuvwxyzABCDEFGHIGKLMNOPQRSTUVWXYZ0123456789| The minimum string length is 6 | Character set for range_id | | range_id.length | integer | False | 16 | Minimum 6 | Id length for range_id algorithm | -## Enabling the Plugin +## Enable Plugin The example below enables the Plugin on a specific Route: @@ -84,9 +84,9 @@ HTTP/1.1 200 OK X-Request-Id: fe32076a-d0a5-49a6-a361-6c244c1df956 ``` -## Disable Plugin +## Delete Plugin -To disable the `request-id` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `request-id` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/5 \ diff --git a/docs/en/latest/plugins/request-validation.md b/docs/en/latest/plugins/request-validation.md index a9fbcee21a82..bd0d80b7f048 100644 --- a/docs/en/latest/plugins/request-validation.md +++ b/docs/en/latest/plugins/request-validation.md @@ -45,7 +45,7 @@ At least one of `header_schema` or `body_schema` should be filled in. ::: -## Enabling the Plugin +## Enable Plugin You can configure the Plugin on a specific Route as shown below: @@ -192,6 +192,23 @@ The examples below shows how you can configure this Plugin for different validat } ``` +### Header validation + +```json +{ + "header_schema": { + "type": "object", + "required": ["Content-Type"], + "properties": { + "Content-Type": { + "type": "string", + "pattern": "^application\/json$" + } + } + } +} +``` + ### Combined validation ```json @@ -265,9 +282,9 @@ curl --header "Content-Type: application/json" \ http://127.0.0.1:9080/get ``` -## Disable Plugin +## Delete Plugin -To disable the `request-validation` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `request-validation` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/5 \ diff --git a/docs/en/latest/plugins/response-rewrite.md b/docs/en/latest/plugins/response-rewrite.md index e95371ac8672..392d367254f2 100644 --- a/docs/en/latest/plugins/response-rewrite.md +++ b/docs/en/latest/plugins/response-rewrite.md @@ -67,7 +67,7 @@ Only one of `body` or `filters` can be configured. ::: -## Enabling the Plugin +## Enable Plugin The example below enables the `response-rewrite` Plugin on a specific Route: @@ -226,9 +226,9 @@ X-Server-id: 3 ``` -## Disable Plugin +## Delete Plugin -To disable the `response-rewrite` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `response-rewrite` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/rocketmq-logger.md b/docs/en/latest/plugins/rocketmq-logger.md index 9a3eb158dc02..be3245aeeadf 100644 --- a/docs/en/latest/plugins/rocketmq-logger.md +++ b/docs/en/latest/plugins/rocketmq-logger.md @@ -154,7 +154,7 @@ With this configuration, your logs would be formatted as shown below: {"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","route_id":"1"} ``` -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the `rocketmq-logger` Plugin on a specific Route: @@ -196,9 +196,9 @@ Now, if you make a request to APISIX, it will be logged in your RocketMQ server: curl -i http://127.0.0.1:9080/hello ``` -## Disable Plugin +## Delete Plugin -To disable the `rocketmq-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `rocketmq-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/server-info.md b/docs/en/latest/plugins/server-info.md index c73997e11afd..9e9005639d22 100644 --- a/docs/en/latest/plugins/server-info.md +++ b/docs/en/latest/plugins/server-info.md @@ -50,7 +50,7 @@ None. This Plugin exposes the endpoint `/v1/server_info` to the [Control API](../control-api.md) -## Enabling the Plugin +## Enable Plugin Add `server-info` to the Plugin list in your configuration file (`conf/config.yaml`): @@ -102,9 +102,9 @@ You can also view the server info report through the [APISIX Dashboard](/docs/da ::: -## Disable Plugin +## Delete Plugin -To disable the Plugin, you can remove `server-info` from the list of Plugins in your configuration file: +To remove the Plugin, you can remove `server-info` from the list of Plugins in your configuration file: ```yaml title="conf/config.yaml" plugins: diff --git a/docs/en/latest/plugins/serverless.md b/docs/en/latest/plugins/serverless.md index df4fed614615..11f3c7c6377a 100644 --- a/docs/en/latest/plugins/serverless.md +++ b/docs/en/latest/plugins/serverless.md @@ -79,7 +79,7 @@ Prior to v2.12.0, the phase `before_proxy` was called `balancer`. This was updat ::: -## Enabling the Plugin +## Enable Plugin The example below enables the Plugin on a specific Route: @@ -116,9 +116,9 @@ curl -i http://127.0.0.1:9080/index.html You will find a message "serverless pre-function" and "match uri /index.html" in the error.log. -## Disable Plugin +## Delete Plugin -To disable the `serverless` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `serverless` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/skywalking-logger.md b/docs/en/latest/plugins/skywalking-logger.md index fb245e973e7e..127cffbb5660 100644 --- a/docs/en/latest/plugins/skywalking-logger.md +++ b/docs/en/latest/plugins/skywalking-logger.md @@ -81,7 +81,7 @@ With this configuration, your logs would be formatted as shown below: {"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","route_id":"1"} ``` -## Enabling the Plugin +## Enable Plugin Once you have set up your SkyWalking OAP server, you can enable the Plugin on a specific Route as shown below: @@ -111,9 +111,9 @@ Now, if you make a request to APISIX, it will be logged in your SkyWalking OAP s curl -i http://127.0.0.1:9080/hello ``` -## Disable Plugin +## Delete Plugin -To disable the `skywalking-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `skywalking-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/skywalking.md b/docs/en/latest/plugins/skywalking.md index 6e3a2965c78e..c9d2920328fc 100644 --- a/docs/en/latest/plugins/skywalking.md +++ b/docs/en/latest/plugins/skywalking.md @@ -94,7 +94,7 @@ nohup java -javaagent:/root/skywalking/app/agent/skywalking-agent.jar \ 2>&1 > /root/skywalking/app/logs/nohup.log & ``` -## Enabling the Plugin +## Enable Plugin To enable the Plugin, you have to add it to your configuration file (`conf/config.yaml`): @@ -197,9 +197,9 @@ You should also be able to see traces from all services: ![ ](../../../assets/images/plugin/skywalking-5.png) -## Disable Plugin +## Delete Plugin -To disable the `skywalking` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `skywalking` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/sls-logger.md b/docs/en/latest/plugins/sls-logger.md index 16b64d974c98..c76ebbbed6f0 100644 --- a/docs/en/latest/plugins/sls-logger.md +++ b/docs/en/latest/plugins/sls-logger.md @@ -86,7 +86,7 @@ With this configuration, your logs would be formatted as shown below: {"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","route_id":"1"} ``` -## Enabling the Plugin +## Enable Plugin The example below shows how you can configure the Plugin on a specific Route: @@ -126,9 +126,9 @@ Now if you check your Ali Cloud log server, you will be able to see the logs: ![sls logger view](../../../assets/images/plugin/sls-logger-1.png "sls logger view") -## Disable Plugin +## Delete Plugin -To disable the `sls-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `sls-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/splunk-hec-logging.md b/docs/en/latest/plugins/splunk-hec-logging.md index e8c8d8e0aa20..d8a847d2a8bf 100644 --- a/docs/en/latest/plugins/splunk-hec-logging.md +++ b/docs/en/latest/plugins/splunk-hec-logging.md @@ -81,7 +81,7 @@ With this configuration, your logs would be formatted as shown below: [{"time":1673976669.269,"source":"apache-apisix-splunk-hec-logging","event":{"host":"localhost","client_ip":"127.0.0.1","@timestamp":"2023-01-09T14:47:25+08:00","route_id":"1"},"host":"DESKTOP-2022Q8F-wsl","sourcetype":"_json"}] ``` -## Enabling the Plugin +## Enable Plugin ### Full configuration @@ -152,9 +152,9 @@ You should be able to login and search these logs from your Splunk dashboard: ![splunk hec search view](../../../assets/images/plugin/splunk-hec-admin-en.png) -## Disable Plugin +## Delete Plugin -To disable the `splunk-hec-logging` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `splunk-hec-logging` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/syslog.md b/docs/en/latest/plugins/syslog.md index e1780549585d..4a38f9728098 100644 --- a/docs/en/latest/plugins/syslog.md +++ b/docs/en/latest/plugins/syslog.md @@ -84,7 +84,7 @@ With this configuration, your logs would be formatted as shown below: {"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","route_id":"1"} ``` -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the Plugin for a specific Route: @@ -116,9 +116,9 @@ Now, if you make a request to APISIX, it will be logged in your Syslog server: curl -i http://127.0.0.1:9080/hello ``` -## Disable Plugin +## Delete Plugin -To disable the `syslog` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `syslog` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/tcp-logger.md b/docs/en/latest/plugins/tcp-logger.md index 19b29459ec6b..3b41582244ab 100644 --- a/docs/en/latest/plugins/tcp-logger.md +++ b/docs/en/latest/plugins/tcp-logger.md @@ -83,7 +83,7 @@ With this configuration, your logs would be formatted as shown below: {"@timestamp":"2023-01-09T14:47:25+08:00","route_id":"1","host":"localhost","client_ip":"127.0.0.1"} ``` -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the `tcp-logger` Plugin on a specific Route: @@ -117,9 +117,9 @@ Now, if you make a request to APISIX, it will be logged in your TCP server: curl -i http://127.0.0.1:9080/hello ``` -## Disable Plugin +## Delete Plugin -To disable the `tcp-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `tcp-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/tencent-cloud-cls.md b/docs/en/latest/plugins/tencent-cloud-cls.md index 552621914997..da9ec84f4e73 100644 --- a/docs/en/latest/plugins/tencent-cloud-cls.md +++ b/docs/en/latest/plugins/tencent-cloud-cls.md @@ -85,7 +85,7 @@ With this configuration, your logs would be formatted as shown below: {"host":"localhost","@timestamp":"2020-09-23T19:05:05-04:00","client_ip":"127.0.0.1","route_id":"1"} ``` -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the Plugin on a specific Route: @@ -125,7 +125,7 @@ Now, if you make a request to APISIX, it will be logged in your cls topic: curl -i http://127.0.0.1:9080/hello ``` -## Disable Plugin +## Delete Plugin To disable this Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. diff --git a/docs/en/latest/plugins/traffic-split.md b/docs/en/latest/plugins/traffic-split.md index e617e22673a4..7c45aa9e7b90 100644 --- a/docs/en/latest/plugins/traffic-split.md +++ b/docs/en/latest/plugins/traffic-split.md @@ -77,7 +77,7 @@ If only the `weight` attribute is configured, it corresponds to the weight of th ::: -## Enabling the Plugin +## Enable Plugin You can configure the Plugin on a Route as shown below: @@ -616,9 +616,9 @@ curl http://127.0.0.1:9080/hello -H 'x-api-id: 3' 1980 ``` -## Disable Plugin +## Delete Plugin -To disable the `traffic-split` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `traffic-split` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/en/latest/plugins/ua-restriction.md b/docs/en/latest/plugins/ua-restriction.md index 84bd2cb14303..070f08ba6a85 100644 --- a/docs/en/latest/plugins/ua-restriction.md +++ b/docs/en/latest/plugins/ua-restriction.md @@ -43,11 +43,11 @@ A common scenario is to set crawler rules. `User-Agent` is the identity of the c :::note -Both `allowlist` and `denylist` can be used on their own. If they are used together, the `allowlist` matches before the `denylist`. +Both `allowlist` and `denylist` can't be used at the same time. ::: -## Enabling the Plugin +## Enable Plugin You can enable the Plugin on a Route or a Service as shown below: @@ -116,9 +116,9 @@ HTTP/1.1 403 Forbidden {"message":"Not allowed"} ``` -## Disable Plugin +## Delete Plugin -To disable the `ua-restriction` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `ua-restriction` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/udp-logger.md b/docs/en/latest/plugins/udp-logger.md index 48ec4ee49dfd..8b4eb7f232d2 100644 --- a/docs/en/latest/plugins/udp-logger.md +++ b/docs/en/latest/plugins/udp-logger.md @@ -81,7 +81,7 @@ With this configuration, your logs would be formatted as shown below: {"@timestamp":"2023-01-09T14:47:25+08:00","route_id":"1","host":"localhost","client_ip":"127.0.0.1"} ``` -## Enabling the Plugin +## Enable Plugin The example below shows how you can enable the Plugin on a specific Route: @@ -114,9 +114,9 @@ Now, if you make a request to APISIX, it will be logged in your UDP server: curl -i http://127.0.0.1:9080/hello ``` -## Disable Plugin +## Delete Plugin -To disable the `udp-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `udp-logger` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/uri-blocker.md b/docs/en/latest/plugins/uri-blocker.md index c7db356bcd09..18be67c04797 100644 --- a/docs/en/latest/plugins/uri-blocker.md +++ b/docs/en/latest/plugins/uri-blocker.md @@ -39,7 +39,7 @@ The `uri-blocker` Plugin intercepts user requests with a set of `block_rules`. | rejected_msg | string | False | | non-empty | HTTP response body returned when the request URI hits any of the `block_rules`. | | case_insensitive | boolean | False | false | | When set to `true`, ignores the case when matching request URI. | -## Enabling the Plugin +## Enable Plugin The example below enables the `uri-blocker` Plugin on a specific Route: @@ -93,9 +93,9 @@ Server: APISIX web server {"error_msg":"access is not allowed"} ``` -## Disable Plugin +## Delete Plugin -To disable the `uri-blocker` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `uri-blocker` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/wolf-rbac.md b/docs/en/latest/plugins/wolf-rbac.md index 87bd409875cc..954c894ebf10 100644 --- a/docs/en/latest/plugins/wolf-rbac.md +++ b/docs/en/latest/plugins/wolf-rbac.md @@ -60,7 +60,7 @@ To use this Plugin, you have to first [install wolf](https://github.com/iGeeky/w Once you have done that you need to add `application`, `admin`, `normal user`, `permission`, `resource` and user authorize to the [wolf-console](https://github.com/iGeeky/wolf/blob/master/docs/usage.md). -## Enabling the Plugin +## Enable Plugin You need to first configure the Plugin on a Consumer: @@ -266,9 +266,9 @@ HTTP/1.1 200 OK {"message":"success to change password"} ``` -## Disable Plugin +## Delete Plugin -To disable the `wolf-rbac` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `wolf-rbac` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/plugins/workflow.md b/docs/en/latest/plugins/workflow.md index ed1d8adaa8c0..6448d0224156 100644 --- a/docs/en/latest/plugins/workflow.md +++ b/docs/en/latest/plugins/workflow.md @@ -61,7 +61,7 @@ In `rules`, match `case` in order according to the index of the `rules`, and exe ::: -## Enabling the Plugin +## Enable Plugin You can configure the `workflow` plugin on a Route as shown below: @@ -147,9 +147,9 @@ curl http://127.0.0.1:0080/hello/fake -i HTTP/1.1 200 OK ``` -## Disable Plugin +## Delete Plugin -To disable the `workflow` plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `workflow` plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/en/latest/plugins/zipkin.md b/docs/en/latest/plugins/zipkin.md index 1a870441b1ad..2a772e608f0d 100644 --- a/docs/en/latest/plugins/zipkin.md +++ b/docs/en/latest/plugins/zipkin.md @@ -106,7 +106,7 @@ func main(){ } ``` -## Enabling the Plugin +## Enable Plugin The example below enables the Plugin on a specific Route: @@ -216,9 +216,9 @@ You can access the Jaeger UI to view the traces in endpoint [http://127.0.0.1:16 ![jaeger web-ui trace](../../../assets/images/plugin/jaeger-2.png) -## Disable Plugin +## Delete Plugin -To disable the `zipkin` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. +To remove the `zipkin` Plugin, you can delete the corresponding JSON configuration from the Plugin configuration. APISIX will automatically reload and you do not have to restart for this to take effect. ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/en/latest/ssl-protocol.md b/docs/en/latest/ssl-protocol.md new file mode 100644 index 000000000000..53dbcfbc752b --- /dev/null +++ b/docs/en/latest/ssl-protocol.md @@ -0,0 +1,343 @@ +--- +title: SSL Protocol +--- + + + +`APISIX` supports set TLS protocol and also supports dynamically specifying different TLS protocol versions for each [SNI](https://en.wikipedia.org/wiki/Server_Name_Indication). + +**For security reasons, the encryption suite used by default in `APISIX` does not support TLSv1.1 and lower versions.** +**If you need to enable the TLSv1.1 protocol, please add the encryption suite supported by the TLSv1.1 protocol to the configuration item `apisix.ssl.ssl_ciphers` in `config.yaml`.** + +## ssl_protocols Configuration + +### Static Configuration + +The `ssl_protocols` parameter in the static configuration `config.yaml` applies to the entire APISIX, but cannot be dynamically modified. It only takes effect when the matching SSL resource does not set `ssl_protocols`. + +```yaml +apisix: + ssl: + ssl_protocols: TLSv1.2 TLSv1.3 # default TLSv1.2 TLSv1.3 +``` + +### Dynamic Configuration + +Use the `ssl_protocols` field in the `ssl` resource to dynamically specify different TLS protocol versions for each SNI. + +Specify the `test.com` domain uses the TLSv1.2 and TLSv1.3: + +```bash +{ + "cert": "$cert", + "key": "$key", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.2", + "TLSv1.3" + ] +} +``` + +### Notes + +- Dynamic configuration has a higher priority than static configuration. When the `ssl_protocols` configuration item in the ssl resource is not empty, the static configuration will be overridden. +- The static configuration applies to the entire APISIX and requires a reload of APISIX to take effect. +- Dynamic configuration can control the TLS protocol version of each SNI in a fine-grained manner and can be dynamically modified, which is more flexible than static configuration. + +## Examples + +### How to specify the TLSv1.1 protocol + +While newer products utilize higher security-level TLS protocol versions, there are still legacy clients that rely on the lower-level TLSv1.1 protocol. However, enabling TLSv1.1 for new products presents potential security risks. In order to maintain the security of the API, it is crucial to have the ability to seamlessly switch between different protocol versions based on specific requirements and circumstances. +For example, consider two domain names: `test.com`, utilized by legacy clients requiring TLSv1.1 configuration, and `test2.com`, associated with new products that support TLSv1.2 and TLSv1.3 protocols. + +1. `config.yaml` configuration. + +```yaml +apisix: + ssl: + ssl_protocols: TLSv1.3 + # ssl_ciphers is for reference only + ssl_ciphers: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA +``` + +2. Specify the TLSv1.1 protocol version for the test.com domain. + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.1" + ] +}' +``` + +3. Create an SSL object for test.com without specifying the TLS protocol version, which will use the static configuration by default. + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server2.crt)"'", + "key": "'"$(cat server2.key)"'", + "snis": ["test2.com"] +}' +``` + +4. Access Verification + +Failed, accessed test.com with TLSv1.3: + +```shell +$ curl --tls-max 1.3 --tlsv1.3 https://test.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.3 (OUT), TLS handshake, Client hello (1): +* TLSv1.3 (IN), TLS alert, protocol version (582): +* error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +* Closing connection 0 +curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +``` + +Successfully, accessed test.com with TLSv1.1: + +```shell +$ curl --tls-max 1.1 --tlsv1.1 https://test.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.1 (OUT), TLS handshake, Client hello (1): +* TLSv1.1 (IN), TLS handshake, Server hello (2): +* TLSv1.1 (IN), TLS handshake, Certificate (11): +* TLSv1.1 (IN), TLS handshake, Server key exchange (12): +* TLSv1.1 (IN), TLS handshake, Server finished (14): +* TLSv1.1 (OUT), TLS handshake, Client key exchange (16): +* TLSv1.1 (OUT), TLS change cipher, Change cipher spec (1): +* TLSv1.1 (OUT), TLS handshake, Finished (20): +* TLSv1.1 (IN), TLS handshake, Finished (20): +* SSL connection using TLSv1.1 / ECDHE-RSA-AES256-SHA +``` + +Successfully, accessed test2.com with TLSv1.3: + +```shell +$ curl --tls-max 1.3 --tlsv1.3 https://test2.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test2.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.3 (OUT), TLS handshake, Client hello (1): +* TLSv1.3 (IN), TLS handshake, Server hello (2): +* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): +* TLSv1.3 (IN), TLS handshake, Certificate (11): +* TLSv1.3 (IN), TLS handshake, CERT verify (15): +* TLSv1.3 (IN), TLS handshake, Finished (20): +* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): +* TLSv1.3 (OUT), TLS handshake, Finished (20): +* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 +``` + +Failed, accessed test2.com with TLSv1.1: + +```shell +curl --tls-max 1.1 --tlsv1.1 https://test2.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test2.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.1 (OUT), TLS handshake, Client hello (1): +* TLSv1.1 (IN), TLS alert, protocol version (582): +* error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +* Closing connection 0 +curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +``` + +### Certificates are associated with multiple domains, but different TLS protocols are used between domains + +Sometimes, we may encounter a situation where a certificate is associated with multiple domains, but they need to use different TLS protocols to ensure security. For example, the test.com domain needs to use the TLSv1.2 protocol, while the test2.com domain needs to use the TLSv1.3 protocol. In this case, we cannot simply create an SSL object for all domains, but need to create an SSL object for each domain separately and specify the appropriate protocol version. This way, we can perform the correct SSL handshake and encrypted communication based on different domains and protocol versions. The example is as follows: + +1. Create an SSL object for test.com using the certificate and specify the TLSv1.2 protocol. + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.2" + ] +}' +``` + +2. Use the same certificate as test.com to create an SSL object for test2.com and specify the TLSv1.3 protocol. + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/2 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test2.com"], + "ssl_protocols": [ + "TLSv1.3" + ] +}' +``` + +3. Access verification + +Successfully, accessed test.com with TLSv1.2: + +```shell +$ curl --tls-max 1.2 --tlsv1.2 https://test.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.2 (OUT), TLS handshake, Client hello (1): +* TLSv1.2 (IN), TLS handshake, Server hello (2): +* TLSv1.2 (IN), TLS handshake, Certificate (11): +* TLSv1.2 (IN), TLS handshake, Server key exchange (12): +* TLSv1.2 (IN), TLS handshake, Server finished (14): +* TLSv1.2 (OUT), TLS handshake, Client key exchange (16): +* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1): +* TLSv1.2 (OUT), TLS handshake, Finished (20): +* TLSv1.2 (IN), TLS handshake, Finished (20): +* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256 +* ALPN, server accepted to use h2 +* Server certificate: +* subject: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test.com +* start date: Jul 20 15:50:08 2023 GMT +* expire date: Jul 17 15:50:08 2033 GMT +* issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test.com +* SSL certificate verify result: EE certificate key too weak (66), continuing anyway. +* Using HTTP2, server supports multi-use +* Connection state changed (HTTP/2 confirmed) +* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 +* Using Stream ID: 1 (easy handle 0x5608905ee2e0) +> HEAD / HTTP/2 +> Host: test.com:9443 +> user-agent: curl/7.74.0 +> accept: */* + +``` + +Failed, accessed test.com with TLSv1.3: + +```shell +$ curl --tls-max 1.3 --tlsv1.3 https://test.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.3 (OUT), TLS handshake, Client hello (1): +* TLSv1.3 (IN), TLS alert, protocol version (582): +* error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +* Closing connection 0 +curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version + +``` + +Successfully, accessed test2.com with TLSv1.3: + +```shell +$ curl --tls-max 1.3 --tlsv1.3 https://test2.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test2.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.3 (OUT), TLS handshake, Client hello (1): +* TLSv1.3 (IN), TLS handshake, Server hello (2): +* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): +* TLSv1.3 (IN), TLS handshake, Certificate (11): +* TLSv1.3 (IN), TLS handshake, CERT verify (15): +* TLSv1.3 (IN), TLS handshake, Finished (20): +* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): +* TLSv1.3 (OUT), TLS handshake, Finished (20): +* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 +* ALPN, server accepted to use h2 +* Server certificate: +* subject: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test2.com +* start date: Jul 20 16:05:47 2023 GMT +* expire date: Jul 17 16:05:47 2033 GMT +* issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test2.com +* SSL certificate verify result: EE certificate key too weak (66), continuing anyway. +* Using HTTP2, server supports multi-use +* Connection state changed (HTTP/2 confirmed) +* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 +* Using Stream ID: 1 (easy handle 0x55569cbe42e0) +> HEAD / HTTP/2 +> Host: test2.com:9443 +> user-agent: curl/7.74.0 +> accept: */* +> +* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): +* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): +* old SSL session ID is stale, removing +``` + +Failed, accessed test2.com with TLSv1.2: + +```shell +$ curl --tls-max 1.2 --tlsv1.2 https://test2.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test2.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.2 (OUT), TLS handshake, Client hello (1): +* TLSv1.2 (IN), TLS alert, protocol version (582): +* error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +* Closing connection 0 +curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +``` diff --git a/docs/en/latest/stream-proxy.md b/docs/en/latest/stream-proxy.md index c68173d09c59..9354c96b255b 100644 --- a/docs/en/latest/stream-proxy.md +++ b/docs/en/latest/stream-proxy.md @@ -29,7 +29,12 @@ APISIX can serve as a stream proxy, in addition to being an application layer pr By default, stream proxy is disabled. -To enable the option, add the `apisix.stream_proxy` option in `conf/config.yaml` and specify a list of addresses which APISIX should act as a stream proxy and listen for incoming requests. +To enable this option, set `apisix.proxy_mode` to `stream` or `http&stream`, depending on whether you want stream proxy only or both http and stream. Then add the `apisix.stream_proxy` option in `conf/config.yaml` and specify the list of addresses where APISIX should act as a stream proxy and listen for incoming requests. +:::note + +This "apisix.stream_proxy" option has only been added in versions after 3.2.1. + +::: ```yaml apisix: @@ -42,19 +47,6 @@ apisix: - "127.0.0.1:9211" ``` -If `apisix.enable_admin` is true, both HTTP and stream proxy are enabled with the configuration above. - -If you have set the `enable_admin` to false, and need to enable both HTTP and stream proxy, set the `only` to false: - -```yaml -apisix: - enable_admin: false - stream_proxy: - only: false - tcp: - - 9100 -``` - If `apisix.stream_proxy` is undefined in `conf/config.yaml`, you will encounter an error similar to the following and not be able to add a stream route: ``` diff --git a/docs/en/latest/terminology/plugin.md b/docs/en/latest/terminology/plugin.md index 2c8e260ef49a..051fef32b07d 100644 --- a/docs/en/latest/terminology/plugin.md +++ b/docs/en/latest/terminology/plugin.md @@ -30,58 +30,83 @@ description: This article introduces the related information of the APISIX Plugi ## Description -This represents the configuration of the plugins that are executed during the HTTP request/response lifecycle. A **Plugin** configuration can be bound directly to a [`Route`](./route.md), a [`Service`](./service.md), a [`Consumer`](./consumer.md) or a [`Plugin Config`](./plugin-config.md). +APISIX Plugins extend APISIX's functionalities to meet organization or user-specific requirements in traffic management, observability, security, request/response transformation, serverless computing, and more. -You can also refer to [Admin API](../admin-api.md#plugin) for how to use this resource. +A **Plugin** configuration can be bound directly to a [`Route`](route.md), [`Service`](service.md), [`Consumer`](consumer.md) or [`Plugin Config`](plugin-config.md). You can refer to [Admin API plugins](../admin-api.md#plugin) for how to use this resource. -:::note +If existing APISIX Plugins do not meet your needs, you can also write your own plugins in Lua or other languages such as Java, Python, Go, and Wasm. -While configuring the same plugin, only one copy of the configuration is valid. The order of precedence is always `Consumer` > `Consumer Group` > `Route` > `Plugin Config` > `Service`. +## Plugins installation -::: +APISIX comes with a default configuration file called `config-default.yaml` and a user-defined configuration file called `config.yaml`. These files are located in the `conf` directory. If the same key (e.g. `plugins`) exists in both files, the configuration values for the key in `config.yaml` will overwrite those in `config-default.yaml`. -While configuring APISIX, you can declare the Plugins that are supported by the local APISIX node. This acts as a whitelisting mechanism as Plugins that are not in this whitelist will be automatically ignored. So, this feature can be used to temporarily turn off/turn on specific plugins. +The `plugins` block is where you can declare the Plugins loaded to your APISIX instance: -## Adding a Plugin +```yaml +plugins: + - real-ip # loaded + - ai + - client-control + - proxy-control + - request-id + - zipkin + # - skywalking # not loaded +... +``` -For adding new plugins based on existing plugins, copy the data in the `plugins` node from the default configuration file `conf/config-default.yaml` to your configuration file (`conf/config.yaml`). +## Plugins execution lifecycle -In a request, a Plugin is only executed once. This is true even if it is bound to multiple different objects like Routes and Services. The order in which Plugins are run is determined by its configured priorities: +An installed plugin is first initialized. The configuration of the plugin is then checked against the defined [JSON Schema](https://json-schema.org) to make sure the plugins configuration schema is correct. -```lua -local _M = { - version = 0.1, - priority = 0, -- the priority of this plugin will be 0 - name = plugin_name, - schema = schema, - metadata_schema = metadata_schema, -} -``` +When a request goes through APISIX, the plugin's corresponding methods are executed in one or more of the following phases : `rewrite`, `access`, `before_proxy`, `header_filter`, `body_filter`, and `log`. These phases are largely influenced by the [OpenResty directives](https://openresty-reference.readthedocs.io/en/latest/Directives/). + +
+
+Routes Diagram +
+
+ +## Plugins execution order + +In general, plugins are executed in the following order: + +1. Plugins in [global rules](./global-rule.md) + 1. plugins in rewrite phase + 2. plugins in access phase + +2. Plugins bound to other objects + 1. plugins in rewrite phase + 2. plugins in access phase -A Plugin configuration is submitted as part of the Route or Service and is placed under `plugins`. It internally uses the Plugin name as the hash key to holding the configuration items for the different Plugins. +Within each phase, you can optionally define a new priority number in the `_meta.priority` field of the plugin, which takes precedence over the default plugins priority during execution. Plugins with higher priority numbers are executed first. + +For example, if you want to have `limit-count` (priority 1002) run before `ip-restriction` (priority 3000) when requests hit a route, you can do so by passing a higher priority number to `_meta.priority` field of `limit-count`: ```json { - ... - "plugins": { - "limit-count": { - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr" - }, - "prometheus": {} + ..., + "plugins": { + "limit-count": { + ..., + "_meta": { + "priority": 3010 + } } + } } ``` -Not all Plugins have specific configuration items (for example, [prometheus](/docs/apisix/plugins/prometheus/)). In such cases, an empty object identifier can be used. +To reset the priority of this plugin instance to the default, simply remove the `_meta.priority` field from your plugin configuration. -A warning level log as shown below indicates that the request was rejected by the Plugin. +## Plugins merging precedence -```shell -ip-restriction exits with http status code 403 -``` +When the same plugin is configured both globally in a global rule and locally in an object (e.g. a route), both plugin instances are executed sequentially. + +However, if the same plugin is configured locally on multiple objects, such as on [Route](./route.md), [Service](./service.md), [Consumer](./consumer.md), [Consumer Group](./consumer-group.md), or [Plugin Config](./plugin-config.md), only one copy of configuration is used as each non-global plugin is only executed once. This is because during execution, plugins configured in these objects are merged with respect to a specific order of precedence: + +`Consumer` > `Consumer Group` > `Route` > `Plugin Config` > `Service` + +such that if the same plugin has different configurations in different objects, the plugin configuration with the highest order of precedence during merging will be used. ## Plugin common configuration @@ -276,6 +301,8 @@ curl http://127.0.0.1:9180/apisix/admin/plugins/reload -H 'X-API-KEY: edd1c9f034 If a configured Plugin is disabled, then its execution will be skipped. +::: + ### Hot reload in standalone mode For hot-reloading in standalone mode, see the plugin related section in [stand alone mode](../deployment-modes.md#standalone). diff --git a/docs/en/latest/tutorials/monitor-api-health-check.md b/docs/en/latest/tutorials/monitor-api-health-check.md new file mode 100644 index 000000000000..e90142b89cfe --- /dev/null +++ b/docs/en/latest/tutorials/monitor-api-health-check.md @@ -0,0 +1,192 @@ +--- +title: Monitor API Health Check with Prometheus +keywords: + - API Health Check + - Monitoring with Prometheus + - API Gateway +description: In this tutorial, we'll guide you on how to enable and monitor API health checks using APISIX and Prometheus. +--- + + + +[APISIX](https://apisix.apache.org/) has a [health check](https://apisix.apache.org/docs/apisix/tutorials/health-check/) mechanism, which proactively checks the health status of the upstream nodes in your system. Also, APISIX integrates with [Prometheus](https://prometheus.io/) through its [plugin](https://apisix.apache.org/docs/apisix/plugins/prometheus/) that exposes upstream nodes (multiple instances of a backend API service that APISIX manages) health check metrics on the Prometheus metrics endpoint typically, on URL path **`/apisix/prometheus/metrics`**. + +In this tutorial, we'll guide you on how to **enable and monitor API health checks** using APISIX and Prometheus. + +## Prerequisite(s) + +- Before you start, it is good to have a basic understanding of APISIX. Familiarity with [API gateway](https://apisix.apache.org/docs/apisix/terminology/api-gateway/), and its key concepts such as [routes](https://docs.api7.ai/apisix/key-concepts/routes), [upstream](https://docs.api7.ai/apisix/key-concepts/upstreams), [Admin API](https://apisix.apache.org/docs/apisix/admin-api/), [plugins](https://docs.api7.ai/apisix/key-concepts/plugins), and HTTP protocol will also be beneficial. +- [Docker](https://docs.docker.com/get-docker/) is used to install the containerized etcd and APISIX. +- Install [cURL](https://curl.se/) to send requests to the services for validation. + +## Start the APISIX demo project + +This project leverages the pre-defined [Docker Compose configuration](https://github.com/apache/apisix-docker/blob/master/example/docker-compose.yml) file to set up, deploy and run APISIX, etcd, Prometheus, and other services with a single command. First, clone the [apisix-docker](https://github.com/apache/apisix-docker) repo on GitHub and open it in your favorite editor, navigate to `/example` folder, and start the project by simply running `docker compose up` from the folder. + +When you start the project, Docker downloads any images it needs to run. You can see the full list of services in [docker-compose.yaml](https://github.com/apache/apisix-docker/blob/master/example/docker-compose.yml) file. + +## Add health check API endpoints in upstream + +To check API health periodically, APISIX needs an HTTP path of the health endpoint of the upstream service. So, you need first to add `/health` endpoint for your backend service. From there, you inspect the most relevant metrics for that service such as memory usage, database connectivity, response duration, and more. Assume that we have two backend REST API services web1 and web2 running using the demo project and each has its **own health check** endpoint at URL path `/health`. At this point, you do not need to make additional configurations. In reality, you can replace them with your backend services. + +> The simplest and standardized way to validate the status of a service is to define a new [health check](https://datatracker.ietf.org/doc/html/draft-inadarei-api-health-check) endpoint like `/health` or `/status` + +## Setting Up Health Checks in APISIX + +This process involves checking the operational status of the 'upstream' nodes. APISIX provides two types of health checks: **Active checks** and **Passive Checks** respectively. Read more about Health Checks and how to enable them [here](https://apisix.apache.org/docs/apisix/tutorials/health-check/). Use the [Admin API](https://apisix.apache.org/docs/apisix/admin-api/) to create an Upstream object. Here is an example of creating an [Upstream](https://apisix.apache.org/docs/apisix/terminology/upstream/) object with two nodes (Per each backend service we defined) and configuring the health check parameters in the upstream object: + +```bash +curl "http://127.0.0.1:9180/apisix/admin/upstreams/1" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d ' +{ + "nodes":{ + "web1:80":1, + "web2:80":1 + }, + "checks":{ + "active":{ + "timeout":5, + "type":"http", + "http_path":"/health", + "healthy":{ + "interval":2, + "successes":1 + }, + "unhealthy":{ + "interval":1, + "http_failures":2 + } + } + } +}' +``` + +This example configures an active health check on the **`/health`** endpoint of the node. It considers the node healthy after **one successful health check** and unhealthy **after two failed health checks**. + +> Note that sometimes you might need the IP addresses of upstream nodes, not their domains (`web1` and `web2`) if you are running services outside docker network. Health check will be started only if the number of nodes (resolved IPs) is bigger than 1. + +## Enable the Prometheus Plugin + +Create a global rule to enable the `prometheus` plugin on all routes by adding `"prometheus": {}` in the plugins option. APISIX gathers internal runtime metrics and exposes them through port `9091` and URI path `/apisix/prometheus/metrics` by default that Prometheus can scrape. It is also possible to customize the export port and **URI path**, **add** **extra labels, the frequency of these scrapes, and other parameters** by configuring them in the Prometheus configuration `/prometheus_conf/prometheus.yml`file. + +```bash +curl "http://127.0.0.1:9180/apisix/admin/global_rules" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d ' +{ + "id":"rule-for-metrics", + "plugins":{ + "prometheus":{ + } + } +}' +``` + +## Create a Route + +Create a [Route](https://apisix.apache.org/docs/apisix/terminology/route/) object to route incoming requests to upstream nodes: + +```bash +curl "http://127.0.0.1:9180/apisix/admin/routes/1" -H "X-API-KEY: edd1c9f034335f136f87ad84b625c8f1" -X PUT -d ' +{ + "name":"backend-service-route", + "methods":[ + "GET" + ], + "uri":"/", + "upstream_id":"1" +}' +``` + +## Send validation requests to the route + +To generate some metrics, you try to send few requests to the route we created in the previous step: + +```bash +curl -i -X GET "http://localhost:9080/" +``` + +If you run the above requests a couple of times, you can see from responses that APISX routes some requests to `node1` and others to `node2`. That’s how Gateway load balancing works! + +```bash +HTTP/1.1 200 OK +Content-Type: text/plain; charset=utf-8 +Content-Length: 10 +Connection: keep-alive +Date: Sat, 22 Jul 2023 10:16:38 GMT +Server: APISIX/3.3.0 + +hello web2 + +... + +HTTP/1.1 200 OK +Content-Type: text/plain; charset=utf-8 +Content-Length: 10 +Connection: keep-alive +Date: Sat, 22 Jul 2023 10:16:39 GMT +Server: APISIX/3.3.0 + +hello web1 +``` + +## Collecting health check data with the Prometheus plugin + +Once the health checks and route are configured in APISIX, you can employ Prometheus to monitor health checks. APISIX **automatically exposes health check metrics data** for your APIs if the health check parameter is enabled for upstream nodes. You will see metrics in the response after fetching them from APISIX: + +```bash +curl -i http://127.0.0.1:9091/apisix/prometheus/metrics +``` + +Example Output: + +```bash +# HELP apisix_http_requests_total The total number of client requests since APISIX started +# TYPE apisix_http_requests_total gauge +apisix_http_requests_total 119740 +# HELP apisix_http_status HTTP status codes per service in APISIX +# TYPE apisix_http_status counter +apisix_http_status{code="200",route="1",matched_uri="/",matched_host="",service="",consumer="",node="172.27.0.5"} 29 +apisix_http_status{code="200",route="1",matched_uri="/",matched_host="",service="",consumer="",node="172.27.0.7"} 12 +# HELP apisix_upstream_status Upstream status from health check +# TYPE apisix_upstream_status gauge +apisix_upstream_status{name="/apisix/upstreams/1",ip="172.27.0.5",port="443"} 0 +apisix_upstream_status{name="/apisix/upstreams/1",ip="172.27.0.5",port="80"} 1 +apisix_upstream_status{name="/apisix/upstreams/1",ip="172.27.0.7",port="443"} 0 +apisix_upstream_status{name="/apisix/upstreams/1",ip="172.27.0.7",port="80"} 1 +``` + +Health check data is represented with metrics label `apisix_upstream_status`. It has attributes like upstream `name`, `ip` and `port`. A value of 1 represents healthy and 0 means the upstream node is unhealthy. + +## Visualize the data in the Prometheus dashboard + +Navigate to http://localhost:9090/ where the Prometheus instance is running in Docker and type **Expression** `apisix_upstream_status` in the search bar. You can also see the output of the health check statuses of upstream nodes on the **Prometheus dashboard** in the table or graph view: + +![Visualize the data in Prometheus dashboard](https://static.apiseven.com/uploads/2023/07/20/OGBtqbDq_output.png) + +## Next Steps + +You have now learned how to set up and monitor API health checks with Prometheus and APISIX. APISIX Prometheus plugin is configured to connect [Grafana](https://grafana.com/) automatically to visualize metrics. Keep exploring the data and customize the [Grafana dashboard](https://grafana.com/grafana/dashboards/11719-apache-apisix/) by adding a panel that shows the number of active health checks. + +### Related resources + +- [Monitoring API Metrics: How to Ensure Optimal Performance of Your API?](https://api7.ai/blog/api7-portal-monitor-api-metrics) +- [Monitoring Microservices with Prometheus and Grafana](https://api7.ai/blog/introduction-to-monitoring-microservices) + +### Recommended content + +- [Implementing resilient applications with API Gateway (Health Check)](https://dev.to/apisix/implementing-resilient-applications-with-api-gateway-health-check-338c) diff --git a/docs/zh/latest/CHANGELOG.md b/docs/zh/latest/CHANGELOG.md index eda9e3c22532..be16cb06949a 100644 --- a/docs/zh/latest/CHANGELOG.md +++ b/docs/zh/latest/CHANGELOG.md @@ -23,6 +23,7 @@ title: CHANGELOG ## Table of Contents +- [3.4.0](#340) - [3.3.0](#330) - [3.2.1](#321) - [3.2.0](#320) @@ -70,6 +71,38 @@ title: CHANGELOG - [0.7.0](#070) - [0.6.0](#060) +## 3.4.0 + +### Core + +- :sunrise: 支持路由级别的 MTLS [#9322](https://github.com/apache/apisix/pull/9322) +- :sunrise: 支持全局规则的 id schema [#9517](https://github.com/apache/apisix/pull/9517) +- :sunrise: 支持使用单个长连接来监视 etcd 的所有资源 [#9456](https://github.com/apache/apisix/pull/9456) +- :sunrise: 支持 ssl 标签的最大长度为 256 [#9301](https://github.com/apache/apisix/pull/9301) + +### Plugins + +- :sunrise: 支持 proxy_rewrite 插件的多个正则表达式匹配 [#9194](https://github.com/apache/apisix/pull/9194) +- :sunrise: 添加 loki-logger 插件 [#9399](https://github.com/apache/apisix/pull/9399) +- :sunrise: 允许用户为 prometheus 插件配置 DEFAULT_BUCKETS [#9673](https://github.com/apache/apisix/pull/9673) + +### Bugfixes + +- 修复 (body-transformer):xml2lua 将空表替换为空字符串 [#9669](https://github.com/apache/apisix/pull/9669) +- 修复:opentelemetry 和 grpc-transcode 插件无法同时启用 [#9606](https://github.com/apache/apisix/pull/9606) +- 修复 (skywalking-logger, error-log-logger):支持在 skywalking service_instance_name 中使用 $hostname [#9401](https://github.com/apache/apisix/pull/9401) +- 修复 (admin):修复 secrets 不支持通过 PATCH 更新属性 [#9510](https://github.com/apache/apisix/pull/9510) +- 修复 (http-logger):默认请求路径应为'/' [#9472](https://github.com/apache/apisix/pull/9472) +- 修复:syslog 插件不起作用 [#9425](https://github.com/apache/apisix/pull/9425) +- 修复:splunk-hec-logging 的日志格式错误 [#9478](https://github.com/apache/apisix/pull/9478) +- 修复:etcd 复用 cli 并启用 keepalive [#9420](https://github.com/apache/apisix/pull/9420) +- 修复:upstream key 添加 mqtt_client_id 支持 [#9450](https://github.com/apache/apisix/pull/9450) +- 修复:body-transformer 插件总是返回原始 body [#9446](https://github.com/apache/apisix/pull/9446) +- 修复:当 consumer 使用 wolf-rbac 插件时,consumer 中的其他插件无效 [#9298](https://github.com/apache/apisix/pull/9298) +- 修复:当 host 是域名时,总是解析域名 [#9332](https://github.com/apache/apisix/pull/9332) +- 修复:response-rewrite 插件不能只添加一个字符 [#9372](https://github.com/apache/apisix/pull/9372) +- 修复:consul 支持只获取 health endpoint [#9204](https://github.com/apache/apisix/pull/9204) + ## 3.3.0 ### Change diff --git a/docs/zh/latest/admin-api.md b/docs/zh/latest/admin-api.md index 5e623d990794..e1fd063e8a61 100644 --- a/docs/zh/latest/admin-api.md +++ b/docs/zh/latest/admin-api.md @@ -105,6 +105,46 @@ deployment: 首先查找环境变量 `ADMIN_KEY`,如果该环境变量不存在,它将使用 `edd1c9f034335f136f87ad84b625c8f1` 作为默认值。 +您还可以在 yaml 键中指定环境变量。这在 `standalone` 模式 中特别有用,您可以在其中指定上游节点,如下所示: + +```yaml title="./conf/apisix.yaml" +routes: + - + uri: "/test" + upstream: + nodes: + "${{HOST_IP}}:${{PORT}}": 1 + type: roundrobin +#END +``` + +### 强制删除 {#force-delete} + +默认情况下,Admin API 会检查资源间的引用关系,将会拒绝删除正在使用中的资源。 + +可以通过在删除请求中添加请求参数 `force=true` 来进行强制删除,例如: + +```bash +$ curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{ + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" +}' +$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d '{ + "uri": "/*", + "upstream_id": 1 +}' +{"value":{"priority":0,"upstream_id":1,"uri":"/*","create_time":1689038794,"id":"1","status":1,"update_time":1689038916},"key":"/apisix/routes/1"} + +$ curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"error_msg":"can not delete this upstream, route [1] is still using it now"} +$ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1?force=anyvalue" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"error_msg":"can not delete this upstream, route [1] is still using it now"} +$ curl "http://127.0.0.1:9180/apisix/admin/upstreams/1?force=true" -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE +{"deleted":"1","key":"/apisix/upstreams/1"} +``` + ## v3 版本新功能 {#v3-new-function} 在 APISIX v3 版本中,Admin API 支持了一些不向下兼容的新特性,比如支持新的响应体格式、支持分页查询、支持过滤资源等。 @@ -1175,6 +1215,7 @@ SSL 资源请求地址:/apisix/admin/ssls/{id} | update_time | 否 | 辅助 | epoch 时间戳,单位为秒。如果不指定则自动创建。 | 1602883670 | | type | 否 | 辅助 | 标识证书的类型,默认值为 `server`。 | `client` 表示证书是客户端证书,APISIX 访问上游时使用;`server` 表示证书是服务端证书,APISIX 验证客户端请求时使用。 | | status | 否 | 辅助 | 当设置为 `1` 时,启用此 SSL,默认值为 `1`。 | `1` 表示启用,`0` 表示禁用 | +| ssl_protocols | 否 | tls 协议字符串数组 | 用于控制服务器与客户端之间使用的 SSL/TLS 协议版本。更多的配置示例,请参考[SSL 协议](./ssl-protocol.md)。 | | SSL 对象 JSON 配置示例: @@ -1319,6 +1360,14 @@ Content-Type: text/plain Plugin 资源请求地址:/apisix/admin/plugins/{plugin_name} +### 请求参数 + +| 名称 | 描述 | 默认 | +| --------- | -------------------------------------- | -------- | +| subsystem | 插件子系统。 | http | + +可以在子系统上过滤插件,以便在通过查询参数传递的子系统中搜索 ({plugin_name}) + ### 请求方法 {#plugin-request-methods} | 名称        | 请求 URI | 请求 body | 描述          | @@ -1346,7 +1395,7 @@ Plugin 资源请求地址:/apisix/admin/plugins/{plugin_name} - 获取指定插件的属性 ```shell - curl "http://127.0.0.1:9180/apisix/admin/plugins/key-auth" \ + curl "http://127.0.0.1:9180/apisix/admin/plugins/key-auth?subsystem=http" \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' ``` @@ -1358,7 +1407,7 @@ Plugin 资源请求地址:/apisix/admin/plugins/{plugin_name} 你可以使用 `/apisix/admin/plugins?all=true` 接口获取所有插件的所有属性,每个插件包括 `name`,`priority`,`type`,`schema`,`consumer_schema` 和 `version`。 -默认情况下,该接口只返回 L7 插件。如果你需要获取 L4 / Stream 插件,需要使用 `/apisix/admin/plugins?all=true&subsystem=stream`。 +您可以使用“/apisix/admin/plugins?all=true”获取所有插件的所有属性。这个 API 将很快被弃用 ::: diff --git a/docs/zh/latest/building-apisix.md b/docs/zh/latest/building-apisix.md index 8966eeb7b085..95672c82b1c7 100644 --- a/docs/zh/latest/building-apisix.md +++ b/docs/zh/latest/building-apisix.md @@ -53,7 +53,7 @@ curl https://raw.githubusercontent.com/apache/apisix/master/utils/install-depend 然后,创建一个目录并设置环境变量 `APISIX_VERSION`: ```shell -APISIX_VERSION='3.3.0' +APISIX_VERSION='3.4.0' mkdir apisix-${APISIX_VERSION} ``` diff --git a/docs/zh/latest/certificate.md b/docs/zh/latest/certificate.md index 7c2648458e23..1d5c8a825f9c 100644 --- a/docs/zh/latest/certificate.md +++ b/docs/zh/latest/certificate.md @@ -33,85 +33,79 @@ SNI(Server Name Indication)是用来改善 SSL 和 TLS 的一项特性,它 * `key`:SSL 密钥对的私钥,pem 格式 * `snis`:SSL 证书所指定的一个或多个域名,注意在设置这个参数之前,你需要确保这个证书对应的私钥是有效的。 -为了简化示例,我们会使用下面的 Python 脚本: - -```python title="create-ssl.py" -#!/usr/bin/env python -# coding: utf-8 -import sys -# sudo pip install requests -import requests - -if len(sys.argv) <= 3: - print("bad argument") - sys.exit(1) -with open(sys.argv[1]) as f: - cert = f.read() -with open(sys.argv[2]) as f: - key = f.read() -sni = sys.argv[3] -api_key = "edd1c9f034335f136f87ad84b625c8f1" -resp = requests.put("http://127.0.0.1:9180/apisix/admin/ssls/1", json={ - "cert": cert, - "key": key, - "snis": [sni], -}, headers={ - "X-API-KEY": api_key, -}) -print(resp.status_code) -print(resp.text) -``` +创建一个包含证书和密钥,单一域名 SNI 的 SSL 对象: ```shell -# 创建 SSL 对象 -./create-ssl.py t.crt t.key test.com +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat t/certs/apisix.crt)"'", + "key": "'"$(cat t/certs/apisix.key)"'", + "snis": ["test.com"] +}' +``` -# 创建 Router 对象 +创建路由: + +```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d ' { - "uri": "/hello", + "uri": "/get", "hosts": ["test.com"], "methods": ["GET"], "upstream": { "type": "roundrobin", "nodes": { - "127.0.0.1:1980": 1 + "httpbin.org": 1 } } }' +``` -# 测试一下 +测试: + +```shell +curl --resolve 'test.com:9443:127.0.0.1' https://test.com:9443/get -k -vvv -curl --resolve 'test.com:9443:127.0.0.1' https://test.com:9443/hello -vvv * Added test.com:9443:127.0.0.1 to DNS cache * About to connect() to test.com port 9443 (#0) * Trying 127.0.0.1... * Connected to test.com (127.0.0.1) port 9443 (#0) -* Initializing NSS with certpath: sql:/etc/pki/nssdb -* skipping SSL peer certificate verification -* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 +* ALPN, server accepted to use h2 * Server certificate: -* subject: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN -* start date: Jun 24 22:18:05 2019 GMT -* expire date: May 31 22:18:05 2119 GMT -* common name: test.com -* issuer: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN -> GET /hello HTTP/1.1 -> User-Agent: curl/7.29.0 +* subject: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com +* start date: Jun 24 22:18:05 2019 GMT +* expire date: May 31 22:18:05 2119 GMT +* issuer: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com +* SSL certificate verify result: self-signed certificate (18), continuing anyway. +> GET /get HTTP/2 > Host: test.com:9443 -> Accept: */* +> user-agent: curl/7.81.0 +> accept: */* ``` ### 泛域名 -一个 SSL 证书的域名也可能包含泛域名,如 `*.test.com`,它代表所有以 `test.com` 结尾的域名都可以使用该证书。 -比如 `*.test.com`,可以匹配 `www.test.com`、`mail.test.com`。 +一个 SSL 证书的域名也可能包含泛域名,如 `*.test.com`,它代表所有以 `test.com` 结尾的域名都可以使用该证书。比如 `*.test.com`,可以匹配 `www.test.com`、`mail.test.com`。 + +以下是在 APISIX 中配置泛域名 SNI 的 SSL 证书的示例。 -看下面这个例子,请注意我们把 `*.test.com` 作为 sni 传递进来: +创建一个包含证书和密钥,泛域名 SNI 的 SSL 对象: ```shell -./create-ssl.py t.crt t.key '*.test.com' +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat t/certs/apisix.crt)"'", + "key": "'"$(cat t/certs/apisix.key)"'", + "snis": ["*.test.com"] +}' +``` + +创建路由: +```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -i -d ' { "uri": "/hello", @@ -124,33 +118,34 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 } } }' +``` -# 测试一下 +测试: -curl --resolve 'www.test.com:9443:127.0.0.1' https://www.test.com:9443/hello -vvv -* Added test.com:9443:127.0.0.1 to DNS cache -* About to connect() to test.com port 9443 (#0) -* Trying 127.0.0.1... -* Connected to test.com (127.0.0.1) port 9443 (#0) -* Initializing NSS with certpath: sql:/etc/pki/nssdb -* skipping SSL peer certificate verification -* SSL connection using TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 +```shell +curl --resolve 'www.test.com:9443:127.0.0.1' https://www.test.com:9443/get -k -vvv + +* Added www.test.com:9443:127.0.0.1 to DNS cache +* Hostname www.test.com was found in DNS cache +* Trying 127.0.0.1:9443... +* Connected to www.test.com (127.0.0.1) port 9443 (#0) +* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 +* ALPN, server accepted to use h2 * Server certificate: -* subject: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN -* start date: Jun 24 22:18:05 2019 GMT -* expire date: May 31 22:18:05 2119 GMT -* common name: test.com -* issuer: CN=test.com,O=iresty,L=ZhuHai,ST=GuangDong,C=CN -> GET /hello HTTP/1.1 -> User-Agent: curl/7.29.0 -> Host: test.com:9443 -> Accept: */* +* subject: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com +* start date: Jun 24 22:18:05 2019 GMT +* expire date: May 31 22:18:05 2119 GMT +* issuer: C=CN; ST=GuangDong; L=ZhuHai; O=iresty; CN=test.com +* SSL certificate verify result: self signed certificate (18), continuing anyway. +> GET /get HTTP/2 +> Host: www.test.com:9443 +> user-agent: curl/7.74.0 +> accept: */* ``` ### 多域名的情况 -如果一个 SSL 证书包含多个独立域名,比如 `www.test.com` 和 `mail.test.com`, -你可以把它们都放入 `snis` 数组中,就像这样: +如果一个 SSL 证书包含多个独立域名,比如 `www.test.com` 和 `mail.test.com`,你可以把它们都放入 `snis` 数组中,就像这样: ```json { diff --git a/docs/zh/latest/config.json b/docs/zh/latest/config.json index 907c5b23d4e4..7ac2791cad6c 100644 --- a/docs/zh/latest/config.json +++ b/docs/zh/latest/config.json @@ -1,5 +1,5 @@ { - "version": "3.3.0", + "version": "3.4.0", "sidebar": [ { "type": "doc", @@ -104,7 +104,8 @@ "plugins/consumer-restriction", "plugins/csrf", "plugins/public-api", - "plugins/gm" + "plugins/gm", + "plugins/chaitin-waf" ] }, { @@ -300,6 +301,10 @@ { "type": "doc", "id": "profile" + }, + { + "type": "doc", + "id": "ssl-protocol" } ] }, diff --git a/docs/zh/latest/discovery/kubernetes.md b/docs/zh/latest/discovery/kubernetes.md index 0c390b785fce..c4b751889cf3 100644 --- a/docs/zh/latest/discovery/kubernetes.md +++ b/docs/zh/latest/discovery/kubernetes.md @@ -34,12 +34,6 @@ Kubernetes 服务发现以 [_List-Watch_](https://kubernetes.io/docs/reference/u 同时遵循 [_APISIX Discovery 规范_](../discovery.md) 提供了节点查询接口。 -:::note - -在四层中使用 Kubernetes 服务发现要求 OpenResty 版本大于等于 1.19.9.1 - -::: - ## Kubernetes 服务发现的使用 目前 Kubernetes 服务发现支持单集群和多集群模式,分别适用于待发现的服务分布在单个或多个 Kubernetes 的场景。 diff --git a/docs/zh/latest/plugins/api-breaker.md b/docs/zh/latest/plugins/api-breaker.md index 9f522d8e25f0..ff46dfff94a6 100644 --- a/docs/zh/latest/plugins/api-breaker.md +++ b/docs/zh/latest/plugins/api-breaker.md @@ -108,7 +108,7 @@ HTTP/1.1 502 Bad Gateway ``` -## 禁用插件 +## 删除插件 当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/authz-casbin.md b/docs/zh/latest/plugins/authz-casbin.md index 8908456bf0e5..d9fa29305d48 100644 --- a/docs/zh/latest/plugins/authz-casbin.md +++ b/docs/zh/latest/plugins/authz-casbin.md @@ -241,7 +241,7 @@ HTTP/1.1 403 Forbidden curl -i http://127.0.0.1:9080/res -H 'user: alice' -X GET ``` -## 禁用插件 +## 删除插件 当你需要禁用 `authz-casbin` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/authz-casdoor.md b/docs/zh/latest/plugins/authz-casdoor.md index 64d9315783bc..6cc5551f0696 100644 --- a/docs/zh/latest/plugins/authz-casdoor.md +++ b/docs/zh/latest/plugins/authz-casdoor.md @@ -85,7 +85,7 @@ curl "http://127.0.0.1:9180/apisix/admin/routes/1" -H "X-API-KEY: edd1c9f034335f 上述操作完成后,用户就会被重定向到目标 URL。 -## 禁用插件 +## 删除插件 当需要禁用 `authz-casdoor` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/authz-keycloak.md b/docs/zh/latest/plugins/authz-keycloak.md index 986a1cdce793..18979ad48f13 100644 --- a/docs/zh/latest/plugins/authz-keycloak.md +++ b/docs/zh/latest/plugins/authz-keycloak.md @@ -173,7 +173,7 @@ curl http://127.0.0.1:9080/get \ -H 'Authorization: Bearer {JWT Token}' ``` -## 禁用插件 +## 删除插件 当你需要禁用 `authz-keycloak` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/aws-lambda.md b/docs/zh/latest/plugins/aws-lambda.md index ea536f31ec6b..5163b05a9fb3 100644 --- a/docs/zh/latest/plugins/aws-lambda.md +++ b/docs/zh/latest/plugins/aws-lambda.md @@ -199,9 +199,9 @@ Content-Type: application/json "Hello, APISIX!" ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/azure-functions.md b/docs/zh/latest/plugins/azure-functions.md index c2eb94a893b4..99b4439eb649 100644 --- a/docs/zh/latest/plugins/azure-functions.md +++ b/docs/zh/latest/plugins/azure-functions.md @@ -185,9 +185,9 @@ Content-Type: text/plain; charset=utf-8 Hello, APISIX ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/basic-auth.md b/docs/zh/latest/plugins/basic-auth.md index c094d60d0ecb..eaded4128017 100644 --- a/docs/zh/latest/plugins/basic-auth.md +++ b/docs/zh/latest/plugins/basic-auth.md @@ -129,7 +129,7 @@ HTTP/1.1 401 Unauthorized {"message":"Invalid user authorization"} ``` -## 禁用插件 +## 删除插件 当你需要禁用 `basic-auth` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/batch-requests.md b/docs/zh/latest/plugins/batch-requests.md index 276dd805ce08..58a4b37c2760 100644 --- a/docs/zh/latest/plugins/batch-requests.md +++ b/docs/zh/latest/plugins/batch-requests.md @@ -214,7 +214,7 @@ curl --location --request POST 'http://127.0.0.1:9080/apisix/batch-requests' \ ] ``` -## 禁用插件 +## 删除插件 如果你想禁用插件,可以将 `batch-requests` 从配置文件中的插件列表删除,重新加载 APISIX 后即可生效。 diff --git a/docs/zh/latest/plugins/chaitin-waf.md b/docs/zh/latest/plugins/chaitin-waf.md new file mode 100644 index 000000000000..8d8331caac1d --- /dev/null +++ b/docs/zh/latest/plugins/chaitin-waf.md @@ -0,0 +1,253 @@ +--- +title: chaitin-waf +keywords: + - Apache APISIX + - API 网关 + - Plugin + - WAF +description: 本文介绍了关于 Apache APISIX `chaitin-waf` 插件的基本信息及使用方法。 +--- + + + +## 描述 + +在启用 `chaitin-waf` 插件后,流量将被转发给长亭 WAF 服务,用以检测和防止各种 Web 应用程序攻击,以保护应用程序和用户数据的安全。 + +## 响应头 + +根据插件配置,可以选择是否附加额外的响应头。 + +响应头的信息如下: + +- **X-APISIX-CHAITIN-WAF**:APISIX 是否将请求转发给 WAF 服务器。 + - yes:转发 + - no:不转发 + - unhealthy:符合匹配条件,但没有可用的 WAF 服务器 + - err:插件执行过程中出错。此时会附带 **X-APISIX-CHAITIN-WAF-ERROR** 请求头 + - waf-err:与 WAF 服务器交互时出错。此时会附带 **X-APISIX-CHAITIN-WAF-ERROR** 请求头 + - timeout:与 WAF 服务器的交互超时 +- **X-APISIX-CHAITIN-WAF-ERROR**:调试用响应头。APISIX 与 WAF 交互时的错误信息。 +- **X-APISIX-CHAITIN-WAF-TIME**:APISIX 与 WAF 交互所耗费的时间,单位是毫秒。 +- **X-APISIX-CHAITIN-WAF-STATUS**:WAF 服务器返回给 APISIX 的状态码。 +- **X-APISIX-CHAITIN-WAF-ACTION**:WAF 服务器返回给 APISIX 的处理结果。 + - pass:请求合法 + - reject:请求被 WAF 服务器拒绝 +- **X-APISIX-CHAITIN-WAF-SERVER**:调试用响应头。所使用的 WAF 服务器。 + +## 插件元数据 + +| 名称 | 类型 | 必选项 | 默认值 | 描述 | +|--------------------------|---------------|-----|-------|--------------------------------------------| +| nodes | array(object) | 必选 | | 长亭 WAF 的地址列表。 | +| nodes[0].host | string | 必选 | | 长亭 WAF 的地址,支持 IPV4、IPV6、Unix Socket 等配置方式。 | +| nodes[0].port | string | 可选 | 80 | 长亭 WAF 的端口。 | +| config | object | 否 | | 长亭 WAF 服务的配置参数值。当路由没有配置时将使用这里所配置的参数。 | +| config.connect_timeout | integer | 否 | 1000 | connect timeout, 毫秒 | +| config.send_timeout | integer | 否 | 1000 | send timeout, 毫秒 | +| config.read_timeout | integer | 否 | 1000 | read timeout, 毫秒 | +| config.req_body_size | integer | 否 | 1024 | 请求体大小,单位为 KB | +| config.keepalive_size | integer | 否 | 256 | 长亭 WAF 服务的最大并发空闲连接数 | +| config.keepalive_timeout | integer | 否 | 60000 | 空闲链接超时,毫秒 | + +一个典型的示例配置如下: + +```bash +curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/chaitin-waf -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "nodes":[ + { + "host": "unix:/path/to/safeline/resources/detector/snserver.sock", + "port": 8000 + } + ] +}' +``` + +## 属性 + +| 名称 | 类型 | 必选项 | 默认值 | 描述 | +|--------------------------|---------------|-----|-------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| match | array[object] | 否 | | 匹配规则列表,默认为空且规则将被无条件执行。 | +| match.vars | array[array] | 否 | | 由一个或多个 `{var, operator, val}` 元素组成的列表,例如:`{"arg_name", "==", "json"}`,表示当前请求参数 `name` 是 `json`。这里的 `var` 与 NGINX 内部自身变量命名是保持一致,所以也可以使用 `request_uri`、`host` 等;对于已支持的运算符,具体用法请参考 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list) 的 `operator-list` 部分。 | +| append_waf_resp_header | bool | 否 | true | 是否添加响应头 | +| append_waf_debug_header | bool | 否 | false | 是否添加调试用响应头,`add_header` 为 `true` 时才生效 | +| config | object | 否 | | 长亭 WAF 服务的配置参数值。当路由没有配置时将使用元数据里所配置的参数。 | +| config.connect_timeout | integer | 否 | | connect timeout, 毫秒 | +| config.send_timeout | integer | 否 | | send timeout, 毫秒 | +| config.read_timeout | integer | 否 | | read timeout, 毫秒 | +| config.req_body_size | integer | 否 | | 请求体大小,单位为 KB | +| config.keepalive_size | integer | 否 | | 长亭 WAF 服务的最大并发空闲连接数 | +| config.keepalive_timeout | integer | 否 | | 空闲链接超时,毫秒 | + +一个典型的示例配置如下,这里使用 `httpbun.org` 作为示例后端,可以按需替换: + +```bash +curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri": "/*", + "plugins": { + "chaitin-waf": { + "match": [ + { + "vars": [ + ["http_waf","==","true"] + ] + } + ] + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "httpbun.org:80": 1 + } + } +}' +``` + +## 测试插件 + +以上述的示例配置为例进行测试。 + +不满足匹配条件时,请求可以正常触达: + +```bash +curl -H "Host: httpbun.org" http://127.0.0.1:9080/get -i + +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 408 +Connection: keep-alive +X-APISIX-CHAITIN-WAF: no +Date: Wed, 19 Jul 2023 09:30:42 GMT +X-Powered-By: httpbun/3c0dc05883dd9212ac38b04705037d50b02f2596 +Server: APISIX/3.3.0 + +{ + "args": {}, + "headers": { + "Accept": "*/*", + "Connection": "close", + "Host": "httpbun.org", + "User-Agent": "curl/8.1.2", + "X-Forwarded-For": "127.0.0.1", + "X-Forwarded-Host": "httpbun.org", + "X-Forwarded-Port": "9080", + "X-Forwarded-Proto": "http", + "X-Real-Ip": "127.0.0.1" + }, + "method": "GET", + "origin": "127.0.0.1, 122.231.76.178", + "url": "http://httpbun.org/get" +} +``` + +面对潜在的注入请求也原样转发并遇到 404 错误: + +```bash +curl -H "Host: httpbun.org" http://127.0.0.1:9080/getid=1%20AND%201=1 -i + +HTTP/1.1 404 Not Found +Content-Type: text/plain; charset=utf-8 +Content-Length: 19 +Connection: keep-alive +X-APISIX-CHAITIN-WAF: no +Date: Wed, 19 Jul 2023 09:30:28 GMT +X-Content-Type-Options: nosniff +X-Powered-By: httpbun/3c0dc05883dd9212ac38b04705037d50b02f2596 +Server: APISIX/3.3.0 + +404 page not found +``` + +当满足匹配条件时,正常请求依然可以触达上游: + +```bash +curl -H "Host: httpbun.org" -H "waf: true" http://127.0.0.1:9080/get -i + +HTTP/1.1 200 OK +Content-Type: application/json +Content-Length: 427 +Connection: keep-alive +X-APISIX-CHAITIN-WAF-TIME: 2 +X-APISIX-CHAITIN-WAF-STATUS: 200 +X-APISIX-CHAITIN-WAF: yes +X-APISIX-CHAITIN-WAF-ACTION: pass +Date: Wed, 19 Jul 2023 09:29:58 GMT +X-Powered-By: httpbun/3c0dc05883dd9212ac38b04705037d50b02f2596 +Server: APISIX/3.3.0 + +{ + "args": {}, + "headers": { + "Accept": "*/*", + "Connection": "close", + "Host": "httpbun.org", + "User-Agent": "curl/8.1.2", + "Waf": "true", + "X-Forwarded-For": "127.0.0.1", + "X-Forwarded-Host": "httpbun.org", + "X-Forwarded-Port": "9080", + "X-Forwarded-Proto": "http", + "X-Real-Ip": "127.0.0.1" + }, + "method": "GET", + "origin": "127.0.0.1, 122.231.76.178", + "url": "http://httpbun.org/get" +} +``` + +而潜在的攻击请求将会被拦截并返回 403 错误: + +```bash +curl -H "Host: httpbun.org" -H "waf: true" http://127.0.0.1:9080/getid=1%20AND%201=1 -i + +HTTP/1.1 403 Forbidden +Date: Wed, 19 Jul 2023 09:29:06 GMT +Content-Type: text/plain; charset=utf-8 +Transfer-Encoding: chunked +Connection: keep-alive +X-APISIX-CHAITIN-WAF: yes +X-APISIX-CHAITIN-WAF-TIME: 2 +X-APISIX-CHAITIN-WAF-ACTION: reject +X-APISIX-CHAITIN-WAF-STATUS: 403 +Server: APISIX/3.3.0 +Set-Cookie: sl-session=UdywdGL+uGS7q8xMfnJlbQ==; Domain=; Path=/; Max-Age=86400 + +{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "51a268653f2c4189bfa3ec66afbcb26d"} +``` + +## 删除插件 + +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: + +```bash +$ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri": "/*", + "upstream": { + "type": "roundrobin", + "nodes": { + "httpbun.org:80": 1 + } + } +}' +``` diff --git a/docs/zh/latest/plugins/clickhouse-logger.md b/docs/zh/latest/plugins/clickhouse-logger.md index b3851377c6b5..09b07f0b63b2 100644 --- a/docs/zh/latest/plugins/clickhouse-logger.md +++ b/docs/zh/latest/plugins/clickhouse-logger.md @@ -134,9 +134,9 @@ curl 'http://localhost:8123/?query=select%20*%20from%20default.test' 127.0.0.1 127.0.0.1 1 2023-05-08T19:15:53+05:30 ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/client-control.md b/docs/zh/latest/plugins/client-control.md index a86cdb95dbfd..581d6c6877ff 100644 --- a/docs/zh/latest/plugins/client-control.md +++ b/docs/zh/latest/plugins/client-control.md @@ -87,9 +87,9 @@ HTTP/1.1 413 Request Entity Too Large ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/consumer-restriction.md b/docs/zh/latest/plugins/consumer-restriction.md index 6a4b5f841e7d..55a43234de34 100644 --- a/docs/zh/latest/plugins/consumer-restriction.md +++ b/docs/zh/latest/plugins/consumer-restriction.md @@ -32,14 +32,16 @@ description: Consumer Restriction 插件允许用户根据 Route、Service 或 C ## 属性 -| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | -| --------- | ------------- | ------ | -----------------| -------------------------|------------------------| -| type | string | 否 | consumer_name | ["consumer_name", "consumer_group_id", "service_id", "route_id"] | 支持设置访问限制的对象类型。 | -| whitelist | array[string] | 是 | | | 加入白名单的对象,优先级高于 `allowed_by_methods`。 | -| blacklist | array[string] | 是 | | | 加入黑名单的对象,优先级高于 `whitelist`。 | -| rejected_code | integer | 否 | 403 | [200,...] | 当请求被拒绝时,返回的 HTTP 状态码。 | -| rejected_msg | string | 否 | | | 当请求被拒绝时,返回的错误信息。 | -| allowed_by_methods | array[object] | 否 | | ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE", "PURGE"] | 为 Consumer 设置的允许的 HTTP 方法列表。 | +| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | +| -------------------------- | ------------- | ------ | ------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | +| type | string | 否 | consumer_name | ["consumer_name", "consumer_group_id", "service_id", "route_id"] | 支持设置访问限制的对象类型。 | +| whitelist | array[string] | 是 | | | 加入白名单的对象,优先级高于`allowed_by_methods`。 | +| blacklist | array[string] | 是 | | | 加入黑名单的对象,优先级高于`whitelist`。 | +| rejected_code | integer | 否 | 403 | [200,...] | 当请求被拒绝时,返回的 HTTP 状态码。 | +| rejected_msg | string | 否 | | | 当请求被拒绝时,返回的错误信息。 | +| allowed_by_methods | array[object] | 否 | | | 一组为 Consumer 设置允许的配置,包括用户名和允许的 HTTP 方法列表。 | +| allowed_by_methods.user | string | 否 | | | 为 Consumer 设置的用户名。 | +| allowed_by_methods.methods | array[string] | 否 | | ["GET", "POST", "PUT", "DELETE", "PATCH", "HEAD", "OPTIONS", "CONNECT", "TRACE", "PURGE"] | 为 Consumer 设置的允许的 HTTP 方法列表。 | :::note @@ -320,9 +322,9 @@ HTTP/1.1 403 Forbidden {"message":"The service_id is forbidden."} ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/zh/latest/plugins/cors.md b/docs/zh/latest/plugins/cors.md index 48a627a553b5..e9c60d465a38 100644 --- a/docs/zh/latest/plugins/cors.md +++ b/docs/zh/latest/plugins/cors.md @@ -99,7 +99,7 @@ curl http://127.0.0.1:9080/hello -v ... ``` -## 禁用插件 +## 删除插件 当你需要禁用 `cors` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/csrf.md b/docs/zh/latest/plugins/csrf.md index 7476babfe3e0..555b0038ba39 100644 --- a/docs/zh/latest/plugins/csrf.md +++ b/docs/zh/latest/plugins/csrf.md @@ -127,9 +127,9 @@ curl -i http://127.0.0.1:9080/hello -X POST -H 'apisix-csrf-token: eyJyYW5kb20iO HTTP/1.1 200 OK ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/zh/latest/plugins/datadog.md b/docs/zh/latest/plugins/datadog.md index b2e0f6f165d2..76219dad55ff 100644 --- a/docs/zh/latest/plugins/datadog.md +++ b/docs/zh/latest/plugins/datadog.md @@ -106,7 +106,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 现在,任何对 uri `/hello` 的请求都会生成上述指标,并推送到 Datadog Agent 的 DogStatsD 服务器。 -### 禁用插件 +### 删除插件 删除插件配置中相应的 JSON 配置以禁用 `datadog`。 APISIX 插件是支持热加载的,所以不用重新启动 APISIX,配置就能生效。 diff --git a/docs/zh/latest/plugins/dubbo-proxy.md b/docs/zh/latest/plugins/dubbo-proxy.md index b92509c3c5bb..f8900c7597a2 100644 --- a/docs/zh/latest/plugins/dubbo-proxy.md +++ b/docs/zh/latest/plugins/dubbo-proxy.md @@ -114,7 +114,7 @@ header2: value2 blahblah # "body" will be the body ``` -## 禁用插件 +## 删除插件 当你想在某个路由或服务中禁用 `dubbo-proxy` 插件,非常简单,你可以直接删除插件配置中的 `json` 配置,不需要重启服务就能立即生效: diff --git a/docs/zh/latest/plugins/echo.md b/docs/zh/latest/plugins/echo.md index 020c1553df3c..4ce489f09ccc 100644 --- a/docs/zh/latest/plugins/echo.md +++ b/docs/zh/latest/plugins/echo.md @@ -92,7 +92,7 @@ HTTP/1.1 200 OK before the body modification hello world ``` -## 禁用插件 +## 删除插件 当你需要禁用 `echo` 插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/elasticsearch-logger.md b/docs/zh/latest/plugins/elasticsearch-logger.md index ae69e5ceeb07..d81e41be9c8a 100644 --- a/docs/zh/latest/plugins/elasticsearch-logger.md +++ b/docs/zh/latest/plugins/elasticsearch-logger.md @@ -255,16 +255,16 @@ curl -X GET "http://127.0.0.1:9200/services/_search" | jq . } ``` -### 禁用插件元数据 +### 删除插件元数据 ```shell curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/elasticsearch-logger \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X DELETE ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/error-log-logger.md b/docs/zh/latest/plugins/error-log-logger.md index 8ca4ee771bfc..cc3a34b41b79 100644 --- a/docs/zh/latest/plugins/error-log-logger.md +++ b/docs/zh/latest/plugins/error-log-logger.md @@ -162,7 +162,7 @@ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/error-log-logger \ }' ``` -## 禁用插件 +## 删除插件 当你不再需要该插件时,只需要在 `./conf/config.yaml` 中删除或注释该插件即可。 diff --git a/docs/zh/latest/plugins/ext-plugin-post-resp.md b/docs/zh/latest/plugins/ext-plugin-post-resp.md index 11c4b85659cc..35fb72f7a0de 100644 --- a/docs/zh/latest/plugins/ext-plugin-post-resp.md +++ b/docs/zh/latest/plugins/ext-plugin-post-resp.md @@ -90,7 +90,7 @@ curl -i http://127.0.0.1:9080/index.html 在返回结果中可以看到刚刚配置的 Plugin Runner 已经被触发,同时 `ext-plugin-A` 插件也已经被执行。 -## 禁用插件 +## 删除插件 当你需要禁用 `ext-plugin-post-resp` 插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/ext-plugin-pre-req.md b/docs/zh/latest/plugins/ext-plugin-pre-req.md index c3971e85da1c..3ad41500a749 100644 --- a/docs/zh/latest/plugins/ext-plugin-pre-req.md +++ b/docs/zh/latest/plugins/ext-plugin-pre-req.md @@ -81,7 +81,7 @@ curl -i http://127.0.0.1:9080/index.html 在返回结果中可以看到刚刚配置的 Plugin Runner 已经被触发,同时 `ext-plugin-A` 插件也已经被执行。 -## 禁用插件 +## 删除插件 当你需要禁用 `ext-plugin-pre-req` 插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/fault-injection.md b/docs/zh/latest/plugins/fault-injection.md index 102851d9f1b8..9ef8be668ac0 100644 --- a/docs/zh/latest/plugins/fault-injection.md +++ b/docs/zh/latest/plugins/fault-injection.md @@ -269,7 +269,7 @@ Server: APISIX/2.2 Fault Injection! ``` -## 禁用插件 +## 删除插件 当你需要禁用 `fault-injection` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/file-logger.md b/docs/zh/latest/plugins/file-logger.md index 34f3dcd72c41..313cf9e25307 100644 --- a/docs/zh/latest/plugins/file-logger.md +++ b/docs/zh/latest/plugins/file-logger.md @@ -51,6 +51,7 @@ description: API 网关 Apache APISIX file-logger 插件可用于将日志数据 | log_format | object | 否 | 以 JSON 格式的键值对来声明日志格式。对于值部分,仅支持字符串。如果是以 `$` 开头,则表明是要获取 [APISIX 变量](../apisix-variable.md) 或 [NGINX 内置变量](http://nginx.org/en/docs/varindex.html)。 | | include_resp_body | boolean | 否 | 当设置为 `true` 时,生成的文件包含响应体。 | | include_resp_body_expr | array | 否 | 当 `include_resp_body` 属性设置为 `true` 时,使用该属性并基于 [lua-resty-expr](https://github.com/api7/lua-resty-expr) 进行过滤。如果存在,则仅在表达式计算结果为 `true` 时记录响应。 | +| match | array[] | 否 | 当设置了这个选项后,只有匹配规则的日志才会被记录。`match` 是一个表达式列表,具体请参考 [lua-resty-expr](https://github.com/api7/lua-resty-expr#operator-list)。 | ## 插件元数据设置 @@ -124,9 +125,49 @@ hello, world 访问成功后,你可以在对应的 `logs` 目录下找到 `file.log` 文件。 -## 禁用插件 +## 过滤日志 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +```shell +curl http://127.0.0.1:9180/apisix/admin/routes/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "plugins": { + "file-logger": { + "path": "logs/file.log", + "match": { + { + { "arg_name","==","jack" } + } + } + } + }, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:9001": 1 + } + }, + "uri": "/hello" +}' +``` + +测试: + +```shell +curl -i http://127.0.0.1:9080/hello?name=jack +``` + +在 `logs/file.log` 中可以看到日志记录 + +```shell +curl -i http://127.0.0.1:9080/hello?name=rose +``` + +在 `logs/file.log` 中看不到日志记录 + +## 删除插件 + +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/forward-auth.md b/docs/zh/latest/plugins/forward-auth.md index 80fc7e3c49e1..a7babfe4601e 100644 --- a/docs/zh/latest/plugins/forward-auth.md +++ b/docs/zh/latest/plugins/forward-auth.md @@ -157,7 +157,7 @@ HTTP/1.1 403 Forbidden Location: http://example.com/auth ``` -## 禁用插件 +## 删除插件 当你需要禁用 `forward-auth` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/gm.md b/docs/zh/latest/plugins/gm.md index 49f6d7ad5fc0..68c3d48dded7 100644 --- a/docs/zh/latest/plugins/gm.md +++ b/docs/zh/latest/plugins/gm.md @@ -186,6 +186,6 @@ New, NTLSv1.1, Cipher is ECDHE-SM2-SM4-CBC-SM3 ... ``` -## 禁用插件 +## 删除插件 如果不再使用此插件,可将 `gm` 插件从 `./conf/config.yaml` 配置文件中移除,然后重启 APISIX 或者通过插件热加载的接口触发插件的卸载。 diff --git a/docs/zh/latest/plugins/google-cloud-logging.md b/docs/zh/latest/plugins/google-cloud-logging.md index 2fc51a026e51..c720fa515dfa 100644 --- a/docs/zh/latest/plugins/google-cloud-logging.md +++ b/docs/zh/latest/plugins/google-cloud-logging.md @@ -37,6 +37,7 @@ description: API 网关 Apache APISIX 的 google-cloud-logging 插件可用于 | 名称 | 必选项 | 默认值 | 描述 | | ----------------------- | -------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | | auth_config | 是 | | `auth_config` 和 `auth_file` 必须配置一个。 | +| auth_config.client_email | 是 | | 谷歌服务帐号的 email 参数。 | | auth_config.private_key | 是 | | 谷歌服务帐号的私钥参数。 | | auth_config.project_id | 是 | | 谷歌服务帐号的项目 ID。 | | auth_config.token_uri | 是 | https://oauth2.googleapis.com/token | 请求谷歌服务帐户的令牌的 URI。 | @@ -46,7 +47,7 @@ description: API 网关 Apache APISIX 的 google-cloud-logging 插件可用于 | ssl_verify | 否 | true | 当设置为 `true` 时,启用 `SSL` 验证。 | | resource | 否 | {"type": "global"} | 谷歌监控资源,请参考 [MonitoredResource](https://cloud.google.com/logging/docs/reference/v2/rest/v2/MonitoredResource)。 | | log_id | 否 | apisix.apache.org%2Flogs | 谷歌日志 ID,请参考 [LogEntry](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry)。 | -| log_format | 否 | | 以 JSON 格式的键值对来声明日志格式。对于值部分,仅支持字符串。如果是以 `$` 开头,则表明是要获取 [APISIX 变量](../apisix-variable.md) 或 [NGINX 内置变量](http://nginx.org/en/docs/varindex.html)。 | +| log_format | 否 |{"host": "$host", "@timestamp": "$time_iso8601", "client_ip": "$remote_addr"}| 以 JSON 格式的键值对来声明日志格式。对于值部分,仅支持字符串。如果是以 `$` 开头,则表明是要获取 [APISIX 变量](../apisix-variable.md) 或 [NGINX 内置变量](http://nginx.org/en/docs/varindex.html)。 | 注意:schema 中还定义了 `encrypt_fields = {"auth_config.private_key"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。 @@ -98,6 +99,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \ "google-cloud-logging": { "auth_config":{ "project_id":"apisix", + "client_email":"your service account email@apisix.iam.gserviceaccount.com", "private_key":"-----BEGIN RSA PRIVATE KEY-----your private key-----END RSA PRIVATE KEY-----", "token_uri":"https://oauth2.googleapis.com/token", "scopes":[ @@ -137,6 +139,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \ "google-cloud-logging": { "auth_config":{ "project_id":"apisix", + "client_email":"your service account email@apisix.iam.gserviceaccount.com", "private_key":"-----BEGIN RSA PRIVATE KEY-----your private key-----END RSA PRIVATE KEY-----" } } @@ -167,9 +170,9 @@ hello, world 访问成功后,你可以登录 [Google Cloud Logging Service](https://console.cloud.google.com/logs/viewer) 查看相关日志。 -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/grpc-transcode.md b/docs/zh/latest/plugins/grpc-transcode.md index 35ef481c80a7..4c03f4cf9735 100644 --- a/docs/zh/latest/plugins/grpc-transcode.md +++ b/docs/zh/latest/plugins/grpc-transcode.md @@ -239,7 +239,7 @@ Trailer: grpc-message 上传 proto 文件: ```shell -curl http://127.0.0.1:9080/apisix/admin/proto/1 \ +curl http://127.0.0.1:9080/apisix/admin/protos/1 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "content" : "syntax = \"proto3\"; @@ -309,7 +309,7 @@ Server: APISIX web server 注意返回体中还存在未解码的字段,如果需要解码该字段,需要在上传的 proto 文件中加上该字段对应的 `message type`。 ```shell -curl http://127.0.0.1:9080/apisix/admin/proto/1 \ +curl http://127.0.0.1:9080/apisix/admin/protos/1 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { "content" : "syntax = \"proto3\"; @@ -376,7 +376,7 @@ Server: APISIX web server {"error":{"details":[{"type":"service","message":"The server is out of service","code":1}],"message":"Out of service","code":14}} ``` -## 禁用插件 +## 删除插件 当你需要禁用 `grpc-transcode` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/grpc-web.md b/docs/zh/latest/plugins/grpc-web.md index b64b74385d7b..48077a1c2400 100644 --- a/docs/zh/latest/plugins/grpc-web.md +++ b/docs/zh/latest/plugins/grpc-web.md @@ -76,7 +76,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \ ::: -## 禁用插件 +## 删除插件 当你需要禁用 `grpc-web` 插件时,可以通过如下命令删除相应的 `JSON` 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/gzip.md b/docs/zh/latest/plugins/gzip.md index 7cd485f53d59..c97072d27f17 100644 --- a/docs/zh/latest/plugins/gzip.md +++ b/docs/zh/latest/plugins/gzip.md @@ -96,7 +96,7 @@ Warning: curl to output it to your terminal anyway, or consider "--output Warning: " to save to a file. ``` -## 禁用插件 +## 删除插件 当你需要禁用 `gzip` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/hmac-auth.md b/docs/zh/latest/plugins/hmac-auth.md index de043f2f3ffd..c5a07f5f1efa 100644 --- a/docs/zh/latest/plugins/hmac-auth.md +++ b/docs/zh/latest/plugins/hmac-auth.md @@ -381,9 +381,9 @@ Accept-Ranges: bytes ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/http-logger.md b/docs/zh/latest/plugins/http-logger.md index 53cc67083462..b9bb7674b4e3 100644 --- a/docs/zh/latest/plugins/http-logger.md +++ b/docs/zh/latest/plugins/http-logger.md @@ -115,9 +115,9 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \ curl -i http://127.0.0.1:9080/hello ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/ip-restriction.md b/docs/zh/latest/plugins/ip-restriction.md index e2ac77cc010e..66db9829ade5 100644 --- a/docs/zh/latest/plugins/ip-restriction.md +++ b/docs/zh/latest/plugins/ip-restriction.md @@ -139,7 +139,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 }' ``` -## 禁用插件 +## 删除插件 当你需要禁用 `ip-restriction` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/jwt-auth.md b/docs/zh/latest/plugins/jwt-auth.md index bc5ba62d490c..c06f9e59b557 100644 --- a/docs/zh/latest/plugins/jwt-auth.md +++ b/docs/zh/latest/plugins/jwt-auth.md @@ -252,7 +252,7 @@ Accept-Ranges: bytes ... ``` -## 禁用插件 +## 删除插件 当你需要禁用 `jwt-auth` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/kafka-logger.md b/docs/zh/latest/plugins/kafka-logger.md index dee5510765b3..a704bfbf8ea4 100644 --- a/docs/zh/latest/plugins/kafka-logger.md +++ b/docs/zh/latest/plugins/kafka-logger.md @@ -214,9 +214,9 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \ curl -i http://127.0.0.1:9080/hello ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/key-auth.md b/docs/zh/latest/plugins/key-auth.md index 6a3232b3ffcd..f4a568ec8524 100644 --- a/docs/zh/latest/plugins/key-auth.md +++ b/docs/zh/latest/plugins/key-auth.md @@ -151,7 +151,7 @@ HTTP/1.1 401 Unauthorized {"message":"Invalid API key in request"} ``` -## 禁用插件 +## 删除插件 当你需要禁用 `key-auth` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/ldap-auth.md b/docs/zh/latest/plugins/ldap-auth.md index 8a7d049e4e01..2a4af09bed53 100644 --- a/docs/zh/latest/plugins/ldap-auth.md +++ b/docs/zh/latest/plugins/ldap-auth.md @@ -137,7 +137,7 @@ HTTP/1.1 401 Unauthorized {"message":"Invalid user authorization"} ``` -## 禁用插件 +## 删除插件 当你需要禁用 `ldap-auth` 插件时,可以通过以下命令删除相应的 JSON 配置。APISIX 将自动重新加载,无需重启服务: diff --git a/docs/zh/latest/plugins/limit-conn.md b/docs/zh/latest/plugins/limit-conn.md index c51ba2eb1560..794835e42eb9 100644 --- a/docs/zh/latest/plugins/limit-conn.md +++ b/docs/zh/latest/plugins/limit-conn.md @@ -122,9 +122,9 @@ curl -i http://127.0.0.1:9080/index.html?sleep=20 ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/limit-count.md b/docs/zh/latest/plugins/limit-count.md index ff4e5a724297..467666b8adbd 100644 --- a/docs/zh/latest/plugins/limit-count.md +++ b/docs/zh/latest/plugins/limit-count.md @@ -47,7 +47,7 @@ description: 本文介绍了 Apache APISIX limit-count 插件的相关操作, | group | string | 否 | | 非空 | 配置相同 group 的路由将共享相同的限流计数器。 | | redis_host | string | 否 | | | 当使用 `redis` 限速策略时,Redis 服务节点的地址。**当 `policy` 属性设置为 `redis` 时必选。**| | redis_port | integer | 否 | 6379 | [1,...] | 当使用 `redis` 限速策略时,Redis 服务节点的端口。| -| redis_username | string | 否 | | | Redis 服务器的用户名。当 `policy` 设置为 `redis` 时使用。| +| redis_username | string | 否 | | | 若使用 Redis ACL 进行身份验证(适用于 Redis 版本 >=6.0),则需要提供 Redis 用户名。若使用 Redis legacy 方式 `requirepass` 进行身份验证,则只需将密码配置在 `redis_password`。当 `policy` 设置为 `redis` 时使用。| | redis_password | string | 否 | | | 当使用 `redis` 或者 `redis-cluster` 限速策略时,Redis 服务节点的密码。| | redis_ssl | boolean | 否 | false | | 当使用 `redis` 限速策略时,如果设置为 true,则使用 SSL 连接到 `redis` | | redis_ssl_verify | boolean | 否 | false | | 当使用 `redis` 限速策略时,如果设置为 true,则验证服务器 SSL 证书的有效性,具体请参考 [tcpsock:sslhandshake](https://github.com/openresty/lua-nginx-module#tcpsocksslhandshake). | @@ -299,9 +299,9 @@ Server: APISIX web server {"error_msg":"Requests are too frequent, please try again later."} ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/limit-req.md b/docs/zh/latest/plugins/limit-req.md index 2de947cbfbd8..6f844611dd2f 100644 --- a/docs/zh/latest/plugins/limit-req.md +++ b/docs/zh/latest/plugins/limit-req.md @@ -213,9 +213,9 @@ HTTP/1.1 403 Forbidden ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/log-rotate.md b/docs/zh/latest/plugins/log-rotate.md index 61a6e5b37e17..61a2b9684d5d 100644 --- a/docs/zh/latest/plugins/log-rotate.md +++ b/docs/zh/latest/plugins/log-rotate.md @@ -99,7 +99,7 @@ plugin_attr: 配置完成,你需要重新加载 APISIX。 -## 禁用插件 +## 删除插件 当你不再需要该插件时,只需要在 `./conf/config.yaml` 中删除或注释该插件即可。 diff --git a/docs/zh/latest/plugins/loggly.md b/docs/zh/latest/plugins/loggly.md index df0376092aa5..9c5b74010696 100644 --- a/docs/zh/latest/plugins/loggly.md +++ b/docs/zh/latest/plugins/loggly.md @@ -147,9 +147,9 @@ curl -i http://127.0.0.1:9080/index.html ![Loggly Dashboard](../../../assets/images/plugin/loggly-dashboard.png) -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/mocking.md b/docs/zh/latest/plugins/mocking.md index c6c944f31927..c6ea804c7599 100644 --- a/docs/zh/latest/plugins/mocking.md +++ b/docs/zh/latest/plugins/mocking.md @@ -41,6 +41,7 @@ description: 本文介绍了关于 Apache APISIX `mocking` 插件的基本信息 | response_example| string | 否 | | 返回响应的 Body,支持使用变量,例如 `$remote_addr $consumer_name`,与 `response_schema` 字段二选一。 | | response_schema | object | 否 | | 指定响应的 `jsonschema` 对象,未指定 `response_example` 字段时生效。 | | with_mock_header| boolean| 否 | true | 当设置为 `true` 时,将添加响应头 `x-mock-by: APISIX/{version}`。设置为 `false` 时则不添加该响应头。 | +| response_headers| object | 否 | | 要在模拟响应中添加的标头。示例:`{"X-Foo": "bar", "X-Few": "baz"}` | JSON Schema 在其字段中支持以下类型: @@ -224,7 +225,7 @@ Server: APISIX/2.10.0 {"a":1,"b":2} ``` -## 禁用插件 +## 删除插件 当你需要禁用 `mocking` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/mqtt-proxy.md b/docs/zh/latest/plugins/mqtt-proxy.md index 7cd779787dbc..edee5b141062 100644 --- a/docs/zh/latest/plugins/mqtt-proxy.md +++ b/docs/zh/latest/plugins/mqtt-proxy.md @@ -50,7 +50,6 @@ description: 本文档介绍了 Apache APISIX mqtt-proxy 插件的信息,通 http: 'radixtree_uri' ssl: 'radixtree_sni' stream_proxy: # TCP/UDP proxy - only: false # 如需 HTTP 与 Stream 代理同时生效,需要增加该键值 tcp: # TCP proxy port list - 9100 dns_resolver: @@ -155,9 +154,9 @@ curl 127.0.0.1:9180/apisix/admin/stream_routes/1 \ ::: -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/stream_routes/1 \ diff --git a/docs/zh/latest/plugins/node-status.md b/docs/zh/latest/plugins/node-status.md index 824751d324a8..34231f0d7a35 100644 --- a/docs/zh/latest/plugins/node-status.md +++ b/docs/zh/latest/plugins/node-status.md @@ -97,7 +97,7 @@ Server: APISIX web server | reading | 当前正在读取请求头的连接数。 | | id | APISIX UID 信息,保存在 `./conf/apisix.uid` 文件中。 | -## 禁用插件 +## 删除插件 如果你不再需要该插件,可以从配置文件 (`./conf/config.yaml`) 中删除它: diff --git a/docs/zh/latest/plugins/opa.md b/docs/zh/latest/plugins/opa.md index 1f02964c4f8b..8ca473876dad 100644 --- a/docs/zh/latest/plugins/opa.md +++ b/docs/zh/latest/plugins/opa.md @@ -299,7 +299,7 @@ curl -X GET 127.0.0.1:9080/get } ``` -## 禁用插件 +## 删除插件 当你需要禁用 `opa` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/openfunction.md b/docs/zh/latest/plugins/openfunction.md index 2b3eb8df7fa3..cc88feb6f527 100644 --- a/docs/zh/latest/plugins/openfunction.md +++ b/docs/zh/latest/plugins/openfunction.md @@ -142,7 +142,7 @@ curl http://127.0.0.1:9080/hello/123 Hello, 123! ``` -## 禁用插件 +## 删除插件 当你需要禁用 `openfunction` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/openid-connect.md b/docs/zh/latest/plugins/openid-connect.md index c914f5241511..aececc4986e0 100644 --- a/docs/zh/latest/plugins/openid-connect.md +++ b/docs/zh/latest/plugins/openid-connect.md @@ -60,6 +60,13 @@ description: OpenID Connect(OIDC)是基于 OAuth 2.0 的身份认证协议 | set_refresh_token_header | boolean | 否 | false | | 当设置为 `true` 并且刷新令牌可用时,则会将该属性设置在`X-Refresh-Token`请求头中。 | | session | object | 否 | | | 当设置 bearer_only 为 false 时,openid-connect 插件将使用 Authorization Code 在 IDP 上进行认证,因此你必须设置 session 相关设置。 | | session.secret | string | 是 | 自动生成 | 16 个以上字符 | 用于 session 加密和 HMAC 计算的密钥。 | +| proxy_opts | object | 否 | | | 给 openid-connect 插件配置一个 proxy。 | +| proxy_opts | object | 否 | | | 用来访问身份认证服务器的代理服务器。 | +| proxy_opts.proxy_opts.http_proxy | string | 否 | | http://proxy-server:port | HTTP 代理服务器地址。 | +| proxy_opts.proxy_opts.https_proxy | string | 否 | | http://proxy-server:port | HTTPS 代理服务器地址。 | +| proxy_opts.http_proxy_authorization | string | 否 | | Basic [base64 username:password] | `http_proxy` 默认的 `Proxy-Authorization` 请求头参数值。 | +| proxy_opts.https_proxy_authorization | string | 否 | | Basic [base64 username:password] | 与`http_proxy_authorization`相同,但与`https_proxy`一起使用(因为使用 HTTPS 时,授权是在连接时完成的,因此不能通过传递 Proxy-Authorization 请求头来覆盖此授权)。 | +| proxy_opts.no_proxy | string | 否 | | | 不应被代理的主机的逗号分隔列表。 | 注意:schema 中还定义了 `encrypt_fields = {"client_secret"}`,这意味着该字段将会被加密存储在 etcd 中。具体参考 [加密存储字段](../plugin-develop.md#加密存储字段)。 diff --git a/docs/zh/latest/plugins/opentelemetry.md b/docs/zh/latest/plugins/opentelemetry.md index adb7ca551fc2..e4794c8e2ca8 100644 --- a/docs/zh/latest/plugins/opentelemetry.md +++ b/docs/zh/latest/plugins/opentelemetry.md @@ -124,7 +124,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \ }' ``` -## 禁用插件 +## 删除插件 当你需要禁用 `opentelemetry` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/openwhisk.md b/docs/zh/latest/plugins/openwhisk.md index d69747fa4c4c..061c2955bf00 100644 --- a/docs/zh/latest/plugins/openwhisk.md +++ b/docs/zh/latest/plugins/openwhisk.md @@ -118,9 +118,9 @@ curl -i http://127.0.0.1:9080/hello { "ready": true } ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/prometheus.md b/docs/zh/latest/plugins/prometheus.md index f386d681e875..ea0a9d096639 100644 --- a/docs/zh/latest/plugins/prometheus.md +++ b/docs/zh/latest/plugins/prometheus.md @@ -329,7 +329,7 @@ apisix_upstream_status{name="/apisix/routes/1",ip="100.24.156.8",port="80"} 0 apisix_upstream_status{name="/apisix/routes/1",ip="52.86.68.46",port="80"} 1 ``` -## 禁用插件 +## 删除插件 当你需要禁用 `prometheus` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/proxy-cache.md b/docs/zh/latest/plugins/proxy-cache.md index 27820cc8e8f2..ca4bea58b7ad 100644 --- a/docs/zh/latest/plugins/proxy-cache.md +++ b/docs/zh/latest/plugins/proxy-cache.md @@ -58,22 +58,33 @@ description: 本文介绍了 Apache APISIX proxy-cache 插件的相关操作, 你可以在 APISIX 配置文件 `conf/config.yaml` 中添加你的缓存配置,示例如下: ```yaml title="conf/config.yaml" -proxy_cache: # 代理缓存配置 - cache_ttl: 10s # 如果上游未指定缓存时间,则为默认缓存时间 - zones: # 缓存的参数 - - name: disk_cache_one # 缓存名称(缓存区域),管理员可以通过 admin api 中的 cache_zone 字段指定要使用的缓存区域 - memory_size: 50m # 共享内存的大小,用于存储缓存索引 - disk_size: 1G # 磁盘大小,用于存储缓存数据 - disk_path: "/tmp/disk_cache_one" # 存储缓存数据的路径 - cache_levels: "1:2" # 缓存的层次结构级别 +apisix: + proxy_cache: + cache_ttl: 10s # 如果上游未指定缓存时间,则为默认磁盘缓存时间 + zones: + - name: disk_cache_one + memory_size: 50m + disk_size: 1G + disk_path: /tmp/disk_cache_one + cache_levels: 1:2 + # - name: disk_cache_two + # memory_size: 50m + # disk_size: 1G + # disk_path: "/tmp/disk_cache_two" + # cache_levels: "1:2" + - name: memory_cache + memory_size: 50m ``` -以下示例展示了如何在指定路由上启用 `proxy-cache` 插件,`cache_zone` 字段默认设置为 `disk_cache_one`: +### 使用基于磁盘的缓存 + +以下示例展示了如何在路由上启用 `proxy-cache` 插件。该插件默认使用基于磁盘的 `cache_strategy` 和默认使用`disk_cache_one` 为 `cache_zone`: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { + "uri": "/ip", "plugins": { "proxy-cache": { "cache_key": ["$uri", "-cache-id"], @@ -86,11 +97,35 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \ }, "upstream": { "nodes": { - "127.0.0.1:1999": 1 + "httpbin.org": 1 }, "type": "roundrobin" + } +}' +``` + +### 使用基于内存的缓存 + +以下示例展示了如何在路由上启用 `proxy-cache` 插件,并使用基于内存的 `cache_strategy` 和相应的基于内存的 `cache_zone`。 + +```shell +curl http://127.0.0.1:9180/apisix/admin/routes/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "uri": "/ip", + "plugins": { + "proxy-cache": { + "cache_strategy": "memory", + "cache_zone": "memory_cache", + "cache_ttl": 10 + } }, - "uri": "/hello" + "upstream": { + "nodes": { + "httpbin.org": 1 + }, + "type": "roundrobin" + } }' ``` @@ -99,7 +134,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \ 按上述配置启用插件后,使用 `curl` 命令请求该路由: ```shell -curl http://127.0.0.1:9080/hello -i +curl http://127.0.0.1:9080/ip -i ``` 如果返回 `200` HTTP 状态码,并且响应头中包含 `Apisix-Cache-Status`字段,则表示该插件已启用: @@ -115,7 +150,7 @@ hello 如果你是第一次请求该路由,数据未缓存,那么 `Apisix-Cache-Status` 字段应为 `MISS`。此时再次请求该路由: ```shell -curl http://127.0.0.1:9080/hello -i +curl http://127.0.0.1:9080/ip -i ``` 如果返回的响应头中 `Apisix-Cache-Status` 字段变为 `HIT`,则表示数据已被缓存,插件生效: @@ -135,7 +170,7 @@ hello 为了清除缓存数据,你只需要指定请求的 method 为 `PURGE`: ```shell -curl -i http://127.0.0.1:9080/hello -X PURGE +curl -i http://127.0.0.1:9080/ip -X PURGE ``` HTTP 响应码为 `200` 即表示删除成功,如果缓存的数据未找到将返回 `404`: @@ -146,20 +181,20 @@ HTTP/1.1 200 OK ::: -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { - "uri": "/hello", + "uri": "/ip", "plugins": {}, "upstream": { "type": "roundrobin", "nodes": { - "127.0.0.1:1999": 1 + "httpbin.org": 1 } } }' diff --git a/docs/zh/latest/plugins/proxy-control.md b/docs/zh/latest/plugins/proxy-control.md index 50e6f00c9eec..a64603043bad 100644 --- a/docs/zh/latest/plugins/proxy-control.md +++ b/docs/zh/latest/plugins/proxy-control.md @@ -75,9 +75,9 @@ curl -i http://127.0.0.1:9080/upload -d @very_big_file 如果在错误日志中没有找到关于 "a client request body is buffered to a temporary file" 的信息,则说明插件生效。 -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/proxy-mirror.md b/docs/zh/latest/plugins/proxy-mirror.md index e1c7af5e5f11..5b39a556f996 100644 --- a/docs/zh/latest/plugins/proxy-mirror.md +++ b/docs/zh/latest/plugins/proxy-mirror.md @@ -115,9 +115,9 @@ HTTP/1.1 200 OK hello world ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/proxy-rewrite.md b/docs/zh/latest/plugins/proxy-rewrite.md index 9716fd4911bd..1b5de57d296d 100644 --- a/docs/zh/latest/plugins/proxy-rewrite.md +++ b/docs/zh/latest/plugins/proxy-rewrite.md @@ -103,7 +103,7 @@ curl -X GET http://127.0.0.1:9080/test/index.html 127.0.0.1 - [26/Sep/2019:10:52:20 +0800] iresty.com GET /test/home.html HTTP/1.1 200 38 - curl/7.29.0 - 0.000 199 107 ``` -## 禁用插件 +## 删除插件 当你需要禁用 `proxy-rewrite` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/public-api.md b/docs/zh/latest/plugins/public-api.md index c6ffbe45159f..303fd3c04260 100644 --- a/docs/zh/latest/plugins/public-api.md +++ b/docs/zh/latest/plugins/public-api.md @@ -156,9 +156,9 @@ curl -i 'http://127.0.0.1:9080/gen_token?key=user-key' HTTP/1.1 401 Unauthorized ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/zh/latest/plugins/real-ip.md b/docs/zh/latest/plugins/real-ip.md index 3fa52484b36c..0922541afef1 100644 --- a/docs/zh/latest/plugins/real-ip.md +++ b/docs/zh/latest/plugins/real-ip.md @@ -43,7 +43,7 @@ description: 本文介绍了关于 Apache APISIX `real-ip` 插件的基本信息 | 名称 | 类型 | 必选项 | 有效值 | 描述 | |-------------------|---------------|--|-------------------------------------------------------------|----------------------------------------------------------------------| -| source | string | 是 | 任何 NGINX 变量,如 `arg_realip` 或 `http_x_forwarded_for` 。 | 动态设置客户端的 IP 地址和端口。如果该值不包含端口,则不会更改客户端的端口。| +| source | string | 是 | 任何 NGINX 变量,如 `arg_realip` 或 `http_x_forwarded_for` 。 | 动态设置客户端的 IP 地址和端口,或主机名。如果该值不包含端口,则不会更改客户端的端口。 | | trusted_addresses | array[string] | 否 | IP 或 CIDR 范围列表。 | 动态设置 `set_real_ip_from` 字段。 | | recursive | boolean | 否 | true 或者 false,默认是 false | 如果禁用递归搜索,则与受信任地址之一匹配的原始客户端地址将替换为配置的`source`中发送的最后一个地址。如果启用递归搜索,则与受信任地址之一匹配的原始客户端地址将替换为配置的`source`中发送的最后一个非受信任地址。 | @@ -97,7 +97,7 @@ remote-addr: 1.2.3.4 remote-port: 9080 ``` -## 禁用插件 +## 删除插件 当你需要禁用 `real-ip` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/redirect.md b/docs/zh/latest/plugins/redirect.md index adf45f1677c0..e700a36d59d4 100644 --- a/docs/zh/latest/plugins/redirect.md +++ b/docs/zh/latest/plugins/redirect.md @@ -147,7 +147,7 @@ Location: https://127.0.0.1:9443/hello ... ``` -## 禁用插件 +## 删除插件 当你需要禁用 `redirect` 插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/referer-restriction.md b/docs/zh/latest/plugins/referer-restriction.md index 430f8c112775..73c60b1de5ba 100644 --- a/docs/zh/latest/plugins/referer-restriction.md +++ b/docs/zh/latest/plugins/referer-restriction.md @@ -113,9 +113,9 @@ HTTP/1.1 200 OK ... ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/zh/latest/plugins/request-id.md b/docs/zh/latest/plugins/request-id.md index a44dfc97c9ae..01b49ee9f96a 100644 --- a/docs/zh/latest/plugins/request-id.md +++ b/docs/zh/latest/plugins/request-id.md @@ -86,9 +86,9 @@ HTTP/1.1 200 OK X-Request-Id: fe32076a-d0a5-49a6-a361-6c244c1df956 ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/5 \ diff --git a/docs/zh/latest/plugins/request-validation.md b/docs/zh/latest/plugins/request-validation.md index ac49c50ecf2c..9ea38161c0ae 100644 --- a/docs/zh/latest/plugins/request-validation.md +++ b/docs/zh/latest/plugins/request-validation.md @@ -265,9 +265,9 @@ curl --header "Content-Type: application/json" \ 现在只允许符合已配置规则的有效请求到达上游服务。不符合配置的请求将被拒绝,并返回 `400` 或自定义状态码。 -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/5 \ diff --git a/docs/zh/latest/plugins/response-rewrite.md b/docs/zh/latest/plugins/response-rewrite.md index c7d992ee50c7..a59c34f54806 100644 --- a/docs/zh/latest/plugins/response-rewrite.md +++ b/docs/zh/latest/plugins/response-rewrite.md @@ -225,7 +225,7 @@ X-Server-id: 3 ``` -## 禁用插件 +## 删除插件 当你需要禁用 `response-rewrite` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/rocketmq-logger.md b/docs/zh/latest/plugins/rocketmq-logger.md index 9428e80f5228..afdd40e52a32 100644 --- a/docs/zh/latest/plugins/rocketmq-logger.md +++ b/docs/zh/latest/plugins/rocketmq-logger.md @@ -193,9 +193,9 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \ curl -i http://127.0.0.1:9080/hello ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/server-info.md b/docs/zh/latest/plugins/server-info.md index 9d52c3d01e82..9fa5aac8468b 100644 --- a/docs/zh/latest/plugins/server-info.md +++ b/docs/zh/latest/plugins/server-info.md @@ -102,7 +102,7 @@ curl http://127.0.0.1:9090/v1/server_info -s | jq . ::: -## 禁用插件 +## 删除插件 如果你想禁用插件,可以将 `server-info` 从配置文件中的插件列表删除,重新加载 APISIX 后即可生效。 diff --git a/docs/zh/latest/plugins/serverless.md b/docs/zh/latest/plugins/serverless.md index 058e03758df2..97d6ee6a8a8f 100644 --- a/docs/zh/latest/plugins/serverless.md +++ b/docs/zh/latest/plugins/serverless.md @@ -117,9 +117,9 @@ curl -i http://127.0.0.1:9080/index.html 如果你在 `./logs/error.log` 中发现 `serverless pre function` 和 `match uri /index.html` 两个 error 级别的日志,表示指定的函数已经生效。 -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/skywalking-logger.md b/docs/zh/latest/plugins/skywalking-logger.md index 0b69d614f558..9c3d282bddc8 100644 --- a/docs/zh/latest/plugins/skywalking-logger.md +++ b/docs/zh/latest/plugins/skywalking-logger.md @@ -117,9 +117,9 @@ curl -i http://127.0.0.1:9080/hello 完成上述步骤后,你可以在 SkyWalking UI 查看到相关日志。 -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/skywalking.md b/docs/zh/latest/plugins/skywalking.md index d135b6716b92..324e108174c0 100644 --- a/docs/zh/latest/plugins/skywalking.md +++ b/docs/zh/latest/plugins/skywalking.md @@ -199,7 +199,7 @@ OK ![plugin_skywalking](../../../assets/images/plugin/skywalking-5.png) -## 禁用插件 +## 删除插件 当你需要禁用 `skywalking` 插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/sls-logger.md b/docs/zh/latest/plugins/sls-logger.md index b4d21a463726..16d520521448 100644 --- a/docs/zh/latest/plugins/sls-logger.md +++ b/docs/zh/latest/plugins/sls-logger.md @@ -121,7 +121,7 @@ hello, world * 查看阿里云日志服务上传记录 ![sls logger view](../../../assets/images/plugin/sls-logger-1.png "阿里云日志服务预览") -## 禁用插件 +## 删除插件 想要禁用“sls-logger”插件,是非常简单的,将对应的插件配置从 json 配置删除,就会立即生效,不需要重新启动服务: diff --git a/docs/zh/latest/plugins/splunk-hec-logging.md b/docs/zh/latest/plugins/splunk-hec-logging.md index 7a67f3aad762..57ea2fc37e13 100644 --- a/docs/zh/latest/plugins/splunk-hec-logging.md +++ b/docs/zh/latest/plugins/splunk-hec-logging.md @@ -157,9 +157,9 @@ hello, world ![splunk hec search view](../../../assets/images/plugin/splunk-hec-admin-cn.png) -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过如下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/syslog.md b/docs/zh/latest/plugins/syslog.md index 8e16fe69b86b..357c3038d128 100644 --- a/docs/zh/latest/plugins/syslog.md +++ b/docs/zh/latest/plugins/syslog.md @@ -92,9 +92,9 @@ HTTP/1.1 200 OK hello, world ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/tcp-logger.md b/docs/zh/latest/plugins/tcp-logger.md index 81dbc98badc8..c6a9679580a5 100644 --- a/docs/zh/latest/plugins/tcp-logger.md +++ b/docs/zh/latest/plugins/tcp-logger.md @@ -120,9 +120,9 @@ HTTP/1.1 200 OK hello, world ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/tencent-cloud-cls.md b/docs/zh/latest/plugins/tencent-cloud-cls.md index b32bff50de95..70ca669e5f9a 100644 --- a/docs/zh/latest/plugins/tencent-cloud-cls.md +++ b/docs/zh/latest/plugins/tencent-cloud-cls.md @@ -129,9 +129,9 @@ HTTP/1.1 200 OK hello, world ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/traffic-split.md b/docs/zh/latest/plugins/traffic-split.md index e43146131703..68e52baac842 100644 --- a/docs/zh/latest/plugins/traffic-split.md +++ b/docs/zh/latest/plugins/traffic-split.md @@ -623,9 +623,9 @@ curl http://127.0.0.1:9080/hello -H 'x-api-id: 3' 1980 ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ diff --git a/docs/zh/latest/plugins/ua-restriction.md b/docs/zh/latest/plugins/ua-restriction.md index 3f31e092c2ae..e65a55a890fa 100644 --- a/docs/zh/latest/plugins/ua-restriction.md +++ b/docs/zh/latest/plugins/ua-restriction.md @@ -43,7 +43,7 @@ description: 本文介绍了 Apache APISIX ua-restriction 插件的使用方法 :::note -`allowlist` 和 `denylist` 可以同时启用。同时启用时,插件会根据 `User-Agent` 先检查 `allowlist`,再检查 `denylist`。 +`allowlist` 和 `denylist` 不可以同时启用。 ::: @@ -120,7 +120,7 @@ HTTP/1.1 403 Forbidden {"message":"Not allowed"} ``` -## 禁用插件 +## 删除插件 当你需要禁用 `ua-restriction` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/udp-logger.md b/docs/zh/latest/plugins/udp-logger.md index d9fd580c28c0..703b3ab536fc 100644 --- a/docs/zh/latest/plugins/udp-logger.md +++ b/docs/zh/latest/plugins/udp-logger.md @@ -118,9 +118,9 @@ HTTP/1.1 200 OK hello, world ``` -## 禁用插件 +## 删除插件 -当你需要禁用该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: +当你需要删除该插件时,可通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: ```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' diff --git a/docs/zh/latest/plugins/uri-blocker.md b/docs/zh/latest/plugins/uri-blocker.md index b58a0e692ace..33c92dbf39a5 100644 --- a/docs/zh/latest/plugins/uri-blocker.md +++ b/docs/zh/latest/plugins/uri-blocker.md @@ -89,7 +89,7 @@ Server: APISIX web server ... ``` -## 禁用插件 +## 删除插件 当你需要禁用 `uri-blocker` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/wolf-rbac.md b/docs/zh/latest/plugins/wolf-rbac.md index 231ea08367f7..dad4f26d13ff 100644 --- a/docs/zh/latest/plugins/wolf-rbac.md +++ b/docs/zh/latest/plugins/wolf-rbac.md @@ -269,7 +269,7 @@ HTTP/1.1 200 OK {"message":"success to change password"} ``` -## 禁用插件 +## 删除插件 当你需要禁用 `wolf-rbac` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/workflow.md b/docs/zh/latest/plugins/workflow.md index e4c0b082c5be..0c7228a9e904 100644 --- a/docs/zh/latest/plugins/workflow.md +++ b/docs/zh/latest/plugins/workflow.md @@ -147,7 +147,7 @@ curl http://127.0.0.1:0080/hello/fake -i HTTP/1.1 200 OK ``` -## Disable Plugin +## Delete Plugin 当你需要禁用 `workflow` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/plugins/zipkin.md b/docs/zh/latest/plugins/zipkin.md index 6721fe8cfcca..6f6e0d2b8148 100644 --- a/docs/zh/latest/plugins/zipkin.md +++ b/docs/zh/latest/plugins/zipkin.md @@ -216,7 +216,7 @@ HTTP/1.1 200 OK ![jaeger web-ui trace](../../../assets/images/plugin/jaeger-2.png) -## 禁用插件 +## 删除插件 当你需要禁用 `zipkin` 插件时,可以通过以下命令删除相应的 JSON 配置,APISIX 将会自动重新加载相关配置,无需重启服务: diff --git a/docs/zh/latest/ssl-protocol.md b/docs/zh/latest/ssl-protocol.md new file mode 100644 index 000000000000..ee886a621c08 --- /dev/null +++ b/docs/zh/latest/ssl-protocol.md @@ -0,0 +1,343 @@ +--- +title: SSL 协议 +--- + + + +`APISIX` 支持,还支持动态的为每一个 SNI 指定不同的 TLS 协议版本。 + +**为了安全考虑,APISIX 默认使用的加密套件不支持 TLSv1.1 以及更低的版本。** +**如果你需要启用 TLSv1.1 协议,请在 config.yaml 的配置项 apisix.ssl.ssl_ciphers 增加 TLSv1.1 协议所支持的加密套件。** + +## ssl_protocols 配置 + +### 静态配置 + +静态配置中 config.yaml 的 ssl_protocols 参数会作用于 APISIX 全局,但是不能动态修改,仅当匹配的 SSL 资源未设置 `ssl_protocols`,静态配置才会生效。 + +```yaml +apisix: + ssl: + ssl_protocols: TLSv1.2 TLSv1.3 # default TLSv1.2 TLSv1.3 +``` + +### 动态配置 + +使用 ssl 资源中 ssl_protocols 字段动态的为每一个 SNI 指定不同的 TLS 协议版本。 + +指定 test.com 域名使用 TLSv1.2 TLSv1.3 协议版本: + +```bash +{ + "cert": "$cert", + "key": "$key", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.2", + "TLSv1.3" + ] +} +``` + +### 注意事项 + +- 动态配置优先级比静态配置更高,当 ssl 资源配置项 ssl_protocols 不为空时 静态配置将会被覆盖。 +- 静态配置作用于全局需要重启 apisix 才能生效。 +- 动态配置可细粒度的控制每个 SNI 的 TLS 协议版本,并且能够动态修改,相比于静态配置更加灵活。 + +## 使用示例 + +### 如何指定 TLSv1.1 协议 + +存在一些老旧的客户端,仍然采用较低级别的 TLSv1.1 协议版本,而新的产品则使用较高安全级别的 TLS 协议版本。如果让新产品支持 TLSv1.1 可能会带来一些安全隐患。为了保证 API 的安全性,我们需要在协议版本之间进行灵活转换。 +例如:test.com 是老旧客户端所使用的域名,需要将其配置为 TLSv1.1 而 test2.com 属于新产品,同时支持了 TLSv1.2,TLSv1.3 协议。 + +1. config.yaml 配置。 + +```yaml +apisix: + ssl: + ssl_protocols: TLSv1.3 + # ssl_ciphers is for reference only + ssl_ciphers: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA +``` + +2. 为 test.com 域名指定 TLSv1.1 协议版本。 + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.1" + ] +}' +``` + +3. 为 test.com 创建 SSL 对象,未指定 TLS 协议版本,将默认使用静态配置。 + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server2.crt)"'", + "key": "'"$(cat server2.key)"'", + "snis": ["test2.com"] +}' +``` + +4. 访问验证 + +使用 TLSv1.3 访问 test.com 失败: + +```shell +$ curl --tls-max 1.3 --tlsv1.3 https://test.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.3 (OUT), TLS handshake, Client hello (1): +* TLSv1.3 (IN), TLS alert, protocol version (582): +* error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +* Closing connection 0 +curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +``` + +使用 TLSv1.1 访问 test.com 成功: + +```shell +$ curl --tls-max 1.1 --tlsv1.1 https://test.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.1 (OUT), TLS handshake, Client hello (1): +* TLSv1.1 (IN), TLS handshake, Server hello (2): +* TLSv1.1 (IN), TLS handshake, Certificate (11): +* TLSv1.1 (IN), TLS handshake, Server key exchange (12): +* TLSv1.1 (IN), TLS handshake, Server finished (14): +* TLSv1.1 (OUT), TLS handshake, Client key exchange (16): +* TLSv1.1 (OUT), TLS change cipher, Change cipher spec (1): +* TLSv1.1 (OUT), TLS handshake, Finished (20): +* TLSv1.1 (IN), TLS handshake, Finished (20): +* SSL connection using TLSv1.1 / ECDHE-RSA-AES256-SHA +``` + +使用 TLSv1.3 访问 test2.com 成功: + +```shell +$ curl --tls-max 1.3 --tlsv1.3 https://test2.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test2.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.3 (OUT), TLS handshake, Client hello (1): +* TLSv1.3 (IN), TLS handshake, Server hello (2): +* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): +* TLSv1.3 (IN), TLS handshake, Certificate (11): +* TLSv1.3 (IN), TLS handshake, CERT verify (15): +* TLSv1.3 (IN), TLS handshake, Finished (20): +* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): +* TLSv1.3 (OUT), TLS handshake, Finished (20): +* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 +``` + +使用 TLSv1.3 访问 test2.com 失败: + +```shell +curl --tls-max 1.1 --tlsv1.1 https://test2.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test2.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.1 (OUT), TLS handshake, Client hello (1): +* TLSv1.1 (IN), TLS alert, protocol version (582): +* error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +* Closing connection 0 +curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +``` + +### 证书关联多个域名,但域名之间使用不同的 TLS 协议 + +有时候,我们可能会遇到这样一种情况,即一个证书关联了多个域名,但是它们需要使用不同的 TLS 协议来保证安全性。例如 test.com 域名需要使用 TlSv1.2 协议,而 test2.com 域名则需要使用 TLSv1.3 协议。在这种情况下,我们不能简单地为所有的域名创建一个 SSL 对象,而是需要为每个域名单独创建一个 SSL 对象,并指定相应的协议版本。这样,我们就可以根据不同的域名和协议版本来进行正确的 SSL 握手和加密通信。示例如下: + +1. 使用证书为 test.com 创建 ssl 对象,并指定 TLSv1.2 协议。 + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/1 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test.com"], + "ssl_protocols": [ + "TLSv1.2" + ] +}' +``` + +2. 使用与 test.com 同一证书,为 test2.com 创建 ssl 对象,并指定 TLSv1.3 协议。 + +```bash +curl http://127.0.0.1:9180/apisix/admin/ssls/2 \ +-H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' +{ + "cert" : "'"$(cat server.crt)"'", + "key": "'"$(cat server.key)"'", + "snis": ["test2.com"], + "ssl_protocols": [ + "TLSv1.3" + ] +}' +``` + +3. 访问验证 + +使用 TLSv1.2 访问 test.com 成功: + +```shell +$ curl --tls-max 1.2 --tlsv1.2 https://test.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.2 (OUT), TLS handshake, Client hello (1): +* TLSv1.2 (IN), TLS handshake, Server hello (2): +* TLSv1.2 (IN), TLS handshake, Certificate (11): +* TLSv1.2 (IN), TLS handshake, Server key exchange (12): +* TLSv1.2 (IN), TLS handshake, Server finished (14): +* TLSv1.2 (OUT), TLS handshake, Client key exchange (16): +* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1): +* TLSv1.2 (OUT), TLS handshake, Finished (20): +* TLSv1.2 (IN), TLS handshake, Finished (20): +* SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256 +* ALPN, server accepted to use h2 +* Server certificate: +* subject: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test.com +* start date: Jul 20 15:50:08 2023 GMT +* expire date: Jul 17 15:50:08 2033 GMT +* issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test.com +* SSL certificate verify result: EE certificate key too weak (66), continuing anyway. +* Using HTTP2, server supports multi-use +* Connection state changed (HTTP/2 confirmed) +* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 +* Using Stream ID: 1 (easy handle 0x5608905ee2e0) +> HEAD / HTTP/2 +> Host: test.com:9443 +> user-agent: curl/7.74.0 +> accept: */* + +``` + +使用 TLSv1.3 协议访问 test.com 失败: + +```shell +$ curl --tls-max 1.3 --tlsv1.3 https://test.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.3 (OUT), TLS handshake, Client hello (1): +* TLSv1.3 (IN), TLS alert, protocol version (582): +* error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +* Closing connection 0 +curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version + +``` + +使用 TLSv1.3 协议访问 test2.com 成功: + +```shell +$ curl --tls-max 1.3 --tlsv1.3 https://test2.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test2.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.3 (OUT), TLS handshake, Client hello (1): +* TLSv1.3 (IN), TLS handshake, Server hello (2): +* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8): +* TLSv1.3 (IN), TLS handshake, Certificate (11): +* TLSv1.3 (IN), TLS handshake, CERT verify (15): +* TLSv1.3 (IN), TLS handshake, Finished (20): +* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1): +* TLSv1.3 (OUT), TLS handshake, Finished (20): +* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384 +* ALPN, server accepted to use h2 +* Server certificate: +* subject: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test2.com +* start date: Jul 20 16:05:47 2023 GMT +* expire date: Jul 17 16:05:47 2033 GMT +* issuer: C=AU; ST=Some-State; O=Internet Widgits Pty Ltd; CN=test2.com +* SSL certificate verify result: EE certificate key too weak (66), continuing anyway. +* Using HTTP2, server supports multi-use +* Connection state changed (HTTP/2 confirmed) +* Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0 +* Using Stream ID: 1 (easy handle 0x55569cbe42e0) +> HEAD / HTTP/2 +> Host: test2.com:9443 +> user-agent: curl/7.74.0 +> accept: */* +> +* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): +* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4): +* old SSL session ID is stale, removing +``` + +使用 TLSv1.2 协议访问 test2.com 失败: + +```shell +$ curl --tls-max 1.2 --tlsv1.2 https://test2.com:9443 -v -k -I +* Trying 127.0.0.1:9443... +* Connected to test2.com (127.0.0.1) port 9443 (#0) +* ALPN, offering h2 +* ALPN, offering http/1.1 +* successfully set certificate verify locations: +* CAfile: /etc/ssl/certs/ca-certificates.crt +* CApath: /etc/ssl/certs +* TLSv1.2 (OUT), TLS handshake, Client hello (1): +* TLSv1.2 (IN), TLS alert, protocol version (582): +* error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +* Closing connection 0 +curl: (35) error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version +``` diff --git a/docs/zh/latest/stream-proxy.md b/docs/zh/latest/stream-proxy.md index e2c1de110f43..22a17be6cdb3 100644 --- a/docs/zh/latest/stream-proxy.md +++ b/docs/zh/latest/stream-proxy.md @@ -27,7 +27,7 @@ APISIX 可以对 TCP/UDP 协议进行代理并实现动态负载均衡。在 ngi ## 如何开启 Stream 代理 -在 `conf/config.yaml` 配置文件设置 `stream_proxy` 选项,指定一组需要进行动态代理的 IP 地址。默认情况不开启 stream 代理。 +要启用该选项,请将 `apisix.proxy_mode` 设置为 `stream` 或 `http&stream`,具体取决于您是只需要流代理还是需要 http 和流。然后在 conf/config.yaml 中添加 apisix.stream_proxy 选项并指定 APISIX 应充当流代理并侦听传入请求的地址列表。 ```yaml apisix: @@ -40,19 +40,6 @@ apisix: - "127.0.0.1:9211" ``` -如果 `apisix.enable_admin` 为 true,上面的配置会同时启用 HTTP 和 stream 代理。 - -如果你设置 `enable_admin` 为 false,且需要同时启用 HTTP 和 stream 代理,设置 `only` 为 false: - -```yaml -apisix: - enable_admin: false - stream_proxy: # TCP/UDP proxy - only: false - tcp: # TCP proxy address list - - 9100 -``` - ## 如何设置 route 简例如下: diff --git a/docs/zh/latest/terminology/plugin.md b/docs/zh/latest/terminology/plugin.md index 3d50cd108779..bf76760f4529 100644 --- a/docs/zh/latest/terminology/plugin.md +++ b/docs/zh/latest/terminology/plugin.md @@ -29,63 +29,83 @@ description: 本文介绍了 APISIX Plugin 对象的相关信息及其使用方 ## 描述 -Plugin 表示将在 HTTP 请求/响应生命周期期间执行的插件配置。Plugin 的配置信息可以直接绑定在 [Route](./route.md) 上,也可以被绑定在 [Service](./service.md)、[Consumer](./consumer.md) 或 [Plugin Config](./plugin-config.md) 上。 +APISIX 插件可以扩展 APISIX 的功能,以满足组织或用户特定的流量管理、可观测性、安全、请求/响应转换、无服务器计算等需求。 -你也可以参考 [Admin API](../admin-api.md#plugin) 了解如何使用该资源。 +APISIX 提供了许多现有的插件,可以定制和编排以满足你的需求。这些插件可以全局启用,以在每个传入请求上触发,也可以局部绑定到其他对象,例如在 [Route](./route.md)、[Service](./service.md)、[Consumer](./consumer.md) 或 [Plugin Config](./plugin-config.md) 上。你可以参考 [Admin API](../admin-api.md#plugin) 了解如何使用该资源。 -:::note 注意 +如果现有的 APISIX 插件不满足需求,你还可以使用 Lua 或其他语言(如 Java、Python、Go 和 Wasm)编写自定义插件。 -对于同一个插件的配置,只能有一个是有效的,其插件配置优先级为 Consumer > Route > Plugin Config > Service。 +## 插件安装 -::: +APISIX 附带一个`config-default.yaml`的默认配置文件和一个 `config.yaml` 的用户自定义配置文件。这些文件位于`conf`目录中。如果两个文件中都存在相同的键 (例如`plugins`),则`config.yaml`文件中该键的配置值将覆盖`config-default.yaml`文件中的配置值。 -## 配置简介 +例如: -如果你想在现有插件的基础上新增插件,请复制 `./conf/config-default.yaml` 中的 `plugins` 参数下的插件列表到 `./conf/config.yaml` 的 `plugins` 参数中。 +```yaml +plugins: + - real-ip # 安装 + - ai + - client-control + - proxy-control + - request-id + - zipkin + # - skywalking # 未安装 +... +``` -:::tip 提示 +## 插件执行生命周期 -在 `./conf/config.yaml` 中的 `plugins` 参数中,可以声明本地 APISIX 节点支持了哪些插件。这是个白名单机制,不在该白名单的插件配置将被自动忽略。该特性可用于临时关闭或打开特定插件,应对突发情况非常有效。 +安装的插件首先会被初始化。然后会检查插件的配置,以确保插件配置遵循定义的[JSON Schema](https://json-schema.org)。 -::: +当一个请求通过 APISIX 时,插件的相应方法会在以下一个或多个阶段中执行: `rewrite`, `access`, `before_proxy`, `header_filter`, `body_filter`, and `log`。这些阶段在很大程度上受到[OpenResty 指令](https://openresty-reference.readthedocs.io/en/latest/Directives/)的影响。 -一个插件在一次请求中只会执行一次,即使被同时绑定到多个不同对象中(比如 Route 或 Service)。插件运行先后顺序是根据插件自身的优先级来决定的,例如: +
+
+Routes Diagram +
+
-```lua -local _M = { - version = 0.1, - priority = 0, -- 这个插件的优先级为 0 - name = plugin_name, - schema = schema, - metadata_schema = metadata_schema, -} -``` +## 插件执行顺序 + +通常情况下,插件按照以下顺序执行: -插件的配置信息,可以存放 Route、Service、Plugin Config 等对象中的 `plugins` 参数下。如下所示的配置中,包含 `limit-count` 和 `prometheus` 两个插件的配置信息: +1. [全局规则](./global-rule.md) 插件 + 1. rewrite 阶段的插件 + 2. access 阶段的插件 + +2. 绑定到其他对象的插件 + 1. rewrite 阶段的插件 + 2. access 阶段的插件 + +在每个阶段内,你可以在插件的 `_meta.priority` 字段中可选地定义一个新的优先级数,该优先级数优先于默认插件优先级在执行期间。具有更高优先级数的插件首先执行。 + +例如,如果你想在请求到达路由时,让 `limit-count`(优先级 1002)先于 `ip-restriction`(优先级 3000)运行,可以通过将更高的优先级数传递给 `limit-count` 的 `_meta.priority` 字段来实现: ```json { - "plugins": { - "limit-count": { - "count": 2, - "time_window": 60, - "rejected_code": 503, - "key": "remote_addr" - }, - "prometheus": {} + ..., + "plugins": { + "limit-count": { + ..., + "_meta": { + "priority": 3010 + } } + } } ``` -并不是所有插件都有具体配置项,比如 [prometheus](../plugins/prometheus.md) 下是没有任何具体配置项,此时可以使用一个空对象启用该插件。 +若要将此插件实例的优先级重置为默认值,只需从插件配置中删除`_meta.priority`字段即可。 -如果一个请求因为某个插件而被拒绝,会有类似如下 `warn` 级别的日志: +## 插件合并优先顺序 -```shell +当同一个插件在全局规则中和局部规则(例如路由)中同时配置时,两个插件将顺序执行。 -ip-restriction exits with http status code 403 +然而,如果相同的插件在多个对象上本地配置,例如在[`Route`](route.md), [`Service`](service.md), [`Consumer`](consumer.md) 或[`Plugin Config`](plugin-config.md) 上,每个非全局插件只会执行一次,因为在执行期间,针对特定的优先顺序,这些对象中配置的插件会被合并: -``` +`Consumer` > `Consumer Group` > `Route` > `Plugin Config` > `Service` + +因此,如果相同的插件在不同的对象中具有不同的配置,则合并期间具有最高优先顺序的插件配置将被使用。 ## 通用配置 diff --git a/docs/zh/latest/terminology/upstream.md b/docs/zh/latest/terminology/upstream.md index 65ef3b5e6135..dca9e56c25c9 100644 --- a/docs/zh/latest/terminology/upstream.md +++ b/docs/zh/latest/terminology/upstream.md @@ -75,7 +75,7 @@ APISIX 的 Upstream 对象除了基本的负载均衡算法外,还支持对上 以下示例是将上游信息直接配置在路由中: - ```shell +```shell curl http://127.0.0.1:9180/apisix/admin/routes/1 \ -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1' -X PUT -d ' { @@ -95,7 +95,7 @@ APISIX 的 Upstream 对象除了基本的负载均衡算法外,还支持对上 } } }' - ``` +``` ## 使用示例 diff --git a/rockspec/apisix-3.4.0-0.rockspec b/rockspec/apisix-3.4.0-0.rockspec new file mode 100644 index 000000000000..50988dc78a35 --- /dev/null +++ b/rockspec/apisix-3.4.0-0.rockspec @@ -0,0 +1,103 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- + +package = "apisix" +version = "3.4.0-0" +supported_platforms = {"linux", "macosx"} + +source = { + url = "git://github.com/apache/apisix", + branch = "3.4.0", +} + +description = { + summary = "Apache APISIX is a cloud-native microservices API gateway, delivering the ultimate performance, security, open source and scalable platform for all your APIs and microservices.", + homepage = "https://github.com/apache/apisix", + license = "Apache License 2.0", +} + +dependencies = { + "lua-resty-ctxdump = 0.1-0", + "api7-lua-resty-dns-client = 7.0.1", + "lua-resty-template = 2.0", + "lua-resty-etcd = 1.10.4", + "api7-lua-resty-http = 0.2.0", + "lua-resty-balancer = 0.04", + "lua-resty-ngxvar = 0.5.2", + "lua-resty-jit-uuid = 0.0.7", + "lua-resty-healthcheck-api7 = 3.0.0", + "api7-lua-resty-jwt = 0.2.4", + "lua-resty-hmac-ffi = 0.05", + "lua-resty-cookie = 0.1.0", + "lua-resty-session = 3.10", + "opentracing-openresty = 0.1", + "lua-resty-radixtree = 2.8.2", + "lua-protobuf = 0.4.1", + "lua-resty-openidc = 1.7.5", + "luafilesystem = 1.7.0-2", + "api7-lua-tinyyaml = 0.4.2", + "nginx-lua-prometheus = 0.20221218", + "jsonschema = 0.9.8", + "lua-resty-ipmatcher = 0.6.1", + "lua-resty-kafka = 0.20-0", + "lua-resty-logger-socket = 2.0.1-0", + "skywalking-nginx-lua = 0.6.0", + "base64 = 1.5-2", + "binaryheap = 0.4", + "api7-dkjson = 0.1.1", + "resty-redis-cluster = 1.02-4", + "lua-resty-expr = 1.3.2", + "graphql = 0.0.2", + "argparse = 0.7.1-1", + "luasocket = 3.1.0-1", + "luasec = 0.9-1", + "lua-resty-consul = 0.3-2", + "penlight = 1.9.2-1", + "ext-plugin-proto = 0.6.0", + "casbin = 1.41.5", + "api7-snowflake = 2.0-1", + "inspect == 3.1.1", + "lualdap = 1.2.6-1", + "lua-resty-rocketmq = 0.3.0-0", + "opentelemetry-lua = 0.2-3", + "net-url = 0.9-1", + "xml2lua = 1.5-2", + "nanoid = 0.1-1", + "lua-resty-mediador = 0.1.2-1", + "lua-resty-ldap = 0.2.2-0" +} + +build = { + type = "make", + build_variables = { + CFLAGS="$(CFLAGS)", + LIBFLAG="$(LIBFLAG)", + LUA_LIBDIR="$(LUA_LIBDIR)", + LUA_BINDIR="$(LUA_BINDIR)", + LUA_INCDIR="$(LUA_INCDIR)", + LUA="$(LUA)", + OPENSSL_INCDIR="$(OPENSSL_INCDIR)", + OPENSSL_LIBDIR="$(OPENSSL_LIBDIR)", + }, + install_variables = { + ENV_INST_PREFIX="$(PREFIX)", + ENV_INST_BINDIR="$(BINDIR)", + ENV_INST_LIBDIR="$(LIBDIR)", + ENV_INST_LUADIR="$(LUADIR)", + ENV_INST_CONFDIR="$(CONFDIR)", + }, +} diff --git a/rockspec/apisix-master-0.rockspec b/rockspec/apisix-master-0.rockspec index 273ce2e1d834..00c67f7bc135 100644 --- a/rockspec/apisix-master-0.rockspec +++ b/rockspec/apisix-master-0.rockspec @@ -40,35 +40,35 @@ dependencies = { "lua-resty-ngxvar = 0.5.2", "lua-resty-jit-uuid = 0.0.7", "lua-resty-healthcheck-api7 = 3.0.0", - "api7-lua-resty-jwt = 0.2.4", - "lua-resty-hmac-ffi = 0.05", - "lua-resty-cookie = 0.1.0", + "api7-lua-resty-jwt = 0.2.5", + "lua-resty-hmac-ffi = 0.06-1", + "lua-resty-cookie = 0.2.0-1", "lua-resty-session = 3.10", "opentracing-openresty = 0.1", "lua-resty-radixtree = 2.8.2", - "lua-protobuf = 0.4.1", - "lua-resty-openidc = 1.7.5", + "lua-protobuf = 0.5.0-1", + "lua-resty-openidc = 1.7.6-3", "luafilesystem = 1.7.0-2", "api7-lua-tinyyaml = 0.4.2", - "nginx-lua-prometheus = 0.20221218", + "nginx-lua-prometheus = 0.20230607-1", "jsonschema = 0.9.8", "lua-resty-ipmatcher = 0.6.1", - "lua-resty-kafka = 0.20-0", + "lua-resty-kafka = 0.22-0", "lua-resty-logger-socket = 2.0.1-0", "skywalking-nginx-lua = 0.6.0", "base64 = 1.5-2", "binaryheap = 0.4", "api7-dkjson = 0.1.1", - "resty-redis-cluster = 1.02-4", + "resty-redis-cluster = 1.05-1", "lua-resty-expr = 1.3.2", "graphql = 0.0.2", "argparse = 0.7.1-1", "luasocket = 3.1.0-1", "luasec = 0.9-1", "lua-resty-consul = 0.3-2", - "penlight = 1.9.2-1", + "penlight = 1.13.1", "ext-plugin-proto = 0.6.0", - "casbin = 1.41.5", + "casbin = 1.41.8-1", "inspect == 3.1.1", "lualdap = 1.2.6-1", "lua-resty-rocketmq = 0.3.0-0", @@ -77,7 +77,8 @@ dependencies = { "xml2lua = 1.5-2", "nanoid = 0.1-1", "lua-resty-mediador = 0.1.2-1", - "lua-resty-ldap = 0.2.2-0" + "lua-resty-ldap = 0.1.0-0", + "lua-resty-t1k = 1.1.0" } build = { diff --git a/t/APISIX.pm b/t/APISIX.pm index 0c057b5381eb..0738f3ecffd7 100644 --- a/t/APISIX.pm +++ b/t/APISIX.pm @@ -102,6 +102,7 @@ my $etcd_key = read_file("t/certs/etcd.key"); $user_yaml_config = <<_EOC_; apisix: node_listen: 1984 + proxy_mode: http&stream stream_proxy: tcp: - 9100 @@ -266,6 +267,7 @@ env ENABLE_ETCD_AUTH; env APISIX_PROFILE; env PATH; # for searching external plugin runner's binary env TEST_NGINX_HTML_DIR; +env OPENSSL111_BIN; _EOC_ @@ -711,6 +713,12 @@ _EOC_ ssl_certificate_key cert/apisix.key; lua_ssl_trusted_certificate cert/apisix.crt; + ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3; + + ssl_client_hello_by_lua_block { + apisix.http_ssl_client_hello_phase() + } + ssl_certificate_by_lua_block { apisix.http_ssl_phase() } diff --git a/t/admin/consumer-group-force-delete.t b/t/admin/consumer-group-force-delete.t new file mode 100644 index 000000000000..4b2fb2d09a67 --- /dev/null +++ b/t/admin/consumer-group-force-delete.t @@ -0,0 +1,163 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set consumer_group(id: 1) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumer_groups/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "limit-count": { + "count": 200, + "time_window": 60, + "rejected_code": 503, + "group": "$consumer_group_id" + } + } + }]] + ) + ngx.status = code + ngx.say(body) + } + } +--- error_code: 201 +--- response_body +passed + + + +=== TEST 2: add consumer +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/consumers/1', + ngx.HTTP_PUT, + [[{ + "username": "1", + "plugins": { + "key-auth": { + "key": "auth-one" + } + }, + "group_id": "1" + }]] + ) + if code >= 300 then + ngx.status = code + ngx.print(message) + return + end + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 3: delete consumer_group(wrong header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/consumer_groups/1?force=anyvalue', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this consumer group, consumer [1] is still using it now"} + + + +=== TEST 4: delete consumer_group(without force delete header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/consumer_groups/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this consumer group, consumer [1] is still using it now"} + + + +=== TEST 5: delete consumer_group(force delete) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/consumer_groups/1?force=true', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed + + + +=== TEST 6: delete consumer +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/consumers/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed diff --git a/t/admin/plugin-configs-force-delete.t b/t/admin/plugin-configs-force-delete.t new file mode 100644 index 000000000000..7d4f73739e34 --- /dev/null +++ b/t/admin/plugin-configs-force-delete.t @@ -0,0 +1,163 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set plugin_configs(id: 1) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/plugin_configs/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "rejected_code": 503 + } + } + }]] + ) + ngx.status = code + ngx.say(body) + } + } +--- error_code: 201 +--- response_body +passed + + + +=== TEST 2: add route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugin_config_id": 1, + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + }, + "uri": "/index.html" + }]] + ) + if code >= 300 then + ngx.status = code + ngx.print(message) + return + end + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 3: delete plugin_configs(wrong header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/plugin_configs/1?force=anyvalue', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this plugin config, route [1] is still using it now"} + + + +=== TEST 4: delete plugin_configs(without force delete header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/plugin_configs/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this plugin config, route [1] is still using it now"} + + + +=== TEST 5: delete plugin_configs(force delete) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/plugin_configs/1?force=true', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed + + + +=== TEST 6: delete route +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed diff --git a/t/admin/plugins.t b/t/admin/plugins.t index ae7617dfd23d..e7bcf09e97b4 100644 --- a/t/admin/plugins.t +++ b/t/admin/plugins.t @@ -74,6 +74,7 @@ referer-restriction csrf uri-blocker request-validation +chaitin-waf openid-connect cas-auth authz-casbin @@ -138,12 +139,12 @@ ext-plugin-post-resp -=== TEST 2: wrong path +=== TEST 2: invalid plugin --- request -GET /apisix/admin/plugins ---- error_code: 400 +GET /apisix/admin/plugins/asdf +--- error_code: 404 --- response_body -{"error_msg":"not found plugin name"} +{"error_msg":"plugin not found in subsystem http"} @@ -412,3 +413,54 @@ plugins: } --- response_body {"batch-requests":"global","error-log-logger":"global","node-status":"global","server-info":"global"} + + + +=== TEST 13: check with wrong plugin subsystem +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + local _, message, _ = t('/apisix/admin/plugins?subsystem=asdf', + ngx.HTTP_GET + ) + ngx.say(message) + } + } +--- response_body eval +qr/\{"error_msg":"unsupported subsystem: asdf"\}/ + + + +=== TEST 14: check with right plugin in wrong subsystem +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + local _, message, _ = t('/apisix/admin/plugins/http-logger?subsystem=stream', + ngx.HTTP_GET + ) + ngx.say(message) + } + } +--- response_body eval +qr/\{"error_msg":"plugin not found in subsystem stream"\}/ + + + +=== TEST 15: check with right plugin in right subsystem +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + + local _, _ , message = t('/apisix/admin/plugins/http-logger?subsystem=http', + ngx.HTTP_GET + ) + ngx.say(message) + } + } +--- response_body eval +qr/this is a mark for our injected plugin schema/ diff --git a/t/admin/protos-force-delete.t b/t/admin/protos-force-delete.t new file mode 100644 index 000000000000..909128924bfe --- /dev/null +++ b/t/admin/protos-force-delete.t @@ -0,0 +1,175 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set proto(id: 1) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/protos/1', + ngx.HTTP_PUT, + [[{ + "content" : "syntax = \"proto3\"; + package helloworld; + service Greeter { + rpc SayHello (HelloRequest) returns (HelloReply) {} + } + message HelloRequest { + string name = 1; + } + message HelloReply { + string message = 1; + }" + }]] + ) + ngx.status = code + ngx.say(body) + } + } +--- error_code: 201 +--- response_body +passed + + + +=== TEST 2: add route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "methods": ["GET"], + "uri": "/grpctest", + "plugins": { + "grpc-transcode": { + "proto_id": "1", + "service": "helloworld.Greeter", + "method": "SayHello" + } + }, + "upstream": { + "scheme": "grpc", + "type": "roundrobin", + "nodes": { + "127.0.0.1:50051": 1 + } + } + }]] + ) + if code >= 300 then + ngx.status = code + ngx.print(message) + return + end + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 3: delete proto(wrong header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/protos/1?force=anyvalue', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this proto, route [1] is still using it now"} + + + +=== TEST 4: delete proto(without force delete header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/protos/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this proto, route [1] is still using it now"} + + + +=== TEST 5: delete proto(force delete) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/protos/1?force=true', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed + + + +=== TEST 6: delete route +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed diff --git a/t/admin/schema.t b/t/admin/schema.t index 35ac20187053..7853addd32d2 100644 --- a/t/admin/schema.t +++ b/t/admin/schema.t @@ -112,7 +112,7 @@ qr/"required":\["count","time_window"\]/ === TEST 8: get not exist plugin --- request GET /apisix/admin/schema/plugins/no-exist ---- error_code: 400 +--- error_code: 404 diff --git a/t/admin/services-force-delete.t b/t/admin/services-force-delete.t new file mode 100644 index 000000000000..439b44e098ab --- /dev/null +++ b/t/admin/services-force-delete.t @@ -0,0 +1,156 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set service(id: 1) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/services/1', + ngx.HTTP_PUT, + [[{ + "upstream": { + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + } + }]] + ) + ngx.status = code + ngx.say(body) + } + } +--- error_code: 201 +--- response_body +passed + + + +=== TEST 2: add route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "service_id": 1, + "uri": "/index.html" + }]] + ) + if code >= 300 then + ngx.status = code + ngx.print(message) + return + end + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 3: delete service(wrong header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/services/1?force=anyvalue', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this service directly, route [1] is still using it now"} + + + +=== TEST 4: delete service(without force delete header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/services/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this service directly, route [1] is still using it now"} + + + +=== TEST 5: delete service(force delete) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/services/1?force=true', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed + + + +=== TEST 6: delete route +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed diff --git a/t/admin/ssl5.t b/t/admin/ssl5.t new file mode 100644 index 000000000000..c9bd7b162133 --- /dev/null +++ b/t/admin/ssl5.t @@ -0,0 +1,86 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +no_root_location(); + +run_tests; + +__DATA__ + +=== TEST 1: Not supported set TLSv1.0 for ssl_protocols +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/apisix.crt") + local ssl_key = t.read_file("t/certs/apisix.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test.com", ssl_protocols = {"TLSv1.0", "TLSv1.2"}} + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.print(body) + } + } +--- request +GET /t +--- error_code: 400 +--- response_body +{"error_msg":"invalid configuration: property \"ssl_protocols\" validation failed: failed to validate item 1: matches none of the enum values"} + + + +=== TEST 2: The default value for the ssl_protocols is null +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/apisix.crt") + local ssl_key = t.read_file("t/certs/apisix.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"} + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "sni": "test.com", + "ssl_protocols": null, + }, + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed diff --git a/t/admin/upstream-force-delete.t b/t/admin/upstream-force-delete.t new file mode 100644 index 000000000000..6d834b11469a --- /dev/null +++ b/t/admin/upstream-force-delete.t @@ -0,0 +1,154 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); +log_level("info"); + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->request) { + $block->set_value("request", "GET /t"); + } + + if (!$block->no_error_log && !$block->error_log) { + $block->set_value("no_error_log", "[error]\n[alert]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set upstream(id: 1) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/upstreams/1', + ngx.HTTP_PUT, + [[{ + "nodes": { + "127.0.0.1:8080": 1 + }, + "type": "roundrobin" + }]] + ) + ngx.status = code + ngx.say(body) + } + } +--- error_code: 201 +--- response_body +passed + + + +=== TEST 2: add route +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "upstream_id": 1, + "uri": "/index.html" + }]] + ) + if code >= 300 then + ngx.status = code + ngx.print(message) + return + end + ngx.say(message) + } + } +--- response_body +passed + + + +=== TEST 3: delete upstream(wrong header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/upstreams/1?force=anyvalue', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this upstream, route [1] is still using it now"} + + + +=== TEST 4: delete upstream(without force delete header) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/upstreams/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body +[delete] code: 400 message: {"error_msg":"can not delete this upstream, route [1] is still using it now"} + + + +=== TEST 5: delete upstream(force delete) +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/upstreams/1?force=true', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed + + + +=== TEST 6: delete route +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.3) + local t = require("lib.test_admin").test + local code, message = t('/apisix/admin/routes/1', + ngx.HTTP_DELETE + ) + ngx.print("[delete] code: ", code, " message: ", message) + } + } +--- response_body chomp +[delete] code: 200 message: passed diff --git a/t/admin/upstream5.t b/t/admin/upstream5.t index f589a415d470..572d2925773f 100644 --- a/t/admin/upstream5.t +++ b/t/admin/upstream5.t @@ -339,7 +339,7 @@ passed } --- error_code: 400 --- response_body -{"error_msg":"can not delete this upstream, plugin in global_rule [1] is still using it now"} +{"error_msg":"can not delete this upstream, plugin in global_rules [1] is still using it now"} diff --git a/t/certs/localhost_slapd_cert.pem b/t/certs/localhost_slapd_cert.pem index 6140ea5f630c..0830e61b761d 100644 --- a/t/certs/localhost_slapd_cert.pem +++ b/t/certs/localhost_slapd_cert.pem @@ -1,24 +1,21 @@ -----BEGIN CERTIFICATE----- -MIIECDCCAnCgAwIBAgIUc40/PofbLcrqu/2MJMEkYfrxB+4wDQYJKoZIhvcNAQEL -BQAwVjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCUd1YW5nRG9uZzEPMA0GA1UEBwwG -Wmh1SGFpMQ8wDQYDVQQKDAZpcmVzdHkxETAPBgNVBAMMCHRlc3QuY29tMB4XDTIy -MDgwMjA1NDI1OFoXDTIzMDgwMjA1NDI1OFowLjESMBAGA1UEAxMJbG9jYWxob3N0 -MRgwFgYDVQQKEw9FeGFtcGxlIENvbXBhbnkwggEiMA0GCSqGSIb3DQEBAQUAA4IB -DwAwggEKAoIBAQCxE5zfta69uPsQVDiV0OwWHDGxTBYNzmp5zsVwOF3bOH+hyB4M -+qFxPEuH84/Ib4GJdLM67qZth1azHudKy/QGPFkoeFUW1JhB9QGyjh/URwxTy05b -Ce5w7Ee1rMV/GWu6fxMfIE3o5U0XuW1IKQFaZVdNuQlvG4VjL59BfnEF+YXb1QDB -kIpvf59q+UuZgit8CrO1dDYeJ/xO3N9v2CS2u6si9/XWgIwayw67tmb7cbTu/srB -C99w97IMP5/Vkeu6fkg2jTuvCRARzMQJ11krDmtGeYum9SSCdyTLxK1u7w33DuhQ -3HE/PfHJj9QV1MKIeruVjEvawJsRiWQG0Ai7AgMBAAGjdjB0MAwGA1UdEwEB/wQC -MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0PAQH/BAUDAwegADAdBgNVHQ4E -FgQUcGOrPCoztq5Z7mjgGtaCkPkmDWowHwYDVR0jBBgwFoAUmbUr1fJgcJdG6ZLx -bYMojlFHG7MwDQYJKoZIhvcNAQELBQADggGBABNOTIiLHNQJfyV20UxcyzZ9xTuc -DuMzEexWJ6S33yJTyp5jni0vFaF9wnT1MOtp+Zizz0hQq0d+GvsmBzjkDdipFqUB -Dt4517l4Z/H4n4FV0jhqQhhzcPRWI5H2MNU0Ezno1iCaKD29Kq61fo2qrU7SNDre -RjnGueTW6u+YLj1ss+UK2rTCRX/Nqqz+MrvIift5Kj4c/8sAD3Zn2aXlH0dXSTcX -DaqNDPQvcdlqNMRSJSthLXYBn40Ro6mH7uA+e4aIVn4jyYvyb8qY5LhQPesTcJZw -IEDmIgFEIh0k1YoGvLD6TkMdKPUG536zH+4iZjKpwGwNQ/dTBgn4+5UOqguiYgXd -MP/eeXSCGLAIjQ4+i1ghv1eAlHuHSQ3Dm75icpAL7VHFdoI7I3wqeE5+IyrUXjX0 -s1bCjIuwGxgoBBTzv25OijmTmMcLYDp04PR5qSwckvsrrxHr+2ujeqS+AGxzZ4Sk -N1JSJL69zUwfCVdE3mR+6OmmDcuVlB3u+grLFQ== +MIIDcjCCAdoCFCS4ndwl6lusO7yj4zxbngp17nNUMA0GCSqGSIb3DQEBCwUAMFYx +CzAJBgNVBAYTAkNOMRIwEAYDVQQIDAlHdWFuZ0RvbmcxDzANBgNVBAcMBlpodUhh +aTEPMA0GA1UECgwGaXJlc3R5MREwDwYDVQQDDAh0ZXN0LmNvbTAgFw0yMzA4MDMw +NjM3NDhaGA8yMTA1MDkyMjA2Mzc0OFowEzERMA8GA1UEAwwIdGVzdC5jb20wggEi +MA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC4mQik/vxL1bdjXcXWNT6DBVGL +A87CeVYleNE5Fx5KROU5Y388h80VgSTmV3ytu9ZuNEB8hcZqmAttZXcipMyNm9PI +vTXaDFaQVclYNJ27hy7rpaJ29WkeLKlUx/pRUpOCg3lbafSmURf5C234LZL1qe5O +0CIvY3uAzkGWMUE8ABYnef+ucULZPLa2+Y9wIx76oP5tfmcM/pQhDXt+GK/bZyat +1sUmEHCVC2gjvHoZO8T7n4ccpi5v06Klj8BKVxRlGVkO2w4hlDbNyh6FWKK31nF8 +BLu/TKF70xSOzX4OFNT6/GJ8R9AyeK52f6OzNmlNUY3UsMEeX8Y2qL4hNrFhAgMB +AAEwDQYJKoZIhvcNAQELBQADggGBAL6g7NZfVTEVklJPmTFSqwQfuu0YXmIvUQIF +jvbNOmwC+nHSq6yuJFC+83R/on/IPWrkP489bEVwqaBQXnZnIMOTjIk8k9elBm/N +1BBpzWUiKNp+HqRjPCanvRCIPUZ9hWpmJy6uzG6VodEtxZgJ7lsArj0AlOYlkHHa +Ctmnl5g6H4m8sNACZaAixesb9wM8Slvn1zhpAeIYZsvIaBZOZnWuwHD1R7seh4ob +BDhDaUfXOYb0VJaKNWnJ5ItPxh4/YMSuS7mG5o2ELnzWN6OeDEQrqKFW17qWLXko +DXEfyrQnODDI+fXvasJhQ62hH33rQF/Q4yJQOEEr7gQUxtMYCxtGCumx2/5MFTuB +E8sf8FykV5jGjwdwMHhPGAmhpMJwM6i65P9GwZguqVmeFv2l4eSTmMinURlkwaAw +cx+LrigAxSKOCcnnnC6Uza1VShyDAuj+XKPglwwJd99UJlk1VG/9TXp3WZTOvSt+ +KttglpiMHyqzCYcMDTGbjPm/UsjFTw== -----END CERTIFICATE----- diff --git a/t/certs/localhost_slapd_key.pem b/t/certs/localhost_slapd_key.pem index fa33248c6240..2b23ac4887c9 100644 --- a/t/certs/localhost_slapd_key.pem +++ b/t/certs/localhost_slapd_key.pem @@ -1,27 +1,28 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEogIBAAKCAQEAsROc37Wuvbj7EFQ4ldDsFhwxsUwWDc5qec7FcDhd2zh/ocge -DPqhcTxLh/OPyG+BiXSzOu6mbYdWsx7nSsv0BjxZKHhVFtSYQfUBso4f1EcMU8tO -WwnucOxHtazFfxlrun8THyBN6OVNF7ltSCkBWmVXTbkJbxuFYy+fQX5xBfmF29UA -wZCKb3+favlLmYIrfAqztXQ2Hif8Ttzfb9gktrurIvf11oCMGssOu7Zm+3G07v7K -wQvfcPeyDD+f1ZHrun5INo07rwkQEczECddZKw5rRnmLpvUkgncky8Stbu8N9w7o -UNxxPz3xyY/UFdTCiHq7lYxL2sCbEYlkBtAIuwIDAQABAoIBAGDANpaEzlUbHRJu -8fvpixUJkp0s1V/1yHeFYptOMPn2hMYAcWrmBg+4wgwmKAl742sXOFaazpRJvjVg -TT+w8EP39T8HgHZY8lgXZjYJMZrqtvGRw946Lu3EK+o33DD10sazZ98551e48cZk -qjEjNnoNpQXydBUhFGB9RKakT1zTb8e+ZQdsrE+ZzgM9/xVFRx4gsfNbed/5TMHZ -QbwaqPzQRiS9ScRwvZ+TE20cGQ66qZqR6+JCatc8BpXA9Q6ZmTj61MSl6MMzCuOS -yIGm5J+siPkLV/ki+MAHk59G9iEsTjS1T1l4aQn0kTtdMx9oVCPODY6Jdi8jIaU/ -TwGWuQECgYEAxJEg/YKjZGQFhidP64OGi1ochFZxuJFwcZ17DgmZPkiU+vpC8KYl -QpR0r0zN9vqP+71nMMoVJfektXRMP4cy0ebSAbx47X5IfdYUhID+/OAlxbl1O9ah -lGWk90zknVvQKahImtYZqepQEYyetQiDB4gX2bLT+8IIt16ebGC/TyUCgYEA5p3g -Tcj69nxyy4BuGxYuNfTORTCzd9zhURN7325HVBMlhen/f1e+yjV1zth9yLDl5Wyl -99jkVCvy6p83s+1EDKdgOTYrxgD31Y934De/m53U6P/yHeic3z9dIgIAn+qcJqU6 -CL28lXEV8jKLNmlR0crWSjtSBDIpA3BWWN834l8CgYAxgcPnVZHFZROnGBue2391 -dXqdMhBuReMmGl21yWEZOLqdA478gTv9KtrAk/2D6NN+udNVjHALIfYP5XyWu3xn -NVVLLqbeWeH0H4kHXl3aXrHkvLL0ITiM4ZTM3EbwAwHInCO9K5NHIkaMRPhr6/rk -WLh5Efsl+1aqqGAKN8u3KQKBgFDjcUh3RSdtkSo12ujfR8gfHLaCFYDmVZWFev5s -hNJFgPTOlZJJ6Z6tT6wEnWHmQkzNZg1f4v5vB94piHUwtJynnIWUrZfewQ8EKmzX -wPpJSuOK2paI/3UCmZ0TDLsKpEidzZRBUMMuDh+MgO3N1Sf7uFwDIIpeOap+HZtA -eC6LAoGAFaN/0hr3kBCGGUQ0MKSEw1A4jJntR+Enz5+vJ1F/yW7E3SNp5gHz8sF1 -ppt3OZKtZeIoaCapIEr4hRZzzZr2zNHu3tyizscLAdcqKbt2o7OlPK7Z5mhREN8E -F4obLQI+YsAv2aOY2EFTSPq70N2OL45NLsdq3igpKZEIbpUgnwA= ------END RSA PRIVATE KEY----- +-----BEGIN PRIVATE KEY----- +MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4mQik/vxL1bdj +XcXWNT6DBVGLA87CeVYleNE5Fx5KROU5Y388h80VgSTmV3ytu9ZuNEB8hcZqmAtt +ZXcipMyNm9PIvTXaDFaQVclYNJ27hy7rpaJ29WkeLKlUx/pRUpOCg3lbafSmURf5 +C234LZL1qe5O0CIvY3uAzkGWMUE8ABYnef+ucULZPLa2+Y9wIx76oP5tfmcM/pQh +DXt+GK/bZyat1sUmEHCVC2gjvHoZO8T7n4ccpi5v06Klj8BKVxRlGVkO2w4hlDbN +yh6FWKK31nF8BLu/TKF70xSOzX4OFNT6/GJ8R9AyeK52f6OzNmlNUY3UsMEeX8Y2 +qL4hNrFhAgMBAAECggEAKO9euGAHCLYS3ckcpvzVqO5T/9TPU9beYJ7jHprez69p +eYlz3LNsqhkiWqYJ8ujVi0ixCCwOLPMcjZzTh24uIjTtCPXUbE8SHx228YVxePVo +VT88wM55CgTzY+aYvtHl/iozji733q3a+BItx7wre6i8POPwwLt51r1mU+0GP0yQ ++spwGR3POjRZeXiWYKSwhfu/STBpQXLANHCpQOmYFCbjTVpCzJ03msQUfYJNmETd +DqyLGbE4aBoPmekrk8GQa/gn04SIsOi8WZeNhsUT9WXyeLFV0DEwnx0sv6IwOk2o +Fqymr71fKNMIvTpCt8wB0Q/rmvPzrprC+hHIZyX5AQKBgQDol5SYzTijZQasV+2d +DqO5IxE8xl8z10bLgsExKcHC6xyhk+el0XFO1fWs0SJ9ptNuKH32C8IlfEr6M3EE +XovQcRfT4crtnWhLmGPAFYKo91f5fiKy1YWggEtsnY+OODo34RCtORtKD6+iC+XE +LFbLMNQA6sHXVONthiEQ6fhIkQKBgQDLLPHgFFL8kSj+Tt/7ctRnwA7qPTUtAwWG +b2QzDMEpifu4O9DJWoE3sfMYkQhyz2g6S+O5XXm56CDtkV+Miazt29z0dM2Jz7uV +NLtymba/s6wBiWFUggHA4Dro1vYa4MJ94ampqi+XPJaJP/j6WoYIu/JhKQDIBtlP +ARaG0O3D0QKBgQCYMyB8wMXQPfqY6kzFml/OdUEBWPnRTYy4/U34Is/1Aa7RmJxb +6GrR4LaLqKp+OJ1gF0UdrWIU73mMsf7BkjDBbE/gSX9l77vgw856UlkWwgwiacTA +63IureUtJQlcUjTefftQru7JjuwqCMkIjs8Y1VHVa8j+ZEESWVPn4oKi0QKBgQCT +4YnHlGN2q91PhG9ooILTZSo1+gj7UyixWeBve8gYiPMrfHYSKIrG1AHhjqa8khQF +4njE0bGoy7kz0UzfiNHSauYfE+kKdqXNCw2ocxNd4tO+ZpTuIpZOIacfFF8a3x8Q +6rBH6rQq+xGCooqBBmRqdQoNCAAmlz2SUHNp+yYkEQKBgQDNBbH7WPjiggahF0TI +oweS86hQ9tDlUFMg/On2eMOY0juSU1yPsPA/JsIglSTDWGRasjFDhd29FVrnGGY5 +5GHy/Gh/6ZZUdrJVsypGw/Dy9amLgmkKTJU4SWDYOb6s1ocGvNPFSYgw0yFe56nx +TU+2zHJo/t2FXssGfnbFWrQAxA== +-----END PRIVATE KEY----- diff --git a/t/chaos/utils/Dockerfile b/t/chaos/utils/Dockerfile index 36bf2212f96d..9ab2a0509112 100644 --- a/t/chaos/utils/Dockerfile +++ b/t/chaos/utils/Dockerfile @@ -17,7 +17,7 @@ ARG ENABLE_PROXY=false -FROM openresty/openresty:1.19.3.2-alpine-fat AS production-stage +FROM openresty/openresty:1.21.4.1-alpine-fat AS production-stage ARG ENABLE_PROXY ARG APISIX_PATH diff --git a/t/cli/test_access_log.sh b/t/cli/test_access_log.sh index 7c40b35a3b8a..58faba74e527 100755 --- a/t/cli/test_access_log.sh +++ b/t/cli/test_access_log.sh @@ -230,6 +230,7 @@ echo "passed: should find upstream scheme" # check stream logs echo ' apisix: + proxy_mode: stream stream_proxy: # UDP proxy udp: - "127.0.0.1:9200" diff --git a/t/cli/test_cmd.sh b/t/cli/test_cmd.sh index 449069577ae7..c02b4dbeed64 100755 --- a/t/cli/test_cmd.sh +++ b/t/cli/test_cmd.sh @@ -86,10 +86,19 @@ fi make stop echo "pass: check APISIX running" -# check customized config.yaml is copied and reverted. +# check customized config git checkout conf/config.yaml +# start with not existed customized config +make init + +if ./bin/apisix start -c conf/not_existed_config.yaml; then + echo "failed: apisix still start with invalid customized config.yaml" + exit 1 +fi + +# start with customized config echo " deployment: admin: @@ -101,37 +110,129 @@ deployment: admin_ssl_cert_key: '../t/certs/apisix_admin_ssl.key' " > conf/customized_config.yaml -cp conf/config.yaml conf/config_original.yaml +./bin/apisix start -c conf/customized_config.yaml -make init +# check if .customized_config_path has been created +if [ ! -e conf/.customized_config_path ]; then + rm conf/customized_config.yaml + echo ".config_path file should exits" + exit 1 +fi -if ./bin/apisix start -c conf/not_existed_config.yaml; then - echo "failed: apisix still start with invalid customized config.yaml" +# check if the custom config is used +code=$(curl -k -i -m 20 -o /dev/null -s -w %{http_code} https://127.0.0.1:9180/apisix/admin/routes -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1') +if [ ! $code -eq 200 ]; then + rm conf/customized_config.yaml + echo "failed: customized config.yaml not be used" exit 1 fi -./bin/apisix start -c conf/customized_config.yaml +make stop -if cmp -s "conf/config.yaml" "conf/config_original.yaml"; then - rm conf/config_original.yaml - echo "failed: customized config.yaml copied failed" +# check if .customized_config_path has been removed +if [ -e conf/.config_path ]; then + rm conf/customized_config_path.yaml + echo ".config_path file should be removed" exit 1 fi -code=$(curl -k -i -m 20 -o /dev/null -s -w %{http_code} https://127.0.0.1:9180/apisix/admin/routes -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1') +# start with invalied config +echo "abc" > conf/customized_config.yaml + +if ./bin/apisix start -c conf/customized_config.yaml ; then + rm conf/customized_config.yaml + echo "start should be failed" + exit 1 +fi + +# check if apisix can be started use correctly default config. (https://github.com/apache/apisix/issues/9700) +./bin/apisix start + +code=$(curl -k -i -m 20 -o /dev/null -s -w %{http_code} http://127.0.0.1:9180/apisix/admin/routes -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1') if [ ! $code -eq 200 ]; then - rm conf/config_original.yaml conf/customized_config.yaml - echo "failed: customized config.yaml not be used" + rm conf/customized_config.yaml + echo "failed: should use default config" exit 1 fi make stop -if ! cmp -s "conf/config.yaml" "conf/config_original.yaml"; then - rm conf/config_original.yaml conf/customized_config.yaml - echo "failed: customized config.yaml reverted failed" +# check if apisix can be started after multiple start failures. (https://github.com/apache/apisix/issues/9171) +echo " +deployment: + admin: + admin_listen: + port: 9180 + https_admin: true + admin_api_mtls: + admin_ssl_cert: '../t/certs/apisix_admin_ssl.crt' + admin_ssl_cert_key: '../t/certs/apisix_admin_ssl.key' + etcd: + host: + - http://127.0.0.1:22379 +" > conf/customized_config.yaml + +./bin/apisix start -c conf/customized_config.yaml || true +./bin/apisix start -c conf/customized_config.yaml || true +./bin/apisix start -c conf/customized_config.yaml || true + +echo " +deployment: + admin: + admin_listen: + port: 9180 + https_admin: true + admin_api_mtls: + admin_ssl_cert: '../t/certs/apisix_admin_ssl.crt' + admin_ssl_cert_key: '../t/certs/apisix_admin_ssl.key' +" > conf/customized_config.yaml + +./bin/apisix start -c conf/customized_config.yaml + +code=$(curl -k -i -m 20 -o /dev/null -s -w %{http_code} https://127.0.0.1:9180/apisix/admin/routes -H 'X-API-KEY: edd1c9f034335f136f87ad84b625c8f1') +if [ ! $code -eq 200 ]; then + rm conf/customized_config.yaml + echo "failed: should use default config" + exit 1 +fi + +rm conf/customized_config.yaml +echo "passed: test customized config successful" + +# test quit command +bin/apisix start + +if ! ps -ef | grep "apisix" | grep "master process" | grep -v "grep"; then + echo "apisix not started" + exit 1 +fi + +bin/apisix quit + +sleep 0.5 + +if ps -ef | grep "worker process is shutting down" | grep -v "grep"; then + echo "all workers should exited" + exit 1 +fi + +echo "passed: test quit command successful" + +# test reload command +bin/apisix start + +if ! ps -ef | grep "apisix" | grep "master process" | grep -v "grep"; then + echo "apisix not started" + exit 1 +fi + +bin/apisix reload + +sleep 0.5 + +if ps -ef | grep "worker process is shutting down" | grep -v "grep"; then + echo "old workers should exited" exit 1 fi -rm conf/config_original.yaml conf/customized_config.yaml -echo "passed: customized config.yaml copied and reverted succeeded" +echo "passed: test reload command successful" diff --git a/t/cli/test_core_config.sh b/t/cli/test_core_config.sh index 7b96820539cc..f799241b945d 100755 --- a/t/cli/test_core_config.sh +++ b/t/cli/test_core_config.sh @@ -45,6 +45,7 @@ echo "passed: set lua_max_running_timers successfully" echo " apisix: + proxy_mode: http&stream stream_proxy: tcp: - addr: 9100 diff --git a/t/cli/test_deployment_traditional.sh b/t/cli/test_deployment_traditional.sh index 1dead769bc10..2699c3d2aecd 100755 --- a/t/cli/test_deployment_traditional.sh +++ b/t/cli/test_deployment_traditional.sh @@ -45,6 +45,7 @@ fi # Both HTTP and Stream echo ' apisix: + proxy_mode: http&stream enable_admin: true stream_proxy: tcp: @@ -74,6 +75,7 @@ fi echo ' apisix: enable_admin: false + proxy_mode: stream stream_proxy: tcp: - addr: 9100 diff --git a/t/cli/test_dns.sh b/t/cli/test_dns.sh index cb8f8eaee565..86dd9dbb1f19 100755 --- a/t/cli/test_dns.sh +++ b/t/cli/test_dns.sh @@ -41,6 +41,7 @@ fi echo ' apisix: + proxy_mode: http&stream stream_proxy: tcp: - 9100 @@ -62,6 +63,7 @@ echo "pass: dns_resolver_valid takes effect" echo ' apisix: + proxy_mode: http&stream stream_proxy: tcp: - 9100 @@ -130,6 +132,7 @@ rm logs/error.log || true echo " apisix: enable_admin: true + proxy_mode: http&stream stream_proxy: tcp: - addr: 9100 diff --git a/t/cli/test_etcd_grpc_mtls.sh b/t/cli/test_etcd_grpc_mtls.sh index 8f37a711272e..90c151a62d7a 100755 --- a/t/cli/test_etcd_grpc_mtls.sh +++ b/t/cli/test_etcd_grpc_mtls.sh @@ -105,6 +105,7 @@ echo "passed: certificate verify with CA success expectedly" # etcd mTLS in stream subsystem echo ' apisix: + proxy_mode: http&stream stream_proxy: tcp: - addr: 9100 diff --git a/t/cli/test_etcd_mtls.sh b/t/cli/test_etcd_mtls.sh index d61d6d517c1f..5d0152ff64f1 100755 --- a/t/cli/test_etcd_mtls.sh +++ b/t/cli/test_etcd_mtls.sh @@ -102,6 +102,7 @@ echo "passed: certificate verify with CA success expectedly" # etcd mTLS in stream subsystem echo ' apisix: + proxy_mode: http&stream stream_proxy: tcp: - addr: 9100 diff --git a/t/cli/test_main.sh b/t/cli/test_main.sh index 29534c83aa54..3b0cab766d59 100755 --- a/t/cli/test_main.sh +++ b/t/cli/test_main.sh @@ -670,10 +670,10 @@ echo "passed: bad lua_module_hook should be rejected" echo ' apisix: + proxy_mode: http&stream extra_lua_path: "\$prefix/example/?.lua" lua_module_hook: "my_hook" stream_proxy: - only: false tcp: - addr: 9100 ' > conf/config.yaml @@ -810,6 +810,7 @@ git checkout conf/config.yaml echo ' apisix: + proxy_mode: http&stream stream_proxy: tcp: - addr: 9100 diff --git a/t/cli/test_prometheus_run_in_privileged.sh b/t/cli/test_prometheus_run_in_privileged.sh index 7f8a3e2ec5ff..a97cf307e26c 100755 --- a/t/cli/test_prometheus_run_in_privileged.sh +++ b/t/cli/test_prometheus_run_in_privileged.sh @@ -55,6 +55,7 @@ rm logs/error.log || true echo " apisix: + proxy_mode: http&stream extra_lua_path: "\$prefix/t/lib/?.lua" enable_admin: true stream_proxy: @@ -87,6 +88,7 @@ rm logs/error.log || true echo " apisix: + proxy_mode: http&stream extra_lua_path: "\$prefix/t/lib/?.lua" enable_admin: false stream_proxy: diff --git a/t/cli/test_prometheus_stream.sh b/t/cli/test_prometheus_stream.sh index 561b9a820cf5..abf960e776a5 100755 --- a/t/cli/test_prometheus_stream.sh +++ b/t/cli/test_prometheus_stream.sh @@ -23,6 +23,7 @@ exit_if_not_customed_nginx echo " apisix: + proxy_mode: http&stream enable_admin: true stream_proxy: tcp: @@ -65,6 +66,7 @@ echo "passed: prometheus works when both http & stream are enabled" echo " apisix: + proxy_mode: stream enable_admin: false stream_proxy: tcp: diff --git a/t/cli/test_snippet.sh b/t/cli/test_snippet.sh index 1b545dd9cf0a..72eee7e64a96 100755 --- a/t/cli/test_snippet.sh +++ b/t/cli/test_snippet.sh @@ -25,8 +25,8 @@ echo ' apisix: node_listen: 9080 enable_admin: true + proxy_mode: http&stream stream_proxy: - only: false tcp: - 9100 nginx_config: diff --git a/t/cli/test_standalone.sh b/t/cli/test_standalone.sh index 2a3add66662b..a0d91c11c4a0 100755 --- a/t/cli/test_standalone.sh +++ b/t/cli/test_standalone.sh @@ -26,7 +26,7 @@ standalone() { trap standalone EXIT -# support environment variables +# support environment variables in yaml values echo ' apisix: enable_admin: false @@ -69,6 +69,33 @@ fi echo "passed: resolve variables in apisix.yaml conf success" +# support environment variables in yaml keys +echo ' +routes: + - + uri: "/test" + plugins: + proxy-rewrite: + uri: "/apisix/nginx_status" + upstream: + nodes: + "${{HOST_IP}}:${{PORT}}": 1 + type: roundrobin +#END +' > conf/apisix.yaml + +# variable is valid +HOST_IP="127.0.0.1" PORT="9091" make init +HOST_IP="127.0.0.1" PORT="9091" make run +sleep 0.1 + +code=$(curl -o /dev/null -s -m 5 -w %{http_code} http://127.0.0.1:9080/test) +if [ ! $code -eq 200 ]; then + echo "failed: resolve variables in apisix.yaml conf failed" +fi + +echo "passed: resolve variables in apisix.yaml conf success" + # configure standalone via deployment echo ' deployment: diff --git a/t/cli/test_stream_config.sh b/t/cli/test_stream_config.sh index 5a15ae10fe2f..baab138a0c99 100755 --- a/t/cli/test_stream_config.sh +++ b/t/cli/test_stream_config.sh @@ -22,6 +22,7 @@ echo " apisix: enable_admin: false + proxy_mode: stream stream_proxy: tcp: - addr: 9100 @@ -40,8 +41,8 @@ echo "passed: enable stream proxy only by default" echo " apisix: enable_admin: false + proxy_mode: http&stream stream_proxy: - only: false tcp: - addr: 9100 " > conf/config.yaml @@ -57,6 +58,7 @@ fi echo " apisix: enable_admin: true + proxy_mode: http&stream stream_proxy: tcp: - addr: 9100 @@ -76,6 +78,7 @@ echo " apisix: ssl: ssl_trusted_certificate: t/certs/mtls_ca.crt + proxy_mode: http&stream stream_proxy: tcp: - addr: 9100 @@ -92,6 +95,7 @@ echo "passed: set trust certificate" echo " apisix: + proxy_mode: http&stream stream_proxy: tcp: - addr: 9100 @@ -108,6 +112,7 @@ fi echo " apisix: + proxy_mode: http&stream stream_proxy: tcp: - addr: 9100 diff --git a/t/cli/test_tls_over_tcp.sh b/t/cli/test_tls_over_tcp.sh index 566af9418a24..5d378ce6a9ad 100755 --- a/t/cli/test_tls_over_tcp.sh +++ b/t/cli/test_tls_over_tcp.sh @@ -22,8 +22,8 @@ # check tls over tcp proxy echo " apisix: + proxy_mode: http&stream stream_proxy: - only: false tcp: - addr: 9100 tls: true diff --git a/t/cli/test_validate_config.sh b/t/cli/test_validate_config.sh index 1c00360f1c30..8db581684332 100755 --- a/t/cli/test_validate_config.sh +++ b/t/cli/test_validate_config.sh @@ -82,6 +82,7 @@ deployment: apisix: node_listen: 9080 enable_admin: true + proxy_mode: http&stream stream_proxy: tcp: - "localhost:9100" diff --git a/t/config-center-yaml/plugin-configs.t b/t/config-center-yaml/plugin-configs.t index 2958b13785fa..f10c3651ad45 100644 --- a/t/config-center-yaml/plugin-configs.t +++ b/t/config-center-yaml/plugin-configs.t @@ -115,7 +115,7 @@ world --- response_headers in: out --- error_log eval -qr/conf_version: \d+#1,/ +qr/conf_version: \d+#\d+,/ diff --git a/t/core/config_etcd.t b/t/core/config_etcd.t index 666d001b272b..2bdddcdbdb52 100644 --- a/t/core/config_etcd.t +++ b/t/core/config_etcd.t @@ -443,3 +443,81 @@ qr/readdir key: fake res: \{.*"nodes":\[\{.*"value":\["bar"\].*\}\].*\}/ --- wait: 1 --- no_error_log [error] + + + +=== TEST 13: test route with special character "-" +--- yaml_config +deployment: + role: traditional + role_traditional: + config_provider: etcd + admin: + admin_key: null + etcd: + prefix: "/apisix-test" +--- config + location /t { + content_by_lua_block { + ngx.sleep(0.5) + + local http = require "resty.http" + local t = require("lib.test_admin").test + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + } + }]] + ) + if code >= 300 then + ngx.status = code + return + end + ngx.say(body) + + -- hit + local httpc = http.new() + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri, { + method = "GET" + }) + + if not res then + ngx.log(ngx.ERR, err) + return + end + ngx.print(res.body) + + -- delete route + code, body = t('/apisix/admin/routes/1', ngx.HTTP_DELETE) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + + -- hit + res, err = httpc:request_uri(uri, { + method = "GET" + }) + + if not res then + ngx.log(ngx.ERR, err) + return + end + ngx.print(res.body) + } + } +--- request +GET /t +--- response_body +passed +hello world +passed +{"error_msg":"404 Route Not Found"} diff --git a/t/grpc_server_example/go.mod b/t/grpc_server_example/go.mod index 6e7a0fb6de8a..d440130899a9 100644 --- a/t/grpc_server_example/go.mod +++ b/t/grpc_server_example/go.mod @@ -3,8 +3,8 @@ module github.com/api7/grpc_server_example go 1.11 require ( - github.com/golang/protobuf v1.5.0 + github.com/golang/protobuf v1.5.2 golang.org/x/net v0.7.0 - google.golang.org/grpc v1.32.0 - google.golang.org/protobuf v1.27.1 + google.golang.org/grpc v1.53.0 + google.golang.org/protobuf v1.28.1 ) diff --git a/t/grpc_server_example/go.sum b/t/grpc_server_example/go.sum index 3a6620532bc8..66241a2668d4 100644 --- a/t/grpc_server_example/go.sum +++ b/t/grpc_server_example/go.sum @@ -1,83 +1,1116 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= +cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.1/go.mod h1:fs4QogzfH5n2pBXBP9vRiU+eCny7lD2vmFZy79Iuw1U= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= +cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc= +cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU= +cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA= +cloud.google.com/go v0.105.0/go.mod h1:PrLgOJNe5nfE9UMxKxgXj4mD3voiP+YQ6gdt6KMFOKM= +cloud.google.com/go v0.107.0/go.mod h1:wpc2eNrD7hXUTy8EKS10jkxpZBjASrORK7goS+3YX2I= +cloud.google.com/go/accessapproval v1.4.0/go.mod h1:zybIuC3KpDOvotz59lFe5qxRZx6C75OtwbisN56xYB4= +cloud.google.com/go/accessapproval v1.5.0/go.mod h1:HFy3tuiGvMdcd/u+Cu5b9NkO1pEICJ46IR82PoUdplw= +cloud.google.com/go/accesscontextmanager v1.3.0/go.mod h1:TgCBehyr5gNMz7ZaH9xubp+CE8dkrszb4oK9CWyvD4o= +cloud.google.com/go/accesscontextmanager v1.4.0/go.mod h1:/Kjh7BBu/Gh83sv+K60vN9QE5NJcd80sU33vIe2IFPE= +cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw= +cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY= +cloud.google.com/go/aiplatform v1.27.0/go.mod h1:Bvxqtl40l0WImSb04d0hXFU7gDOiq9jQmorivIiWcKg= +cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI= +cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4= +cloud.google.com/go/apigateway v1.3.0/go.mod h1:89Z8Bhpmxu6AmUxuVRg/ECRGReEdiP3vQtk4Z1J9rJk= +cloud.google.com/go/apigateway v1.4.0/go.mod h1:pHVY9MKGaH9PQ3pJ4YLzoj6U5FUDeDFBllIz7WmzJoc= +cloud.google.com/go/apigeeconnect v1.3.0/go.mod h1:G/AwXFAKo0gIXkPTVfZDd2qA1TxBXJ3MgMRBQkIi9jc= +cloud.google.com/go/apigeeconnect v1.4.0/go.mod h1:kV4NwOKqjvt2JYR0AoIWo2QGfoRtn/pkS3QlHp0Ni04= +cloud.google.com/go/appengine v1.4.0/go.mod h1:CS2NhuBuDXM9f+qscZ6V86m1MIIqPj3WC/UoEuR1Sno= +cloud.google.com/go/appengine v1.5.0/go.mod h1:TfasSozdkFI0zeoxW3PTBLiNqRmzraodCWatWI9Dmak= +cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4= +cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0= +cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ= +cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk= +cloud.google.com/go/artifactregistry v1.8.0/go.mod h1:w3GQXkJX8hiKN0v+at4b0qotwijQbYUqF2GWkZzAhC0= +cloud.google.com/go/artifactregistry v1.9.0/go.mod h1:2K2RqvA2CYvAeARHRkLDhMDJ3OXy26h3XW+3/Jh2uYc= +cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o= +cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s= +cloud.google.com/go/asset v1.8.0/go.mod h1:mUNGKhiqIdbr8X7KNayoYvyc4HbbFO9URsjbytpUaW0= +cloud.google.com/go/asset v1.9.0/go.mod h1:83MOE6jEJBMqFKadM9NLRcs80Gdw76qGuHn8m3h8oHQ= +cloud.google.com/go/asset v1.10.0/go.mod h1:pLz7uokL80qKhzKr4xXGvBQXnzHn5evJAEAtZiIb0wY= +cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY= +cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw= +cloud.google.com/go/assuredworkloads v1.7.0/go.mod h1:z/736/oNmtGAyU47reJgGN+KVoYoxeLBoj4XkKYscNI= +cloud.google.com/go/assuredworkloads v1.8.0/go.mod h1:AsX2cqyNCOvEQC8RMPnoc0yEarXQk6WEKkxYfL6kGIo= +cloud.google.com/go/assuredworkloads v1.9.0/go.mod h1:kFuI1P78bplYtT77Tb1hi0FMxM0vVpRC7VVoJC3ZoT0= +cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0= +cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8= +cloud.google.com/go/automl v1.7.0/go.mod h1:RL9MYCCsJEOmt0Wf3z9uzG0a7adTT1fe+aObgSpkCt8= +cloud.google.com/go/automl v1.8.0/go.mod h1:xWx7G/aPEe/NP+qzYXktoBSDfjO+vnKMGgsApGJJquM= +cloud.google.com/go/baremetalsolution v0.3.0/go.mod h1:XOrocE+pvK1xFfleEnShBlNAXf+j5blPPxrhjKgnIFc= +cloud.google.com/go/baremetalsolution v0.4.0/go.mod h1:BymplhAadOO/eBa7KewQ0Ppg4A4Wplbn+PsFKRLo0uI= +cloud.google.com/go/batch v0.3.0/go.mod h1:TR18ZoAekj1GuirsUsR1ZTKN3FC/4UDnScjT8NXImFE= +cloud.google.com/go/batch v0.4.0/go.mod h1:WZkHnP43R/QCGQsZ+0JyG4i79ranE2u8xvjq/9+STPE= +cloud.google.com/go/beyondcorp v0.2.0/go.mod h1:TB7Bd+EEtcw9PCPQhCJtJGjk/7TC6ckmnSFS+xwTfm4= +cloud.google.com/go/beyondcorp v0.3.0/go.mod h1:E5U5lcrcXMsCuoDNyGrpyTm/hn7ne941Jz2vmksAxW8= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA= +cloud.google.com/go/bigquery v1.43.0/go.mod h1:ZMQcXHsl+xmU1z36G2jNGZmKp9zNY5BUua5wDgmNCfw= +cloud.google.com/go/bigquery v1.44.0/go.mod h1:0Y33VqXTEsbamHJvJHdFmtqHvMIY28aK1+dFsvaChGc= +cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY= +cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s= +cloud.google.com/go/billing v1.6.0/go.mod h1:WoXzguj+BeHXPbKfNWkqVtDdzORazmCjraY+vrxcyvI= +cloud.google.com/go/billing v1.7.0/go.mod h1:q457N3Hbj9lYwwRbnlD7vUpyjq6u5U1RAOArInEiD5Y= +cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM= +cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI= +cloud.google.com/go/binaryauthorization v1.3.0/go.mod h1:lRZbKgjDIIQvzYQS1p99A7/U1JqvqeZg0wiI5tp6tg0= +cloud.google.com/go/binaryauthorization v1.4.0/go.mod h1:tsSPQrBd77VLplV70GUhBf/Zm3FsKmgSqgm4UmiDItk= +cloud.google.com/go/certificatemanager v1.3.0/go.mod h1:n6twGDvcUBFu9uBgt4eYvvf3sQ6My8jADcOVwHmzadg= +cloud.google.com/go/certificatemanager v1.4.0/go.mod h1:vowpercVFyqs8ABSmrdV+GiFf2H/ch3KyudYQEMM590= +cloud.google.com/go/channel v1.8.0/go.mod h1:W5SwCXDJsq/rg3tn3oG0LOxpAo6IMxNa09ngphpSlnk= +cloud.google.com/go/channel v1.9.0/go.mod h1:jcu05W0my9Vx4mt3/rEHpfxc9eKi9XwsdDL8yBMbKUk= +cloud.google.com/go/cloudbuild v1.3.0/go.mod h1:WequR4ULxlqvMsjDEEEFnOG5ZSRSgWOywXYDb1vPE6U= +cloud.google.com/go/cloudbuild v1.4.0/go.mod h1:5Qwa40LHiOXmz3386FrjrYM93rM/hdRr7b53sySrTqA= +cloud.google.com/go/clouddms v1.3.0/go.mod h1:oK6XsCDdW4Ib3jCCBugx+gVjevp2TMXFtgxvPSee3OM= +cloud.google.com/go/clouddms v1.4.0/go.mod h1:Eh7sUGCC+aKry14O1NRljhjyrr0NFC0G2cjwX0cByRk= +cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY= +cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI= +cloud.google.com/go/cloudtasks v1.7.0/go.mod h1:ImsfdYWwlWNJbdgPIIGJWC+gemEGTBK/SunNQQNCAb4= +cloud.google.com/go/cloudtasks v1.8.0/go.mod h1:gQXUIwCSOI4yPVK7DgTVFiiP0ZW/eQkydWzwVMdHxrI= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= +cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU= +cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U= +cloud.google.com/go/compute v1.10.0/go.mod h1:ER5CLbMxl90o2jtNbGSbtfOpQKR0t15FOtRsugnLrlU= +cloud.google.com/go/compute v1.12.0/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.12.1/go.mod h1:e8yNOBcBONZU1vJKCvCoDw/4JQsA0dpM4x/6PIIOocU= +cloud.google.com/go/compute v1.13.0/go.mod h1:5aPTS0cUNMIc1CE546K+Th6weJUNQErARyZtRXDJ8GE= +cloud.google.com/go/compute v1.14.0/go.mod h1:YfLtxrj9sU4Yxv+sXzZkyPjEyPBZfXHUvjxega5vAdo= +cloud.google.com/go/compute v1.15.1/go.mod h1:bjjoF/NtFUrkD/urWfdHaKuOPDR5nWIs63rR+SXhcpA= +cloud.google.com/go/compute/metadata v0.1.0/go.mod h1:Z1VN+bulIf6bt4P/C37K4DyZYZEXYonfTBHHFPO/4UU= +cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.2.1/go.mod h1:jgHgmJd2RKBGzXqF5LR2EZMGxBkeanZ9wwa75XHJgOM= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +cloud.google.com/go/contactcenterinsights v1.3.0/go.mod h1:Eu2oemoePuEFc/xKFPjbTuPSj0fYJcPls9TFlPNnHHY= +cloud.google.com/go/contactcenterinsights v1.4.0/go.mod h1:L2YzkGbPsv+vMQMCADxJoT9YiTTnSEd6fEvCeHTYVck= +cloud.google.com/go/container v1.6.0/go.mod h1:Xazp7GjJSeUYo688S+6J5V+n/t+G5sKBTFkKNudGRxg= +cloud.google.com/go/container v1.7.0/go.mod h1:Dp5AHtmothHGX3DwwIHPgq45Y8KmNsgN3amoYfxVkLo= +cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I= +cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4= +cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0= +cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs= +cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc= +cloud.google.com/go/datacatalog v1.7.0/go.mod h1:9mEl4AuDYWw81UGc41HonIHH7/sn52H0/tc8f8ZbZIE= +cloud.google.com/go/datacatalog v1.8.0/go.mod h1:KYuoVOv9BM8EYz/4eMFxrr4DUKhGIOXxZoKYF5wdISM= +cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM= +cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ= +cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo= +cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE= +cloud.google.com/go/dataform v0.5.0/go.mod h1:GFUYRe8IBa2hcomWplodVmUx/iTL0FrsauObOM3Ipr0= +cloud.google.com/go/datafusion v1.4.0/go.mod h1:1Zb6VN+W6ALo85cXnM1IKiPw+yQMKMhB9TsTSRDo/38= +cloud.google.com/go/datafusion v1.5.0/go.mod h1:Kz+l1FGHB0J+4XF2fud96WMmRiq/wj8N9u007vyXZ2w= +cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I= +cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ= +cloud.google.com/go/dataplex v1.3.0/go.mod h1:hQuRtDg+fCiFgC8j0zV222HvzFQdRd+SVX8gdmFcZzA= +cloud.google.com/go/dataplex v1.4.0/go.mod h1:X51GfLXEMVJ6UN47ESVqvlsRplbLhcsAt0kZCCKsU0A= +cloud.google.com/go/dataproc v1.7.0/go.mod h1:CKAlMjII9H90RXaMpSxQ8EU6dQx6iAYNPcYPOkSbi8s= +cloud.google.com/go/dataproc v1.8.0/go.mod h1:5OW+zNAH0pMpw14JVrPONsxMQYMBqJuzORhIBfBn9uI= +cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo= +cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/datastore v1.10.0/go.mod h1:PC5UzAmDEkAmkfaknstTYbNpgE49HAgW2J1gcgUfmdM= +cloud.google.com/go/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo= +cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ= +cloud.google.com/go/datastream v1.4.0/go.mod h1:h9dpzScPhDTs5noEMQVWP8Wx8AFBRyS0s8KWPx/9r0g= +cloud.google.com/go/datastream v1.5.0/go.mod h1:6TZMMNPwjUqZHBKPQ1wwXpb0d5VDVPl2/XoS5yi88q4= +cloud.google.com/go/deploy v1.4.0/go.mod h1:5Xghikd4VrmMLNaF6FiRFDlHb59VM59YoDQnOUdsH/c= +cloud.google.com/go/deploy v1.5.0/go.mod h1:ffgdD0B89tToyW/U/D2eL0jN2+IEV/3EMuXHA0l4r+s= +cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4= +cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0= +cloud.google.com/go/dialogflow v1.17.0/go.mod h1:YNP09C/kXA1aZdBgC/VtXX74G/TKn7XVCcVumTflA+8= +cloud.google.com/go/dialogflow v1.18.0/go.mod h1:trO7Zu5YdyEuR+BhSNOqJezyFQ3aUzz0njv7sMx/iek= +cloud.google.com/go/dialogflow v1.19.0/go.mod h1:JVmlG1TwykZDtxtTXujec4tQ+D8SBFMoosgy+6Gn0s0= +cloud.google.com/go/dlp v1.6.0/go.mod h1:9eyB2xIhpU0sVwUixfBubDoRwP+GjeUoxxeueZmqvmM= +cloud.google.com/go/dlp v1.7.0/go.mod h1:68ak9vCiMBjbasxeVD17hVPxDEck+ExiHavX8kiHG+Q= +cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU= +cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU= +cloud.google.com/go/documentai v1.9.0/go.mod h1:FS5485S8R00U10GhgBC0aNGrJxBP8ZVpEeJ7PQDZd6k= +cloud.google.com/go/documentai v1.10.0/go.mod h1:vod47hKQIPeCfN2QS/jULIvQTugbmdc0ZvxxfQY1bg4= +cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y= +cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg= +cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk= +cloud.google.com/go/edgecontainer v0.2.0/go.mod h1:RTmLijy+lGpQ7BXuTDa4C4ssxyXT34NIuHIgKuP4s5w= +cloud.google.com/go/errorreporting v0.3.0/go.mod h1:xsP2yaAp+OAW4OIm60An2bbLpqIhKXdWR/tawvl7QzU= +cloud.google.com/go/essentialcontacts v1.3.0/go.mod h1:r+OnHa5jfj90qIfZDO/VztSFqbQan7HV75p8sA+mdGI= +cloud.google.com/go/essentialcontacts v1.4.0/go.mod h1:8tRldvHYsmnBCHdFpvU+GL75oWiBKl80BiqlFh9tp+8= +cloud.google.com/go/eventarc v1.7.0/go.mod h1:6ctpF3zTnaQCxUjHUdcfgcA1A2T309+omHZth7gDfmc= +cloud.google.com/go/eventarc v1.8.0/go.mod h1:imbzxkyAU4ubfsaKYdQg04WS1NvncblHEup4kvF+4gw= +cloud.google.com/go/filestore v1.3.0/go.mod h1:+qbvHGvXU1HaKX2nD0WEPo92TP/8AQuCVEBXNY9z0+w= +cloud.google.com/go/filestore v1.4.0/go.mod h1:PaG5oDfo9r224f8OYXURtAsY+Fbyq/bLYoINEK8XQAI= +cloud.google.com/go/firestore v1.9.0/go.mod h1:HMkjKHNTtRyZNiMzu7YAsLr9K3X2udY2AMwDaMEQiiE= +cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk= +cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg= +cloud.google.com/go/functions v1.8.0/go.mod h1:RTZ4/HsQjIqIYP9a9YPbU+QFoQsAlYgrwOXJWHn1POY= +cloud.google.com/go/functions v1.9.0/go.mod h1:Y+Dz8yGguzO3PpIjhLTbnqV1CWmgQ5UwtlpzoyquQ08= +cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM= +cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA= +cloud.google.com/go/gaming v1.7.0/go.mod h1:LrB8U7MHdGgFG851iHAfqUdLcKBdQ55hzXy9xBJz0+w= +cloud.google.com/go/gaming v1.8.0/go.mod h1:xAqjS8b7jAVW0KFYeRUxngo9My3f33kFmua++Pi+ggM= +cloud.google.com/go/gkebackup v0.2.0/go.mod h1:XKvv/4LfG829/B8B7xRkk8zRrOEbKtEam6yNfuQNH60= +cloud.google.com/go/gkebackup v0.3.0/go.mod h1:n/E671i1aOQvUxT541aTkCwExO/bTer2HDlj4TsBRAo= +cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o= +cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A= +cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0= +cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0= +cloud.google.com/go/gkemulticloud v0.3.0/go.mod h1:7orzy7O0S+5kq95e4Hpn7RysVA7dPs8W/GgfUtsPbrA= +cloud.google.com/go/gkemulticloud v0.4.0/go.mod h1:E9gxVBnseLWCk24ch+P9+B2CoDFJZTyIgLKSalC7tuI= +cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc= +cloud.google.com/go/gsuiteaddons v1.3.0/go.mod h1:EUNK/J1lZEZO8yPtykKxLXI6JSVN2rg9bN8SXOa0bgM= +cloud.google.com/go/gsuiteaddons v1.4.0/go.mod h1:rZK5I8hht7u7HxFQcFei0+AtfS9uSushomRlg+3ua1o= +cloud.google.com/go/iam v0.1.0/go.mod h1:vcUNEa0pEm0qRVpmWepWaFMIAI8/hjB9mO8rNCJtF6c= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= +cloud.google.com/go/iam v0.5.0/go.mod h1:wPU9Vt0P4UmCux7mqtRu6jcpPAb74cP1fh50J3QpkUc= +cloud.google.com/go/iam v0.6.0/go.mod h1:+1AH33ueBne5MzYccyMHtEKqLE4/kJOibtffMHDMFMc= +cloud.google.com/go/iam v0.7.0/go.mod h1:H5Br8wRaDGNc8XP3keLc4unfUUZeyH3Sfl9XpQEYOeg= +cloud.google.com/go/iam v0.8.0/go.mod h1:lga0/y3iH6CX7sYqypWJ33hf7kkfXJag67naqGESjkE= +cloud.google.com/go/iap v1.4.0/go.mod h1:RGFwRJdihTINIe4wZ2iCP0zF/qu18ZwyKxrhMhygBEc= +cloud.google.com/go/iap v1.5.0/go.mod h1:UH/CGgKd4KyohZL5Pt0jSKE4m3FR51qg6FKQ/z/Ix9A= +cloud.google.com/go/ids v1.1.0/go.mod h1:WIuwCaYVOzHIj2OhN9HAwvW+DBdmUAdcWlFxRl+KubM= +cloud.google.com/go/ids v1.2.0/go.mod h1:5WXvp4n25S0rA/mQWAg1YEEBBq6/s+7ml1RDCW1IrcY= +cloud.google.com/go/iot v1.3.0/go.mod h1:r7RGh2B61+B8oz0AGE+J72AhA0G7tdXItODWsaA2oLs= +cloud.google.com/go/iot v1.4.0/go.mod h1:dIDxPOn0UvNDUMD8Ger7FIaTuvMkj+aGk94RPP0iV+g= +cloud.google.com/go/kms v1.4.0/go.mod h1:fajBHndQ+6ubNw6Ss2sSd+SWvjL26RNo/dr7uxsnnOA= +cloud.google.com/go/kms v1.5.0/go.mod h1:QJS2YY0eJGBg3mnDfuaCyLauWwBJiHRboYxJ++1xJNg= +cloud.google.com/go/kms v1.6.0/go.mod h1:Jjy850yySiasBUDi6KFUwUv2n1+o7QZFyuUJg6OgjA0= +cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic= +cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI= +cloud.google.com/go/language v1.7.0/go.mod h1:DJ6dYN/W+SQOjF8e1hLQXMF21AkH2w9wiPzPCJa2MIE= +cloud.google.com/go/language v1.8.0/go.mod h1:qYPVHf7SPoNNiCL2Dr0FfEFNil1qi3pQEyygwpgVKB8= +cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8= +cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08= +cloud.google.com/go/logging v1.6.1/go.mod h1:5ZO0mHHbvm8gEmeEUHrmDlTDSu5imF6MUP9OfilNXBw= +cloud.google.com/go/longrunning v0.1.1/go.mod h1:UUFxuDWkv22EuY93jjmDMFT5GPQKeFVJBIF6QlTqdsE= +cloud.google.com/go/longrunning v0.3.0/go.mod h1:qth9Y41RRSUE69rDcOn6DdK3HfQfsUI0YSmW3iIlLJc= +cloud.google.com/go/managedidentities v1.3.0/go.mod h1:UzlW3cBOiPrzucO5qWkNkh0w33KFtBJU281hacNvsdE= +cloud.google.com/go/managedidentities v1.4.0/go.mod h1:NWSBYbEMgqmbZsLIyKvxrYbtqOsxY1ZrGM+9RgDqInM= +cloud.google.com/go/maps v0.1.0/go.mod h1:BQM97WGyfw9FWEmQMpZ5T6cpovXXSd1cGmFma94eubI= +cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4= +cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w= +cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE= +cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM= +cloud.google.com/go/memcache v1.6.0/go.mod h1:XS5xB0eQZdHtTuTF9Hf8eJkKtR3pVRCcvJwtm68T3rA= +cloud.google.com/go/memcache v1.7.0/go.mod h1:ywMKfjWhNtkQTxrWxCkCFkoPjLHPW6A7WOTVI8xy3LY= +cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY= +cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s= +cloud.google.com/go/metastore v1.7.0/go.mod h1:s45D0B4IlsINu87/AsWiEVYbLaIMeUSoxlKKDqBGFS8= +cloud.google.com/go/metastore v1.8.0/go.mod h1:zHiMc4ZUpBiM7twCIFQmJ9JMEkDSyZS9U12uf7wHqSI= +cloud.google.com/go/monitoring v1.7.0/go.mod h1:HpYse6kkGo//7p6sT0wsIC6IBDET0RhIsnmlA53dvEk= +cloud.google.com/go/monitoring v1.8.0/go.mod h1:E7PtoMJ1kQXWxPjB6mv2fhC5/15jInuulFdYYtlcvT4= +cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA= +cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o= +cloud.google.com/go/networkconnectivity v1.6.0/go.mod h1:OJOoEXW+0LAxHh89nXd64uGG+FbQoeH8DtxCHVOMlaM= +cloud.google.com/go/networkconnectivity v1.7.0/go.mod h1:RMuSbkdbPwNMQjB5HBWD5MpTBnNm39iAVpC3TmsExt8= +cloud.google.com/go/networkmanagement v1.4.0/go.mod h1:Q9mdLLRn60AsOrPc8rs8iNV6OHXaGcDdsIQe1ohekq8= +cloud.google.com/go/networkmanagement v1.5.0/go.mod h1:ZnOeZ/evzUdUsnvRt792H0uYEnHQEMaz+REhhzJRcf4= +cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ= +cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU= +cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY= +cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34= +cloud.google.com/go/notebooks v1.4.0/go.mod h1:4QPMngcwmgb6uw7Po99B2xv5ufVoIQ7nOGDyL4P8AgA= +cloud.google.com/go/notebooks v1.5.0/go.mod h1:q8mwhnP9aR8Hpfnrc5iN5IBhrXUy8S2vuYs+kBJ/gu0= +cloud.google.com/go/optimization v1.1.0/go.mod h1:5po+wfvX5AQlPznyVEZjGJTMr4+CAkJf2XSTQOOl9l4= +cloud.google.com/go/optimization v1.2.0/go.mod h1:Lr7SOHdRDENsh+WXVmQhQTrzdu9ybg0NecjHidBq6xs= +cloud.google.com/go/orchestration v1.3.0/go.mod h1:Sj5tq/JpWiB//X/q3Ngwdl5K7B7Y0KZ7bfv0wL6fqVA= +cloud.google.com/go/orchestration v1.4.0/go.mod h1:6W5NLFWs2TlniBphAViZEVhrXRSMgUGDfW7vrWKvsBk= +cloud.google.com/go/orgpolicy v1.4.0/go.mod h1:xrSLIV4RePWmP9P3tBl8S93lTmlAxjm06NSm2UTmKvE= +cloud.google.com/go/orgpolicy v1.5.0/go.mod h1:hZEc5q3wzwXJaKrsx5+Ewg0u1LxJ51nNFlext7Tanwc= +cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs= +cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg= +cloud.google.com/go/osconfig v1.9.0/go.mod h1:Yx+IeIZJ3bdWmzbQU4fxNl8xsZ4amB+dygAwFPlvnNo= +cloud.google.com/go/osconfig v1.10.0/go.mod h1:uMhCzqC5I8zfD9zDEAfvgVhDS8oIjySWh+l4WK6GnWw= +cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E= +cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU= +cloud.google.com/go/oslogin v1.6.0/go.mod h1:zOJ1O3+dTU8WPlGEkFSh7qeHPPSoxrcMbbK1Nm2iX70= +cloud.google.com/go/oslogin v1.7.0/go.mod h1:e04SN0xO1UNJ1M5GP0vzVBFicIe4O53FOfcixIqTyXo= +cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0= +cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA= +cloud.google.com/go/policytroubleshooter v1.3.0/go.mod h1:qy0+VwANja+kKrjlQuOzmlvscn4RNsAc0e15GGqfMxg= +cloud.google.com/go/policytroubleshooter v1.4.0/go.mod h1:DZT4BcRw3QoO8ota9xw/LKtPa8lKeCByYeKTIf/vxdE= +cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0= +cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/pubsub v1.26.0/go.mod h1:QgBH3U/jdJy/ftjPhTkyXNj543Tin1pRYcdcPRnFIRI= +cloud.google.com/go/pubsub v1.27.1/go.mod h1:hQN39ymbV9geqBnfQq6Xf63yNhUAhv9CZhzp5O6qsW0= +cloud.google.com/go/pubsublite v1.5.0/go.mod h1:xapqNQ1CuLfGi23Yda/9l4bBCKz/wC3KIJ5gKcxveZg= +cloud.google.com/go/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4= +cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o= +cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk= +cloud.google.com/go/recaptchaenterprise/v2 v2.3.0/go.mod h1:O9LwGCjrhGHBQET5CA7dd5NwwNQUErSgEDit1DLNTdo= +cloud.google.com/go/recaptchaenterprise/v2 v2.4.0/go.mod h1:Am3LHfOuBstrLrNCBrlI5sbwx9LBg3te2N6hGvHn2mE= +cloud.google.com/go/recaptchaenterprise/v2 v2.5.0/go.mod h1:O8LzcHXN3rz0j+LBC91jrwI3R+1ZSZEWrfL7XHgNo9U= +cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg= +cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4= +cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg= +cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c= +cloud.google.com/go/recommender v1.7.0/go.mod h1:XLHs/W+T8olwlGOgfQenXBTbIseGclClff6lhFVe9Bs= +cloud.google.com/go/recommender v1.8.0/go.mod h1:PkjXrTT05BFKwxaUxQmtIlrtj0kph108r02ZZQ5FE70= +cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y= +cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A= +cloud.google.com/go/redis v1.9.0/go.mod h1:HMYQuajvb2D0LvMgZmLDZW8V5aOC/WxstZHiy4g8OiA= +cloud.google.com/go/redis v1.10.0/go.mod h1:ThJf3mMBQtW18JzGgh41/Wld6vnDDc/F/F35UolRZPM= +cloud.google.com/go/resourcemanager v1.3.0/go.mod h1:bAtrTjZQFJkiWTPDb1WBjzvc6/kifjj4QBYuKCCoqKA= +cloud.google.com/go/resourcemanager v1.4.0/go.mod h1:MwxuzkumyTX7/a3n37gmsT3py7LIXwrShilPh3P1tR0= +cloud.google.com/go/resourcesettings v1.3.0/go.mod h1:lzew8VfESA5DQ8gdlHwMrqZs1S9V87v3oCnKCWoOuQU= +cloud.google.com/go/resourcesettings v1.4.0/go.mod h1:ldiH9IJpcrlC3VSuCGvjR5of/ezRrOxFtpJoJo5SmXg= +cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4= +cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY= +cloud.google.com/go/retail v1.10.0/go.mod h1:2gDk9HsL4HMS4oZwz6daui2/jmKvqShXKQuB2RZ+cCc= +cloud.google.com/go/retail v1.11.0/go.mod h1:MBLk1NaWPmh6iVFSz9MeKG/Psyd7TAgm6y/9L2B4x9Y= +cloud.google.com/go/run v0.2.0/go.mod h1:CNtKsTA1sDcnqqIFR3Pb5Tq0usWxJJvsWOCPldRU3Do= +cloud.google.com/go/run v0.3.0/go.mod h1:TuyY1+taHxTjrD0ZFk2iAR+xyOXEA0ztb7U3UNA0zBo= +cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s= +cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI= +cloud.google.com/go/scheduler v1.6.0/go.mod h1:SgeKVM7MIwPn3BqtcBntpLyrIJftQISRrYB5ZtT+KOk= +cloud.google.com/go/scheduler v1.7.0/go.mod h1:jyCiBqWW956uBjjPMMuX09n3x37mtyPJegEWKxRsn44= +cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA= +cloud.google.com/go/secretmanager v1.8.0/go.mod h1:hnVgi/bN5MYHd3Gt0SPuTPPp5ENina1/LxM+2W9U9J4= +cloud.google.com/go/secretmanager v1.9.0/go.mod h1:b71qH2l1yHmWQHt9LC80akm86mX8AL6X1MA01dW8ht4= +cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4= +cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0= +cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU= +cloud.google.com/go/security v1.9.0/go.mod h1:6Ta1bO8LXI89nZnmnsZGp9lVoVWXqsVbIq/t9dzI+2Q= +cloud.google.com/go/security v1.10.0/go.mod h1:QtOMZByJVlibUT2h9afNDWRZ1G96gVywH8T5GUSb9IA= +cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU= +cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc= +cloud.google.com/go/securitycenter v1.15.0/go.mod h1:PeKJ0t8MoFmmXLXWm41JidyzI3PJjd8sXWaVqg43WWk= +cloud.google.com/go/securitycenter v1.16.0/go.mod h1:Q9GMaLQFUD+5ZTabrbujNWLtSLZIZF7SAR0wWECrjdk= +cloud.google.com/go/servicecontrol v1.4.0/go.mod h1:o0hUSJ1TXJAmi/7fLJAedOovnujSEvjKCAFNXPQ1RaU= +cloud.google.com/go/servicecontrol v1.5.0/go.mod h1:qM0CnXHhyqKVuiZnGKrIurvVImCs8gmqWsDoqe9sU1s= +cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs= +cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg= +cloud.google.com/go/servicedirectory v1.6.0/go.mod h1:pUlbnWsLH9c13yGkxCmfumWEPjsRs1RlmJ4pqiNjVL4= +cloud.google.com/go/servicedirectory v1.7.0/go.mod h1:5p/U5oyvgYGYejufvxhgwjL8UVXjkuw7q5XcG10wx1U= +cloud.google.com/go/servicemanagement v1.4.0/go.mod h1:d8t8MDbezI7Z2R1O/wu8oTggo3BI2GKYbdG4y/SJTco= +cloud.google.com/go/servicemanagement v1.5.0/go.mod h1:XGaCRe57kfqu4+lRxaFEAuqmjzF0r+gWHjWqKqBvKFo= +cloud.google.com/go/serviceusage v1.3.0/go.mod h1:Hya1cozXM4SeSKTAgGXgj97GlqUvF5JaoXacR1JTP/E= +cloud.google.com/go/serviceusage v1.4.0/go.mod h1:SB4yxXSaYVuUBYUml6qklyONXNLt83U0Rb+CXyhjEeU= +cloud.google.com/go/shell v1.3.0/go.mod h1:VZ9HmRjZBsjLGXusm7K5Q5lzzByZmJHf1d0IWHEN5X4= +cloud.google.com/go/shell v1.4.0/go.mod h1:HDxPzZf3GkDdhExzD/gs8Grqk+dmYcEjGShZgYa9URw= +cloud.google.com/go/spanner v1.41.0/go.mod h1:MLYDBJR/dY4Wt7ZaMIQ7rXOTLjYrmxLE/5ve9vFfWos= +cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM= +cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ= +cloud.google.com/go/speech v1.8.0/go.mod h1:9bYIl1/tjsAnMgKGHKmBZzXKEkGgtU+MpdDPTE9f7y0= +cloud.google.com/go/speech v1.9.0/go.mod h1:xQ0jTcmnRFFM2RfX/U+rk6FQNUF6DQlydUSyoooSpco= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= +cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc= +cloud.google.com/go/storage v1.27.0/go.mod h1:x9DOL8TK/ygDUMieqwfhdpQryTeEkhGKMi80i/iqR2s= +cloud.google.com/go/storagetransfer v1.5.0/go.mod h1:dxNzUopWy7RQevYFHewchb29POFv3/AaBgnhqzqiK0w= +cloud.google.com/go/storagetransfer v1.6.0/go.mod h1:y77xm4CQV/ZhFZH75PLEXY0ROiS7Gh6pSKrM8dJyg6I= +cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw= +cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g= +cloud.google.com/go/talent v1.3.0/go.mod h1:CmcxwJ/PKfRgd1pBjQgU6W3YBwiewmUzQYH5HHmSCmM= +cloud.google.com/go/talent v1.4.0/go.mod h1:ezFtAgVuRf8jRsvyE6EwmbTK5LKciD4KVnHuDEFmOOA= +cloud.google.com/go/texttospeech v1.4.0/go.mod h1:FX8HQHA6sEpJ7rCMSfXuzBcysDAuWusNNNvN9FELDd8= +cloud.google.com/go/texttospeech v1.5.0/go.mod h1:oKPLhR4n4ZdQqWKURdwxMy0uiTS1xU161C8W57Wkea4= +cloud.google.com/go/tpu v1.3.0/go.mod h1:aJIManG0o20tfDQlRIej44FcwGGl/cD0oiRyMKG19IQ= +cloud.google.com/go/tpu v1.4.0/go.mod h1:mjZaX8p0VBgllCzF6wcU2ovUXN9TONFLd7iz227X2Xg= +cloud.google.com/go/trace v1.3.0/go.mod h1:FFUE83d9Ca57C+K8rDl/Ih8LwOzWIV1krKgxg6N0G28= +cloud.google.com/go/trace v1.4.0/go.mod h1:UG0v8UBqzusp+z63o7FK74SdFE+AXpCLdFb1rshXG+Y= +cloud.google.com/go/translate v1.3.0/go.mod h1:gzMUwRjvOqj5i69y/LYLd8RrNQk+hOmIXTi9+nb3Djs= +cloud.google.com/go/translate v1.4.0/go.mod h1:06Dn/ppvLD6WvA5Rhdp029IX2Mi3Mn7fpMRLPvXT5Wg= +cloud.google.com/go/video v1.8.0/go.mod h1:sTzKFc0bUSByE8Yoh8X0mn8bMymItVGPfTuUBUyRgxk= +cloud.google.com/go/video v1.9.0/go.mod h1:0RhNKFRF5v92f8dQt0yhaHrEuH95m068JYOvLZYnJSw= +cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU= +cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4= +cloud.google.com/go/videointelligence v1.8.0/go.mod h1:dIcCn4gVDdS7yte/w+koiXn5dWVplOZkE+xwG9FgK+M= +cloud.google.com/go/videointelligence v1.9.0/go.mod h1:29lVRMPDYHikk3v8EdPSaL8Ku+eMzDljjuvRs105XoU= +cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0= +cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo= +cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo= +cloud.google.com/go/vision/v2 v2.4.0/go.mod h1:VtI579ll9RpVTrdKdkMzckdnwMyX2JILb+MhPqRbPsY= +cloud.google.com/go/vision/v2 v2.5.0/go.mod h1:MmaezXOOE+IWa+cS7OhRRLK2cNv1ZL98zhqFFZaaH2E= +cloud.google.com/go/vmmigration v1.2.0/go.mod h1:IRf0o7myyWFSmVR1ItrBSFLFD/rJkfDCUTO4vLlJvsE= +cloud.google.com/go/vmmigration v1.3.0/go.mod h1:oGJ6ZgGPQOFdjHuocGcLqX4lc98YQ7Ygq8YQwHh9A7g= +cloud.google.com/go/vmwareengine v0.1.0/go.mod h1:RsdNEf/8UDvKllXhMz5J40XxDrNJNN4sagiox+OI208= +cloud.google.com/go/vpcaccess v1.4.0/go.mod h1:aQHVbTWDYUR1EbTApSVvMq1EnT57ppDmQzZ3imqIk4w= +cloud.google.com/go/vpcaccess v1.5.0/go.mod h1:drmg4HLk9NkZpGfCmZ3Tz0Bwnm2+DKqViEpeEpOq0m8= +cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE= +cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= +cloud.google.com/go/webrisk v1.6.0/go.mod h1:65sW9V9rOosnc9ZY7A7jsy1zoHS5W9IAXv6dGqhMQMc= +cloud.google.com/go/webrisk v1.7.0/go.mod h1:mVMHgEYH0r337nmt1JyLthzMr6YxwN1aAIEc2fTcq7A= +cloud.google.com/go/websecurityscanner v1.3.0/go.mod h1:uImdKm2wyeXQevQJXeh8Uun/Ym1VqworNDlBXQevGMo= +cloud.google.com/go/websecurityscanner v1.4.0/go.mod h1:ebit/Fp0a+FWu5j4JOmJEV8S8CzdTkAS77oDsiSqYWQ= +cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= +cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cloud.google.com/go/workflows v1.8.0/go.mod h1:ysGhmEajwZxGn1OhGOGKsTXc5PyxOc0vfKf5Af+to4M= +cloud.google.com/go/workflows v1.9.0/go.mod h1:ZGkj1aFIOd9c8Gerkjjq7OW7I5+l6cSvT3ujaO/WwSA= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20220314180256-7f1daf1720fc/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20230105202645-06c439db220b/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= +github.com/envoyproxy/go-control-plane v0.10.3/go.mod h1:fJJn/j26vwOu972OllsvAgJJM//w9BV6Fxbg2LuVd34= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J6romD608Ba7Hij42vrOBCo= +github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= +github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c= +github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo= +github.com/googleapis/gax-go/v2 v2.6.0/go.mod h1:1mjbznJAPHFpesgE5ucqfYEscaz5kMdcIDwU/6+DDoY= +github.com/googleapis/gax-go/v2 v2.7.0/go.mod h1:TEop28CZZQ2y+c0VxMUmu1lV+fQx57QpBWsYpwqHJx8= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3/go.mod h1:o//XUCC/F+yRGJoPO/VU0GSB0f8Nhgmxx0VIRUvaC0w= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lyft/protoc-gen-star v0.6.0/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/lyft/protoc-gen-star v0.6.1/go.mod h1:TGAoBVkt8w7MPG72TrKIu85MIdXwDuzJYeZuUPFPNwA= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= +github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/afero v1.9.2/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.opentelemetry.io/proto/otlp v0.15.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221012135044-0b7e1fb9d458/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws= golang.org/x/net v0.7.0 h1:rJrUqqhjsgNp7KqAIc25s9pZnjU7TUcSY7HcVZjdn1g= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE= +golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221006150949-b44042a4b9c1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20220922220347-f3bd1da661af/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.1.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= +golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= +google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= +google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= +google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= +google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg= +google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o= +google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g= +google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw= +google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI= +google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.98.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s= +google.golang.org/api v0.99.0/go.mod h1:1YOf74vkVndF7pG6hIHuINsM7eWwpVTAfNMNiL91A08= +google.golang.org/api v0.100.0/go.mod h1:ZE3Z2+ZOr87Rx7dqFsdRQkRBk36kDtp/h+QpHbB7a70= +google.golang.org/api v0.102.0/go.mod h1:3VFl6/fzoA+qNuS1N1/VfXY4LjoXN/wzeIp7TweWwGo= +google.golang.org/api v0.103.0/go.mod h1:hGtW6nK1AC+d9si/UBhw8Xli+QMOf6xyNAyJw4qU9w0= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220329172620-7be39ac1afc7/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= +google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= +google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE= +google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc= +google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo= +google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw= +google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI= +google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U= +google.golang.org/genproto v0.0.0-20221014173430-6e2ab493f96b/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221014213838-99cd37c6964a/go.mod h1:1vXfmgAz9N9Jx0QA82PqRVauvCz1SGSz739p0f183jM= +google.golang.org/genproto v0.0.0-20221024153911-1573dae28c9c/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221024183307-1bc688fe9f3e/go.mod h1:9qHF0xnpdSfF6knlcsnpzUu5y+rpwgbvsyGAZPBMg4s= +google.golang.org/genproto v0.0.0-20221027153422-115e99e71e1c/go.mod h1:CGI5F/G+E5bKwmfYo09AXuVN4dD894kIKUFmVbP2/Fo= +google.golang.org/genproto v0.0.0-20221114212237-e4508ebdbee1/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221117204609-8f9c96812029/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221201164419-0e50fba7f41c/go.mod h1:rZS5c/ZVYMaOGBfO68GWtjOw/eLaZM1X6iVtgjZ+EWg= +google.golang.org/genproto v0.0.0-20221202195650-67e5cbc046fd/go.mod h1:cTsE614GARnxrLsqKREzmNYJACSWWpAWdNMwnD7c2BE= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f h1:BWUVssLB0HVOSY78gIdvk1dTVYtT1y8SBWtPYuTJ/6w= +google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f/go.mod h1:RGgjbofJ8xD9Sq1VVhDM1Vok1vRONV+rg+CjzG4SZKM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.32.0 h1:zWTV+LMdc3kaiJMSTOFz2UgSBgx8RNQoTGiZu3fR9S0= -google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= +google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.50.1/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI= +google.golang.org/grpc v1.51.0/go.mod h1:wgNDFcnuBGmxLKI/qn4T+m5BtEBYXJPvibbUPsAIPww= +google.golang.org/grpc v1.53.0 h1:LAv2ds7cmFV/XTS3XG1NneeENYrXGmorPxsBbptIjNc= +google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/t/kubernetes/discovery/stream/kubernetes.t b/t/kubernetes/discovery/stream/kubernetes.t index 3df431fb64a8..5d9e06c86291 100644 --- a/t/kubernetes/discovery/stream/kubernetes.t +++ b/t/kubernetes/discovery/stream/kubernetes.t @@ -51,11 +51,7 @@ use t::APISIX; my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; my $version = eval { `$nginx_binary -V 2>&1` }; -if ($version =~ m/\/1.19.3/) { - plan(skip_all => "require OpenResty version >= 1.19.9.1"); -} else { - plan('no_plan'); -} +plan('no_plan'); repeat_each(1); log_level('warn'); diff --git a/t/lib/chaitin_waf_server.lua b/t/lib/chaitin_waf_server.lua new file mode 100644 index 000000000000..4130bd019edb --- /dev/null +++ b/t/lib/chaitin_waf_server.lua @@ -0,0 +1,60 @@ +-- +-- Licensed to the Apache Software Foundation (ASF) under one or more +-- contributor license agreements. See the NOTICE file distributed with +-- this work for additional information regarding copyright ownership. +-- The ASF licenses this file to You under the Apache License, Version 2.0 +-- (the "License"); you may not use this file except in compliance with +-- the License. You may obtain a copy of the License at +-- +-- http://www.apache.org/licenses/LICENSE-2.0 +-- +-- Unless required by applicable law or agreed to in writing, software +-- distributed under the License is distributed on an "AS IS" BASIS, +-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +-- See the License for the specific language governing permissions and +-- limitations under the License. +-- +local _M = {} + +local function get_socket() + ngx.flush(true) + local sock, err = ngx.req.socket(true) + if not sock then + ngx.log(ngx.ERR, "failed to get the request socket: " .. tostring(err)) + return nil + end + return sock +end + +function _M.pass() + local sock = get_socket() + sock:send({ string.char(65), string.char(1), string.char(0), string.char(0), string.char(0) }) + sock:send(".") + sock:send({ string.char(165), string.char(77), string.char(0), string.char(0), string.char(0) }) + sock:send("{\"event_id\":\"1e902e84bf5a4ead8f7760a0fe2c7719\",\"request_hit_whitelist\":false}") + + ngx.exit(200) +end + +function _M.reject() + local sock = get_socket() + sock:send({ string.char(65), string.char(1), string.char(0), string.char(0), string.char(0) }) + sock:send("?") + sock:send({ string.char(2), string.char(3), string.char(0), string.char(0), string.char(0) }) + sock:send("403") + sock:send({ string.char(37), string.char(77), string.char(0), string.char(0), string.char(0) }) + sock:send("{\"event_id\":\"b3c6ce574dc24f09a01f634a39dca83b\",\"request_hit_whitelist\":false}") + sock:send({ string.char(35), string.char(79), string.char(0), string.char(0), string.char(0) }) + sock:send("Set-Cookie:sl-session=ulgbPfMSuWRNsi/u7Aj9aA==; Domain=; Path=/; Max-Age=86400\n") + sock:send({ string.char(164), string.char(51), string.char(0), string.char(0), string.char(0) }) + sock:send("") + + ngx.exit(200) +end + +function _M.timeout() + ngx.sleep(100) + _M.pass() +end + +return _M diff --git a/t/node/healthcheck-stop-checker.t b/t/node/healthcheck-stop-checker.t index 1a5eaf286704..54ed61763c8f 100644 --- a/t/node/healthcheck-stop-checker.t +++ b/t/node/healthcheck-stop-checker.t @@ -165,15 +165,6 @@ create new checker: table: 0x content_by_lua_block { local t = require("lib.test_admin").test - -- release the clean handler of previous test - local code, _, body = t('/apisix/admin/routes/1', "DELETE") - - if code > 300 then - ngx.status = code - ngx.say(body) - return - end - local code, _, body = t('/apisix/admin/upstreams/stopchecker', "PUT", [[{"type":"roundrobin","nodes":{"127.0.0.1:1980":1,"127.0.0.1:1981":1},"checks":{"active":{"http_path":"/status","healthy":{"interval":1,"successes":1},"unhealthy":{"interval":1,"http_failures":2}}}}]] @@ -256,7 +247,6 @@ ok --- grep_error_log eval qr/create new checker: table: 0x|try to release checker: table: 0x/ --- grep_error_log_out -try to release checker: table: 0x create new checker: table: 0x try to release checker: table: 0x create new checker: table: 0x diff --git a/t/node/plugin-configs.t b/t/node/plugin-configs.t index 11f9601030ad..f601ae86d773 100644 --- a/t/node/plugin-configs.t +++ b/t/node/plugin-configs.t @@ -113,10 +113,10 @@ __DATA__ hello world --- grep_error_log eval -qr/conf_version: \d+#\d/ +qr/conf_version: \d+#\d+/ --- grep_error_log_out eval -qr/conf_version: \d+#1 -conf_version: \d+#2 +qr/conf_version: \d+#\d+ +conf_version: \d+#\d+ / diff --git a/t/node/ssl-protocols.t b/t/node/ssl-protocols.t new file mode 100644 index 000000000000..76fba7ff06ea --- /dev/null +++ b/t/node/ssl-protocols.t @@ -0,0 +1,298 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX; + +repeat_each(1); +log_level('info'); +no_root_location(); +no_shuffle(); + +my $openssl_bin = $ENV{OPENSSL111_BIN}; +if (! -x $openssl_bin) { + $ENV{OPENSSL111_BIN} = '/usr/local/openresty/openssl111/bin/openssl'; + if (! -x $ENV{OPENSSL111_BIN}) { + plan(skip_all => "openssl111 not installed"); + } +} + +plan('no_plan'); + +add_block_preprocessor(sub { + my ($block) = @_; + + my $yaml_config = $block->yaml_config // <<_EOC_; +deployment: + role: traditional + role_traditional: + config_provider: etcd + admin: + admin_key: null +apisix: + node_listen: 1984 + proxy_mode: http&stream + stream_proxy: + tcp: + - 9100 + enable_resolv_search_opt: false + ssl: + ssl_protocols: TLSv1.1 TLSv1.2 TLSv1.3 + ssl_ciphers: ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES256-SHA:DHE-DSS-AES256-SHA +_EOC_ + + $block->set_value("yaml_config", $yaml_config); +}); + +run_tests(); + +__DATA__ + +=== TEST 1: set route +--- config +location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/hello", "/world"] + }]] + ) + if code >= 300 then + ngx.status = code + ngx.say(message) + return + end + ngx.say(body) + } +} +--- request +GET /t +--- response_body +passed + + + +=== TEST 2: create ssl for test.com (unset ssl_protocols) +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/apisix.crt") + local ssl_key = t.read_file("t/certs/apisix.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test.com"} + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "sni": "test.com", + "ssl_protocols": null, + }, + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 3: Successfully, access test.com with TLSv1.3 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test.com -tls1_3 2>&1 | cat +--- response_body eval +qr/Server certificate/ + + + +=== TEST 4: Successfully, access test.com with TLSv1.2 +--- exec +curl -k -v --tls-max 1.2 --tlsv1.2 --resolve "test.com:1994:127.0.0.1" https://test.com:1994/hello 2>&1 | cat +--- response_body eval +qr/TLSv1\.2 \(IN\), TLS handshake, Server hello(?s).*hello world/ + + + +=== TEST 5: Successfully, access test.com with TLSv1.1 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test.com -tls1_1 2>&1 | cat +--- response_body eval +qr/Server certificate/ + + + +=== TEST 6: set TLSv1.2 and TLSv1.3 for test.com +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/apisix.crt") + local ssl_key = t.read_file("t/certs/apisix.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test.com", ssl_protocols = {"TLSv1.2", "TLSv1.3"}} + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "sni": "test.com", + "ssl_protocols": ["TLSv1.2", "TLSv1.3"], + }, + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 7: Set TLSv1.3 for the test2.com +--- config +location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/test2.crt") + local ssl_key = t.read_file("t/certs/test2.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test2.com", ssl_protocols = {"TLSv1.3"}} + + local code, body = t.test('/apisix/admin/ssls/2', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "sni": "test2.com" + }, + "key": "/apisix/ssls/2" + }]] + ) + + ngx.status = code + ngx.say(body) + } +} +--- response_body +passed +--- request +GET /t + + + +=== TEST 8: Successfully, access test.com with TLSv1.3 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test.com -tls1_3 2>&1 | cat +--- response_body eval +qr/Server certificate/ + + + +=== TEST 9: Successfully, access test.com with TLSv1.2 +--- exec +curl -k -v --tls-max 1.2 --tlsv1.2 --resolve "test.com:1994:127.0.0.1" https://test.com:1994/hello 2>&1 | cat +--- response_body eval +qr/TLSv1\.2 \(IN\), TLS handshake, Server hello(?s).*hello world/ + + + +=== TEST 10: Successfully, access test2.com with TLSv1.3 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test2.com -tls1_3 2>&1 | cat +--- response_body eval +qr/Server certificate/ + + + +=== TEST 11: Failed, access test2.com with TLSv1.2 +--- exec +curl -k -v --tls-max 1.2 --tlsv1.2 --resolve "test2.com:1994:127.0.0.1" https://test2.com:1994/hello 2>&1 | cat +--- response_body eval +qr/TLSv1\.2 \(IN\), TLS alert/ + + + +=== TEST 12: set TLSv1.1 for test.com +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin") + + local ssl_cert = t.read_file("t/certs/apisix.crt") + local ssl_key = t.read_file("t/certs/apisix.key") + local data = {cert = ssl_cert, key = ssl_key, sni = "test.com", ssl_protocols = {"TLSv1.1"}} + + local code, body = t.test('/apisix/admin/ssls/1', + ngx.HTTP_PUT, + core.json.encode(data), + [[{ + "value": { + "sni": "test.com", + "ssl_protocols": ["TLSv1.1"], + }, + "key": "/apisix/ssls/1" + }]] + ) + + ngx.status = code + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 13: Successfully, access test.com with TLSv1.1 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test.com -tls1_1 2>&1 | cat +--- response_body eval +qr/Server certificate/ + + + +=== TEST 14: Failed, access test.com with TLSv1.3 +--- exec +echo -n "Q" | $OPENSSL111_BIN s_client -connect 127.0.0.1:1994 -servername test.com -tls1_3 2>&1 | cat +--- response_body eval +qr/tlsv1 alert/ diff --git a/t/plugin/chaitin-waf-reject.t b/t/plugin/chaitin-waf-reject.t new file mode 100644 index 000000000000..262ed28f04f6 --- /dev/null +++ b/t/plugin/chaitin-waf-reject.t @@ -0,0 +1,139 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); + +add_block_preprocessor(sub { + my ($block) = @_; + + my $stream_default_server = <<_EOC_; + server { + listen 8088; + listen 8089; + content_by_lua_block { + require("lib.chaitin_waf_server").reject() + } + } +_EOC_ + + $block->set_value("extra_stream_config", $stream_default_server); + $block->set_value("stream_conf_enable", 1); + + # setup default conf.yaml + my $extra_yaml_config = $block->extra_yaml_config // <<_EOC_; +apisix: + stream_proxy: # TCP/UDP L4 proxy + only: true # Enable L4 proxy only without L7 proxy. + tcp: + - addr: 9100 # Set the TCP proxy listening ports. + tls: true + - addr: "127.0.0.1:9101" + udp: # Set the UDP proxy listening ports. + - 9200 + - "127.0.0.1:9201" +_EOC_ + + $block->set_value("extra_yaml_config", $extra_yaml_config); + + if (!$block->request) { + # use /do instead of /t because stream server will inject a default /t location + $block->set_value("request", "GET /do"); + } + + if ((!defined $block->error_log) && (!defined $block->no_error_log)) { + $block->set_value("no_error_log", "[error]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set route +--- config + location /do { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/plugin_metadata/chaitin-waf', + ngx.HTTP_PUT, + [[{ + "nodes": [ + { + "host": "127.0.0.1", + "port": 8088 + }, + { + "host": "127.0.0.1", + "port": 8089 + } + ] + }]] + ) + + if code >= 300 then + ngx.status = code + return ngx.print(body) + end + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "methods": ["GET"], + "plugins": { + "chaitin-waf": { + "upstream": { + "servers": ["httpbun.org"] + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/*" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: pass +--- request +GET /hello +--- error_code: 403 +--- response_body +{"code": 403, "success":false, "message": "blocked by Chaitin SafeLine Web Application Firewall", "event_id": "b3c6ce574dc24f09a01f634a39dca83b"} +--- error_log +--- response_headers +X-APISIX-CHAITIN-WAF: yes +X-APISIX-CHAITIN-WAF-STATUS: 403 +X-APISIX-CHAITIN-WAF-ACTION: reject +--- response_headers_like +X-APISIX-CHAITIN-WAF-TIME: diff --git a/t/plugin/chaitin-waf-timeout.t b/t/plugin/chaitin-waf-timeout.t new file mode 100644 index 000000000000..063f1bc4e186 --- /dev/null +++ b/t/plugin/chaitin-waf-timeout.t @@ -0,0 +1,139 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); + +add_block_preprocessor(sub { + my ($block) = @_; + + my $stream_default_server = <<_EOC_; + server { + listen 8088; + listen 8089; + content_by_lua_block { + require("lib.chaitin_waf_server").timeout() + } + } +_EOC_ + + $block->set_value("extra_stream_config", $stream_default_server); + $block->set_value("stream_conf_enable", 1); + + # setup default conf.yaml + my $extra_yaml_config = $block->extra_yaml_config // <<_EOC_; +apisix: + stream_proxy: # TCP/UDP L4 proxy + only: true # Enable L4 proxy only without L7 proxy. + tcp: + - addr: 9100 # Set the TCP proxy listening ports. + tls: true + - addr: "127.0.0.1:9101" + udp: # Set the UDP proxy listening ports. + - 9200 + - "127.0.0.1:9201" +plugins: + - chaitin-waf +_EOC_ + + $block->set_value("extra_yaml_config", $extra_yaml_config); + + if (!$block->request) { + # use /do instead of /t because stream server will inject a default /t location + $block->set_value("request", "GET /do"); + } + + if ((!defined $block->error_log) && (!defined $block->no_error_log)) { + $block->set_value("no_error_log", "[error]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: set route +--- config + location /do { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/plugin_metadata/chaitin-waf', + ngx.HTTP_PUT, + [[{ + "nodes": [ + { + "host": "127.0.0.1", + "port": 8088 + }, + { + "host": "127.0.0.1", + "port": 8089 + } + ] + }]] + ) + + if code >= 300 then + ngx.status = code + return ngx.print(body) + end + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "methods": ["GET"], + "plugins": { + "chaitin-waf": { + "upstream": { + "servers": ["httpbun.org"] + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/*" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: timeout +--- request +GET /hello +--- error_code: 200 +--- response_body +hello world +--- error_log +--- response_headers +X-APISIX-CHAITIN-WAF: timeout +--- response_headers_like +X-APISIX-CHAITIN-WAF-TIME: diff --git a/t/plugin/chaitin-waf.t b/t/plugin/chaitin-waf.t new file mode 100644 index 000000000000..e2ee42f93bad --- /dev/null +++ b/t/plugin/chaitin-waf.t @@ -0,0 +1,267 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +repeat_each(1); +no_long_string(); +no_root_location(); + +add_block_preprocessor(sub { + my ($block) = @_; + + my $stream_default_server = <<_EOC_; + server { + listen 8088; + listen 8089; + content_by_lua_block { + require("lib.chaitin_waf_server").pass() + } + } +_EOC_ + + $block->set_value("extra_stream_config", $stream_default_server); + $block->set_value("stream_conf_enable", 1); + + # setup default conf.yaml + my $extra_yaml_config = $block->extra_yaml_config // <<_EOC_; +apisix: + stream_proxy: # TCP/UDP L4 proxy + only: true # Enable L4 proxy only without L7 proxy. + tcp: + - addr: 9100 # Set the TCP proxy listening ports. + tls: true + - addr: "127.0.0.1:9101" + udp: # Set the UDP proxy listening ports. + - 9200 + - "127.0.0.1:9201" +plugins: + - chaitin-waf +_EOC_ + + $block->set_value("extra_yaml_config", $extra_yaml_config); + + if (!$block->request) { + # use /do instead of /t because stream server will inject a default /t location + $block->set_value("request", "GET /do"); + } + + if ((!defined $block->error_log) && (!defined $block->no_error_log)) { + $block->set_value("no_error_log", "[error]"); + } +}); + +run_tests; + +__DATA__ + +=== TEST 1: wrong schema: nodes empty +--- config + location /do { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/plugin_metadata/chaitin-waf', + ngx.HTTP_PUT, + [[{ + "nodes": [] + } + ]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"invalid configuration: property \"nodes\" validation failed: expect array to have at least 1 items"} + + + +=== TEST 2: wrong schema: nodes misses host +--- config + location /do { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/plugin_metadata/chaitin-waf', + ngx.HTTP_PUT, + [[{ + "nodes": [ + {} + ] + } + ]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"invalid configuration: property \"nodes\" validation failed: failed to validate item 1: property \"host\" is required"} + + + +=== TEST 3: sanity +--- config + location /do { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/plugin_metadata/chaitin-waf', + ngx.HTTP_PUT, + [[{ + "nodes": [ + { + "host": "127.0.0.1", + "port": 8088 + }, + { + "host": "127.0.0.1", + "port": 8089 + } + ] + }]] + ) + + if code >= 300 then + ngx.status = code + return ngx.print(body) + end + + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "methods": ["GET"], + "plugins": { + "chaitin-waf": { + "upstream": { + "servers": ["httpbun.org"] + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/*" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 4: pass +--- request +GET /hello +--- error_code: 200 +--- response_body +hello world +--- error_log +--- response_headers +X-APISIX-CHAITIN-WAF: yes +X-APISIX-CHAITIN-WAF-STATUS: 200 +X-APISIX-CHAITIN-WAF-ACTION: pass +--- response_headers_like +X-APISIX-CHAITIN-WAF-TIME: + + + +=== TEST 5: match condition +--- config + location /do { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "methods": ["GET"], + "plugins": { + "chaitin-waf": { + "upstream": { + "servers": ["httpbun.org"] + }, + "match": [ + { + "vars": [ + ["http_waf","==","true"] + ] + } + ] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/*" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 6: no match +--- request +GET /hello +--- error_code: 200 +--- response_body +hello world +--- error_log +--- response_headers +X-APISIX-CHAITIN-WAF: no + + + +=== TEST 7: matched +--- request +GET /hello +--- more_headers +waf: true +--- error_code: 200 +--- response_body +hello world +--- error_log +--- response_headers +X-APISIX-CHAITIN-WAF: yes +X-APISIX-CHAITIN-WAF-STATUS: 200 +X-APISIX-CHAITIN-WAF-ACTION: pass +--- response_headers_like +X-APISIX-CHAITIN-WAF-TIME: diff --git a/t/plugin/consumer-restriction.t b/t/plugin/consumer-restriction.t index 862e8695dd92..cc86aacdcd7d 100644 --- a/t/plugin/consumer-restriction.t +++ b/t/plugin/consumer-restriction.t @@ -314,7 +314,7 @@ passed GET /hello --- error_code: 401 --- response_body -{"message":"Missing authentication or identity verification."} +{"message":"The request is rejected, please check the consumer_name for this request"} @@ -325,7 +325,7 @@ GET /hello Authorization: Basic amFjazIwMTk6MTIzNDU2 --- error_code: 401 --- response_body -{"message":"Missing authentication or identity verification."} +{"message":"The request is rejected, please check the consumer_name for this request"} @@ -336,7 +336,7 @@ GET /hello Authorization: Basic amFjazIwMjA6MTIzNDU2 --- error_code: 401 --- response_body -{"message":"Missing authentication or identity verification."} +{"message":"The request is rejected, please check the consumer_name for this request"} @@ -383,7 +383,7 @@ passed GET /hello --- error_code: 401 --- response_body -{"message":"Missing authentication or identity verification."} +{"message":"The request is rejected, please check the consumer_name for this request"} @@ -394,7 +394,7 @@ GET /hello Authorization: Basic amFjazIwMTk6MTIzNDU2 --- error_code: 401 --- response_body -{"message":"Missing authentication or identity verification."} +{"message":"The request is rejected, please check the consumer_name for this request"} @@ -405,7 +405,7 @@ GET /hello Authorization: Basic amFjazIwMjA6MTIzNDU2 --- error_code: 401 --- response_body -{"message":"Missing authentication or identity verification."} +{"message":"The request is rejected, please check the consumer_name for this request"} diff --git a/t/plugin/consumer-restriction2.t b/t/plugin/consumer-restriction2.t index 6fdba1daafe1..febae314df76 100644 --- a/t/plugin/consumer-restriction2.t +++ b/t/plugin/consumer-restriction2.t @@ -135,7 +135,37 @@ passed -=== TEST 5: set whitelist +=== TEST 5: consumer jack3 with no consumer group +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/consumers', + ngx.HTTP_PUT, + [[{ + "username": "jack3", + "plugins": { + "basic-auth": { + "username": "jack2021", + "password": "123456" + } + } + }]] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 6: set whitelist --- config location /t { content_by_lua_block { @@ -175,7 +205,7 @@ passed -=== TEST 6: verify unauthorized +=== TEST 7: verify unauthorized --- request GET /hello --- error_code: 401 @@ -184,7 +214,7 @@ GET /hello -=== TEST 7: verify jack1 +=== TEST 8: verify jack1 --- request GET /hello --- more_headers @@ -194,7 +224,7 @@ hello world -=== TEST 8: verify jack2 +=== TEST 9: verify jack2 --- request GET /hello --- more_headers @@ -205,7 +235,7 @@ Authorization: Basic amFjazIwMjA6MTIzNDU2 -=== TEST 9: set blacklist +=== TEST 10: set blacklist --- config location /t { content_by_lua_block { @@ -246,7 +276,7 @@ passed -=== TEST 10: verify unauthorized +=== TEST 11: verify unauthorized --- request GET /hello --- error_code: 401 @@ -255,7 +285,7 @@ GET /hello -=== TEST 11: verify jack1 +=== TEST 12: verify jack1 --- request GET /hello --- more_headers @@ -266,10 +296,119 @@ Authorization: Basic amFjazIwMTk6MTIzNDU2 -=== TEST 12: verify jack2 +=== TEST 13: verify jack2 --- request GET /hello --- more_headers Authorization: Basic amFjazIwMjA6MTIzNDU2 --- response_body hello world + + + +=== TEST 14: verify jack2 +--- request +GET /hello +--- more_headers +Authorization: Basic amFjazIwMjE6MTIzNDU2 +--- error_code: 401 +--- response_body +{"message":"The request is rejected, please check the consumer_group_id for this request"} + + + +=== TEST 15: set blacklist with service_id +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + }, + "plugins": { + "consumer-restriction": { + "type": "service_id", + "blacklist": [ + "1" + ], + "rejected_msg": "request is forbidden" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 16: hit +--- request +GET /hello +--- error_code: 401 +--- response_body +{"message":"The request is rejected, please check the service_id for this request"} + + + +=== TEST 17: set whitelist with service_id +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + }, + "plugins": { + "consumer-restriction": { + "type": "service_id", + "whitelist": [ + "1" + ], + "rejected_msg": "request is forbidden" + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 18: hit +--- request +GET /hello +--- error_code: 401 +--- response_body +{"message":"The request is rejected, please check the service_id for this request"} diff --git a/t/plugin/file-logger2.t b/t/plugin/file-logger2.t index 90ce001c64c0..5cf3a76ccf4d 100644 --- a/t/plugin/file-logger2.t +++ b/t/plugin/file-logger2.t @@ -274,3 +274,95 @@ passed } --- response_body write file log success + + + +=== TEST 8: Add new configuration with match +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "file-logger": { + "path": "file-with-match.log", + "match": [ + [ + [ "arg_name","==","jack" ] + ] + ], + "log_format": { + "request": "$request" + } + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1982": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 9: Request match +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin").test + local code = t("/hello?name=jack", ngx.HTTP_GET) + local fd, err = io.open("file-with-match.log", 'r') + if not fd then + core.log.error("failed to open file: file-with-match.log, error info: ", err) + return + end + local msg = fd:read() + + local new_msg = core.json.decode(msg) + if new_msg.request == 'GET /hello?name=jack HTTP/1.1' + and new_msg.route_id == '1' + then + msg = "write file log success" + ngx.status = code + ngx.say(msg) + end + + os.remove("file-with-match.log") + } + } +--- response_body +write file log success + + + +=== TEST 10: Request not match +--- config + location /t { + content_by_lua_block { + local core = require("apisix.core") + local t = require("lib.test_admin").test + local code = t("/hello?name=tony", ngx.HTTP_GET) + local fd, err = io.open("file-with-match.log", 'r') + if not fd then + local msg = "not write file log" + ngx.say(msg) + return + end + } + } +--- response_body +not write file log diff --git a/t/plugin/google-cloud-logging.t b/t/plugin/google-cloud-logging.t index ff03e157400a..bc4293cf6755 100644 --- a/t/plugin/google-cloud-logging.t +++ b/t/plugin/google-cloud-logging.t @@ -74,6 +74,7 @@ passed local plugin = require("apisix.plugins.google-cloud-logging") local ok, err = plugin.check_schema({ auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", private_key = "private_key", project_id = "apisix", token_uri = "http://127.0.0.1:1980/token", @@ -132,6 +133,7 @@ passed local plugin = require("apisix.plugins.google-cloud-logging") local ok, err = plugin.check_schema({ auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", private_key = "private_key", project_id = "apisix", token_uri = "http://127.0.0.1:1980/token", @@ -190,6 +192,7 @@ value should match only one schema, but matches none plugins = { ["google-cloud-logging"] = { auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", private_key = [[ -----BEGIN RSA PRIVATE KEY----- MIIBOwIBAAJBAKeXgPvU/dAfVhOPk5BTBXCaOXy/0S3mY9VHyqvWZBJ97g6tGbLZ @@ -260,6 +263,7 @@ Batch Processor[google-cloud-logging] exceeded the max_retry_count plugins = { ["google-cloud-logging"] = { auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", private_key = [[ -----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR @@ -346,6 +350,7 @@ Batch Processor[google-cloud-logging] exceeded the max_retry_count plugins = { ["google-cloud-logging"] = { auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", private_key = [[ -----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR @@ -428,6 +433,7 @@ hello world plugins = { ["google-cloud-logging"] = { auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", private_key = [[ -----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR @@ -510,6 +516,7 @@ hello world plugins = { ["google-cloud-logging"] = { auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", private_key = [[ -----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR diff --git a/t/plugin/google-cloud-logging2.t b/t/plugin/google-cloud-logging2.t index 4290d3fc84b9..def2ca0ca00a 100644 --- a/t/plugin/google-cloud-logging2.t +++ b/t/plugin/google-cloud-logging2.t @@ -93,6 +93,7 @@ apisix: plugins = { ["google-cloud-logging"] = { auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", private_key = [[ -----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR @@ -211,6 +212,7 @@ YnwwDKc5vNzo0OU4StTRQbwgCnTZ3dmYiBFm8aGnvTxlE86D2nT07Q3BWhUdky6OGIox4MRLbiHz13NZ plugins = { ["google-cloud-logging"] = { auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", private_key = [[ -----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR @@ -350,6 +352,7 @@ the mock backend is hit plugins = { ["google-cloud-logging"] = { auth_config = { + client_email = "email@apisix.iam.gserviceaccount.com", private_key = [[ -----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDDzrFwnA3EvYyR diff --git a/t/plugin/ldap-auth.t b/t/plugin/ldap-auth.t index e16bc05135a3..ba16f2c75b0b 100644 --- a/t/plugin/ldap-auth.t +++ b/t/plugin/ldap-auth.t @@ -359,7 +359,7 @@ find consumer user01 "plugins": { "ldap-auth": { "base_dn": "ou=users,dc=example,dc=org", - "ldap_uri": "localhost:1636", + "ldap_uri": "test.com:1636", "uid": "cn", "use_tls": true } @@ -408,7 +408,7 @@ find consumer user01 "plugins": { "ldap-auth": { "base_dn": "ou=users,dc=example,dc=org", - "ldap_uri": "localhost:1636", + "ldap_uri": "test.com:1636", "uid": "cn", "use_tls": true, "tls_verify": true diff --git a/t/plugin/limit-count-redis-cluster2.t b/t/plugin/limit-count-redis-cluster2.t index d5363c016d8b..ede8ee9dcc82 100644 --- a/t/plugin/limit-count-redis-cluster2.t +++ b/t/plugin/limit-count-redis-cluster2.t @@ -87,3 +87,53 @@ OK OK OK Done + + + +=== TEST 2: test header X-RateLimit-Reset shouldn't be set to 0 after request be rejected +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello2", + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "key": "remote_addr", + "policy": "redis-cluster", + "redis_cluster_nodes": [ + "127.0.0.1:5000", + "127.0.0.1:5001" + ], + "redis_cluster_name": "redis-cluster-1" + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + } + }]] + ) + for i = 1, 3 do + local _, _, headers = t('/hello2', ngx.HTTP_GET) + ngx.sleep(1) + if tonumber(headers["X-RateLimit-Reset"]) > 0 then + ngx.say("OK") + else + ngx.say("WRONG") + end + end + ngx.say("Done") + } + } +--- response_body +OK +OK +OK +Done diff --git a/t/plugin/limit-count-redis3.t b/t/plugin/limit-count-redis3.t index a7694f63b518..bf952df1863b 100644 --- a/t/plugin/limit-count-redis3.t +++ b/t/plugin/limit-count-redis3.t @@ -301,3 +301,74 @@ GET /hello hello world --- error_log failed to do ssl handshake + + + +=== TEST 10: set router +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "plugins": { + "limit-count": { + "count": 2, + "time_window": 60, + "policy": "redis", + "redis_host": "127.0.0.1", + "redis_port": 6379, + "redis_database": 1, + "redis_timeout": 1001 + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 11: test header X-RateLimit-Reset shouldn't be set to 0 after request be rejected +--- config + location /t { + content_by_lua_block { + local json = require "t.toolkit.json" + local http = require "resty.http" + local uri = "http://127.0.0.1:" .. ngx.var.server_port + .. "/hello" + local ress = {} + for i = 1, 3 do + local httpc = http.new() + local res, err = httpc:request_uri(uri) + if not res then + ngx.say(err) + return + end + ngx.sleep(1) + local reset = res.headers["X-RateLimit-Reset"] + if tonumber(reset) <= 0 then + ngx.say("failed") + end + + end + ngx.say("success") + } + } +--- response_body +success diff --git a/t/plugin/limit-count4.t b/t/plugin/limit-count4.t index bcefe5156fd0..a3453c58caaf 100644 --- a/t/plugin/limit-count4.t +++ b/t/plugin/limit-count4.t @@ -171,3 +171,34 @@ passed } --- response_body ["1","0","0"] + + + +=== TEST 5: test header X-RateLimit-Reset shouldn't be set to 0 after request be rejected +--- config + location /t { + content_by_lua_block { + local json = require "t.toolkit.json" + local http = require "resty.http" + local uri = "http://127.0.0.1:" .. ngx.var.server_port + .. "/hello" + local ress = {} + for i = 1, 3 do + local httpc = http.new() + local res, err = httpc:request_uri(uri) + if not res then + ngx.say(err) + return + end + ngx.sleep(1) + local reset = res.headers["X-RateLimit-Reset"] + if tonumber(reset) <= 0 then + ngx.say("failed") + end + + end + ngx.say("success") + } + } +--- response_body +success diff --git a/t/plugin/loki-logger.t b/t/plugin/loki-logger.t index faa8749a917d..72c79f1e97a6 100644 --- a/t/plugin/loki-logger.t +++ b/t/plugin/loki-logger.t @@ -306,3 +306,71 @@ hello world } } --- error_code: 200 + + + +=== TEST 12: setup route (with log_labels as variables) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "loki-logger": { + "endpoint_addrs": ["http://127.0.0.1:3100"], + "tenant_id": "tenant_1", + "log_labels": { + "custom_label": "$remote_addr" + }, + "batch_max_size": 1 + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 13: hit route +--- request +GET /hello +--- response_body +hello world + + + +=== TEST 14: check loki log (with custom_label) +--- config + location /t { + content_by_lua_block { + local cjson = require("cjson") + local now = ngx.now() * 1000 + local data, err = require("lib.grafana_loki").fetch_logs_from_loki( + tostring(now - 3000) .. "000000", -- from + tostring(now) .. "000000", -- to + { query = [[{custom_label="127.0.0.1"} | json]] } + ) + + assert(err == nil, "fetch logs error: " .. (err or "")) + assert(data.status == "success", "loki response error: " .. cjson.encode(data)) + assert(#data.data.result > 0, "loki log empty: " .. cjson.encode(data)) + } + } +--- error_code: 200 diff --git a/t/plugin/mocking.t b/t/plugin/mocking.t index 644ee2cf38df..46d82ef80a8f 100644 --- a/t/plugin/mocking.t +++ b/t/plugin/mocking.t @@ -424,3 +424,44 @@ passed GET /hello --- response_body chomp empty_var: + + + +=== TEST 19: set route (return headers) +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "mocking": { + "response_example": "hello world", + "response_headers": { + "X-Apisix": "is, cool", + "X-Really": "yes" + } + } + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 20: hit route +--- request +GET /hello +--- response_headers +X-Apisix: is, cool +X-Really: yes diff --git a/t/plugin/opa.t b/t/plugin/opa.t index 9354b35d1043..9d731ae0682c 100644 --- a/t/plugin/opa.t +++ b/t/plugin/opa.t @@ -179,3 +179,47 @@ test-header: only-for-test --- error_code: 403 --- response {"code":40001,"desc":"Give you a object reason"} + + + +=== TEST 12: setup route with plugin +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "opa": { + "host": "http://127.0.0.1:8181", + "policy": "example", + "send_headers_upstream": ["user"] + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uris": ["/echo"] + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 13: hit route +--- request +GET /echo?test=1234&user=none +--- response_headers +user: none diff --git a/t/plugin/openid-connect3.t b/t/plugin/openid-connect3.t new file mode 100644 index 000000000000..84eb7c26de2b --- /dev/null +++ b/t/plugin/openid-connect3.t @@ -0,0 +1,111 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX 'no_plan'; + +log_level('debug'); +repeat_each(1); +no_long_string(); +no_root_location(); +no_shuffle(); + +add_block_preprocessor(sub { + my ($block) = @_; + + if ((!defined $block->error_log) && (!defined $block->no_error_log)) { + $block->set_value("no_error_log", "[error]"); + } + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } +}); + +run_tests(); + +__DATA__ + +=== TEST 1: Set up new route access the auth server via http proxy +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "openid-connect": { + "client_id": "kbyuFDidLLm280LIwVFiazOqjO3ty8KH", + "client_secret": "60Op4HFM0I8ajz0WdiStAbziZ-VFQttXuxixHHs2R7r7-CW8GR79l-mmLqMhc-Sa", + "discovery": "https://samples.auth0.com/.well-known/openid-configuration", + "redirect_uri": "https://iresty.com", + "ssl_verify": false, + "timeout": 10, + "scope": "apisix", + "proxy_opts": { + "http_proxy": "http://127.0.0.1:8080", + "http_proxy_authorization": "Basic dXNlcm5hbWU6cGFzc3dvcmQK" + }, + "use_pkce": false + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1980": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + + } + } +--- response_body +passed + + + +=== TEST 2: Access route w/o bearer token. Should redirect to authentication endpoint of ID provider. +--- config + location /t { + content_by_lua_block { + local http = require "resty.http" + local httpc = http.new() + local uri = "http://127.0.0.1:" .. ngx.var.server_port .. "/hello" + local res, err = httpc:request_uri(uri, {method = "GET"}) + ngx.status = res.status + local location = res.headers['Location'] + if location and string.find(location, 'https://samples.auth0.com/authorize') ~= -1 and + string.find(location, 'scope=apisix') ~= -1 and + string.find(location, 'client_id=kbyuFDidLLm280LIwVFiazOqjO3ty8KH') ~= -1 and + string.find(location, 'response_type=code') ~= -1 and + string.find(location, 'redirect_uri=https://iresty.com') ~= -1 then + ngx.say(true) + end + } + } +--- timeout: 10s +--- response_body +true +--- error_code: 302 +--- error_log +use http proxy diff --git a/t/plugin/proxy-rewrite3.t b/t/plugin/proxy-rewrite3.t index 98f27de74177..55afe14cc738 100644 --- a/t/plugin/proxy-rewrite3.t +++ b/t/plugin/proxy-rewrite3.t @@ -942,3 +942,60 @@ GET /test/plugin/proxy/rewrite/world HTTP/1.1 } --- response_body /world/plugin_proxy_rewrite + + + +=== TEST 40: use regex uri with unsafe allowed +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "proxy-rewrite": { + "regex_uri": [ + "/hello/(.+)", + "/hello?unsafe_variable=$1" + ], + "use_real_request_uri_unsafe": true + } + }, + "upstream": { + "nodes": { + "127.0.0.1:8125": 1 + }, + "type": "roundrobin" + }, + "uri": "/hello/*" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- request +GET /t +--- response_body +passed + + + +=== TEST 41: hit +--- request +GET /hello/%ED%85%8C%EC%8A%A4%ED%8A%B8 HTTP/1.1 +--- http_config + server { + listen 8125; + location / { + content_by_lua_block { + ngx.say(ngx.var.request_uri) + } + } + } +--- response_body +/hello?unsafe_variable=%ED%85%8C%EC%8A%A4%ED%8A%B8 diff --git a/t/plugin/request-id2.t b/t/plugin/request-id2.t index a66c6b3ddaa3..421a0891c39f 100644 --- a/t/plugin/request-id2.t +++ b/t/plugin/request-id2.t @@ -70,7 +70,7 @@ passed -=== TEST 2: add plugin with algorithm range_id +=== TEST 2: add plugin with algorithm range_id (set automatic default) --- config location /t { content_by_lua_block { @@ -103,7 +103,13 @@ passed -=== TEST 3: add plugin with algorithm range_id +=== TEST 3: hit +--- request +GET /opentracing + + + +=== TEST 4: add plugin with algorithm range_id --- config location /t { content_by_lua_block { diff --git a/t/plugin/tencent-cloud-cls.t b/t/plugin/tencent-cloud-cls.t index fff3bc12950e..6ad4d585b3b1 100644 --- a/t/plugin/tencent-cloud-cls.t +++ b/t/plugin/tencent-cloud-cls.t @@ -500,3 +500,61 @@ GET /opentracing --- response_body opentracing --- wait: 0.5 + + + +=== TEST 15: add plugin +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "tencent-cloud-cls": { + "cls_host": "127.0.0.1:10420", + "cls_topic": "143b5d70-139b-4aec-b54e-bb97756916de", + "secret_id": "secret_id", + "secret_key": "secret_key", + "batch_max_size": 1, + "max_retry_count": 1, + "retry_delay": 2, + "buffer_duration": 2, + "inactive_timeout": 2 + } + }, + "upstream": { + "nodes": { + "127.0.0.1:1982": 1 + }, + "type": "roundrobin" + }, + "uri": "/opentracing" + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 16: test resolvt e ip failed +--- extra_init_by_lua + local socket = require("socket") + socket.dns.toip = function(address) + return nil, "address can't be resolved" + end +--- request +GET /opentracing +--- response_body +opentracing +--- error_log eval +qr/resolve ip failed, hostname: .*, error: address can't be resolved/ +--- wait: 0.5 diff --git a/t/plugin/traffic-split2.t b/t/plugin/traffic-split2.t index 05b8047d3f24..5100337e48d9 100644 --- a/t/plugin/traffic-split2.t +++ b/t/plugin/traffic-split2.t @@ -752,3 +752,50 @@ GET /uri?id=1 qr/host: 127.0.0.1/ --- error_log proxy request to 127.0.0.1:1980 + + + +=== TEST 20: invalid upstream_id should report failure +--- config + location /t { + content_by_lua_block { + local json = require("toolkit.json") + local t = require("lib.test_admin").test + + local data = { + uri = "/route", + plugins = { + ["traffic-split"] = { + rules = { + { + weighted_upstreams = { + { + upstream_id = "invalid-id", + weight = 1 + } + } + }, + } + } + }, + upstream = { + type = "roundrobin", + nodes = { + ["127.0.0.1:1980"] = 1 + } + } + } + + code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PATCH, + json.encode(data) + ) + ngx.status, body = t('/route', ngx.HTTP_GET) + ngx.say(body) + } + } +--- request +GET /t +--- error_code: 500 +--- error_log +failed to find upstream by id: invalid-id diff --git a/t/plugin/ua-restriction.t b/t/plugin/ua-restriction.t index 0e8a9544bd34..56f07b39b13f 100644 --- a/t/plugin/ua-restriction.t +++ b/t/plugin/ua-restriction.t @@ -38,13 +38,12 @@ run_tests; __DATA__ -=== TEST 1: set allowlist, denylist, bypass_missing and user-defined message +=== TEST 1: set both allowlist and denylist --- config location /t { content_by_lua_block { local plugin = require("apisix.plugins.ua-restriction") local conf = { - bypass_missing = true, allowlist = { "my-bot1", "my-bot2" @@ -53,18 +52,18 @@ __DATA__ "my-bot1", "my-bot2" }, - message = "User-Agent Forbidden", } local ok, err = plugin.check_schema(conf) if not ok then ngx.say(err) + return end ngx.say(require("toolkit.json").encode(conf)) } } --- response_body -{"allowlist":["my-bot1","my-bot2"],"bypass_missing":true,"denylist":["my-bot1","my-bot2"],"message":"User-Agent Forbidden"} +value should match only one schema, but matches both schemas 1 and 2 @@ -216,88 +215,30 @@ User-Agent:my-bot2 -=== TEST 9: hit route and user-agent match denylist regex +=== TEST 9: hit route and user-agent in denylist with reverse order multiple user-agent --- request GET /hello --- more_headers -User-Agent:Baiduspider/3.0 +User-Agent:my-bot2 +User-Agent:my-bot1 --- error_code: 403 --- response_body {"message":"Not allowed"} -=== TEST 10: hit route and user-agent not in denylist ---- request -GET /hello ---- more_headers -User-Agent:foo/bar ---- error_code: 200 ---- response_body -hello world - - - -=== TEST 11: set allowlist ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1', - ngx.HTTP_PUT, - [[{ - "uri": "/hello", - "upstream": { - "type": "roundrobin", - "nodes": { - "127.0.0.1:1980": 1 - } - }, - "plugins": { - "ua-restriction": { - "allowlist": [ - "my-bot1", - "(Baiduspider)/(\\d+)\\.(\\d+)" - ] - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 12: hit route and user-agent in allowlist ---- request -GET /hello ---- more_headers -User-Agent:my-bot1 ---- error_code: 200 ---- response_body -hello world - - - -=== TEST 13: hit route and user-agent match allowlist regex +=== TEST 10: hit route and user-agent match denylist regex --- request GET /hello --- more_headers User-Agent:Baiduspider/3.0 ---- error_code: 200 +--- error_code: 403 --- response_body -hello world +{"message":"Not allowed"} -=== TEST 14: hit route and user-agent not in allowlist +=== TEST 11: hit route and user-agent not in denylist --- request GET /hello --- more_headers @@ -308,7 +249,7 @@ hello world -=== TEST 15: set config: user-agent in both allowlist and denylist +=== TEST 12: set allowlist --- config location /t { content_by_lua_block { @@ -326,11 +267,7 @@ hello world "plugins": { "ua-restriction": { "allowlist": [ - "foo/bar", - "(Baiduspider)/(\\d+)\\.(\\d+)" - ], - "denylist": [ - "foo/bar", + "my-bot1", "(Baiduspider)/(\\d+)\\.(\\d+)" ] } @@ -349,157 +286,62 @@ passed -=== TEST 16: hit route and user-agent in both allowlist and denylist, pass(part 1) +=== TEST 13: hit route and user-agent in allowlist --- request GET /hello --- more_headers -User-Agent:foo/bar +User-Agent:my-bot1 --- error_code: 200 --- response_body hello world -=== TEST 17: hit route and user-agent in both allowlist and denylist, pass(part 2) +=== TEST 14: hit route and user-agent match allowlist regex --- request GET /hello --- more_headers -User-Agent:Baiduspider/1.0 +User-Agent:Baiduspider/3.0 --- error_code: 200 --- response_body hello world -=== TEST 18: bypass_missing test, using default, reset conf(part1) ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1', - ngx.HTTP_PUT, - [[{ - "uri": "/hello", - "upstream": { - "type": "roundrobin", - "nodes": { - "127.0.0.1:1980": 1 - } - }, - "plugins": { - "ua-restriction": { - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 19: bypass_missing test, using default, send request without User-Agent(part2) +=== TEST 15: hit route and user-agent not in allowlist --- request GET /hello +--- more_headers +User-Agent:foo/bar --- error_code: 403 --- response_body {"message":"Not allowed"} -=== TEST 20: bypass_missing test, set to true(part1) ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1', - ngx.HTTP_PUT, - [[{ - "uri": "/hello", - "upstream": { - "type": "roundrobin", - "nodes": { - "127.0.0.1:1980": 1 - } - }, - "plugins": { - "ua-restriction": { - "bypass_missing": true - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 21: bypass_missing test, set to true, send request without User-Agent(part2) +=== TEST 16: hit route and user-agent in allowlist with multiple user-agent --- request GET /hello ---- error_code: 200 +--- more_headers +User-Agent:foo/bar +User-Agent:my-bot1 --- response_body hello world -=== TEST 22: bypass_missing test, set to false(part1) ---- config - location /t { - content_by_lua_block { - local t = require("lib.test_admin").test - local code, body = t('/apisix/admin/routes/1', - ngx.HTTP_PUT, - [[{ - "uri": "/hello", - "upstream": { - "type": "roundrobin", - "nodes": { - "127.0.0.1:1980": 1 - } - }, - "plugins": { - "ua-restriction": { - "bypass_missing": false - } - } - }]] - ) - - if code >= 300 then - ngx.status = code - end - ngx.say(body) - } - } ---- response_body -passed - - - -=== TEST 23: bypass_missing test, set to false, send request without User-Agent(part2) +=== TEST 17: hit route and user-agent in allowlist with reverse order multiple user-agent --- request GET /hello ---- error_code: 403 +--- more_headers +User-Agent:my-bot1 +User-Agent:foo/bar --- response_body -{"message":"Not allowed"} +hello world -=== TEST 24: message that do not reach the minimum range +=== TEST 18: message that do not reach the minimum range --- config location /t { content_by_lua_block { @@ -530,7 +372,7 @@ qr/string too short, expected at least 1, got 0/ -=== TEST 25: exceeds the maximum limit of message +=== TEST 19: exceeds the maximum limit of message --- config location /t { content_by_lua_block { @@ -568,7 +410,7 @@ qr/string too long, expected at most 1024, got 1025/ -=== TEST 26: set custom message +=== TEST 20: set custom message --- config location /t { content_by_lua_block { @@ -606,7 +448,7 @@ passed -=== TEST 27: test custom message +=== TEST 21: test custom message --- request GET /hello --- more_headers @@ -617,7 +459,7 @@ User-Agent:Baiduspider/1.0 -=== TEST 28: test remove ua-restriction, add denylist(part 1) +=== TEST 22: test remove ua-restriction, add denylist(part 1) --- config location /enable { content_by_lua_block { @@ -656,7 +498,7 @@ passed -=== TEST 29: test remove ua-restriction, fail(part 2) +=== TEST 23: test remove ua-restriction, fail(part 2) --- request GET /hello --- more_headers @@ -667,7 +509,7 @@ User-Agent:Baiduspider/1.0 -=== TEST 30: test remove ua-restriction, remove plugin(part 3) +=== TEST 24: test remove ua-restriction, remove plugin(part 3) --- config location /disable { content_by_lua_block { @@ -701,7 +543,7 @@ passed -=== TEST 31: test remove ua-restriction, check spider User-Agent(part 4) +=== TEST 25: test remove ua-restriction, check spider User-Agent(part 4) --- request GET /hello --- more_headers @@ -711,7 +553,7 @@ hello world -=== TEST 32: set disable=true +=== TEST 26: set disable=true --- config location /t { content_by_lua_block { @@ -744,7 +586,7 @@ passed -=== TEST 33: the element in allowlist is null +=== TEST 27: the element in allowlist is null --- config location /t { content_by_lua_block { @@ -771,7 +613,7 @@ done -=== TEST 34: the element in denylist is null +=== TEST 28: the element in denylist is null --- config location /t { content_by_lua_block { @@ -795,3 +637,125 @@ done --- response_body property "denylist" validation failed: wrong type: expected array, got table done + + + +=== TEST 29: test both allowlist and denylist are not exist +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + }, + "plugins": { + "ua-restriction": { + } + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.print(body) + } + } +--- error_code: 400 +--- response_body +{"error_msg":"failed to check the configuration of plugin ua-restriction err: value should match only one schema, but matches none"} + + + +=== TEST 30: test bypass_missing +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + }, + "plugins": { + "ua-restriction": { + "allowlist": [ + "my-bot1" + ] + } + } + }]] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 31: hit +--- request +GET /hello +--- error_code: 403 +--- response_body +{"message":"Not allowed"} + + + +=== TEST 32: test bypass_missing with true +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/routes/1', + ngx.HTTP_PUT, + [[{ + "uri": "/hello", + "upstream": { + "type": "roundrobin", + "nodes": { + "127.0.0.1:1980": 1 + } + }, + "plugins": { + "ua-restriction": { + "bypass_missing": true, + "denylist": [ + "my-bot1" + ] + } + } + }]] + ) + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 33: hit +--- request +GET /hello +--- response_body +hello world diff --git a/t/router/radixtree-sni.t b/t/router/radixtree-sni.t index 9a28e3d78f78..781bf28d9b95 100644 --- a/t/router/radixtree-sni.t +++ b/t/router/radixtree-sni.t @@ -202,7 +202,7 @@ location /t { connected: 1 failed to do SSL handshake: handshake failed --- error_log -failed to find any SSL certificate by SNI +failed to match any SSL certificate by SNI diff --git a/t/router/radixtree-sni2.t b/t/router/radixtree-sni2.t index c64a20aae344..c761c9043d45 100644 --- a/t/router/radixtree-sni2.t +++ b/t/router/radixtree-sni2.t @@ -405,7 +405,7 @@ location /t { --- response_body failed to do SSL handshake: handshake failed --- error_log -failed to fetch ssl config: failed to find SNI: please check if the client requests via IP or uses an outdated protocol +failed to find SNI: please check if the client requests via IP or uses an outdated protocol --- no_error_log [alert] diff --git a/t/stream-node/sni.t b/t/stream-node/sni.t index f2833d2f494c..41554ba6c0c2 100644 --- a/t/stream-node/sni.t +++ b/t/stream-node/sni.t @@ -276,6 +276,7 @@ proxy request to 127.0.0.2:1995 --- yaml_config apisix: node_listen: 1984 + proxy_mode: http&stream stream_proxy: tcp: - 9100 diff --git a/t/stream-plugin/limit-conn2.t b/t/stream-plugin/limit-conn2.t new file mode 100644 index 000000000000..9efb2b6dfb1f --- /dev/null +++ b/t/stream-plugin/limit-conn2.t @@ -0,0 +1,134 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +use t::APISIX; + +my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; +my $version = eval { `$nginx_binary -V 2>&1` }; + +if ($version !~ m/\/apisix-nginx-module/) { + plan(skip_all => "apisix-nginx-module not installed"); +} else { + plan('no_plan'); +} + +$ENV{TEST_NGINX_REDIS_PORT} ||= 1985; + +add_block_preprocessor(sub { + my ($block) = @_; + + if (!$block->extra_yaml_config) { + my $extra_yaml_config = <<_EOC_; +xrpc: + protocols: + - name: redis +_EOC_ + $block->set_value("extra_yaml_config", $extra_yaml_config); + } + + + if (!defined $block->request) { + $block->set_value("request", "GET /t"); + } + + $block; +}); + +worker_connections(1024); +run_tests; + +__DATA__ + +=== TEST 1: create a stream router with limit-conn +--- config + location /t { + content_by_lua_block { + local t = require("lib.test_admin").test + local code, body = t('/apisix/admin/stream_routes/1', + ngx.HTTP_PUT, + [[{ + "plugins": { + "limit-conn": { + "conn": 2, + "burst": 1, + "default_conn_delay": 0.1, + "key": "$remote_port $server_addr", + "key_type": "var_combination" + } + }, + "upstream": { + "type": "none", + "nodes": { + "127.0.0.1:6379": 1 + } + }, + "protocol": { + "name": "redis" + } + }]] + ) + + if code >= 300 then + ngx.status = code + end + ngx.say(body) + } + } +--- response_body +passed + + + +=== TEST 2: access the redis via proxy +--- config + location /t { + content_by_lua_block { + local redis = require "resty.redis" + local red = redis:new() + + local ok, err = red:connect("127.0.0.1", $TEST_NGINX_REDIS_PORT) + if not ok then + ngx.say("failed to connect: ", err) + return + end + + local res, err = red:hmset("animals", "dog", "bark", "cat", "meow") + if not res then + ngx.say("failed to set animals: ", err) + return + end + ngx.say("hmset animals: ", res) + + local res, err = red:hmget("animals", "dog", "cat") + if not res then + ngx.say("failed to get animals: ", err) + return + end + ngx.say("hmget animals: ", res) + + ok, err = red:close() + if not ok then + ngx.say("failed to close: ", err) + return + end + } + } +--- response_body +hmset animals: OK +hmget animals: barkmeow +--- no_error_log +attempt to perform arithmetic on field 'request_time' +--- stream_conf_enable diff --git a/t/tars/discovery/stream/tars.t b/t/tars/discovery/stream/tars.t index b7c55a0f49f5..b674970272ba 100644 --- a/t/tars/discovery/stream/tars.t +++ b/t/tars/discovery/stream/tars.t @@ -19,11 +19,7 @@ use t::APISIX; my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; my $version = eval { `$nginx_binary -V 2>&1` }; -if ($version =~ m/\/1.19.3/) { - plan(skip_all => "require OpenResty version >= 1.19.9.1"); -} else { - plan('no_plan'); -} +plan('no_plan'); repeat_each(1); log_level('warn'); diff --git a/t/xrpc/redis2.t b/t/xrpc/redis2.t index 7e378f836e49..076a406b4f4f 100644 --- a/t/xrpc/redis2.t +++ b/t/xrpc/redis2.t @@ -16,6 +16,8 @@ # use t::APISIX; +log_level("warn"); + my $nginx_binary = $ENV{'TEST_NGINX_BINARY'} || 'nginx'; my $version = eval { `$nginx_binary -V 2>&1` }; @@ -117,7 +119,7 @@ passed { name = "syslog", filter = { - {"rpc_time", ">=", 0.01}, + {"rpc_time", ">=", 0.001}, }, conf = { host = "127.0.0.1", @@ -149,6 +151,7 @@ passed === TEST 3: verify the data received by the log server +--- stream_conf_enable --- config location /t { content_by_lua_block { @@ -190,11 +193,10 @@ passed hmset animals: OK hmget animals: barkmeow ping: pong ---- stream_conf_enable --- wait: 1 --- grep_error_log eval -qr/message received:.*\"redis_cmd_line\":[^,]+/ +qr/message received:.*\"redis_cmd_line\":[^}|^,]+/ --- grep_error_log_out eval -[qr/message received:.*\"redis_cmd_line\":\"hmset animals dog bark cat meow\"/, -qr/message received:.*\"redis_cmd_line\":\"hmget animals dog cat\"/, -qr/message received:.*\"redis_cmd_line\":\"ping\"/] +qr{message received:.*\"redis_cmd_line\":\"hmset animals dog bark cat meow\"(?s).* +message received:.*\"redis_cmd_line\":\"hmget animals dog cat\"(?s).* +message received:.*\"redis_cmd_line\":\"ping\"} diff --git a/utils/install-dependencies.sh b/utils/install-dependencies.sh index 6ddc25804cd8..220407808985 100755 --- a/utils/install-dependencies.sh +++ b/utils/install-dependencies.sh @@ -112,7 +112,7 @@ function multi_distro_installation() { install_dependencies_with_apt "debian" elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/*-release; then install_dependencies_with_apt "ubuntu" - elif grep -Eqi "Arch" /etc/issue || grep -Eq "Arch" /etc/*-release; then + elif grep -Eqi "Arch" /etc/issue || grep -Eqi "EndeavourOS" /etc/issue || grep -Eq "Arch" /etc/*-release; then install_dependencies_with_aur else echo "Non-supported operating system version"