Why did git stop working after server disabled SSLv3?

Like most others, our repository server needs to disable SSLv3 (and v2) ASAP.

However, doing so seems to break our git-clients -- at least, on RHEL5 (connections from my FreeBSD desktop work fine). Even the most recent git (2.1.2) fails, and upgrading OpenSSL libraries to the latest from the vendor did not help.

However! The same git-client works just fine against github.com -- and github.com already has SSLv3 disabled too. By trial and error, I set our server's (Apache) SSL-configuration to match that of github:

SSLProtocol     ALL -SSLv2 -SSLv3
SSLHonorCipherOrder On
SSLCipherSuite  "AES128-SHA AES256-SHA RC4-SHA"

By running sslscan against our server and github, I get the identical list of ciphers accepted and rejected. But git continues to fail:

    % git clone https://git.example.net/git/puppet-hiera
    Cloning into 'puppet-hiera'...
    * Couldn't find host git.example.net in the .netrc file, using defaults
    * About to connect() to git.example.net port 443
    *   Trying 10.89.8.27... * connected
    * Connected to git.example.net (10.89.8.27) port 443
    * successfully set certificate verify locations:
    *   CAfile: /etc/pki/tls/certs/ca-bundle.crt
      CApath: none
    * Unknown SSL protocol error in connection to git.example.net:443
    * Closing connection #0
    fatal: unable to access 'https://git.example.net/git/puppet-hiera/': Unknown SSL protocol error in connection to git.example.net:443

Now, the only perceptible difference remaining between our server's SSL and GitHub's is that sslscan is able to output details of GitHub's certificate, but fails to obtain those from our server.

When I connect to our git-server from my FreeBSD desktop, the same git clone command works. Instead of failing, after outputting CApath: none, I see:

      CApath: none
    * SSL connection using AES128-SHA
    * Server certificate:
             subject: C=US; postalCode= ............

and the cloning succeeds. How do I configure our server so that git works with it even from the old RHEL5-systems -- as it does against GitHub-servers?

Update: trying to access our server with simply curl, I got a similar error over SSL-compatibility. However, I was able to overcome it by invoking curl with an explicit --tlsv1 option (also known as -1). So, the software on RHEL5 systems is capable of the necessary protocols and ciphers -- how do I make it use them by default instead of trying the old ones and failing?


Ok, here is the deal. Disabling SSLv3 in today's Apache means, the server would not even tell the client, that it wants to use TLS. If the client does not begin the conversation with TLS, the client will fail -- even if could talk TLS. Many thanks to user Chris S., who analyzed the problem and even offered a patch for Apache's mod_ssl in answering a related question.

Having looked at Chris' patch, Apache developers have come up with a more comprehensive one, which might even become part of the next Apache-release. It introduces a new option for Apache's SSLProtocols directive: ANY. When Apache encounters the ANY, it will advise the connecting client (via SSLv2Hello), that it must switch to TLS:

SSLProtocol ANY -SSLv2 -SSLv3

I'm pasting the patch here for those, who can't afford to wait for Apache 2.4.11.

Index: modules/ssl/ssl_private.h
===================================================================
--- modules/ssl/ssl_private.h    (revision 1635012)
+++ modules/ssl/ssl_private.h    (working copy)
@@ -295,8 +295,10 @@ typedef int ssl_opt_t;
 #define SSL_PROTOCOL_TLSV1_2 (1<<4)
 #define SSL_PROTOCOL_ALL   (SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1| \
                 SSL_PROTOCOL_TLSV1_1|SSL_PROTOCOL_TLSV1_2)
+#define SSL_PROTOCOL_ANY   (1<<5)
 #else
 #define SSL_PROTOCOL_ALL   (SSL_PROTOCOL_SSLV3|SSL_PROTOCOL_TLSV1)
+#define SSL_PROTOCOL_ANY   (1<<3)
 #endif
 typedef int ssl_proto_t;

Index: modules/ssl/ssl_engine_init.c
===================================================================
--- modules/ssl/ssl_engine_init.c    (revision 1635012)
+++ modules/ssl/ssl_engine_init.c    (working copy)
@@ -490,6 +490,7 @@ static apr_status_t ssl_init_ctx_protocol(server_r
     }

     cp = apr_pstrcat(p,
+                     (protocol & SSL_PROTOCOL_ANY ? "SSLv23, " : ""),
              (protocol & SSL_PROTOCOL_SSLV3 ? "SSLv3, " : ""),
              (protocol & SSL_PROTOCOL_TLSV1 ? "TLSv1, " : ""),
 #ifdef HAVE_TLSV1_X
Index: modules/ssl/ssl_engine_config.c
===================================================================
--- modules/ssl/ssl_engine_config.c    (revision 1635012)
+++ modules/ssl/ssl_engine_config.c    (working copy)
@@ -1311,6 +1311,9 @@ static const char *ssl_cmd_protocol_parse(cmd_parm
     else if (strcEQ(w, "all")) {
         thisopt = SSL_PROTOCOL_ALL;
     }
+        else if (strcEQ(w, "any")) {
+            thisopt = SSL_PROTOCOL_ANY|SSL_PROTOCOL_ALL;
+        }
     else {
         return apr_pstrcat(parms->temp_pool,
                parms->cmd->name,
Index: modules/ssl/ssl_engine_io.c
===================================================================
--- modules/ssl/ssl_engine_io.c    (revision 1635012)
+++ modules/ssl/ssl_engine_io.c    (working copy)
@@ -1137,6 +1137,7 @@ static apr_status_t ssl_io_filter_handshake(ssl_fi
      * IPv4 and IPv6 addresses are not permitted".)
      */
     if (hostname_note &&
+            !(sc->proxy->protocol & SSL_PROTOCOL_ANY) &&
         sc->proxy->protocol != SSL_PROTOCOL_SSLV3 &&
         apr_ipsubnet_create(&ip, hostname_note, NULL,
                 c->pool) != APR_SUCCESS) {