﻿<chapter id="svn.serverconfig">
  <title>配置服务器</title>
  
  <simplesect>
    
    <para>一个Subversion的版本库可以和客户端同时运行在同一个机器上，使用<literal>file:///</literal>访问，但是一个典型的Subversion设置应该包括一个单独的服务器，可以被办公室的所有客户端访问&mdash;或者有可能是整个世界。</para>

    
    <para>本小节描述了怎样将一个Subversion的版本库暴露给远程客户端，我们会覆盖Subversion已存在的服务器机制，讨论各种方式的配置和使用。经过阅读本小节，你可以决定你需要哪种网络设置，并且明白怎样在你的主机上进行配置。</para>
    
  </simplesect>
  
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.serverconfig.overview">
    
    <title>概述</title>
    
    <para>Subversion的设计包括一个抽象的网络层，这意味着版本库可以通过各种服务器进程访问，而且客户端<quote>版本库访问</quote>的API允许程序员写出相关协议的插件，理论上讲，Subversion可以使用无限数量的网络协议实现，目前实践中存在着两种服务器。</para>
    
    <para>Apache是最流行的web服务器，通过使用<command>mod_dav_svn</command>模块，Apache可以访问版本库，并且可以使客户端使用HTTP的扩展协议WebDAV/DeltaV进行访问，另一个是<command>svnserve</command>：一个小的，独立服务器，使用自己定义的协议和客户端，表格6-1比较了这两种服务器。</para>

    <para>需要注意到Subversion作为一个开源的项目，并没有官方的指定何种服务器是<quote>主要的</quote>或者是<quote>官方的</quote>，并没有那种网络实现被视作二等公民，每种服务器都有自己的优点和缺点，事实上，不同的服务器可以并行工作，分别通过自己的方式访问版本库，它们之间不会互相阻碍（见<xref
      linkend="svn.serverconfig.multimethod"/>）。<xref
      linkend="svn.serverconfig.overview.tbl-1"/>是对两种存在的Subversion服务器的比较&mdash;作为一个管理员，你更加胜任给你和你的用户挑选服务器的任务。</para>
      

    <table id="svn.serverconfig.overview.tbl-1">
      <title>网络服务器比较</title>
      <tgroup cols="3">
        <thead>
          <row>
            <entry>特性</entry>
            <entry>Apache + mod_dav_svn</entry>
            <entry>svnserve</entry>
          </row>
        </thead>
        <tbody>
          <row>
            <entry>认证选项</entry>
            
            <entry>HTTP(S) basic auth、X.509 certificates、LDAP、NTLM或任何Apache httpd已经具备的方式</entry>
            
            <entry>CRAM-MD5或SSH</entry>
          </row>
          
          <row>
            <entry>用户帐号选项</entry>
            
            <entry>私有的'users'文件</entry>
            
            <entry>私有的'users'文件，或存在的系统(SSH)帐户
              </entry>
          </row>
          
          <row>
            <entry>授权选项</entry>
            
            <entry>整体的读/写访问，或者是每目录的读/写访问</entry>
            
            <entry>整体的读/写访问，或者是使用pre-commit钩子的每目录写访问（但不是读）</entry>
          </row>
          
          <row>
            <entry>加密</entry>
            
            <entry>通过选择SSL</entry>

            <entry>通过选择SSH通道</entry>
          </row>

          <row>
            <entry>交互性</entry>
            
            <entry>可以部分的被其他WebDAV客户端使用</entry>

            <entry>不能被其他客户端使用</entry>
          </row>

          <row>
            <entry>Web浏览能力</entry>
            
            <entry>有限的内置支持，或者通过第三方工具，如ViewVC</entry>

            <entry>通过第三方工具，如ViewVC</entry>
          </row>

          <row>
            <entry>速度</entry>
            
            <entry>有些慢</entry>

            <entry>快一点</entry>
          </row>

          <row>
            <entry>初始化配置</entry>
            
            <entry>有些复杂</entry>

            <entry>相当简单</entry>
          </row>

        </tbody>
      </tgroup>      
    </table>
    
  </sect1>

  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.serverconfig.netmodel">

    <title>网络模型</title>

    <para>这部分是讨论了Subversion客户端和服务器怎样互相交流，不考虑具体使用的网络实现，通过阅读，你会很好的理解服务器的行为方式和多种客户端与之响应的配置方式。</para>

    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.netmodel.reqresp">
      <title>请求和响应</title>

      <para>Subversion客户端花费大量的时间来管理工作拷贝，当它需要版本库信息，它会做一个网络请求，然后服务器给一个恰当的回答，具体的网络协议细节对用户不可见，客户端尝试去访问一个URL，根据URL模式的不同，会使用特定的协议与服务器联系（见<xref
        linkend="svn.basic.in-action.wc.sb-1"/>），用户可以运行<command>svn
        --version</command>来查看客户端可以使用的URL模式和协议。</para>

      <para>当服务器处理一个客户端请求，它通常会要求客户端确定它自己的身份，它会发出一个认证请求给客户端，而客户端通过提供<firstterm>凭证</firstterm>给服务器作为响应，一旦认证结束，服务器会响应客户端最初请求的信息。注意这个系统与CVS之类的系统不一样，它们会在请求之前，预先提供凭证（<quote>logs
        in</quote>）给服务器，在Subversion里，服务器通过请求客户端适时地<quote>拖入</quote>凭证，而不是客户端<quote>推</quote>出。这使得这种操作更加的优雅，例如，如果一个服务器配置为世界上的任何人都可以读取版本库，在客户使用<command>svn
        checkout</command>时，服务器永远不会发起一个认证请求。</para>

      <para>如果客户端请求往版本库写入新的数据（例如<command>svn commit</command>），这会建立新的修订版本树，如果客户端的请求是经过认证的，认证过的用户的用户名就会作为<literal>svn:author</literal>属性的值保存到新的修订本里（见<xref linkend="svn.reposadmin.basics.revprops"/>）。如果客户端没有经过认证（换句话说，服务器没有发起过认证请求），这时修订本的<literal>svn:author</literal>的值是空的。<footnote><para>这个问题实际上是一个FAQ，源自错误的服务器配置。</para></footnote></para>

    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.netmodel.credcache">
      <title>客户端凭证缓存</title>

      <para>许多服务器配置为在每次请求时要求认证，这对一次次输入用户名和密码的用户来说是非常恼人的事情。</para>

      <para>令人高兴的是，Subversion客户端对此有一个修补：存在一个在磁盘上保存认证凭证缓存的系统，缺省情况下，当一个命令行客户端成功的响应了服务器的认证请求，它会保存一个认证文件到用户的私有运行配置区&mdash;类Unix系统下会在<filename>~/.subversion/auth/</filename>，Windows下在<filename>%APPDATA%/Subversion/auth/</filename>（运行区在<xref
        linkend="svn.advanced.confarea"/>会有更多细节描述）。成功的凭证会缓存在磁盘，以主机名、端口和认证域的组合作为唯一性区别。</para>  

      <para>当客户端接收到一个认证请求，它会首先查找用户磁盘中的认证凭证缓存，如果没有发现，或者是缓存的凭证认证失败，客户端会提示用户需要这些信息。</para>

      <para>十分关心安全的人们一定会想<quote>把密码缓存在磁盘？太可怕了，永远不要这样做！</quote>但是请保持冷静，并没有你想象得那么可怕。</para>
     
      <itemizedlist>

        <listitem>
          <para><filename>auth/</filename>缓存区只有用户（拥有者）可以访问，而不是全世界都可以，操作系统的访问许可可以保护密码文件。</para>
        </listitem>

        <listitem>
          <para>在Windows 2000或更新的系统上，Subversion客户端使用标准Windows加密服务来加密磁盘上的密码。因为加密密钥是Windows管理的，与用户的登陆凭证相关，只有用户可以解密密码。（注意：如果用户的Windows账户密码被管理员重置，所有的缓存密码就不可以解密了，此时Subversion客户端就会当它们根本不存在，在需要时继续询问密码。）</para>
        </listitem>

        <listitem>
          <para>真正的偏执狂才会牺牲所有的便利，可以完全的关闭凭证缓存。
          </para>
        </listitem>

      </itemizedlist>
     
     <para>你可以关闭凭证缓存，只需要一个简单的命令，使用参数<option>--no-auth-cache</option>：</para>

<screen>
$ svn commit -F log_msg.txt --no-auth-cache
Authentication realm: &lt;svn://host.example.com:3690&gt; example realm
Username:  joe
Password for 'joe':

Adding         newfile
Transmitting file data .
Committed revision 2324.

# password was not cached, so a second commit still prompts us

$ svn delete newfile
$ svn commit -F new_msg.txt
Authentication realm: &lt;svn://host.example.com:3690&gt; example realm
Username:  joe
&hellip;
</screen>

      <para>或许，你希望永远关闭凭证缓存，你可以编辑你的运行<filename>配置</filename>文件（坐落在<filename>auth/</filename>目录），只需要把<literal>store-auth-creds</literal>设置为<literal>no</literal>，这样就不会有凭证缓存在磁盘。</para>

      <screen>
[auth]
store-auth-creds = no
</screen>

      <para>有时候，用户希望从磁盘缓存删除特定的凭证，为此你可以浏览到<filename>auth/</filename>区域，删除特定的缓存文件，凭证都是作为一个单独的文件缓存，如果你打开每一个文件，你会看到键和值，<literal>svn:realmstring</literal>描述了这个文件关联的特定服务器的域：</para>

      <screen>
