Назад | Перейти на главную страницу

Catch-All HTTPS Vhost на Apache 2.4

Можно ли настроить универсальный (по умолчанию) HTTPS Vhost на Apache 2.4? В настоящее время у меня есть 4 домена, и HTTP перехватывает все, но как только я пытаюсь добавить какую-либо конфигурацию, мои другие vhosts ломаются. Вот как выглядит моя конфигурация:

<VirtualHost _default_:80>
    # Default catch-all virtual host.
    Redirect permanent / https://example-prod.com
</VirtualHost>

<VirtualHost _default_:80>
    ServerName example-prod.com
    ServerName www.example-prod.com
    Include conf/sites/example-prod.com.conf
</VirtualHost>

<VirtualHost _default_:80>
    ServerName example-dev.com
    Include conf/sites/example-dev.com.conf
</VirtualHost>

#
# This is the virtual host I'm missing and that I cannot get to work.
#
#<VirtualHost _default_:443>
#   # Default catch-all virtual host.
#   ServerAlias *
#   SSLEngine on
#   SSLCertificateFile "C:/prod/hosts.crt.pem"
#   SSLCertificateKeyFile "C:/prod/hosts.key.pem"
#   SSLCertificateChainFile "C:/prod/intermediate.crt.pem"  
#   Redirect permanent / https://example-prod.com
#</VirtualHost>

<VirtualHost _default_:443>
    ServerName example-prod.com
    ServerName www.example-prod.com
    SSLEngine on
    SSLCertificateFile "C:/prod/hosts.crt.pem"
    SSLCertificateKeyFile "C:/prod/hosts.key.pem"
    SSLCertificateChainFile "C:/prod/intermediate.crt.pem"  
    Include conf/sites/example-prod.com.conf
</VirtualHost>

<VirtualHost _default_:443>
    ServerName example-dev.com
    SSLEngine on
    SSLCertificateFile "C:/dev/hosts.crt.pem"
    SSLCertificateKeyFile "C:/dev/hosts.key.pem"
    SSLCertificateChainFile "C:/dev/intermediate.crt.pem"   
    Include conf/sites/example-dev.com.conf
</VirtualHost>

В моем httpd.conf больше нет DocumentRoot - все есть в хосте и включает. Это тоже выделенный сервер и IP.

Как я могу решить эту проблему?

Проблема была решена, но возникли недоразумения. Действительно существует требование, чтобы HTTPS нуждался в соответствующем сертификате, но проблема, вызванная этим, заключается в том, что соединение не будет доверенным, если имя хоста не соответствует сертификатам. Распространенное имя или перечислен в Альтернативное имя субъекта:

  • Такое же несоответствие сохраняется даже с RewriteRule решение, данное в другом ответе.

  • Если "универсальные" имена хостов являются субдоменами example.com и у вас есть подстановочный сертификат для *.example.com, он будет соответствовать.

  • С другой стороны, большинство людей, пытаясь получить доступ something.example.com, вводит его в адресную строку браузера без http:// или https:// префикс, а браузеры по умолчанию используют HTTP. Таким образом, наличие "всеобъемлющего" перенаправления на HTTPS даже с несоответствующим сертификатом обычно не вызывает реальных проблем: лишь немногие люди когда-либо видят SSL_ERROR_BAD_CERT_DOMAIN ошибка.

В Сопоставление виртуального хоста работает одинаково с TLS или без него.

Если у вас нет SNI:

Первый виртуальный хост на основе имени в файле конфигурации для данного IP:port пара важна, потому что она используется для всех запросов, полученных на этот адрес и порт, для которых нет другого виртуального хоста для этого IP:port пара имеет соответствие ServerName или ServerAlias. Он также используется для всех SSL-соединений, если сервер не поддерживает Индикация имени сервера.

Без SNI сертификат с первого VirtualHost используется для рукопожатия:

На самом деле Apache позволит вам настраивать виртуальные хосты SSL на основе имен, но он всегда будет использовать конфигурацию виртуального хоста, указанного первым (на выбранном IP-адресе и порту), для настройки уровня шифрования.

Основная проблема с вашей первоначальной попыткой заключалась в том, что ServerAlias * и не имея никаких ServerName. Для универсального хоста он работал бы с все, кроме другой ServerNames от других VirtualHostс. Если другого совпадения нет, Apache возвращается к значениям по умолчанию VirtualHost раздел; в зависимости от того, какой из разделов является первым (который соответствует поиску по IP, когда поиск по имени не выполняется).

