В данной статье разбираемся как организовать проверку jwt-токенов на стороне Nginx, как собрать Nginx из исходного кода с целью подключения дополнительных сторонних модулей ngx-http-auth-jwt-module и echo-nginx-module. Так же включаем логирование на уровне debug.
Интродукция
Установка Nginx из репозитория вашей операционной системы дело совсем не трудное. Одна строчка в терминале и готово.
Например для Debian, Ubuntu, Mint:
# apt install nginx
или openSUSE:
# zypper install nginx
и т.д. подобным образом и в остальных дистрибутивах.
Но при таком способе установки, что называется "из коробки", мы получаем преднастроенный Nginx с фиксированным набором подключенных модулей. Но когда возникает необходимость подключить дополнительные модули, или включить отключенные по умолчанию параметры, то приходится собирать Nginx из исходного кода с указанием этих самых дополнительных модулей и параметров. А это уже задачка чуть посложнее.
Предвижу что статья получится большая, и легко будет потерять ход повествования. По этому для понимания необходимо объяснить некоторые моменты. Основная задача - организовать работу Nginx с JWT-токенами с помощью JWT-модуля. Дополнительные задачи - подключить Echo-модуль и включить логирование в режиме debug.
Оригинальный Nginx-JWT-модуль от разработчиков Nginx является платным и входит в платную версию Nginx Plus. По этому будем ставить вот этот https://github.com/TeslaGov/ngx-http-auth-jwt-module сторонний бесплатный JWT-модуль. Есть и другие, но я решил разобраться с этим.
Nginx-JWT-модуль зависит от C-библиотеки JWT, которая в свою очередь зависит от C-библиотеки Jansson. Nginx-Echo-Module не требует зависимостей. Режим debug просто включается как параметр. То есть последовательность установки следующая:
- Jansson - установка из репозитория или скачивание и компиляция
- C-JWT - скачивание и компиляция
- Nginx-JWT-Module - только скачивание исходного кода
- Nginx-Echo-Module - только скачивание исходного кода
- Сборка Nginx - компиляция вместе со скаченными модулями и включенным debug
0) Некоторые подготовительные моменты
Если доступ в интернет осуществляется через прокси-сервер, то чтобы в дальнейшем использовать wget или клонировать git-репозитории, необходимо указать данные для авторизации прокси в переменные окружения http_proxy и https_proxy:
# export http_proxy=http://<ваш_логин>:<ваш_пароль>@<ip_прокси_сервера>:<порт>
# export https_proxy=https://<ваш_логин>:<ваш_пароль>@<ip_прокси_сервера>:<порт>
1) Устанавливаем C-библиотеку jansson (два способа)
Простой вариант
Если библиотека jansson доступна из репозитория вашей ОС, то установка будет очень простая. Например в случае openSUSE:
# zypper in libjansson-devel
Для проверки успешной установки библиотеки jansson вы можете использовать команду pkg-config в терминале. Если библиотека jansson установлена успешно, вы увидите версию установленной библиотеки в выводе этой команды, в моем случае версия 2.14.
# pkg-config --modversion jansson
Вариант посложнее
Если библиотека jansson недоступна из репозитория вашей ОС, то можно скачать её исходный код, скомпилировать и установить. Для этого:
Переходим в директорию /usr/local/src. Сюда обычно принято складывать исходные коды программ для последующей сборки. С помощью wget скачаем исходный код библиотеки jansson (искать ссылки здесь https://github.com/akheron/jansson или http://www.digip.org/jansson/#releases).
# cd /usr/local/src
# wget https://github.com/akheron/jansson/releases/download/v2.14/jansson-2.14.tar.gz
Перед установкой библиотеки jansson необходимо установить пакеты: gcc, make. Без этих программ ./confugure выдаёт ошибки. Но для общего развития можете сразу запустить ./confugure, увидеть как он "ругается" и затем доустанавливать недостающие пакеты или библиотеки, о которых сообщает ./configure после неудачных попыток конфигурации. Так вы научитесь понимать чего не хватает компилятору и это будет полезно в будущем при сборке других программ.
# zypper in gcc
# zypper in make
Распакуем, сконфигурируем, соберем и установим jansson:
# tar -xvf jansson-2.14.tar.gz
# cd jansson-2.14/
# ./configure
# make
# make install
# cd ..
И снова для проверки успешной установки библиотеки jansson вы можете использовать команду pkg-config в командной строке. Если библиотека jansson установлена успешно, вы увидите версию установленной библиотеки в выводе этой команды.
# pkg-config --modversion jansson
2) Устанавливаем C-библиотеку JWT
Для сборки библиотеки JWT нам понадобятся: git, autoconf, automake, cmake, libtool, libopenssl-devel.
# zypper in git
# zypper in autoconf
# zypper in automake
# zypper in cmake
# zypper in libtool
# zypper in libopenssl-devel
Переходим в директорию /usr/local/src, клонируем git-репозиторий, конфигурируем, собираем, устанавливаем:
# cd /usr/local/src
# git clone https://github.com/benmcollins/libjwt
# cd libjwt
# autoreconf -i
# ./configure
# make
# make install
# cd ..
Для проверки успешной установки библиотеки libjwt вы можете использовать команду pkg-config в командной строке. Если библиотека libjwt установлена успешно, вы увидите версию установленной библиотеки в выводе этой команды. В моем случае версия 1.17.0.
# pkg-config --modversion libjwt
3) Собираем Nginx
Логика установки Nginx следующая: сначала устанавливаем Nginx из официального репозитория openSUSE, а затем скачаем исходный код Nginx той же версии с сайта https://nginx.org. Соберем Nginx из исходников, добавив новые параметры к уже имеющимся параметрам по умолчанию, и добавив необходимые дополнительные модули для сборки. Модули должны быть предварительно скачаны. Соберем и заменим установленный из репозитория Nginx на наш собранный из исходников. Поехали!
Установим коробочный Nginx
Установим Nginx из репозитория openSUSE, проверим статус, включим автозапуск, и еще раз проверим статус:
# zypper in nginx
# systemctl status nginx
# systemctl enable nginx
# systemctl start nginx
# systemctl status nginx
Для компиляции Nginx потребуется доустановить следующие пакеты:
# zypper in pcre-devel
# zypper in libxml2-devel
# zypper in libxslt-devel
# zypper in gd-devel
Далее сохраним список параметров "коробочной" сборки Nginx, установленного из репозитория, в файл nginx-arguments.txt. Другими словами перенаправим вывод команды nginx -V (где -V обязательно в верхем регистре) в наш файл nginx- arguments.txt.
# nginx -V &> /usr/local/src/nginx-arguments.txt
Получаем следующее содержимое нашего файла nginx-arguments.txt где аргументы сборки перечислены непосредственно после фразы "configure arguments:" и до конца файла:
nginx version: nginx/1.21.5
built by gcc 7.5.0 (SUSE Linux)
built with OpenSSL 1.1.1l 24 Aug 2021 SUSE release SUSE_OPENSSL_RELEASE (running with OpenSSL 1.1.1l-fips 24 Aug 2021 SUSE release 150500.17.25.1)
TLS SNI support enabled
configure arguments: --prefix=/usr/ --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/run/nginx.pid --lock-path=/run/nginx.lock --http-client-body-temp-path=/var/lib/nginx/tmp/ --http-proxy-temp-path=/var/lib/nginx/proxy/ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi/ --http-uwsgi-temp-path=/var/lib/nginx/uwsgi/ --http-scgi-temp-path=/var/lib/nginx/scgi/ --user=nginx --group=nginx --without-select_module --without-poll_module --with-threads --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-perl=/usr/bin/perl --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module --with-pcre --with-pcre-jit --with-cc-opt='-fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -fPIC -D_GNU_SOURCE' --with-ld-opt='-Wl,-z,relro,-z,now -pie' --with-compat
Эти аргументы понадобятся нам далее для сборки из исходника.
Переходим в директорию /usr/local/src, качаем Nginx, распаковываем:
# cd /usr/local/src
# wget https://nginx.org/download/nginx-1.21.5.tar.gz
# tar -xvf nginx-1.21.5.tar.gz
# cd nginx-1.21.5
# cd ..
Пока не запускаем сборку Nginx т.к. нам необходимо скачать дополнительные модули.
Качаем Nginx Echo-модуль
Клонируем git-репозиторий echo-nginx-module:
$ git clone https://github.com/openresty/echo-nginx-module
Или второй вариант скачать архив с echo-nginx-module и распаковываем его:
$ cd /usr/local/src
$ wget https://github.com/openresty/echo-nginx-module/archive/refs/tags/v0.63.tar.gz
$ tar -xzvf v0.63.tar.gz
В обоих случаях результат один и тот же - это папка с исходным кодом. Но имя папки после распаковки архива может отличаться по этому её можно переименовать как вам удобно. В моем примере папка /usr/local/src/echo-nginx-module.
Качаем Nginx JWT-модуль
Клонируем git-репозиторий ngx-http-auth-jwt-module:
# cd /usr/local/src
# git clone https://github.com/TeslaGov/ngx-http-auth-jwt-module
На просторах рекомендуют создать директорию /lib/x86_64-linux-gnu и скопировать в нее скомпилированную библиотеку libjwt для того чтобы Nginx увидел её при компиляции. Но в моем случае делать этого нет необходимости. Nginx увидел библиотеку сам. Но на всякий случай оставлю описание этого действия в статье, вдруг когда-нибудь понадобится:
# sudo mkdir /lib/x86_64-linux-gnu
# sudo cp /usr/local/lib/libjwt.* /lib/x86_64-linux-gnu/
Разбираемся с параметрами сборки Nginx
Теперь, когда дополнительные модули скачаны, добавим ссылки на них в наш список параметров компиляции Nginx, который мы сохранили ранее в файл nginx-arguments.txt. Добавим к параметрам сборки включение режима debug и наши скаченные модули jwt-модуль и echo-модуль. Допишем в конец файла nginx-arguments.txt новые параметры сборки:
# echo "--with-debug --add-module=../ngx-http-auth-jwt-module --add-module=/usr/local/src/echo-nginx-module" >> nginx-arguments.txt
Как видно в примере ссылки можно указывать как относительные (../ngx-http-auth-jwt-module) так и абсолютные (/usr/local/src/echo-nginx-module).
Поскольку вывод информации об Nginx (команда nginx -V) выдает не только параметры сборки но и другую информацию об Nginx, то нам необходимо вырезать из этого текста все лишнее оставив только параметры сборки. С помощью mcedit удалим начальную часть файла nginx-arguments.txt вместе с фразой "configure arguments: ". Оставим весь текст что находится после фразы "configure arguments: " и до конца файла. Результат следующий:
--prefix=/usr/ --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/run/nginx.pid --lock-path=/run/nginx.lock --http-client-body-temp-path=/var/lib/nginx/tmp/ --http-proxy-temp-path=/var/lib/nginx/proxy/ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi/ --http-uwsgi-temp-path=/var/lib/nginx/uwsgi/ --http-scgi-temp-path=/var/lib/nginx/scgi/ --user=nginx --group=nginx --without-select_module --without-poll_module --with-threads --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-perl=/usr/bin/perl --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module --with-pcre --with-pcre-jit --with-cc-opt='-fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -fPIC -D_GNU_SOURCE' --with-ld-opt='-Wl,-z,relro,-z,now -pie' --with-compat --with-debug --add-module=../ngx-http-auth-jwt-module --add-module=/usr/local/src/echo-nginx-module
Компилируем и устанавливаем Nginx
Конфигурируем сборку Nginx с добавлением новых параметров, компилируем, устанавливаем:
# cd /usr/local/src/nginx-1.21.5
# ./configure --prefix=/usr/ --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/run/nginx.pid --lock-path=/run/nginx.lock --http-client-body-temp-path=/var/lib/nginx/tmp/ --http-proxy-temp-path=/var/lib/nginx/proxy/ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi/ --http-uwsgi-temp-path=/var/lib/nginx/uwsgi/ --http-scgi-temp-path=/var/lib/nginx/scgi/ --user=nginx --group=nginx --without-select_module --without-poll_module --with-threads --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-perl=/usr/bin/perl --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module --with-pcre --with-pcre-jit --with-cc-opt='-fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -fPIC -D_GNU_SOURCE' --with-ld-opt='-Wl,-z,relro,-z,now -pie' --with-compat --with-debug --add-module=../ngx-http-auth-jwt-module --add-module=/usr/local/src/echo-nginx-module
# make
# make install
Результат успешной компиляции make выглядит так (нет сообщений об ошибках):
... Configuration summary
+ using threads
+ using system PCRE library
+ using system OpenSSL library
+ using system zlib library
nginx path prefix: "/usr/"
nginx binary file: "/usr/sbin/nginx"
nginx modules path: "/usr/lib64/nginx/modules"
nginx configuration prefix: "/etc/nginx"
nginx configuration file: "/etc/nginx/nginx.conf"
nginx pid file: "/run/nginx.pid"
nginx error log file: "/var/log/nginx/error.log"
nginx http access log file: "/var/log/nginx/access.log"
nginx http client request body temporary files: "/var/lib/nginx/tmp/"
nginx http proxy temporary files: "/var/lib/nginx/proxy/"
nginx http fastcgi temporary files: "/var/lib/nginx/fastcgi/"
nginx http uwsgi temporary files: "/var/lib/nginx/uwsgi/"
nginx http scgi temporary files: "/var/lib/nginx/scgi/"
./configure: warning: the "--with-ipv6" option is deprecated
Успешный результат make install выглядит примерно так (нет сообщений об ошибках):
...
cp objs/ngx_mail_module.so
'/usr/lib64/nginx/modules/ngx_mail_module.so' test ! -f
'/usr/lib64/nginx/modules/ngx_stream_module.so' \ || mv
'/usr/lib64/nginx/modules/ngx_stream_module.so' \
'/usr/lib64/nginx/modules/ngx_stream_module.so.old' cp
objs/ngx_stream_module.so
'/usr/lib64/nginx/modules/ngx_stream_module.so' make[1]: выход из
каталога «/usr/local/src/nginx-1.21.5»
Для понимания, компиляция с ошибками выглядит примерно так:
...
--modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/run/nginx.pid --lock-path=/run/nginx.lock --http-client-body-temp-path=/var/lib/nginx/tmp/ --http-proxy-temp-path=/var/lib/nginx/proxy/ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi/ --http-uwsgi-temp-path=/var/lib/nginx/uwsgi/ --http-scgi-temp-path=/var/lib/nginx/scgi/ --user=nginx --group=nginx --without-select_module --without-poll_module --with-threads --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-perl=/usr/bin/perl --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module --with-pcre --with-pcre-jit --with-cc-opt='-fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -fPIC -D_GNU_SOURCE' --with-ld-opt='-Wl,-z,relro,-z,now -pie' --with-compat --with-debug --add-module=../ngx-http-auth-jwt-module --add-module=/usr/local/src/echo-nginx-module/conf/nginx.conf|" \ -e "s|%%ERROR_LOG_PATH%%|/usr/ --sbin-path=/usr/sbin/nginx
--modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/run/nginx.pid --lock-path=/run/nginx.lock --http-client-body-temp-path=/var/lib/nginx/tmp/ --http-proxy-temp-path=/var/lib/nginx/proxy/ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi/ --http-uwsgi-temp-path=/var/lib/nginx/uwsgi/ --http-scgi-temp-path=/var/lib/nginx/scgi/ --user=nginx --group=nginx --without-select_module --without-poll_module --with-threads --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_auth_request_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-perl=/usr/bin/perl --with-mail=dynamic --with-mail_ssl_module --with-stream=dynamic --with-stream_ssl_module --with-stream_realip_module --with-stream_ssl_preread_module --with-pcre --with-pcre-jit --with-cc-opt='-fmessage-length=0 -grecord-gcc-switches -O2 -Wall -D_FORTIFY_SOURCE=2 -fstack-protector-strong -funwind-tables -fasynchronous-unwind-tables -fstack-clash-protection -g -fPIC -D_GNU_SOURCE' --with-ld-opt='-Wl,-z,relro,-z,now -pie' --with-compat --with-debug --add-module=../ngx-http-auth-jwt-module --add-module=/usr/local/src/echo-nginx-module/logs/error.log|" \ < man/nginx.8 > objs/nginx.8 make[1]: выход из каталога
«/usr/local/src/nginx-1.21.5»
Соответственно и make intall тоже будет с ошибками:
...
строка 0: test: слишком много аргументов mkdir: неверный ключ — «g» По команде
«mkdir --help» можно получить дополнительную информацию. make[1]: ***
[objs/Makefile:1206: install] Ошибка 1 make[1]: выход из каталога
«/usr/local/src/nginx-1.21.5» make: *** [Makefile:13: install] Ошибка 2
4) Проверяем работу подключенных модулей
Отредактируем файл конфигурации сервера. Файл конфигурации находится /etc/nginx/nginx.conf. Во первых включим логирование в режиме debug:
error_log /var/log/nginx/error.log debug;
Проверим работу echo-модуля.
Например сделаем так чтобы при вызове клиентом эндпоинта /hello наш echo-module выдавал в ответ строку "Answer using echo module." Для этого в файле конфигурации добавим следующий location:
location /hello {
echo "Answer using echo module."; #here we use echo module just for test message
}
Перезагрузим Nginx чтобы он подхватил новую конфигурацию.
systemctl start nginx
Проверим ответ сервера с помощью curl. Условно если ip-адрес сервера 192.168.1.100 тогда запрос:
curl http://192.168.1.100/hello
получить ответ:
Answer using echo-module
Проверим работу jwt-модуля.
Генерируем ключ в терминале, согласно инструкции от разработчика:
# openssl rand -hex 32
Получаем следующий hex32-ключ:
bf3708c8579fd47fa9f01c9b6b4531b359996bc4a71004d86a3e717a1c9bbdc7
Так же в инструкции сказано, что в конфигурационном файле Nginx ключ должен быть прописан в binhex формате. По этому преобразуем наш ключ в binhex формат для последующего добавления в конфиг. В терминале выполняем преобразование:
# printf bf3708c8579fd47fa9f01c9b6b4531b359996bc4a71004d86a3e717a1c9bbdc7 | xxd -p -c256
Получаем ключ в формате binhex:
62663337303863383537396664343766613966303163396236623435333162333539393936626334613731303034643836613365373137613163396262646337
! Сохраните куда-нибудь в файл оба варианта ключа hex32 и binhex !
В конфигурационный файл nginx для эндпоинта /hello добавим binhex-ключ авторизации, включим проверку токена, укажем используемый алгоритм HS256 (несмотря на то, что он используется по умолчанию), и пропишем тестовую строку echo, которую выдаст клиенту наш сервер в случае успешной аутентификации по токену:
location /hello {
auth_jwt_key "62663337303863383537396664343766613966303163396236623435333162333539393936626334613731303034643836613365373137613163396262646337";
auth_jwt_enabled on;
auth_jwt_algorithm HS256;
echo "Valid token. Please welcome!"; #here we use echo module just for test message
}
Далее сгенерируем токен на сайте https://jwt.io. Для генерации токена необходимо использовать первоначальный формат ключа - hex32. В playload токена, помимо прочих необходимых вашему приложению данных, обязательно необходимо добавить параметр exp со значением даты в формате UnixTimeStamp. Это дата истечения срока действия токена. Например "exp": 2000000000 где 2000000000 = «18.05.2033, 06:33:20».
Пример playload:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"exp": 2000000000
}
Без параметра exp наш ngx-http-auth-jwt-module не пропустит токен, т.к. будет считать его просроченным. Так же необходимо отключить base64-кодирование ключа (снять галочку "secret base64 encoded") т.к. ngx-http-auth-jwt-module сам его не раскодирует при проверке токена.
Эти тонкости выявлены методом проб и ошибок, методом научного тыка, и во многом благодаря включенному режиму debug. Именно потому, что режим debug очень полезен вцелом и особенно полезен при работе с неофициальными сторонними малозадокументированными модулями, в данной статье и описано как включить режим debug.
В итоге сайт jwt.io генерирует нам токен с нашими данными:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE4MDAwMDAwMDB9.TJ6nTHJoKkYqfmVi4e8FVAT6Om3-gfKHzMsxiYpnMuk
Естественно, в будущем нам придется научить наш REST-API-бэкенд генерировать jwt-токены. Для этого в различных системах программирования есть соответствующие библиотеки или модули. Но пока для проверки мы генерируем токен на сайте.
Ну и теперь с помощью curl, postman, bruno, arc или любым другим API-клиентом, которым вам удобнее пользоваться, мы делаем запрос на API-эндпоинт /hello с нашим токеном размещенном в заголовке с именем "authorization":
curl -H "authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJleHAiOjE4MDAwMDAwMDB9.TJ6nTHJoKkYqfmVi4e8FVAT6Om3-gfKHzMsxiYpnMuk" http://192.168.1.100/hello
Получаем ответ:
Valid token. Please welcome!
Без валидного токена мы не пройдем авторизацию и вместо контента получим ответ: "401 Unauthorized".
В инструкции к nginx-jwt-модулю сказано что можно придумать свой абсолютно любой заголовок, в котором должен передаваться токен, при этом свой заголовок необходимо прописать в nginx.conf чтобы nginx-jwt-модуль знал в каком заголовке искать токен.
Заключение
Мы с вами реализовали базовую систему авторизации на основе jwt-токенов на стороне Nginx. Теперь Nginx умеет отдавать ресурсы в обмен на токен, а тех у кого нет токена или токен просрочен Nginx шлёт лесом. Такая система существенно облегчает работу по защите ресурсов. Теперь достаточно выдавать клиенту jwt-токены, а не проверять аутентификацию при каждом чихе клиента. Это снижает нагрузку на сервер аутентификации. Разумеется система выдачи токенов клиентам это отдельная большая история. Надеюсь в будущем появится статья и об этом.
Так же система токенов позволяет наладить балансировку чтобы клиент с токеном мог обращаться к различным Nginx в зависимости от того, куда клиента направит балансировщик. При этом любой из Nginx одинаково самостоятельно может проверить токен, и принять решение выдать ресурс или не выдать.
Полезные ссылки
- Проект JWT: https://jwt.io/
- Nginx JWT-модуль: https://github.com/TeslaGov/ngx-http-auth-jwt-module
- Nginx Echo-модуль: https://github.com/openresty/echo-nginx-module
Замечания
Как выяснилось позже, модуль jwt-авторизации от TeslaGov (https://github.com/TeslaGov/ngx-http-auth-jwt-module) не поддерживает извлечение токена из строки запроса. Токен извлекается только из заголовка или из куки. Передача токена в строке запроса может быть необходима, в случае когда клиент не имеет возможности установить заголовок.
Альтернативные jwt-модули (поддержка токена в строке запроса)
https://github.com/kjdev/nginx-auth-jwt