简体   繁体   English

带和不带 SSL 的 Apache 反向代理之间行为不同的原因

[英]Reasons for differing behavior between Apache reverse proxy with and without SSL

I've been working on a local reverse proxy that routes traffic between two local Apache installations (each running a different version of mod_wsgi, which is the reason for the bifurcation).我一直在研究一个本地反向代理,它在两个本地 Apache 安装之间路由流量(每个都运行不同版本的 mod_wsgi,这是分叉的原因)。 I want this reverse proxy to work whether the requests are HTTP or HTTPS.我希望这个反向代理能够工作,无论请求是 HTTP 还是 HTTPS。

However, when using SSL, the Location response header isn't being modified (properly) by ProxyPassReverse.但是,当使用 SSL 时,ProxyPassReverse 不会(正确地)修改位置响应 header。

Below are the VirtualHost definitions for HTTP and HTTPS traffic, respectively:下面分别是 HTTP 和 HTTPS 流量的 VirtualHost 定义:

<VirtualHost *:80>
        # Proxy traffic for Version 6 with an alias of: 6x/
        ProxyPass /6x/ http://localhost:10090/
        ProxyPassReverse /6x/ http://localhost:10090/

        # Proxy traffic for previous versions with aliases of: 5x/, 4x/, and /
        ProxyPass /5x/ http://localhost:10080/
        ProxyPassReverse /5x/ http://localhost:10080/
        ProxyPass /4x/ http://localhost:10080/
        ProxyPassReverse /4x/ http://localhost:10080/
        ProxyPass / http://localhost:10080/
        ProxyPassReverse / http://localhost:10080/
</VirtualHost>
<IfModule mod_ssl.c>
        <VirtualHost *:443>
                ServerName snakeoil.us.com

                ProxyPreserveHost on
                ProxyRequests off
                SSLEngine on
                SSLProxyEngine on
                SSLProxyVerify none
                SSLProxyCheckPeerCN off
                SSLProxyCheckPeerName off
                SSLProxyCheckPeerExpire off

                SSLCertificateFile /etc/ssl/certs/snakeoil.crt
                SSLCertificateKeyFile /etc/ssl/certs/snakeoil.key
                SSLCertificateChainFile /etc/ssl/certs/bundle-client.crt

                # Proxy traffic for Version 6 with an alias of: 6x/
                ProxyPass /6x/ https://localhost:10453/
                ProxyPassReverse /6x/ https://localhost:10453/

                # Proxy traffic for previous versions with aliases of: 5x/, 4x/, and /
                ProxyPass /5x/ https://localhost:10443/
                ProxyPassReverse /5x/ https://localhost:10443/
                ProxyPass /4x/ https://localhost:10443/
                ProxyPassReverse /4x/ https://localhost:10443/
                ProxyPass / https://localhost:10443/
                ProxyPassReverse / https://localhost:10443/
        </VirtualHost>
</IfModule>

When I access the url http://snakeoil.us.com/6x/snk610/index , the location header comes back as: Location: http://snakeoil.us.com/6x/snk610/index . When I access the url http://snakeoil.us.com/6x/snk610/index , the location header comes back as: Location: http://snakeoil.us.com/6x/snk610/index .

However, when I access the url https://snakeoil.us.com/6x/snk610/index , the location header comes back as: Location: https://snakeoil.us.com/snk610/index , which results in a 404 since only one of the two local Apache instances (the one associated with the 6x route) being proxied recognizes the snk610 alias (and it isn't the instance being routed to in this case). However, when I access the url https://snakeoil.us.com/6x/snk610/index , the location header comes back as: Location: https://snakeoil.us.com/snk610/index , which results in a 404,因为被代理的两个本地 Apache 实例(与 6x 路由关联的实例)中只有一个识别snk610别名(在这种情况下,它不是路由到的实例)。

