【问题标题】:Convince Apache of the original client protocol让 Apache 相信原始客户端协议
【发布时间】:2013-06-30 15:09:51
【问题描述】:

所以我有一个使用 SSL 卸载和 HTTP 负载平衡器的相对简单的服务器堆栈。设置如下所示:

(client) -> (SSL offload - stud) -> (balancer - haproxy) -> (http server - apache)

我的问题不是要解决所有这些问题。它工作得很好,老实说,我对设置它的直接程度感到震惊。我还将添加 HTTP 客户端直接连接到 haproxy,从而绕过 SSL 卸载。 为了记录,上述每个部分都有多余的合作伙伴。

这个问题有点抽象。我将从演示开始。客户端通过设置向https://myserver.tld/scp 发出请求(删除了一些标头以保持清晰)

GET /scp HTTP/1.1
Host: myserver.tld
(headers added by haproxy)
X-Forwarded-For: [original::client:ip]
X-Forwarded-Proto: https

服务器响应

HTTP/1.1 301 Moved Permanently
Date: Wed, 03 Jul 2013 03:16:25 GMT
Server: Apache/2.2.15 (CentOS)
Location: http://myserver.tld/scp/
Content-Length: 344
Content-Type: text/html; charset=iso-8859-1

因此 Apache mod_dir 正在发送重定向到带有尾部斜杠的相同 URL。这样做是正确的。那不是问题。问题是 HTTPS 协议丢失了。同样,我认为 Apache 重定向到 HTTP URL 是正确的,毕竟它从上述堆栈接收到的连接是常规 HTTP 连接。

所以从 Apache 的角度来看,客户端请求了一个常规的 HTTP 连接。 HTTPS 标志以及所有其他 SSL 信息都处于关闭状态,因为会话的 SSL 部分由 stud 处理。

虽然(在 Apache 内部)我有一个有效的 X-Forwarded-Proto 标头,但我无法告诉 Apache 原始客户端连接是 HTTPS 并且由 mod_dir 重定向的目录斜杠应该使用 https:// 协议。我错过了什么?

我想出的唯一方法是重写 haproxy 中的 Location 标头以用于 HTTPS 转发连接,以将 http:// 替换为 https://,但我真的不认为这种方法非常优雅。我更希望 Apache(以及(不要伤害我)PHP 更进一步)被告知并将连接视为正常的 HTTPS 连接。

请帮忙!

PS - 我之前听说过,如果你不得不问,那你就做错了。也许这就是这里的根本问题,但这似乎是一个如此简单的困境,我觉得我是唯一一个来到这里的人。

【问题讨论】:

    标签: apache ssl haproxy


    【解决方案1】:

    您可以通过在 ServerName 指令中包含协议来做到这一点:

    ServerName https://my-server-name
    

    根据 Apache docs:

    有时,服务器在处理 SSL 的设备后面运行,例如反向代理、负载平衡器或 SSL 卸载设备。在这种情况下,请在 ServerName 指令中指定客户端连接的 https:// 方案和端口号,以确保服务器生成正确的自引用 URL。

    【讨论】:

    • 如果您希望应用程序有多个入口点,其中一个是 https://,另一个是 http://,则此方法不起作用。 mod_dir 将始终使用 ServerName 指令中指定的方案。在某些情况下,生产 URL 将具有 https://,但内部 URL(用于测试等)可能具有 http,尤其是在您想要直接测试/监控 Apache 实例的 SSL/TLS 卸载情况下。现在,“非规范”URL 是否无法正确生成重定向可能无关紧要,但它很混乱:/
    • @Tom17 在这种情况下,您可以为 http 和 https 站点创建单独的 VirtualHosts,除了端口和 ServerName 之外具有相同的设置。
    【解决方案2】:
    1. 目录斜线重定向:你想要的是mod_rewrite

      RewriteEngine On
      RewriteCond %{HTTP:X-Forwarded-Proto} =https
      RewriteCond %{REQUEST_FILENAME} -d
      RewriteRule ^(.+[^/])$          https://www.example.com/$1/  [R=301,L,QSA]
      

      如果标头设置为 https 并且请求的文件名是目录 (-d),则重写该目录(将 example.com 替换为您自己的域)。

    2. 至于让PHP把连接当作普通的HTTPS连接,设置环境变量HTTPSon

      SetEnvIf X-Forwarded-Proto https HTTPS=on
      

    【讨论】:

    • 我必须调整建议 1 以使其正常工作以供我使用。首先,我将第二个 RewriteCond 切换为:RewriteCond %{LA-U:REQUEST_FILENAME} -d 以使匹配正常工作(基于Apache's docs,特别是关于“基于 URL 的前瞻”的注释。我还调整了重写本身以动态包含主机名, 并避免重复的 '/': RewriteRule ^(.+[^/])$ https://%{HTTP_HOST}$1/ [R=301,L,QSA] $1 var 本身提供了初始斜线,因此在重写 URL 中不需要它。
    • Apache 2.4 需要 RewriteOptions AllowNoSlash。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-07-27
    • 2012-09-25
    • 2021-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多