Nginx + Google-authenticator or not everything is not so simple ...
- Tutorial
Authorization in nginx based on google one-time passwords.
For various reasons, I had to refuse authorization auth_basic and a file with passwords, it’s not secure and that’s all.
There are many users with different levels of knowledge, so certificate authorization is not suitable.
They suggested a solution based on Nginx (http_auth_request_module) + Apache (google-authenticator-apache-module).
He picked it up for several days, but could not understand some points of how it works. Poking around also figured out.

The idea is as follows. Authorization check does not occur on nginx itself, but on the apache server. Nginx sends the auth_request subquery to the apache 'Authentication Server' if authorization is passed apache responds with HTTP 200, and nginx happily considers the user to be authorized.
How it works. On the 'Authorization Server' you create a file (File name - login) for each user that contains a certain secure key.
Without going into details, we assume that on the basis of this secure key and the current time, on the server side and on the client side, independently of each other, one-time passwords are generated every minute.
As a password generating application the following is used:
Google Authenticator for Android .
Google Authenticator for iOS .
If you do not have a phone, then there is an application for Windows:
Google Authenticator for Windows .
There is also under java ...
How to synchronize the application with the server a little later, but now what needs to be configured on the server.
Unfortunately, there is no module under Nginx and it cannot be due to the peculiarities of the Nginx implementation. Why I can’t say for sure, but they said words about state mashine and so on ... :)
But there is a module under Apache !!! It is called google-authenticator-apache-module.
There are also binary assemblies for centsOS 6 - it works and checked. There are source codes and there is more recent in the repo. I played around with the binary assembly and then assembled the module from the source.
So, on apache + google-authenticator-apache-module, we make an “authorization server” (you can even have 2 servers for reliability) and connect Nginx_y to it on all servers where we need it.
So we take apache2, collect or take the google-authenticator-apache-module binary module.
and write this config:
We restart apache, try to open it in the browser - is there an authorization request? Perfectly!!!
Next ...
/ etc / httpd / ga_auth - the directory where the files with private keys are.
How and how to create them: google-authenticator
Download, libpam-google-authenticator , collect, we get google-authenticator. Here she can generate what we need.
I made a script so that the file went right where I needed it.
We pass the user login parameter to the script.
Having finished the script, it will create a file and output it to the console:
the / etc / httpd / ga_auth / _user_login_ file contains a lot of different things but nothing affects the operation of google-authenticator-apache-module except “Your new secret key is:”, at least I experimented - nothing ... so you can leave only the first line with QUCFKE6AK3PBA4QA but you can not touch the file.
then open the link https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/zzzz@mydomain.auth%3Fsecret%3DQYYY34XXXX534A4QA
(the link is intentionally a curve)
and we see

Install on the phone (if not installed yet) Google Authenticator . We scan the QR code, and the whole phone starts generating a one-time password every minute.
You can try to log in to apache, it should work, but it can issue 404 - because after authorization apache wants to show some kind of index.html but you probably don’t have it.
By the way, the google-authenticator-apache-module module honestly writes to / var / log / httpd / error_log.
And so, we have an “authorization server” and a phone - both generate the same sequence of one-time passwords.
Let's move on to nginx configuration.
we take Nginx fresher, I took the last stable 1.5.7 , we collect with --with-http_auth_request_module (http_auth_request_module key point).
I selected this piece of config in a separate file and I will include it where necessary.
in /etc/nginx/nginx.conf at the end we add the apache + server pool at least 2 for reliability:
now wherever we need for Nginx add:
Check :)
/etc/nginx/allow_nets.txt - a list of IPs that do not need to be authorized. I think it’s not necessary to torment everyone with authorization.
Now the most interesting thing is the subtleties:
It all works as follows.
Logged in. While you are working on the site, your cookie authorization is constantly being checked.
It is constantly being updated, 1390714695 - the time when the cookie expires. It is constantly updating the current time + {GoogleAuthCookieLife 600} from the apache config. You can say this time of inactivity on the page. departed for 10 minutes - log in again.
And the second point. The one-time password you entered does not last forever. In general, the term is 1 minute and that’s it. But using the parameter
GoogleAuthEntryWindow 3, you can expand the "time window" of the password.
The point is that google-authenticator-apache-module can generate, in addition to the current password, GoogleAuthEntryWindow passwords before and after, this is done so that if the clock on your mobile and the clock on the server do not match, you can still log in.
You can do the same so that your one-time password goes through authorization longer, this will allow you to keep authorization “passed” longer;
And finally, a piece of the log from apache:
Gradually, or rather, once a minute, lines of the type
will be reduced until there is only a line with your password, and after a minute - the authorization is over.
For various reasons, I had to refuse authorization auth_basic and a file with passwords, it’s not secure and that’s all.
There are many users with different levels of knowledge, so certificate authorization is not suitable.
They suggested a solution based on Nginx (http_auth_request_module) + Apache (google-authenticator-apache-module).
He picked it up for several days, but could not understand some points of how it works. Poking around also figured out.

