Redirecting http traffic to https in Apache withou

2019-09-15 02:36发布

问题:

I am trying a pretty simple thing that I would normally do using a RewriteRule (and which works perfectly when I do).

My goal: Force certain (not all) URLs to use the https protocol.

Caveats:

  • Apache is behind a crypto-offloader. We would like a single virtual host that deals with both the unencrypted and the decrypted traffic. The offloader forwards all traffic to the same port and sets the X-Forwarded-Proto header. (It also sets the new Forwarded header for future compatibility, but I will leave that out below for brevity.)
  • Everything I have read lately suggests to use Redirect or RedirectMatch instead of RewriteRule. Accordingly, I would like to avoid RewriteRule if possible.
  • The URLs I wish to force are reverse proxies that I have configured in a <LocationMatch> section within my virtual host.
  • The virtual host has multiple Alias entries that all correspond to valid names that are included in the TLS certificate (i.e. I don't want/need to force users to use the canonical name)
  • It seems that Redirect and RedirectMatch are unable to make use of the %{HTTP_HOST} server variable. This means I would need extra logic to have a different RedirectMatch line depending on the host that was use by the client (Messy).
  • In the event that I NEED to use mod_rewrite for this, it would be good to have the RewriteRule inside the <LocationMatch> section to keep the configuration for that path/proxy in one place. However this also seems to cause problems.

Time for some example code to help explain this. All of the following examples reside within a VirtualHost section as follows:

<VirtualHost 192.168.0.1:80>
    ServerName www.example.com
    ServerAlias example.com
    ServerAlias anothervalidalias.com

Option 1 - Using RedirectMatch. This works but forces the hostname.

<LocationMatch "/backendcontextroot">
    ProxyPreserveHost on
    ProxyPass "http://192.168.0.2:8080/backendcontextroot"
    <If "-z req('X-Forwarded-Proto')">
        RedirectMatch ^(.*) https://www.example.com/$1
    </If>
</LocationMatch>

Option 2 - Using RedirectMatch. This would be my ideal solution, but the HTTP_HOST does not work. Apache sends no response back to the client.

<LocationMatch "/backendcontextroot">
    ProxyPreserveHost on
    ProxyPass "http://192.168.0.2:8080/backendcontextroot"
    <If "-z req('X-Forwarded-Proto')">
        RedirectMatch ^(.*) https://%{HTTP_HOST}/$1
    </If>
</LocationMatch>

Option 3 - Using RewriteRule within a LocationMatch block. If I really cannot use Redirect and am forced to use RewriteRule then this would be my preferred method as it keeps the configuration for the /backendcontextroot reverse proxy together in one place. However, this produces some 'funky' redirects...

<LocationMatch "/backendcontextroot">
    ProxyPreserveHost on
    ProxyPass "http://192.168.0.2:8080/backendcontextroot"
    RewriteEngine on
    RewriteCond %{HTTP:X-Forwarded-Proto} !https
    RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1
</LocationMatch>

This redirects to: https://www.example.com/proxy:http://192.168.0.2:8080/backendcontextroot

Option 4 - Using RewriteRule outside the LocationMatch block. This works, but the configuration for this reverse proxy is then 'spread around'. It also uses RewriteRule despite suggestions that I should avoid it if possible.

RewriteEngine on
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteCond %{REQUEST_URI} ^/backendcontextroot
RewriteRule ^(.*)$ https://%{HTTP_HOST}/$1

<LocationMatch "/backendcontextroot">
    ProxyPreserveHost on
    ProxyPass "http://192.168.0.2:8080/backendcontextroot"
</LocationMatch>

Am I stuck using option 4 or is there a more elegant way?

回答1:

RedirectMatch is only good for relatively indiscriminate redirects, which is why it meets the needs of most. Keep your crypto proxy clean and either add a header to the back-end request denoting the connection was encrypted (not sure how to do that), or connect to a different port, e.g. 8443 that you would add to the same Listen or <VirtualHost> that has the 8080 in the back-end. The back-end would still not use an encrypted connection but would know whether the the connection is "secure" by checking headers or port. These checks can be performed by the back-end – quite easily – in either RewriteConds via %{HTTP:X-Your-Https-Header} or %{SERVER_PORT}, or in the site's scripting language and have redirects sent from it, since only it knows which URLs require redirects. It can send out the redirect complete with https: since that's what the client needs to request, and your back-end will get the new request proxied with the correct header/port.

Edit: example of configuration on crypto server

<LocationMatch "/backendcontextroot">
    ProxyPreserveHost on
    <If "%{HTTPS} == 'on'">
        ProxyPass "http://192.168.0.2:8443/backendcontextroot"
    <Else>
        ProxyPass "http://192.168.0.2:8080/backendcontextroot"
    </If>
</LocationMatch>