Opened 3 months ago

Closed 3 months ago

#2584 closed defect (duplicate)

Deny IP doesn't work when behind multiple proxies.

Reported by: andrew-pickin-epi@… Owned by:
Priority: minor Milestone:
Component: nginx-module Version: 1.25.x
Keywords: Cc:
uname -a: kubernetes 1.27
nginx -V: registry.k8s.io/ingress-nginx/controller:v1.9.3@sha256:8fd21d59428507671ce0fb47f818b1d859c92d2ad07bb7c947268d433030ba98

Description

Nginx controller is accessed via external 3rd party proxy (then AWS ELB) for most locations paths.
For monitoring tools Nginx controller ignores the 3rd party proxy and goes directly to ELB.
Config (real-ip module enabled).

real_ip_header proxy_protocol;


real_ip_recursive on;


set_real_ip_from 0.0.0.0/0;

deny <ip_addr>/32;

The deny works for the monitoring paths but not others.

Change History (4)

comment:1 by andrew-pickin-epi@…, 3 months ago

Component is incorrect

comment:2 by Maxim Dounin, 3 months ago

Component: documentationnginx-module
Resolution: duplicate
Status: newclosed

The real_ip_header proxy_protocol; implies that your are using PROXY protocol to provide original IP address information to nginx.

Note that in PROXY protocol only one address is provided, therefore real_ip_recursive on; is a noop.

Depending on your setup, you should either ensure that correct address is provided via PROXY protocol (if it's possible to configure AWS ELB this way), or use other address sources, such as X-Forwarded-For, and make sure only trusted proxies which properly update the header are allowed to connect, or combine information from multiple sources, such as PROXY protocol and X-Forwarded-For (which is not currently directly possible, but can be implemented with additional proxying, see #2350).

Also, just in case, please note well that set_real_ip_from 0.0.0.0/0; is insecure and shouldn't be used for anything but testing.

Closing this as a duplicate of #2350.

comment:3 by andrew-pickin-epi@…, 3 months ago

Resolution: duplicate
Status: closedreopened

The setup:
Requests can come in from anywhere. The original IP might be anything.
Requests to domain A go though a 3rd party's proxy.
Depending on the path these are forwarded to various supplier domains. Let's call my domain B.

                       < Domain A >   <--------------- Domain B ------------------->

Business requests        +-------+    +---------+    +--------+    +--------------+
come from anywhere ----->| Proxy |--->| AWS ELB |--->| Ngninx |--->| Applications |+
on the internet          +-------+    +---------+    +--------+    +--------------+|
                                                                    +--------------+   

Business requests go via domain A proxy.
These arrive at Nginx with the X-Forwarded-For listing each step so:
<origin ip addr>, <proxy ip addr>, <ELB ip addr>

I think I can show that the correct original IP is being provided by the AWS ELB as demonstrated in that I can deny access via IP when the request arrives directly at the AWS ELB.
Eg:

    Monitoring Request      +---------+    +--------+      +----------------+
----  to Domain B     ----->| AWS ELB |--->| Ngninx |--//  | Monitoring App |
     from blocked IP        +---------+    +--------+      +----------------+

Note: I don't actually want to block here, I've plenty of security on the monitoring tools, it's just a test/demonstration that the ELB is behaving correctly.

However this same configuration doesn't block a Business request via domain A's proxy.

When using the ingress-nginx controller behind AWS ELB the use of use-proxy-protocol is required. It simple doesn't work without.

However this forces real_ip_header proxy_protocol;
See the template code.

Specifically:

    {{/* Enable the real_ip module only if we use either X-Forwarded headers or Proxy Protocol. */}}
    {{/* we use the value of the real IP for the geo_ip module */}}
    {{ if or (or $cfg.UseForwardedHeaders $cfg.UseProxyProtocol) $cfg.EnableRealIP }}
    {{ if $cfg.UseProxyProtocol }}
    real_ip_header      proxy_protocol;
    {{ else }}
    real_ip_header      {{ $cfg.ForwardedForHeader }};
    {{ end }}

So the two suggestions:

  1. Ensuring the correct PROXY protocol is provided via AWS ELB.

I think this has been demonstrated in the denying of Monitor requests.
But does this demonstrate that it isn't provided via the proxy in Domain A?

  1. Use other address sources, such as X-Forwarded-For

I take it this means:

real_ip_header X-Forwarded-For;

instead of

real_ip_header proxy_protocol;

which is impossible when using use-proxy-protocol
and presumably I should create a ticket with the helm chart maintainers?

Last edited 3 months ago by andrew-pickin-epi@… (previous) (diff)

comment:4 by Maxim Dounin, 3 months ago

Resolution: duplicate
Status: reopenedclosed

I think I can show that the correct original IP is being provided by the AWS ELB as demonstrated in that I can deny access via IP when the request arrives directly at the AWS ELB.
...
However this same configuration doesn't block a Business request via domain A's proxy.

This actually demonstrates that the IP address as provided by AWS ELB via PROXY protocol is the address used in connection to AWS ELB, and not the correct original IP in your configuration with the proxy. That is, the address as provided by AWS ELB is the address of the proxy, and not the correct original IP.

And that's exactly the reason why deny ... does not block requests which come through the proxy: in your configuration with real_ip_header proxy_protocol; and AWS ELB reporting the IP address of the client via PROXY protocol, such requests are seen by nginx as requests from the proxy IP address.

As suggested, you have to reconfigure nginx if you need this to work. The most universal approach would be to combine the PROXY protocol information as provided by AWS ELB and the X-Forwarded-For header as sent by the proxy, see ticket #2350 on suggestions on how to do it with additional proxying within nginx.

If you have issues with configuring nginx via 3rd party configuration tools, such as with ingress controller, unfortunately we can't help. Consider contacting corresponding tools maintainers for help.

Note: See TracTickets for help on using tickets.