Nginx + Google-authenticator или не все не так просто…

в 3:47, , рубрики: nginx

Авторизация в nginx на базе google одноразовых паролей.

По разным причинам пришлось отказаться от авторизации auth_basic и файла с паролями, несекурно и все тут.
Пользователей много с разным уровнем знаний, поэтому авторизация по сертификатам не подходит.

Подсказали решение на базе Nginx (http_auth_request_module) + Apache (google-authenticator-apache-module).

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

Nginx + Google authenticator или не все не так просто…

Идея в следующем. Проверка авторизации происходит не на самом nginx а на сервере apache. Nginx посылает подзапрос auth_request на 'Сервер авторизации' apache, если авторизация пройдена apache отвечает HTTP 200, и nginx радостно считает что пользователь авторизацию прошел.

Как это работает. На 'Сервере авторизации' вы создаете файл(Имя файла — логин) для каждого пользователя который содержит некий secure key.
Не вдаваясь в подробности будем считать что на базе этого secure key и текущего времени, на стороне сервера и на стороне клиента, независимо друг от друга, каждую минуту генерируются одноразовые пароли.

В качестве приложения генерирующего пароли используется:

Google Authenticator for Android.
Google Authenticator for iOS.
Если у вас нет телефона, то есть приложение и под Windows:
Google Authenticator for Windows.
Тамже есть и под java…

Как синхронизировать приложение с сервером чуть позже, а сейчас что же надо настроить на сервере.

К сожалению модуля под Nginx нету и быть не может из-за особенностей реализации Nginx. Почему точно сказать не могу, но мне говорили слова про state mashine и тд… :)

Но под Apache модуль есть!!! Зовется google-authenticator-apache-module

Там есть и бинарные сборки под centsOS 6 — работает проверил. Есть и исходники и есть более свежее в репо. Я поигрался с бинарной сборкой а потом собрал модуль из исходников.

Значит так на apache + google-authenticator-apache-module делаем «сервер авторизации» (можно даже 2 сервера для надежности) и к нему подключаем Nginx_ы на всех серверах где нам надо.

Итак берем apache2, собираем или берем бинарный модуль google-authenticator-apache-module.

и пишем такой конфиг:

Loadmodule authn_google_module  modules/mod_authn_google.so
Listen *:8888

<Location ~ "/(|_auth/)" >
# разрешаем только сети где у нас стоят Nginx_ы. Я еще и файрволом прикрыл на всякий случай :)
   Order deny,allow
   Deny from all
   Allow from 10.0.0.0/8
   Allow from 192.168.0.0/24

   AuthType Basic
   AuthName "My Closed Zone  Gauth"
   AuthBasicProvider "google_authenticator"
   Require valid-user
   GoogleAuthUserPath /etc/httpd/ga_auth
   GoogleAuthCookieLife 600
   GoogleAuthEntryWindow 3
   #  GoogleAuthLogLevel - работает только в последней версии собранной из исходников.
   GoogleAuthLogLevel 1
</Location>

Рестартуем apache, пробуем в юраузере отрыть его — есть запрос авторизации? Прекрасно!!!

Дальше…

/etc/httpd/ga_auth — директория где лежат файлы с секретными ключами.

Как и чем их создавать: google-authenticator

Качаем, libpam-google-authenticator, собираем получаем google-authenticator. Вот она может генерировать то что нам надо.

Я сделал скрипт чтоб файл ложился сразу куда надо.

#!/bin/bash
/usr/bin/google-authenticator -t -D -f -l$1@mydomain.com -r3 -R600 -s /etc/httpd/ga_auth/$1 -w2
/bin/chown apache:apache  /etc/httpd/ga_auth/$1

Скрипту передаем параметром логин пользователя.

Отработав скрипт создаст файл и выдаст в консоль:

https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/zzzz@mydomain.auth%3Fsecret%3DQYYY34XXXX534A4QA
Your new secret key is: DQYYY34XXXX534A4QA
Your verification code is 123456
Your emergency scratch codes are:
  99942105
  28654999
  45999608
  33300650
  99907825

в файл /etc/httpd/ga_auth/_user_login_ пришется много разного но ничего не влияет на работу google-authenticator-apache-module кроме «Your new secret key is:», по крайне мере я экспериментировал — ничего… так что можно оставить только первую строку с QUCFKE6AK3PBA4QA а можно и не трогать файл.

дальше открываем ссылку https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/zzzz@mydomain.auth%3Fsecret%3DQYYY34XXXX534A4QA
(ссылка намеренно кривая)
и видим

Nginx + Google authenticator или не все не так просто…

Устанавливаем на телефон (если пока еще не установили) Google Authenticator. Сканируем QR код, и все телефон начинает каждую минуту генерировать одноразовый пароль.

Можете попробовать авторизоваться в apache, должно сработать, но может выдать 404 — т.к. после авторизации apache хочет показать какой нибудь index.html а его у вас возможно нет.

Кстати google-authenticator-apache-module модуль честно пишет в /var/log/httpd/error_log.

