Anders Brownworth

Technology and Disruption

Git over Apache HTTPS / SSL with Digest Authentication

I was trying to get a git repo going over https but I was running into this when trying to clone the repository:

error: RPC failed; result=35, HTTP code = 0

There is a good deal of mis-information about how to deploy git over http as the newer mode of support for it (smart-http mode) is fairly new. (March, 2010) The old-school way to deploy a git repo was over WebDAV and let git clients do fairly non-optimized access. (pull entire files rather than only the lines they needed) More recently, support for smart-http has been added in the form of a CGI which is run by the webserver instead. Simple enough, you would think, but I was having significant difficulty getting it to work over https. I have a self-signed cert and most of the docs on the net deal with bad cert issues but that didn't end up being my problem. The setup worked without SSL and strangely worked fine for simple pull operations over SSL. I was also doing digest authentication but that too didn't seem to be my problem because it didn't matter if I removed it. (and the pulls worked!)

Here's the setup. I had an apache virtual host defined as git.example.com that is configured to assume all URLs are git urls: (note the ScriptAlias starting at /)

<VirtualHost 192.168.1.230:443>
ServerName git.example.com
DocumentRoot /sites/git.example.com/web
ErrorLog /sites/git.example.com/logs/error.log
CustomLog /sites/git.example.com/logs/access.log common
<Location />
AuthType Digest
AuthName git
AuthDigestDomain /
AuthDigestProvider file
AuthUserFile /sites/git.example.com/conf/users.digest
Order allow,deny
Deny from all
Satisfy any
Require valid-user
</Location>

SetEnv GIT_PROJECT_ROOT /sites/git.example.com/repo
SetEnv GIT_HTTP_EXPORT_ALL
ScriptAlias / /usr/libexec/git-core/git-http-backend/

SSLEngine on
SSLProtocol all
SSLCipherSuite HIGH:MEDIUM
SSLCertificateFile /sites/git.example.com/conf/git.example.com.crt
SSLCertificateKeyFile /sites/git.example.com/conf/git.example.com.key
SSLCACertificateFile /etc/certs/ca.crt
SetEnvIf User-Agent ".*MSIE.*" nokeepalive ssl-unclean-shutdown downgrade-1.0 force-response-1.0
</VirtualHost>

After initializing a git repo, adding some files and bare cloning somewhere in the htdocs tree:

git clone --bare /path/to/core /sites/git.example.com/repo/core.git

I marked it exportable:

cd /sites/git.example.com/repo/core.git
touch git-daemon-export-ok

and updated it as is well documented:

git --bare update-server-info

However, when I went to clone it on another machine, I got:

anders@vm1 ~ $ git clone https://anders@git.example.com/core.git
Cloning into core...
Password:
error: RPC failed; result=35, HTTP code = 0
fatal: The remote end hung up unexpectedly

So I turned up some verbose logging:

anders@vm1 ~ $ export GIT_CURL_VERBOSE=1

and tried it again. This time I got a bit more info:

