Re: ошибка в nginx ?

Gena Makhomed gmm at csdoc.com
Sat Jul 12 19:01:25 MSD 2008


On Friday, July 11, 2008 at 23:50:36, Andrei Nigmatulin wrote:

>> IS> Единственное, что можно делать:
>> IS> ждать секунду и повторять попытку до истечения ..._connect_timeout.

>> backlog обычно бывает переполнен при (очень) большой нагрузке,
>> это сделает веб-сервера под управлением nginx на операционной
>> системе с ядром Linux гораздо более уязвимыми к DDOS-атакам.

[ ... ]

AN> А в "не-linux" по-другому ?

Да.

в других системах (например, FreeBSD) при попытке выполнить connect
в неблокирующем режиме к unix domain socket в ситуации переполнения
backlog`а этого socket`а возвращается ECONNREFUSED, в Linux - EAGAIN.

а например, там где все остальные POSIX-совместимые системы
возвращают EINPROGRESS - Windows возвращает WSAEWOULDBLOCK
и поскольку код WSAEWOULDBLOCK ~= EAGAIN, то для nginx везде
реакция на EAGAIN оказывается такой же, как и на EINPROGRESS.

src\event\ngx_event_connect.c
===========================================================

    if (rc == -1) {
        err = ngx_socket_errno;

        /* Winsock returns WSAEWOULDBLOCK (NGX_EAGAIN) */

        if (err != NGX_EINPROGRESS && err != NGX_EAGAIN) {

===========================================================

для FreeBSD - проблем нет, потому что функция connect никогда
не возвращает EAGAIN, из-за этого проблемы есть только в Linux.

POSIX connect:
http://www.opengroup.org/onlinepubs/000095399/functions/connect.html

WINSOCK connect:
http://msdn.microsoft.com/en-us/library/ms737625(VS.85).aspx

>> nginx будет ..._connect_timeout секунд держать tcp соединение
>> с клиентом, и будет раз в секунду пытаться установить соединение
>> с сервером при переполненном backlog`е - впустую растрачивая
>> системные ресурсы. и workaround к этой уязвимости неизвестен.

AN> Если бэкенд (на одной машине с nginx, ведь мы до сих пор говорим
AN> про локальные сокеты) настолько загружен, что не успевает разгребать
AN> очередь новых запросов, то у nginx должна быть четкая стратегия -
AN> держаться до последнего и делать все возможное.

попытками выполнить connect с интервалом в 1 секунду в течение 60 секунд
nginx будет только мешать backend`у обрабатывать другие клиентские запросы.

в случае подключения к backend`у на FreeBSD и при переполнении backlog`а
nginx сразу получает ответ ECONNREFUSED и сразу возвращает клиенту 502.

стратегия должна быть, прежде всего в виде одинаковой реакции
на одинаковые внешние события. "делать все возможное" в случае
перегрузки backend`а клиентскими запросами - это прежде всего,
не мешать ему работать и не создавать лишнюю нагрузку на сервер.

AN> Если он будет выдавать 502, то это значит атака удалась.

атака еще больше удалась, когда вместо 1 запроса к backend`у
nginx будет делать 60 запросов, на каждый из них затрачивая
системные ресурсы и без того уже перегруженного сервера.

например, если backlog для unix domain socket выставлен в 1024
и этот backlog уже полностью заполнен - нет смысла даже пытаться
создавать еще один "програмный backlog" на уровне nginx frontend`а
тем более - "програмный backlog" с неограниченным размером, который
администратору никак нельзя будет уменьшить или выключить.

потому что пока backend обработает 512 первых запросов из backlog`а,
часть клиентов просто отвалится по своему собственному connect_timeout
не дождавшись ответа от сервера, или пользователь закроет окно браузера.

AN> А продолжать попытки обработать запрос - это правильно, ведь в конце
AN> концов на это есть fastcgi_connect_timeout секунд, а вдруг получится.

fastcgi_connect_timeout - это не то, это connect timeout.
в данном случае нет timeout`а, а есть код ошибки EAGAIN.

и причина этой проблемы в nginx с EAGAIN на Linux - это Windows.

===================================================================

реакция на EAGAIN в Linux должна быть такой же как на ECONNREFUSED
в случае попытки connect к unix domain socket в non-bloking режиме

===================================================================

реакция на EAGAIN в Windows должна быть такой же как на EINPROGRESS
в случае попытки connect к anything domain socket в non-bloking режиме

===================================================================

если больше никакие системы кроме Linux и Windows
не возвращают EAGAIN из connect, то патч к функции
src\event\ngx_event_connect.c можно сделать в виде
блока условной компиляции, что позволит даже
несколько ускорить работу nginx на FreeBSD.

-- 
Best regards,
 Gena






More information about the nginx-ru mailing list