2

I am attempting to request a json response from an intranet site that uses Kerberos authentication.

I make a kinit like so (without a keytab):

kinit employee_id@CORP.MYCOMPANY.COM

After doing so, the following code works and gives me the exact json response that I want:

def __make_request(self):
    curl = pycurl.Curl()
    data = BytesIO()
    curl.setopt(pycurl.HTTPAUTH, pycurl.HTTPAUTH_GSSNEGOTIATE)
    curl.setopt(pycurl.USERPWD, self.pwd)
    curl.setopt(pycurl.WRITEFUNCTION, data.write)
    curl.setopt(pycurl.URL, self.url)
    curl.perform()
    curl.close()
    return json.loads(data.getvalue())

This of course seems too easy. I decided to try it with the gssapi:

server_name = gssapi.Name('HTTP/service_principal_here@OTHER_REALM.COM') <-listed on TGT
canon_name = server_name.canonicalize(gssapi.MechType.kerberos)
ctx = gssapi.SecurityContext(name=canon_name, usage='initiate')
token = ctx.step()
token64 = base64.b64encode(token)

(token64 will print a long encoded token)

h = {"www-authenticate": "Negotiate " + token64}
r = requests.get("same url used with pycurl above", headers = h)

This results in a 403 (not a 401)

Any ideas on what is wrong with my request?

  • Out of curiosity, how does that intranet site handle _session credentials_? It's generally considered a bad practice to enforce SPNego/Kerberos for all requests because it's inefficient; for instance the Hadoop ecosystem uses a session token stored in a "signed cookie" _(with one exception where the client has to retrieve the token explicitly then pass it explicitly in the URL)_. You don't seem to manage cookies, so I'm wondering. – Samson Scharfrichter Aug 15 '18 at 20:22
  • What do you mean by session credentials? In my real code, I get the o/p (via subprocess module) of klist -s and check if the return code is 0 (meaning a valid ticket). Apparently my employee ID and REALM (used when performing a kinit) are enough as that is listed as the service principal? Are you saying that I shouldn't just create a token for each request.. but check to see if there is already a token? As of now I just check if the TGT is valid (it's at default of 10 hours) and then create a token. –  Aug 16 '18 at 13:24
  • I guess I should also mention that the only user of this token is my Flask web app ie: the intranet site (when it needs to hit the other server for the json response) –  Aug 16 '18 at 13:26
  • Kerberos uses _tickets_. Applications may use _tokens_ internally. Strong authentication once, against a specific service on a specific server, then just a token valid for all servers/containers, but for a few minutes only. – Samson Scharfrichter Aug 16 '18 at 17:49
  • Got it. Thank you for the help –  Aug 16 '18 at 22:50

1 Answers1

1

Ok I figured it out. I was sending the token in the header incorrectly. The header should actually look like this:

headers = {"Authorization": "Negotiate " + bt64}

I am unsure why anyone would use gssapi and requests over pycurl... especially where the documentation for gssapi is not that great.

  • And especially since pushing the Kerberos token blindly, without being challenged by a 401, negates the "negotiate" assumption of `SPNego` protocol. – Samson Scharfrichter Aug 15 '18 at 20:18