anders@vm1 ~ $ git clone https://anders@git.example.com/core.git
Cloning into core...
Password:
* Couldn't find host git.example.com in the .netrc file; using defaults
* About to connect() to git.example.com port 443 (#0)
* Trying 192.168.1.230... * Connected to git.example.com (192.168.1.230) port 443 (#0)
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/andersCA.pem
CApath: /etc/ssl/certs
* SSL connection using DHE-RSA-CAMELLIA256-SHA
* Server certificate:
* subject: C=US; ST=Massachusetts; L=Cambridge; O=Example; OU=Certs; CN=git.example.com
* start date: 2011-01-13 23:15:32 GMT
* expire date: 2021-01-10 23:15:32 GMT
* common name: git.example.com (matched)
* issuer: C=US; ST=Massachusetts; L=Cambridge; O=Anders.com; OU=Certs; CN=anders.com
* SSL certificate verify ok.
> GET /core.git/info/refs?service=git-upload-pack HTTP/1.1
User-Agent: git/1.7.3
Host: git.example.com
Accept: */*
Pragma: no-cache

< HTTP/1.1 401 Authorization Required
< Date: Thu, 13 Jan 2011 19:45:46 GMT
< Server: Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/0.9.8l DAV/2 SVN/1.6.15 Phusion_Passenger/3.0.0 mod_jk/1.2.28
< WWW-Authenticate: Digest realm="git", nonce="2gNhkL+ABAZ=04b8a5e762d15a9a9c571907f1e248d6cfcf43fc", algorithm=MD5, domain="/", qop="auth"
< Content-Length: 401
< Content-Type: text/html; charset=iso-8859-1
<
* Ignoring the response-body
* Connection #0 to host git.example.com left intact
* Issue another request to this URL: 'https://anders@git.example.com/core.git/info/refs?service=git-upload-pack'
* Couldn't find host git.example.com in the .netrc file; using defaults
* Re-using existing connection! (#0) with host git.example.com
* Connected to git.example.com (192.168.1.230) port 443 (#0)
* Server auth using Digest with user 'anders'
> GET /core.git/info/refs?service=git-upload-pack HTTP/1.1
Authorization: Digest username="anders", realm="git", nonce="2gNhkL+ABAZ=04b8a5e762d15a9a9c571907f1e248d6cfcf43fc", uri="/core.git/info/refs?service=git-upload-pack", cnonce="TOU3MTUx", nc=00000001, qop="auth", response="e22b99a4b8384f1b767d1ffebfe4eb5a", algorithm="MD5"
User-Agent: git/1.7.3
Host: git.example.com
Accept: */*
Pragma: no-cache

< HTTP/1.1 200 OK
< Date: Thu, 13 Jan 2011 19:45:47 GMT
< Server: Apache/2.2.17 (Unix) mod_ssl/2.2.17 OpenSSL/0.9.8l DAV/2 SVN/1.6.15 Phusion_Passenger/3.0.0 mod_jk/1.2.28
< Expires: Fri, 01 Jan 1980 00:00:00 GMT
< Pragma: no-cache
< Cache-Control: no-cache, max-age=0, must-revalidate
< Authentication-Info: rspauth="2bb0252ed8e7084de067bada6cf57cb7", cnonce="TOU3MTUx", nc=00000001, qop=auth
< Transfer-Encoding: chunked
< Content-Type: application/x-git-upload-pack-advertisement
<
* Expire cleared
* Connection #0 to host git.example.com left intact
* Couldn't find host git.example.com in the .netrc file; using defaults
* About to connect() to git.example.com port 443 (#0)
* Trying 192.168.1.230... * connected
* Connected to git.example.com (192.168.1.230) port 443 (#0)
* successfully set certificate verify locations:
* CAfile: /etc/ssl/certs/andersCA.pem
CApath: /etc/ssl/certs
* SSL re-using session ID
* error:140920DF:SSL routines:SSL3_GET_SERVER_HELLO:parse tlsext
* Closing connection #0
error: RPC failed; result=35, HTTP code = 0
fatal: The remote end hung up unexpectedly

Whoops, that's the issue there. Notice how we seem to happily talk back and forth over SSL in the first connection and then suddenly we get this "result=35" error? Strange, huh?

Upon further investigation, the line above the error - SSL re-using session ID - looks suspicious. We should be able to re-use session IDs but apparently the server was forgetting about the one I'm using here.

The fix was to add the following SSLSessionCache setting to httpd.conf.

SSLSessionCache shm:/tmp/sessioncache

This doesn't seem to make a whole lot of sense to me because if SSLSessionCache is set to "none" (the default) the docs claim there is no drawback in functionality. Well, apparently there is in this case because when I set it to a hash table in shared memory as above, the server suddenly starts to keep track of sessions. (is the default - without inter-process cacheing - broken?)

In any event, I now have my git https server with digest authentication.

As a side note, this example requires a user / pass for everything, even reading the repository, so you might want to do something like this:

<LocationMatch "^/.*/git-receive-pack$">

instead.

I hope this helps others avoid the nearly 2 days of time this took me!

Comments (1)

Anders from Cambridge, MA

Apache 2.4 update:

LoadModule ssl_module modules/mod_ssl.so
LoadModule socache_shmcb_module modules/mod_socache_shmcb.so

<IfModule ssl_module>
SSLRandomSeed startup builtin
SSLRandomSeed connect builtin
SSLSessionCache shmcb:/tmp/sessioncache
</IfModule>

Leave a Comment

Name:
Location: (city / state / country)
Email: (not published / no spam)
Comment:

No HTML is allowed. Cookies must be enabled to post. Your comment will appear on this page after a moderator OKs it. Offensive content will not be published.

Click the umbrella to submit your comment.

To create links in comments:
[link:https://andersbrownworth.com/] becomes https://andersbrownworth.com/
[link:https://andersbrownworth.com/|AndersBrownworth.com] becomes AndersBrownworth.com
Notice there is no rel="nofollow" in these hrefs. Links in comments will carry page rank from this site so only link to things worthy of people's attention.