Catch-All HTTPS Vhost no Apache 2.4

5

É possível configurar um Vhost HTTPS pega-tudo (padrão) no Apache 2.4? Eu atualmente tenho 4 domínios, e um HTTP pega tudo, mas assim que eu tento adicionar qualquer tipo de configuração meus outros vhosts quebram. Aqui está o que minha configuração parece:

<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>

Meu httpd.conf não tem mais DocumentRoot - tudo está no vhost e inclui. Este também é um servidor dedicado e IP.

Como posso resolver isso?

    
por Nicolas Bouvrette 09.04.2017 / 04:15

3 respostas

5

O problema foi resolvido, mas houve alguns mal-entendidos. Existe realmente o requisito de que HTTPS precisa de um certificado correspondente, mas o problema causado por isso é que a conexão não será confiável com o nome do host certificados correspondentes Nome comum ou listados em Nome alternativo do assunto :

  • A mesma incompatibilidade permanece igual à solução RewriteRule dada na outra resposta.

  • Se os nomes de host "pega-tudo" forem todos subdomínios de example.com e você tiver um certificado curinga para *.example.com , ele corresponderá.

  • Por outro lado, a maioria das pessoas, ao tentar acessar something.example.com , o digita na barra de endereço do navegador sem o prefixo http:// ou https:// e o padrão dos navegadores é HTTP. Portanto, ter um redirecionamento "catch-all" em HTTPS, mesmo com certificados incompatíveis, geralmente não causará problemas reais: apenas algumas pessoas já veem o erro SSL_ERROR_BAD_CERT_DOMAIN .

A Correspondência de host virtual funciona da mesma maneira com ou sem TLS.

Se você não tiver SNI :

The first name-based vhost in the configuration file for a given IP:port pair is significant because it is used for all requests received on that address and port for which no other vhost for that IP:port pair has a matching ServerName or ServerAlias. It is also used for all SSL connections if the server does not support Server Name Indication.

Sem o SNI , o certificado do primeiro VirtualHost é usado para handshake:

In reality, Apache will allow you to configure name-based SSL virtual hosts, but it will always use the configuration from the first-listed virtual host (on the selected IP address and port) to setup the encryption layer.

O principal problema com sua tentativa original era ter ServerAlias * e não ter ServerName . Para um host "pega-tudo", ele teria trabalhado com qualquer coisa, menos os outros ServerName s de outros VirtualHost s. Se não houver outra correspondência, o Apache volta para a seção VirtualHost padrão; seja qual for a primeira seção (que corresponde à pesquisa baseada em IP, quando a pesquisa baseada em nome falha).

Name-based virtual hosts for the best-matching set of <virtualhost>s are processed in the order they appear in the configuration. The first matching ServerName or ServerAlias is used, with no different precedence for wildcards (nor for ServerName vs. ServerAlias).

Deve haver ALGUMA ServerName porque:

The ServerName directive may appear anywhere within the definition of a server. However, each appearance overrides the previous appearance (within that server).

If no ServerName is specified, the server attempts to deduce the client visible hostname by first asking the operating system for the system hostname, and if that fails, performing a reverse lookup on an IP address present on the system.

Isso resultaria em configuração assim:

<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>

Por favor, observe as outras coisas que mudei:

  1. dev.example.com usa o mesmo certificado que faria sem o SNI.
  2. Use <VirtualHost *:443> em vez de _default_:443 como _default_ tem um propósito especial:

    Any vhost that includes the magic _default_ wildcard is given the same ServerName as the main server.

    (Isto também significa que você pode usar _default_:443 no seu "pega-tudo", não nos outros. Você pode tentar!)

  3. O domínio é substituído por Nomes de domínio de exemplo reservados .

  4. Eu prefiro ter www.example.com como parte do "pega-tudo" (em vez de um alias) para ter apenas um endereço canônico para o seu site. Por isso eu mudei para lá.

Se você tiver SNI , o processamento imita o mesmo comportamento, mas é um pouco diferente em detalhes:

Before there is even an SSL handshake, Apache finds the best match for the IP address and TCP port the connection is established on (IP-based virtual hosting)

If there is a NameVirtualHost directive that has the same literal arguments as this best-matching VirtualHost, Apache will instead consider ALL VirtualHost entries with identical arguments to the matched VirtualHost. Otherwise, SNI processing has no selection to perform.

If the client sends a hostname along with its TLS handshake request, Apache will compare this TLS hostname to the ServerName/ServerAlias of the candidate VirtualHost set determined in the preceding steps.

Whichever VirtualHost is selected on the preceding basis will have its SSL configuration used to continue the handshake. Notably, the contents of the certificates are not used in any comparison.

Com o SNI você pode ter o certificado adicional para dev.example.com .

Se todos os pré-requisitos para o SNI forem atendidos, ele deverá funcionar automaticamente e error.log mostrará [warn] Init: Name-based SSL virtual hosts only work for clients with TLS server name indication support (RFC 4366) .

    
por 09.04.2017 / 09:55
3

HTTPS requer um nome de domínio, que corresponde ao certificado, por isso, *:443 sem um ServerName correspondente não faz sentido.

No entanto, você pode usar um redirecionamento em suas outras entradas <VirtualHost> , com um 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]

Você deseja uma condição ( RewriteCond ) que verifique se apenas os domínios determinados são redirecionados conforme o esperado. Você deve saber todos os nomes possíveis, mas se você adicionar dinamicamente novos nomes de domínio, esperamos que você possa usar um regex que corresponda a todos esses subdomínios dinâmicos.

    
por 09.04.2017 / 05:04
3

Embora seja possível redirecionar todo o tráfego HTTPS desconhecido para um host virtual específico, o Apache não facilitou:

  1. Cada HTTPS VirtualHost precisa de um ServerName , que não temos para o host pega-tudo. Esse é um requisito do HTTPS, pois os certificados são normalmente associados a hosts ( ServerName ou ServerAlias ).
  2. O Apache usará o primeiro host virtual como padrão quando todo o resto falhar. Certifique-se de que você não tenha nenhuma outra configuração com o mesmo IP de porta configurado ou que seu pega-tudo falhará.
  3. Eu tinha erros de digitação na minha configuração original, o que provavelmente causou alguns loops redirecionados (eu tinha 2% deServerName em alguns VirtualHost). Eu adoraria entender um pouco mais os detalhes aqui, mas não é o foco da questão.

Com base nisso, existem duas soluções. Eu prefiro o primeiro, pois é provavelmente mais escalável (não há necessidade de exceções atualizadas) e também desempenho (não é necessário usar módulos extras).

Captura usando um ServerName falso (sugerido por Esa)

    #
    # 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 (sugerido por Alexis)

    <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>

Agora, por que algo tão simples é tão complexo? O Apache está mostrando sinais de idade? Pelo menos existem maneiras de resolver esta situação.

    
por 09.04.2017 / 06:28