Настройка аутентификации в Nginx через Active Directory (LDAP)

В данной статье мы рассмотрим, как настроить basic аутентификацию в Nginx через LDAP каталог. В качестве сервера LDAP будем использовать Windows Active Directory. Так как у Nginx нет официального встроенного модуля авторизации через LDAP (в отличии от Apache или Tomcat), разработчики предлагают использовать сторонний сервис nginx-ldap-auth, который будет обращаться к LDAP и выступать своего рода прокси между Nginx и сервером каталога LDAP ldap сервером. Для начала давайте запустим сервис и посмотрим, как он работает.

Настройка и запуск сервиса аутентификации nginx-ldap-auth

Для запуска сервиса nginx-ldap-auth будем использовать docker. Для начала нужно клонировать репозиторий с сервисом с github:

# sudo git clone https://github.com/nginxinc/nginx-ldap-auth

# cd nginx-ldap-auth

Если у вас не установлен утилиты git, можно просто скачать архив с исходниками и распаковать его в директорию nginx-ldap-auth.

В директории уже имеется готовый Dockerfile. Для сборки контейнера нужно выполнить:

# sudo docker build -t nginx-ldap-auth-daemon .

Вывод при сборке будет таким:

Sending build context to Docker daemon 208.9kB
Step 1/7 : ARG PYTHON_VERSION=2
Step 2/7 : FROM python:${PYTHON_VERSION}-alpine
---> 8579e446340f
Step 3/7 : COPY nginx-ldap-auth-daemon.py /usr/src/app/
---> Using cache
%MINIFYHTML724dcb9f6f04351bc2885ece6f04a8066%---> a0b2c58fd4af Step 4/7 : WORKDIR /usr/src/app/ ---> Using cache ---> 2d93d045af89 Step 5/7 : RUN apk --no-cache add openldap-dev && apk --no-cache add --virtual build-dependencies build-base && pip install python-ldap && apk del build -dependencies ---> Using cache ---> fd942ca28c6e Step 6/7 : EXPOSE 8888 ---> Using cache ---> 0d5b7bab2edf Step 7/7 : CMD ["python", "/usr/src/app/nginx-ldap-auth-daemon.py", "--host", "0.0.0.0", "--port", "8888"] ---> Using cache ---> 3eb60dda0847 Successfully built 3eb60dda0847 Successfully tagged nginx-ldap-auth-daemon:latest

установка контейнера nginx-ldap-auth

Docker image готов, можно запустить контейнер. Для этого выполните команду:

# sudo docker run -p 8888:8888 --name ldap-auth nginx-ldap-auth-daemon

Вывод данной команды будет таким:

Start listening on 0.0.0.0:8888…

Эта команда запустит контейнер из образа nginx-ldap-auth-daemon, c названием контейнера ldap-auth и пробросит порт 8888 из контейнера на host машину. Проброс портов нужен, чтобы проверить работу и выполнить отладку аутентификации в Active Directory. В дальнейшем, можно исключить этот параметр. Также для отладки мы не добавили ключ -d, который запустил бы контейнер и отвязал бы его от консоли, но тогда не было видно лога работы, который полезен при отладке.

Давайте теперь разберем, как работает сервис nginx-ldap-auth. Данный демон является небольшим web сервисом, к которому можно отправить HTTP запрос методом get с определенными заголовками, он в свою очередь обратится в Active Directory и проверит авторизационные данные. Если они верны, он ответит HTTP кодом 200 ОК, если нет, то вернет 401 код (не авторизован).

Для проверки нужен настроенный сервер Active Directory либо OpenLDAP. В данной статье будет рассмотрен контроллер домена на Windows Server 2008 R2, но в README репозитория демона заявлена поддержка и других LDAP серверов.

  • Имя домена: corp.to.high
  • IP контроллера домена: 192.168.0.16

Создадим пользователя с правами гость. Назовем его ldap_reader. Данный пользователь нужен, чтобы от его имени демон обращался в AD и проверял переданную при авторизации связку логин и пароль.

Данные пользователя следующие

  • Логин: ldap_reader@corp.to.high
  • Пароль: r05-2020
  • Группа AD: Гости домена.

пользователь для доступа к домену

гость домена

Запретите пользователю менять пароль и установите неограниченный срок действия пароля (Never Expires), так как это сервисная УЗ.

Для тестирования можно скачать утилиту для поиска в дереве LDAP, например ldapsearch.

Устанавливается командой:

# sudo apt install ldap-utils