$ ls ~/.subversion/auth/svn.simple/
5671adf2865e267db74f09ba6f872c28        
3893ed123b39500bca8a0b382839198e
5c3c22968347b390f349ff340196ed39

$ cat ~/.subversion/auth/svn.simple/5671adf2865e267db74f09ba6f872c28

K 8
username
V 3
joe
K 8
password
V 4
blah
K 15
svn:realmstring
V 45
&lt;https://svn.domain.com:443&gt; Joe's repository
END
</screen>

      <para>一旦你定位了正确的缓存文件，只需要删除它。</para>

      <para>客户端认证的行为的最后一点：对使用<option>--username</option>和<option>--password</option>选项的一点说明，许多客户端和子命令接受这个选项，但是要明白使用这个选项<emphasis>不会</emphasis>主动地发送凭证信息到服务器，就像前面讨论过的，服务器会在需要的时候才会从客户端<quote>拖</quote>入凭证，客户端不会随意<quote>推</quote>出。如果一个用户名和/或者密码作为选项传入，它们<emphasis>只会</emphasis>在服务器需要时展现给服务器。<footnote><para>再次重申，一个常见的错误是把服务器配置为从不会请求认证，当用户传递<option>--username</option>和<option>--password</option>给客户端时，他们惊奇的发现它们没有被使用，如新的修订版本看起来始终是由匿名用户提交的！</para></footnote>通常，只有在如下情况下才会使用这些选项：</para>

      <itemizedlist>
        <listitem>
          <para>用户希望使用与登陆系统不同的名字认证，或者</para>
        </listitem>
        <listitem>
          <para>一段不希望使用缓存凭证但需要认证的脚本</para>
        </listitem>
      </itemizedlist>
          

      <para>这里是Subversion客户端在收到认证请求的时候的行为方式：</para>

      <orderedlist>
        <listitem>
          <para>检查用户是否通过<option>--username</option>和/或<option>--password</option>命令选项指定了任何凭证信息，如果没有，或者这些选项没有认证成功，然后</para>
        </listitem>

        <listitem>
          <para>查找运行中的<filename>auth/</filename>区域保存的服务器域信息，来确定用户是否已经有了恰当的认证缓存，如果没有，或者缓存凭证认证失败，然后</para>
        </listitem>

        <listitem>
          <para>提示用户输入。</para>
        </listitem>
      </orderedlist>

      <para>如果客户端通过以上的任何一种方式成功认证，它会尝试在磁盘缓存凭证（除非用户已经关闭了这种行为方式，在前面提到过。）</para>

    </sect2>

  </sect1>


  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.serverconfig.svnserve">
    
    <title>svnserve，一个自定义的服务器</title>

    <para><command>svnserve</command>是一个轻型的服务器，可以同客户端通过在TCP/IP基础上的自定义有状态协议通讯，客户端通过使用开头为<literal>svn://</literal>或者<literal>svn+ssh://</literal><command>svnserve</command>的URL来访问一个<command>svnserve</command>服务器。这一小节将会解释运行<command>svnserve</command>的不同方式，客户端怎样实现服务器的认证，怎样配置版本库恰当的访问控制。</para>

    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.svnserve.invoking">
      <title>调用服务器</title>

      <para>有许多调用<command>svnserve</command>的方式，如果调用时没有参数，你只会看到一些帮助信息，然而，如果你计划使用<command>inetd</command>启动进程，你可以传递<option>-i</option>（<option>--inetd</option>）选项：</para>

      <screen>
$ svnserve -i
( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline ) ) )
</screen>

      <para>当用参数<option>--inetd</option>调用时，<command>svnserve</command>会尝试使用自定义协议通过<emphasis>stdin</emphasis>和<emphasis>stdout</emphasis>来与Subversion客户端通话，这是使用<command>inetd</command>工作的标准方式，IANA为Subversion协议保留3690端口，所以在类Unix系统你可以在<filename>/etc/services</filename>添加如下的几行（如果他们还不存在）：</para>

      <screen>
svn           3690/tcp   # Subversion
svn           3690/udp   # Subversion
</screen>

      <para>如果系统是使用经典的类Unix的<command>inetd</command>守护进程，你可以在<filename>/etc/inetd.conf</filename>添加这几行：</para>

      <screen>
svn stream tcp nowait svnowner /usr/bin/svnserve svnserve -i
</screen>

      <para>确定<quote>svnowner</quote>用户拥有访问版本库的适当权限，现在如果一个客户连接来到你的服务器的端口3690，<command>inetd</command>会产生一个<command>svnserve</command>进程来做服务。</para>

      <para>在一个Windows系统，有第三方工具可以将<command>svnserve</command>作为服务运行，请看Subversion的网站的工具列表。</para>

      <para><command>svnserve</command>的第二个选项是作为独立<quote>守护</quote>进程，为此要使用<option>-d</option>选项：</para>

        <screen>
$ svnserve -d
$               # svnserve is now running, listening on port 3690
</screen>

      <para>当以守护模式运行<command>svnserve</command>时，你可以使用<option>--listen-port=</option>和<option>--listen-host=</option>选项来自定义<quote>绑定</quote>的端口和主机名。</para>

      <para>也一直有第三种方式，使用<option>-t</option>选项的<quote>管道模式</quote>，这个模式假定一个分布式服务程序如<command>RSH</command>或<command>SSH</command>已经验证了一个用户，并且<emphasis>以这个用户</emphasis>调用了一个私有<command>svnserve</command>进程，<command>svnserve</command>运作如常（通过<emphasis>stdin</emphasis>和<emphasis>stdout</emphasis>通讯），并且可以设想通讯是自动转向到一种通道传递回客户端，当<command>svnserve</command>被这样的通道代理调用，确定认证用户对版本数据库有完全的读写权限，（见<xref
        linkend="svn.serverconfig.svnserve.invoking.sb-1"/>。）这与本地用户通过<literal>file:///</literal>URl访问版本库同样重要。</para>

      <sidebar id="svn.serverconfig.svnserve.invoking.sb-1">
        <title>服务器和访问许可：一个警告</title>        

        <para>首先需要记住，一个Subversion版本库是一组数据库文件，任何进程直接访问版本库需要对整个版本库有正确的读写许可，如果你不仔细处理，这会变得很头痛，特别是当你使用Berkeley DB数据库而不是FSFS时，详细信息可以阅读<xref linkend="svn.serverconfig.multimethod"/>。</para>

        <para>第二点，当配置<command>svnserve</command>、Apache <command>httpd</command>或者其它任何服务器时，不要使用<literal>root</literal>用户（或者其它具备无限制权限的用户）启动服务器进程，根据所有权和版本库允许的权限，通常应该创建一个新的自定义用户，例如很多管理员会创建一个叫做<literal>svn</literal>的用户，赋予这个用户排他的拥有权和对Subversion版本库的导出权利，只让服务器以这个用户运行。</para>
      </sidebar>


      <para>一旦<command>svnserve</command>已经运行，它会将你系统中所有版本库发布到网络，一个客户端需要指定版本库在URL中的<emphasis>绝对</emphasis>路径，举个例子，如果一个版本库是位于<filename>/usr/local/repositories/project1</filename>，则一个客户端可以使用<systemitem
        class="url">svn://host.example.com/usr/local/repositories/project1
        </systemitem>来进行访问，为了提高安全性，你可以使用<command>svnserve</command>的<option>-r</option>选项，这样会限制只输出指定路径下的版本库：</para>
      
      <screen>
$ svnserve -d -r /usr/local/repositories
&hellip;
</screen>

      <para>使用<option>-r</option>可以有效地改变文件系统的根位置，客户端可以使用去掉前半部分的路径，留下的要短一些的（更加有提示性）URL：</para>
      
      <screen>
$ svn checkout svn://host.example.com/project1
&hellip;
</screen>
 
    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.svnserve.auth">
      <title>内置的认证和授权</title>

      <para>如果一个客户端连接到<command>svnserve</command>进程，如下事情会发生：</para>

      <itemizedlist>
        <listitem><para>客户端选择特定的版本库。</para></listitem>

        <listitem><para>服务器处理版本库的<filename>conf/svnserve.conf</filename>文件，并且执行里面定义的所有认证和授权政策。</para></listitem>

        <listitem><para>依赖于位置和授权政策，</para>

          <itemizedlist>
            <listitem><para>如果没有收到认证请求，客户端可能被允许匿名访问，或者</para></listitem>

            <listitem><para>客户端收到认证请求，或者</para></listitem>

            <listitem><para>如果操作在<quote>通道模式</quote>，客户端会宣布自己已经在外部得到认证。</para></listitem>
          </itemizedlist>
        </listitem>

      </itemizedlist>

      <para>在撰写本文时，服务器还只知道怎样发送CRAM-MD5<footnote><para>见RFC 2195。</para></footnote>认证请求，本质上讲，就是服务器发送一些数据到客户端，客户端使用MD5哈希算法创建这些数据组合密码的指纹，然后返回指纹，服务器执行同样的计算并且来计算结果的一致性，<emphasis>真正的密码并没有在互联网上传递。</emphasis></para>

      <para>当然也有可能，如果客户端在外部通过通道代理认证，如<command>SSH</command>，在那种情况下，服务器简单的检验作为那个用户的运行，然后使用它作为认证用户名，更多信息请看<xref
        linkend="svn.serverconfig.svnserve.sshauth"/>。</para>

      <para>像你已经猜测到的，版本库的<filename>svnserve.conf</filename>文件是控制认证和授权政策的中央机构，这文件与其它配置文件格式相同（见<xref linkend="svn.advanced.confarea"/>）：小节名称使用方括号标记（<literal>[</literal>和<literal>]</literal>），注释以井号（<literal>#</literal>）开始，每一小节都有一些参数可以设置（<literal>variable =
        value</literal>），让我们浏览这个文件并且学习怎样使用它们。</para>

      <sect3 id="svn.serverconfig.svnserve.auth.users">
        <title>创建一个用户文件和域</title>

        <para>此时，<filename>svnserve.conf</filename>文件的<literal>[general]</literal>部分包括所有你需要的变量，开始先定义一个保存用户名和密码的文件和一个认证域：</para>

        <screen>