И так, у нас есть «сервер авторизации» и телефон — оба генерируют одинаковую последовательность одноразовых паролей.

Перейдем к настройке nginx.

берем Nginx посвежее, я взял последний стабильный 1.5.7, собираем с --with-http_auth_request_module (http_auth_request_module ключевой момент).

        auth_request_set $auth_cookie $upstream_http_set_cookie;
        add_header Set-Cookie $auth_cookie;


        location  = /_auth/ {
           internal;
                proxy_pass http://gauth_pool/;
                proxy_pass_request_body off;
                proxy_buffering off;
                proxy_cache off;
                proxy_set_header Content-Length "";
                proxy_set_header Host mydomain.com;
        }

я выделил этот кусок конфига в отдельный файл и буду include там где будет нужно.

в /etc/nginx/nginx.conf в конце добавляем пул серверов apache + хотя бы 2 для надежности:

upstream  gauth_pool  {
        server ga1.mydomain.com:8888 weight=1;
        server ga2.mydomain.com:8888 weight=5;
}

теперь везде где надо для Nginx добавляем:


server {
        listen 443 ssl spdy;
#       listen 80;

        server_name www.mydomain.com;

        satisfy  any;
        include   /etc/nginx/allow_nets.txt;
        deny  all;
        auth_request /_auth/;

        include location_auth.conf;

Проверяем :)

/etc/nginx/allow_nets.txt — список IP которым не надо авторизоваться. Думаю не надо всех мучать авторизацией.

Теперь самое интересное — тонкости:

Работает все это следующим образом.

Авторизовались. Пока работаете на сайте постоянно происходит проверка вашей авторизации по куки
set-cookie: google_authn=user:1390714695:FRxZCSDzox/a5KEGXXXXXXX5TYGIYZrRf=

Она постоянно обновляется, 1390714695 — время когда истекает срок действия куки. Она постоянно обновляется текущее время + {GoogleAuthCookieLife 600} из конфига apache. Можно сказать это время бездействия на страничке. отошли на 10 минут — заново авторизуйтесь.

И второй момент. Введенный вами одноразовый пароль действует не вечно. Вообще срок ему 1 минута и все. Но с помощью параметра
GoogleAuthEntryWindow 3, можно раздвинуть «временное окно» действия паролей.
Смысл в том что google-authenticator-apache-module может генерировать помимо текущего пароля, пароли до и после в количестве GoogleAuthEntryWindow, это сделано для того что если часы на вашем мобильном и часы на сервер не совпадают — вы все равно смогли залогиниться.

Это же можно сделать чтоб ваш одноразовый пароль дольше проходил авторизацию, это будет позволять дольше сохранять авторизацию «пройденой»;

Ну и напоследок кусок лога из apache:

[Sun Jan 20 09:01:49 2014] [error] [client 1.2.3.4] **** COOKIE AUTH at  T=1390714549, referer: https://www.mydomain.com/
[Sun Jan 20 09:01:49 2014] [error] [client 1.2.3.4] Cookie auth is DECLINED, referer: https://www.mydomain.com/
[Sun Jan 20 09:01:49 2014] [error] [client 1.2.3.4] **** PW AUTH at  T=1390714549  user  "my_user", referer: https://www.mydomain.com/
[Sun Jan 20 09:01:49 2014] [error] [client 1.2.3.4] getUserSecret with username "my_user", referer: https://www.mydomain.com/
[Sun Jan 20 09:01:49 2014] [error] [client 1.2.3.4] OPENING FILENAME /etc/httpd/ga_auth/my_user, referer: https://www.mydomain.com/
[Sun Jan 20 09:01:49 2014] [error] [client 1.2.3.4] Comparing Authentication   @ T=46017151 Code=475002 "332994" vs.  "475002", referer: https://www.mydomain.com/
[Sun Jan 20 09:01:49 2014] [error] [client 1.2.3.4] Comparing Authentication   @ T=46017151 Code=87841 "332994" vs.  "087841", referer: https://www.mydomain.com/
[Sun Jan 20 09:01:49 2014] [error] [client 1.2.3.4] Comparing Authentication   @ T=46017151 Code=627132 "332994" vs.  "627132", referer: https://www.mydomain.com/
[Sun Jan 20 09:01:49 2014] [error] [client 1.2.3.4] Comparing Authentication   @ T=46017151 Code=332994 "332994" vs.  "332994", referer: https://www.mydomain.com/
[Sun Jan 20 09:01:49 2014] [error] [client 1.2.3.4] Created cookie expires 1390715149 hash is sEFQLm92bSI= Cookie: google_authn=my_user:1390715149:sEFQLm92bSI=, referer: https://www.mydomain.com/

Постепенно, а точнее раз в минуту строки типа
Comparing Authentication @ T=46017151 Code=87841 "332994" vs.
будут сокращаться, пока не останется строка только с вашим паролем, и через минуту — авторизация по новой.

Автор: rukhem

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js