fix: only trust X-Forwarded-* headers from trusted_addresses#12551
Conversation
|
|
||
| local proto = api_ctx.var.http_x_forwarded_proto | ||
| if proto then | ||
| api_ctx.var.var_x_forwarded_proto = proto | ||
| end | ||
|
|
||
| local x_forwarded_host = api_ctx.var.http_x_forwarded_host | ||
| if x_forwarded_host then | ||
| api_ctx.var.var_x_forwarded_host = x_forwarded_host | ||
| end | ||
|
|
||
| local port = api_ctx.var.http_x_forwarded_port | ||
| if port then | ||
| api_ctx.var.var_x_forwarded_port = port | ||
| end |
There was a problem hiding this comment.
Lines 277 to 282 in 09f6e36
Because the code above will replace handle_upstream with ai_upstream below:
Lines 115 to 117 in 09f6e36
This will cause this deleted part of the code to be skipped.
Now that we need to handle X-Forwarded-*, we need to update these var_x_forwarded_*, so we need to extract this part of the code as update_var_x_forwarded_headers.
Lines 835 to 838 in 3260931
trusted_addresses to handle X-Forwarded-* headersX-Forwarded-* headers from trusted_addresses
X-Forwarded-* headers from trusted_addressesX-Forwarded-* headers from trusted_addresses
There was a problem hiding this comment.
Pull Request Overview
This PR implements security hardening by restricting APISIX to only trust X-Forwarded-* headers from configured trusted IP addresses/CIDRs. Previously, APISIX accepted these headers from any source, creating security vulnerabilities where malicious clients could bypass features like HTTP-to-HTTPS redirection.
- Adds
trusted_addressesconfiguration to specify which IPs/CIDRs can send trustedX-Forwarded-*headers - Creates utility module to validate and match trusted addresses using IP/CIDR notation
- Modifies request processing to override untrusted
X-Forwarded-*headers with server-generated values
Reviewed Changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| apisix/utils/trusted-addresses.lua | New utility module for validating and matching trusted IP addresses/CIDRs |
| apisix/init.lua | Core logic to handle X-Forwarded headers based on client trust status |
| conf/config.yaml.example | Configuration example for trusted_addresses setting |
| t/core/trusted-addresses.t | Comprehensive tests for trusted address functionality |
| docs/en/latest/plugins/*.md | Documentation updates explaining trusted address requirements |
| docs/zh/latest/plugins/*.md | Chinese documentation updates for trusted address requirements |
| t/APISIX.pm | Default test configuration with localhost as trusted address |
| t/plugin/*.t | Test configuration updates to include trusted addresses |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| node_listen: 1984 | ||
| enable_admin: false | ||
| trusted_addresses: | ||
| - "127.0.0.1" |
There was a problem hiding this comment.
Added in batches, removed
| my $yaml_config = $block->yaml_config // <<_EOC_; | ||
| apisix: | ||
| node_listen: 1984 | ||
| trusted_addresses: |
There was a problem hiding this comment.
| enable_encrypt_fields: true | ||
| keyring: | ||
| - edd1c9f0985e76a2 | ||
| trusted_addresses: |
There was a problem hiding this comment.
Added in batches, removed
| @@ -0,0 +1,219 @@ | |||
| # | |||
There was a problem hiding this comment.
Need to test the situation where trusted-addresses contain multiple IP addresses
There was a problem hiding this comment.
and need test cases in plugins.
There was a problem hiding this comment.
Other than chaitin-waf, the original PR didn't find any tests covering the real_client_ip=true scenario, and after spending time researching, I also couldn't find the actual purpose of this parameter.
| # When set to true, it overrides all upstream healthcheck configurations and globally disabling healthchecks. | ||
| # trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-*` Headers | ||
| # - 127.0.0.1 # passed in requests from the IP/CIDR in the list. | ||
| # - 0.0.0.0/0 # CAUTION: When not configured, APISIX will remove `X-Forwarded-*` headers |
There was a problem hiding this comment.
No IP restriction is a bad example
| # or (standalone mode) the config isn't loaded yet either via file or Admin API. | ||
| # disable_upstream_healthcheck: false # A global switch for healthcheck. Defaults to false. | ||
| # When set to true, it overrides all upstream healthcheck configurations and globally disabling healthchecks. | ||
| # trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-*` Headers |
There was a problem hiding this comment.
| # trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-*` Headers | |
| # trusted_addresses: # When enabled, APISIX will trust the `X-Forwarded-*` Headers |
how to disabled?
There was a problem hiding this comment.
If not configured, it is disabled by default.
This statement references
apisix/conf/config.yaml.example
Line 138 in 1e3950e
| --- request | ||
| GET /old_uri | ||
| --- error_log | ||
| invalid IP/CIDR '1.0.0' exists in trusted_addresses |
There was a problem hiding this comment.
we also need to check the value of trusted_addresses when apisix starts, not only at runtime.
| type = "array", | ||
| minItems = 1, | ||
| items = { | ||
| type = "string", |
There was a problem hiding this comment.
"oneOf": [
{"format": "ipv4"},
{"format": "ipv6"}
]
There was a problem hiding this comment.
There are a few problems with doing this:
- Need CIDR support
- As discussed in fix: only trust
X-Forwarded-*headers fromtrusted_addresses#12551 (comment), try to use the existing schema_def. A feasible solution is to copy the corresponding schema again.
There was a problem hiding this comment.
Currently, I will try to copy a schema from schema_def to implement this function.
| api_ctx.var.http_x_forwarded_port = port | ||
| api_ctx.var.http_x_forwarded_for = nil | ||
|
|
||
| -- override the x-forwarded-* headers to the trusted ones |
There was a problem hiding this comment.
In the if not trusted logic of this code, where the trusted ones come from?
There was a problem hiding this comment.
Lines 830 to 837 in 33b9632
Referenced the implementation of APISIX.
| local proto = api_ctx.var.http_x_forwarded_proto | ||
| if proto then | ||
| api_ctx.var.var_x_forwarded_proto = proto | ||
| end |
There was a problem hiding this comment.
why not set X-Forwarded-Proto as http_x_forwarded_proto?
There was a problem hiding this comment.
I don't quite understand.
- This function is extracted from the following code without any adjustments.
X-Forwarded-Protoin the header ==http_x_forwarded_proto
| local _M = {} | ||
|
|
||
|
|
||
| local function validate_trusted_addresses(trusted_addresses) |
There was a problem hiding this comment.
we also need to check this when apisix starts.
| end | ||
|
|
||
|
|
||
| local function handle_x_forwarded_headers(api_ctx) |
There was a problem hiding this comment.
Please add some comments to explain the purpose of each code segment
| required = {"prefix", "host"} | ||
| } | ||
|
|
||
| -- copy from apisix/schema_def.lua |
There was a problem hiding this comment.
please do not copy. we should find the root reason
There was a problem hiding this comment.
My suggestion is to extract this schema separately.
There was a problem hiding this comment.
This part has been handled by @nic-6443 . Thanks to nic for the assistance.
Signed-off-by: Nic <[email protected]>
Signed-off-by: Nic <[email protected]>
…to young/feat/trusted_addresses
Description
In the past, APISIX trusted the
X-Forwarded-*headers from all IPs, which caused many problems. For example:When using the
redirectplugin, even ifhttp_to_httpsis enabled, as long as the user tries to requestcurl http://route -H "X-Forwarded-Proto: https"(or uses any value other thanhttp. Allow any value will be fixed in #12561), they can bypass the redirection logic and downgrade to http.apisix/apisix/plugins/redirect.lua
Lines 192 to 195 in 167b655
This PR implements that
X-Forwarded-*headers are only trusted if they are sent by IPs or CIDRs configured inapisix.trusted_addresses. Therefore, all plugins or features that useX-Forwarded-*headers require configuration ofapisix.trusted_addressesto take effect.apisix.trusted_addressessupports matching both IPs and CIDRs.Solve part of #12500
Which issue(s) this PR fixes:
Fixes #
Checklist