ModSecurity-nginx icon indicating copy to clipboard operation
ModSecurity-nginx copied to clipboard

Add a Directive to get a Proxied Client's IP Address (Closes #341)

Open thekief opened this issue 10 months ago • 12 comments

As written in #341, this patch adds functionality to obtain a client's IP address, if the request has been proxied using the Proxy Protocol.

Besides the check, if the option is enabled, I added a check if the Proxy Protocol is actually being used.

thekief avatar Feb 13 '25 11:02 thekief

Please let me know, if the whitespace changes in the readme should be removed. I made the changes using VS Codium and didn't know that it forces a newline encoding.

thekief avatar Feb 13 '25 12:02 thekief

Please let me know, if the whitespace changes in the readme should be removed.

No worries, that's in a good place.

Anyway, thanks for the PR - I'm thinking about we should add new test cases for each new PR (which adds a new feature - like this). I mean please take a look at the regression test file of this repository, how could we make a config where we can see that the modified code does what the author wants. And this is important, because if someone in the future sends a new PR, we must be sure this behavior does not change. Do you have any idea?

airween avatar Feb 17 '25 15:02 airween

Yeah, I have an idea for a test case. curl not only supports the Proxy Protocol ^1, but also the option to set the client's IP address ^2.

If that's alright with you, I would add 2 new endpoints to the test configuration:

  • one that returns the client's ip with the new directive enabled
  • another one hat returns the client's ip with the new directive disabled

What do you think of that?

thekief avatar Feb 19 '25 09:02 thekief

Hi @thekief,

I tried to check your patch, but may be I'm doing something wrong, I can't see the client's IP in my log.

Here is my config:

server {
    listen 8088;
    server_name _;

    root /var/www/html;
    index index.html;

    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsecurity_includes.conf;
    modsecurity_proxy_protocol_ip on;

    error_log /var/log/nginx/backend_error.log info;
    access_log /var/log/nginx/backend_acces.log;

    location / {
        try_files $uri $uri/ =404;
    }
}

server {
    listen          8080;
    server_name     proxytest;

    modsecurity off;
    location / {
        proxy_read_timeout 300;
        proxy_connect_timeout 300;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:8088;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_buffering off;
        proxy_redirect http:// https://;
    }
}

As you can see I created a simple vhost for static files, there I turned on modsecurity, load rules and turned on your new feature. I also set a separated logs (both access and error).

Below that I created a proxy which sends all requests to that site.

Here is how I checked that:

curl --interface 172.35.40.12 -H "Host: proxytest" "http://localhost:8080/?q=/bin/bash"

where 172.35.40.12 is the IP of my physical interface, but the request sent to localhost (with that IP) with HTTP header Host: proxytest (see above the proxy config).

With this request I get this line in my error.log:

2025/02/19 21:04:29 [info] 255143#255143: *3 ModSecurity: Warning. Matched "Operator `ValidateByteRange' with parameter `38,44-46,48-58,61,65-90,95,97-122' against variable `ARGS:q' (Value: `/bin/bash' ) [file "/home/airween/src/coreruleset/rules/REQUEST-920-PROTOCOL-ENFORCEMENT.conf"] [line "1723"] [id "920273"] [rev ""] [msg "Invalid character in request (outside of very strict set)"] [data "ARGS:q=/bin/bash"] [severity "2"] [ver "OWASP_CRS/4.12.0-dev"] [maturity "0"] [accuracy "0"] [tag "application-multi"] [tag "language-multi"] [tag "platform-multi"] [tag "attack-protocol"] [tag "paranoia-level/4"] [tag "OWASP_CRS"] [tag "capec/1000/210/272"] [hostname "127.0.0.1"] [uri "/"] [unique_id "173999546951.849862"] [ref "o0,1o4,1v8,9t:urlDecodeUni"], client: 127.0.0.1, server: _, request: "GET /?q=/bin/bash HTTP/1.1", host: "proxytest"

