在Apache 2.4上捕获所有HTTPS Vhost

是否可以在Apache 2.4上configuration全部(默认)HTTPS Vhost? 我目前有4个域名和一个HTTP捕获所有,但只要我尝试添加任何types的configuration我的其他虚拟主机破坏。 这是我的configuration看起来像:

<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解决scheme,同样的不匹配仍然存在。

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

  • 另一方面,大多数人在尝试访问something.example.com ,将其input浏览器地址栏,而不使用http://https://前缀,而浏览器默认使用HTTP。 因此,即使证书不匹配,在HTTPS上进行“全面覆盖”redirect通常也不会导致任何实际问题:只有less数人看到SSL_ERROR_BAD_CERT_DOMAIN错误。

无论是否使用TLS, 虚拟主机匹配的工作方式都是相同的。

如果你没有SNI :

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

在没有SNI的情况下,第一个VirtualHost的证书用于握手:

实际上,Apache将允许您configuration基于名称的SSL虚拟主机,但它将始终使用首先列出的虚拟主机(在所选IP地址和端口上)的configuration来设置encryption层。

你最初的尝试的主要问题是有ServerAlias *而没有任何ServerName 。 对于一个“ 无所不能 ”的主机来说, 除了来自其他VirtualHost主机的其他ServerName ,其他任何东西都可以使用。 如果没有其他匹配,Apache会回退到默认的VirtualHost部分; 第一部分(匹配基于IP的查找,基于名称的查找失败时)。

最匹配的<virtualhost>集合的基于名称的虚拟主机按照它们在configuration中出现的顺序进行处理。 使用第一个匹配的ServerNameServerAlias ,对于通配符没有不同的优先级( ServerNameServerAlias )。

必须有SOME ServerName因为:

ServerName指令可能出现在服务器定义的任何地方。 但是,每个外观都会覆盖之前的外观(在该服务器内)。

如果未指定ServerName ,则服务器首先向操作系统请求系统主机名,然后尝试推断客户端可见主机名,如果失败,则对系统上存在的IP地址执行反向查找。

这会导致像这样的configuration:

 <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 ,而不是在其他的中,你可以试试!)

  3. 域被replace为保留示例域名 。

  4. 为了让您的网站只有一个规范地址 ,我宁愿将www.example.com作为“全部”(而不是别名)的一部分。 所以我把它移到了那里。


如果你有SNI ,处理过程会模仿相同的行为,但在细节上稍有不同:

在甚至有一个SSL握手之前,Apachefind最佳匹配的IP地址和build立连接的TCP端口(基于IP的虚拟主机)

如果有一个与此最匹配的VirtualHost具有相同文字参数的NameVirtualHost指令,则Apache将会考虑具有与匹配的VirtualHost相同参数的所有VirtualHost条目。 否则,SNI处理没有select执行。

如果客户端发送主机名及其TLS握手请求,则Apache会将此TLS主机名与前述步骤中确定的候选VirtualHost集的ServerName / ServerAlias进行比较。

无论先前select哪个VirtualHost,都将使用其SSLconfiguration继续握手。 值得注意的是,证书的内容并没有用于比较。

通过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需要一个与证书相匹配的域名,所以*:443没有相应的ServerName是没有意义的。

但是,您可以使用RewriteRule在其他<VirtualHost>条目中使用redirect。

  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 )来检查只有给定的域如预期的那样得到redirect。 您应该知道所有可能的名称,但是如果您dynamic地添加新的域名,希望您可以使用匹配所有这些dynamic子域的正则expression式。

虽然可以将所有未知的HTTPSstream量redirect到特定的虚拟主机,但是Apache并不容易:

  1. 每个HTTPS VirtualHost都需要一个ServerName ,这是我们没有的全能主机。 这是HTTPS的要求,因为证书通常与主机( ServerNameServerAlias )相关联。
  2. 当一切都失败时,Apache将把第一台虚拟主机作为默认主机 。 确保没有configuration相同端口IP的任何其他configuration,否则捕获将失败。
  3. 我在我的原始configuration中可能导致一些redirect循环(我有一些VirtualHost中有2个ServerName语句)的拼写错误。 我想了解更多的细节,但这不是问题的重点。

基于此,有两种解决scheme。 我更喜欢第一个,因为它可能更具可扩展性(不需要更新exception),而且性能更高(不需要使用额外的模块)。

Catch-all使用假的ServerName (由Esabuild议)

  # # 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 (由Alexisbuild议)

  <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是​​否显示年龄的迹象? 至less有办法解决这种情况。