Apacheのmod_auth_tktとPythonのAuthTkt

# 情報の鮮度

* 2014-01-08時点
* Debian GNU/Linux 7.3 (wheezy)
* Python 2.7 (apt-get経由)
* AuthTkt 0.3.2 (pip経由)
* mod_auth_tkt 2.1.0-6 (apt-get経由)
* django 1.何か

# バックグラウンド

* djangoで作られたWebサイトがありました
* djangoの各ページは適宜認証・認可が出来ています
* 静的ファイルはdjangoの管理下ではなくApacheに管理させています
* 静的ファイルは、独立したBASIC認証でお茶を濁していました
* djangoの認証を直接使おうとしたが……良くわからない状態 :-(
    * 静的ファイルへアクセスした際にdjango認証を通ってなかったらdjangoの方へリダイレクト、
とか出来たらいいんですが、そちらは見つかりませんでした。
* 「チケットベース」の認証システムの存在を聞きました
* 使ってみようと思いました

# 関連ドキュメントについて

まずmod_auth_tktが提供する「チケット認証」とやらの概要を知るため、ドキュメント漁りです。
しかし両モジュール共にドキュメントがあまりありません。

ただし、クッキーに収められている情報自体は単純なので、そんなに面倒ではない感じでした。

主要な情報ソースですが……

* mod_auth_tktは man mod_auth_tkt で設定の上で必要な情報がほぼ手に入ります。
    * あるいはこういうの http://linux.die.net/man/3/mod_auth_tkt
    * mod_auth_tktのソースまでは見ませんでした
* AuthTkt は輪をかけて情報が少ないので、Perlのオリジナルの実装 (AuthTktは移植したもの) を参考にするか、ソースコード上のコメントを見ます。
    * AuthTktのソースを読んでもいいし、対話環境でhelp(authtkt.AuthTicket)とかやっても良いと思います
    * ただし実装が完成されているわけでもないので、ソースちら見した方がはまりづらいはず|дﾟ)ﾁﾗｯ

この記事では概要だけ書きます。

# mod_auth_tkt/AuthTkt のがいよー

チケット発行者 (今回はdjangoのサイト)とチケット消費者 (今回はApacheの特定のディレクトリ) は、
以下を共有します。

* 共通の秘密 (TKTAuthSecret)
    * 適当に生成しておきます。全体で1つ (これは利用パターンによっては大問題かも)
* ハッシュアルゴリズム (TKTAuthDigestType)
    * デフォルトではMD5です
    * AuthTktがMD5しか対応してません
* クッキーの名前 (デフォルトで"auth_tkt"で変える必要あまりない)
* クッキーのドメイン (指定したければ)

この二つを共有している状態で、チケット発行者は

* 共通の秘密
* ハッシュアルゴリズム
* ユーザ名
* IPアドレス
* Token (0個以上の文字列。後述)
* など

を元にチケットをクッキーとして保存します。
並べてありますが、きちっと説明すれば「共通の秘密」「ハッシュアルゴリズム」はチケットを作る際に必要になるもので、
一方「ユーザ名」や「IPアドレス」はそのチケット (クッキー) に含まれる情報です。

Apacheはクッキーの名前やらと共通の秘密を用いてチケットを確認し、

* お望みのユーザ名か
* IPアドレスが変わってないか
* Tokenは期待したものか

などを確認して、認証・認可を行います。

Tokenは双方複数指定できます (無くても良い)。
"admin" Tokenを消費する側が期待したら、
発行者は"admin" Tokenをチケットに埋め込んでおかなければならないのです。
ある程度のアクセスコントロールを実現出来るかと思います。
(ただ、共通の秘密が1つしか持てなくてアレなので、あんまりこれでガッチガチACLやっても何かゼイジャクな感じ)

チケットの有効範囲や有効期限に関してはそれなりに細かく指定できるようですが、
本稿の範囲ではありません。


# 環境の用意

```
# apt-get install mod_auth_tkt
# pip install -U AuthTkt
```
# Apache側設定例

mod_auth_tktのmanページに例が結構書いてあるので、
それを見たほうが良いと思いますが……

```
TKTAuthSecret "うかが"

<Directory /opt/griflet/data/result/>
    AuthType None
    TKTAuthLoginURL http://example.com/django/issue_ticket
    TKTAuthDomain example.com
    TKTAuthDebug 3
</Directory>
```

Debianについては/etc/apache2/mods-enabled/auth_tkt.conf にTKTAuthSecretを書くよう期待されている節があります。

TKTAuthDebugをつけておくとerror.logにたんまりエラーが出ます。説明は全部manページに丸投げ。

# django側実装例

特にクッキーの設定が割といい加減の可能性が高いですが、とりあえず。
(urls.pyなり他の全ては省略)

```py
@login_required
def issue_ticket(request):
    user = request.user
    # http://stackoverflow.com/questions/4581789/how-do-i-get-user-ip-address-in-django
    ip = utils.get_client_ip(request)
    token = authtkt.AuthTicket(TKT_AUTH_SECRET,
                               user.username,
                               ip,
                               tokens=['user'])
    # TODO: Redirect しましょう
    response = render(request, '{}/hello.html'.format(NAMESPACE), {})
    response.set_cookie('auth_tkt', token.cookie_value(),
                        domain=TKT_AUTH_DOMAIN)
    return response
```

TODOで書いてありますが、"TKTAuthLoginURL"にチケット無しでアクセスした際、queryに"back"という戻り先URLが設定された状態でこのページにリダイレクトされます。ユーザのためにredirectしてあげましょう

# 参考

特にPython実装側について、紛らわしいモジュールがたくさんあります

* django-authtkt 本モジュールとは関係ありません
* mod_auth_pubtkt 本モジュールとは関係ありません
* auth_tkt pipで見つかりますが、これもやっぱり関係ありません
* AuthTkt これが今回の記事の利用したモジュール

どれが良いかは適宜判断してください。
ただ、消費側 (Apache) の仕様が変わってなければ実質的にやることには大差はないはずです。

# 感じた課題

 * mod_auth_tkt: 最近nginxですよね
 * AuthTkt: MD5だけですか……
 * そもそも共通の秘密がWebサーバで一つ……

今回考えている小規模ではまぁいいとして、規模大きいとかだとまた考えることは多そうです。

# まとめ

そういうことも出来るのだと覚えておくと調べやすいでしょう。

# 追記 (2014-01-10)

もう少し調べた結果自分の用途には合わなかったということでそれも報告します。

例えば、動的に複数のプロジェクトが生成されて消滅するdjangoのWebサイトを構築したとします (例えば、プロジェクト管理)。その際、プロジェクト毎に異なる種類のチケットを発行して、チケットの種類に応じてダウンロードできるファイルを変えたいと考えます。

これが、どーもApacheのmod_auth_tktだけでは出来ないのです。

具体的には、mod_auth_tktの現在の実装ではいかなる意味でもTKTAuthTokenに変数を許しません。Apacheの環境変数を仮にSetEnvIfで設定しても、それを取得することが少なくとも検証したバージョンでは出来ないようです。ソースコードを見て見ましたが、やはりそれをハンドルするロジックはない。

X-SendFileの話を聞いたので、そちらを試すことにします。