nginx: ограничить доступ по IP для прокси, кроме серверов Cloudflare?

Вопрос или проблема

Мы используем прокси Cloudflare, поэтому настоящий IP скрыт. Но если вы узнаете настоящий IP, вы все равно можете получить доступ к сайту, используя его. Есть ли способ ограничить доступ по этому IP?

Я имею в виду:

по домену: https://some-domain.com -> ОК
по прокси IP: https://1.2.3.4 -> ОК
по реальному IP: https://5.6.7.8 -> ограничить.

Или бессмысленно пытаться ограничить его?

Пример серверных блоков nginx.conf:

    server {
        listen 80 default_server;
        server_name some-domain.com;
        return 301 https://$host$request_uri;
    }
    charset utf-8;
    server {
        server_name some-domain.com;
        listen 443 ssl http2;
        ...
        ...
    }

Вы смешиваете две разные вещи здесь. Первое — это ограничение доступа для клиентов, у которых в запросах не установлен правильный заголовок Host (это обычно боты, сетевые сканеры и т.д.). Для этого вам нужен какой-то блок сервера типа catch-all, чтобы отклонять такие запросы. Обратитесь к этому ответу на StackOverflow для примеров.

Второе — это ограничение запросов с правильным заголовком Host, но исходящих из источников, отличных от серверов, находящихся на периферии сети Cloudflare. Это, как правило, хорошая практика, так как Cloudflare действует как анти-DDoS (и, возможно, WAF) услуга для вашего сайта. Официальный список диапазонов IP-адресов Cloudflare доступен на их сайте: IP Ranges, или в виде простого текста по адресам IPv4 и IPv6. Вы можете использовать этот список, чтобы отклонять такие запросы с помощью ngx_http_geo_module следующим образом:

