Обнаружение Эффекта недоступности из-за большого числа обращений в nginx

Вы хранящий пароли в классическом "склепе" формат DES (14 символов в длину, a-z, A-Z, 0-9, "." и/или "/" символы, как: "papAq5PwY/QQM", без $ входит в систему зашифрованный пароль)?

Если бы Вы, который объяснил бы это. Тот формат ограничен 8 символами, таким образом, что-либо прошлые 8 символов проигнорировано. Если правильный пароль равняется 8 (или больше) символы, добавляя, что больше символов в конце не будет иметь значения.

Для фиксации его используйте MD5 или SHA для хранения паролей вместо этого. Если Вы используете"htpasswd"управляйте, добавьте"-m"или"-s"к Вашим опциям, когда Вы устанавливаете пароль.

10
задан 27 September 2012 в 19:52
4 ответа

Наиболее эффективным решением может быть создание демона, который будет tail -f the access.log и отслеживать Поле $ http_referer .

Однако быстрым и грязным решением было бы добавить дополнительный файл access_log , чтобы регистрировать только переменную $ http_referer с настраиваемой ] log_format , а также для автоматической ротации журнала каждые X минут.

  • Это может быть выполнено с помощью стандартных скриптов logrotate, которым может потребоваться постепенный перезапуск nginx для повторного открытия файлов (например, стандартную процедуру, посмотрите / a / 15183322 на SO для простого сценария, основанного на времени)…

  • Или, используя переменные в access_log , возможно, получив спецификация минут из $ time_iso8601 с помощью карты или директивы if (в зависимости от того, где вы хотите разместить свой [116106] 9] access_log ).

Таким образом, у вас может быть 6 файлов журнала, каждый из которых охватывает период в 10 минут, http_referer.Txx {0,1,2,3,4,5 } x.log , например, получая первую цифру минуты, чтобы различать каждый файл.

Теперь все, что вам нужно сделать, это иметь простой сценарий оболочки, который мог бы запускаться каждые 10 минут, cat все вышеперечисленные файлы вместе, конвейер для sort , конвейер для uniq -c , для sort -rn , для head -16 , и у вас есть список из 16 наиболее распространенных вариантов Referer - вы можете самостоятельно решать, превышают ли какие-либо комбинации чисел и полей ваши критерии, и выполнять уведомление.

Впоследствии, после за одно успешное уведомление вы можете удалить все эти 6 файлов и, при последующих запусках, не выдавать никаких уведомлений, ЕСЛИ не присутствуют все шесть файлов (и / или определенное другое число по вашему усмотрению).

3
ответ дан 2 December 2019 в 22:01

Я думаю, что это было бы намного лучше сделать с помощью logtail и grep. Даже если это можно сделать с помощью встроенного lua, вам не нужны эти накладные расходы для каждого запроса, и вы особенно не хотите этого, когда вы были отмечены Slashdotted.

Вот 5-секундная версия. Вставьте его в сценарий и поместите вокруг него более читаемый текст, и вы будете золотыми.

5 * * * * logtail -f /var/log/nginx/access_log -o /tmp/nginx-logtail.offset | grep -c "http://[^ ]slashdot.org"

Конечно, это полностью игнорирует reddit.com, facebook.com и все миллионы других сайтов, которые могут отправить вам много трафика. Не говоря уже о 100 разных сайтах, каждый из которых отправляет вам 20 посетителей. Вероятно, у вас должен быть просто старый добрый порог трафика , при котором вам будет отправляться электронное письмо, независимо от реферера.

13
ответ дан 2 December 2019 в 22:01

Директива nginx limit_req_zone может основывать свои зоны на любой переменной, включая $ http_referrer.

