10. apache に Subversion をホストさせる

この方法については、わざわざ私が改めて書くこともないと思われるほど情報が多いのですが、認証周りの設定までを一通り書いておきます。

DAV を有効化

httpd.conf にて dav と dav_svn のモジュールを有効にし、公開する URL を決定します。

# DAV モジュール
LoadModule dav_module modules/mod_dav.so
LoadModule dav_svn_module modules/mod_dav_svn.so

# こちらは DAV とは関係ない承認モジュール(後述)
LoadModule authz_svn_module modules/mod_authz_svn.so

# /svn というパスの設定
<Location /svn>
    DAV                svn
    SVNParentPath      "D:/repos"
    SVNListParentPath  on
    #SVNIndexXSLT      "/svnindex.xsl"
    AuthzSVNAccessFile "D:/etc/svnaccess.conf"
</Location>

この例では、SVNParentPath を使用して D:\repos\{repository} にあるリポジトリをすべて http://localhost/svn/{repository} という URL で公開します。実は、Location を設定するとき /svn にするか /svn/ にするかは悩ましいところです。
Windows の共有フォルダやネットワークドライブの機能を用いて上記の URL に接続する場合、Windows は / がない URL にアクセスしてくるため、Location を /svn/ と設定していると共有フォルダやネットワークドライブとして開けなくなります。これだけ考えると Location は /svn にすることになります。しかし、

  • AuthzSVNAccessFile を使用してアクセス承認を行う
  • SVNParentPath を使用して複数のリポジトリをまとめて公開する
  • SVNListParentPath を on にしてリポジトリの一覧を表示できるようにする

という3つの条件がそろうと、Location を /svn としている状態でブラウザから /svn という URL にアクセスした場合に問題になります。この URL ではリポジトリが未指定なのでリポジトリ名が null になるようなのですが、後述の AuthzSVNAccessFile にて参照されるアクセス許可を確認する処理が、リポジトリ名が null だとエラーになるため /svn という URL でリポジトリ一覧を開こうとするとエラーになってしまいます。URL として /svn/ を指定すると、最後に / があるためかリポジトリ名が空文字列になるため [/] の設定を参照してリポジトリ一覧に対するアクセス承認がおりればリポジトリ一覧が表示されるという動きになります。
とりあえず、私は共有フォルダやネットワークドライブとしての利用をあきらめるようにして、

RedirectMatch permanent ^/svn$ /svn/
<Location /svn/>
    ....
</Location>

としています。

承認設定

リポジトリのアクセス承認の設定は AuthzSVNAccessFile で指定したファイルで行います。この例では D:\etc\svnaccess.conf に従うように指定していますので、このファイルを作成します。内容は、

# ユーザをグループ化して一括設定する場合、グループの設定を行う
[groups]
admins = user1, user2
group1 = user3, user4, user5
group2 = user3, user6, user7

# 全体のデフォルト設定
# SVNParentPath が on の場合、リポジトリ一覧の参照権限として使われるので誰でも読めるとする
[/]
* = r
? = r

# project1 リポジトリのデフォルト設定
#   admins, group1 のメンバーは読み書き可能
#   認証ユーザは読み取り可能
#   匿名ユーザは読み取り不可能
[project1:/]
@admins = rw
@group1 = rw
* = r
? = 

# SVK によるミラーリング用のディレクトリは外部から更新させない
[project1:/vendor]
* = r

こんなかんじの記述を行います。/vendor は SVK によって管理されるため、外部からは更新できないようにしています、これは SVKapache を経由しないでリポジトリを操作するため、このファイルに指定された読み書きの制限は受けないことを利用して、あやまってチームのメンバーがミラー先を更新しないように読み取り専用しておきます。*1

認証設定

承認設定ファイルだけでは誰がアクセスしても匿名ユーザとして扱われてしまうので、ユーザ認証の設定を行います。
apache のモジュールでは承認(authorize)に関するモジュールは authz、認証(authentication)に関するモジュールは authn という名前になっていますので、authn という名前のモジュールを有効にして設定します。一部の古いモジュールは auth という n も z もない名前になっていたり、authz と authn の両方の機能をもつものは authnz となっていたりします。
ここでは、例として mod_auth_sspi を用いて Windows のユーザとパスワードを使ってアクセスできるようにします。