[general]
password-db = userfile
realm = example realm
</screen>

        <para><literal>realm</literal>是你定义的名称，这告诉客户端连接的<quote>认证命名空间</quote>，Subversion会在认证提示里显示，并且作为凭证缓存（见<xref
          linkend="svn.serverconfig.netmodel.credcache"/>。）的关键字（还有服务器的主机名和端口），<literal>password-db</literal>参数指出了保存用户和密码列表文件，这个文件使用同样熟悉的格式，举个例子：</para>

        <screen>
[users]
harry = foopassword
sally = barpassword
</screen>

        <para><literal>password-db</literal>的值可以是用户文件的绝对或相对路径，对许多管理员来说，把文件保存在版本库<filename>conf/</filename>下的<filename>svnserve.conf</filename>旁边是一个简单的方法。另一方面，可能你的多个版本库使用同一个用户文件，此时，这个文件应该在更公开的地方，版本库分享用户文件时必须配置为相同的域，因为用户列表本质上定义了一个认证域，无论这个文件在哪里，必须设置好文件的读写权限，如果你知道运行<command>svnserve</command>的用户，限定这个用户对这个文件有读权限是必须的。</para>

      </sect3>

      <sect3 id="svn.serverconfig.svnserve.auth.general">
        <title>设置访问控制</title>

        <para><filename>svnserve.conf</filename>有两个或多个参数需要设置：它们确定未认证（匿名）和认证用户可以做的事情，参数<literal>anon-access</literal>和<literal>auth-access</literal>可以设置为<literal>none</literal>、<literal>read</literal>或者<literal>write</literal>，设置为<literal>none</literal>会限制所有方式的访问，<literal>read</literal>允许只读访问，而<literal>write</literal>允许对版本库完全的读/写权限：</para>

        <screen>
[general]
password-db = userfile
realm = example realm

# anonymous users can only read the repository
anon-access = read

# authenticated users can both read and write
auth-access = write
</screen>

        <para>实例中的设置实际上是参数的缺省值，你一定不要忘了设置它们，如果你希望更保守一点，你可以完全封锁匿名访问：</para>

        <screen>
[general]
password-db = userfile
realm = example realm

# anonymous users aren't allowed
anon-access = none

# authenticated users can both read and write
auth-access = write
</screen>

        <para>注意<command>svnserve</command>只能识别<quote>整体</quote>的访问控制，一个用户可以有全体的读/写权限，或者只读权限，或没有访问权限，没有对版本库具体路径访问的细节控制，很多项目和站点，这种 访问控制已经完全足够了，然而，如果你希望单个目录访问控制，你会需要使用包括<command>mod_authz_svn</command>（见<xref linkend="svn.serverconfig.httpd.authz.perdir"/>）的Apache，或者是使用<command>pre-commit</command>钩子脚本来控制写访问（见<xref linkend="svn.reposadmin.create.hooks"/>），Subversion的分发版本包含一个<command>commit-access-control.pl</command>和一个更加复杂的<command>svnperms.py</command>脚本可以作为pre-commit脚本使用。</para>

      </sect3>
    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.svnserve.sshauth">
      <title>SSH认证和授权</title>

      <para><command>svnserve</command>的内置认证会非常容易得到，因为它避免了创建真实的系统帐号，另一方面，一些管理员已经创建好了SSH认证框架，在这种情况下，所有的项目用户已经拥有了系统帐号和有能力<quote>SSH到</quote>服务器。</para>

      <para>SSH与<command>svnserve</command>结合很简单，客户端只需要使用<literal>svn+ssh://</literal>的URL模式来连接：</para>

      <screen>
$ whoami
harry

$ svn list svn+ssh://host.example.com/repos/project
harry@host.example.com's password:  *****

foo
bar
baz
&hellip;
</screen>

      <para>在这个例子里，Subversion客户端会调用一个<command>ssh</command>进程，连接到<literal>host.example.com</literal>，使用用户<literal>harry</literal>认证，然后会有一个<command>svnserve</command>私有进程以用户<literal>harry</literal>运行。<command>svnserve</command>是以管道模式调用的（<option>-t</option>），它的网络协议是通过<command>ssh</command><quote>封装的</quote>，被管道代理的<command>svnserve</command>会知道程序是以用户<literal>harry</literal>运行的，如果客户执行一个提交，认证的用户名会作为版本的参数保存到新的修订本。</para>

      <para>这里要理解的最重要的事情是Subversion客户端<emphasis>不</emphasis>是连接到运行中的<command>svnserve</command>守护进程，这种访问方法不需要一个运行的守护进程，也不需要在必要时唤醒一个，它依赖于<command>ssh</command>来发起一个<command>svnserve</command>进程，然后网络断开后终止进程。</para>

      <para>当使用<literal>svn+ssh://</literal>的URL访问版本库时，记住是<command>ssh</command>提示请求认证，而<emphasis>不</emphasis>是<command>svn</command>客户端程序。这意味着密码不会有自动缓存（见<xref linkend="svn.serverconfig.netmodel.credcache"/>），Subversion客户端通常会建立多个版本库的连接，但用户通常会因为密码缓存特性而没有注意到这一点，当使用<literal>svn+ssh://</literal>的URL时，用户会为<command>ssh</command>在每次建立连接时重复的询问密码感到讨厌，解决方案是用一个独立的SSH密码缓存工具，像类Unix系统的<command>ssh-agent</command>或者是Windows下的<command>pageant</command>。</para>

      <para>当在一个管道上运行时，认证通常是基于操作系统对版本库数据库文件的访问控制，这同Harry直接通过<literal>file:///</literal>的URL直接访问版本库非常类似，如果有多个系统用户要直接访问版本库，你会希望将他们放到一个常见的组里，你应该小心的使用umasks。（确定要阅读<xref
        linkend="svn.serverconfig.multimethod"/>）但是即使是在管道模式时，文件<filename>svnserve.conf</filename>还是可以阻止用户访问，如<literal>auth-access = read</literal>或者<literal>auth-access
        = none</literal>。</para>
      
      <para>你会认为SSH管道的故事该结束了，但还不是，Subversion允许你在运行配置文件<filename>config</filename>（见<xref linkend="svn.advanced.confarea"/>）创建一个自定义的管道行为方式，举个例子，假定你希望使用RSH而不是SSH，在<filename>config</filename>文件的<literal>[tunnels]</literal>部分作如下定义：</para>

      <screen>
[tunnels]
rsh = rsh
</screen>

      <para>现在你可以通过指定与定义匹配的URL模式来使用新的管道定义：<literal>svn+rsh://host/path</literal>。当使用新的URL模式时，Subversion客户端实际上会在后台运行<command>rsh host svnserve -t</command>这个命令，如果你在URL中包括一个用户名（例如，<literal>svn+rsh://username@host/path</literal>），客户端也会在自己的命令中包含这部分（<command>rsh
        username@host svnserve -t</command>），但是你可以定义比这个更加智能的新的管道模式：</para>

      <screen>
[tunnels]
joessh = $JOESSH /opt/alternate/ssh -p 29934
</screen>

      <para>这个例子里论证了一些事情，首先，它展现了如何让Subversion客户端启动一个特定的管道程序（这个在<filename>/opt/alternate/ssh</filename>），在这个例子里，使用<literal>svn+joessh://</literal>的URL会以<option>-p 29934</option>参数调用特定的SSH程序&mdash;对连接到非标准端口的程序非常有用。</para>

      <para>第二点，它展示了怎样定义一个自定义的环境变量来覆盖管道程序中的名字，设置<literal>SVN_SSH</literal>环境变量是覆盖缺省的SSH管道的一种简便方法，但是如果你需要为多个服务器做出多个不同的覆盖，或许每一个都联系不同的端口或传递不同的SSH选项，你可以使用本例论述的机制。现在如果我们设置<literal>JOESSH</literal>环境变量，它的值会覆盖管道中的变量值&mdash;会执行<command>$JOESSH</command>而不是<command>/opt/alternate/ssh -p
        29934</command>。</para>

    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.svnserve.sshtricks">
      <title>SSH配置技巧</title>

      <para>不仅仅是可以控制客户端调用<command>ssh</command>方式，也可以控制服务器中的<command>sshd</command>的行为方式，在本小节，我们会展示怎样控制<command>sshd</command>执行<command>svnserve</command>，包括如何让多个用户分享同一个系统帐户。</para>
      
      <sect3 id="svn.serverconfig.svnserve.sshtricks.setup">
        <title>初始设置</title>
        
        <para>作为开始，定位到你启动<command>svnserve</command>的帐号的主目录，确定这个账户已经安装了一套SSH公开/私有密钥对，用户可以通过公开密钥认证，因为所有如下的技巧围绕着使用SSH<filename>authorized_keys</filename>文件，密码认证在这里不会工作。</para>

        <para>如果这个文件还不存在，创建一个<filename>authorized_keys</filename>文件（在UNIX下通常是<filename>~/.ssh/authorized_keys</filename>），这个文件的每一行描述了一个允许连接的公钥，这些行通常是下面的形式：
       </para>

        <screen>
  ssh-dsa AAAABtce9euch.... user@example.com
