Repository navigation
Description
I'm trying to use requests-kerberos on macOS, and can't seem to authenticate properly. I'm a Kerberos newbie, so it's very possible I'm missing something basic here.
I use kinit to authenticate myself and populate the credential cache. I then used klist to verify that I have a cached TGT there:
❯ klist -A
Credentials cache: API:F56611A6-CC9E-447F-9438-E264BAF2ECFD
Principal: john.smith@FOOBAR.COM
Issued Expires Principal
May 8 14:06:10 2025 May 9 00:06:10 2025 krbtgt/FOOBAR.COM@FOOBAR.COM
I did notice that the "Credentials cache" specifies "API:" as the location, rather than say, a temporary file somewhere - but from my research I believe that's normal/expected on macOS as it's no longer file-backed. (Source: jcmturner/gokrb5#412)
When I make my request to the remote server with requests-kerberos, requests.text simply contains "Unauthorised". Sample code:
def main():
logging.basicConfig(level=logging.DEBUG)
kerberos_auth = HTTPKerberosAuth(principal="krbtgt/foobar.COM@foobar.COM")
r = requests.get("http://proxy1.orthrus.byted.org:8080/api/v1/gss/list", auth=kerberos_auth)
print(r.text)
I turned on verbose logging (logging.basicConfig(level=logging.DEBUG)), and I tried again, full output is below:
However, it's a little unclear to me if requests-kerberos is actually successfully pulling the cached credentials or not.
Is there some way to verify that it's actually pulling them, and presenting them, or is there something else I'm missing here?
DEBUG:urllib3.connectionpool:Starting new HTTP connection (1): proxy1.foobar.org:8080
DEBUG:urllib3.connectionpool:http://proxy1.foobar.org:8080 "GET /api/v1/gss/list HTTP/1.1" 401 15
DEBUG:requests_kerberos.kerberos_:handle_401(): Handling: 401
ERROR:requests_kerberos.kerberos_:generate_request_header(): ctx init failed:
Traceback (most recent call last):
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 363, in __init__
gssapi_credential = _get_gssapi_credential(
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 120, in _get_gssapi_credential
gss_cred = gssapi.Credentials(name=principal, usage=usage, mechs=[mech])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/gssapi/creds.py", line 77, in __new__
res = cls.acquire(name, lifetime, mechs, usage,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/gssapi/creds.py", line 163, in acquire
res = rcreds.acquire_cred(name, lifetime,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "gssapi/raw/creds.pyx", line 127, in gssapi.raw.creds.acquire_cred
gssapi.raw.exceptions.MissingCredentialsError: Major (458752): No credentials were supplied, or the credentials were unavailable or inaccessible., Minor (0): unknown mech-code 0 for mech unknown
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/requests_kerberos/kerberos_.py", line 213, in generate_request_header
self._context[host] = ctx = spnego.client(
^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/auth.py", line 169, in client
return _new_context(
^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/auth.py", line 84, in _new_context
return proxy(
^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 370, in __init__
raise SpnegoError(base_error=gss_err, context_msg="Getting GSSAPI credential") from gss_err
spnego.exceptions.NoCredentialError: SpnegoError (7): Major (458752): No credentials were supplied, or the credentials were unavailable or inaccessible., Minor (0): unknown mech-code 0 for mech unknown, Context: Getting GSSAPI credential
ERROR:requests_kerberos.kerberos_:SpnegoError (7): Major (458752): No credentials were supplied, or the credentials were unavailable or inaccessible., Minor (0): unknown mech-code 0 for mech unknown, Context: Getting GSSAPI credential
Traceback (most recent call last):
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 363, in __init__
gssapi_credential = _get_gssapi_credential(
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 120, in _get_gssapi_credential
gss_cred = gssapi.Credentials(name=principal, usage=usage, mechs=[mech])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/gssapi/creds.py", line 77, in __new__
res = cls.acquire(name, lifetime, mechs, usage,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/gssapi/creds.py", line 163, in acquire
res = rcreds.acquire_cred(name, lifetime,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "gssapi/raw/creds.pyx", line 127, in gssapi.raw.creds.acquire_cred
gssapi.raw.exceptions.MissingCredentialsError: Major (458752): No credentials were supplied, or the credentials were unavailable or inaccessible., Minor (0): unknown mech-code 0 for mech unknown
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/requests_kerberos/kerberos_.py", line 213, in generate_request_header
self._context[host] = ctx = spnego.client(
^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/auth.py", line 169, in client
return _new_context(
^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/auth.py", line 84, in _new_context
return proxy(
^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 370, in __init__
raise SpnegoError(base_error=gss_err, context_msg="Getting GSSAPI credential") from gss_err
spnego.exceptions.NoCredentialError: SpnegoError (7): Major (458752): No credentials were supplied, or the credentials were unavailable or inaccessible., Minor (0): unknown mech-code 0 for mech unknown, Context: Getting GSSAPI credential
DEBUG:requests_kerberos.kerberos_:handle_401(): returning <Response [401]>
DEBUG:requests_kerberos.kerberos_:handle_response(): returning <Response [401]>
DEBUG:requests_kerberos.kerberos_:handle_response() has seen 0 401 responses
DEBUG:requests_kerberos.kerberos_:handle_401(): Handling: 401
ERROR:requests_kerberos.kerberos_:generate_request_header(): ctx init failed:
Traceback (most recent call last):
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 363, in __init__
gssapi_credential = _get_gssapi_credential(
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 120, in _get_gssapi_credential
gss_cred = gssapi.Credentials(name=principal, usage=usage, mechs=[mech])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/gssapi/creds.py", line 77, in __new__
res = cls.acquire(name, lifetime, mechs, usage,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/gssapi/creds.py", line 163, in acquire
res = rcreds.acquire_cred(name, lifetime,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "gssapi/raw/creds.pyx", line 127, in gssapi.raw.creds.acquire_cred
gssapi.raw.exceptions.MissingCredentialsError: Major (458752): No credentials were supplied, or the credentials were unavailable or inaccessible., Minor (0): unknown mech-code 0 for mech unknown
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/requests_kerberos/kerberos_.py", line 213, in generate_request_header
self._context[host] = ctx = spnego.client(
^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/auth.py", line 169, in client
return _new_context(
^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/auth.py", line 84, in _new_context
return proxy(
^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 370, in __init__
raise SpnegoError(base_error=gss_err, context_msg="Getting GSSAPI credential") from gss_err
spnego.exceptions.NoCredentialError: SpnegoError (7): Major (458752): No credentials were supplied, or the credentials were unavailable or inaccessible., Minor (0): unknown mech-code 0 for mech unknown, Context: Getting GSSAPI credential
ERROR:requests_kerberos.kerberos_:SpnegoError (7): Major (458752): No credentials were supplied, or the credentials were unavailable or inaccessible., Minor (0): unknown mech-code 0 for mech unknown, Context: Getting GSSAPI credential
Traceback (most recent call last):
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 363, in __init__
gssapi_credential = _get_gssapi_credential(
^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 120, in _get_gssapi_credential
gss_cred = gssapi.Credentials(name=principal, usage=usage, mechs=[mech])
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/gssapi/creds.py", line 77, in __new__
res = cls.acquire(name, lifetime, mechs, usage,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/gssapi/creds.py", line 163, in acquire
res = rcreds.acquire_cred(name, lifetime,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "gssapi/raw/creds.pyx", line 127, in gssapi.raw.creds.acquire_cred
gssapi.raw.exceptions.MissingCredentialsError: Major (458752): No credentials were supplied, or the credentials were unavailable or inaccessible., Minor (0): unknown mech-code 0 for mech unknown
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/requests_kerberos/kerberos_.py", line 213, in generate_request_header
self._context[host] = ctx = spnego.client(
^^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/auth.py", line 169, in client
return _new_context(
^^^^^^^^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/auth.py", line 84, in _new_context
return proxy(
^^^^^^
File "/Users/foobar/code/foobar/api_menangerie/.venv/lib/python3.12/site-packages/spnego/_gss.py", line 370, in __init__
raise SpnegoError(base_error=gss_err, context_msg="Getting GSSAPI credential") from gss_err
spnego.exceptions.NoCredentialError: SpnegoError (7): Major (458752): No credentials were supplied, or the credentials were unavailable or inaccessible., Minor (0): unknown mech-code 0 for mech unknown, Context: Getting GSSAPI credential
DEBUG:requests_kerberos.kerberos_:handle_401(): returning <Response [401]>
DEBUG:requests_kerberos.kerberos_:handle_response(): returning <Response [401]>
DEBUG:requests_kerberos.kerberos_:handle_response() has seen 1 401 responses
DEBUG:requests_kerberos.kerberos_:handle_response(): returning 401 <Response [401]>
Unauthorised.
Process finished with exit code 0
Activity
jborean93 commentedon May 8, 2025
Try just
principal='john.smith@FOOBAR.COM'as thekrbtgt/...principal is the target/service principal used to retrieve the TGT and not the actual client principal in the cache you wish to use. Technically a cache can have multiple principal if the ccache type supports it so specifying the client principal to use is a way to pick which once you want.victorhooi commentedon May 8, 2025
@jborean93 thanks for the suggestion!
OK, I've edited that line to set the principal as suggested:
Running it again - the error message is different -
A parameter was malformed Miscellaneous failure (see text), Minor (0): Success- is that a good thing and it's further along in the process? =)However, I'm not sure what ot make of that error. Have you seen that before, or any ideas?
victorhooi commentedon May 9, 2025
Just another datapoint - we have an official CLI tool that hits the same endpoint, and authenticates successfully (as you'd expect). I was able to take a packet capture from that tool.
If I take the HTTP
Authorizationheader from that capture, I can use that in a HTTP client (e.g. Yaak, Curl etc), and it also works there as well. (However, it does stop working after a while, I assume due to expiry?)Here is a sample HTTP Authorization header from the official client - which works, and you can use to reply requests (for a short time):
And here is the one from the debug line in requests-kerberos:
It seems there's some extra stuff in the requests_kerberos line? (The HTTP endpoint is in there, b64 encoded?) Does that tell you anything useful about what might be going on?
(Also, I did a naive search, and saw #173 - just for completeness, I did try appending
send_cbt=Falseto the kerberos_auth line, and the issue still occurs - so I'm assuming it's not the channel-binding stuff).jborean93 commentedon May 9, 2025
If you compare the raw tokens that were exchanged here, the one send and received by
requests-kerberosis:The one that from a working client is:
We can see that the server rejected the Kerberos token from
requests-kerberosbut it doesn't really indicate why/what went wrong. The only real way to determine what went wrong would be to look at the logs on the server side to see if it gives any indications of what went wrong.There are some differences through:
requests-kerberosjust sends the raw Kerberos token whereas the other tool wraps the Kerberos token inside a SPNEGO payloadrequests-kerberostoken is for the SPNHTTP/proxy1.realm.comwhereas the other tool isHTTP/authzd-proxy-i18n.realm.comrequests-kerberosjust uses the hostname from the requesthostname_overridekwarg can be used to override this behaviour and provide a different hostname in the SPN if you wishkrb5.confalso has a few options around hostname canonicalisation but the options and what they do depend on the platform and version you are running onThe first difference can be solved by using the requests-gssapi library which has a similar API to this one but uses SPNEGO/Negotiate packets rather than just pure Kerberos. This may solve the problem if that's what the server did not like but if it was because of the incorrect SPN being used then you will have to look at your
krb5.confor provide a custom hostname override so it uses the correct one rather than the hostname from the request.victorhooi commentedon May 9, 2025
Aha - I got it working! Still one very small question about usability though - as I need to mutate the hostname_override, and I'm not sure if that's possible?
Anyway - my notes on what worked here. There are two types of HTTP requests I need to make here - a GET to one endpoint, and a PUT to a different endpoint. They're both meant to use Kerberos auth.
For the GET requests - it appears to be the SPNEGO wrapping you mentioned. Replacing:
with:
However, for the POST query - using
requests_gssapi.HTTPKerberosAuthstill fails with the below error:Here are the sample HTTP Authorization headers using the official client, for both types of queries.
HTTP GET request:
HTTP POST request:
I did try the
python3 -m spnego --format yaml --tokentrick you showed in the last post 😃, and did a quick diff on the output - but I couldn't spot any obvious differences, aside from the sname -> name-string.However, I found out that using a
hostname_overridecombined withrequests_gssapimakes the POST request to the other endpoint work 🤷.Previously I was using a single
kerberos_auth = HTTPKerberosAuth(...)object, to make all therequestscalls - is that how I should be doing it?Also, I'm assuming you can't set, then unset 'hostname_override`, right? Or is there some way to achieve that?
jborean93 commentedon May 12, 2025
The only way to achieve is by using the
hostname_overridekwarg when building theHTTPKerberoAuthobject. If you are using this against multiple hosts then you'll have to provide one of these objects per host.That would tell me the local GSSAPI library doesn't support the mechanism requested. This is more a question on
requests-gssapibut it does have a kwarg to specify a custom mech. By default it uses SPNEGO so you may want to change it to just Kerberos.