-
Notifications
You must be signed in to change notification settings - Fork 52
Specifying KRB5CCNAME #309
-
It looks like by default python-gssapi create a ticket cache located in API:xxxxxxx. Is there a way to tell python-gssapi to create the ticket in a specific folder?
Beta Was this translation helpful? Give feedback.
All reactions
Replies: 6 comments 4 replies
-
It just calls the C api so you can specify the env var KRB5CCNAME
using os.environ
and it will work in the same way. What API are you trying to call, what are you trying to achieve with a custom ccache?
Beta Was this translation helpful? Give feedback.
All reactions
-
Hi Jordan,
I am calling two API. One is gssapi.raw.acquire_cred_with_password and the other is gssapi.SecurityContext. I'm thinking that the first API is the one generating the ticket. I can easily supply the KRBCCNAME env variable (I do that for other apps using the KRB5 library). I am doing this in Docker because the I believe it is expecting to look for the ticket cache in a "temp/" folder. So I need to tell to use a specific one since. If I run this within Pycharm I don't need to do anything, but when I put it in a container, it can't find the ticket. I have a separate python file called kerberos.py (below). You can see the commented out lines where I was trying to use the gssapi.raw.ext_cred_store.store_cred_into API, obviously I don't know what I'm doing! This was from an example I found online, I think it might be yours?
import gssapi
def kinit(username=None, password=None, realm=None, exe=None, keytab=None, krb5ccname=None, verbose=False):
server_name = gssapi.Name('krbtgt/' + realm)
user = gssapi.Name(base=username, name_type=gssapi.NameType.user)
bpass = password.encode('utf-8')
result = False
try:
creds = gssapi.raw.acquire_cred_with_password(user, bpass, usage='initiate')
creds = creds.creds
# cache_store = {'FILE', '/etc/ccache'}
# gssapi.raw.ext_cred_store.store_cred_into(cache_store)
context = gssapi.SecurityContext(name=server_name, creds=creds, usage='initiate')
result = True
except AttributeError:
print("AttributeError")
except gssapi.exceptions.GSSError as er:
print(er)
# acquire_cred_with_password returns a wrapper, we want the creds
# object inside this wrapper
print(result)
Beta Was this translation helpful? Give feedback.
All reactions
-
The gss_store_cred_into
can be annoying to use, I've found that the cache needs to exist for it to work unless you specify overwrite=True
. We even do this in the tests
python-gssapi/gssapi/tests/test_raw.py
Lines 472 to 473 in b056666
A few more things to point out
- The cache store key should identify the type of store to use, for Kerberos you want
ccache
acquire_cred_with_password
will always get a MEMORY ccache credential- Unless you need persistence beyond the process you don't need to store it in a file
- Using the
creds
kwarg onSecurityContext
will have it only use that credential, it won't lookup the ccache ofKRB5CCNAME
So for this to work you need to do
import os import gssapi username = '...' password = '...' user = gssapi.Name(base=username, name_type=gssapi.NameType.user) bpass = password.encode('utf-8') creds = gssapi.raw.acquire_cred_with_password(user, bpass, usage='initiate').creds if krb5ccname := os.environ.get('KRB5CCNAME', None): store = { 'ccache': f'FILE:{krb5ccname}', } gssapi.raw.ext_cred_store.store_cred_into(store, creds, overwrite=True) ...
Beta Was this translation helpful? Give feedback.
All reactions
-
I gave this a try today and I am running into this issue:
gssapi.raw.ext_cred_store.store_cred_into(store, creds, overwrite=True)
AttributeError: module 'gssapi.raw' has no attribute 'ext_cred_store'
I commented out the AttributeError catch block and let the code fall through to the next general exception catch. That's how I got the details above.
I've checked the docs and there indeed is an 'ext_cred_store' attribute.
Do I need to import something else?
Here's the code I used:
def kinit(username=None, password=None, realm=None, exe=None, keytab=None, verbose=False):
server_name = gssapi.Name('krbtgt/' + realm)
user = gssapi.Name(base=username, name_type=gssapi.NameType.user)
bpass = password.encode('utf-8')
result = False
try:
creds = gssapi.raw.acquire_cred_with_password(user, bpass, usage='initiate')
creds = creds.creds
if krb5ccname := os.environ.get('KRB5CCNAME', None):
krb5ccname='temp'
store = {
'ccache': f'FILE:{krb5ccname}',
}
gssapi.raw.ext_cred_store.store_cred_into(store, creds, overwrite=True)
result = True
#except AttributeError:
# print("AttributeError")
except gssapi.exceptions.GSSError as er:
print(er)
# acquire_cred_with_password returns a wrapper, we want the creds
# object inside this wrapper
print(result)
Beta Was this translation helpful? Give feedback.
All reactions
-
AttributeError: module 'gssapi.raw' has no attribute 'ext_cred_store'
This means that your GSSAPI C library doesn't have the extension methods present so you won't be able to call it in python-gssapi. There's little that we can do about that unfortunately. If the C library doesn't have it Python can't call it. I believe this has only been recently added to Heimdal based GSSAPI libs but that hasn't made it into an actual release. Considering your default is API
I'm guessing you are on a macOS host which uses their own forked version of Heimdal so it makes sense they don't have it.
Unfortunately your options are limited, if you really need to persist the credentials to a file you might be better off using the krb5 APIs directly with https://github.com/jborean93/pykrb5. if you didn't need to persist the TGT then why are you wanting to call store_cred_into
?
An example of kinit
using the raw krb5 APIs can be seen in https://github.com/jborean93/pyspnego/blob/main/src/spnego/_gss.py#L183-L285. It shows you how it can store a credential in a custom ccache (the implementation there uses a unique MEMORY ccache but you can change it to use a file). It also shows how you can load a credential from the library for use with this one.
Beta Was this translation helpful? Give feedback.
All reactions
-
Hi Jordan,
The reason I want to keep this TGT in it's own location is because it is a predictable location. When I run this in Python I can use the memory location fine, but when I package it up in Docker, it can't find the TGT. Why, I don't know. However,using a pre-defined location I can say "Go look here for the ticket" and it will find it. I'll look into using the KRB5 library. Worse case, I guess I could shell out to the kinit app.
Beta Was this translation helpful? Give feedback.
All reactions
-
But if you are using an explicit credential at runtime you don't need to store the TGT at all. You have an in memory credential acquired by acquire_cred_with_password
that you can use directly with gssapi.SecurityContext(creds=cred)
. I don't understand why you need to cache it, and if you do, why can't you just rely on whatever has been cached already.
Beta Was this translation helpful? Give feedback.
All reactions
-
The main reason I am trying to assert some kind of control is that within a container it doesn't seem to work. Like I said, outside of a Docker container it works great (using memory credential). But when that same code is running within a Docker container, it doesn't know where to find the ticket. I get an error like this. Note that it is looking for the ticket in FILE:/tmp/krbcc_0.
pyodbc.Error: ('HY000', '[HY000] [Microsoft][ODBC Driver 18 for SQL Server]SSPI Provider: No Kerberos credentials available (default cache: FILE:/tmp/krb5cc_0) (851968) (SQLDriverConnect)')
Beta Was this translation helpful? Give feedback.
All reactions
-
Ah ok I think I see now, you don't control the code that creates the security context so you can't provide it the credentials retrieved from acquire_cred_with_password
? At least with Heimdal based implementations without store_cred_into
you are limited to the following:
- Calling
kinit
as a subprocess with an explicitKRB5CCNAME
env var - Using the krb5 API directly to get the cred name to set to
KRB5CCNAME
- you should still be able to use the MEMORY ccache it's just now you know the ccache type and name to set toKRB5CCNAME
Beta Was this translation helpful? Give feedback.
All reactions
-
Yeah, it's not the end of the working to call kinit as a sub-process, but requires I install kinit into the container. Also, not a big deal. All I'm trying to solve for now is to be able to use a service account to make connections to the db and that requires kerberos. It's the curse of working in a serverless environment. The containers (in AWS) are not domain-joined so they have no awareness of AD/KDC. You have to do this manually. With Fargate, which allows multiple containers to run in the same task, I use a sidecar that does all the KDC Auth and brings the TGT back to the container and shares the ticket with the application container. Unfortunately the new service I am using, AWS Batch, only a single container to run so that's what I am trying to create now: one image that get an ticket for a given service principal and password, and then running some processing against a SQL Server db (uses a trusted connection) then exiting the container.
Beta Was this translation helpful? Give feedback.