</screen>
          
        <para>第一个字段描述了密钥的类型，第二个字段是未加密的密钥本身，第三个字段是注释。然而，这是一个很少人知道的事实，可以使用一个<literal>command</literal>来处理整行：</para>

        <screen>
  command="program" ssh-dsa AAAABtce9euch.... user@example.com
</screen>

        <para>当<literal>command</literal>字段设置后，SSH守护进程运行命名的程序而不是通常Subversion客户端询问的<command>svnserve -t</command>。这为实施许多服务器端技巧开启了大门，在下面的例子里，我们简写了文件的这些行：</para>

        <screen>
  command="program" TYPE KEY COMMENT
</screen>

      </sect3>
      
      <sect3 id="svn.serverconfig.svnserve.sshtricks.fixedcmd">
        <title>控制调用的命令</title>

        <para>因为我们可以指定服务器端执行的命令，我们很容易来选择运行一个特定的<command>svnserve</command>程序来并且传递给它额外的参数：</para>
        
        <screen>
  command="/path/to/svnserve -t -r /virtual/root" TYPE KEY COMMENT
</screen>

        <para>在这个例子里，<filename>/path/to/svnserve</filename>也许会是一个<command>svnserve</command>程序的包裹脚本，会来设置umask（见<xref linkend="svn.serverconfig.multimethod"/>）。它也展示了怎样在虚拟根目录定位一个<command>svnserve</command>，就像我们经常在使用守护进程模式下运行<command>svnserve</command>一样。这样做不仅可以把访问限制在系统的一部分，也可以使用户不需要在<literal>svn+ssh://</literal>URL里输入绝对路径。</para>
        
        <para>多个用户也可以共享同一个帐号，作为为每个用户创建系统帐户的替代，我们创建一个公开/私有密钥对，然后在<filename>authorized_users</filename>文件里放置各自的公钥，一个用户一行，使用<option>--tunnel-user</option>选项：</para>

        <screen>
  command="svnserve -t --tunnel-user=harry" TYPE1 KEY1 harry@example.com
  command="svnserve -t --tunnel-user=sally" TYPE2 KEY2 sally@example.com
</screen>

        <para>这个例子允许Harry和Sally通过公钥认证连接同一个的账户，每个人自定义的命令将会执行。<option>--tunnel-user</option>选项告诉<command>svnserve -t</command>命令采用命名的参数作为经过认证的用户，如果没有<option>--tunnel-user</option>，所有的提交会作为共享的系统帐户提交。</para>

        <para>最后要小心：设定通过公钥共享账户进行用户访问时还会允许其它形式的SSH访问，即使你设置了<filename>authorized_keys</filename>的<literal>command</literal>值，举个例子，用户仍然可以通过SSH得到shell访问，或者是通过服务器执行X11或者是端口转发。为了给用户尽可能少的访问权限，你或许希望在<literal>command</literal>命令之后指定一些限制选项：</para>

        <screen>
  command="svnserve -t --tunnel-user=harry",no-port-forwarding,\
           no-agent-forwarding,no-X11-forwarding,no-pty \
           TYPE1 KEY1 harry@example.com
</screen>

      </sect3>

    </sect2>
    
  </sect1>


  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.serverconfig.httpd">
    
    <title>httpd，Apache的HTTP服务器</title>

    <para>Apache的HTTP服务器是一个Subversion可以利用的<quote>重型</quote>网络服务器，通过一个自定义模块，<command>httpd</command>可以让Subversion版本库通过WebDAV/DeltaV协议在客户端前可见，WebDAV/DeltaV协议是HTTP 1.1的扩展（见<ulink url="http://www.webdav.org/"/>来查看详细信息）。这个协议利用了无处不在的HTTP协议是广域网的核心这一点，添加了写能力&mdash;更明确一点，版本化的写&mdash;能力。结果就是这样一个标准化的健壮的系统，作为Apache 2.0软件的一部分打包，被许多操作系统和第三方产品支持，网络管理员也不需要打开另一个自定义端口。  <footnote>
        <para>他们讨厌这样做。</para>
      </footnote>这样一个Apache-Subversion服务器具备了许多<command>svnserve</command>没有的特性，但是也有一点难于配置，灵活通常会带来复杂性。</para>

    <para>下面的讨论包括了对Apache配置指示的引用，给了一些使用这些指示的例子，详细地描述不在本章的范围之内，Apache小组维护了完美的文档，公开存放在他们的站点<ulink url="http://httpd.apache.org"/>。例如，一个一般的配置参考位于<ulink url="
      http://httpd.apache.org/docs-2.0/mod/directives.html"/>。</para>
    
    <para>同样，当你修改你的Apache设置，很有可能会出现一些错误，如果你还不熟悉Apache的日志子系统，你一定需要认识到这一点。在你的文件<filename>httpd.conf</filename>里会指定Apache生成的访问和错误日志（<literal>CustomLog</literal>和<literal>ErrorLog</literal>指示）的磁盘位置。Subversion的mod_dav_svn使用Apache的错误日志接口，你可以浏览这个文件的内容查看信息来查找难于发现的问题根源。</para>
    
    <sidebar>
      <title>为什么是Apache 2？</title>

      <para>如果你系统管理员，很有可能是你已经运行了Apache服务器，并且有一些高级经验。写本文的时候，Apache 1.3是Apache最流行的版本，这个世界因为许多原因而放缓升级到2.X系列：如人们害怕改变，特别是像web服务器这种重要的变化，有些人需要一些在Apache 1.3 API下工作的插件模块，在等待2.X的版本。无论什么原因，许多人会在首次发现Subversion的Apache模块只是为Apache 2 API写的后开始担心。</para>

      <para>对此问题的适当反应是：不需要担心，同时运行Apache 1.3和Apache 2非常简单，只需要安装到不同的位置，用Apache 2作为Subversion的专用服务器，并且不使用80端口，客户端可以访问版本库时在URL里指定端口：</para>

      <screen>
$ svn checkout http://host.example.com:7382/repos/project
&hellip;
</screen>
    </sidebar>

    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.httpd.prereqs">
      <title>必备条件</title>
      
      <para>为了让你的版本库使用HTTP网络，你基本上需要两个包里的四个部分。你需要Apache <command>httpd</command> 2.0和包括的<command>mod_dav</command> DAV模块，Subversion和与之一同分发的<command>mod_dav_svn</command>文件系统提供者模块，如果你有了这些组件，网络化你的版本库将非常简单，如：</para>
      
      <itemizedlist>
        <listitem>
          <para>配置好httpd 2.0，并且使用mod_dav启动，</para>
        </listitem>
        <listitem>
          <para>为mod_dav安装mod_dav_svn插件，它会使用Subversion的库访问版本库，并且</para>
        </listitem>
        <listitem>
          <para>配置你的<filename>httpd.conf</filename>来输出（或者说暴露）版本库。</para>
        </listitem>
      </itemizedlist>
      
      <para>你可以通过从源代码编译<command>httpd</command>和Subversion来完成前两个项目，也可以通过你的系统上的已经编译好的二进制包来安装。最新的使用Apache HTTP的Subversion的编译方法和Apache的配置方式可以看Subversion源代码树根目录的<filename>INSTALL</filename>文件。</para>
      
    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.httpd.basic">
      <title>基本的Apache配置</title>
      
      <para>一旦你安装了必须的组件，剩下的工作就是在<filename>httpd.conf</filename>里配置Apache，使用<literal>LoadModule</literal>来加载mod_dav_svn模块，这个指示必须先与其它Subversion相关的其它配置出现，如果你的Apache使用缺省布局安装，你的<command>mod_dav_svn</command>模块一定在Apache安装目录（通常是在<filename>/usr/local/apache2</filename>）的<filename>modules</filename>子目录，<literal>LoadModule</literal>指示的语法很简单，影射一个名字到它的共享库的物理位置：</para>
    
        <screen>
LoadModule dav_svn_module     modules/mod_dav_svn.so
</screen>

      <para>注意，如果<command>mod_dav</command>是作为共享对象编译（而不是静态链接到<command>httpd</command>程序），你需要为它使用使用<literal>LoadModule</literal>语句，一定确定它在<command>mod_dav_svn</command>之前：</para>

        <screen>
LoadModule dav_module         modules/mod_dav.so
LoadModule dav_svn_module     modules/mod_dav_svn.so
</screen>

    
      <para>在你的配置文件后面的位置，你需要告诉Apache你在什么地方保存Subversion版本库（也许是多个），<literal>位置</literal>指示有一个很像XML的符号，开始于一个开始标签，以一个结束标签结束，配合中间许多的其它配置。<literal>Location</literal>指示的目的是告诉Apache在特定的URL以及子URL下需要特殊的处理，如果是为Subversion准备的，你希望可以通过告诉Apache特定URL是指向版本化的资源，从而把支持转交给DAV层，你可以告诉Apache将所有路径部分（URL中服务器名称和端口之后的部分）以<filename>/repos/</filename>开头的URL交由DAV服务提供者处理。一个DAV服务提供者的版本库位于<filename>/absolute/path/to/repository</filename>，可以使用如下的<filename>httpd.conf</filename>语法：</para>
                
        <screen>