http {
    limit_req_zone  $http_referrer  zone=one:10m   rate=1r/s;

    ...

    server {

        ...

        location /search/ {
            limit_req   zone=one  burst=5;
        }

Вы также захотите сделать что-то, чтобы ограничить количество состояний, требуемых на веб-сервере, поскольку заголовки реферера могут быть довольно длинными и разнообразными, и вы можете увидеть бесконечное разнообразие. Вы можете использовать функцию nginx split_clients , чтобы установить переменную для всех запросов, основанную на хэше заголовка реферера. В приведенном ниже примере используется только 10 баксов, но вы можете сделать это и с 1000 так же легко. Таким образом, если у вас есть косая черта, люди, реферер которых попал в тот же сегмент, что и URL-адрес slashdot, также будут заблокированы, но вы можете ограничить это до 0,1% посетителей, используя 1000 сегментов в split_clients.

Это будет выглядеть примерно так. это (полностью непроверено, но направлено правильно):

http {

split_clients $http_referrer $refhash {
               10%               x01;
               10%               x02;
               10%               x03;
               10%               x04;
               10%               x05;
               10%               x06;
               10%               x07;
               10%               x08;
               10%               x09;
               *                 x10;
               }

limit_req_zone  $refhash  zone=one:10m   rate=1r/s;

...

server {

    ...

    location /search/ {
        limit_req   zone=one  burst=5;
    }
4
ответ дан 2 December 2019 в 22:01

Да, конечно, это возможно в NGINX!

Что вы Можно было бы реализовать следующий DFA :

  1. Реализовать ограничение скорости на основе $ http_referer , возможно, используя некоторое регулярное выражение через карту для нормализации значений. Когда лимит превышен, открывается страница внутренней ошибки, которую вы можете перехватить с помощью обработчика error_page как на соответствующий вопрос , перейдя в новое внутреннее расположение в качестве внутреннего перенаправления ( не виден клиенту).

  2. В указанном выше месте для превышения пределов вы выполняете запрос предупреждения, позволяя внешней логике выполнить уведомление; этот запрос впоследствии кэшируется, гарантируя, что вы получите только 1 уникальный запрос в заданное временное окно.

  3. Перехватите код состояния HTTP предыдущего запроса (путем возврата кода состояния ≥ 300 и использования proxy_intercept_errors на или, в качестве альтернативы, используйте не встроенный по умолчанию auth_request или add_after_body , чтобы сделать «бесплатный» подзапрос), и выполните исходный запрос, как если бы предыдущий шаг не был т вовлечен. Обратите внимание, что нам нужно включить рекурсивную обработку error_page , чтобы это работало.

Вот мой PoC и MVP, также на https://github.com/cnst/StackOverflow.cnst.nginx .conf / blob / master / sf.432636.detecting-slashdot-effect-in-nginx.conf :

limit_req_zone $http_referer zone=slash:10m rate=1r/m;  # XXX: how many req/minute?
server {
    listen 2636;
    location / {
        limit_req zone=slash nodelay;
        #limit_req_status 429;  #nginx 1.3.15
        #error_page 429 = @dot;
        error_page 503 = @dot;
        proxy_pass http://localhost:2635;
        # an outright `return 200` has a higher precedence over the limit
    }
    recursive_error_pages on;
    location @dot {
        proxy_pass http://127.0.0.1:2637/?ref=$http_referer;
        # if you don't have `resolver`, no URI modification is allowed:
        #proxy_pass http://localhost:2637;
        proxy_intercept_errors on;
        error_page 429 = @slash;
    }
    location @slash {
        # XXX: placeholder for your content:
        return 200 "$uri: we're too fast!\n";
    }
}
server {
    listen 2635;
    # XXX: placeholder for your content:
    return 200 "$uri: going steady\n";
}
proxy_cache_path /tmp/nginx/slashdotted inactive=1h
        max_size=64m keys_zone=slashdotted:10m;
server {
    # we need to flip the 200 status into the one >=300, so that
    # we can then catch it through proxy_intercept_errors above
    listen 2637;
    error_page 429 @/.;
    return 429;
    location @/. {
        proxy_cache slashdotted;
        proxy_cache_valid 200 60s;  # XXX: how often to get notifications?
        proxy_pass http://localhost:2638;
    }
}
server {
    # IRL this would be an actual script, or
    # a proxy_pass redirect to an HTTP to SMS or SMTP gateway
    listen 2638;
    return 200 authorities_alerted\n;
}

Обратите внимание, что это работает, как ожидалось:

% sh -c 'rm /tmp/slashdotted.nginx/*; mkdir /tmp/slashdotted.nginx; nginx -s reload; for i in 1 2 3; do curl -H "Referer: test" localhost:2636; sleep 2; done; tail /var/log/nginx/access.log'
/: going steady
/: we're too fast!
/: we're too fast!

127.0.0.1 - - [26/Aug/2017:02:05:49 +0200] "GET / HTTP/1.1" 200 16 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:49 +0200] "GET / HTTP/1.0" 200 16 "test" "curl/7.26.0"

127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET / HTTP/1.1" 200 19 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET /?ref=test HTTP/1.0" 200 20 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:51 +0200] "GET /?ref=test HTTP/1.0" 429 20 "test" "curl/7.26.0"

127.0.0.1 - - [26/Aug/2017:02:05:53 +0200] "GET / HTTP/1.1" 200 19 "test" "curl/7.26.0"
127.0.0.1 - - [26/Aug/2017:02:05:53 +0200] "GET /?ref=test HTTP/1.0" 429 20 "test" "curl/7.26.0"
%

Вы можете видеть, что первый запрос приводит к одному фронту -end и одно обращение к бэкэнду, как и ожидалось (мне пришлось добавить фиктивный бэкэнд к месту, имеющему limit_req , потому что return 200 будет иметь приоритет над лимитами, реальный бэкэнд не требуется для остальной обработки).

Второй запрос превышает лимит, поэтому мы отправляем предупреждение (получаем 200 ) и кэшируем его, возвращая 429 (это необходимо из-за вышеупомянутого ограничения, заключающегося в том, что запросы ниже 300 не могут быть перехвачены), который впоследствии перехватывается внешним интерфейсом, который теперь свободен делать все, что хочет.

Третий запрос все еще остается превышен лимит, но мы уже отправили предупреждение, поэтому новое предупреждение не отправляется.

Готово! Не забудьте разветвить его на GitHub!

2
ответ дан 2 December 2019 в 22:01

Теги

Похожие вопросы