Недавно нам с коллегами понадобилось реализовать прозрачную (SSO) авторизацию в нашем проекте. Сейчас довольно мало информации по теме особенно на русском языке. По этой причине решено было поделиться с потомками реализацией подобного функционала.

Итак задача заключалась в следующем: необходимо было настроить прозрачную авторизацию через GSSAPI от пользователя на сервер, а так же иметь потом возможность от имени этого пользователя ходить в БД.

У нас имелось:

  • настроенный сервер Kerberos+LDAP
  • сервер PostgreSQL, настроенный на авторизацию исключительно по GSSAPI
  • сервер приложения Django+UWSGI+nginx, с настроенным Kerberos

Изначально была идея делегировать авторизацию пользователей в приложении веб серверу, настроив на нем авторизацию по GSSAPI, а Django указать, что мы будем работать с RemoteUser. В рамках данного описания, я не буду рассказывать, как настроить nginx на работу по GSSAPI, а Django на делегацию авторизации на веб сервер, это хорошо задокументированная часть, да и статей по этому поводу довольно много. После настройки и проведенных тестов — мы поняли, что это абсолютно не то, что нам нужно. Да мы можем провести авторизацию и получить user principal name, но мы не имеем прав от имени этого пользователя ничего сделать.

Далее нами были предприняты попытки найти что-то стоящее на просторах интернета. Они увенчались относительным успехом, были найдены следующие пакеты для Django: django-kerberos, django-auth-spnego, django-auth-kerbero. По сути все эти пакеты делали одно и тоже, некоторые не обновлялись уже давно и пришлось долго «танцевать с бубном», что бы хоть что то заработало. Они предоставляли такой же функционал как и связка nginx(GSSAPI)+Django(RemouteUser). Все они помогли прийти к решению проблемы своим исходным кодом.

Далее были найдены следующие пакеты для работы с GSSAPI в Python: ccs-pykerberos и python-gssapi, по сути они импортируют реализацию стандарта RFC2744 и RFC4559 в Python. С помощью пакета ccs-pykerberos у нас как раз и получилось реализовать задуманный функционал, далее я покажу немного кода, где реализуется общение с LDAP`ом и пользователем, а так же запрос в БД от его имени.

from django.shortcuts import render
from django.template.response import TemplateResponse
import kerberos
import psycopg2


def index(request):    
    if 'HTTP_AUTHORIZATION' in request.META:
        kind, initial_client_token = request.META['HTTP_AUTHORIZATION'].split(' ', 1)
        if kind == 'Negotiate':
            service = 'HTTP@django-server-pricipal.che.ru'
            _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(
                host='postgresql-server-host',
                user=principal,
                dbname='request-db',
            )
            cursor = conn.cursor()
            cursor.execute("SELECT version()")
            records = cursor.fetchall()

    else:
        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)

Сначала нужно проверить передан ли нам заголовок авторизации, если нет — мы должны направить в ответ заголовок с Negotiate, и снова ждать от пользователя Negotiate токен. Этот токен выглядит как большая портянка закодированная в base64. После получения токена, мы инициализируем (авторизуем) сервер нашего приложения в LDAP сервисе, используя функцию authGSSServerInit(). Далее мы авторизуемся в LDAP сервисе от имени пользователя, используя для этого как раз тот токен, который получили из заголовка, функция authGSSServerStep(). Потом мы получаем principal пользователя, который будем использовать в качестве user, при выполнении запроса в БД. А так же, нам необходимо сформировать кэш битела Kerberos, который будет использован автоматически для того, что бы авторизовать нас в PostgreSQL, функция authGSSServerStoreDelegate(). Данная функция есть только в самой последней версии этого пакета, поэтому нужно клонировать себе исходники с git и сделать build.

Браузер должен быть настроен на отдачу Negotiate, по умолчанию эта опция отключена. Все тесты проводились на Firefox в CentOS7, так же на всех серверах был установлен CentOS7.

В добавок, у нас может возникнуть проблема, при которой кэш билета, сформированный нашей функцией, не будет виден нашему процессу и соответственно мы получим, что пользователь не авторизовался в GSSAPI. Она решается настройкой кеширования билетов в krb5.conf. На всякий случай приведу пример нашего конфига:

/etc/krb5.conf
includedir /etc/krb5.conf.d/
includedir /var/lib/sss/pubconf/krb5.include.d/

[libdefaults]
  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



[realms]
  DOMAIN.RU = {
    kdc = ldap-server-host.domain.ru:88
    master_kdc = ldap-server-host.domain.ru:88
    admin_server = ldap-server-host.domain.ru:749
    kpasswd_server = ldap-server-host.domain.ru:464 
    default_domain = domain.ru
    pkinit_anchors = FILE:/etc/domain/ca.crt

  }

[domain_realm]
  .domain.ru = DOMAIN.RU
  domain.ru = DOMAIN.RU
  .domain.ru = DOMAIN.RU



Данный кусок кода создан для того, что бы помочь понять как происходит авторизация и делегация прав, далее с этими знаниями можно строить декораторы авторизации и бэки общения с базой. Пакет ccs-pykerberos был разработан компанией Apple, для своих внутренних нужд, соответственно приведу ссылку на их код, где они его используют. Нам он очень помог в понимании того, что они разработали ccs-calendarserver/twistedcaldav/authkerb.py

Полезные ссылки


Комментарии (0)