Search code examples
gitlibcurlhttp-authenticationspnego

How to force Git (2.5+) HTTP transport prefer SPNEGO over Basic authentication?


Summary: I am using Git for Windows 2.5.1 to authenticate with a Kerbesized Git server. When I am using the URL in the form https://el2-gitlab.sa.c/kkm/GrammarTools.git, Git does not even attempt the Negotiate authentication, and asks for the user name and password. A workarouond to force Git to use SPNEGO is to provide empty username and password in the URL itself, as in https://:@el2-gitlab.sa.c/kkm/GrammarTools.git. In this case, Git happily authenticates with the existing Kerberos ticket.

Can I configure Git to try SPNEGO without tweaking the remote URL?

More details. I spent quite a time trying to solve the problem. First I tried giving an empty user name in .gitconfig, but to no avail:

[credential "https://el2-gitlab.sa.c"]
   username = ''

Not once I came across questions on a reverse problem, when Git refused to revert to Basic after trying and failing Negotiate, but the behavior is confirmed to have changed in 2.3.1.

Responding to the prompts with the empty username and password does not help, contrary to some suggestions I could find on SO (but they may pre-date version 2.3.1).

Finally, verbose libcurl output (abridged here) shows that Git indeed attempts Basic authentication and forgoes Negotiate altogether:

$ export GIT_CURL_VERBOSE=1
$ git clone https://el2-gitlab.sa.c/kkm/GrammarTools.git kerbtest
Cloning into 'kerbtest'...
* Couldn't find host el2-gitlab.sa.c in the _netrc file; using defaults
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
> GET /kkm/GrammarTools.git/info/refs?service=git-upload-pack HTTP/1.1
Host: el2-gitlab.sa.c
User-Agent: git/2.5.1.windows.1

< HTTP/1.1 401 Unauthorized
< Status: 401 Unauthorized
< Www-Authenticate: Basic realm=""
< Www-Authenticate: Negotiate
<
* Connection #0 to host el2-gitlab.sa.c left intact
Username for 'https://el2-gitlab.sa.c':

Also may be of interest is that the Git client retries the unauthenticated request on a 401 for the second time before responding with the ticket:

$ git clone https://:@el2-gitlab.sa.c/kkm/GrammarTools.git kerbtest
Cloning into 'kerbtest'...
* Couldn't find host el2-gitlab.sa.c in the _netrc file; using defaults
> GET /kkm/GrammarTools.git/info/refs?service=git-upload-pack HTTP/1.1
Host: el2-gitlab.sa.c
User-Agent: git/2.5.1.windows.1

< HTTP/1.1 401 Unauthorized
< Status: 401 Unauthorized
< Www-Authenticate: Basic realm=""
< Www-Authenticate: Negotiate
* Connection #0 to host el2-gitlab.sa.c left intact
* Issue another request to this URL: 'https://:@el2-gitlab.sa.c/kkm/GrammarTools.git/info/refs?service=git-upload-pack'
* Couldn't find host el2-gitlab.sa.c in the _netrc file; using defaults
> GET /kkm/GrammarTools.git/info/refs?service=git-upload-pack HTTP/1.1
Host: el2-gitlab.sa.c
User-Agent: git/2.5.1.windows.1

< HTTP/1.1 401 Unauthorized
< Status: 401 Unauthorized
< Www-Authenticate: Basic realm=""
< Www-Authenticate: Negotiate
<
* Issue another request to this URL: 'https://:@el2-gitlab.sa.c/kkm/GrammarTools.git/info/refs?service=git-upload-pack'
* Couldn't find host el2-gitlab.sa.c in the _netrc file; using defaults
> GET /kkm/GrammarTools.git/info/refs?service=git-upload-pack HTTP/1.1
Host: el2-gitlab.sa.c
Authorization: Negotiate YIIGtg[ .... trimmed ... ]
User-Agent: git/2.5.1.windows.1

< HTTP/1.1 200 OK

Solution

  • git 2.8 (March 2016) should alleviate that issue and force an empty username and password during http authentication:

    See commit 121061f (15 Feb 2016) by brian m. carlson (bk2204).
    (Merged by Junio C Hamano -- gitster -- in commit 65ba75b, 24 Feb 2016)

    http: add option to try authentication without username

    Performing GSS-Negotiate authentication using Kerberos does not require specifying a username or password, since that information is already included in the ticket itself.
    However, libcurl refuses to perform authentication if it has not been provided with a username and password.

    Add an option, http.emptyAuth, that provides libcurl with an empty username and password to make it attempt authentication anyway.

    The git config documentation will mention:

    http.emptyAuth:
    

    Attempt authentication without seeking a username or password.
    This can be used to attempt GSS-Negotiate authentication without specifying a username in the URL, as libcurl normally requires a username for authentication.


    Git 2.10.2 (Octobre 2016) will improve that.

    See commit 5275c30 (04 Oct 2016) by David Turner (csusbdt).
    (Merged by Junio C Hamano -- gitster -- in commit c6400bf, 17 Oct 2016)

    http: http.emptyauth should allow empty (not just NULL) usernames

    When using Kerberos authentication with newer versions of libcurl, CURLOPT_USERPWD must be set to a value, even if it is an empty value. The value is never sent to the server.
    Previous versions of libcurl did not require this variable to be set.
    One way that some users express the empty username/password is http://:@gitserver.example.com, which http.emptyauth was designed to support.
    Another, equivalent, URL is http://@gitserver.example.com.
    The latter leads to a username of zero-length, rather than a NULL username, but CURLOPT_USERPWD still needs to be set (if http.emptyauth is set).
    Do so.


    Git 2.13 (Q2 2017) will reduce authentication round-trip over HTTP when the server supports just a single authentication method.

    See commit 40a18fc (25 Feb 2017), and commit 840398f (22 Feb 2017) by Jeff King (peff).
    Helped-by: Johannes Schindelin (dscho).
    (Merged by Junio C Hamano -- gitster -- in commit 92718f5, 10 Mar 2017)

    http: add an "auto" mode for http.emptyauth

    This variable (http.emptyauth) needs to be specified to make some types of non-basic authentication work, but ideally this would just work out of the box for everyone.

    However, simply setting it to "1" by default introduces an extra round-trip for cases where it isn't useful. We end up sending a bogus empty credential that the server rejects.

    The "auto" mode should make it work out of the box, without incurring any extra round-trips for people hitting Basic-only servers.