&lt;Location /repos&gt;
  DAV svn
  SVNPath /absolute/path/to/repository
&lt;/Location&gt;
</screen>
            
      <para>如果你计划支持多个具备相同父目录的Subversion版本库，你有另外的选择，<literal>SVNParentPath</literal>指示，来表示共同的父目录。举个例子，如果你知道你会在<filename>/usr/local/svn</filename>下创建多个Subversion版本库，并且通过类似<systemitem
        class="url">http://my.server.com/svn/repos1</systemitem>，<systemitem
        class="url">http://my.server.com/svn/repos2</systemitem>的URL访问，你可以用后面例子中的<filename>httpd.conf</filename>配置语法：</para>
              
        <screen>
&lt;Location /svn&gt;
  DAV svn

  # any "/svn/foo" URL will map to a repository /usr/local/svn/foo
  SVNParentPath /usr/local/svn
&lt;/Location&gt;
</screen>
            
      <para>使用上面的语法，Apache会代理所有URL路径部分为<filename>/svn/</filename>的请求到Subversion的DAV提供者，Subversion会认为<literal>SVNParentPath</literal>指定的目录下的所有项目是真实的Subversion版本库，这通常是一个便利的语法，不像是用<literal>SVNPath</literal>指示，我们在此不必为创建新的版本库而重启Apache。</para>      

      <para>请确定当你定义新的<literal>位置</literal>，不会与其它输出的位置重叠，例如你的主要<literal>DocumentRoot</literal>是<filename>/www</filename>，不要把Subversion版本库输出到<literal>&lt;Location
        /www/repos&gt;</literal>，如果一个请求的URI是<filename>/www/repos/foo.c</filename>，Apache不知道是直接到<filename>repos/foo.c</filename>访问这个文件还是让<command>mod_dav_svn</command>代理从Subversion版本库返回<filename>foo.c</filename>。</para>

      <sidebar>
        <title>服务器名称和拷贝请求</title>
        
        <para>Subversion利用<literal>COPY</literal>请求类型来执行服务器端的文件和目录拷贝，作为一个健全的Apache模块的一部分，拷贝源和拷贝的目标通常坐落在同一个机器上，为了满足这个需求，你或许需要告诉mod_dav服务器主机的名称，通常你可以使用<filename>httpd.conf</filename>的<literal>ServerName</literal>指示来完成此目的。</para>
        
        <screen>
ServerName svn.example.com
</screen>
            
        <para>如果你通过<literal>NameVirtualHost</literal>指示使用Apache的虚拟主机，你或许需要<literal>ServerAlias</literal>指示来指定额外的名称，再说一次，可以查看Apache文档的来得到更多细节。</para>
      </sidebar>

      <para>在本阶段，你一定要考虑访问权限问题，如果你已经作为普通的web服务器运行过Apache，你一定有了一些内容&mdash;网页、脚本和其他。这些项目已经配置了许多在Apache下可以工作的访问许可，或者更准确一点，允许Apache与这些文件一起工作。Apache当作为Subversion服务器运行时，同样需要正确的访问许可来读写你的Subversion版本库。（见<xref linkend="svn.serverconfig.svnserve.invoking.sb-1"/>。）</para>
    
      <para>你会需要检验权限系统的设置满足Subversion的需求，同时不会把以前的页面和脚本搞乱。这或许意味着修改Subversion的访问许可来配合Apache服务器已经使用的工具，或者可能意味着需要使用<filename>httpd.conf</filename>的<literal>User</literal>和<literal>Group</literal>指示来指定Apache作为运行的用户和Subversion版本库的组。并不是只有一条正确的方式来设置许可，每个管理员都有不同的原因来以特定的方式操作，只需要意识到许可关联的问题经常在为Apache配置Subversion版本库的过程中被疏忽。</para>

    </sect2>

    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.httpd.authn">
      <title>认证选项</title>

      <para>此时，如果你配置的<filename>httpd.conf</filename>保存如下的内容</para>

      <screen>
&lt;Location /svn&gt;
  DAV svn
  SVNParentPath /usr/local/svn
&lt;/Location&gt;
</screen>

      <para>这样你的版本库对全世界是可以<quote>匿名</quote>访问的，直到你配置了一些认证授权政策，你通过<literal>Location</literal>指示来使Subversion版本库可以被任何人访问，换句话说，</para>
      
      <itemizedlist>
        <listitem>
          <para>任何人可以使用Subversion客户端来从版本库URL取出一个工作拷贝（或者是它的子目录），</para>
        </listitem>
        <listitem>
          <para>任何人可以在浏览器输入版本库URL交互浏览的方式来查看版本库的最新修订版本，并且</para>
        </listitem>
        <listitem>
          <para>任何人可以提交到版本库。</para>
        </listitem>
      </itemizedlist>

      <sect3 id="svn.serverconfig.httpd.authn.basic">
        <title>基本HTTP认证</title>
        
        <para>最简单的客户端认证方式是通过HTTP基本认证机制，简单的使用用户名和密码来验证一个用户所自称的身份，Apache提供了一个<command>htpasswd</command>工具来管理可接受的用户名和密码，这些就是你希望赋予Subversion特别权限的用户，让我们给Sally和Harry赋予提交权限，首先，我们需要添加他们到密码文件。</para>
    
        <screen>
$ ### First time: use -c to create the file
$ ### Use -m to use MD5 encryption of the password, which is more secure
$ htpasswd -cm /etc/svn-auth-file harry
New password: ***** 
Re-type new password: *****
Adding password for user harry
$ htpasswd -m /etc/svn-auth-file sally
New password: *******
Re-type new password: *******
Adding password for user sally
$
</screen>

        <para>下一步，你需要在<filename>httpd.conf</filename>的<literal>Location</literal>区里添加一些指示来告诉Apache如何来使用这些密码文件，<literal>AuthType</literal>指示指定系统使用的认证类型，这种情况下，我们需要指定<literal>Basic</literal>认证系统，<literal>AuthName</literal>是你提供给认证域一个任意名称，大多数浏览器会在向用户询问名称和密码的弹出窗口里显示这个名称，最终，使用<literal>AuthUserFile</literal>指示来指定使用<command>htpasswd</command>创建的密码文件的位置。</para>
    
        <para>添加完这三个指示，你的<literal>&lt;Location&gt;</literal>区块一定像这个样子：</para>
    
        <screen>
&lt;Location /svn&gt;
  DAV svn
  SVNParentPath /usr/local/svn
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /etc/svn-auth-file
&lt;/Location&gt;
</screen>

        <para>这个<literal>&lt;Location&gt;</literal>区块还没有结束，还不能做任何有用的事情，它只是告诉Apache当需要授权时，要去向Subversion客户端索要用户名和密码。我们这里遗漏的，是一些告诉Apache<emphasis>什么样</emphasis>客户端需要授权的指示。哪里需要授权，Apache就会在哪里要求认证，最简单的方式是保护所有的请求，添加<literal>Require valid-user</literal>来告诉Apache任何请求需要认证的用户：</para>

        <screen>
&lt;Location /svn&gt;
  DAV svn
  SVNParentPath /usr/local/svn
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /etc/svn-auth-file
  Require valid-user
&lt;/Location&gt;
</screen>

        <para>一定要阅读后面的部分（<xref
          linkend="svn.serverconfig.httpd.authz"/>）来得到<literal>Require</literal>的细节，和授权政策的其他设置方法。</para>

        <para>需要警惕：HTTP基本认证的密码是用明文传输，因此非常不可靠的，如果你担心密码偷窥，最好是使用某种SSL加密，所以客户端认证使用<literal>https://</literal>而不是<literal>http://</literal>，为了方便，你可以配置Apache为自签名认证。
          <footnote>
            <para>当使用自签名的服务器时仍会遭受<quote>中间人</quote>攻击，但是与偷取未保护的密码相比，这样的攻击比一个偶然的获取要艰难许多。</para>
          </footnote>
          参考Apache的文档（和OpenSSL文档）来查看怎样做。</para>

      </sect3>


      <sect3 id="svn.serverconfig.httpd.authn.sslcerts">
        <title>SSL证书管理</title>
        
        <para>商业应用需要越过公司防火墙的版本库访问，防火墙需要小心的考虑非认证用户<quote>吸取</quote>他们的网络流量的情况，SSL让那种形式的关注更不容易导致敏感数据泄露。</para>

        <para>如果Subversion使用OpenSSL编译，它就会具备与Subversion服务器使用<literal>https://</literal>的URL通讯的能力，Subversion客户端使用的Neon库不仅仅可以用来验证服务器证书，也可以必要时提供客户端证书，如果客户端和服务器交换了SSL证书并且成功地互相认证，所有剩下的交流都会通过一个会话关键字加密。</para>

        <para>怎样产生客户端和服务器端证书以及怎样使用它们已经超出了本书的范围，许多书籍，包括Apache自己的文档，描述这个任务，现在我们<emphasis>可以</emphasis>覆盖的是普通的客户端怎样来管理服务器与客户端证书。</para>

        <para>当通过<literal>https://</literal>与Apache通讯时，一个Subversion客户端可以接收两种类型的信息：</para>

        <itemizedlist>
          <listitem><para>一个服务器证书</para></listitem>
          <listitem><para>一个客户端证书的要求</para></listitem>
        </itemizedlist>

        <para>如果客户端接收了一个服务器证书，它需要去验证它是可以相信的：这个服务器是它自称的那一个吗？OpenSSL库会去检验服务器证书的签名人或者是<firstterm>核证机构</firstterm>（CA）。如果OpenSSL不可以自动信任这个CA，或者是一些其他的问题（如证书过期或者是主机名不匹配），Subversion命令行客户端会询问你是否愿意仍然信任这个证书：</para>

        <screen>
$ svn list https://host.example.com/repos/project

Error validating server certificate for 'https://host.example.com:443':
 - The certificate is not issued by a trusted authority. Use the
   fingerprint to validate the certificate manually!
Certificate information:
 - Hostname: host.example.com
 - Valid: from Jan 30 19:23:56 2004 GMT until Jan 30 19:23:56 2006 GMT
 - Issuer: CA, example.com, Sometown, California, US
 - Fingerprint: 7d:e1:a9:34:33:39:ba:6a:e9:a5:c4:22:98:7b:76:5c:92:a0:9c:7b

(R)eject, accept (t)emporarily or accept (p)ermanently?
</screen>

        <para>这个对话看起来很熟悉，这是你会在web浏览器（另一种HTTP客户端，就像Subversion）经常看到的问题，如果你选择(p)ermanent选项，服务器证书会存放在你存放那个用户名和密码缓存（见<xref
          linkend="svn.serverconfig.netmodel.credcache"/>。）的私有运行区<filename>auth/</filename>中，缓存后，Subversion会自动记住在以后的交流中信任这个证书。</para>

        <para>你的运行中<filename>servers</filename>文件也会给你能力可以让Subversion客户端自动信任特定的CA，包括全局的或是每主机为基础的，只需要设置<literal>ssl-authority-files</literal>为一组逗号隔开的PEM加密的CA证书列表：</para>

        <screen>
[global]
ssl-authority-files = /path/to/CAcert1.pem;/path/to/CAcert2.pem
</screen>
        
        <para>许多OpenSSL安装包括一些预先定义好的可以普遍信任的<quote>缺省的</quote>CA，为了让Subversion客户端自动信任这些标准权威，设置<literal>ssl-trust-default-ca</literal>为<literal>true</literal>。</para>

        <para>当与Apache通话时，Subversion客户端也会收到一个证书的要求，Apache是询问客户端来证明自己的身份：这个客户端是否是他所说的那一个？如果一切正常，Subversion客户端会发送回一个通过Apache信任的CA签名的私有证书，一个客户端证书通常会以加密方式存放在磁盘，使用本地密码保护，当Subversion收到这个要求，它会询问你证书的路径和保护用的密码：</para>

        <screen>
$ svn list https://host.example.com/repos/project

Authentication realm: https://host.example.com:443
Client certificate filename: /path/to/my/cert.p12
Passphrase for '/path/to/my/cert.p12':  ********
&hellip;
</screen>

        <para>注意这个客户端证书是一个<quote>p12</quote>文件，为了让Subversion使用客户端证书，它必须是运输标准的PKCS#12格式，大多数浏览器可以导入和导出这种格式的证书，另一个选择是用OpenSSL命令行工具来转化存在的证书为PKCS#12格式。</para>

        <para>再次，运行中<filename>servers</filename>文件允许你为每个主机自动响应这种要求，单个或两条信息可以用运行参数来描述：</para>

        <screen>
[groups]
examplehost = host.example.com

[examplehost]
ssl-client-cert-file = /path/to/my/cert.p12
ssl-client-cert-password = somepassword
</screen>

        <para>一旦你设置了<literal>ssl-client-cert-file</literal>和
          <literal>ssl-client-cert-password</literal>参数，Subversion客户端可以自动响应客户端证书请求而不会打扰你。
          <footnote>
            <para>更多有安全意识的人不会希望在运行中<filename>servers</filename>文件保存客户端证书密码。</para>
          </footnote>
        </para>

      </sect3>

    </sect2>
    
    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.httpd.authz">
      <title>授权选项</title>

      <para>此刻，你已经配置了认证，但是没有配置授权，Apache可以要求用户认证并且确定身份，但是并没有说明这个身份的怎样允许和限制，这个部分描述了两种控制访问版本库的策略。</para>

      <sect3 id="svn.serverconfig.httpd.authz.blanket">
        <title>整体访问控制</title>

        <para>最简单的访问控制形式是授权特定用户为只读版本库访问或者是读/写访问版本库。</para>

        <para>你可以通过在<literal>&lt;Location&gt;</literal>区块添加<literal>Require valid-user</literal>指示来限制所有的版本库操作，使用我们前面的例子，这意味着只有客户端只可以是<literal>harry</literal>或者<literal>sally</literal>，而且他们必须提供正确的用户名及对应密码，这样允许对Subversion版本库做任何事：</para>
    
        <screen>
&lt;Location /svn&gt;
  DAV svn
  SVNParentPath /usr/local/svn

  # how to authenticate a user
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /path/to/users/file
  
  # only authenticated users may access the repository
  Require valid-user
&lt;/Location&gt;
</screen>

        <para>有时候，你不需要这样严密，举个例子，Subversion自己在<ulink url="http://svn.collab.net/repos/svn"/>的源代码允许全世界的人执行版本库的只读操作（例如检出我们的工作拷贝和使用浏览器浏览版本库），但是限定只有认证用户可以执行写操作。为了执行特定的限制，你可以使用<literal>Limit</literal>和<literal>LimitExcept</literal>配置指示，就像<literal>Location</literal>指示，这个区块有开始和结束标签，你需要在<literal>&lt;Location&gt;</literal>中添加这个指示。</para>
  
        <para>在<literal>Limit</literal>和<literal>LimitExcept</literal>中使用的参数是可以被这个区块影响的HTTP请求类型，举个例子，如果你希望禁止所有的版本库访问，只是保留当前支持的只读操作，你可以使用<literal>LimitExcept</literal>指示，并且使用<literal>GET</literal>，<literal>PROPFIND</literal>，<literal>OPTIONS</literal>和<literal>REPORT</literal>请求类型参数，然后前面提到过的<literal>Require valid-user</literal>指示将会在<literal>&lt;LimitExcept&gt;</literal>区块中而不是在<literal>&lt;Location&gt;</literal>区块。</para>
    
        <screen>
&lt;Location /svn&gt;
  DAV svn
  SVNParentPath /usr/local/svn

  # how to authenticate a user
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /path/to/users/file

  # For any operations other than these, require an authenticated user.
  &lt;LimitExcept GET PROPFIND OPTIONS REPORT&gt;
    Require valid-user
  &lt;/LimitExcept&gt;
&lt;/Location&gt;
</screen>

        <para>这里只是一些简单的例子，想看关于Apache访问控制<literal>Require</literal>指示的更深入信息，可以查看Apache文档中的教程集<ulink
           url="http://httpd.apache.org/docs-2.0/misc/tutorials.html"/>中的<literal>Security</literal>部分。</para>
              

      </sect3>

      <sect3 id="svn.serverconfig.httpd.authz.perdir">
        <title>每目录访问控制</title>

        <para>也可以使用Apache的httpd模块<command>mod_authz_svn</command>更加细致的设置访问权限，这个模块收集客户端传递过来的不同的晦涩的URL信息，询问<command>mod_dav_svn</command>来解码，然后根据在配置文件定义的访问政策来裁决请求。</para>

        <para>如果你从源代码创建Subversion，<command>mod_authz_svn</command>会自动附加到<command>mod_dav_svn</command>，许多二进制分发版本也会自动安装，为了验证它是安装正确，确定它是在<filename>httpd.conf</filename>的<literal>LoadModule</literal>指示中的<command>mod_dav_svn</command>后面：</para>

        <screen>
LoadModule dav_module         modules/mod_dav.so
LoadModule dav_svn_module     modules/mod_dav_svn.so
LoadModule authz_svn_module   modules/mod_authz_svn.so
</screen>

        <para>为了激活这个模块，你需要配置你的<literal>Location</literal>区块的<literal>AuthzSVNAccessFile</literal>指示，指定保存路径中的版本库访问政策的文件。（一会儿我们将会讨论这个文件的格式。）</para>

        <para>Apache非常的灵活，你可以从三种模式里选择一种来配置你的区块，作为开始，你选择一种基本的配置模式。（下面的例子非常简单；见Apache自己的文档中的认证和授权选项来查看更多的细节。）</para>

        <para>最简单的区块是允许任何人可以访问，在这个场景里，Apache决不会发送认证请求，所有的用户作为<quote>匿名</quote>对待。</para>

        <example id="svn.serverconfig.httpd.authz.perdir.ex-1">
          <title>匿名访问的配置实例。</title>
          <programlisting>
&lt;Location /repos&gt;
  DAV svn
  SVNParentPath /usr/local/svn

  # our access control policy
  AuthzSVNAccessFile /path/to/access/file                 
&lt;/Location&gt;
          </programlisting>
        </example>

        <para>在另一个极端，你可以配置为拒绝所有人的认证，所有客户端必须提供证明自己身份的证书，你通过<literal>Require valid-user</literal>指示来阻止无条件的认证，并且定义一种认证的手段。</para>

        <example id="svn.serverconfig.httpd.authz.perdir.ex-2">
          <title>一个认证访问的配置实例。</title>
          <programlisting>
