Re: Firefox повторно исплользует соединение IPv6 на основании IPv4. Как с этим жить?

Maxim Dounin mdounin на mdounin.ru
Ср Июн 1 19:15:24 UTC 2016


Hello!

On Wed, Jun 01, 2016 at 08:50:34PM +0300, Иван wrote:

> Здравствуйте!
> 
> Я прошу прощения за осознанный оффтопик. Но, к сожалению, я вряд ли где еще 
> найду столько людей понимающих как работает SPDY и HTTP/2, чем тут. К тому же 
> проблема косвенно затрагивает конфигурирование nginx и, когда IPv6 станет более 
> массовым с проблемой столкнется, мне кажется, большинство присутствующих.
> 
> Некоторое время назад я обнаружил и зарепортил следующий баг: 
> https://bugzilla.mozilla.org/show_bug.cgi?id=1190136
> 
> Выглядело это как будто, обращаясь к одному виртуалхосту, я получаю контент 
> другого.
> 
> Конкретно понимание проблемы пришло тут 
> https://bugzilla.mozilla.org/show_bug.cgi?id=1190136#c22 .
> 
> Как вопроизвести, описано тут 
> https://bugzilla.mozilla.org/show_bug.cgi?id=1190136#c19

Т.е. проблема в том, что Firefox пытается делать connection reuse 
для всех IP-адресов двух хостов, если у этих хостов хотя бы один 
общий IP-адреса?

Это выглядит как нарушение стандарта.  RFC 7540 говорит нам, что 
reuse возможен только в том случае, если хост резолвится в тот же 
IP-адрес, к которому установлено существующее соединение 
(https://tools.ietf.org/html/rfc7540#section-9.1.1):

   A connection can be reused as long as the origin server
   is authoritative (Section 10.1).  For TCP connections without TLS,
   this depends on the host having resolved to the same IP address.

В вашем же случае случае хост не резолвится в тот IP-адрес, к 
которому установлено соединение.  Соответственно, reuse такого 
соединения - недопустим.

[...]

> Разработчик, отвественный за эту часть броузера сказал, что это не баг и чинить он 
> его не будет. Посоветовав использовать разные сертификаты для разных 
> субдоменов (а не wildcard) или пользоваться http-ответом с кодом 451.

Вероятно, имелся в виду код 421.  Но получилось особенно смешно.

> Однако я с этим в корне не согласен, более того, считаю, что такое поведение 
> открывает уязвимость, которую я описал тут:
> https://bugzilla.mozilla.org/show_bug.cgi?id=1190136#c22
> Но технической квалификации и политического веса в мире броузеров и серверов 
> мне не хватает. Так же мало кто используется ipv6 на клиенте, поэтому массовка еще 
> набралась.

Уязвимости тут как таковой нет: без SSL/TLS Firefox не умеет 
HTTP/2, а для connection reuse в случае TLS нужно ещё, чтобы был 
представлен корректный сертификат, покрывающий оба доменных имени.

> Что я хочу, написав, это письмо здесь?
> 1) Оцените, пожалуйста, насколько я прав. Может быть все не так, как мне кажется?
> Дальше только, если я прав.

Мы тут вообще считаем, что connection reuse - это очень плохая 
фича.  В SPDY её в своё время совсем выпилили, т.к. жить с этим - 
невозможно.  Но, видимо, у кого-то в Гугле что-то чешется и покоя 
не даёт, так что в HTTP/2 её засунули обратно, к сожалению.  
Самое простое решение - не использовать HTTP/2.

В данном конкретном случае - это явно баг Firefox'а, т.к. в 
соответствии со спецификацией HTTP/2 он не имеет права для 
запросов к хосту повтороно использовать соединение к IP-адресу, в 
который данный хост не резолвится.

> 2) Помогите найти аргументы для убеждения разработчиков ФФ, что этот баг надо 
> исправлять. Если кто готов отписаться там, я открою обратно этот баг.

См. аргументы выше.  На лицо явное нарушение стандарта.

> 3) Может быть как-то можно сконфигурировать nginx, чтобы решить эту проблему, 
> кроме использования раздельных сертификатов для поддоменов? Ибо раздельные 
> сертификаты - не всегда возможно.

Если я правильно понимаю конфигурацию, она какая-то такая:

    server {
        listen 192.0.2.1:443 ssl http2;
        listen [2001:db8::1]:443 ssl http2;
        server_name one.example.com;
    }

    server {
        listen 192.0.2.1:443 ssl http2;
        listen [2001:db8::2]:443 ssl http2;
        server_name two.example.com;
    }

и соответственно для IPv4 используются виртуальные сервера, а для 
IPv6 - адреса различаются, и соответственно каждый сервер является 
сервером по умолчанию для своего IPv6-адреса.

Совсем тривиальное решение: настроить nginx так, чтобы все сервера 
были виртуальными и выбирались по имени.  Проще всего - на 
wildcard-адресах, т.е. как-то так:

    server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name one.example.com;
    }

    server {
        listen 443 ssl http2;
        listen [::]:443 ssl http2;
        server_name two.example.com;
    }

Если физически машины, отвечающие за разные IPv6-адреса, разные, 
или по каким-то ещё причинам уравнивать адреса не хочется, то
то можно возвращать 421 Misdirected Request, если пытаются 
запросить неизвестное имя, как-то так:

    server {
        listen [2001:db8::1]:443 ssl http2 default_server;
        listen [2001:db8::2]:443 ssl http2 default_server;
        return 421;
    }

Для Firefox'а этого должно быть достаточно, чтобы переоткрыть 
соединение.

Нюанс: в результате теряется возможность обрабатывать на этих 
адресах запросы без имени, т.е. от совсем старых клиентов, которые 
ещё HTTP/1.0 или ранее и к тому же не умеют заголовок Host.  Их, 
впрочем, почти не осталось, из доживших до наших времён я такой, 
пожалуй, могу назвать только один: openssl ocsp.  Впрочем, для 
IPv4-адресов такие запросы всё равно в рассматриваемой 
конфигурации работать не будут.

-- 
Maxim Dounin
http://nginx.org/



Подробная информация о списке рассылки nginx-ru