diff --git a/.requirements b/.requirements index 1dbfc0f7ea09..f633665c68fd 100644 --- a/.requirements +++ b/.requirements @@ -18,4 +18,4 @@ APISIX_PACKAGE_NAME=apisix APISIX_RUNTIME=1.3.18 -APISIX_DASHBOARD_COMMIT=045e3142867e3b7d5d1b8ec40bf8f66a7ce24a64 +APISIX_DASHBOARD_COMMIT=fa2fd0f60f8afffb096476333b9ba63b4c518fa3 diff --git a/CHANGELOG.md b/CHANGELOG.md index e2ab2afb1f9d..03359db9b012 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ title: Changelog ## Table of Contents +- [3.19.0](#3190) - [3.18.0](#3180) - [3.17.0](#3170) - [3.16.0](#3160) @@ -86,6 +87,67 @@ title: Changelog - [0.7.0](#070) - [0.6.0](#060) +## 3.19.0 + +**The changes marked with :warning: are not backward compatible.** + +### Change + +- :warning: feat(upstream): verify the upstream certificate against configurable CAs. `upstream.tls.verify` was only read by the `kafka` scheme and is now honoured for `https` and `grpcs` as well, so an upstream that already carried `verify: true` starts rejecting a certificate it cannot validate; `tls.ca_certs` picks the trust anchors per upstream [#13863](https://github.com/apache/apisix/pull/13863) +- :warning: fix(openid-connect): validate the introspection issuer. With an explicit `claim_validator.issuer.valid_issuers`, a successful remote introspection response must now carry a string `iss` matching one of them, or the request is rejected with 401; omit the allowlist to keep the previous behavior [#13916](https://github.com/apache/apisix/pull/13916) +- :warning: fix(batch-requests): bound aggregated response bodies. New `max_response_body_size` (1 MiB) and `max_response_body_size_total` (10 MiB) plugin metadata; a pipeline above either limit now returns 502 instead of the full aggregate [#13906](https://github.com/apache/apisix/pull/13906) +- :warning: fix(basic-auth): reject an empty consumer password. `password` requires `minLength: 1`, so the Admin API rejects an empty value and a consumer already stored with one fails closed with 401 [#13884](https://github.com/apache/apisix/pull/13884) +- :warning: fix(ai-proxy-multi): reject instances that share a name. `instance.name` is the instance identity across the balancer, the health checker, `ai-rate-limiting` and `semantic_opts.fallback`, so a route whose instances share a name is now rejected on write and dropped on reload [#13851](https://github.com/apache/apisix/pull/13851) +- :warning: fix(workflow): reject invalid case expressions and missing action conf. A `case` expression that was silently accepted and then matched every request is now a schema error, and `actions` is pinned to exactly one `[name, conf]` pair, so a rule carrying several actions (only the first ever ran) or an action without its conf is rejected on write and dropped on reload [#13862](https://github.com/apache/apisix/pull/13862) +- :warning: feat(websocket): label successful WebSocket upgrades with `request_type=websocket`. On existing `enable_websocket` routes, successful 101 responses change from `traditional_http` to `websocket` in `apisix_http_status`, `apisix_http_latency` and `apisix_bandwidth`; update PromQL selectors, recording rules, dashboards and alerts that filter on the old value. The new `ws`/`wss` proxy path reports the same label [#13909](https://github.com/apache/apisix/pull/13909) + This also includes the cached-variable correction from [#13915](https://github.com/apache/apisix/pull/13915), so a plugin that resolves `$request_type` before the upgrade cannot leave the old value cached. +- :warning: fix(redis): send the TLS SNI and add `redis_server_name`. For the single-node `policy: redis` path with `redis_ssl_verify: true`, `redis_host` (or `redis_server_name`) is now used for hostname verification, so a DNS alias not covered by the certificate fails; set `redis_server_name` to the certificate identity or replace the certificate. Cluster and Sentinel policies are unchanged, and an IP literal sends no SNI [#13938](https://github.com/apache/apisix/pull/13938) +- :warning: fix(feishu-auth, dingtalk-auth): bind the authorization code to the session that started the login. Existing browser flows that only forward the code now return 401; the application serving `redirect_uri` must pass the generated `state` to the identity provider and preserve it on the callback. The header code path (`X-Feishu-Code` / `X-DingTalk-Code`) is unchanged [#13806](https://github.com/apache/apisix/pull/13806) +- :warning: fix(jwe-decrypt): accept JWE tokens that authenticate the protected header, as RFC 7516 requires, and reject an explicitly unsupported `alg` or `enc` with 400. Token generators that emit misleading values must use `alg: dir` and `enc: A256GCM`; legacy no-AAD tokens and headers that omit those fields remain accepted [#13889](https://github.com/apache/apisix/pull/13889) +- :warning: fix(control): always report healthcheck nodes as a JSON array. The JSON type of `nodes` and the top-level `/v1/healthcheck` response changes from `{}` to `[]` when empty, so strict clients must accept an empty array [#13891](https://github.com/apache/apisix/pull/13891) + +### Core + +- feat(stream): support TLS passthrough on the stream proxy, so a stream route can pick its upstream from the SNI in the prereaded ClientHello and still forward the session encrypted [#13912](https://github.com/apache/apisix/pull/13912) +- feat(stream): match a stream route by several SNIs through the new `snis` field, mutually exclusive with `sni` [#13911](https://github.com/apache/apisix/pull/13911) +- feat(websocket): add the `ws`/`wss` upstream scheme, which proxies frames through APISIX itself and exposes the `ws_handshake`, `ws_client_frame`, `ws_upstream_frame` and `ws_close` plugin phases plus the `core.websocket` API [#13939](https://github.com/apache/apisix/pull/13939) +- fix(websocket): address review findings on the `ws`/`wss` proxy path: dispatch on `ctx.upstream_scheme` so an inline upstream picked by `traffic-split` takes it, send the Host `proxy_pass` would send and use it as the SNI, answer the client with the subprotocol the upstream selected, retry after a non-101 answer, keep the request URI out of the connect failure log, and reject `tls.ca_certs`, which these schemes cannot apply [#13977](https://github.com/apache/apisix/pull/13977) +- feat(upstream): slow start for newly observed upstream nodes via `warm_up_conf` [#13941](https://github.com/apache/apisix/pull/13941) +- feat: improve API-driven standalone update reliability: workers report application of the configuration digest for each tracked resource type, `PUT /apisix/admin/configs` accepts a `wait` parameter and answers 200 once every worker has loaded the configuration (202 otherwise), and the shdict format carries the digest outside the JSON [#13904](https://github.com/apache/apisix/pull/13904) +- feat(control-api): report the health checks a plugin owns, so `ai-proxy-multi` instance checkers show up in `/v1/healthcheck` [#13899](https://github.com/apache/apisix/pull/13899) +- chore: upgrade lua-resty-dns-client to 7.1.2, fixing `finalCacheOnly` so a CNAME chain no longer fails with `empty record received` when the answer carries an EDNS(0) OPT record or is not in chain order [#13875](https://github.com/apache/apisix/pull/13875) +- fix(etcd): watch from the revision the configuration was read at, so a write made while APISIX is starting is no longer lost [#13917](https://github.com/apache/apisix/pull/13917) +- fix(etcd): check etcd availability before starting the watcher, so config objects do not wait on a watcher that never connected [#13934](https://github.com/apache/apisix/pull/13934) +- fix(etcd): do not block writes when the deployment role cannot be read [#13885](https://github.com/apache/apisix/pull/13885) +- fix(standalone): stop aborting stream connections before the first config arrives [#13855](https://github.com/apache/apisix/pull/13855) +- fix(standalone): harden the declarative configuration paths: validate the shape of the request body instead of 500ing, log the parser error rather than the body (which can carry credentials and private keys), check a stream route's `superior_id` self reference during validation, and guard null deployment sections in the CLI [#13886](https://github.com/apache/apisix/pull/13886) +- fix(plugin): align unavailable plugin handling: reject unknown plugin names before persistence, keep data-plane loading tolerant of them, and warn when one is skipped [#13928](https://github.com/apache/apisix/pull/13928) +- fix: preserve servlet upstream URI boundaries by encoding the original path before proxying when servlet-style normalization is enabled [#13914](https://github.com/apache/apisix/pull/13914) + +### Plugins + +- feat: add the `openapi-to-mcp` plugin, serving an HTTP API to MCP clients from its OpenAPI document over Streamable HTTP and HTTP+SSE [#13942](https://github.com/apache/apisix/pull/13942) +- feat(websocket): add the `websocket-proxy` plugin to customize proxy behaviors, starting with `client_max_payload_len` / `upstream_max_payload_len` for `ws`/`wss` upstreams [#13972](https://github.com/apache/apisix/pull/13972) +- feat(graphql-limit-count): rate limit by GraphQL query cost. New `complexity` and `node_quantifier` cost strategies with per-field weights stored as `graphql_cost_decorations` under a Service; `depth` stays the default [#13840](https://github.com/apache/apisix/pull/13840) +- feat: chaitin-waf response logging through `log_resp`, `resp_body_size` and `extra_ignored_content_types`, reported asynchronously after the response has been handed back to the client [#13763](https://github.com/apache/apisix/pull/13763) +- feat(ai-proxy-multi): let configured HTTP statuses trigger a fallback via `fallback_http_statuses` [#13852](https://github.com/apache/apisix/pull/13852) +- feat(saml-auth): add the lua-resty-saml 0.2.6 validation options `idp_issuers`, `sp_acs_url`, `sp_audiences`, `clock_skew`, `replay_dict` and `replay_ttl` [#13964](https://github.com/apache/apisix/pull/13964) +- fix(ai-proxy): return 502 when a streaming upstream produces no output, instead of falling through to `balancer_by_lua` and answering nothing [#13870](https://github.com/apache/apisix/pull/13870) +- fix(ai-proxy): do not turn a streaming read error after partial output into a 5xx, and do not retry a request whose partial output already reached the client [#13876](https://github.com/apache/apisix/pull/13876) +- fix(ai-proxy): avoid aborting streams on empty flushes [#13947](https://github.com/apache/apisix/pull/13947) +- fix(ai-providers): encode the Vertex AI model path segment [#13872](https://github.com/apache/apisix/pull/13872) +- fix(ai-cache): key the passthrough protocol on the client method, path and query, so two upstream endpoints no longer collide on one cache entry [#13887](https://github.com/apache/apisix/pull/13887) +- fix(ai-aliyun-content-moderation): report final results without usage [#13922](https://github.com/apache/apisix/pull/13922) +- fix(jwe-decrypt): reject malformed tokens with 400 instead of returning 500 [#13844](https://github.com/apache/apisix/pull/13844) +- fix(basic-auth): split credentials on the first colon only, so a password containing `:` is no longer truncated [#13836](https://github.com/apache/apisix/pull/13836) +- fix(data-mask): keep request header masking effective in the log phase, where `set_header()` silently did nothing on a 400 response [#13839](https://github.com/apache/apisix/pull/13839) +- fix(redirect): compare `X-Forwarded-Proto` case-insensitively, so a proxy forwarding `HTTPS` no longer triggers an `http_to_https` redirect loop [#13865](https://github.com/apache/apisix/pull/13865) +- fix(ua-restriction): deny the request when any User-Agent header matches the `deny_list` [#13869](https://github.com/apache/apisix/pull/13869) +- fix(traffic-label): cache the compiled match expressions outside the plugin config, so the route configuration stays JSON encodable [#13901](https://github.com/apache/apisix/pull/13901) +- fix(aws-lambda): request `/` for a path-less `function_uri` and log function error responses [#13908](https://github.com/apache/apisix/pull/13908) +- fix(ext-plugin-post-resp): set `upstream_addr` and `upstream_response_time` for loggers [#13940](https://github.com/apache/apisix/pull/13940) +- fix(openapi-to-mcp): apply schema defaults before validation, keep the resolved `base_url` and headers for the lifetime of an SSE session, and reject a document that is not an OpenAPI document [#13956](https://github.com/apache/apisix/pull/13956) + ## 3.18.0 **The changes marked with :warning: are not backward compatible.** diff --git a/apisix/core/version.lua b/apisix/core/version.lua index b02e89d44c29..2f6b772076a3 100644 --- a/apisix/core/version.lua +++ b/apisix/core/version.lua @@ -20,5 +20,5 @@ -- @module core.version return { - VERSION = "3.18.0" + VERSION = "3.19.0" } diff --git a/ci/check_changelog_prs.ts b/ci/check_changelog_prs.ts index b69b20d73d6c..b9fc4f6da610 100755 --- a/ci/check_changelog_prs.ts +++ b/ci/check_changelog_prs.ts @@ -69,6 +69,10 @@ const IGNORE_PRS = [ // "fix(ci)", "fix(dev-image)", "build:") dodges the docs/chore/test/ci type // filter but which do not belong in a user changelog. 13526, 13554, 13679, 13709, 13815, 13824, + // 3.19.0 + // CI-only changes whose "fix(ci)" subject prefix dodges the docs/chore/test/ci + // type filter but which do not belong in a user changelog. + 13921, 13923, ]; diff --git a/docs/en/latest/config.json b/docs/en/latest/config.json index 7397a7cf7a15..bf2424a500ac 100644 --- a/docs/en/latest/config.json +++ b/docs/en/latest/config.json @@ -1,5 +1,5 @@ { - "version": "3.18.0", + "version": "3.19.0", "sidebar": [ { "type": "category", diff --git a/docs/en/latest/plugins/batch-requests.md b/docs/en/latest/plugins/batch-requests.md index fd5be40dd55f..90bc1eb14f1c 100644 --- a/docs/en/latest/plugins/batch-requests.md +++ b/docs/en/latest/plugins/batch-requests.md @@ -69,7 +69,7 @@ plugins: ## Configuration -By default, the maximum body size that can be sent to `/apisix/batch-requests` can't be larger than 1 MiB. You can change this configuration of the Plugin through the endpoint `apisix/admin/plugin_metadata/batch-requests`: +By default, the maximum body size that can be sent to `/apisix/batch-requests` and the maximum response body size for each pipeline request are both 1 MiB. The maximum total response body size for a pipeline is 10 MiB. You can change these global Plugin metadata settings through the endpoint `/apisix/admin/plugin_metadata/batch-requests`: :::note You can fetch the `admin_key` from `config.yaml` and save to an environment variable with the following command: @@ -83,16 +83,22 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/ ```shell curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/batch-requests -H "X-API-KEY: $admin_key" -X PUT -d ' { - "max_body_size": 4194304 + "max_body_size": 4194304, + "max_response_body_size": 2097152, + "max_response_body_size_total": 20971520 }' ``` +These metadata settings are global and apply to every request handled by the `batch-requests` Plugin. + ## Metadata -| Name | Type | Required | Default | Valid values | Description | -| ------------------ | ------- | -------- | ------- | ------------ | -------------------------------------------------------- | -| max_body_size | integer | True | 1048576 | [1, ...] | Maximum size of the request body in bytes. | -| max_pipeline_items | integer | True | 1000 | [1, ...] | Maximum number of requests allowed in a single pipeline. | +| Name | Type | Required | Default | Valid values | Description | +| ---------------------------- | ------- | -------- | -------- | ------------ | ------------------------------------------------------------------ | +| max_body_size | integer | True | 1048576 | [1, ...] | Maximum size of the request body in bytes. | +| max_pipeline_items | integer | True | 1000 | [1, ...] | Maximum number of requests allowed in a single pipeline. | +| max_response_body_size | integer | False | 1048576 | [1, ...] | Maximum response body size in bytes for each pipeline request. | +| max_response_body_size_total | integer | False | 10485760 | [1, ...] | Maximum total response body size in bytes for a single pipeline. | ## Request and response format diff --git a/docs/zh/latest/config.json b/docs/zh/latest/config.json index baf9fb0b1b7a..36da7ac936a0 100644 --- a/docs/zh/latest/config.json +++ b/docs/zh/latest/config.json @@ -1,5 +1,5 @@ { - "version": "3.18.0", + "version": "3.19.0", "sidebar": [ { "type": "category", diff --git a/docs/zh/latest/plugins/batch-requests.md b/docs/zh/latest/plugins/batch-requests.md index d43e9abe3c1c..68eec33f6d88 100644 --- a/docs/zh/latest/plugins/batch-requests.md +++ b/docs/zh/latest/plugins/batch-requests.md @@ -69,7 +69,7 @@ plugins: ## 配置插件 -默认情况下,可以发送到 `/apisix/batch-requests` 的最大请求体不能大于 1 MiB。你可以通过 `apisix/admin/plugin_metadata/batch-requests` 更改插件的此配置: +默认情况下,可以发送到 `/apisix/batch-requests` 的最大请求体和每个 pipeline 请求的最大响应体均为 1 MiB,单个 pipeline 的响应体总大小上限为 10 MiB。你可以通过 `/apisix/admin/plugin_metadata/batch-requests` 更改这些全局插件元数据配置: :::note @@ -85,16 +85,22 @@ admin_key=$(yq '.deployment.admin.admin_key[0].key' conf/config.yaml | sed 's/"/ curl http://127.0.0.1:9180/apisix/admin/plugin_metadata/batch-requests \ -H "X-API-KEY: $admin_key" -X PUT -d ' { - "max_body_size": 4194304 + "max_body_size": 4194304, + "max_response_body_size": 2097152, + "max_response_body_size_total": 20971520 }' ``` +这些元数据配置在全局范围内生效,并应用于 `batch-requests` 插件处理的所有请求。 + ## 元数据 -| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | -| ------------------ | ------- | -------| ------- | ------ | ---------------------------- | -| max_body_size | integer | 是 | 1048576 |[1, ...]| 请求体的最大大小,单位:bytes。 | -| max_pipeline_items | integer | 是 | 1000 |[1, ...]| 单个 pipeline 中允许的最大请求数量。 | +| 名称 | 类型 | 必选项 | 默认值 | 有效值 | 描述 | +| ---------------------------- | ------- | ------ | -------- | -------- | ------------------------------------------------ | +| max_body_size | integer | 是 | 1048576 | [1, ...] | 请求体的最大大小,单位:bytes。 | +| max_pipeline_items | integer | 是 | 1000 | [1, ...] | 单个 pipeline 中允许的最大请求数量。 | +| max_response_body_size | integer | 否 | 1048576 | [1, ...] | 每个 pipeline 请求的最大响应体大小,单位:bytes。 | +| max_response_body_size_total | integer | 否 | 10485760 | [1, ...] | 单个 pipeline 的最大响应体总大小,单位:bytes。 | ## 请求和响应格式