nginx + cookie 認証の仕組みを作成

概要

「nginxで認証用proxyサーバを作成」をもとに設定
全てのアクセスでわざわざPHPを呼び出すのは非効率なので、セッションの判定部分はNginxのみで解決することにより高速化可能
http://blog.asial.co.jp/1046

GOAL

cookieにauthidというキーで、ランダム値を保存
memcachedにランダム値をキーにして、認証状態(1)をセット
cookieとmemcachedの値から、ログインしているかどうかを判定する
今回はランダム値を”abcde”とする
できればサードパーティのモジュールevalは利用しない方向で検討する

各バージョン

root@hostname:/home/shimizu# uname -a
Linux hostname 3.2.0-4-amd64 #1 SMP Debian 3.2.54-2 x86_64 GNU/Linux
root@hostname:/home/shimizu# memcached -h
memcached 1.4.13
root@hostname:/home/shimizu# nginx -v
nginx version: nginx/1.2.1
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --with-pcre-jit --with-debug --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_realip_module --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module --with-http_xslt_module --with-ipv6 --with-sha1=/usr/include/openssl --with-md5=/usr/include/openssl --with-mail --with-mail_ssl_module --add-module=/tmp/buildd/nginx-1.2.1/debian/modules/nginx-auth-pam --add-module=/tmp/buildd/nginx-1.2.1/debian/modules/nginx-echo --add-module=/tmp/buildd/nginx-1.2.1/debian/modules/nginx-upstream-fair --add-module=/tmp/buildd/nginx-1.2.1/debian/modules/nginx-dav-ext-module

memcached

memcachedにkeyを設定する

root@hostname:/var/nginx# telnet localhost 11211
Trying ::1...
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
set abcde 0 0 1
1
STORED
get abcde
VALUE abcde 0 1
1
END
quit

keyの中身をGUIで確認する

root@hostname:/var/www/htdocs# wget -d http://livebookmark.net/memcachephp/memcachephp.zip
DEBUG output created by Wget 1.13.4 on linux-gnu.

URI encoding = `UTF-8′
–2014-07-11 22:52:38– http://livebookmark.net/memcachephp/memcachephp.zip
livebookmark.net (livebookmark.net) をDNSに問いあわせています… 108.61.16.67
Caching livebookmark.net => 108.61.16.67
…(中略)
root@hostname:/var/www/htdocs# unzip memcachephp.zip
Archive: memcachephp.zip
inflating: memcache.php
root@hostname:/var/www/htdocs# chown www-data:www-data memcache.php
root@hostname:/var/www/htdocs# vi memcache.php
==================
$MEMCACHE_SERVERS[] = ‘localhost:11211’; // add more as an array
//$MEMCACHE_SERVERS[] = ‘mymemcache-server2:11211’; // add more as an array
==================

2014-07-11_230850

2014-07-11_230931

※phpからmemcachedに設定した場合、有効期限はphp.iniのsession.gc_maxlifetimeが使用される

cookieを設定する

root@hostname:/var/www/htdocs# vi setcookie.php
=====
<?php
setcookie("authid", "abcde");
echo "get cookie!"
?>
=====

setcookie.phpにアクセスする

cookieを確認する

root@hostname:/var/www/htdocs# vi getcookie.php
=====
<?php
var_dump($_COOKIE);
?>
=====

ブラウザからgetcookie.phpにアクセスすると

array(2) {
  ["__atuvc"]=>
  string(4) "6|28"
  ["authid"]=>
  string(5) "abcde"
}

ブラウザの設定画面から

2014-07-11_233444

2014-07-12_000433

nginx + memcached その1

upstream memcache {
        server 127.0.0.1:11211;
}

server {
        root /var/www/htdocs;
        index index.html index.htm;
        server_name x.x.x.x;

        location / {
                set $memcached_key abcde;
                memcached_pass memcache;
                default_type "text/html; charset=utf-8";
                error_page 404 = @notkey;
        }

        location @notkey {
                proxy_pass http://www.google.co.jp;
        }
}

memcached_passディレクティブは、キー検索を実施する際に$memcached_key変数を利用する
対応するkeyが存在しない場合は、error_page 404となり、上記場合はhttp://www.google.co.jpに転送される
対応するkeyが存在した場合はvalueを返す
通常はvalueにキャッシュを保存して利用する

nginx + memcached その2

        location / {

                if ($cookie_authid) {
                        set $memcached_key $cookie_authid;
                        memcached_pass memcache;
                        default_type "text/html; charset=utf-8";
                        error_page 404 = @notkey;
                }
        }

HTTPモジュールで$cookie_nameは、nameという名前のcookieの値と定義されている
つまり、$cookie_authidはauthidの値が入る
…ただしmemcached_passにはvalueを返すしかできず、evalモジュールが必須のようなので他の方法を模索してみる

ngx_http_map_moduleの利用

map $cookie_authid $cookie_flag {
        abcde 1;
        default 0;
}

server {
        root /var/www/htdocs;
        index index.html index.htm;
        server_name 49.212.204.46;

        location / {
                if ( $cookie_flag = 0 ){
                proxy_pass http://www.google.co.jp;
                }

                if ( $cookie_flag = 1 ){
                proxy_pass http://www.yahoo.co.jp;
                }

}

cookieのauthidの値がabcdeのときは、$cookie_flagを1に、それ以外は0にする
cookieの値に応じて動作を変更できるが、、cookieの値を変更するたびに設定を変更する必要がある、、他の方法を模索する

ngx_http_userid_moduleの利用

cookieを操作するモジュール、apacheでいうところのmod_uid

location / {
        userid          on;
        userid_name     authid_userid;
        userid_domain   49.212.204.46;
        userid_path     /;
        userid_expires  365d;
        }

/にアクセスすると、cookieが設定される
2014-07-14_115751
userごとにcookieを設定し、logformatで$uid_gotを利用するとcookieの値が取得できるため、ユーザの動きを確認できるが、、今回の要件とは合わないため他の方法を模索する

lua-nginx-moduleの利用

Note that the ngx_eval module can be approximated by using rewrite_by_lua
nginx-extrasパッケージをインストールし、この手法で実現した!

location / {
        # ファイルが存在する場合
        if (-f $request_filename) {
        rewrite_by_lua '
                local res = ngx.location.capture("/auth")
                if res.status == ngx.HTTP_OK then
                        ngx.exit(ngx.OK)
                else
                        return ngx.redirect("http://www.google.co.jp/")
                end
        ';
        break;
        }
        # ファイルが存在しなかった場合
        if  (!-e $request_filename) {
                rewrite ^(.*)$ http://www.yahoo.co.jp permanent;
        }
}

location /auth {
     internal;
     set $memcached_key $cookie_authid;
     memcached_pass 127.0.0.1:11211;
}
  • rewrite_by_luaでngx.exit(ngx.OK)を利用すると、nginxはcontentハンドラの処理に移る

おまけ-S3との連携

2014-07-21_000216

参考

http://livebookmark.net/journal/2008/05/21/memcachephp-stats-like-apcphp/
http://qiita.com/akiko-pusu/items/aae6be5fe4b77f588a80
http://wiki.nginx.org/HttpLuaModule