LoadModule sspi_auth_module modules/mod_auth_sspi.so

と、事前に mod_auth_sspi を読み込ませておいて の間に、

    # 認証に使う Realm の名前
    # 後で trac でも認証を行うことを考えて、svn のような名前はやめましょう
    AuthName         "My Server"
    
    # この Location にて、mod_auth_sspi を有効化
    AuthType          SSPI
    SSPIAuth          on

    # NTLM 認証を有効にします (ログインしているユーザで自動的ログインを試みる)
    SSPIOfferSSPI     on

    # Basic 認証を有効にします (ユーザ/パスワードを尋ねるダイアログを表示する)
    SSPIOfferBasic    on

    # 上記2つがともに ON の場合、どちらを優先するか
    SSPIBasicPreferred  off
        # on の場合、Basic 認証が優先されるので、常にユーザ名とパスワードを聞かれます
        # off の場合、NTLM 認証が優先され、ログインできないときだけ聞かれます

    # mod_auth_sspi の認証失敗を apache の認証結果として採用する
    SSPIAuthoritative on

    # 使用するドメイン名またはコンピュータ名
    SSPIDomain        host1   
        # コンピュータ名を指定するとローカル ユーザで認証されます
        # ドメイン名を指定すると、指定されたドメイン ユーザで認証されます

    # 認証結果のドメイン名を捨てます
    SSPIOmitDomain    on      
       # domain1\user1 で認証したとき、apache や Subversion には user1 だけが渡ります。
       # off にした場合、少し前の例で AuthzSVNAccessFile で指定した svnaccess.conf にも
       # user1 ではなく domain1\user1 と書かなければなりません。

    # 認証結果を小文字に変換します
    SSPIUsernameCase  lower   
        # Windows では大文字小文字を区別していないので、認証時に user1 ではなく、
        # User1 や USER1 と入力しても認証が成功します。
        # apache や Subversion は大文字小文字を区別するので、user1 と User1 は別人だと
        # 認識します。AuthzSVNAccessFile で指定した svnaccess.conf に全パターンを登録する
        # のは現実的ではないので、全て小文字に変換するように指定して統一します。
    

というようなかんじで記述を行います。もし、Subversion Users のようなグループに参加しているユーザのみを対象にする場合は、

    require group "host1\Web Users"

としておくと、host1 のローカル グループである Web Users に参加している Windows ユーザのみが認証に成功します。ドメイン環境の場合は、host1 のかわりにドメイン名を指定してドメインのグループで制限できます。認証成功すれば誰でもアクセスできるようにするのであれば、

    require valid-user

とすれば OK です。また、匿名でのアクセスも許可する場合には、

    Satisfy Any

を追加することで、匿名ユーザで参照可能な範囲はユーザ認証の要求が発生しなくなります。実際のサーバでは、

  • 社員のユーザ認証は、会社の Active Directory で認証する
  • 外部作業員(外注)のユーザ認証は、サーバのローカル ユーザとして認証する
  • 匿名のアクセスも認める

という設定なので、

    AuthName         "My Server"

    AuthType          SSPI
    SSPIAuth          on
    SSPIOfferSSPI     on
    SSPIOfferBasic    on
    SSPIBasicPreferred  off

    # mod_auth_sspi の認証失敗を apache の認証結果として採用しない
    SSPIAuthoritative on
        # 採用しない=失敗したら次の authn モジュールへ認証を委任する

    # ローカル コンピュータで認証を試みる
    SSPIDomain        host1
    SSPIOmitDomain    on   
    SSPIUsernameCase  lower

    # ローカル グループ Web Users に所属していることを要求
    require group "host1\Web Users"

    # 平分パスワード認証を有効化
    AuthType          Basic

    # パスワードの保存先は LDAP
    AuthBasicProvider ldap

    # LDAP の接続設定とクエリ
    AuthLDAPBindDN       "..."
    AuthLDAPBindPassword "...."
    AuthLDAPURL          "ldap://..."

    # LDAP の属性に特定の値があることを要求
    require ldap-attribute ...

    # 認証に失敗してもアクセス元が社内なら匿名として認める
    Allow from ...
    Satisfy Any

*1:SVK の入門記事などでは、よくミラー先に誤ってコミットすると元リポジトリに直接コミットされてしまうので気をつけるような話がありますが、それを設定で不可能にしておこうというようなものです。