The bottom line is that the HTTP VirtualHost definition proxies requests between the two local Apache instances without fail.底线是 HTTP VirtualHost 定义代理两个本地 Apache 实例之间的请求不会失败。 However, the HTTPS VirtualHost definition does not and it isn't clear to me what causes this discrepancy.但是,HTTPS VirtualHost 定义没有,我不清楚导致这种差异的原因。

Managed to find the solution.设法找到解决方案。 In retrospect, it should have been more obvious.回想起来,它应该更明显。

On the Apache instances being proxied to, I changed the access_log format to be the following:在代理到的 Apache 实例上,我将 access_log 格式更改为以下内容:

LogFormat "%h %l %u %t \"%r\" %>s %b   -->   ResponseLocation: '%{Location}o'" common

This causes the outgoing response location to be logged.这会导致记录传出响应位置。

Here is the output from the Apache HTTP instance (being proxied to):这是来自 Apache HTTP 实例(被代理)的 output:

[snake6x@test1 httpd6x]$ grep "ResponseLocation: 'http" logs/access_log
::1 - - [06/May/2020:15:43:25 -0400] "GET /snk610 HTTP/1.1" 301 233   -->   ResponseLocation: 'http://localhost:10090/snk610/index'
::1 - - [06/May/2020:15:43:30 -0400] "GET /snk610/index HTTP/1.1" 302 247   -->   ResponseLocation: 'http://localhost:10090/snk610/login?params=&message=&redirect_to=index'
::1 - - [06/May/2020:15:43:32 -0400] "POST /snk610/auth?redirect_to=index&params= HTTP/1.1" 302 204   -->   ResponseLocation: 'http://localhost:10090/snk610/index'

From the above, you can see that the response location header looks as expected, ie ProxyPassReverse should be able to successfully make its replacement.从上面可以看出,响应位置 header 看起来和预期的一样,即 ProxyPassReverse 应该可以成功替换它。

Conversely, here is the output from the Apache HTTPS instance (being proxied to):相反,这里是 output 来自 Apache HTTPS 实例(被代理到):

[snake6x@test1 httpd]$ grep "ResponseLocation: 'http" logs/ssl_request_log
[06/May/2020:19:53:38 +0000] ::1 "GET /snk610 HTTP/1.1" 240 2645788   -->   ResponseLocation: 'https://snakeoil.us.com/snk610/index'
[06/May/2020:19:56:21 +0000] ::1 "GET /snk610/index HTTP/1.1" 254 2682899   -->   ResponseLocation: 'https://snakeoil.us.com/snk610/login?params=&message=&redirect_to=index'
[06/May/2020:19:56:23 +0000] ::1 "POST /snk610/auth?redirect_to=index&params= HTTP/1.1" 240 752392   -->   ResponseLocation: 'https://snakeoil.us.com/snk610/index'

From the above, you can see that the server name has been substituted for the incoming host name in the response location header.从上面可以看出,在响应位置 header 中,服务器名称已替换为传入的主机名称。 This is what was causing ProxyPassReverse to fail to replace outgoing hostname (on the reverse proxy server).这就是导致 ProxyPassReverse 无法替换传出主机名(在反向代理服务器上)的原因。

I resolved this problem by explicitly updating the outgoing location header on the server being proxied to:我通过显式更新被代理到的服务器上的传出位置 header 解决了这个问题:

    # Since this server has a proxy immediately in front of it, we need the outgoing
    # location to match the incoming location. However, the ServerName tag will
    # cause the incoming location to be changed to include the ServerName, which will
    # cause the upstream ProxyPassReverse to fail to update the outgoing location
    # properly.
    #
    # This Header modification replaces the outgoing ServerName with the incoming
    # name.
    #
    # FIXME: There is surely a better way to do this with a variable that contains
    # the incoming host
    Header edit Location ^https://snakeoil.us.com:443 https://localhost:10453
    Header edit Location ^https://snakeoil.us.com https://localhost:10453

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM