Pull to refresh

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

Reading time 8 min
Views 6.9K
Существует множество решений, позволяющих в том или ином виде реализовать технологию единого входа (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 позволяет с минимальными усилиями реализовать для них централизованный контроль доступа.

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

Tags:
Hubs:
+13
Comments 3
Comments Comments 3

Articles

Information

Website
www.techart.ru
Registered
Founded
Employees
101–200 employees
Location
Россия