Bug 54656 - SNI and SSLProxyCheckPeerCN based on "connection" instead of "request" hostname
Summary: SNI and SSLProxyCheckPeerCN based on "connection" instead of "request" hostname
Alias: None
Product: Apache httpd-2
Classification: Unclassified
Component: mod_ssl (show other bugs)
Version: 2.4.3
Hardware: All Linux
: P2 major (vote)
Target Milestone: ---
Assignee: Apache HTTPD Bugs Mailing List
Depends on:
Reported: 2013-03-09 01:07 UTC by EugeneL
Modified: 2016-03-16 21:37 UTC (History)
2 users (show)

new mod_ssl directive to use proxy-connection-hostname instead of proxy-request-hostname for SNI and SSLProxyCheckPeerCN (4.70 KB, application/octet-stream)
2013-03-09 01:07 UTC, EugeneL
new mod_ssl directive to use proxy-connection-hostname instead of proxy-request-hostname for SNI and SSLProxyCheckPeerCN (4.70 KB, patch)
2013-04-10 01:26 UTC, EugeneL
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description EugeneL 2013-03-09 01:07:51 UTC
Created attachment 30029 [details]
new mod_ssl directive to use proxy-connection-hostname instead of proxy-request-hostname for SNI and SSLProxyCheckPeerCN

It seems that `SSLProxyCheckPeerCN on` doesn't work well with `ProxyPreserveHost on`.  I have a patch for a new directive that would allow it.

The use case:  An Apache-based reverse proxy gets https traffic for multiple domains: e.g. CN=www.example.com, CN=www.example.org, etc.  The reverse proxy has `ProxyPreserveHost on` so that multiple domains can be served by the same backend server (e.g. backend.example.com) .  The backend server recovers the requested domain by inspecting Host header (I know there's also X-Forwarded-Host, but let's say the backend isn't running Apache or anything as extensible).  Unlike the reverse proxy, the backend server has only one SSL port, which has the backend domain name in its cert's CN (e.g. CN=backend.example.com).

The problem: When a request with Host: www.example.com is handled by the reverse proxy, mod_proxy_http assigns "www.example.com" to the proxy-request-hostname connection note.  ssl_engine_io.c will pull out this note and use it for SNI and SSLProxyCheckPeerCN.  Unfortunately, www.example.com does not match backend.example.com.  ssl_engine_io.c will abort the connection like so:

[Tue Mar 05 02:27:51.522901 2013] [ssl:info] [pid 12902] [remote] AH02005: SSL Proxy: Peer certificate CN mismatch: Certificate CN: backend.example.com Requested hostname: www.example.com
[Tue Mar 05 02:27:51.522923 2013] [ssl:info] [pid 12902] [remote] AH01998: Connection closed to child 0 with abortive shutdown (server reverse-proxy-1.example.com:443)

Previous discussion here ... http://mail-archives.apache.org/mod_mbox/httpd-dev/201209.mbox/%3C50462600.7010607@kippdata.de%3E ... suggests that the 2.4.3 is the "correct" behavior.  I believe it is correct only when X-Forwarded-Host can be used.  But if ProxyPreserveHost is on and the backend server is truely doing "mass name-based virtual hosting" and doesn't have all the certs, then it seems the Apache-based reverse proxy should be not be picky about the mismatch.  The reverse proxy shouldn't expect CN=www.example.com, CN=www.example.org, etc. when the backend only has CN=backend.example.com.

My proposed fix: Patch mod_proxy_http to also add a note named "proxy-connection-hostname" which will always refer to the hostname in the ProxyPass or [proxy] RewriteRule.  Then, depending on a new directive (SSLProxyHostnameSource) mod_ssl will consult either proxy-request-hostname or proxy-connection-hostname to perform SNI and SSLProxyCheckPeerCN.
Comment 1 EugeneL 2013-04-10 01:26:33 UTC
Created attachment 30172 [details]
new mod_ssl directive to use proxy-connection-hostname instead of proxy-request-hostname for SNI and SSLProxyCheckPeerCN

re-uploading with proper content type
Comment 2 EugeneL 2013-05-22 06:47:44 UTC
Discussions on dev@httpd.apache.org last month suggests that backend webservers behind reverse proxies should be obeying HTTP standards by always returning certs that has a CN equal to the Host: header.

A next action should be to correct the ProxyPreserveHost documentation to warn users against this new default behavior in 2.4
Comment 3 William A. Rowe Jr. 2013-12-11 22:40:10 UTC
Earlier guidance on dev@httpd was misguided.

A similar problem is present for all forward-proxied requests.

The SNI defined hostname can only be used to help route the correct certificate.
The SNI definition of a hostname is independent of the definition of the 
HTTP Host: field and any assumptions that they would be identical is misguided.

The SNI hostname may not be an IP-address, while the Host: header may be.

The SNI hostname is the next-hop hostname (without a port), while the Host: header 
is the hostname (including optional port) component of the target URI.  In the
forward proxy case, these always differ.

The SNI logic further fails to test alt-subject names, wildcard cn's and a host 
of other design errors.

I expect your report has equal validity in light of these other design flaws and
I'm evaluating this within the context of the current mis-implementation.
Comment 4 Santiago Garcia Mantinan 2016-03-16 15:00:46 UTC
Before SNI support on Apache 2.4 one could have a server serve an external.domain site using a internal.domain certificate, since SNI we have three hostnames, the SNI, the presented certificate and the Host header and an apache server will force the Host header to match the SNI, otherwise we'll get the known error:

Hostname internal.domain provided via SNI and hostname external.domain provided via HTTP are different

The proposed patch solves the problem of the Apache proxy asking for a certificate for internal.domain and sending a SNI of external.domain (which is the case now if you have ProxyPreserveHost On) but doesn't solve the problem of the Apache backend server giving that error, so this patch should go together with one adding a new directive to relax the SNI check on the server side, otherwise this patch is not coherent with Apache server behaviour.

I'd like to know if this relaxing is something that could be accepted so that we could have pre-SNI behabiours back to apache 2.4 or if ProxyPreserveHost is needed then ssl must be disabled on Apache backend servers which means lowering security compared to what we had at 2.2.
Comment 5 Yann Ylavic 2016-03-16 21:37:14 UTC
In latests 2.2 and 2.4 versions, mod_proxy will always set the SNI for the backend connections to its request's Host header (reverse, r1356881 from 2.4.3) or targeted host (forward, r1673941 from 2.4.13), and will not reuse any connection with SNI if that host differs (r1587201 from 2.4.10).

I think there is no point in using SSLProxyCheckPeerCN, requesting a particular host (be it preserved or not), getting a response from another host (backend certificate's CN), and be fine with it.
If this is expected, just don't set "SSLProxyCheckPeerCN on".

Please note that one can use a wildcard or multi-CN-subjectAltName(s) certificate on the backend, and mod_proxy will also accept those if they match the requested host (r1485667 from 2.4.5). 

Thus I'm marking this report as invalid.