Bug 55866

Summary: When ProxyPreserveHost is on, SSL expects the wrong CN from the backend
Product: Apache httpd-2 Reporter: Mikhail T. <mi+apache>
Component: mod_proxy_httpAssignee: Apache HTTPD Bugs Mailing List <bugs>
Status: NEW ---    
Severity: normal    
Priority: P2    
Version: 2.4.4   
Target Milestone: ---   
Hardware: All   
OS: All   

Description Mikhail T. 2013-12-11 19:45:36 UTC
Consider the following sample configuration for Apache running on front.example.com:

SSLProxyEngine  on
<Location /foo/>
        Require all granted
        ProxyPreserveHost       on
        ProxyPass       https://back.example.com/foo/

Accessing http://front.example.com/foo/ will cause an SSL-connection to be opened from front to back, as expected.

Unfortunately, the SSL-engine on the client-side (on front) will expect the certificate presented by the back to contain "front.example.com" in its CN, rather than the "back.example.com":

AH02005: SSL Proxy: Peer certificate CN mismatch: Certificate CN: back.example.com Requested hostname: front.example.com

This is not happening, when ProxyPreserveHost is set to off, as is the default.

As a work-around, one can set SSLProxyCheckPeerCN to off, but that defeats most of the usefulness of SSL between front and back.

The fix would change things so that the proxy-code still passes the original front.example.com in the Host:-header (because the ProxyPreserveHost is set to "on"), but expects the back.example.com in the CN of the certificate returned by the back end.
Comment 1 Yann Ylavic 2013-12-11 21:04:14 UTC
If your backend does not use the same host name (and hence certificate CN) the client is requesting on the frontend, you shouldn't use ProxyPreserveHost (or expect SSLProxyCheckPeerCN to accept the peer certificate).

See http://www.mail-archive.com/dev@httpd.apache.org/msg56672.html for a discussion about this.
Comment 2 Yann Ylavic 2013-12-11 21:17:25 UTC
Possible duplicate of Bug 54656.
Comment 3 Mikhail T. 2013-12-12 01:01:50 UTC
(In reply to Yann Ylavic from comment #1)
> If your backend does not use the same host name (and hence certificate CN)
> the client is requesting on the frontend, you shouldn't use
> ProxyPreserveHost (or expect SSLProxyCheckPeerCN to accept the peer
> certificate).

I'm using ProxyPreserveHost because the back-end's behavior depends on the Host-header, that's simple enough. In our particular case, the back-end needs to set a cookie for mod_auth_form. Though cookie-verification happens on the front-ends, the cookie-settings is proxied to the central server.

That central server is setting the same cookie for dozens (if not hundreds) of domains. Placing them all into its certificate -- and remaking the certificate each time a new front is added -- is impractical. The cookie-issuing back should not even need to know about all possible fronts -- indeed, there may be an infinite number of them.

Nor should it be necessary -- the purpose of using SSL on the proxy->back connection is to ensure the integrity of THAT connection only. The client->proxy may or may not even be using SSL at all, but the SSL's rejection of the back-end should signal the danger of back-end having been hijacked.

Disabling SSLProxyCheckPeerCN defeats the major purpose of using SSL -- allowing man-in-the-middle attack against the proxy->back connections.

> Possible duplicate of Bug 54656.

Well, they are related, yes. And I agree with William's comment-3 there. But whereas Bug 54656 seeks to add a new configuration directive to allow turning the alternative behavior on, I argue, that the current behavior is plainly incorrect and ought to be corrected.
Comment 4 Yann Ylavic 2013-12-24 13:04:38 UTC
The proxy is requesting front.example.com but gets a certificate from back.example.com, how could it validate the peer's CN positively?
Isn't back.example.com the man-in-the-middle?

When ProxyPreserveHost is on, the host part of the ProxyPass's URL is used only to resolve the IP address (which could be used there instead, with no difference).

Contrariwise, if one uses ProxyPreserveHost because the/some backend uses the same Host as the requested one, should the check fail because (s)he sets an IP address (or a private hostname) in the ProxyPass?

When ProxyPreserveHost is on, either a new directive has to be added to select the expected peer's hostname (Host vs ProxyPass, bug 54656), or the current behaviour be applied.