UMGUM.COM (лучше) 

Nginx + LDAP Auth ( Наладка аутентификации пользователей сайта под управлением Nginx через внешний LDAP-сервис. )

4 мая 2020

OS: "Linux Debian 8/9/10", "Linux Ubuntu 16/18 LTS".
Application: Nginx, Python, LDAP.

Задача: обеспечить аутентификацию пользователей сайта через внешний LDAP-сервис, средствами web-сервера "Nginx".

С точки зрения эксплуатационщика на предприятии удобно проводить аутентификацию пользователей внутренних сервисов через некий централизованный каталог с учётными данными. Чаще всего для этого применяется сервис "Microsoft Active Directory", но технически почти всегда в этой роли можно использовать иные реализации LDAP, вроде "OpenLDAP" или "389-DS".

В сложносочинённых сайтах задачи аутентификации и авторизации обычно возлагаются на специально для этого предназначенные компоненты внутри информационной системы, для реализации которых во всех распространённых языках программирования имеются библиотеки функций взаимодействия с LDAP. С мелкими сайтами, возможно даже полностью статическими, сложнее - у них нет подкапотных механизмов, посредством которых можно было бы проверять право доступа пользователя к запрашиваемым данным. В таком случае на роль привратника остаётся один кандидат - web-браузер. И вот на этом этапе выясняется, что любимый нами "Nginx" не имеет модуля аутентификации через LDAP.

Как вариант решения поставленной задачи, можно воспользоваться системных модулем "auth_pam", указав "Nginx" проводить аутентификацию через PAM несущей операционной системы ("Linux" или xBSD), в которой настроена связка с LDAP. Лет пять назад разрабатывалась реализация в виде модуля "nginx-auth-ldap", но он устарел сейчас разработчики "Nginx" официально предлагают использовать другой подход, с промежуточным сервисом "nginx-ldap-auth-daemon".

Предлагаемая схема взаимодействий проста и прозрачна. Когда пользователь обращается к защищённому разделу сайта, web-сервер "Nginx" запрашивает посредством протокола "HTTP Basic authentication" логин и пароль. Полученные аутентификационные данные встроенным модулем "http_auth_request" сразу отправляются по протоколу проксирования фоновому web-сервису "LDAP Auth Daemon", который в свою очередь обращается к указанному в его настройках LDAP-серверу за подтверждением существования пользователя с предъявленными логином и паролем. Положительный или отрицательный ответ доставляется обратно по цепочке web-серверу "Nginx", который допускает или нет пользователя до запрашиваемого контента. Этот процесс красиво расписан в официальной документации.


Перейдём же к практике.

Установка "Nginx LDAP Auth Daemon".

Простенькое приложение-посредник написано на "Python", который почти всегда уже установлен:

# aptitude install python2.7-minimal python-ldap git

Загружаем дистрибутив "Nginx LDAP Auth Daemon":

# cd /usr/src
# git clone https://github.com/nginxinc/nginx-ldap-auth

Копируем единственный нужный исполняемый файл в наиболее подходящее ему место в файловой системе:

# cp ./nginx-ldap-auth/nginx-ldap-auth-daemon.py /usr/local/bin/

Заготавливаем место для журналов событий (аутентификация дело ответственное):

# mkdir -p -m 740 /var/log/nginx-ldap-auth
# chown -R www-data /var/log/nginx-ldap-auth

Пробуем запустить приложение:

# cd /usr/local/bin
# LOG=/var/log/nginx-ldap-auth/daemon.log ./nginx-ldap-auth-daemon.py start

По умолчанию "Nginx LDAP Auth Daemon" прослушивает запросы только на "локальной сетевой петле", на обычно свободном порту, что меня полностью устраивает и освобождает от необходимости что-то настраивать:

# netstat -apn | grep -i python

tcp ... 127.0.0.1:8888 0.0.0.0:* LISTEN .../python

Оставим пока приложение запущенным и перейдём к настройке web-сервера.

Настройка аутентификации через LDAP в "Nginx".

Принимая за данность, что web-сервер "Nginx" уже корректно настроен, дополним настройки конкретного сайта блоком параметров проксирования запросов аутентификации к "Nginx LDAP Auth Daemon":

# vi /etc/nginx/sites-available/site.example.net.conf

....
server {
  ....

  # Перечисляем требования, которым должны удовлетворять пользователи сайта
  satisfy all;
  #
  # (пользователи из локальных сетей)
  allow 10.0.0.0/8;
  allow 172.16.0.0/12;
  allow 192.168.0.0/16;
  deny  all;
  #
  # (прошедшие аутентификацию)
  auth_request /auth-proxy;

  # Блок параметров запроса к "Nginx LDAP Auth Daemon"
  location = /auth-proxy {
    internal;
    proxy_pass_request_body off;
    proxy_set_header Content-Length "";
    proxy_pass http://127.0.0.1:8888;
    proxy_set_header X-Ldap-URL "ldaps://ldap.example.net:636";
    proxy_set_header X-Ldap-Template "(&(uid=%(username)s)(objectclass=person)(memberOf=cn=web-service-users,ou=groups,dc=example,dc=net))";
    proxy_set_header X-Ldap-BaseDN "ou=People,dc=example,dc=net";
    proxy_set_header X-Ldap-BindDN "uid=nginx-web-service,ou=Accounts,ou=Services,dc=example,dc=net";
    proxy_set_header X-Ldap-BindPass "***";
  }

  ....
}
....

