Authorization of users in Django through GSSAPI and delegation of user rights to the server

Recently, my colleagues and I needed to implement transparent (SSO) authorization in our project. Now there is quite a bit of information on the topic, especially in Russian. For this reason, it was decided to share with the descendants the implementation of this functionality.

So, the task was as follows: it was necessary to configure transparent authorization via GSSAPI from the user to the server, and also to have the opportunity to go to the database on behalf of this user.

We had:

  • configured Kerberos + LDAP server
  • PostgreSQL server configured to authorize exclusively using GSSAPI
  • Django + UWSGI + nginx application server, with Kerberos configured

Initially, the idea was to delegate user authorization in a web server application, setting up GSSAPI authorization on it, and Django to indicate that we will work with RemoteUser. As part of this description, I will not tell you how to configure nginx to work on GSSAPI, and Django to delegate authorization to a web server, this is a well-documented part, and there are quite a lot of articles on this. After setting up and conducting tests - we realized that this is absolutely not what we need. Yes, we can authorize and get the user principal name, but we do not have rights to do anything on behalf of this user.

Next, we have attempted to find something worthwhile on the Internet. They were crowned with relative success, the following packages for Django were found: django-kerberos ,django-auth-spnego , django-auth-kerbero . In fact, all these packages did the same thing, some were not updated for a long time and had to “dance with a tambourine” for a long time, so that at least something would work. They provided the same functionality as the nginx bundle (GSSAPI) + Django (RemouteUser). They all helped to come to the solution of the problem with their source code.

Next, the following packages were found for working with GSSAPI in Python: ccs-pykerberos and python-gssapi , in fact, they import the implementation of the RFC2744 standard and RFC4559 in Python. With the help of the ccs-pykerberos package, we just managed to implement the conceived functionality, then I will show some code where communication with LDAP and the user is realized, as well as a query to the database on its behalf.

from django.shortcuts import render
from django.template.response import TemplateResponse
import kerberos
import psycopg2
defindex(request):if'HTTP_AUTHORIZATION'in request.META:
        kind, initial_client_token = request.META['HTTP_AUTHORIZATION'].split(' ', 1)
        if kind == 'Negotiate':
            service = ''
            _ignore_result, krb_context = kerberos.authGSSServerInit(service)                        
            kerberos.authGSSServerStep(krb_context, initial_client_token)            
            principal = kerberos.authGSSServerUserName(krb_context)
            _ignore_result = kerberos.authGSSServerStoreDelegate(krb_context)            
            conn = psycopg2.connect(
            cursor = conn.cursor()
            cursor.execute("SELECT version()")
            records = cursor.fetchall()
        unauthorized_template_name = 'gssapi_test/unauthorized.html'
        response = TemplateResponse(request, 'gssapi_test/index.html', status=401)
        response['WWW-Authenticate'] = 'Negotiate'return response    
    content = {'records': records}
    return render(request, 'gssapi_test/index.html', content)

First, we need to check whether the authorization header was passed to us, if not - we should send a response with Negotiate in response, and then wait for the token from the Negotiate user. This token looks like a large footcloth encoded in base64. After receiving the token, we initialize (authorize) the server of our application in the LDAP service using the authGSSServerInit () function. Next, we log in to the LDAP service on behalf of the user, using just the token that was received from the header, the authGSSServerStep () function. Then we get the user's principal, which we will use as the user, when executing the query in the database. And also, we need to create a Kerberos bitel cache, which will be used automatically to authorize us in PostgreSQL, the authGSSServerStoreDelegate () function.

The browser must be configured to return Negotiate, by default this option is disabled. All tests were conducted on Firefox in CentOS7, CentOS7 was installed on all servers as well.

In addition, we may have a problem in which the ticket cache generated by our function will not be visible to our process and, accordingly, we will receive that the user has not logged in to the GSSAPI. It is solved by setting ticket caching in krb5.conf. Just in case I will give an example of our config:

includedir /etc/krb5.conf.d/
includedir /var/lib/sss/pubconf/krb5.include.d/
  default_realm = DOMAIN.RU
  dns_lookup_realm = false
  dns_lookup_kdc = false
  rdns = false
  ticket_lifetime = 24h
  forwardable = true
  udp_preference_limit = 0# если раскомментировать опцию - работать не будет#default_ccache_name = KEYRING:persistent:%{uid}#Нужно для определения параметра KRB5_KTNAME в облатси видимости приложения
  default_keytab_name = FILE:/etc/httpd/http.keytab
    kdc =
    master_kdc =
    admin_server =
    kpasswd_server = 
    default_domain =
    pkinit_anchors = FILE:/etc/domain/ca.crt
[domain_realm] = DOMAIN.RU = DOMAIN.RU = DOMAIN.RU

This piece of code was created in order to help understand how authorization occurs and the delegation of rights, then with this knowledge you can build authorization decorators and backlinks to communicate with the database. The ccs-pykerberos package was developed by Apple, for its internal needs, respectively, I will provide a link to their code where they use it. He helped us a lot in understanding that they developed ccs-calendarserver / twistedcaldav /

useful links

Also popular now: