• Active Directoryと apacheで 統合Windows認証

    ad_apache.png
    ウェブで事例を見かけて以来、やってみたいと考えていた。
    ↓このページを見て何度かトライしてみたんだけどずーっと上手くいってなかった。
    using mod_auth_kerb and Windows 2000/2003 as KDC

    けど、ついに自分ところの環境で動いたのでメモ。

    手順や設定はほぼ上のURLに書いてあるとおりなんだけど、自分のところでは
    KrbServiceName HTTP
    だとダメで
    KrbServiceName HTTP/ウェブサーバのFQDN
    にしたらあっさり通った。
    ネットで見かけた設定例だと「HTTP」か設定なしなので自分ところだけおかしいのか~。わからん。

    LogLevelをdebugにしてerror.logを見る。

    KrbServiceNameをHTTPに設定していると以下の通り「HTTP@ウェブサーバのFQDN」に対するcredentialを得ようとしているっぽい。
    [debug] src/mod_auth_kerb.c(1147): [client 192.*.*.*] Acquiring creds for HTTP@ウェブサーバのFQDN
    [error] [client 192.*.*.*] gss_acquire_cred() failed: An invalid name was supplied (Hostname cannot be canonicalized)
    keytabのプリンシパルはHTTP/www.example.cc@EXAMPLE.CCという感じなので@以降が補完されたとしても合致しないことになる。

    mod_auth_kerb.c(これは5.4のソース)のget_gss_credsを見ると「/」が含まれるかどうかで挙動が変わるっぽい。
       have_server_princ = conf->krb_service_name && strchr(conf->krb_service_name,'/') != NULL;
       if (have_server_princ)
          strncpy(buf, conf->krb_service_name, sizeof(buf));
       else if (conf->krb_service_name && strcmp(conf->krb_service_name,"Any") == 0) {
          *server_creds = GSS_C_NO_CREDENTIAL;
          return 0;
       }
       else
          snprintf(buf, sizeof(buf), "%s@%s",
               (conf->krb_service_name) ? conf->krb_service_name : SERVICE_NAME,
               ap_get_server_name(r));
    
    とにかく僕のところの環境ではKrbServiceNameにサービスプリンシパル名を記述することにした。
    副作用というかなんというか結果的にプリンシパル名中のFQDNとウェブサーバのFQDNが異なってても認証が通るのでVirtualHostを使っててもkeytabが同じでいいので手間は少ない?

    サーバ側の準備

    • ADサーバとドメイン構築
      • ADサーバからwebサーバのFQDNを引けるように。
    • ADサーバで
      • サービスマップ用のダミーアカウントを作成
      • ktpass.exeでサービスプリンシパルをダミーアカウントにマップしつつkeytabファイル作成
        ktpass.exe -princ HTTP/ウェブサーバのFQDN@EXAMPLE.CC 
        -crypto rc4-hmac-nt -ptype KRB5_NT_SRV_HST
        -mapuser ダミーアカウント@EXAMPLE.CC -pass パスワード -out 出力先ファイル名
      • 古いktpass.exeには問題があるそうなので、MSから最新版を入手して使った
        http://support.microsoft.com/kb/926027
    • webサーバで
      • /etc/krb5.conf設定
        [libdefaults]
         default_realm = EXAMPLE.CC
         dns_lookup_realm = false
         dns_lookup_kdc = false
         ticket_lifetime = 24h
         forwardable = yes

        [realms]
         EXAMPLE.CC = {
          kdc = ADサーバ
          default_domain = example.cc
         }

        [domain_realm]
         .example.cc = EXAMPLE.CC
         example.cc = EXAMPLE.CC
      • ADサーバ上で作ったkeytabファイルをwebサーバに持ってくる
      • apacheユーザから参照できるようにowner/groupを設定しchmodで400に
      • 認証領域設定。locationでもdirectoryでも。
            <Location /認証領域/>
                AuthType  Kerberos
                KrbAuthRealms EXAMPLE.CC
                KrbServiceName HTTP/ウェブサーバのFQDN(keytab作成したときと同じ名前)
                Krb5Keytab keytabファイルのpath
                KrbMethodNegotiate on
                KrbMethodK5Passwd off
                Require valid-user
      • reloadなり再起動なり

    クライアント側の準備

    • クライアントPCをドメインに参加させる
    • ドメインユーザでログオン
    • IEの場合
      • オプションから統合Windows認証を有効に。デフォルトは有効
      • 統合Windows認証の対象にしたいサイトのドメインを「イントラネット」扱いに設定
        • [ツール]→[オプション]→セキュリティタブ→「サイト」ボタン→「詳細設定」ボタン
        • ドメインを入力して→「追加」ボタン
        • httpsを必要とするかどうかは適宜
    • Firefoxの場合
      • about:configを開く
      • network.negotiate-auth.trusted-urisを編集、統合Windows認証の対象にしたいドメイン名を設定

    環境

    CentOS: 5.2
      mod_auth_kerb-5.1-3.el5
      httpd-2.2.3-11.el5_2.centos.4
    W2k3 R2 EE SP2
      SupportToolsの更新版
      IE7
    Windows XP
      IE6
      Fx3.0.5

    おまけ

    統合Windows認証を通過するとREMOTE_USERが「user1@EXAMPLE.CC」のようにrealm付きの値になる。
    mod_auth_kerbが5.4の場合、KrbLocalUserMappingをonにしておくとrealm部分がstripされて上記例なら「user1」になる。
  • Catalystとmod_perlでapacheのアクセスログにユーザを記録する

    ひさしぶりにwebアプリを書くことになり、既存のフレームワークを利用することにした。
    Rails周辺の喧騒はなんとなく気にしていて興味はあったのだけど、rubyを最後に触ったのが5年前で、今回はより慣れているperlでいくことに。

    「まるごとperl」でフレームワークの特集記事があって、Catalyst・Sledge・Jiftyが紹介されていた。Jiftyのポニーの由来とDB上のスキーマありき「ではない」ところにすごく惹かれたけど、今回はあまり七転八倒する時間的猶予がなく、web上の日本語情報が多そうなCatalyst+mod_perlに決めた。

    フレームワークというのはなんだかんだでフレームワーク自身の内部的な動きも含めて理解しないと、ホントに便利にはならないものだと再認識。
    特にプラグインをパイプ的にチェーンするフレームワークでは。

    フレームワーク固有というよりはutf8フラグ絡みなんだけど、文字化けとの格闘を越えてやっと便利だなぁと思えるようになってきた。

    apacheのアクセスログにユーザを記録する

    認証といえばインフラのレイヤに任せる、という方式を好むのだけど、今回はアプリの都合もあってフレームワークとアプリで認証の面倒を見ることにした。
    そうするとapacheのアクセスログにリモートユーザ(LogFormatでの%u指定)を記録するようにしていても「-(ハイフン)」で記録されてしまう。

    • 今回の環境は、apache 2.2.3、mod_perl 2.0.2、Catalyst 5.7006、perl 5.8.8、認証/セッションはAuthentication::CDBIとSession::FastMmapを使用。
    • コンテキストオブジェクトから、Apache2::RequestRecを得る。
      my $rr = $c->apache;
    • あとは、認証ユーザを設定するだけ。Root.pmのauto内で設定している。
      $rr->user( $c->req->{user} );
    • ただし、テストサーバで実行中はコンテキストオブジェクトにapacheメソッドがないので、こんな感じにした。
      $c->apache->user( $c->req->{user} ) if $c->can("apache") && $c->req->{user};
    既存のアクセスログ解析ツールをそのまま使えるので都合がいい。
    • staticなリソースについてはCatalystを経由せずapacheが直接serveするようにしているので相変わらず「-(ハイフン)」だけど。
  • ウェブサービスを書いてみる - コネクタ編

    アクセス解析をapacheのログとまとめて行えると便利なので、コネクタを利用してapache→Tomcatへの接続ルートを用意する。 ウチのサーバの場合、外に公開するのはNAT内のapache(TCP:80)だけにしておきたいので、Tomcat(TCP:8080)を野ざらしにしない意味もある。

    mod_jk

    • Apache Tomcat - Apache Tomcatから、トップ→[Download/Tomcat Connectors]。
    • JKをダウンロードする。今回使用したのは「JK 1.2.15 Source Release tar.gz」
    • アーカイブを適当な場所に展開してビルドする。このエントリは事後に書いているので細かいことは忘れてしまったが、たぶん下のような感じ。
      > ./configure --with-apxs=/usr/sbin/apxs
      > make
    • できあがった「mod_jk.so」をapacheのmodule置き場に配置し、confにモジュールのロードとログの設定を書く。/etc/httpd/conf.d/tomcat.conf
      LoadModule jk_module modules/mod_jk.so
      JkLogFile /var/log/httpd/mod_jk.log
      JkLogLevel info
    • ウチのサーバの場合、仮想ホスト運用しているので、必要なVirtualHostディレクティブ内にJKのマウント設定を書く。何もかも外に出す必要はないので最低限の範囲で。そうしないとAdminServiceへも外からアクセスできてしまう。
      JkMount /KeitaisoWS/services/KeitaisoKaisekiService* ajp13

    起動

    • apache→TomcatへTCPセッションが張られるので、Tomcatが先に起動している必要があるみたいだ。
    • Tomcatを再起動した場合、apacheにSIGHUPを送るなどして再接続が必要。これってapacheが先に起動していた場合でもセッションが確立していなかったら改めて接続を試みる、みたいな動きに出来ないのかな。。
    • ブラウザから、http://passing.breeze.cc/KeitaisoWS/services/KeitaisoKaisekiService?wsdl にアクセスしてコネクタが働いているか確認する。
    • ブラウザから、http://passing.breeze.cc/KeitaisoWS/ にアクセスしてコネクタが働いていないことを確認する。