Конструкция "%(username)s" в примере выше используется для получения из набора внутренних переменных "Nginx" имени пользователя (логина), введённого в форму "HTTP Basic authentication".

Проверяем синтаксическую корректность конфигурации "Nginx" и применяем её:

# nginx -t && nginx -s reload

Сразу после запуска можно пробовать обращаться к закрытой части сайта и проверять, работает ли связка "Nginx", "Nginx LDAP Auth Daemon" и LDAP в соответствии с ожиданиями. Если что-то пошло не так, смотрим предусмотрительно заготовленный журнал событий.

Подключение к LDAP с "самоподписанным" SSL-сертификатом.

Почти наверняка LDAP-сервер, работающий только в локальной сети, для шифрования соединений будет предлагать "self-signed" SSL-сертификат. Типовое python-приложение доверяет сертификатам, размещённым в соответствующем хранилище несущей операционной системы, так что самым простым способом наладить связь будет добавление публичной части самодельного сертификата LDAP-сервиса в перечень "доверенных":

# cp ./ss-rootCA.crt /usr/local/share/ca-certificates/
# update-ca-certificates

Последующая проверка (с несущего "Nginx LDAP Auth Daemon" сервера) не должна демонстрировать предупреждений о проблемах распознавания подписи SSL-сертификата целевого сервера:

$ echo QUIT | openssl s_client -connect ldap.exfmple.net:636 -CApath /etc/ssl/certs

О кешировании запросов аутентификации.

Аутентификация через "auth_request" в "Nginx" работает так, что на каждый запрос любого ресурса (даже мелкой картинки в составе страницы) генерируется внутреннее событие требования аутентификации, по которому отправляется запрос в промежуточную подсистему "ldap‑auth", которая в свою очередь обращается за подтверждением подлинности пользователя в LDAP. Таким образом на каждый запрос web-страницы создаётся от десятка до нескольких сотен подзапросов, в которых совершенно нет необходимости. Для снижения затрат ресурсов на такую непродуктивную деятельность в "Nginx" применяется кеширование.

Вначале на уровне глобальной конфигурации "Nginx" указываем желаемое месторасположение директории для файлов данных кешируемых запросов и максимальное время хранения таковых, а после в конфигурации сайта, для доступа к которому требуется аутентификация через LDAP, добавляем указание кешировать успешно завершённые запросы к подсистеме "ldap-auth" на срок до десяти минут:

# vi /etc/nginx/sites-available/site.example.net.conf

proxy_cache_path /var/lib/nginx/cache/ keys_zone=auth_cache:10m;
....
server {
  ....

  location = /auth-proxy {
    internal;
    proxy_cache auth_cache;
    proxy_cache_valid 200 10m;
    ....

# nginx -t && nginx -s reload

Настройка автозапуска "Nginx LDAP Auth Daemon" посредством "Systemd".

Ранее мы запустили приложение-прослойку вручную. Автоматизируем эту процедуру, описав подсистему аутентификации как постоянно работающий простой сервис:

# vi /etc/systemd/system/nginx-ldap-auth-daemon.service

[Unit]
Description=LDAP authentication helper for Nginx
After=network.target network-online.target

[Service]
Type=simple
User=www-data
Group=www-data
WorkingDirectory=/var/run
Environment=LOG=/var/log/nginx-ldap-auth/daemon.log
ExecStart=/usr/local/bin/nginx-ldap-auth-daemon.py
KillMode=process
KillSignal=SIGINT
Restart=on-failure

[Install]
WantedBy=multi-user.target

Указываем "Systemd" перечитать и принять новую конфигурацию, а также явно активируем и запускаем новый сервис:

# systemctl daemon-reload
# systemctl enable nginx-ldap-auth-daemon.service
# systemctl start nginx-ldap-auth-daemon

Журналы событий нам в помощь, если что-то работает не так, как задумывалось:

# systemctl status nginx-ldap-auth-daemon
# journalctl -xe

Наладка ротации журналов событий.

Выше упоминалось, что реализация аутентификации в "Nginx" склонна генерировать массу запросов, которые записываются в журнал событий. Если забыть об этом, то через полгода в файловой системе вырастет огромный ненужный файл. Заранее натравим на него подсистему усечения и оборота журналов:

# vi /etc/logrotate.d/nginx-ldap-auth-daemon

/var/log/nginx-ldap-auth/*.log {
  monthly
  rotate 12
  compress
  delaycompress
  missingok
  notifempty
  copytruncate
  su www-data www-data
}

Проверяем корректность конфигурации, не воздействуя при этом на файлы журналов:

# logrotate -d /etc/logrotate.d/nginx-ldap-auth-daemon


Заметки и комментарии к публикации:


Оставьте свой комментарий ( выразите мнение относительно публикации, поделитесь дополнительными сведениями или укажите на ошибку )