Авторизация в nginx на базе google одноразовых паролей.
По разным причинам пришлось отказаться от авторизации auth_basic и файла с паролями, несекурно и все тут.
Пользователей много с разным уровнем знаний, поэтому авторизация по сертификатам не подходит.
Подсказали решение на базе Nginx (http_auth_request_module) + Apache (google-authenticator-apache-module).
Поковырявшись несколько дней поднял, но не мог понять некоторые моменты как работают. Поковырявшись еще и разобрался.
Идея в следующем. Проверка авторизации происходит не на самом 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
(ссылка намеренно кривая)
и видим
Устанавливаем на телефон (если пока еще не установили) 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