Виртуальные хосты на основе имен для наиболее подходящего набора <virtualhost>s обрабатываются в том порядке, в котором они появляются в конфигурации. Первое совпадение ServerName или ServerAlias используется без разницы в приоритете для подстановочных знаков (ни для ServerName vs. ServerAlias).

Должно быть НЕКОТОРЫЕ ServerName так как:

В ServerName Директива может появиться где угодно в определении сервера. Однако каждый внешний вид отменяет предыдущий внешний вид (на этом сервере).

Если нет ServerName указано, сервер пытается определить видимое клиентом имя хоста, сначала запрашивая у операционной системы имя хоста системы, а если это не удается, выполняет обратный поиск по IP-адресу, присутствующему в системе.

Это приведет к такой конфигурации:

<VirtualHost *:443>
    # Default catch-all (everything that won't match the following VirtualHosts)
    ServerName catch-all.example.com
    ServerAlias www.example.com
    SSLEngine on
    SSLCertificateFile "C:/prod/hosts.crt.pem"
    SSLCertificateKeyFile "C:/prod/hosts.key.pem"
    SSLCertificateChainFile "C:/prod/intermediate.crt.pem"  
    Redirect permanent / https://example.com
</VirtualHost>

<VirtualHost *:443>
    ServerName example.com
    SSLEngine on
    SSLCertificateFile "C:/prod/hosts.crt.pem"
    SSLCertificateKeyFile "C:/prod/hosts.key.pem"
    SSLCertificateChainFile "C:/prod/intermediate.crt.pem"  
    Include conf/sites/example.com.conf
</VirtualHost>

<VirtualHost *:443>
    ServerName dev.example.com
    SSLEngine on
    SSLCertificateFile "C:/prod/hosts.crt.pem"
    SSLCertificateKeyFile "C:/prod/hosts.key.pem"
    SSLCertificateChainFile "C:/prod/intermediate.crt.pem"  
    Include conf/sites/dev.example.com.conf
</VirtualHost>

Обратите внимание на другие вещи, которые я изменил:

  1. dev.example.com использует тот же сертификат, что и без SNI.
  2. Использовать <VirtualHost *:443> вместо того _default_:443 так как _default_ имеет специальное назначение:

    Любой vhost, включающий магию _default_ подстановочному знаку дается то же имя сервера, что и у основного сервера.

    (Это также означает, что можно использовать _default_:443 в вашем «уловке», а не в других. Можешь попробовать!)

  3. Домен заменяется на Зарезервированные примеры доменных имен.

  4. Я бы предпочел иметь www.example.com как часть "всеобъемлющего" (а не как псевдоним), чтобы иметь только один канонический адрес для вашего сайта. Поэтому я перенес его туда.


Если у тебя есть SNI, обработка имитирует то же поведение, но немного отличается в деталях:

Прежде чем произойдет даже подтверждение SSL, Apache находит наилучшее соответствие для IP-адреса и TCP-порта, на котором установлено соединение (виртуальный хостинг на основе IP)

Если есть NameVirtualHost директива, имеющая те же буквальные аргументы, что и эта наиболее подходящая VirtualHost, Apache вместо этого будет рассматривать ВСЕ VirtualHost записи с идентичными аргументами для сопоставленного VirtualHost. В противном случае обработка SNI не требует выбора.

Если клиент отправляет имя хоста вместе со своим запросом подтверждения TLS, Apache сравнивает это имя хоста TLS с ServerName/ServerAlias кандидата VirtualHost набор, определенный на предыдущих шагах.

Какой бы VirtualHost ни был выбран на предыдущей основе, будет использоваться его SSL-конфигурация для продолжения подтверждения. Примечательно, что содержание сертификатов не используется ни для какого сравнения.

С SNI вы можете получить дополнительный сертификат для dev.example.com.

Если все предпосылки для SNI соблюдены, он должен работать автоматически и error.log покажет [warn] Init: Name-based SSL virtual hosts only work for clients with TLS server name indication support (RFC 4366).

Хотя можно перенаправить весь неизвестный трафик HTTPS на определенный виртуальный хост, Apache не упростил эту задачу:

  1. Каждому HTTPS VirtualHost требуется ServerName, которого у нас нет для универсального хоста. Это требование HTTPS, поскольку сертификаты обычно связаны с хостами (ServerName или ServerAlias).
  2. Apache возьмет на себя первый виртуальный хост по умолчанию один, когда все остальное терпит неудачу. Убедитесь, что у вас нет другой конфигурации с таким же настроенным IP-адресом порта, в противном случае у вас не будет работать комплексная проверка.
  3. У меня были опечатки в исходной конфигурации, которые, вероятно, вызывали некоторые перенаправленные циклы (у меня было 2 ServerName заявления в некотором VirtualHost). Я хотел бы немного подробнее разобраться в деталях, но вопрос не в этом.

