[GH-ISSUE #3] Solution to detect the real ip while blocking non cloudflare #3

Open
opened 2026-02-25 22:30:23 +03:00 by kerem · 8 comments
Owner

Originally created by @aseques on GitHub (May 27, 2019).
Original GitHub issue: https://github.com/ergin/nginx-cloudflare-real-ip/issues/3

I'll post here the final approach I took to get at the same time

  • Block non cloudflare IPs (per server)
  • Identify incoming ips
  • No need for a firewall
    Add to nginx.conf (http scope):
geo $realip_remote_addr $cloudflare_ip {
        default          0;
        103.21.244.0/22  1;
        103.22.200.0/22  1;
        103.31.4.0/22    1;
        104.16.0.0/12    1;
        108.162.192.0/18 1;
        131.0.72.0/22    1;
        141.101.64.0/18  1;
        162.158.0.0/15   1;
        172.64.0.0/13    1;
        173.245.48.0/20  1;
        188.114.96.0/20  1;
        190.93.240.0/20  1;
        197.234.240.0/22 1;
        198.41.128.0/17  1;
        199.27.128.0/21  1;
    }

In the server block:

location /route/ {
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_set_header X-NginX-Proxy true;
        proxy_pass http://127.0.0.1:8051/;
        proxy_redirect off;
        if ($cloudflare_ip != 1) {
            return 403;
        }
        include /etc/nginx/snippets/allow_cloudflare.conf;
        deny all;
    }

Being allow_cloudflare.conf the following:

allow 103.21.244.0/22;
allow 103.22.200.0/22;
allow 103.31.4.0/22;
allow 104.16.0.0/12;
allow 108.162.192.0/18;
allow 131.0.72.0/22;
allow 141.101.64.0/18;
allow 162.158.0.0/15;
allow 172.64.0.0/13;
allow 173.245.48.0/20;
allow 188.114.96.0/20;
allow 190.93.240.0/20;
allow 197.234.240.0/22;
allow 198.41.128.0/17;

real_ip_header CF-Connecting-IP;

This works like a charm, if you still want extra security you can configure the firewall to block the other ips but it's not needed.

Originally created by @aseques on GitHub (May 27, 2019). Original GitHub issue: https://github.com/ergin/nginx-cloudflare-real-ip/issues/3 I'll post here the final approach I took to get at the same time - Block non cloudflare IPs (per server) - Identify incoming ips - No need for a firewall Add to nginx.conf (http scope): ``` geo $realip_remote_addr $cloudflare_ip { default 0; 103.21.244.0/22 1; 103.22.200.0/22 1; 103.31.4.0/22 1; 104.16.0.0/12 1; 108.162.192.0/18 1; 131.0.72.0/22 1; 141.101.64.0/18 1; 162.158.0.0/15 1; 172.64.0.0/13 1; 173.245.48.0/20 1; 188.114.96.0/20 1; 190.93.240.0/20 1; 197.234.240.0/22 1; 198.41.128.0/17 1; 199.27.128.0/21 1; } ``` In the server block: ``` location /route/ { proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_set_header X-NginX-Proxy true; proxy_pass http://127.0.0.1:8051/; proxy_redirect off; if ($cloudflare_ip != 1) { return 403; } include /etc/nginx/snippets/allow_cloudflare.conf; deny all; } ``` Being allow_cloudflare.conf the following: ``` allow 103.21.244.0/22; allow 103.22.200.0/22; allow 103.31.4.0/22; allow 104.16.0.0/12; allow 108.162.192.0/18; allow 131.0.72.0/22; allow 141.101.64.0/18; allow 162.158.0.0/15; allow 172.64.0.0/13; allow 173.245.48.0/20; allow 188.114.96.0/20; allow 190.93.240.0/20; allow 197.234.240.0/22; allow 198.41.128.0/17; real_ip_header CF-Connecting-IP; ``` This works like a charm, if you still want extra security you can configure the firewall to block the other ips but it's not needed.
Author
Owner

@Nottt commented on GitHub (May 27, 2019):

This doesn't seem to be more secure than just white-listing cloudflare IP's with ufw and blocking everything else.

You are still allowing traffic from any IP to any open ports in your system

<!-- gh-comment-id:496116827 --> @Nottt commented on GitHub (May 27, 2019): This doesn't seem to be more secure than just white-listing cloudflare IP's with `ufw` and blocking everything else. You are still allowing traffic from any IP to any open ports in your system
Author
Owner

@aseques commented on GitHub (May 27, 2019):

This doesn't seem to be more secure than just white-listing cloudflare IP's with ufw and blocking everything else.
You are still allowing traffic from any IP to any open ports in your system

It's not more secure, but it's more flexible, for example in my case I have a server that includes some internal sites only accessible via cloudflare access (those are restricted to the connections allowed in cloudflare access) and some sites that have public resources not protected by cloudflare.
Being behind a firewall is fine, but sometimes it's a pain to debug when there are issues.

<!-- gh-comment-id:496144428 --> @aseques commented on GitHub (May 27, 2019): > This doesn't seem to be more secure than just white-listing cloudflare IP's with `ufw` and blocking everything else. > You are still allowing traffic from any IP to any open ports in your system It's not more secure, but it's more flexible, for example in my case I have a server that includes some internal sites only accessible via cloudflare access (those are restricted to the connections allowed in cloudflare access) and some sites that have public resources not protected by cloudflare. Being behind a firewall is fine, but sometimes it's a pain to debug when there are issues.
Author
Owner

@ankorez commented on GitHub (Aug 17, 2023):

Thank you very much for this solution i appreciate it

<!-- gh-comment-id:1682786246 --> @ankorez commented on GitHub (Aug 17, 2023): Thank you very much for this solution i appreciate it
Author
Owner

@forgotPassword commented on GitHub (Feb 2, 2024):

@aseques Note that attacker can easily have "cloudflare ip", by using cloudflare workers etc. So it is probably misleading thinking your are "safe" and blocked all evil actors.

You also might not want to advertise what domain you are expecting, by having a first rule to deny all un-matched server_name(s).

server {
    listen 443 ssl;

    ssl_certificate dummy.crt
    ssl_certificate_key dummy.key

    # or
    # ssl_reject_handshake on;

    return 444;
}

See also https://news.ycombinator.com/item?id=26688390

<!-- gh-comment-id:1923618097 --> @forgotPassword commented on GitHub (Feb 2, 2024): @aseques Note that attacker can easily have "cloudflare ip", by using cloudflare workers etc. So it is probably misleading thinking your are "safe" and blocked all evil actors. You also might not want to advertise what domain you are expecting, by having a first rule to deny all un-matched server_name(s). ``` server { listen 443 ssl; ssl_certificate dummy.crt ssl_certificate_key dummy.key # or # ssl_reject_handshake on; return 444; } ``` See also https://news.ycombinator.com/item?id=26688390
Author
Owner

@aseques commented on GitHub (Feb 5, 2024):

hi @forgotPassword good to know, some notes about your points:

  • I can't use the nginx rules because I have more sites on the same server with different requirements (I would use firewall rules otherwise)
  • This is very interesting, I'll will look into it
    If a Cloudflare customer has configured their origin server to respond only to Cloudflare IPs, then they MUST also verify that the "Host" header on any request actually matches their domain name.

Thanks for your notes

<!-- gh-comment-id:1926477247 --> @aseques commented on GitHub (Feb 5, 2024): hi @forgotPassword good to know, some notes about your points: - I can't use the nginx rules because I have more sites on the same server with different requirements (I would use firewall rules otherwise) - This is very interesting, I'll will look into it `If a Cloudflare customer has configured their origin server to respond only to Cloudflare IPs, then they MUST also verify that the "Host" header on any request actually matches their domain name.` Thanks for your notes
Author
Owner

@ankorez commented on GitHub (Feb 5, 2024):

  1. Copy the content below into a file, for example, allow-cloudflare.sh.
#!/bin/bash

# Complete list of Cloudflare IP ranges for IPv4 and IPv6
CLOUDFLARE_IPS=(
"173.245.48.0/20"
"103.21.244.0/22"
"103.22.200.0/22"
"103.31.4.0/22"
"141.101.64.0/18"
"108.162.192.0/18"
"190.93.240.0/20"
"188.114.96.0/20"
"197.234.240.0/22"
"198.41.128.0/17"
"162.158.0.0/15"
"104.16.0.0/13"
"104.24.0.0/14"
"172.64.0.0/13"
"131.0.72.0/22"
"2400:cb00::/32"
"2606:4700::/32"
"2803:f800::/32"
"2405:b500::/32"
"2405:8100::/32"
"2a06:98c0::/29"
"2c0f:f248::/32"
)

# Allow each IP range for ports 80 and 443
for ip in "${CLOUDFLARE_IPS[@]}"; do
    sudo ufw allow proto tcp from $ip to any port 80,443
done

# Display UFW status for verification
sudo ufw status
  1. Make the script executable with the command: chmod +x allow-cloudflare.sh.

  2. Execute the script with ./allow-cloudflare.sh.

  3. Allow SSH before enabling the firewall

sudo ufw allow ssh
  1. Enable the firewall
sudo ufw enable
  1. If you need to open a port, for example, for netdata:
sudo ufw allow from 123.45.67.89 to any port 19999
  1. Nginx configuration for real IP from Cloudflare

Create a new configuration file in /etc/nginx/conf.d/nginx-cloudflare-realip.conf and add the following content:

# Nginx configuration for real IP from Cloudflare

# Set real IP from Cloudflare for IPv4
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 131.0.72.0/22;

# Set real IP from Cloudflare for IPv6
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2a06:98c0::/29;
set_real_ip_from 2c0f:f248::/32;

# Set real IP header for Cloudflare
real_ip_header CF-Connecting-IP;
# real_ip_header X-Forwarded-For;
  1. Restart Nginx:
sudo service nginx restart
<!-- gh-comment-id:1926846571 --> @ankorez commented on GitHub (Feb 5, 2024): 1. Copy the content below into a file, for example, **`allow-cloudflare.sh`**. ```bash #!/bin/bash # Complete list of Cloudflare IP ranges for IPv4 and IPv6 CLOUDFLARE_IPS=( "173.245.48.0/20" "103.21.244.0/22" "103.22.200.0/22" "103.31.4.0/22" "141.101.64.0/18" "108.162.192.0/18" "190.93.240.0/20" "188.114.96.0/20" "197.234.240.0/22" "198.41.128.0/17" "162.158.0.0/15" "104.16.0.0/13" "104.24.0.0/14" "172.64.0.0/13" "131.0.72.0/22" "2400:cb00::/32" "2606:4700::/32" "2803:f800::/32" "2405:b500::/32" "2405:8100::/32" "2a06:98c0::/29" "2c0f:f248::/32" ) # Allow each IP range for ports 80 and 443 for ip in "${CLOUDFLARE_IPS[@]}"; do sudo ufw allow proto tcp from $ip to any port 80,443 done # Display UFW status for verification sudo ufw status ``` 2. Make the script executable with the command: **`chmod +x allow-cloudflare.sh`**. 3. Execute the script with **`./allow-cloudflare.sh`**. 4. **Allow SSH before enabling the firewall** ```bash sudo ufw allow ssh ``` 5. **Enable the firewall** ```bash sudo ufw enable ``` 6. If you need to open a port, for example, for netdata: ```bash sudo ufw allow from 123.45.67.89 to any port 19999 ``` 7. **Nginx configuration for real IP from Cloudflare** Create a new configuration file in `/etc/nginx/conf.d/nginx-cloudflare-realip.conf` and add the following content: ```nginx # Nginx configuration for real IP from Cloudflare # Set real IP from Cloudflare for IPv4 set_real_ip_from 173.245.48.0/20; set_real_ip_from 103.21.244.0/22; set_real_ip_from 103.22.200.0/22; set_real_ip_from 103.31.4.0/22; set_real_ip_from 141.101.64.0/18; set_real_ip_from 108.162.192.0/18; set_real_ip_from 190.93.240.0/20; set_real_ip_from 188.114.96.0/20; set_real_ip_from 197.234.240.0/22; set_real_ip_from 198.41.128.0/17; set_real_ip_from 162.158.0.0/15; set_real_ip_from 104.16.0.0/13; set_real_ip_from 104.24.0.0/14; set_real_ip_from 172.64.0.0/13; set_real_ip_from 131.0.72.0/22; # Set real IP from Cloudflare for IPv6 set_real_ip_from 2400:cb00::/32; set_real_ip_from 2606:4700::/32; set_real_ip_from 2803:f800::/32; set_real_ip_from 2405:b500::/32; set_real_ip_from 2405:8100::/32; set_real_ip_from 2a06:98c0::/29; set_real_ip_from 2c0f:f248::/32; # Set real IP header for Cloudflare real_ip_header CF-Connecting-IP; # real_ip_header X-Forwarded-For; ``` 8. Restart Nginx: ```bash sudo service nginx restart ```
Author
Owner

@aseques commented on GitHub (Feb 5, 2024):

@ankorez it doesn't help when you need two different services in the same IP, one restricted to cloudflare and the other open internet-wide

<!-- gh-comment-id:1927439563 --> @aseques commented on GitHub (Feb 5, 2024): @ankorez it doesn't help when you need two different services in the same IP, one restricted to cloudflare and the other open internet-wide
Author
Owner

@forgotPassword commented on GitHub (Feb 24, 2024):

Another option is to use Authenticated Origin Pulls

ssl_verify_client on;
ssl_client_certificate crt.pem

You can even configure per hostname certs, which should solve the shared cloudflare network access problem. Another benefit is that you don't need to fuss with different ips. Only downside is probably loss of some cpu cycles.

<!-- gh-comment-id:1962368731 --> @forgotPassword commented on GitHub (Feb 24, 2024): Another option is to use [Authenticated Origin Pulls](https://developers.cloudflare.com/ssl/origin-configuration/authenticated-origin-pull) ``` ssl_verify_client on; ssl_client_certificate crt.pem ``` You can even configure per hostname certs, which should solve the shared cloudflare network access problem. Another benefit is that you don't need to fuss with different ips. Only downside is probably loss of some cpu cycles.
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/nginx-cloudflare-real-ip#3
No description provided.