&lt;Location /repos&gt;
  DAV svn
  SVNParentPath /usr/local/svn
            
  # our access control policy
  AuthzSVNAccessFile /path/to/access/file                 
            
  # only authenticated users may access the repository
  Require valid-user
            
  # how to authenticate a user
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /path/to/users/file                  
&lt;/Location&gt;
          </programlisting>
        </example>

        <para>第三种流行的模式是允许认证和匿名用户的组合，举个例子，许多管理员希望允许匿名用户读取特定的版本库路径，但希望只有认证用户可以读（或者写）更多敏感的区域，在这个设置里，所有的用户开始时用匿名用户访问版本库，如果你的访问控制策略在任何时候要求一个真实的用户名，Apache将会要求认证客户端，为­¤，你可以同时使用<literal>Satisfy Any</literal>和<literal>Require valid-user</literal>指示。</para>

        <example id="svn.serverconfig.httpd.authz.perdir.ex-3">
          <title>一个混合认证/匿名访问的配置实例。</title>
          <programlisting>
&lt;Location /repos&gt;
  DAV svn
  SVNParentPath /usr/local/svn
            
  # our access control policy
  AuthzSVNAccessFile /path/to/access/file                 
            
  # try anonymous access first, resort to real 
  # authentication if necessary.
  Satisfy Any
  Require valid-user
            
  # how to authenticate a user
  AuthType Basic
  AuthName "Subversion repository"
  AuthUserFile /path/to/users/file                  
&lt;/Location&gt;
          </programlisting>
        </example>
        
        <para>一旦你的基本<literal>Location</literal>区块已经配置了，你可以创建一个定义一些授权规则的访问文件。</para>

        <para>访问文件的语法与<command>svnserve.conf</command>和运行中配置文件非常相似，以（<literal>#</literal>）开头的行会被忽略，在它的简单形式里，每一小节命名一个版本库和一个里面的路径，认证用户名是在每个小节中的选项名，每个选项的值描述了用户访问版本库的级别：<literal>r</literal>（只读）或者<literal>rw</literal>（读写），如果用户没有提到，访问是不允许的。</para>

        <para>具体一点：这个小节的名称是<literal>[repos-name:path]</literal>或者<literal>[path]</literal>的形式，如果你使用<literal>SVNParentPath</literal>指示，指定版本库的名字是很重要的，如果你漏掉了他们，<literal>[/some/dir]</literal>部分就会与<filename>/some/dir</filename>的所有版本库匹配，如果你使用<literal>SVNPath</literal>指示，因此在你的小节中只是定义路径也很好&mdash;毕竟只有一个版本库。</para>
          
        <screen>
[calc:/branches/calc/bug-142]
harry = rw
sally = r
</screen>

        <para>在第一个例子里，用户<literal>harry</literal>对<literal>calc</literal>版本库中<filename>/branches/calc/bug-142</filename>具备完全的读写权利，但是用户<literal>sally</literal>只有读权利，任何其他用户禁止访问这个目录。</para>

        <para>当然，访问控制是父目录传递给子目录的，这意味着我们可以为Sally指定一个子目录的不同访问策略：</para>

        <screen>
[calc:/branches/calc/bug-142]
harry = rw
sally = r

# give sally write access only to the 'testing' subdir
[calc:/branches/calc/bug-142/testing]
sally = rw
</screen>

        <para>现在Sally可以读取分支的<filename>testing</filename>子目录，但对其他部分还是只可以读，同时，Harry对整个分支还继续有完全的读写权限。</para>

        <para>也可以通过继承规则明确的的拒绝某人的访问，只需要设置用户名参数为空：</para>

        <screen>
[calc:/branches/calc/bug-142]
harry = rw
sally = r

[calc:/branches/calc/bug-142/secret]
harry =
</screen>
        
        <para>在这个例子里，Harry对<filename>bug-142</filename>目录树有完全的读写权限，但是对<filename>secret</filename>子目录没有任何访问权利。</para>

        <para>有一件事需要记住的是需要找到最匹配的目录，<command>mod_authz_svn</command>模块首先找到匹配自己的目录，然后父目录，然后父目录的父目录，就这样继续下去，更具体的路径控制会覆盖所有继承下来的访问控制。</para>

        <para>缺省情况下，没有人对版本库有任何访问，这意味着如果你已经从一个空文件开始，你会希望给所有用户对版本库根目录具备读权限，你可以使用<literal>*</literal>实现，用来代表<quote>所有用户</quote>：</para>

        <screen>
[/]
* = r
</screen>

        <para>这是一个普通的设置；注意在小节名中没有提到版本库名称，这让所有版本库对所有的用户可读，不管你是使用<literal>SVNPath</literal>或是<literal>SVNParentPath</literal>。当所有用户对版本库有了读权利，你可以赋予特定用户对特定子目录的<literal>rw</literal>权限。</para>

        <para>星号（<literal>*</literal>）参数需要在这里详细强调：这是匹配匿名用户的<emphasis>唯一</emphasis>模式，如果你已经配置了你的<literal>Location</literal>区块允许匿名和认证用户的混合访问，所有用户作为Apache匿名用户开始访问，<command>mod_authz_svn</command>会在要访问路径的定义中查找<literal>*</literal>值；如果找不到，Apache就会要求真实的客户端认证。</para>

        <para>访问文件也允许你定义一组的用户，很像Unix的<filename>/etc/group</filename>文件：</para>

        <screen>
[groups]
calc-developers = harry, sally, joe
paint-developers = frank, sally, jane
everyone = harry, sally, joe, frank, sally, jane
</screen>

        <para>组可以被赋予通用户一样的访问权限，使用<quote>at</quote>（<literal>@</literal>）前缀来加以区别：</para>

        <screen>
[calc:/projects/calc]
@calc-developers = rw

[paint:/projects/paint]
@paint-developers = rw
jane = r 
</screen>

        <para>组中也可以定义为包含其它的组：</para>

        <screen>
[groups]
calc-developers = harry, sally, joe
paint-developers = frank, sally, jane
everyone = @calc-developers, @paint-developers
</screen>

        <para>...并且非常接近。</para>

      </sect3>

      <sect3 id="svn.serverconfig.httpd.authz.pathauthzoff">
        <title>关闭路径为基础的检查</title>

        <para><command>mod_dav_svn</command>模块做了许多工作来确定你标记为<quote>不可读</quote>的数据不会因意外而泄露，这意味着需要紧密监控通过<command>svn
          checkout</command>或是<command>svn update</command>返回的路径和文件内容，如果这些命令遇到一些根据认证策略不是可读的路径，这个路径通常会被一起忽略，在历史或者重命名操作时&mdash;例如运行一个类似<command>svn cat -r OLD foo.c</command>的命令来操作一个很久以前改过名字的文件 &mdash; 如果一个对象的以前的名字检测到是只读的，重命令追踪就会终止。</para>

        <para>所有的路径检查在有时会非常昂贵，特别是<command>svn
          log</command>的情况。当检索一列修订版本时，服务器会查看所有修订版本修改的路径，并且检查可读性，如果发现了一个不可读路径，它会从修订版本的修改路径中忽略（可以查看<option>--verbose</option>选项），并且整个的日志信息会被禁止，不必多说，这种影响大量文件修订版本的操作会非常耗时。这是安全的代价：即使你并没有配置<command>mod_authz_svn</command>模块，<command>mod_dav_svn</command>还是会询问<command>httpd</command>来对所有路径运行认证检查，<command>mod_dav_svn</command>模块没有办法知道那个认证模块被安装，所以只有询问Apache来调用所提供的模块。</para>

        <para>在另一方面，也有一个安全舱门允许你用安全特性来交换速度，如果你不是坚持要求有每目录授权（如不使用 <command>mod_authz_svn</command>和类似的模块），你就可以关闭所有的路径检查，在你的<filename>httpd.conf</filename>文件，使用<literal>SVNPathAuthz</literal>指示：</para>

        <example id="svn.serverconfig.httpd.authz.pathauthzoff.ex-1">
          <title>关闭所有的路经检查</title>
          <programlisting>
&lt;Location /repos&gt;
  DAV svn
  SVNParentPath /usr/local/svn
            
  SVNPathAuthz off
&lt;/Location&gt;            
          </programlisting>
        </example>

        <para><literal>SVNPathAuthz</literal>指示缺省是<quote>on</quote>，当设置为<quote>off</quote>时，所有的路径为基础的授权都会关闭；<command>mod_dav_svn</command>停止对每个目录调用授权检查。</para>

      </sect3>

    </sect2>
    
    <!-- =============================================================== -->
    <sect2 id="svn.serverconfig.httpd.extra">
      <title>额外的糖果</title>

      <para>我们已经覆盖了关于认证和授权的Apache和mod_dav_svn的大多数选项，但是Apache还提供了许多很好的特性。</para>

      <sect3 id="svn.serverconfig.httpd.extra.browsing">
        <title>版本库浏览</title>
        
        <para>一个非常有用的好处是使用Apache/WebDAV配置Subversion版本库时可以用普通的浏览器察看最新的版本库文件，因为Subversion使用URL来鉴别版本库版本化的资源，版本库使用的HTTP为基础的URL也可以直接输入到Web浏览器中，你的浏览器会发送一个<literal>GET</literal>请求到URL，根据访问的URL是指向一个版本化的目录还是文件，mod_dav_svn会负责列出目录列表或者是文件内容。</para>

        <para>因为URL不能确定你所希望看到的资源的版本，mod_dav_svn会一直返回最新的版本，这样会有一些美妙的副作用，你可以直接把Subversion的URL传递给文档作为引用，这些URL会一直指向文档最新的材料，当然，你也可以在别的网站作为超链使用这些URL。</para>

        <para>你通常会在版本化的文件的URL之外得到更多地用处&mdash;毕竟那里是有趣的内容存在的地方，但是你会偶尔浏览一个Subversion的目录列表，你会很快发现展示列表生成的HTML非常基本，并且一定没有在外观上（或者是有趣上）下功夫，为了自定义这些目录显示，Subversion提供了一个XML目录特性，一个单独的<literal>SVNIndexXSLT</literal>指示在你的<filename>httpd.conf</filename>文件版本库的<literal>Location</literal>块里，它将会指导mod_dav_svn在显示目录列表的时候生成XML输出，并且引用你选择的XSLT样式表文件：</para>
 
        <screen>