Исходя из этого, есть два решения. Я предпочитаю первый, поскольку он, вероятно, более масштабируемый (нет необходимости обновлять исключения), а также производительность (нет необходимости использовать дополнительные модули).

Поймать все с использованием поддельного ServerName (предложено Эсой)

    #
    # Catch-all virtual hosts.
    #
    <VirtualHost _default_:80>
        # Default catch-all virtual host.
        Redirect permanent / https://example-prod.com 
    </VirtualHost>

    <VirtualHost _default_:443>
        ServerName catch-all
        SSLEngine on
        SSLCertificateFile "C:/dev/hosts.crt.pem"
        SSLCertificateKeyFile "C:/dev/hosts.key.pem"
        SSLCertificateChainFile "C:/dev/intermediate.crt.pem"   
        Redirect permanent / https://example-prod.com 
    </VirtualHost>

    #
    # Real virtual hosts.
    #
    <VirtualHost _default_:80>
        ServerName example-prod.com
        ServerAlias www.example-prod.com
        Include conf/sites/example-prod.com.conf 
    </VirtualHost>

    <VirtualHost _default_:80>
        ServerName example-dev.com
        Include conf/sites/example-dev.com.conf 
    </VirtualHost>

    <VirtualHost _default_:443>
        ServerName example-prod.com
        ServerAlias www.example-prod.com
        SSLEngine on
        SSLCertificateFile "C:/prod/hosts.crt.pem"
        SSLCertificateKeyFile "C:/prod/hosts.key.pem"
        SSLCertificateChainFile "C:/prod/intermediate.crt.pem"  
        Include conf/sites/example-prod.com.conf
    </VirtualHost>

    <VirtualHost _default_:443>
        ServerName example-dev.com
        SSLEngine on
        SSLCertificateFile "C:/dev/hosts.crt.pem"
        SSLCertificateKeyFile "C:/dev/hosts.key.pem"
        SSLCertificateChainFile "C:/dev/intermediate.crt.pem"   
        Include conf/sites/example-dev.com.conf 
    </VirtualHost>

mod_rewrite (предложено Алексисом)

    <VirtualHost _default_:80>
        # Default catch-all virtual host.
        Redirect permanent / https://example-prod.com 
    </VirtualHost>

    <VirtualHost _default_:80>
        ServerName example-prod.com
        ServerName www.example-prod.com
        Include conf/sites/example-prod.com.conf 
    </VirtualHost>

    <VirtualHost _default_:80>
        ServerName example-dev.com
        Include conf/sites/example-dev.com.conf 
    </VirtualHost>

    <VirtualHost _default_:443>
        ServerName example-prod.com
        ServerName www.example-prod.com
        SSLEngine on
        SSLCertificateFile "C:/prod/hosts.crt.pem"
        SSLCertificateKeyFile "C:/prod/hosts.key.pem"
        SSLCertificateChainFile "C:/prod/intermediate.crt.pem"  
        Include conf/sites/example-prod.com.conf
        # Default catch-all HTTPS virtual host.
        # Make sure to add all valid SSL domains on this host to avoid conflicts.
        RewriteEngine on
        RewriteCond %{HTTP_HOST} !^example-prod\.com$ [NC]
        RewriteCond %{HTTP_HOST} !^www\.example-prod\.com$ [NC]
        RewriteCond %{HTTP_HOST} !^example-dev\.com$ [NC]
        RewriteRule .* https://example-prod [R=permanent,L]  
    </VirtualHost>

    <VirtualHost _default_:443>
        ServerName example-dev.com
        SSLEngine on
        SSLCertificateFile "C:/dev/hosts.crt.pem"
        SSLCertificateKeyFile "C:/dev/hosts.key.pem"
        SSLCertificateChainFile "C:/dev/intermediate.crt.pem"   
        Include conf/sites/example-dev.com.conf 
    </VirtualHost>

Теперь почему что-то настолько простое и такое сложное? Есть ли у Apache признаки возраста? По крайней мере, есть способы решить эту ситуацию.

HTTPS требует доменного имени, которое соответствует сертификату, поэтому *:443 без соответствующего ServerName не имеет смысла.

Однако вы можете использовать перенаправление в другом <VirtualHost> записи, с RewriteRule.

    RewriteEngine on
    RewriteCond %{HTTP_HOST} ^(something-else.example-prod.com|whatever.example-prod.com|...others...)$
    RewriteRule ^/(.*) https://www.example-prod.com/$1 [R=permanent,L]

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