それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

Apache認証をSQLでRDBMSへ連携する mod_authn_dbd と mod_dbを使う。

Apache のDigest認証って便利なんだけど

Apache のパスワード認証がSQLで出来たら楽じゃん。htpasswd コマンドでファイルを置いておくと、管理が面倒になってきて書き散らかす。なにより有効期限とか決めたいじゃん。

かといってLDAPを聞いたりPAMに投げるのは、PAM・LDAPにガンガンユーザー増やすことになって本意じゃないし。なんかいい方法ないかとApacheのDocumentをあさってたら dbd とか便利そうなの見つけた。

Fileの代わりにデータベース(SQL:RDMBS)を使える。

mod_db 使ってみた。

mod_db で認証をSQLで連携できる

仕組みとしてはApacheにDigest(Basic)認証で、パスワードをファイルじゃなく、RDBMSSQLを使うことになる。

mod_dbd を使うことで、次のようなパスワード連携が可能になる。

httpリクエスト→ directory → 認証 → DB へ問合せ (SQL

ただし、パスワードの形式は、Basic/Digestで使うパスワード形式になるので注意が必要。

Apacheのmod_auth の命名規則

命名規則 意味
mod_auth_XXXX 認証方法(標準はbasic,digest,formしかない)
mod_authn_XXXX 認証バックエンド(要はユーザーデータベース)
mod_authz_XXXX 承認(アクセス制御)を提供
mod_authnz_XXX 認証・承認のどちらも

承認については、セキュリティ屋は認可と呼んでる。一般的にはアクセス許可と呼ばれる。

設定について

DB 連携でApacheの認証をするには、いろいろ前提条件があるので整理しておく。

前提条件 mod_authnz_dbd mod_dbd 
設定の記述場所 httpd.conf / virtualhost
データベースの保存先 /Users/takuya/Sites/dbdExample/users.sqlite3.db
認証で保護するURL http://localhost/~takuya/dbdExample/
認証方式 Basic 

digest でも良いんだけど動作確認のために、Basicでやりました。通常はDigestを使う。

用語の整理 dbd => DataBaseDriver の略

基本設定 モジュールロード

まず、始めに、apacheでモジュールをロードしておく

今回使ったApacheOSXにバンドルされているApache2.4です

takuya@~/Sites/dbd$ httpd -V
Server version: Apache/2.4.16 (Unix)
Server built:   Aug 22 2015 16:51:57
Server's Module Magic Number: 20120211:47
Server loaded:  APR 1.4.8, APR-UTIL 1.5.2
Compiled using: APR 1.4.8, APR-UTIL 1.5.2
Architecture:   64-bit
Server MPM:     prefork
  threaded:     no
    forked:     yes (variable process count)
Server compiled with....
 -D APR_HAS_SENDFILE
 -D APR_HAS_MMAP
 -D APR_HAVE_IPV6 (IPv4-mapped addresses enabled)
 -D APR_USE_FLOCK_SERIALIZE
 -D APR_USE_PTHREAD_SERIALIZE
 -D SINGLE_LISTEN_UNSERIALIZED_ACCEPT
 -D APR_HAS_OTHER_CHILD
 -D AP_HAVE_RELIABLE_PIPED_LOGS
 -D DYNAMIC_MODULE_LIMIT=256
 -D HTTPD_ROOT="/usr"
 -D SUEXEC_BIN="/usr/bin/suexec"
 -D DEFAULT_PIDLOG="/private/var/run/httpd.pid"
 -D DEFAULT_SCOREBOARD="logs/apache_runtime_status"
 -D DEFAULT_ERRORLOG="logs/error_log"
 -D AP_TYPES_CONFIG_FILE="/private/etc/apache2/mime.types"
 -D SERVER_CONFIG_FILE="/private/etc/apache2/httpd.conf"

httpd.conf でモジュールをロード

dbd と authnz_dbd をロードしておく。

takuya@~/Sites/dbd$ grep dbd /etc/apache2/httpd.conf
69:LoadModule authn_dbd_module libexec/apache2/mod_authn_dbd.so
94:LoadModule dbd_module libexec/apache2/mod_dbd.so

ロード設定を書いたら、ロードが正しく出来るか、チェックする

sudo apachectl -k restart

基本設定 データベースドライバの指定

今回は、/etc/apache2/users/takuya.conf にある。 ユーザーディレクトリの設定に書いた

takuya.conf は httpd.conf からIncludeされるだけなので、httpd.conf の領域に書いたことになる。

/etc/apache2/users/takuya.conf

DBDriver sqlite3
DBDParams  "/Users/takuya/Sites/dbdExample/users.sqlite3.db"

設定が出来たら、DBDriverが存在スルかチェックするために再起動。

sudo apachectl -k restart

認証設定

ここ迄が無事終わったら、認証設定をして当該ディレクトリに認証設定を施す。

DBDriver sqlite3
DBDParams  "/Users/takuya/Sites/dbdExample/users.sqlite3.db"
<Directory /Users/takuya/Sites/dbdExample >

  Authtype Basic
  AuthName "takuya's private "
  AuthBasicProvider dbd
  require valid-user

  AuthDdbUserPwQuery "SELECT password from users where user = %s "
</Directory>

設定を追加したら、認証設定を有効にするために、一旦再起動をしておく。

sudo apachectl -k restart

再起動が終わったら、認証設定が有効になっているか、アクセスして確認しておく

takuya@~/Sites/dbd$ curl http://localhost/~takuya/dbdExample/index.php
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>401 Unauthorized</title>
</head><body>
<h1>Unauthorized</h1>
<p>This server could not verify that you
are authorized to access the document
requested.  Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.</p>
</body></html>

curl でアクセスして、認証設定がオンになっていて、401が返って来てることがわかる。

ブラウザでアクセスしてもいい。Basic認証のダイアログが出てきたら成功

ついでに、適当なパスワードで認証して実験しておく

 curl -I http://takuya:mypass@localhost/~takuya/dbdExample/index.php
HTTP/1.1 401 Unauthorized
Date: Fri, 13 Nov 2015 11:39:23 GMT
Server: Apache/2.4.16 (Unix) Phusion_Passenger/5.0.8 PHP/5.5.29
WWW-Authenticate: Basic realm="takuya's private "
Content-Type: text/html; charset=iso-8859-1

これで、データベースさえ準備すればイイ前提条件が整った。

このときのApacheのエラーログを確認すると・・・

(日付省略) Internal error: AH00629: Can't connect to sqlite3:
 unable to open database file
(日付省略) Internal error: AH00633: failed to initialise
(日付省略) AH01653: Failed to acquire database connection
 to look up user 'takuya'

データベースが見つからないとか、takuya ユーザーのパスワードは取れなかったとか、期待通りにエラーが出てきている。

ここで、はじめてデータベースを作る準備が整った

データベースを作る

データベースをとりあえず、ぱぱっと作る

sqlite3 /Users/takuya/Sites/dbdExample/users.sqlite3.db

sqlite3 コマンドでCREATE TABLEを実行する

SQLite version 3.8.11.1 2015-07-29 20:00:57
Enter ".help" for usage hints.
>>CREATE TABLE users (
 user varchar(255) not null unique ,
 password varchar(255) not null,
);

とりあえず超シンプルなデータベースとテーブルを用意した

テーブルにユーザを作る。

テーブルにユーザを作る。

 insert into users ( user , password ) values ("takuya","takuya");

このパスワードは平文でとりあえず入れている。(エラーになるはず、データベースが見つからないエラは消えるはず)

これでユーザを作れたので、実際にアクセスしてみる

takuya@~/Sites/dbd$ curl -I http://takuya:takuya@localhost/~takuya/dbdExample/index.php
HTTP/1.1 401 Unauthorized
Date: Fri, 13 Nov 2015 11:44:28 GMT
Server: Apache/2.4.16 (Unix) Phusion_Passenger/5.0.8 PHP/5.5.29
WWW-Authenticate: Basic realm="takuya's private "
Content-Type: text/html; charset=iso-8859-1

認証は、通らない。(これはパスワードを平文で入れているため)

この時のApacheのエラーログを確認すると

(日付省略) AH01617: user takuya: authentication failure
 for "/~takuya/dbdExample/index.php": Password Mismatch

エラーのログは「Password Mismatch」に変わった。

これで、データベースへの参照は正しく行われたことがわかる。

apacheが解釈できるパスワードを作る

今回はBasic認証をすることにしたので、Basic認証用のパスワードを作る

作り方はhtpasswd を使うのが早い。

htpasswd -n ユーザー名

実際にヤってみたものが以下のとおりに。

takuya@~$ htpasswd -n takuya
New password:
Re-type new password:
takuya:$apr1$tXIGze4Z$EaCVCt9GTlSpXAUTEwk1x.

パスワードをテーブルに登録。

先程は、適当な平文パスワードを入れたので、Basic認証用で使えるパスワード文字列に変更する

SQLite version 3.8.11.1 2015-07-29 20:00:57
Enter ".help" for usage hints.
>> update users
  set password = '$apr1$tXIGze4Z$EaCVCt9GTlSpXAUTEwk1x.'
 where user = "takuya";
>>

これで、users テーブルの中にあるパスワードが更新された。

ここまでで、完璧に予定通り来ているので、最後に認証を通してみる。

認証データ付きでアクセスしてみる

さて、これでうまくいくとほぼ確信しつつ、認証を通してみる。

takuya@~/Sites/dbd$ curl -I http://takuya:takuya@localhost/~takuya/dbdExample/index.php
HTTP/1.1 200 OK
Date: Fri, 13 Nov 2015 11:51:25 GMT
Server: Apache/2.4.16 (Unix) Phusion_Passenger/5.0.8 PHP/5.5.29
X-Powered-By: PHP/5.5.29
Content-Type: text/html

無事アクセス出来るようになった。やったね。

アクセスはCurlでおこなったけど、ブラウザで確認してもいい。

テーブル構造を変化させてみる。

Apacheが実行するSQLは、directory ディレクティブに記述した次のSQLになる。

AuthDdbUserPwQuery "SELECT password from users where user = %s "

SQLでカラムを指定してるだけ。つまりだ、コレ以外にカラムがあったって問題ないわけですよね。

テーブル構造を変更してみる。

CREATE TABLE users (
 id integer primary key,
 user varchar(255) not null unique ,
 password varchar(255) not null,
 created_at timestamp default current_timestamp not null,
 expires timestamp
);

このようなテーブルに変えてみた。もちろんチャント動く。

SQLite 以外のRDBMSを使う。

mod_dbd のドライバをインストールしていれば

などもチャント動くようです。ドライバがインストールされてて、コネクションできてSQLが実行できればなんでもイイみたい。

SQL をアレンジしてみる。

AuthDdbUserPwQuery \
 "SELECT password from users \
   where user = %s  \
   and expires > current_timestamp"

このようにすれば、アクセスユーザーを増やしても、アクセス期限を設定することが出来て、ユーザーの管理や削除を忘れておける。便利!

このユーザは、いつ、なんのため、いつまで有効か、をコメントで入れなくてもいい。テーブルで管理できる。そして期限が来ても削除する必要がなくてイイ。

htpasswd ファイルとさようなら

はじめて「ホームページ」を作ったアノ頃からお世話になった.htpasswd に別れを告げる時のようだ。

これで、htpasswd ファイルとサヨナラできる。大学生新入生時代から10年近くAapache使ってるけど、ようやく、理解できた。

参考資料

mod_authn_dbd - Apache HTTP Server Version 2.2

mod_dbd - Apache HTTP Server Version 2.2