Apache своеобразно обрабатывает `[::]:443`

Apache, NGINX, PHP ...

Модератор: ROOT

Apache своеобразно обрабатывает `[::]:443`

Сообщение ROOT » 31 авг 2025, 22:56

Введение
Вы сталкивались с внезапными ошибками `ERR_SSL_PROTOCOL_ERROR` или загадочными сообщениями `packet length too long` при попытке зайти на популярные сайты вроде Google? При этом проблемы волшебным образом исчезают, если остановить ваш локальный веб-сервер Apache.
Это не магия и не вирус, а следствие тонкого взаимодействия между IPv6, DNS и конфигурацией веб-сервера. В этой статье мы разберем неочевидную проблему, которая заставляет браузер использовать сертификат вашего локального сайта при попытке подключения к внешним ресурсам, и покажем, как ее окончательно решить.
Сценарий и симптоматика
Представим следующую среду, которая очень распространена в домашних сетях и сетях малых офисов:
  1. Сервер/клиент с Dual Stack: Ваша машина поддерживает как IPv4, так и IPv6.
  2. Локальный DNS (dnsmasq): В сети работает сервер Dnsmasq, который вы настроили для фильтрации рекламы или других целей.
  3. Фильтрация IPv6 для Google: Чтобы избежать назойливой капчи от сервисов Google при использовании IPv6, вы настроили Dnsmasq возвращать адрес `::` (неопределенный адрес в IPv6) для записей `AAAA` (IPv6) доменов вроде `google.com`.
  4. Локальный Apache: На этой же машине работает веб-сервер Apache, serving ваши локальные проекты с SSL.
Симптомы: При обращении к `https://translate.google.com` или другим сайтам браузер выдает ошибку безопасности. При детальном изучении оказывается, что он почему-то пытается применить SSL-сертификат от вашего локального домена (например, `myproject.test`) к Google.
Проблема моментально решается командой `sudo systemctl stop apache2` (или `httpd`), что явно указывает на причастность веб-сервера.
Корень проблемы: Неявное связывание сокетов в Apache
По умолчанию, многие конфигурации Apache используют упрощенные директивы для прослушивания портов:
Код: выделить все
Listen 80
Listen 443 https

Кажется, что это абсолютно нормально. Однако в этой простоте кроется дьявол. Такая конфигурация предписывает Apache слушать на **всех доступных сетевых интерфейсах**, включая все IPv4-адреса (`0.0.0.0:80` и `0.0.0.0:443`) и, что важнее, на **всех IPv6-адресах** (`[::]:80` и `[::]:443`).
Механизм сбоя: Пошаговый разбор
Давайте детально восстановим цепочку событий, которая приводит к ошибке.
  1. Запрос к Google: Вы в браузере переходите на `https://translate.google.com`.
  2. DNS-запрос: Браузер, следующий современным стандартам, сначала запрашивает IPv6-адрес (`AAAA` запись) для этого домена.
  3. Фильтрованный ответ DNS: Ваш локальный Dnsmasq, следуя правилу, возвращает в ответ адрес `::`. Это специальный адрес, эквивалентный IPv4-адресу `0.0.0.0`, который обычно означает "эта машина".
  4. Попытка подключения по IPv6: Браузер, получив IPv6-адрес (`::`), интерпретирует его как валидный и пытается установить SSL-соединение с сервером `translate.google.com` по адресу `[::]:443`.
  5. Ловушка локального Apache: Сетевой пакет, предназначенный для `[::]:443`, приходит на вашу машину. Apache слушает на всех адресах, включая `[::]:443`, и с радостью принимает это входящее соединение, полагая, что оно предназначено для одного из его виртуальных хостов.
  6. Сбой SSL-рукопожатия: Apache начинает SSL-рукопожатие, но вместо сертификата от Google он подставляет первый попавшийся сертификат из своих настроек (часто это сертификат для первого виртуального хоста в списке или локального домена). Браузер, ожидая увидеть сертификат для `*.google.com`, получает сертификат для `myproject.test`. Это приводит к фатальной ошибке `ERR_SSL_PROTOCOL_ERROR` или подобной.
  7. (Опционально) Откат на IPv4: В этот момент современные браузеры, поняв, что соединение по IPv6 не удалось, совершают повторный запрос к DNS, но уже для IPv4 (`A`-запись). Они успешно получают IPv4-адрес Google, соединяются с ним по `443` порту и загружают страницу. Ключевая путаница была в том, что проблема *казалась* временной, потому что браузер в итоге справлялся. Однако сам факт ошибки и задержка — это симптомы неправильной работы.
Почему `::` является слабым местом? Возврат адреса `::` — это нестандартный способ заставить клиента отказаться от IPv6. В нормальных условиях DNS должен возвращать пустой ответ на `AAAA`-запрос, если IPv6 недоступен. Возврат `::` перенаправляет трафик на локальный интерфейс, что и создает конфликт.
Решение: Явное указание IP-адресов для прослушивания
Решение проблемы элегантно и является признаком хорошего тона в администрировании. Вместо того чтобы позволять Apache слушать на всех интерфейсах, мы должны явно указать, на каких конкретных адресах он должен ожидать трафик.
Замените общие директивы apache:
Код: выделить все
Listen 80
Listen 443 https

На явные:
Код: выделить все
Listen 192.168.1.100:80         # Ваш IPv4-адрес
Listen [2001:db8::cafe]:80      # Ваш IPv6-адрес
Listen 192.168.1.100:443 https  # Ваш IPv4-адрес для HTTPS
Listen [2001:db8::cafe]:443 https # Ваш IPv6-адрес для HTTPS

Что это меняет?
После этой правки Apache будет принимать соединения **только** на указанных вами адресах. Он больше не будет "сидеть" на wildcard-адресе `[::]:443`. Теперь, когда браузер попытается подключиться к `[::]:443` (интерпретируя адрес `::` от Google), он получит отказ от сетевого стека, так как никакая служба на этом порту не слушает. Apache проигнорирует этот пакет. Браузер мгновенно и без ошибок откатится к IPv4 и успешно установит соединение с настоящим сервером Google.
Вывод и рекомендации
Описанная проблема — прекрасный пример того, как настройка в одной подсистеме (DNS) неожиданно взаимодействует с настройкой в другой (веб-сервер), приводя к сложным для диагностики последствиям.
Итоговые рекомендации:
  1. Всегда явно указывайте IP-адреса** в директивах `Listen` в Apache и других веб-серверах. Это повышает безопасность и предотвращает подобные конфликты.
  2. Для блокировки IPv6** на DNS-уровне предпочтительнее использовать возврат **пустого ответа** на `AAAA`-запросы, а не адреса `::`. В Dnsmasq это часто настраивается через `address=/example.com/` (без указания адреса) или с помощью более сложных правил.
  3. Проверяйте слушающие порты командой `ss -tulpn | grep :443`. Вы увидите, на каких конкретно адресах (`0.0.0.0`, `[::]` или конкретных IP) работают ваши службы.
Применение этих простых правил поможет избавиться от загадочных сетевых проблем и сделать вашу инфраструктуру более предсказуемой и надежной.
Администрирование Fedora Linux + настройка сети и прочая IT-Ботва


Для желающих поддержать
Карта SB: 5469 4009 6510 2267


Лучше ужасный конец, чем ужас без конца!
Аватар пользователя
ROOT
Администратор
 
Сообщений: 451
Зарегистрирован: 01 авг 2011, 09:36
Откуда: Моск. обл., г. Железнодорожный

Вернуться в Конфигурация WEB - сервисов

Кто сейчас на форуме

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 1

cron