geo $denied {
    default 1;
    173.245.48.0/20 0;
    103.21.244.0/22 0;
    ...
}
server {
    server_name some-domain.com;
    listen 443 ssl http2;
    if ($denied) {
        # незамедлительно завершить запрос
        return 444;
    }
    ...

Список диапазонов IP обновляется периодически, так что я использую следующий скрипт для автоматической генерации списка диапазонов:

#!/bin/sh
{
  echo "# Cloudflare IP ranges - generated on $(date)"
  curl -s https://www.cloudflare.com/ips-v4/ | sed 's/$/ 0;/'
  echo # просто пустая строка
  curl -s https://www.cloudflare.com/ips-v6/ | sed 's/$/ 0;/'
  echo # еще одна пустая строка
} > /etc/nginx/cloudflare.inc

Затем вы можете включить этот список в вашу конфигурацию nginx следующим образом:

geo $denied {
    default 1;
    include cloudflare.inc;
    # вы можете добавить сюда дополнительные IP-адреса
    # (как я иногда добавляю свои домашние/рабочие IP)
}

Вы также можете использовать список диапазонов IP, чтобы восстановить оригинальные IP посетителей (см. официальную статью Cloudflare “Восстановление оригинальных IP посетителей” для деталей). Вообще, в последнее время я использую другой скрипт для автоматической генерации двух файлов:

#!/bin/bash
echo "# Cloudflare IP ranges - generated on $(date)" | tee /etc/nginx/cloudflare.inc > /etc/nginx/real_ip.inc
curl -s https://www.cloudflare.com/ips-v4/ | tee >(sed 's/$/ 0;/' >> /etc/nginx/cloudflare.inc) | sed 's/^/set_real_ip_from /; s/$/;/' >> /etc/nginx/real_ip.inc
echo >> /etc/nginx/cloudflare.inc # Пустая строка для cloudflare.inc
echo >> /etc/nginx/real_ip.inc    # Пустая строка для real_ip.inc
curl -s https://www.cloudflare.com/ips-v6/ | tee >(sed 's/$/ 0;/' >> /etc/nginx/cloudflare.inc) | sed 's/^/set_real_ip_from /; s/$/;/' >> /etc/nginx/real_ip.inc
echo >> /etc/nginx/cloudflare.inc # Еще одна пустая строка для cloudflare.inc
echo >> /etc/nginx/real_ip.inc    # Еще одна пустая строка для real_ip.inc

Второй файл будет содержать записи в следующем формате:

set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
...

Вы можете использовать этот файл следующим образом:

server {
    server_name some-domain.com;
    listen 443 ssl http2;
    if ($denied) {
        # незамедлительно завершить запрос
        return 444;
    }
    include real_ip.inc;
    real_ip_header CF-Connecting-IP;
    ...

Могут быть случаи, когда такая настройка необходима (вот проблема, с которой я однажды столкнулся). Однако, если вы используете эту конфигурацию, помните, что переменная $remote_addr, которая используется по умолчанию в блоке geo для проверки совпадения IP-адреса с заданными диапазонами, более не будет содержать адрес сервера Cloudflare. Вместо этого, вам нужно будет явно указать переменную $realip_remote_addr:

geo $realip_remote_addr $denied {
    default 1;
    include cloudflare.inc;
    ...
}

Ответ или решение

Ограничение доступа по IP в Nginx для работы с Cloudflare

Проблема: Вы используете прокси-сервер Cloudflare, из-за чего реальный IP-адрес пользователя скрыт. Однако если реальный IP-адрес известен, он может быть использован для доступа к вашему сайту напрямую. Вам необходимо ограничить доступ, чтобы запросы могли проходить только через разрешенные IP-адреса (например, прокси-сервера Cloudflare).

Решение:

  1. Ограничение доступа по IP-адресу:
    Используйте модуль ngx_http_geo_module, чтобы разрешать доступ только с IP-адресов, принадлежащих циклу IP Cloudflare. Например, можно сделать это следующим образом:

    geo $denied {
       default 1;
       173.245.48.0/20 0;
       103.21.244.0/22 0;
       ...
    }
    
    server {
       server_name some-domain.com;
       listen 443 ssl http2;
       if ($denied) {
           return 444;  # Прерывание запроса
       }
       ...
    }
  2. Автоматизация обновления IP-диапазонов Cloudflare:
    IP-диапазоны Cloudflare регулярно обновляются, поэтому рекомендуется автоматизировать процесс их обновления. Пример скрипта:

    #!/bin/bash
    echo "# Cloudflare IP ranges - generated on $(date)" | tee /etc/nginx/cloudflare.inc > /etc/nginx/real_ip.inc
    curl -s https://www.cloudflare.com/ips-v4/ | tee >(sed 's/$/ 0;/' >> /etc/nginx/cloudflare.inc) | sed 's/^/set_real_ip_from /; s/$/;/' >> /etc/nginx/real_ip.inc
    echo >> /etc/nginx/cloudflare.inc
    echo >> /etc/nginx/real_ip.inc
    curl -s https://www.cloudflare.com/ips-v6/ | tee >(sed 's/$/ 0;/' >> /etc/nginx/cloudflare.inc) | sed 's/^/set_real_ip_from /; s/$/;/' >> /etc/nginx/real_ip.inc
    echo >> /etc/nginx/cloudflare.inc
    echo >> /etc/nginx/real_ip.inc

    Используйте данный скрипт для регулярного обновления файлов /etc/nginx/cloudflare.inc и /etc/nginx/real_ip.inc.

  3. Восстановление реального IP-пользователя:
    Используйте директиву real_ip_header и включайте файл с IP-адресами Cloudflare, чтобы Nginx принимал заголовок CF-Connecting-IP как реальный IP пользователя:

    server {
       server_name some-domain.com;
       listen 443 ssl http2;
       if ($denied) {
           return 444;
       }
       include real_ip.inc;
       real_ip_header CF-Connecting-IP;
       ...
    }
  4. Учет переменной $realip_remote_addr:
    При использовании восстановления IP важно изменить условие geo, чтобы использовать переменную $realip_remote_addr вместо $remote_addr:

    geo $realip_remote_addr $denied {
       default 1;
       include cloudflare.inc;
       ...
    }

Заключение: Такой подход обеспечивает защиту сайта от прямого доступа с реальных IP-адресов пользователей, обеспечивая безопасность и приватность. Он также позволяет использовать Cloudflare в качестве эффективного средства защиты от DDoS-атак и других угроз.

Оцените материал
Добавить комментарий

Капча загружается...