Настройка LDAP-аутентификации в Nginx: пошаговое руководство по защите веб-интерфейсов | AdminWiki

Настройка LDAP-аутентификации в Nginx: пошаговое руководство по защите веб-интерфейсов

29 июля 2026 10 мин. чтения

Зачем нужна LDAP-аутентификация в Nginx и как это работает

Типичная ситуация: в инфраструктуре десятки веб-интерфейсов - Grafana, Prometheus, Jenkins, внутренние дашборды. Встроенной поддержки LDAP у многих нет. Создавать локальные учетные записи на каждом сервисе неудобно и небезопасно. Решение - вынести аутентификацию на уровень Nginx. Модуль nginx-ldap-auth превращает веб-сервер в шлюз, который проверяет учетные данные через централизованный каталог LDAP.

Схема работы проста: пользователь открывает защищенный ресурс, Nginx запрашивает Basic-аутентификацию, полученные логин и пароль передаются модулю. Тот подключается к LDAP-серверу, выполняет bind от имени пользователя и возвращает результат - успех или отказ. Если проверка прошла, Nginx проксирует запрос к внутреннему сервису. Если нет - возвращает 401.

Преимущества такого подхода:

  • Единая точка управления доступом. Уволили сотрудника - заблокировали учетную запись в LDAP, и доступ ко всем веб-интерфейсам потерян автоматически.
  • Аудит. Логи Nginx фиксируют, кто и когда обращался к ресурсу.
  • Быстрое подключение новых сервисов. Достаточно добавить location с директивой auth_request - сам сервис менять не нужно.
  • Разграничение по группам. Через map-блоки можно пускать к Grafana только участников группы grafana-admins, а к Kibana - kibana-users.

Модуль особенно полезен для legacy-инструментов, которые не обновлялись годами и не получат встроенную поддержку каталогов. Вы добавляете LDAP-логин на уровне Nginx, не трогая код приложения. Материал актуален для версий Nginx 2026 года - модуль стабилен и совместим с последними релизами.

Если вы уже работали с аутентификацией в Nginx, посмотрите руководство по защите приложений с динамическим контентом - там разобраны WAF, CSP и интеграция безопасности в CI/CD.

Подготовка среды: зависимости и сборка модуля nginx-ldap-auth

Модуль nginx-ldap-auth не входит в стандартную поставку Nginx. Его нужно скомпилировать и подключить как динамический модуль. Процесс состоит из трех этапов: установка зависимостей, получение исходников, компиляция.

Перед началом убедитесь, что версия Nginx на сервере совпадает с версией исходников, которые вы будете использовать для сборки. Несовпадение приведет к ошибке при загрузке модуля. Проверить текущую версию:

nginx -v

Установка зависимостей и получение исходников

Для сборки потребуются компилятор, dev-пакеты LDAP, PCRE и OpenSSL. Команды для Debian/Ubuntu:

apt update
apt install build-essential libldap2-dev libpcre3-dev libssl-dev zlib1g-dev wget

Для CentOS/RHEL/Rocky Linux:

dnf groupinstall "Development Tools"
dnf install openldap-devel pcre-devel openssl-devel zlib-devel wget

Скачайте исходники Nginx той же версии, что установлена в системе, и модуль nginx-ldap-auth из репозитория:

cd /usr/local/src
wget https://nginx.org/download/nginx-1.26.2.tar.gz
tar xzf nginx-1.26.2.tar.gz

git clone https://github.com/kvspb/nginx-auth-ldap.git
cd nginx-auth-ldap
# Рекомендуется переключиться на стабильный тег
git checkout v3.0.0

Репозиторий модуля - kvspb/nginx-auth-ldap. На момент написания статьи стабильная версия - 3.0.0. Проверьте актуальный тег в релизах проекта перед сборкой.

Компиляция и установка динамического модуля

Конфигурация исходников Nginx с указанием пути к модулю:

cd /usr/local/src/nginx-1.26.2
./configure --with-compat --add-dynamic-module=../nginx-auth-ldap
make modules

Флаг --with-compat обеспечивает совместимость модуля с разными сборками Nginx в пределах одной основной версии. После компиляции скопируйте полученный .so-файл в директорию модулей:

cp objs/ngx_http_auth_ldap_module.so /etc/nginx/modules/

Если директории modules нет, создайте её. Для загрузки модуля добавьте в начало nginx.conf, до блока events:

load_module modules/ngx_http_auth_ldap_module.so;

events {
    worker_connections 1024;
}

Проверьте корректность конфигурации и перезагрузите Nginx:

nginx -t && systemctl reload nginx

Ошибка module ... is not binary compatible означает несовпадение версий исходников Nginx и установленного пакета. Повторите сборку со строго той же версией. Подробнее работа с динамическими модулями разобрана в статье про сборку и подключение динамических модулей Nginx.

Базовая настройка LDAP-аутентификации в Nginx

Минимальная рабочая конфигурация состоит из двух частей: описание подключения к LDAP-серверу и location с включенной аутентификацией. Начните с этой базы, убедитесь, что связка работает, и только потом переходите к разграничению по группам.

Конфигурация подключения к LDAP-серверу

Создайте файл /etc/nginx/conf.d/ldap_auth.conf. Опишите LDAP-сервер директивой ldap_server:

ldap_server ldap_main {
    url "ldap://ldap.example.com:389/ou=users,dc=example,dc=com?uid?sub?(objectClass=inetOrgPerson)";

    binddn "cn=nginx,ou=services,dc=example,dc=com";
    binddn_passwd "StrongPassword123";

    group_attribute member;
    group_attribute_is_dn on;

    require valid_user;
    satisfy all;
}

Разбор параметров:

  • url - LDAP-URL в формате ldap://хост:порт/базовый_dn?атрибут_для_поиска?область?фильтр. В примере поиск идет по uid в поддереве от ou=users, фильтр ограничивает объектами inetOrgPerson.
  • binddn - DN учетной записи, от имени которой Nginx выполняет поиск пользователя перед bind-проверкой. Это сервисная учетная запись, созданная в LDAP специально для nginx.
  • binddn_passwd - пароль сервисной учетной записи.
  • group_attribute - атрибут, в котором хранятся члены группы. Для OpenLDAP обычно member или uniqueMember.
  • group_attribute_is_dn - указывает, что значения атрибута группы - это DN пользователей, а не просто имена.
  • require - условие успешной аутентификации. valid_user означает, что пользователь должен существовать в LDAP и пройти bind.

Для Active Directory конфигурация будет отличаться:

ldap_server ldap_ad {
    url "ldap://dc.example.com:389/DC=example,DC=com?sAMAccountName?sub?(objectClass=user)";

    binddn "CN=nginx,CN=Users,DC=example,DC=com";
    binddn_passwd "StrongPassword123";

    group_attribute member;
    group_attribute_is_dn on;

    require valid_user;
}

Рекомендация: используйте ldaps и порт 636 для шифрования трафика между Nginx и LDAP-сервером. Для этого замените ldap:// на ldaps:// в URL. Без шифрования учетные данные передаются открытым текстом внутри периметра сети.

Настройка location с Basic-аутентификацией

Теперь примените аутентификацию к конкретному веб-интерфейсу. Пример проксирования внутреннего сервиса с проверкой через LDAP:

server {
    listen 443 ssl;
    server_name grafana.internal.example.com;

    ssl_certificate     /etc/nginx/ssl/grafana.crt;
    ssl_certificate_key /etc/nginx/ssl/grafana.key;

    location / {
        auth_request /ldap-auth;
        auth_request_set $saved_user $remote_user;

        proxy_pass http://127.0.0.1:3000;
        proxy_set_header X-WEBAUTH-USER $remote_user;
    }

    location = /ldap-auth {
        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      "ldap://ldap.example.com:389/ou=users,dc=example,dc=com?uid?sub?(objectClass=inetOrgPerson)";
        proxy_set_header X-Ldap-BindDN   "cn=nginx,ou=services,dc=example,dc=com";
        proxy_set_header X-Ldap-BindPass "StrongPassword123";
    }
}

Как это работает:

  • Запрос к / перехватывается директивой auth_request. Nginx выполняет внутренний подзапрос к /ldap-auth.
  • Локация /ldap-auth помечена как internal - она недоступна извне. Nginx проксирует запрос к демону аутентификации, который слушает порт 8888.
  • Демон (запускается отдельно, входит в состав модуля) получает заголовки с параметрами LDAP, выполняет проверку и возвращает 200 или 401.
  • При успехе Nginx проксирует оригинальный запрос к бэкенду, добавляя заголовок X-WEBAUTH-USER с именем пользователя.

Запустите демон аутентификации:

/usr/local/bin/nginx-ldap-auth-daemon &

Демон слушает порт 8888 по умолчанию. Для продакшена настройте systemd-юнит, чтобы процесс стартовал автоматически и перезапускался при падении.

Важный момент: вся связка должна работать поверх HTTPS. Basic-аутентификация передает логин и пароль в заголовке Authorization в base64-кодировке, что эквивалентно открытому тексту. Без TLS перехват трафика означает компрометацию учетных данных. Настройка SSL/TLS в Nginx детально разобрана в разборе конфигураций nginx.conf для продакшена.

Разграничение доступа по группам LDAP с помощью map-блоков

Базовая аутентификация пускает любого пользователя, существующего в LDAP. На практике нужен гранулярный контроль: разработчики видят Jenkins, администраторы - Grafana и Prometheus, аналитики - Kibana. Map-блоки решают эту задачу, сопоставляя LDAP-группы пользователя с правами доступа к конкретным location.

Механизм: модуль возвращает группы, в которых состоит пользователь, через переменную $ldap_groups. Map-блок анализирует эту переменную и устанавливает флаг доступа - 1 или 0. Дальше location проверяет флаг и разрешает или блокирует запрос.

Создание map-блока для сопоставления групп и ресурсов

Опишите map в секции http файла nginx.conf:

map $ldap_groups $grafana_access {
    default         0;
    "cn=grafana-admins,ou=groups,dc=example,dc=com" 1;
    "cn=devops,ou=groups,dc=example,dc=com"          1;
}

map $ldap_groups $jenkins_access {
    default         0;
    "cn=developers,ou=groups,dc=example,dc=com" 1;
    "cn=devops,ou=groups,dc=example,dc=com"     1;
}

map $ldap_groups $kibana_access {
    default         0;
    "cn=analysts,ou=groups,dc=example,dc=com" 1;
    "cn=devops,ou=groups,dc=example,dc=com"   1;
}

Синтаксис map:

  • Первый параметр - анализируемая переменная ($ldap_groups).
  • Второй - результирующая переменная ($grafana_access).
  • Внутри блока: default задает значение по умолчанию, если ни одно правило не совпало. Далее перечисляются строки-ключи и соответствующие им значения.

Если пользователь состоит в нескольких группах, map проверяет все совпадения. Приоритет имеет последнее подходящее правило. В примере devops получает доступ ко всем трем сервисам, developers - только к Jenkins, analysts - только к Kibana.

Применение ограничений к защищаемым location

Используйте результирующую переменную map в location для запрета доступа:

server {
    listen 443 ssl;
    server_name grafana.internal.example.com;

    location / {
        if ($grafana_access = 0) {
            return 403;
        }

        auth_request /ldap-auth;
        auth_request_set $saved_user $remote_user;

        proxy_pass http://127.0.0.1:3000;
        proxy_set_header X-WEBAUTH-USER $remote_user;
    }

    location = /ldap-auth {
        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      "ldap://ldap.example.com:389/ou=users,dc=example,dc=com?uid?sub?(objectClass=inetOrgPerson)";
        proxy_set_header X-Ldap-BindDN   "cn=nginx,ou=services,dc=example,dc=com";
        proxy_set_header X-Ldap-BindPass "StrongPassword123";
    }
}

Проверка if ($grafana_access = 0) срабатывает до аутентификации. Пользователь, не состоящий в нужной группе, получает 403 даже с корректным паролем. Это корректное поведение: нет смысла проверять учетные данные того, кому доступ запрещен на уровне групп.

Альтернативный подход - вынести проверку групп в отдельный скрипт и использовать auth_request с кастомным эндпоинтом. Этот метод сложнее в настройке, но дает больше гибкости: можно вести детальный аудит отказов, отправлять уведомления о попытках доступа неавторизованных пользователей.

Map-директивы Nginx - мощный инструмент, который не ограничивается аутентификацией. Маршрутизация по заголовкам, выбор бэкенда по домену, условное проксирование - все это строится на map. Примеры расширенного использования смотрите в руководстве по настройке Nginx как L7-маршрутизатора.

Практический пример: защита панели администрирования Grafana

Соберем полную конфигурацию для Grafana - популярного инструмента визуализации метрик. Задача: доступ к Grafana имеют только члены группы grafana-admins, встроенная аутентификация Grafana отключена, пользователь автоматически входит под своей LDAP-учетной записью.

Файл /etc/nginx/sites-available/grafana:

map $ldap_groups $grafana_access {
    default         0;
    "cn=grafana-admins,ou=groups,dc=example,dc=com" 1;
}

server {
    listen 443 ssl http2;
    server_name grafana.example.com;

    ssl_certificate     /etc/nginx/ssl/grafana.example.com.crt;
    ssl_certificate_key /etc/nginx/ssl/grafana.example.com.key;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         HIGH:!aNULL:!MD5;

    location / {
        if ($grafana_access = 0) {
            return 403;
        }

        auth_request /ldap-auth;
        auth_request_set $saved_user $remote_user;

        proxy_pass http://127.0.0.1:3000;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-WEBAUTH-USER $remote_user;
    }

    location = /ldap-auth {
        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      "ldap://ldap.example.com:389/ou=users,dc=example,dc=com?uid?sub?(objectClass=inetOrgPerson)";
        proxy_set_header X-Ldap-BindDN   "cn=nginx,ou=services,dc=example,dc=com";
        proxy_set_header X-Ldap-BindPass "StrongPassword123";
    }
}

Настройки Grafana (/etc/grafana/grafana.ini), которые отключают встроенную аутентификацию и включают прокси-аутентификацию:

