В данной статье мы рассмотрим, как настроить 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 ---> 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
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 и проверял переданную при авторизации связку логин и пароль.
Данные пользователя следующие
- Логин: [email protected]
- Пароль: r05-2020
- Группа AD: Гости домена.
Запретите пользователю менять пароль и установите неограниченный срок действия пароля (Never Expires), так как это сервисная УЗ.
Для тестирования можно скачать утилиту для поиска в дереве LDAP, например ldapsearch.
Устанавливается командой:
# sudo apt install ldap-utils
Для проверки доступности контроллера домена и правильной настройки пользователя можно выполнить команду:
# ldapsearch -v -D " [email protected] " -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 )_x000D_filter: (sAMAccountName=ldap_reader)_x000D_requesting: All userApplication attributes_x000D_# extended LDIF_x000D_#_x000D_# LDAPv3_x000D_# base <DC=corp,DC=to,DC=high> with scope subtree_x000D_# filter: (sAMAccountName=ldap_reader)_x000D_# requesting: ALL_x000D_#_x000D_# ldap_reader, Users, corp.to.high_x000D_dn: CN=ldap_reader,CN=Users,DC=corp,DC=to,DC=high_x000D_objectClass: top_x000D_objectClass: person_x000D_objectClass: organizationalPerson_x000D_objectClass: user_x000D_cn: ldap_reader_x000D_givenName: ldap_reader_x000D_distinguishedName: CN=ldap_reader,CN=Users,DC=corp,DC=to,DC=high_x000D_instanceType: 4_x000D_whenCreated: 20210308212600.0Z_x000D_whenChanged: 20210308212700.0Z_x000D_displayName: ldap_reader_x000D_………..._x000D_# search reference_x000D_ref: ldap://ForestDnsZones.corp.to.high/DC=ForestDnsZones,DC=corp,DC=to,DC=high_x000D_# search reference_x000D_ref: ldap://DomainDnsZones.corp.to.high/DC=DomainDnsZones,DC=corp,DC=to,DC=high_x000D_# search reference_x000D_ref: ldap://corp.to.high/CN=Configuration,DC=corp,DC=to,DC=high_x000D_# search result_x000D_search: 2_x000D_result: 0 Success_x000D_# numResponses: 5_x000D_# numEntries: 1_x000D_# 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: [email protected] '
--header 'X-Ldap-BindPass: r05-2020'
--header 'X-Ldap-Template: (sAMAccountName=%(username)s)' -vv -u ldap_reader:r05-2020
Вывод будет таким:
Note: Unnecessary use of -X or --request, GET is already inferred._x000D_* Rebuilt URL to: http://localhost:8888/_x000D_* Trying 127.0.0.1..._x000D_* TCP_NODELAY set_x000D_* Connected to localhost (127.0.0.1) port 8888 (#0)_x000D_* Server auth using Basic with user 'ldap_reader'_x000D_> GET / HTTP/1.1_x000D_> Host: localhost:8888_x000D_> Authorization: Basic bGRhcF9yZWFkZXI6cjA1LTIwMjA=_x000D_> User-Agent: curl/7.58.0_x000D_> Accept: */*_x000D_> X-Ldap-URL: ldap://192.168.0.16_x000D_> X-Ldap-BaseDN: CN=Users,DC=corp,DC=to,DC=high_x000D_> X-Ldap-BindDN: [email protected]_x000D_> X-Ldap-BindPass: r05-2020_x000D_> X-Ldap-Template: (sAMAccountName=%(username)s)_x000D_>_x000D_* HTTP 1.0, assume close after body_x000D_< HTTP/1.0 200 OK_x000D_< Server: BaseHTTP/0.3 Python/2.7.18_x000D_< Date: Sun, 14 Mar 2021 19:07:48 GMT_x000D_< * 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/_x000D_* Trying 127.0.0.1..._x000D_* TCP_NODELAY set_x000D_* Connected to localhost (127.0.0.1) port 8888 (#0)_x000D_* Server auth using Basic with user 'ldap_reader'_x000D_> GET / HTTP/1.1_x000D_> Host: localhost:8888_x000D_> Authorization: Basic bGRhcF9yZWFkZXI6cjA1LTIwMjAx_x000D_> User-Agent: curl/7.58.0_x000D_> Accept: */*_x000D_> X-Ldap-URL: ldap://192.168.0.16_x000D_> X-Ldap-BaseDN: CN=Users,DC=corp,DC=to,DC=high_x000D_> X-Ldap-BindDN: [email protected]_x000D_> X-Ldap-BindPass: r05-2020_x000D_> X-Ldap-Template: (sAMAccountName=%(username)s)_x000D_>_x000D_* HTTP 1.0, assume close after body_x000D_< HTTP/1.0 401 Unauthorized_x000D_< Server: BaseHTTP/0.3 Python/2.7.18_x000D_< Date: Sun, 14 Mar 2021 19:16:40 GMT_x000D_* Authentication problem. Ignoring this._x000D_< WWW-Authenticate: Basic realm="Restricted"_x000D_< Cache-Control: no-cache_x000D_<_x000D_* Closing connection 0
Также в логах демона можно увидеть вывод:
172.17.0.1 - - [14/Mar/2021 19:17:45] using username/password from authorization header_x000D_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)"_x000D_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"_x000D_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',_x000D_'msgid': 3, 'msgtype': 97, 'result': 49, 'desc': u'Invalid credentials', 'ctrls': []}, server="ldap://192.168.0.16", login="ldap_reader"_x000D_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_x000D_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;_x000D_upstream backend {_x000D_ server registry:5000;_x000D_}_x000D_server {_x000D_ listen 8081;_x000D_ # Protected application_x000D_ location / {_x000D_ auth_request /auth-proxy;_x000D_ proxy_pass http://backend/;_x000D_ }_x000D_ location = /auth-proxy {_x000D_ internal;_x000D_ proxy_cache auth_cache;_x000D_ proxy_cache_valid 200 10m;_x000D_ # The following directive adds the cookie to the cache key_x000D_ proxy_cache_key "$http_authorization$cookie_nginxauth";_x000D_ proxy_pass_request_body off;_x000D_ proxy_set_header Content-Length "";_x000D_ proxy_pass http://ldap-auth:8888;_x000D_ # URL and port for connecting to the LDAP server_x000D_ proxy_set_header X-Ldap-URL "ldap://192.168.0.16";_x000D_ # Base DN_x000D_ proxy_set_header X-Ldap-BaseDN "cn=Users,dc=corp,dc=to, dc=high";_x000D_ # Bind DN_x000D_ proxy_set_header X-Ldap-BindDN "[email protected]";_x000D_ # Bind password_x000D_ proxy_set_header X-Ldap-BindPass "r05-2020";_x000D_ proxy_set_header X-Ldap-Template "(sAMAccountName=%(username)s)";_x000D_ }_x000D_}_x000D_
В данном конфиге указывается 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_x000D_/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/_x000D_/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh_x000D_10-listen-on-ipv6-by-default.sh: info: Getting the checksum of /etc/nginx/conf.d/default.conf_x000D_10-listen-on-ipv6-by-default.sh: info: Enabled listen on IPv6 in /etc/nginx/conf.d/default.conf_x000D_/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh_x000D_/docker-entrypoint.sh: Configuration complete; ready for start up
Тестируем аутентификацию пользователя доменного пользователя AD в Nginx
Теперь откройте браузер и перейдите по адресу: http://localhost:8081/v2/_catalog. После успешной авторизации nginx должен переадрессовать на приватный репозиторий docker, описанный в статье про Docker Registry Нам будет предложено окно базовой авторизации следующего вида.
В логах веб сервера должна появится строку обращения к /v2/_catalog . Т.к. текущий пользователь не авторизирован, сервер вернул код ответа 401 код и предлагает авторизоваться. После успешной авторизации должен вернуться код ответа 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" "-"_x000D_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 репозитории.
У меня в репозитории в данный момент один образ, созданного в статье про создание простого микросервиса в docker .
В логах сервиса, который обращается к серверу ldap можно увидеть следующее:
172.17.0.4 - - [21/Mar/2021 19:18:18] using username/password from authorization header_x000D_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)"_x000D_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"_x000D_172.17.0.4 - ldap_reader [21/Mar/2021 19:18:18] Auth OK for user "ldap_reader"_x000D_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.