&lt;Location /svn&gt;
  DAV svn
  SVNParentPath /usr/local/svn
  SVNIndexXSLT "/svnindex.xsl"
  &hellip;
&lt;/Location&gt;
</screen>

        <para>使用<literal>SVNIndexXSLT</literal>指示和创建一个XSLT样式表，你可以让你的目录列表的颜色模式与你的网站的其它部分匹配，否则，如果你愿意，你可以使用Subversion源分发版本中的<filename>tools/xslt/</filename>目录下的样例样式表。记住提供给<literal>SVNIndexXSLT</literal> 指示的路径是一个URL路径&mdash;浏览器需要阅读你的样式表来利用它们！</para>

        <sidebar>
          <title>我可以看到老的修订版本吗？</title>

          <para>通过一个普通的浏览器？一句话：不可以，至少是当你只使用<command>mod_dav_svn</command>作为唯一的工具时。</para>

          <para>你的Web浏览器只会说普通的HTTP，也就是说它只会GET公共的URL，这个URL代表了最新版本的文件和目录，根据WebDAV/DeltaV规范，每种服务器定义了一种私有的URL语法来代表老的资源的版本，这个语法对客户端是不透明的，为了得到老的版本，一个客户端必须通过一种规范过程来<quote>发现</quote>正确的URL；这个过程包括执行一系列WebDAV PROPFIND请求和理解DeltaV概念，这些事情一般是你的web浏览器做不了的。</para>

          <para>为了回答这些问题，一个明显的看老版本文件和目录的方式是带<option>--revision</option>参数的<command>svn
            list</command>和<command>svn cat</command>命令，为了在浏览器里察看老版本，你可以使用第三方的软件，一个好的例子是ViewVC（<ulink url="http://viewvc.tigris.org/"/>），ViewVC最初写出来是为了在web显示CVS版本库，<footnote>
              <para>之前叫做<quote>ViewCVS</quote>。</para>
            </footnote>最新的带血的（此时正在编写）版本也已经可以理解Subversion版本库了。</para>
        </sidebar>

      </sect3>

      <sect3 id="svn.serverconfig.httpd.extra.other">
        <title>其它特性</title>
        
        <para>Apache作为一个健壮的Web服务器的许多特性也可以用来增加Subversion的功能性和安全性，Subversion使用Neon与Apache通讯，这是一种一般的HTTP/WebDAV库，可以支持SSL和Deflate压缩（是<command>gzip</command>和<command>PKZIP</command>程序用来<quote>压缩</quote>文件为数据块的一样的算法）之类的机制。你只需要编译你希望Subversion和Apache需要的特性，并且正确的配置程序来使用这些特性。</para>
    
        <para>Deflate压缩给服务器和客户端带来了更多地负担，压缩和解压缩减少了网络传输的实际文件的大小，如果网络带宽比较紧缺，这种方法会大大提高服务器和客户端之间发送数据的速度，在极端情况下，这种最小化的传输会造成超时和成功的区别。</para>
  
        <para>不怎么有趣，但同样重要，是Apache和Subversion关系的一些特性，像可以指定自定义的端口（而不是缺省的HTTP的80）或者是一个Subversion可以被访问的虚拟主机名，或者是通过代理服务器访问的能力，这些特性都是Neon所支持的，所以Subversion轻易得到这些支持。</para>

        <para>最后，因为<command>mod_dav_svn</command>是使用一个半完成的WebDAV/DeltaV方言，所以通过第三方的DAV客户端访问也是可能的，几乎所有的现代操作系统（Win32、OS X和Linux）都有把DAV服务器影射为普通的网络<quote>共享</quote>的内置能力，这是一个复杂的主题；察看<xref
          linkend="svn.webdav"/>来得到更多细节。</para>

        
      </sect3>

    </sect2>

  </sect1>


  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <!-- ================================================================= -->
  <sect1 id="svn.serverconfig.multimethod">
    
    <title>支持多种版本库访问方法</title>

    <para>你已经看到了一个版本库可以用多种方式访问，但是可以&mdash;或者说安全的&mdash;用几种方式同时并行的访问你的版本库吗？回答是可以，倘若你有一些深谋远虑的使用。</para>
    
    <para>在任何给定的时间，这些进程会要求读或者写访问你的版本库：</para>
    
    <itemizedlist>
      <listitem>
        <para>常规的系统用户使用Subversion客户端（客户端程序本身）通过<literal>file:///</literal>URL直接访问版本库；</para>
      </listitem>
      <listitem>
        <para>常规的系统用户连接使用SSH调用的访问版本库的<command>svnserve</command>进程（以它们自己运行）；</para>
      </listitem>
      <listitem>
        <para>一个<command>svnserve</command>进程&mdash;是一个守护进程或是通过<command>inetd</command>启动的&mdash;作为一个固定的用户运行；</para>
      </listitem>
      <listitem>
        <para>一个Apache <command>httpd</command>进程，以一个固定用户运行。</para>
      </listitem>
    </itemizedlist>
    
    <para>最通常的一个问题是管理进入到版本库的所有权和访问许可，是前面例子的所有进程 （或者说是用户）都有读写Berkeley DB的权限？假定你有一个类Unix的操作系统，一个直接的办法是在新的<literal>svn</literal>组添加所有潜在的用户，然后让这个组完全拥有版本库，但这样还不足够，因为一个进程会使用不友好的umask来写数据库文件&mdash;用来防止别的用户的访问。</para>
    
    <para>所以下一步我们不选择为每个版本库用户设置一个共同的组的方法，而是强制每个版本库访问进程使用一个健全的umask。对直接访问版本库的用户，你可以使用<command>svn</command>的包裹脚本来首先设置<command>umask 002</command>，然后运行真实的<command>svn</command>客户端程序，你可以为<command>svnserve</command>写相同的脚本，并且增加<command>umask
      002</command>命令到Apache自己的启动脚本<filename>apachectl</filename>中。例如：</para>

    <screen>
$ cat /usr/bin/svn

#!/bin/sh

umask 002
/usr/bin/svn-real "$@"

</screen>

    <para>另一个在类Unix系统下常见的问题是，当版本库在使用时，BerkeleyDB有时候创建一个新的日志文件来记录它的东西，即使这个版本库是完全由<command>svn</command>组拥有，这个新创建的文件不是必须被同一个组拥有，这给你的用户造成了更多地许可问题。一个好的工作区应该设置组的SUID字节到版本库的<filename>db</filename>目录，这会导致所有新创建的日志文件拥有同父目录相同的组拥有者。</para>

    <para>一旦你跳过了这些障碍，你的版本库一定是可以通过各种可能的手段访问了，这看起来有点凌乱和复杂，但是这个让多个用户分享对一个文件的写权限的问题是一个经典问题，并且经常是没有优雅的解决。</para>
    
    <para>幸运的是，大多数版本库管理员不<emphasis>需要</emphasis>这样复杂的配置，用户如果希望访问本机的版本库，并不是一定要通过<literal>file://</literal>的URL&mdash;他们可以用<literal>localhost</literal>机器名联系Apache的HTTP服务器或者是<command>svnserve</command>，协议分别是<literal>http://</literal>或<literal>svn://</literal>。为你的Subversion版本库维护多个服务器进程，版本库会变得超出需要的头痛，我们建议你选择最符合你的需要的版本库，并且坚持使用！</para>

    <sidebar>
      <title>svn+ssh://服务器检查列表</title>

      <para>让一些用户通过存在的SSH帐户来共享版本库而没有访问许可问题是一件很有技巧的事情，如果你为自己需要在（作为一个管理员）类Unix系统上做的事情感到迷惑，这里是一些快速的检查列表，总结了本小节讨论的事情：</para>

      <itemizedlist>
        <listitem>
          <para>所有的SSH用户需要能够读写版本库，把所有的SSH用户放到同一个组里，让版本库完全属于这个组，设置组的权限是读/写。</para>
        </listitem>

        <listitem>
          <para>你的用户在访问版本库时需要使用一个健全的umask，确定<command>svnserve</command>（<filename>/usr/bin/svnserve</filename>或者是任何一个<literal>$PATH</literal>说明的位置）是一个设置了<command>umask 002</command>和执行真正的<command>svnserve</command>程序的包裹脚本，对<command>svnlook</command>和<command>svnadmin</command>使用相同的措施，或者是使用一个健全的umask运行或者是使用上面说明的包裹。</para>
        </listitem>
      </itemizedlist>

    </sidebar>

  </sect1>




</chapter>

<!--
local variables: 
sgml-parent-document: ("book.xml" "chapter")
end:
-->
