CosigNego is a Python library (usable from the shell) which bridges the gap between Cosign and Kerberos (actually GSSAPI via SPNEGO) authentication. It's customised for use on Informatics sites due to limitations in the way Cosign and/or SPNEGO work together.

DICE USERS: This works, and has had a few years' testing, but the tool is available on a Not-A-Service basis only. Use with caution!

The Problem

Our managed clients (and most Kerberised self-managed clients) can bypass the password dialog presented by our Cosign service using a trick on the cosign login page itself. Specifically the Cosign server attempts to access an SPNEGO-protected resource using a Javascript HTTP (HEAD) Request. If this succeeds (demonstrating that the user agent supports SPNEGO and has valid credentials) the Javascript initiates a redirect to an SPNEGO-protected version of the login CGI, allowing appropriate cookies to be set.

The trick seems to be implemented using Javascript because there does not appear to be any other means of conditionally redirecting users from the cosign 'landing' page as provided by cosign client webservers and the downsides of this are obvious: the functionality is being implemented in-band, using a mechanism only supported by browsers which are able and willing to interpret and execute Javascript. Otherwise it requires prior knowledge of the two different, "secret" (well, effectively secret to any client which doesn't talk Javascript) locations in order to pass. A particular problem is that it's difficult even to detect automatically whether or not you're authorised, given that the user agent will end up with a 200 no matter what.

So what we do is check the chain of redirects.

  1. This is what happens if we're pre-authenticated: GET → 200 (dest)
    • The '200' is the requested page (or should be).
  2. This is what happens if there are cosign cookies, but not for this site: GET → 302, 302, 302, 301, 200 (dest)
    • The '200' is the requested page (or should be).
  3. This is what happens if there are no cosign cookies, and no credentials: GET → 302, 302, 200 (weblogin)
    • The '200' is the request for parameters. At this point we should get cookies.
  4. This is what happens if cosign requests revalidation due e.g. to a new client IP addresss: GET →  ... (weblogin)
    • TODO: need to add this chain to the library! It will simply force a new login process as per '3'.

These paths appear unique, and allow us to determine the state of the Cosign service with reasonable accuracy. We can store the session cookies (note this somewhat breaks the session cookie model, but then browsers aren't normally Other checks can help us reduce the need for gratuitous requests for cookies.

We haven't found anynone who has solved this in a standard way. An obvious answer would be to attempt Negotiate-Auth (SPNEGO) and fall back to Basic (as might IIS in a managed environment), but that doesn't work for authentication schemes which involve requesting a username and password in-band using a CGI form. In this method, as far as the browser sees things, you're falling back to no authentication if Negotiate fails, given that the "error" response necessarily presents the login form with status 200. Is this suggestive that the authentication model is broken by design? It's not clear to me...

Solutions

So, the interim solution is my CosigNego library. This makes the following assumptions:

  • that the authentication process results in a number of predictable HTTP redirects when it succeeds or fails
  • there's one (or more) cosign server which can be trusted to handle all authentication
  • there's a standard (site-specific) cosign login path
  • there's a standard (site-specific) SPNEGO route to this same page

This feels like a client-side hack, though; really it should be following a standard routine (if not An Actual Standard), but there seems to be a gap where one should be.

Some other solutions (that we haven't tried, or really researched) include:

  • Some sort of conditional redirect magic on the Apache server hosting the Cosign login servers. This could direct all users to the SPNEGO page and, on error, bounce them back to the "human" login page.
  • Modifying Cosign to return its login dialog within an HTTP 401, in a path which would return the WWW-Authenticate: Negotiate header expected by an SPNEGO client. This should render cosign correct in HTTP terms and permit both machine and human to authenticate.
  • A companion to WWW-Authenticate: Negotiate which allows multiple methods (Inline perhaps)?
  • Some other header-based trickery: HTTP 300 to denote multiple options!?
  • (Getting desperate) A standardised descriptor file in the root, say /negotiate.(txt|yml|json|xml|format-du-jour) , pointing to the alternative Negotiate location.

Usage

Shipped on DICE as the Python library cosignego with commandline utilities authGET, authPOST and authHTTP. As of version 0.5c:

 (Common to all)

Blends Cosign and Kerberos (SPNEGO) authentication into a yummy soup.
Allows CURL-style GET and POST requests against a URL as provided on the commandline.

Environment
-----------

This script uses the following environment variables to control its behaviour:

$COSIGN_COOKIEFILE
    This is the file in which the script will maintain its cosign cookie file.
    This contains SENSITIVE MATERIAL as it allows others to impersonate you using
    cosign and should be kept safe.

    If this is set to None then the cookie will never be stored locally, but you
    will need to perform the authentication dance every time you use the script.

$COSIGN_SPNEGO_HOST
    The host on which to attempt SPNEGO authentication to automatically retrieve
    cosign cookies.

$COSIGN_COOKIE_PATH
    The path to visit to preauthenticate using SPNEGO.

$COSIGN_SPNEGO_PATH
    The path to visit once authenticated to retrieve your cosign cookies.


 $ authGET --help  or authGET --longhelp (or authPOST, or authHTTP)


Command-Line Utilities
----------------------
The package provides three utilities:

authGET [url] 
    Performs a GET of a cosign-protected URL.
    With no arguments, just obtains cookies.

authPOST url [data=value [...]]
    Performs a POST of a cosign-protected URL.
    Performs a preliminary GET against the same URL to obtain cookies.

authHTTP [--request METHOD] [--preload] url [data=value [...]]
    Performs an arbitrary HTTP request of a cosign-protected URL.
    Requires a URL.

You can also perform a GET with k=v parameters (but no guarantees).

All three commands support the same parameters; the principal difference is
that authPOST will perform a preliminary GET on the Cosign-protected URL before
attempting its POST.

Scripted usage:

 $ python3 -c 'import cosignego; help(cosignego.cosignego)'

DESCRIPTION
    CosigNego: Proof-of-concept combining Cosign and SPNEGO.

    class CosigNego
     |  Methods defined here:
     |  
     |  __init__(self, loginhost, cookiefile)
     |  
     |  getCookie(self, path)
     |      Gets a Cosign cookie using SPNEGO
     |  
     |  loadCosignURL(self, url=None, data=None, method='GET', preload=False)
     |      Loads a Cosign-protected URL by pre authenticating with kerberos.
     |      data: urlencoded data (to be used with method=POST), or None
     |      method: one of the standard HTTP request types.
     |      preload: perform an initial GET prior to a POST if required.
     |  
     [...]

Clients / Use Cases

These can be found in the CoUtils area with development versions usually in my bin directory:
  • wakeweb / wakeweb2
  • nagios-ack and nagios-down
  • cw3m (uses internal script curl2w3m to convert cookies to w3m format)
  • Theon UI server API test client (very incomplete)

Future uses

Actually some of these would be better served by dedicated kerberised HTTP APIs:
  • RT commandline client wrapper
  • HTTP-based getpapers, submit (replacing NFS).

Future Improvements

  • Stop matching redirects; start look at the page content for success/failure report
  • Rewrite as handler for urllib2 and/or requests rewrite as Requests Auth Provider is underway.
  • Handle straightforward Negotiate resources, generically, too -- v0.9 supports this via the --negotiate-dest parameter
  • Optionally fall back to password POST
    • Better SSL handling required for this v0.9 now handles SSL properly

-- GrahamDutton - Apr 2013 — Mar 2018

Topic revision: r11 - 05 Aug 2020 - 13:56:48 - GrahamDutton
 
This site is powered by the TWiki collaboration platformCopyright © by the contributing authors. All material on this collaboration platform is the property of the contributing authors.
Ideas, requests, problems regarding TWiki? Send feedback
This Wiki uses Cookies