• Windows 2008な DC相手に keytabで kinitする話

    統合Windows認証用にktpass.exeを用いてサービス用のkeytabを作成。
    Active Directoryと apacheで 統合Windows認証
    kinitにこのkeytabを指定することで、パスワードを入力することなしでTGTを得ることができる。

    W2k8がDCな環境(krb5.confにおいて当該REALMのkdcにこの2008を指定している状態)で、上記エントリで作成したkeytabをkinitしようとすると、以下のエラーになってしまった。

    kinit(v5): Key table entry not found while getting initial credentials
    下のリンクは、2000と2003環境の話だけど、要領は同じみたい。
    IBM Tivoli | Microsoft Windows Server 2003環境でのWindows統合認証設定の考慮点 (Tivoli-08-030):

    クライアント側が合わせる場合:
    上記keytabはRC4で暗号化されたエントリが入っているので、krb5.conf側で暗号化にRC4を明示する。

    [libdefaults]
    default_tkt_enctypes = rc4-hmac
    default_tgs_enctypes = rc4-hmac

    keytab側をAESで暗号化する場合:
    以下のようにして暗号化にAESを明示する。

    ktpass -princ HTTP/somehost.example.com@EXAMPLE.COM -mapuser マップするユーザ名 -crypto AES256-SHA1 -ptype KRB5_NT_SRV_HST +rndPass -out 適当なファイル.keytab

    複数の暗号化でエントリを作成してもいいかも。だけど、どうせなら暗号強度が強い方に倒した方がいいのかなーと。

    $ kinit -kt keytabファイル HTTP/somehost.example.com
    $ klist
    Ticket cache: FILE:/tmp/krb5cc_9999
    Default principal: HTTP/somehost.example.com@EXAMPLE.COM

    Valid starting Expires Service principal
    08/30/10 19:02:41 08/31/10 05:02:42 krbtgt/EXAMPLE.COM@EXAMPLE.COM
            renew until 08/31/10 19:02:41


    Kerberos 4 ticket cache: /tmp/tkt9999
    klist: You have no tickets cached

    まずはこの状態で、SASL-GSSAPIによる認証を用い、ldapsearchでAD上のオブジェクトを検索できるとこまで確認した。つづきはそのうち。


    環境:
    Windows Server 2008 Standard x64
    CentOS 5.5
     krb5-workstation-1.6.1-36.el5_5.5
     krb5-libs-1.6.1-36.el5_5.5


  • 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」になる。
  • LDAP認証でADユーザを使ったredMineへのログイン

    プロジェクトをまたいで自分に関連するチケットをリストアップできる、という点に興味があり、巷でウワサのredMineをたててみる。
    LDAPに既に存在するアカウントでログインできる、という点も魅力だ。

    環境


    redMine環境は、FC5、ruby-1.8.5.35-2.fc5、rails (1.2.3)、mongrel (1.0.1)、mongrel_cluster (1.0.2)、redMine-0.5.1。
    Active DirectoryはW2k3で動作。ユーザの姓名には日本語を指定しており、表示名が「赤峰 るび夫」のようになる運用をしている。ログオン名は姓名とは別の英数字IDを任意で指定している。

    ADドメインはad.example.comとする。

    redMineの設定

    • adminでredMineにログイン
    • 管理>認証>新しい認証モード
      名前:適宜。ADドメイン(のNTドメイン表現)と同じにした。
      ホスト:ADサーバホスト名。
      ポート:389
      アカウント:cn=表示名, cn=Users, dc=ad, dc=example, dc=com
    • このユーザはredmineの認証用に作成した。試行錯誤の過程で英字で姓のみのユーザを作成した。後述のinitialize_ldap_conの関係で日本語姓名だとbindできない様子。
    • ディレクトリに対する匿名アクセス可能なアカウントを作った方がよかったかも。
      パスワード:上記ユーザのパスワード
      BaseDN:cn=Users, dc=ad, dc=example, dc=com

    あわせてユーザを作成:true
    ログイン:sAMAccountName
    →ADユーザのプロパティでは、「アカウント ユーザログオン名」
    名前:givenName
    →ADユーザのプロパティでは、「名」
    苗字:sn
    →ADユーザのプロパティでは、「姓」
    メールアドレス:mail
    →ADユーザのプロパティでは、「電子メール」

    • アスタリスクが必須項目マークだと思うんだけど、マークに従えば、ログインだけ指定すればいいことになる。のだけど、テーブルを見るとNotNull制約みたいなので、4項目とも設定した。
    • AD側のユーザも合わせて上記項目を埋めた。

    mysql> desc users;
    +-------------------+-------------+------+-----+---------+----------------+
    | Field             | Type        | Null | Key | Default | Extra          |
    +-------------------+-------------+------+-----+---------+----------------+
    | id                | int(11)     | NO   | PRI | NULL    | auto_increment |
    | login             | varchar(30) | NO   |     |         |                |
    | hashed_password   | varchar(40) | NO   |     |         |                |
    | firstname         | varchar(30) | NO   |     |         |                |
    | lastname          | varchar(30) | NO   |     |         |                |
    | mail              | varchar(60) | NO   |     |         |                |
    | mail_notification | tinyint(1)  | NO   |     | 1       |                |
    | admin             | tinyint(1)  | NO   |     | 0       |                |
    | status            | int(11)     | NO   |     | 1       |                |
    | last_login_on     | datetime    | YES  |     | NULL    |                |
    | language          | varchar(5)  | YES  |     |         |                |
    | auth_source_id    | int(11)     | YES  |     | NULL    |                |
    | created_on        | datetime    | YES  |     | NULL    |                |
    | updated_on        | datetime    | YES  |     | NULL    |                |
    +-------------------+-------------+------+-----+---------+----------------+

    • 認証モードを保存して一覧に戻る。
    • 「テスト」をクリックして「接続しました。」が表示されることを確認する。

    ログイン失敗

    これで上手くいくかなーと思ったら、ログインできない。
    production.logを見たらiconvがこけてるらしい。

    Processing AccountController#login (for *.*.*.* at 2007-09-19 18:43:52) [POST]
      Session ID: feb2a36cbb1a47dd7b0396964d0185e3
      Parameters: {"action"=>"login", "controller"=>"account", "password"=>"[FILTERED]", "login"=>"ログインID"}


    Iconv::IllegalSequence ("姓 名,CN"...):
        /app/models/auth_source_ldap.rb:72:in `iconv'
        /app/models/auth_source_ldap.rb:72:in `initialize_ldap_con'
        /app/models/auth_source_ldap.rb:47:in `authenticate'
        (略)



    認証モードの設定で、ログイン=sAMAccountNameとしていて日本語周辺は回避した、と思っていたのだが、内部的に以下のような流れらしい。
    1. 認証モードで「アカウント」に指定したDNとパスワードでLDAPに接続
    2. この接続を使って、redmineのログインID==sAMAccountNameとなるエントリを問い合わせる。
    3. 問い合わせた結果得られたDN+ログインフォームに入れたパスワードで再度LDAPに接続
    4. 接続できたら認証OK
    なので、(ここの運用だと)3のDNに日本語が含まれ、これをiso-8859-15→utf-8変換しようとしてiconvで死ぬ。ということみたい。

    とりあえず回避

    場当たり的に、ADからの応答もutf-8だと決めてかかることに。
    app/models/auth_source_ldap.rbをちょこっと書き換え。

    private
      def initialize_ldap_con(ldap_user, ldap_password)
        Net::LDAP.new( {:host => self.host,
                        :port => self.port,
                        #:auth => { :method => :simple, :username => Iconv.new('iso-8859-15', 'utf-8').iconv(ldap_user), :password => Iconv.new('iso-8859-15', 'utf-8').iconv(ldap_password) }}
                        :auth => { :method => :simple, :username => ldap_user, :password => ldap_password }}

        )
      end

    おわり

    mongrel_clusterをrestartし、再度ログインフォーム。
    ADユーザのログオン名+パスワードでredMineにログインできた。
    ふー。

    でも「あるAD上のグループに属しているユーザだけredMineを使える」みたいなコントロールはよくわからず。
    うーん。