pythongssapi / requests-gssapi

An authentication handler for using GSSAPI with Python Requests. Drop-in replacement for old requests-kerberos.
Other
32 stars 21 forks source link

Provide a "for dummies" guide to MutualAuthenticationError #15

Closed ktdreyer closed 5 years ago

ktdreyer commented 5 years ago

I'm using requests-gssapi against a single RHEL Apache server, like this:

from requests_gssapi import HTTPSPNEGOAuth
import requests

gssapi_auth = HTTPSPNEGOAuth(opportunistic_auth=True)

url = 'https://example.com/...'

response = requests.get(url, auth=gssapi_auth)

I don't know the exact sever details but the HTTP headers say Apache/2.4.34 (Red Hat) OpenSSL/1.0.1e-fips mod_auth_kerb/5.4 Phusion_Passenger/4.0.50

Here's the error I get:

    response = requests.get(url, auth=gssapi_auth)
  File "/usr/lib/python3.7/site-packages/requests/api.py", line 75, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/lib/python3.7/site-packages/requests/api.py", line 60, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/lib/python3.7/site-packages/requests/sessions.py", line 524, in request
    resp = self.send(prep, **send_kwargs)
  File "/usr/lib/python3.7/site-packages/requests/sessions.py", line 644, in send
    r = dispatch_hook('response', hooks, r, **kwargs)
  File "/usr/lib/python3.7/site-packages/requests/hooks.py", line 31, in dispatch_hook
    _hook_data = hook(hook_data, **kwargs)
  File "/usr/lib/python3.7/site-packages/requests_gssapi/gssapi_.py", line 288, in handle_response
    _r = self.handle_other(response)
  File "/usr/lib/python3.7/site-packages/requests_gssapi/gssapi_.py", line 219, in handle_other
    "Unable to authenticate {0}".format(response))
requests_gssapi.exceptions.MutualAuthenticationError: Unable to authenticate <Response [200]>

When I re-run my script a second time (within a few seconds), it succeeds.

I'm omitting some things, but the script is really simple. There's no threads or multiple processes involved on my client.

Could we provide a "troubleshooting guide" here for when Python developers encounter this problem?

For example, does this error indicate a problem with the server, or with my client?

What is mutual authentication?

It is safe to disable mutual authentication 100% of the time if the server is always using HTTPS? Is there any way to tell my requests Session "don't ever try to use HTTP" in case I make a mistake in my code or accidentally follow a bad redirect to a plaintext URL?

frozencemetery commented 5 years ago

Is there any way to tell my requests Session "don't ever try to use HTTP" in case I make a mistake in my code or accidentally follow a bad redirect to a plaintext URL?

Given the prevalence of noncompliant servers, my ideal would be to make the default "disabled" on HTTPS connections. I think I can just check request.url for this information at time of __call__(), and add a pseudo-value DEFAULT for mutual_authentication.

Let me think about that for a bit and if I can't come up with a reason not to, I'll go ahead and do that.

ktdreyer commented 5 years ago

Is there anything I can tell my server team to investigate when I hit this error? Like I posted above, I re-ran the same GET request and it succeeded.

Am I hitting some bug in mod_auth_kerb 5.4?

Is there any chance the web app (Rails) is somehow causing this?

ktdreyer commented 5 years ago

I can always set DISABLED, but I was hoping to find out more information about what's broken server-side.

frozencemetery commented 5 years ago

Apologies, I thought you were just asking for me to document all this. (I guess I can start in this comment and expand it in a bit.)

Mutual authentication, in most implementations including krb5, requires another round-trip between the client and server during the authentication handshake. While I can't say who's to blame absent other information, the most common problem is the server not being prepared for this - in some cases I think they even advertise support for it. If one end isn't prepared for multiple round-trips here, that's a very likely culprit - I thought mod_auth_kerb would handle this, but I really don't know. (Its successor, mod_auth_gssapi, does, but I'm also a co-maintainer on that, and I understand you can't necessarily modify server configuration here, nor is it a general solution to the problem.)

So long as you're running over a TLS link whose security guarantees you trust, there's no benefit to mutual authentication. If you don't trust the link at all, mutual authentication won't help (since it's not tamper-proof, and GSSAPI isn't being used post-authentication). There's some middle ground between the two where it helps some (e.g., passive adversary over encrypted-but-unverified channel), but for Negotiate, it's not generally helpful.

Mutual authentication behaves differently when GSSAPI is also used for encryption (which it isn't here, unfortunately). It's still no benefit to krb5 (decryption provides mutual guarantees), but that's not actually mandated, and one could build a mechanism where mutual authentication isn't always provided during encryption. (Please don't do this.)

Does that help / can I tell you more about any of that?

ktdreyer commented 5 years ago

This is fantastic. Thank you Robbie! I was struggling to find this information anywhere on the internet.

I think we could add this into README.rst under "Mutual Authentication". What do you think?