Where should I see my client IP instead of 127.0.0.1 (which is the proxy's IP)?

Or may be I misunderstand something.

airween avatar Feb 19 '25 20:02 airween

To clear up a small misunderstanding, the "Proxy Protocol" I am talking about, is specified in the listen-directive ^1.

I will add an example configuration tomorrow.

thekief avatar Feb 19 '25 20:02 thekief

To clear up a small misunderstanding, the "Proxy Protocol" I am talking about, is specified in the listen-directive 1.

I will add an example configuration tomorrow.

Thanks - then I can help you to create test cases. And of course, examples are always helpful.

airween avatar Feb 19 '25 20:02 airween

Just a quick update that I couldn't get to it yet, as some other things were more pressing. I should have something working by Monday 😅

thekief avatar Feb 21 '25 20:02 thekief

@thekief any update on this?

airween avatar May 05 '25 11:05 airween

Hi, I'm really sorry for my late reply, I had to deal with other things -.-

Thanks - then I can help you to create test cases. And of course, examples are always helpful. As for the test, I would propose the following:

curl has the following two options in regard to the Proxy Protocol:

--haproxy-clientip <ip>                       Set address in HAProxy PROXY
--haproxy-protocol                            Send HAProxy PROXY protocol v1 header

So, for example we could use curl --haproxy-protocol --haproxy-clientip 123.123.123.123 localhost:8443 as the test command.

If modsecurity_proxy_protocol_ip on; is set and a rule is triggered, 123.123.123.123 should be visible in the error log. If modsecurity_proxy_protocol_ip off; is set 127.0.0.1 or localhost should be visible in the log files.

thekief avatar May 22 '25 12:05 thekief

Regarding configuration, I would have done the following:

nginx.conf:

pid /tmp/nginx.pid;

error_log  /dev/stdout  info;

events {
    worker_connections   2000;
}

user www-data;

http {
    server {
        listen 8080 proxy_protocol;

        root /var/www/html;
        index index.html;

        modsecurity on;
        modsecurity_rules_file /etc/nginx/modsecurity_includes.conf;
        modsecurity_proxy_protocol_ip off;

        location / {
            return 200;
        }
    }

    server {
        listen 8081 proxy_protocol;

        root /var/www/html;
        index index.html;

        modsecurity on;
        modsecurity_rules_file /etc/nginx/modsecurity_includes.conf;
        modsecurity_proxy_protocol_ip on;

        location / {
            return 200;
        }
    }
}

REQUEST-900-EXCLUSION-RULES-BEFORE-CRS.conf:

SecRuleRemoveByTag  OWASP_CRS

SecRule REQUEST_HEADERS "@contains worm" \
        "id:1012 \
        ,phase:1 \
        ,deny \
        ,t:none \
        ,status:500 \
        ,log \
        "

Resulting in the following output when executing curl --haproxy-clientip 123.123.123.123 --haproxy-protocol -v -k -H "Animal: worm" localhost:8080:

{"transaction":{"client_ip":"172.21.0.1","time_stamp":"Wed Jun 11 13:32:17 2025","server_id":"575f816b3c1769ef25a49fc7dcabe0a096d605ee","client_port":37068,"host_ip":"172.21.0.2","host_port":8080,"unique_id":"17496487374.770509","request":{"method":"GET","http_version":1.1,"uri":"/","headers":{"Host":"localhost:8080","User-Agent":"curl/8.14.1","Accept":"*/*","Animal":"worm"}}...

If curl --haproxy-clientip 123.123.123.123 --haproxy-protocol -v -k -H "Animal: worm" localhost:8081 is executed, the following output is in the logs:

{"transaction":{"client_ip":"123.123.123.123","time_stamp":"Wed Jun 11 13:32:12 2025","server_id":"575f816b3c1769ef25a49fc7dcabe0a096d605ee","client_port":42630,"host_ip":"172.21.0.2","host_port":8081,"unique_id":"174964873292.593363","request":{"method":"GET","http_version":1.1,"uri":"/","headers":{"Host":"localhost:8081","User-Agent":"curl/8.14.1","Accept":"*/*","Animal":"worm"}}...

thekief avatar Jun 11 '25 13:06 thekief