Для проверки доступности контроллера домена и правильной настройки пользователя можно выполнить команду:

# ldapsearch -v -D "ldap_reader@corp.to.high" -w "r05-2020" -b "DC=corp,DC=to,DC=high" -H "ldap://192.168.0.16" "(sAMAccountName=ldap_reader)"

  • -D binddn учетная запись, для доступа к домену,
  • -w bindpassword пароль учетной записи,
  • -b searchbase – контейнер (OU) AD, в котором нужно искать пользователя (можно сузить область поиска для получения ответа более быстро),
  • -H адрес ldap сервера;
  • Затем указывается LDAP фильтр, по которому нужно выполнить поиск.

Вывод для нашего домена будет такой:

ldap_initialize( ldap://192.168.0.16:389/??base )
filter: (sAMAccountName=ldap_reader)
requesting: All userApplication attributes
# extended LDIF
#
# LDAPv3
# base <DC=corp,DC=to,DC=high> with scope subtree
# filter: (sAMAccountName=ldap_reader)
# requesting: ALL
#
# ldap_reader, Users, corp.to.high
dn: CN=ldap_reader,CN=Users,DC=corp,DC=to,DC=high
objectClass: top
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: ldap_reader
givenName: ldap_reader
distinguishedName: CN=ldap_reader,CN=Users,DC=corp,DC=to,DC=high
instanceType: 4
whenCreated: 20210308212600.0Z
whenChanged: 20210308212700.0Z
displayName: ldap_reader
………...
# search reference
ref: ldap://ForestDnsZones.corp.to.high/DC=ForestDnsZones,DC=corp,DC=to,DC=high
# search reference
ref: ldap://DomainDnsZones.corp.to.high/DC=DomainDnsZones,DC=corp,DC=to,DC=high
# search reference
ref: ldap://corp.to.high/CN=Configuration,DC=corp,DC=to,DC=high
# search result
search: 2
result: 0 Success
# numResponses: 5
# numEntries: 1
# numReferences: 3

Данный лог показывает, что пользователь найден и у пользователя ldap_reader есть доступ на чтение дерева домена. Проверим теперь работу демона авторизации. Для этого, выполним в консоли команду:

# curl --location --request GET 'http://localhost:8888' \
--header 'X-Ldap-URL: ldap://192.168.0.16' \
--header 'X-Ldap-BaseDN: CN=Users,DC=corp,DC=to,DC=high' \
--header 'X-Ldap-BindDN: ldap_reader@corp.to.high' \
--header 'X-Ldap-BindPass: r05-2020' \
--header 'X-Ldap-Template: (sAMAccountName=%(username)s)' -vv -u ldap_reader:r05-2020

Вывод будет таким:

curl с header X-Ldap-BindDN, проверка аутентфикации в Active Directory

Note: Unnecessary use of -X or --request, GET is already inferred.
* Rebuilt URL to: http://localhost:8888/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8888 (#0)
* Server auth using Basic with user 'ldap_reader'
> GET / HTTP/1.1
> Host: localhost:8888
> Authorization: Basic bGRhcF9yZWFkZXI6cjA1LTIwMjA=
> User-Agent: curl/7.58.0
> Accept: */*
> X-Ldap-URL: ldap://192.168.0.16
> X-Ldap-BaseDN: CN=Users,DC=corp,DC=to,DC=high
> X-Ldap-BindDN: ldap_reader@corp.to.high
> X-Ldap-BindPass: r05-2020
> X-Ldap-Template: (sAMAccountName=%(username)s)
>
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
< Server: BaseHTTP/0.3 Python/2.7.18
< Date: Sun, 14 Mar 2021 19:07:48 GMT
< * Closing connection 0

Заголовки, используемые в запросе:

  • X-Ldap-URL — адрес ldap сервера, такой же, как при тестировании с помощью утилиты ldapsearch;
  • X-Ldap-BaseDN — начальный DN для поиска;
  • X-Ldap-BindDN — учетная запись с правами на чтение дерева домена.
  • X-Ldap-BindPass – пароль пользователя для доступа к AD;
  • X-Ldap-Template — в данном заголовке передается LDAP фильтр, по которому будет происходить поиск. В заголовке можно комбинировать разные фильтры. Подробнее можно посмотреть в документации к демону nginx-ldap-auth.
  • Опцией -u <login>:<password> нужно передать креденшелы пользователя, которые нужно проверить в домене

Если передать неверные учетные данные, будет возвращена ошибка 401 ( не авторизован). Попробуем передать неверный пароль, вывод будет такой:

* Rebuilt URL to: http://localhost:8888/
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8888 (#0)
* Server auth using Basic with user 'ldap_reader'
> GET / HTTP/1.1
> Host: localhost:8888
> Authorization: Basic bGRhcF9yZWFkZXI6cjA1LTIwMjAx
> User-Agent: curl/7.58.0
> Accept: */*
> X-Ldap-URL: ldap://192.168.0.16
> X-Ldap-BaseDN: CN=Users,DC=corp,DC=to,DC=high
> X-Ldap-BindDN: ldap_reader@corp.to.high
> X-Ldap-BindPass: r05-2020
> X-Ldap-Template: (sAMAccountName=%(username)s)
>
* HTTP 1.0, assume close after body
< HTTP/1.0 401 Unauthorized
< Server: BaseHTTP/0.3 Python/2.7.18
< Date: Sun, 14 Mar 2021 19:16:40 GMT
* Authentication problem. Ignoring this.
< WWW-Authenticate: Basic realm="Restricted"
< Cache-Control: no-cache
<
* Closing connection 0

Также в логах демона можно увидеть вывод:

авторизация в ad http

172.17.0.1 - - [14/Mar/2021 19:17:45] using username/password from authorization header
172.17.0.1 - ldap_reader [14/Mar/2021 19:17:45] searching on server "ldap://192.168.0.16" with base dn "CN=Users,DC=corp,DC=to,DC=high" with filter "(sAMAccountName=ldap_reader)"
172.17.0.1 - ldap_reader [14/Mar/2021 19:17:45] attempting to bind using dn "CN=ldap_reader,CN=Users,DC=corp,DC=to,DC=high"
172.17.0.1 - ldap_reader [14/Mar/2021 19:17:45] Error while binding as an existing user "CN=ldap_reader,CN=Users,DC=corp,DC=to,DC=high": {'info': u'80090308: LdapErr: DSID-0C0903AA, comment: AcceptSecurityContext error, data 52e, v1772',
'msgid': 3, 'msgtype': 97, 'result': 49, 'desc': u'Invalid credentials', 'ctrls': []}, server="ldap://192.168.0.16", login="ldap_reader"
172.17.0.1 - ldap_reader [14/Mar/2021 19:17:46] "GET / HTTP/1.1" 401 -

Настройка контейнера Nginx для авторизации в Active Directory (LDAP)

Мы проверили работу демона аутентфикации nginx-ldap-auth, теперь можно перейти к настройке Nginx. В данной статье, покажу, как настроить авторизацию для docker registry с учетной записью Active Directory. Nginx будем запускать в docker контейнере. Создадйте Dockerfile:

FROM nginx
COPY nginx-ldap-auth.conf /etc/nginx/conf.d/nginx-ldap-auth.conf

В директории с Dockerfile создадйте файл конфигурации nginx-ldap-auth.conf.
Содержание файла:

proxy_cache_path cache/ keys_zone=auth_cache:10m;
upstream backend {
     server registry:5000;
}
server {
        listen 8081;
        # Protected application
        location / {
            auth_request /auth-proxy;
            proxy_pass http://backend/;
        }
        location = /auth-proxy {
            internal;
            proxy_cache auth_cache;
            proxy_cache_valid 200 10m;
            # The following directive adds the cookie to the cache key
            proxy_cache_key "$http_authorization$cookie_nginxauth";
	proxy_pass_request_body off;
            proxy_set_header Content-Length "";
            proxy_pass http://ldap-auth:8888;
            # URL and port for connecting to the LDAP server
            proxy_set_header X-Ldap-URL "ldap://192.168.0.16";
            # Base DN
            proxy_set_header X-Ldap-BaseDN "cn=Users,dc=corp,dc=to, dc=high";
            # Bind DN
            proxy_set_header X-Ldap-BindDN "ldap_reader@corp.to.high";
            # Bind password
            proxy_set_header X-Ldap-BindPass "r05-2020";
            proxy_set_header X-Ldap-Template "(sAMAccountName=%(username)s)";
        }
}

В данном конфиге указывается location, с опцией auth_request, которая позволяет отправить HTTP запрос для проверки авторизации. Мы указали внутренний адрес /auth-proxy, на который будет переадресован запрос авторизации. В данном location указаны опций, которые настраивают параметры кэширования и заголовки, которые будут отправлены в nginx-ldap-auth.

  • proxy_pass_request_body — запрещает передачу полей заголовка исходного запроса на проксируемый сервер;
  • proxy_set_header — устанавливает заголовок Content-Length;
  • proxy_cache_valid 200 10m — кешировать ответ с кодом 200 на 10 минут (чтобы не отправлять запрос к серверу авторизации при каждом обращении);
  • proxy_cache_path — путь и другие параметры кэша;
  • proxy_cache_key — ключ кэширования;
  • proxy_pass – указывает на контейнера демона авторизации nginx-ldap-auth. Остальные опции аналогичны тем, что использовались при проверке демона. Файл конфига и dockerfile можно взять в github репозитории.

После создания конфига, нужно собрать контейнер:

# sudo docker build . -t nginx-ldap

Запустите контейнер с nginx с выводом в консоль:

# sudo docker run -p:8081:8081 --link ldap-auth --link registry --name nginx-ldap nginx-ldap

  • Запускаем nginx-ldap образ с именем —name nginx-ldap;
  • —link — опция позволяет связать в одну сеть контейнеры, так как мы к ним обращаемся по именам в конфиге nginx. Без этих опций из контейнера nginx не будет резолвиться имя registry и ldap-auth;
  • -p:8081:8081 — прокидываем порт наружу.

Вывод команды будет такой:

/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf
10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Configuration complete; ready for start up

запуск nginx в контейнере с авторизацией в active directory

Тестируем аутентификацию пользователя доменного пользователя AD в Nginx

Теперь откройте браузер и перейдите по адресу: http://localhost:8081/v2/_catalog. После успешной авторизации nginx должен переадрессовать на приватный репозиторий docker, описанный в статье про Docker Registry Нам будет предложено окно базовой авторизации следующего вида.

запрос пароля

В логах веб сервера должна появится строку обращения к /v2/_catalog. Т.к. текущий пользователь не авторизирован, сервер вернул код ответа 401 код и предлагает авторизоваться. После успешной авторизации должен вернуться код ответа 200.

успешная аутентфикация HTTP/1.1 200

172.17.0.1 - - [21/Mar/2021:19:19:09 +0000] "GET /v2/_catalog HTTP/1.1" 401 179 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0" "-"
172.17.0.1 - ldap_reader [21/Mar/2021:19:19:20 +0000] "GET /v2/_catalog HTTP/1.1" 200 20 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0" "-"

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

После успешной авторизации должна открыться страница со списком образов в Docker репозитории.

после аутентфикаии получен доступ к сервису за nginx

У меня в репозитории в данный момент один образ, созданного в статье про создание простого микросервиса в docker.

В логах сервиса, который обращается к серверу ldap можно увидеть следующее:

172.17.0.4 - - [21/Mar/2021 19:18:18] using username/password from authorization header
172.17.0.4 - ldap_reader [21/Mar/2021 19:18:18] searching on server "ldap://192.168.0.16" with base dn "cn=Users,dc=corp,dc=to, dc=high" with filter "(sAMAccountName=ldap_reader)"
172.17.0.4 - ldap_reader [21/Mar/2021 19:18:18] attempting to bind using dn "CN=ldap_reader,CN=Users,DC=corp,DC=to,DC=high"
172.17.0.4 - ldap_reader [21/Mar/2021 19:18:18] Auth OK for user "ldap_reader"
172.17.0.4 - ldap_reader [21/Mar/2021 19:18:18] "GET /auth-proxy HTTP/1.0" 200

По логу видно, что выполнено подключение к LDAP серверу, выполнена авторизация и проверены учетные данные пользователя.

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

Удаляем контейнеры:

# sudo docker rm ldap-auth
# sudo docker rm nginx-ldap

Теперь можно запуститьконтейнеру nginx и nginx-ldap-auth в фоновом режиме. Также мы убрали в контейнере авторизации проброс портов наружу, это больше не нужно.

# sudo docker run -d --name ldap-auth nginx-ldap-auth-daemon
# sudo docker run -p:8081:8081 -d --link ldap-auth --link registry --name nginx-ldap nginx-ldap

В данной статье мы рассмотрели общий принцип настройки авторизации с учетными данными LDAP в nginx. В качестве конечного backend может выступать не обязательно docker registry, таким образом можно настроить аутентификацию в Active Directory в в любом вашем приложении, опубликованном через nginx.


Предыдущая статья Следующая статья


Комментариев: 9 Оставить комментарий

Оставить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Я не робот( Обязательно отметьте)