Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Specifying KRB5CCNAME #309

anybodysguest started this conversation in General
Dec 19, 2022 · 6 comments · 4 replies
Discussion options

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?

You must be logged in to vote

Replies: 6 comments 4 replies

Comment options

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?

You must be logged in to vote
0 replies
Comment options

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)
You must be logged in to vote
0 replies
Comment options

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

store_res = gb.store_cred_into(store, initial_creds, overwrite=True,
**store_kwargs)
for this function. Even then I've not had too much luck with it before.

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 on SecurityContext will have it only use that credential, it won't lookup the ccache of KRB5CCNAME

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)
...
You must be logged in to vote
0 replies
Comment options

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)
You must be logged in to vote
0 replies
Comment options

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.

You must be logged in to vote
4 replies
Comment options

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.

Comment options

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.

Comment options

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)')

Comment options

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 explicit KRB5CCNAME 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 to KRB5CCNAME
Comment options

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.

You must be logged in to vote
0 replies
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet

AltStyle によって変換されたページ (->オリジナル) /