[auth.proxy]
enabled = true
header_name = X-WEBAUTH-USER
header_property = username
auto_sign_up = true
sync_ttl = 60
whitelist = 127.0.0.1

[auth.basic]
enabled = false

[auth.ldap]
enabled = false

Grafana доверяет заголовку X-WEBAUTH-USER, который Nginx пробрасывает после успешной LDAP-проверки. Пользователь не видит форму входа Grafana - он уже аутентифицирован на уровне Nginx. Автоматическая регистрация (auto_sign_up = true) создает учетную запись в Grafana при первом входе.

Для Prometheus, Alertmanager и других инструментов без встроенной аутентификации схема аналогична - достаточно добавить location с auth_request и пробросить заголовок с именем пользователя, если сервис его поддерживает.

Диагностика и решение типичных проблем

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

Анализ логов Nginx и модуля

Включите отладочный лог для модуля. Добавьте в nginx.conf:

error_log /var/log/nginx/error.log debug;

Типичные сообщения и их расшифровка:

  • ldap bind failed - неверные binddn или binddn_passwd. Проверьте учетные данные сервисной записи, срок действия пароля, блокировку учетной записи в LDAP.
  • user not found - фильтр в LDAP-URL не находит пользователя. Проверьте атрибут поиска (uid, sAMAccountName) и базовый DN.
  • connection timeout - Nginx не может достичь LDAP-сервера. Проверьте сетевую связность, порт, файрволл. Попробуйте telnet ldap.example.com 389 с сервера Nginx.
  • module not compatible - версия модуля не соответствует версии Nginx. Пересоберите модуль со строго той же версией исходников.

Проверка LDAP-запросов вручную

Изолируйте проблему: отделите ошибки конфигурации Nginx от проблем с LDAP-сервером. Утилита ldapsearch выполняет те же запросы, что и модуль:

# Поиск пользователя (имитация поиска перед bind)
ldapsearch -x -H ldap://ldap.example.com:389 \
  -D "cn=nginx,ou=services,dc=example,dc=com" \
  -w "StrongPassword123" \
  -b "ou=users,dc=example,dc=com" \
  "(&(uid=testuser)(objectClass=inetOrgPerson))"

# Проверка членства в группах
ldapsearch -x -H ldap://ldap.example.com:389 \
  -D "cn=nginx,ou=services,dc=example,dc=com" \
  -w "StrongPassword123" \
  -b "ou=groups,dc=example,dc=com" \
  "(&(objectClass=groupOfNames)(member=uid=testuser,ou=users,dc=example,dc=com))"

Если ldapsearch возвращает результаты, проблема на стороне Nginx - проверяйте конфигурацию. Если нет - проблема в LDAP: неверный DN, фильтр, права сервисной записи.

Частая ошибка: в Active Directory фильтр (objectClass=user) не находит пользователей. Замените на (objectCategory=person) или уберите фильтр вообще, оставив только атрибут поиска sAMAccountName.

Демон nginx-ldap-auth-daemon пишет логи в stdout/stderr. При запуске вручную вы увидите ошибки сразу в терминале. При работе через systemd логи доступны через journalctl -u nginx-ldap-auth.

Актуальность решения и альтернативы в 2026 году

Модуль nginx-ldap-auth стабилен и поддерживает все версии Nginx вплоть до 1.26.x. Сообщество активно: последние коммиты в репозиторий датируются 2025 годом, issues закрываются. Для связки Nginx + LDAP это зрелое решение без признаков заброшенности.

Альтернативные подходы:

  • OAuth2 Proxy - внешний прокси, который добавляет OAuth2/OIDC-аутентификацию перед веб-интерфейсами. Подходит, если в организации развернут Keycloak, Authentik или другой IdP. Требует настройки редиректов, управления сессиями, работы с куками. Сложнее в первоначальной настройке, но дает SSO и не привязан к LDAP напрямую.
  • Аутентификация на уровне приложений - Grafana, Jenkins, Kibana имеют встроенную поддержку LDAP. Этот подход правильнее архитектурно: каждый сервис сам проверяет права. Но для legacy-инструментов и кастомных дашбордов он недоступен.
  • Authelia - самодостаточный портал аутентификации с поддержкой 2FA, LDAP, файлового бэкенда. Интегрируется с Nginx через auth_request аналогично модулю nginx-ldap-auth. Подробнее этот метод разобран в статье про защиту Nginx в 2026 году.

nginx-ldap-auth остается оптимальным выбором, когда нужно быстро добавить LDAP-логин к веб-интерфейсу без развертывания дополнительной инфраструктуры. Модуль минималистичен: демон аутентификации на Go, никаких баз данных, никаких веб-интерфейсов. Настройка сводится к конфигурационному файлу Nginx и запуску одного бинарника.

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

Поделиться:
Сохранить гайд? В закладки браузера