├── .github ├── FUNDING.yml └── workflows │ └── ShellCheck.yml ├── README.md └── generate.sh /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: PeterDaveHello 2 | open_collective: peterdavehello 3 | ko_fi: peterdavehello 4 | liberapay: PeterDaveHello 5 | issuehunt: peterdavehello 6 | -------------------------------------------------------------------------------- /.github/workflows/ShellCheck.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | jobs: 4 | shellcheck: 5 | name: ShellCheck 6 | runs-on: ubuntu-latest 7 | steps: 8 | - uses: actions/checkout@v3 9 | - name: ShellCheck 10 | uses: ludeeus/action-shellcheck@2.0.0 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-cdn-set-real-ip 2 | 3 | This script generates an nginx configuration file that sets the correct client IP address based on CDN's IP addresses and the corresponding header. 4 | 5 | ## Supported CDN 6 | 7 | - Cloudflare (`cf`, using header `CF-Connecting-IP`) 8 | - Fastly (`fastly`, using header `Fastly-Client-IP`) 9 | 10 | ## Installation 11 | 12 | You can either clone this repository to your server, or download the script directly from the repository: 13 | 14 | ```sh 15 | # Clone the repository 16 | git clone https://github.com/PeterDaveHello/nginx-cdn-set-real-ip /opt/nginx-cdn-set-real-ip 17 | 18 | # OR download the script directly 19 | mkdir -p /opt/nginx-cdn-set-real-ip/ 20 | curl -sLo /opt/nginx-cdn-set-real-ip/generate.sh https://raw.githubusercontent.com/PeterDaveHello/nginx-cdn-set-real-ip/master/generate.sh 21 | ``` 22 | 23 | > Note: The `/opt` directory may require root privileges to write to. If you encounter permission errors, you may need to run the above commands with `sudo`. 24 | 25 | ## Usage 26 | 27 | To execute the script, ensure correct permissions and include supported CDN codes separated by a space if multiple CDNs are needed: 28 | 29 | ```sh 30 | ./generate.sh [[CDN] [CDN]] 31 | ``` 32 | 33 | For example: 34 | 35 | ```sh 36 | $ sudo /opt/nginx-cdn-set-real-ip/generate.sh cf 37 | Start nginx real client ip config generation... 38 | 39 | Config target: /etc/nginx/conf.d/cloudflare-set-real-ip.conf 40 | 41 | Fetching Cloudflare IP addresses... 42 | Generating nginx configuration file... 43 | Nginx configuration for Cloudflare IP addresses added successfully. 44 | ``` 45 | 46 | ### Cronjob 47 | 48 | The script supports a `--cron` argument that causes it to randomly pause for 0-900 seconds before executing, in order to prevent sending too many requests to the CDN from the same region and avoid excessive updates occurring simultaneously. 49 | 50 | You can add a cronjob with supported CDN to trigger the IP update script periodically and reload nginx for the new config. 51 | 52 | For example, create `/etc/cron.d/opt/nginx-cdn-set-real-ip` with the following contents: 53 | 54 | ```cron 55 | 1 1 * * * root /opt/nginx-cdn-set-real-ip/generate.sh fastly --cron && /usr/sbin/service nginx reload 56 | ``` 57 | 58 | This will run the script every day at 01:01 AM and reload nginx with the new configuration. 59 | 60 | ## How it Works 61 | 62 | The script fetches the latest CDN IP addresses from official sources and generates an nginx configuration file in `/etc/nginx/conf.d/cdn-set-real-ip.conf`. 63 | 64 | It uses the `set_real_ip_from` directive to specify the trusted CDN IP addresses and the `real_ip_header` directive to set the corresponding header as the source of the real IP address. 65 | 66 | If there are no changes to the CDN IP addresses, the script will exit without updating the configuration file. 67 | 68 | ## Reference 69 | 70 | ### Cloudflare 71 | 72 | - 73 | - 74 | 75 | ### Fastly 76 | 77 | - 78 | - 79 | -------------------------------------------------------------------------------- /generate.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -euo pipefail 4 | 5 | temp_ips="$(mktemp)" 6 | nginx_ip_conf_dir="${nginx_ip_conf_dir:-/etc/nginx/conf.d}" 7 | sleep_secs="0" 8 | trap 'rm -f "$temp_ips"' EXIT 9 | 10 | for cmd in curl sed mv chmod rm cmp; do 11 | command -v "$cmd" >/dev/null || { echo >&2 "Error: $cmd not found. Please make sure it's installed and try again."; exit 1; } 12 | done 13 | 14 | declare -A CDN_NAME CDN_IP_HEADER REQUESTED_CDN 15 | 16 | CDN_NAME["cf"]="Cloudflare" 17 | CDN_IP_HEADER["cf"]="CF-Connecting-IP" 18 | 19 | CDN_NAME["fastly"]="Fastly" 20 | CDN_IP_HEADER["fastly"]="Fastly-Client-IP" 21 | 22 | fetch_ip_list() { 23 | true > "$temp_ips" 24 | case $1 in 25 | "cf") 26 | for file in ips-v4 ips-v6; do 27 | curl --compressed -sLo- "https://www.cloudflare.com/$file" >> "$temp_ips" 28 | echo '' >> "$temp_ips" 29 | done 30 | ;; 31 | "fastly") 32 | curl --compressed -sLo- https://api.fastly.com/public-ip-list | \ 33 | awk -F'[]["]' '{for(i=1;i<=NF;i++) if ($i ~ /.*\/.*/) print $i}' | \ 34 | sed 's/,\|\"//g' >> "$temp_ips" 35 | ;; 36 | esac 37 | } 38 | 39 | help() { 40 | echo >&2 41 | echo >&2 "This tool help generates nginx config file that sets the correct client IP address based on CDN provider's IP addresses and the corresponding header." 42 | echo >&2 "" 43 | echo >&2 "You need to give me at least one of supported CDN providers here to generate the config." 44 | echo >&2 "" 45 | echo >&2 "Usage:" 46 | echo >&2 "" 47 | echo >&2 "$0 [--cron] [[CDN] [CDN]]" 48 | echo >&2 "" 49 | echo >&2 "Supported CDN:" 50 | echo >&2 "" 51 | for cdn in "${!CDN_NAME[@]}"; do 52 | echo >&2 "- $cdn (${CDN_NAME[$cdn]}, using http header ${CDN_IP_HEADER[$cdn]})" 53 | done 54 | } 55 | 56 | for arg in "$@"; do 57 | case $arg in 58 | "-h"|"--help") 59 | help 60 | exit 61 | ;; 62 | "--cron") 63 | sleep_secs="$((RANDOM % 900))" 64 | continue 65 | ;; 66 | esac 67 | 68 | if [ ! -v 'CDN_NAME["$arg"]' ]; then 69 | echo >&2 "\"$arg\" is not in the supported CDN list nor the supported argument, skipped..." 70 | continue 71 | fi 72 | REQUESTED_CDN[$arg]=1 73 | done 74 | 75 | chmod 644 "$temp_ips" 76 | 77 | if [ ! -v 'REQUESTED_CDN[@]' ]; then 78 | echo >&2 79 | echo >&2 "No valid CDN found!" 80 | help 81 | exit 1 82 | fi 83 | 84 | sleep "$sleep_secs" 85 | echo "Start nginx real client ip config generation..." 86 | 87 | mkdir -p "$nginx_ip_conf_dir" 88 | 89 | for cdn in "${!REQUESTED_CDN[@]}"; do 90 | nginx_ip_conf="$nginx_ip_conf_dir/${CDN_NAME[$cdn],,}-set-real-ip.conf" 91 | echo 92 | echo "Config target: $nginx_ip_conf" 93 | echo 94 | echo "Fetching ${CDN_NAME[$cdn]} IP addresses..." 95 | fetch_ip_list "$cdn" 96 | echo "Generating nginx configuration file..." 97 | sed -i -e 's/^/set_real_ip_from /g' -e 's/$/;/g' -e "1i real_ip_header ${CDN_IP_HEADER[$cdn]};" "$temp_ips" 98 | 99 | if ! [ -e "$nginx_ip_conf" ]; then 100 | mv -f "$temp_ips" "$nginx_ip_conf" 101 | echo "Nginx configuration for ${CDN_NAME[$cdn]} IP addresses added successfully." 102 | elif cmp -s "$temp_ips" "$nginx_ip_conf"; then 103 | echo "No changes detected. We have nothing to do." 104 | rm -f "$temp_ips" 105 | else 106 | echo "${CDN_NAME[$cdn]} IP addresses config have changed. Updating nginx configuration..." 107 | mv -f "$temp_ips" "$nginx_ip_conf" 108 | echo "Nginx configuration for ${CDN_NAME[$cdn]} IP addresses updated successfully." 109 | fi 110 | done 111 | --------------------------------------------------------------------------------