i log it

изучаем IT

JWT-авторизация на стороне Nginx под управлением openSUSE Leap 15.5. Часть 1: Первая кровь.

- Опубликовано в Nginx

В данной статье разбираемся как организовать проверку 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 просто включается как параметр. То есть последовательность установки следующая:

  1. Jansson - установка из репозитория или скачивание и компиляция
  2. C-JWT - скачивание и компиляция
  3. Nginx-JWT-Module - только скачивание исходного кода
  4. Nginx-Echo-Module - только скачивание исходного кода
  5. Сборка 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-авторизации от TeslaGov (https://github.com/TeslaGov/ngx-http-auth-jwt-module) не поддерживает извлечение токена из строки запроса. Токен извлекается только из заголовка или из куки. Передача токена в строке запроса может быть необходима, в случае когда клиент не имеет возможности установить заголовок.

Альтернативные jwt-модули (поддержка токена в строке запроса)

https://github.com/kjdev/nginx-auth-jwt

https://github.com/clark15b/ngx-sso-module

https://github.com/tizpuppi/ngx_http_auth_jwt_module