Apache 2.4 上的 Catch-All HTTPS Vhost

Apache 2.4 上的 Catch-All HTTPS Vhost

是否可以在 Apache 2.4 上配置一个 catch-all(默认)HTTPS Vhost?我目前有 4 个域和一个 HTTP catch-all,但只要我尝试添加任何类型的配置,我的其他 vhost 就会中断。我的配置如下:

<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- 所有内容都在 vhost 中,并且包括。这也是一个专用服务器和 IP。

我该如何解决这个问题?

答案1

问题解决了,但是存在一些误解。HTTPS 确实需要匹配的证书,但由此导致的问题是,如果主机名不匹配证书,则连接将不受信任通用名称或列于主题备用名称

  • RewriteRule即使采用其他答案中给出的解决方案,仍然存在同样的不匹配情况。

  • 如果“catch-all”主机名都是的子域,example.com并且您拥有的通配符证书*.example.com,则它将匹配。

  • 另一方面,大多数人在尝试访问时something.example.com,都会在浏览器地址栏中输入不带http://https://前缀的地址,而浏览器默认使用 HTTP。因此,即使证书不匹配,在 HTTPS 上使用“catch-all”重定向通常也不会导致任何实际问题:只有少数人会看到该SSL_ERROR_BAD_CERT_DOMAIN错误。

虚拟主机匹配无论有没有 TLS,工作方式都相同。

如果你没有信噪比

配置文件中给定对的第一个基于名称的虚拟主机 IP:port非常重要,因为它用于接收该地址和端口上的所有请求,而该对中没有其他虚拟主机 IP:port具有匹配的ServerName或者ServerAlias. 如果服务器不支持,它也用于所有 SSL 连接 服务器名称指示

没有 SNI第一个证书VirtualHost用于握手:

实际上,Apache 将允许您配置基于名称的 SSL 虚拟主机,但它将始终使用第一个列出的虚拟主机(在选定的 IP 地址和端口上)的配置来设置加密层。

您最初尝试的主要问题是有ServerAlias *和没有ServerName。对于“万能”主机,它应该与无论如何其他ServerNames 与其他VirtualHosts 匹配。如果没有其他匹配项,Apache 将返回到默认VirtualHost部分;以第一个部分为准(当基于名称的查找失败时,该部分与基于 IP 的查找匹配)。

最佳匹配集的基于名称的虚拟主机<virtualhost>将按照其在配置中出现的顺序进行处理。第一个匹配ServerNameServerAlias被使用,通配符的优先级没有不同(与 一样ServerNameServerAlias

一定有一些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_有一个特殊用途:

    任何包含魔法_default_通配符的虚拟主机都会被赋予与主服务器相同的 ServerName。

    (这也意味着可以_default_:443在您的“catch-all”中使用,而不能在其他中使用。您可以尝试!)

  3. 域名已替换为预留示例域名

  4. 我更愿意将其www.example.com作为“catch-all”的一部分(而不是作为别名),以便拥有只有一个规范地址您的网站。因此我已将其移至那里。


如果你有信噪比,处理模仿相同的行为,但在细节上略有不同:

在 SSL 握手之前,Apache 就会找到建立连接的 IP 地址和 TCP 端口的最佳匹配(基于 IP 的虚拟托管)

如果存在NameVirtualHost与此最佳匹配项具有相同文字参数的指令VirtualHost,Apache 将改为考虑VirtualHost与匹配的 VirtualHost 具有相同参数的所有条目。否则,SNI 处理将无法执行任何选择。

如果客户端在 TLS 握手请求中发送了主机名,Apache 会将此 TLS 主机名与前面步骤中确定的候选集的ServerName/进行比较。ServerAliasVirtualHost

无论根据上述原则选择哪个 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)

答案2

虽然可以将所有未知的 HTTPS 流量重定向到特定的虚拟主机,但 Apache 并没有让这一切变得简单:

  1. 每个 HTTPS VirtualHost 都需要一个ServerName,而对于 catch-all 主机,我们没有这个。这是 HTTPS 的要求,因为证书通常与主机(ServerNameServerAlias)相关联。
  2. Apache 将接管第一个虚拟主机作为默认当所有其他配置都失败时,请使用一个。请确保您没有配置具有相同端口 IP 的任何其他配置,否则您的 catch-all 将失败。
  3. 我的原始配置中有拼写错误,这可能导致了一些重定向循环(我ServerName在某些 VirtualHost 中有 2 条语句)。我很想进一步了解这里的细节,但这不是问题的重点。

基于此,有两种解决方案。我更喜欢第一种,因为它可能更具可扩展性(不需要更新异常)并且性能更好(不需要使用额外的模块)。

使用虚假的 ServerName 进行捕获(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(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>

那么为什么如此简单的事情却如此复杂?Apache 是否显露出老化的迹象?至少有办法解决这种情况。

答案3

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),用于检查是否只有给定的域按预期进行重定向。您应该知道所有可能的名称,但如果您动态添加新域名,希望您可以使用与所有这些动态子域匹配的正则表达式。

相关内容