From aab690740e9e793c4d80aa24796c249f49527a11 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Tue, 20 Oct 2026 11:13:54 +0000 Subject: [PATCH 1/3] feat(aisix): declare upstream.media_fetch in values.yaml --- charts/aisix/README.md | 3 +++ charts/aisix/values.yaml | 12 ++++++++++++ 2 files changed, 15 insertions(+) diff --git a/charts/aisix/README.md b/charts/aisix/README.md index 8b84cc0..c07fc30 100644 --- a/charts/aisix/README.md +++ b/charts/aisix/README.md @@ -577,6 +577,9 @@ rejects. New installs set the value in its own key in `values.yaml` or under | config.ratelimit.redis | object | `nil` | Extra settings for the shared rate-limit Redis selected by `rateLimit.backend: redis`, e.g. `{timeout_secs: 2}` or `{mode: cluster, nodes: [...]}`. Same keys as `cache.redis`; the URL comes from `rateLimit.redis`, `username` and `password` go through `configSecrets` | | config.shutdown.min_drain_secs | int | `30` | Seconds the gateway keeps accepting after SIGTERM while `/readyz` reports 503. `terminationGracePeriodSeconds` must cover it | | config.upstream.connect_timeout_ms | int | `5000` | Connect timeout in milliseconds | +| config.upstream.media_fetch.allowed_hosts | list | `[]` | Hosts the download may reach although they resolve to a non-public address, as `host` or `host:port` (IPv6 with a port as `[addr]:port`). Every other non-public address is refused | +| config.upstream.media_fetch.enabled | bool | `true` | Download a chat request's media URL when the target upstream cannot read it (Bedrock, Claude on Vertex AI, http:// toward Anthropic or Gemini) and send the bytes inline. false refuses such parts with a 400 | +| config.upstream.media_fetch.max_bytes | int | `52428800` | Largest download accepted per media URL, in bytes | | config.upstream.pool_idle_timeout_secs | int | `30` | Seconds an idle pooled connection is kept. Lower it when a load balancer or NAT between the gateway and the provider closes idle connections sooner | | config.upstream.pool_max_idle_per_host | int | `nil` | Idle pooled connections kept per host. Null means unbounded | | config.upstream.retries | int | `2` | Retry budget every dispatch starts from | diff --git a/charts/aisix/values.yaml b/charts/aisix/values.yaml index 72f21de..e482e13 100644 --- a/charts/aisix/values.yaml +++ b/charts/aisix/values.yaml @@ -343,6 +343,18 @@ config: pool_max_idle_per_host: null # -- Retry budget every dispatch starts from retries: 2 + media_fetch: + # -- Download a chat request's media URL when the target upstream + # cannot read it (Bedrock, Claude on Vertex AI, http:// toward + # Anthropic or Gemini) and send the bytes inline. false refuses such + # parts with a 400 + enabled: true + # -- Hosts the download may reach although they resolve to a + # non-public address, as `host` or `host:port` (IPv6 with a port as + # `[addr]:port`). Every other non-public address is refused + allowed_hosts: [] + # -- Largest download accepted per media URL, in bytes + max_bytes: 52428800 tls: # -- Extra CA bundle, added to the platform trust store, for every # upstream TLS handshake. Mount the file through `extraVolumes` From 196f423903610dc44345415e91501c4c04079251 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Tue, 20 Oct 2026 16:07:17 +0000 Subject: [PATCH 2/3] feat(aisix): declare upstream.bedrock.mantle_endpoint Pairs with api7/aisix#1318: the Bedrock Mantle endpoint count_tokens falls back to for Claude models bedrock-runtime cannot count. Null derives https://bedrock-mantle.{region}.api.aws from the provider key's region. --- charts/aisix/README.md | 1 + charts/aisix/values.yaml | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/charts/aisix/README.md b/charts/aisix/README.md index c07fc30..8423719 100644 --- a/charts/aisix/README.md +++ b/charts/aisix/README.md @@ -576,6 +576,7 @@ rejects. New installs set the value in its own key in `values.yaml` or under | config.ratelimit.concurrency_ttl_secs | int | `300` | Seconds a concurrency-limit slot is held at most | | config.ratelimit.redis | object | `nil` | Extra settings for the shared rate-limit Redis selected by `rateLimit.backend: redis`, e.g. `{timeout_secs: 2}` or `{mode: cluster, nodes: [...]}`. Same keys as `cache.redis`; the URL comes from `rateLimit.redis`, `username` and `password` go through `configSecrets` | | config.shutdown.min_drain_secs | int | `30` | Seconds the gateway keeps accepting after SIGTERM while `/readyz` reports 503. `terminationGracePeriodSeconds` must cover it | +| config.upstream.bedrock.mantle_endpoint | string | `nil` | Bedrock Mantle base URL where `/v1/messages/count_tokens` for a Claude model is counted when bedrock-runtime's CountTokens refuses that model. Null derives the public regional host `https://bedrock-mantle.{region}.api.aws` from the provider key's region; set it for a VPC endpoint or GovCloud | | config.upstream.connect_timeout_ms | int | `5000` | Connect timeout in milliseconds | | config.upstream.media_fetch.allowed_hosts | list | `[]` | Hosts the download may reach although they resolve to a non-public address, as `host` or `host:port` (IPv6 with a port as `[addr]:port`). Every other non-public address is refused | | config.upstream.media_fetch.enabled | bool | `true` | Download a chat request's media URL when the target upstream cannot read it (Bedrock, Claude on Vertex AI, http:// toward Anthropic or Gemini) and send the bytes inline. false refuses such parts with a 400 | diff --git a/charts/aisix/values.yaml b/charts/aisix/values.yaml index e482e13..2656bb1 100644 --- a/charts/aisix/values.yaml +++ b/charts/aisix/values.yaml @@ -355,6 +355,13 @@ config: allowed_hosts: [] # -- Largest download accepted per media URL, in bytes max_bytes: 52428800 + bedrock: + # -- (string) Bedrock Mantle base URL where `/v1/messages/count_tokens` + # for a Claude model is counted when bedrock-runtime's CountTokens + # refuses that model. Null derives the public regional host + # `https://bedrock-mantle.{region}.api.aws` from the provider key's + # region; set it for a VPC endpoint or GovCloud + mantle_endpoint: null tls: # -- Extra CA bundle, added to the platform trust store, for every # upstream TLS handshake. Mount the file through `extraVolumes` From 3fe20a7c0cb01972f8e38bd0341b1a4c8e49e2d7 Mon Sep 17 00:00:00 2001 From: Jarvis Date: Wed, 21 Oct 2026 03:00:45 +0000 Subject: [PATCH 3/3] feat(aisix): declare outbound.ssrf_protection, drop media_fetch.allowed_hosts The gateway moved the media download's allow-list to a product-wide outbound.ssrf_protection block (enabled, allowed_hosts) in api7/aisix#1327. --- charts/aisix/README.md | 3 ++- charts/aisix/values.yaml | 15 +++++++++++---- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/charts/aisix/README.md b/charts/aisix/README.md index 8423719..ebbcfa6 100644 --- a/charts/aisix/README.md +++ b/charts/aisix/README.md @@ -565,6 +565,8 @@ rejects. New installs set the value in its own key in `values.yaml` or under | config.observability.metrics.prometheus.path | string | `"/metrics"` | Metrics path | | config.observability.service_name | string | `"aisix"` | Service name reported in telemetry | | config.observability.usage_event.request_headers | list | `[]` | Request headers whose values every usage event records (lowercase names; credential headers and duplicates are refused at startup) | +| config.outbound.ssrf_protection.allowed_hosts | list | `[]` | Hosts such a fetch may reach although they resolve to a non-public address, as `host` or `host:port` (IPv6 with a port as `[addr]:port`). Behind a fake-ip DNS that maps public names into 198.18.0.0/15, list those hosts here | +| config.outbound.ssrf_protection.enabled | bool | `true` | Only reach publicly routable addresses when fetching a URL an API caller supplied (today the media download). false turns the check off and ignores `allowed_hosts` | | config.proxy.real_ip.header | string | `"x-forwarded-for"` | Header carrying the client IP | | config.proxy.real_ip.recursive | bool | `false` | Walk the header right to left past every trusted proxy | | config.proxy.real_ip.trusted_proxies | list | `[]` | CIDRs (or bare IPs) of proxies trusted to set the client-IP header | @@ -578,7 +580,6 @@ rejects. New installs set the value in its own key in `values.yaml` or under | config.shutdown.min_drain_secs | int | `30` | Seconds the gateway keeps accepting after SIGTERM while `/readyz` reports 503. `terminationGracePeriodSeconds` must cover it | | config.upstream.bedrock.mantle_endpoint | string | `nil` | Bedrock Mantle base URL where `/v1/messages/count_tokens` for a Claude model is counted when bedrock-runtime's CountTokens refuses that model. Null derives the public regional host `https://bedrock-mantle.{region}.api.aws` from the provider key's region; set it for a VPC endpoint or GovCloud | | config.upstream.connect_timeout_ms | int | `5000` | Connect timeout in milliseconds | -| config.upstream.media_fetch.allowed_hosts | list | `[]` | Hosts the download may reach although they resolve to a non-public address, as `host` or `host:port` (IPv6 with a port as `[addr]:port`). Every other non-public address is refused | | config.upstream.media_fetch.enabled | bool | `true` | Download a chat request's media URL when the target upstream cannot read it (Bedrock, Claude on Vertex AI, http:// toward Anthropic or Gemini) and send the bytes inline. false refuses such parts with a 400 | | config.upstream.media_fetch.max_bytes | int | `52428800` | Largest download accepted per media URL, in bytes | | config.upstream.pool_idle_timeout_secs | int | `30` | Seconds an idle pooled connection is kept. Lower it when a load balancer or NAT between the gateway and the provider closes idle connections sooner | diff --git a/charts/aisix/values.yaml b/charts/aisix/values.yaml index 2656bb1..78ceeac 100644 --- a/charts/aisix/values.yaml +++ b/charts/aisix/values.yaml @@ -349,10 +349,6 @@ config: # Anthropic or Gemini) and send the bytes inline. false refuses such # parts with a 400 enabled: true - # -- Hosts the download may reach although they resolve to a - # non-public address, as `host` or `host:port` (IPv6 with a port as - # `[addr]:port`). Every other non-public address is refused - allowed_hosts: [] # -- Largest download accepted per media URL, in bytes max_bytes: 52428800 bedrock: @@ -373,6 +369,17 @@ config: # -- Verify upstream certificates. false accepts any certificate — test # use only verify: true + outbound: + ssrf_protection: + # -- Only reach publicly routable addresses when fetching a URL an + # API caller supplied (today the media download). false turns the + # check off and ignores `allowed_hosts` + enabled: true + # -- Hosts such a fetch may reach although they resolve to a + # non-public address, as `host` or `host:port` (IPv6 with a port as + # `[addr]:port`). Behind a fake-ip DNS that maps public names into + # 198.18.0.0/15, list those hosts here + allowed_hosts: [] downstream: # -- Seconds an idle client connection is held. 0 never closes it idle_timeout_secs: 0