Pubcookie: единая точка аутентификации для веб-приложений

    Существует множество решений, позволяющих в том или ином виде реализовать технологию единого входа (Single Sign On). Под единым входом понимается ситуация, когда авторизовавшись один раз на некотором выделенном сервере авторизации (или просто на своей машине), вы получаете доступ ко всем доступным сетевым ресурсам без дополнительной авторизации.

    Мы сейчас постепенно занимаемся рефакторингом наших внутренних интранет-служб, развивавшихся зачастую стихийно, и рассматриваем различные технологии, использование которых может сделать нашу внутреннюю инфраструктуру удобнее и безопаснее.

    Технологии SSO, как правило, достаточно непростые, однако если ограничиться задачей единого входа только для набора веб-приложений, существует относительно несложное решение, позволяющее реализовать такую возможность, не меняя, или незначительно меняя код приложения. Это решение называется Pubcookie, о нем и пойдет речь.



    Если мы используем Pubcookie, то при заходе на защищенный ресурс нас автоматически редиректят на специальный выделенный логин-сервер, на котором мы авторизуемся и автоматически возвращаемся назад. При этом нам не нужно ничего менять в защищаемом приложении, оно как и раньше получает имя текущего пользователя из REMOTE_USER.

    Посмотрим, каким образом можно настроить этот механизм.

    Схема работы

    В процессе аутентификации задействованы следующие компоненты:

    1. Клиентский браузер;
    2. Сервер приложений, на котором развернуто приложение, требующее аутентификации, в нашем случае это Apache с модулем Pubсookie и, собственно, защищаемым приложением;
    3. Логин-сервер, в нашем случае это опять же Apache со стандартным приложением аутентификации и нашими настройками этого приложения;
    4. Собственно, сервис аутентификации.


    Аутентификация производится следующим образом:

    1. Пользовательский браузер запрашивает определенный ресурс с сервера приложений, сконфигурированного для использования Pubcookie.
    2. Модуль Pubcookie, установленный на сервере приложений, перехватывает запрос и проверяет, что он не связан с валидной текущей сессией и не содержит информацию от логин-сервера (т.н. granting cookie) для создания новой сессии.
      В случае успешной проверки модуль генерирует отклик, содержащий редирект и две куки: presession cookie для приложения и granting request cookie для логин-сервера Обе куки содержат, помимо прочей информации, случайное число, сгенерированное модулем. Все куки передаются в зашифрованном виде.
    3. Клиентский броузер выполняет редирект (granting request) к логин-серверу с передачей ему granting request cookie. Данные запроса позволяют серверу доступа определить сервер приложений, для которого запрашивается аутентификация, URL оригинального запроса, тип авторизации и т.д.
    4. Логин-сервер декодирует granting request cookie и интерпретирует содержимое. Сервер генерирует форму входа и отсылает ее клиенту.
    5. Пользователь вводит свои логин и пароль в форму и отсылает ее на логин-сервер;
    6. Логин-сервер получает логин и пароль и отсылает их используемому сервису аутентификации для проверки.
    7. Логин-сервер получает результат проверки от сервиса аутентификации.
    8. Если проверка прошла успешно, логин-сервер формирует отклик, содержащий редирект и две новых куки. Первая, granting cookie, предназначена для сервера приложений и содержит проверенное имя пользователя и дополнительную информацию, включая случайное число, сгенерированное в пункте 2, подписана приватным ключем сервера доступа и зашифрована симметричным ключем, используемым на сервере приложений и сервере доступа. Вторая, login cookie, предназначена для логин-сервера и используется в случае последующих заходов пользователя на него.
    9. Пользовательский браузер повторно запрашивает защищенный ресурс с сервера приложений. Запрос содержит granting cookie и presession cookie.
    10. Модуль pubcookie на сервере приложений перехватывает запрос, расшифровывает granting cookie, проверяет подпись и сравнивает случайное число в granting cookie с аналогичным числом в presession cookie. Если все сходится, имя пользователя передается приложению и выполняется обработка запроса приложением. При этом также генерируется session cookie, для последующих обращений к защищенным ресурсам. Отклик, сгенерированный приложением, отсылается пользователю.


    В случае, если на шаге 4 granting request содержит также валидную login cookie (установленную на шаге 8), шаги 5, 6 и 7 пропускаются и логин-сервер переходит сразу к шагу 8, генерируя granting cookie использyя имя пользователя, полученное из login cookie. Таким образом, мы получаем элемент технологии Single Sign On для набора веб-приложений: не нужно авторизоваться для каждого веб-приложения, достаточно указать свои логин и пароль один раз.

    Сборка

    Мы рассмотрим для Ubuntu, под FreeBSD тоже не вызывает проблем:

    Скачиваем дистрибутив с www.pubcookie.org. Перед сборкой делаем два патча:

    1. Патчим configure на предмет проблемы с APXS:

    3783c3783
    <   APACHE_PREFIX=`$APXS -q PREFIX`
    ---
    >   APACHE_PREFIX="/usr/share/apache2"
    


    Тут методом удара молотком, можно и правильнее, наверное, сделать.

    2. Патчим src/index.cgi.c на предмет UTF-8 кодировки:

    461c461
    <     print_header (p, "Content-Type: text/html; charset=ISO-8859-1\n");
    ---
    >     print_header (p, "Content-Type: text/html; charset=utf-8\n");
    


    При конфигурировании указызываем сборку модуля и cgi-приложения для входа:

    $./configure --enable-apache --with-apxs=/usr/bin/apxs2 --enable-login
    


    Потом собираем, желательно в deb-пакет.

    Генерация ключей и сертификатов

    Для работы с защищенными ресурсами используется https. Ну, на то они и защищенные.

    Генерируем две пары ключей.

    Первая — SSL key pair, используется апачем для SSL и сервером ключей (о нем ниже):

    $ openssl req -new -x509 -out server.crt -newkey rsa:1024 -nodes -keyout server.key
    


    Вторая — granting key pair, для подписи и проверки granting cookies:

    $ openssl req -new -x509 -out granting.crt -newkey rsa:1024 -nodes -keyout granting.key
    


    Пример чисто демонстрационный, мы используем self-signed сертификаты, в реальности все сложнее. Соответственно генерируем игрушечный CA bundle:

    $ cp server.crt ca-bundle.crt
    


    В результате получаем файлы: server.crt, server.key, granting.crt, granting.key, ca-bundle.crt.

    Настройка логин-сервера

    Пусть сервер называется access.techart.intranet

    На сервере доступа выполняются сервер ключей и приложение аутентификации под управлением веб-сервера.

    Сервер ключей распространяет симметричные ключи, использующиеся для шифрования содержимого кук. Он запускается под xinetd, соответственно, прописываем его туда:

    service keyserver
    {
      type                   = UNLISTED
      protocol             = tcp
      port                   = 2222
      disable              = no
      socket_type           = stream
      wait                  = no
      user                  = root
      group                 = tty
      server                = /usr/local/pubcookie/keyserver
    }
    


    После перезапуска xinetd сервер доступен на порту 2222.

    Приложение аутентификации — это обычное CGI-приложение, которое должно быть доступно по https. Соответственно, для него необходимо настроить virtual host:

    <VirtualHost *:443>
      ServerName login.techart.intranet
      DocumentRoot /usr/local/pubcookie/login
      DirectoryIndex index.cgi
      AddHandler cgi-script cgi
    
      <Directory />
        Options FoolowSymLinks
        Options +ExecCGI
        AllowOverride None
      </Directory>
     
      SSLEngine on
      SSLCertificateFile /usr/local/pubcookie/keys/server.crt
      SSLCertificateKeyFile /usr/local/pubcookie/keys/server.key
    </VirtualHost>
    


    Осталось сконфигурировать pubcookie. Правим файл /usr/local/pubcookie/config:

    # Уровень детализации логов
    logging_level: 1
    
    # Используемый сервис аутентификации. 
    # В нашем примере мы используем пользовательский скрипт auth.php, 
    # который обращается к базе данных.
    basic_verifier: verify_fork
    verify_exe: /usr/local/pubcookie/auth.php
    
    # Пара ключей SSL
    ssl_key_file: /usr/local/pubcookie/keys/server.key
    ssl_cert_file: /usr/local/pubcookie/keys/server.crt
    
    # Пара granting ключей 
    granting_key_file: /usr/local/pubcookie/keys/granting.key
    granting_cert_file: /usr/local/pubcookie/keys/granting.crt 
    
    # Логин-сервер (CGI)
    login_uri: https://access.techart.intranet/
    login_host: access.techart.intranet
    enterprise_domain: .techart.intranet
    logout_prog: /logout/index.cgi
    
    # Сервер ключей
    keymgt_uri: localhost:2222
    ssl_ca_file: /usr/local/pubcookie/keys/ca-bundle.crt
    
    # Срок жизни сессии
    default_l_expire: 8h
    
    # Параметры шаблонов логин-сервера, в частности, сообщение о закрытии сессии.
    app_logout_string-app1.techart.intranet-app1: Разлогинились из app1
    


    В этом примере мы используем режим verify_fork, когда в качестве сервиса аутентификации используется произвольная внешняя программа, возвращающая нормальный или ошибочный код завершения в зависимости от того, был ли опознан пользователь. При этом имя пользователя и пароль программа читает из stdin.

    В нашем случае это php-скрипт, который может, например, обратиться в базе данных или текстовому файлу, вместе с тем, поддерживаются и другие режимы, например, работа с LDAP и Kerberos.

    Теперь генерируем ключ, позволяющий логин-серверу соединиться с сервером ключей:

    $ sudo keyclient -P access.techart.intranet
    


    Сгенерированный ключ будет записан в keys/access.techart.intranet. Эту операцию необходимо выполнить для каждого хоста, использующего Pubcookie.

    После выполнения всех этих манипуляций по адресу access.techart.intranet мы должны увидеть форму логина.

    Настройка сервера приложений.

    На сервере приложения настраиваем конфигурационный файл pubcookie так же, как и на сервере доступа. Помимо этого, необходимо подключить к Апачу модуль mod_pubcookie.

    Для этого создаем файл /etc/apache2/mods-available/pubcookie.conf:

    PubcookieGrantingCertFile /usr/local/pubcookie/keys/granting.crt
    PubcookieSessionKeyFile /usr/local/pubcookie/keys/server.key
    PubcookieSessionCertFile /usr/local/pubcookie/keys/server.crt
    PubcookieKeyDir /usr/local/pubcookie/keys/
    
    PubcookieLogin https://access.techart.intranet/
    PubcookieLoginMethod POST
    PubcookieDomain .techart.intranet
    
    PubcookieEncryption AES
    PubcookieAuthTypeNames EGNetID
    


    Дальше все как обычно:

    /etc/apache2/mods-available/pubcookie.load:
    
    LoadModule pubcookie_module /usr/lib/apache2/modules/mod_pubcookie.so
    $ sudo a2enmod pubcookie
    $ sudo service restart apache2
    


    Теперь настраиваем собственно приложение:

    <VirtualHost *:443>
      ServerName app1.techart.intranet
      <Directory /srv/http/app1>
    
        AuthType EGNetID
        require valid-user
        PubcookieAppID app1
    
        Options Indexes FollowSymLinks MultiViews
        AllowOverride None
        AllowOverride AuthConfig
        Order allow,deny
        allow from all
      </Directory>
    
      SSLEngine on
      SSLCertificateFile    /usr/local/pubcookie/keys/server.cert
      SSLCertificateKeyFile /usr/local/pubcookie/keys/server.key
    </VirtualHost>
    


    В данном случае мы закрыли доступ ко всему приложению, однако мы могли бы прописать опции аутентификации для конкретного подкаталога, вынести их в .htaccess и т.д.

    Установка опции AllowOverride AuthConfig позволяет приложению получить имя текущего пользователя из REMOTE_USER.

    Перезапускаем Apache, и если все прошло нормально, то при заходе на app1.techart.intranet нас должно отредиректить на login.techart.intranet, где мы авторизуемся и автоматически возвращаемся обратно.

    Виртуальные хосты

    Все вышеописанное работает для виртуальных хостов, привязанных к выделенному IP. Для работы с name-based virtual hosts необходима поддержка SNI в OpenSSL, (с версии 0.9.8j) и в Apache (с 2.2.12). Кроме того, SNI не поддерживается IE версий ниже 7.

    Для такой конфигурации поле Common Name должно содержать список используемых хостов. Правим openssl.cnf и заново генерируем набор ключей.

    [req_distinguished_name]
    0.commonName      = Common Name (eg, YOUR name)
    0.commonName_default    = app1.techart.intranet
    0.commonName_max    = 64
    
    1.commonName      = Common Name (eg, YOUR name)
    1.commonName_default    = app2.techart.intranet
    1.commonName_max    = 64
    ...
    


    В описание виртуального хоста в Apache прописываем:

    SLCipherSuite HIGH
    SSLProtocol all -SSLv2
    


    и перезапускаем apache.

    Настройка пользовательского интерфейса сервера доступа

    Внешний вид формы логина и служебных страниц логин-сервера можно настроить с помощью шаблонов, которые лежат в каталоге login_templates. Для использования UTF-8 нужно пропатчить index.cgi.c.

    Настройка logout

    Закрытие сессии может быть выполнено как в отдельном приложении, так и на логин-сервере. Самые простой способ сделать это на логин-сервере — создать подкаталог logout с .htaccess, содержащим единственную директиву:

    PubcookieEndSession redirect
    


    Итого

    В целом механизм достаточно простой, основная сложность в настройке связана не столько с Pubcookie, сколько с OpenSSL.

    Соответственно, в случае внедрения самое важное — правильно настроить базовую инфраструктуру управления ключами/сертификатами.

    В то же время, в ситуации, когда в интранет-сети есть достаточно много legacy-приложений, использование Pubcookie позволяет с минимальными усилиями реализовать для них централизованный контроль доступа.

    Несколько ссылок:

    Метки:
    Маркетинговая группа TechArt 32,24
    Компания
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 3
    • 0
      Рискну заметить, что приложения бывают разные. Для одних достаточно знать что это пользователь Nice Girl пусть и с валидным адресом электроной почты, другим же, необходимо знать что это точно Мария Ивановна Иванова, 25.10.1917 г.р. Как ваша технология может обеспечить достоверность данных?

      По этой причине скептически отношусь к авторизациям через соц. сети. Сами знаете какие там ники.
      • 0
        Честно говоря, мы рассматриваем этот механизм как вариант для интранет-сети, тут таких проблем быть не должно. Собственно, по большей части этот механизм так и используется, наиболее распространенная область применения — внутренние университетские сети.
        • 0
          а просто CAS поставить не пробовали?

          Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

          Самое читаемое