The idea is as follows. Authorization check does not occur on nginx itself, but on the apache server. Nginx sends the auth_request subquery to the apache 'Authentication Server' if authorization is passed apache responds with HTTP 200, and nginx happily considers the user to be authorized.
How it works. On the 'Authorization Server' you create a file (File name - login) for each user that contains a certain secure key.
Without going into details, we assume that on the basis of this secure key and the current time, on the server side and on the client side, independently of each other, one-time passwords are generated every minute.
As a password generating application the following is used:
Google Authenticator for Android .
Google Authenticator for iOS .
If you do not have a phone, then there is an application for Windows:
Google Authenticator for Windows .
There is also under java ...
How to synchronize the application with the server a little later, but now what needs to be configured on the server.
Unfortunately, there is no module under Nginx and it cannot be due to the peculiarities of the Nginx implementation. Why I can’t say for sure, but they said words about state mashine and so on ... :)
But there is a module under Apache !!! It is called google-authenticator-apache-module.
There are also binary assemblies for centsOS 6 - it works and checked. There are source codes and there is more recent in the repo. I played around with the binary assembly and then assembled the module from the source.
So, on apache + google-authenticator-apache-module, we make an “authorization server” (you can even have 2 servers for reliability) and connect Nginx_y to it on all servers where we need it.
So we take apache2, collect or take the google-authenticator-apache-module binary module.
and write this config:
Loadmodule authn_google_module modules/mod_authn_google.so
Listen *:8888
# разрешаем только сети где у нас стоят 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
We restart apache, try to open it in the browser - is there an authorization request? Perfectly!!!
Next ...
/ etc / httpd / ga_auth - the directory where the files with private keys are.
How and how to create them: google-authenticator
Download, libpam-google-authenticator , collect, we get google-authenticator. Here she can generate what we need.
I made a script so that the file went right where I needed it.
#!/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
We pass the user login parameter to the script.
Having finished the script, it will create a file and output it to the console:
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
the / etc / httpd / ga_auth / _user_login_ file contains a lot of different things but nothing affects the operation of google-authenticator-apache-module except “Your new secret key is:”, at least I experimented - nothing ... so you can leave only the first line with QUCFKE6AK3PBA4QA but you can not touch the file.
then open the link https://www.google.com/chart?chs=200x200&chld=M|0&cht=qr&chl=otpauth://totp/zzzz@mydomain.auth%3Fsecret%3DQYYY34XXXX534A4QA
(the link is intentionally a curve)
and we see

Install on the phone (if not installed yet) Google Authenticator . We scan the QR code, and the whole phone starts generating a one-time password every minute.
You can try to log in to apache, it should work, but it can issue 404 - because after authorization apache wants to show some kind of index.html but you probably don’t have it.
By the way, the google-authenticator-apache-module module honestly writes to / var / log / httpd / error_log.
And so, we have an “authorization server” and a phone - both generate the same sequence of one-time passwords.
Let's move on to nginx configuration.
we take Nginx fresher, I took the last stable 1.5.7 , we collect with --with-http_auth_request_module (http_auth_request_module key point).
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;
}
I selected this piece of config in a separate file and I will include it where necessary.
in /etc/nginx/nginx.conf at the end we add the apache + server pool at least 2 for reliability:
upstream gauth_pool {
server ga1.mydomain.com:8888 weight=1;
server ga2.mydomain.com:8888 weight=5;
}
now wherever we need for Nginx add:
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;
Check :)
/etc/nginx/allow_nets.txt - a list of IPs that do not need to be authorized. I think it’s not necessary to torment everyone with authorization.
Now the most interesting thing is the subtleties:
It all works as follows.
Logged in. While you are working on the site, your cookie authorization is constantly being checked.
set-cookie: google_authn=user:1390714695:FRxZCSDzox/a5KEGXXXXXXX5TYGIYZrRf=
It is constantly being updated, 1390714695 - the time when the cookie expires. It is constantly updating the current time + {GoogleAuthCookieLife 600} from the apache config. You can say this time of inactivity on the page. departed for 10 minutes - log in again.
And the second point. The one-time password you entered does not last forever. In general, the term is 1 minute and that’s it. But using the parameter
GoogleAuthEntryWindow 3, you can expand the "time window" of the password.
The point is that google-authenticator-apache-module can generate, in addition to the current password, GoogleAuthEntryWindow passwords before and after, this is done so that if the clock on your mobile and the clock on the server do not match, you can still log in.
You can do the same so that your one-time password goes through authorization longer, this will allow you to keep authorization “passed” longer;
And finally, a piece of the log from 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/
Gradually, or rather, once a minute, lines of the type
Comparing Authentication @ T=46017151 Code=87841 "332994" vs.
will be reduced until there is only a line with your password, and after a minute - the authorization is over.