svnserve,定制的服务器

svnserve程序是一个轻量级的服务器,使用基于TCP/IP上的一个自定义的、有状态的协议来与客户端通信。 客户端用以svn://svn+ssh://开头的URL模式来连接svnserve服务。 本节将解释运行svnserve的不同方式,客户端如何在服务器上认证他们,以及如何为你的资料库配置合适的访问控制。

调用服务器

调用svnserve程序有几种不同的方式。如果不带参数调用,那么你将只看到帮助信息。但是,如果你计划 让inetd运行这个进程,那么你可以转递-i--inetd)参数:

$ svnserve -i
( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline ) ) )

当用 --inetd 参数调用时,svnserve尝试通过stdinstdout用一种自定的协议于Subversion客户端通信。这是通过inetd来运行的程序的 标准行为。IANA为Subversion协议保留了3690端口,因此,在Unix类系统上你可以在/etc/services中添加如下几行( 如果它们不存在的话):

svn           3690/tcp   # Subversion
svn           3690/udp   # Subversion

并且如果你的系统使用经典的Unix类的inetd守护程序,你可以在/etc/inetd.conf添加这样一行:

svn stream tcp nowait svnowner /usr/local/bin/svnserve svnserve -i

确认“svnowner”是一个有适当的访问你资料库的权限。现在,当一个客户连接连接到你的服务器的3690端口时, inetd将生成一个svnserve来为它服务。

在Windows系统上,有些第三方工具可以使svnserve作为服务运行。查看Subversion的网站可以找到工具列表。

另一种选择是让svnserve作为一个独立的“守护daemon”进程运行。这要使用-d参数:

$ svnserve -d
$               # svnserve is now running, listening on port 3690

当在守护模式运行svnserve时,你可以使用--listen-port=--listen-host= 选项来定制要“绑定”的主机名和实际的端口号。

还有第三种调用svnserve的方法,这是一种“隧道模式”,要用-t选项。 这种模式假设一个远程服务程序比如RSHSSH已经成功地认证了一个用户并且现在是 以这个用户来调用svnserve进程。svnserve程序正常运转(通过 stdinstdout来通信),并假定通信流被自动重定向到客户端背后的某种隧道。 当 svnserve 是通过被像这样的隧道代理程序调用时,要确认认证的用户有对资料库数据库文件的完全的读写权限 (参见服务器和权限:一点警告)。这本质上和本地用户通过file:///这样的URL访问资料库是一样的。

一旦svnserve开始运行,它将使你的系统上的所有资料库上网。客户端需要在资料库URL中 指定绝对路径。例如,如果一个资料库位于/usr/local/repositories/project1,那么客户端 要通过svn://host.example.com/usr/local/repositories/project1 来访问它。为了增强完全性,你可以传递-r选项给svnserve,它限定了 只公开在指定路径下的资料库

$ svnserve -d -r /usr/local/repositories
…

使用-r选项有效的修改了服务程序作为远程文件系统空间的根目录的位置。从而客户端可以使用不包含这部分路径的URL, 使得URL短很多(并更少暴露内情):

$ svn checkout svn://host.example.com/project1
…

内建的认证和授权

当一个客户端连接一个svnserve进程时,发生了下列的事情:

  • 客户端选择一个指定的资料库。

  • 服务器处理资料库的conf/svnserve.conf文件,并开始执行所有在那儿定义的认证和授权政策。

  • 根据情况和授权政策,

    • 允许客户端匿名发出请求,而不会受到任何认证要求,或

    • 在任何时候客户端都可能被要求认证,或

    • 如果在“隧道模式”下操作,客户端会宣布它自己已经被外部认证了。

在写的时候,服务器只知道如何发送一个CRAM-MD5[20]认证要求。本质上,服务器 发送了一点数据给数据库。数据库使用MD5散列算法来创建数据和密码结合后的指纹,然后把指纹作为一个响应发送。服务器用保存的 密码执行相同的计算来验证结果是否相同。真实的密码永远不会通过网络来传输。

当然,对客户端来说也可能通过通道代理程序外部认证,比如 SSH。在这种情况下,服务器只检查运行它的用户,并 把这作为认证后的用户名。

你应该已经猜到,资料库的svnserve.conf是控制认证和授权的中心机制。这个文件和其他的配置文件 (参见???)有相同的格式:段名用方括号标记([]), 注释以井号(#)开头,每段包含可以设置(variable = value)的特定变量。让我们浏览一下这个文件并学习如何使用它。

创建 'users' 文件和范围

现在,svnserve.conf文件的[general]段有你需要的所有变量。开始于定义一个包含 用户名和密码的文件,以及一个认证范围。

[general]
password-db = userfile
realm = example realm

realm是一个你定义的名字。它告诉客户端它们连接到哪种“认证名字空间”;Subversion客户端在 认证提示的时候显示它,并用它作为一个键(连同服务器的主机名和端口)来在磁盘上缓存凭证(参见“客户凭证缓存”一节)。password-db变量指向一个不同的文件,它包含用户名和密码的 列表,并使用同样的我们已经熟悉的格式。例如

[users]
harry = foopassword
sally = barpassword

password-db的值可以是用户文件的绝对或相对路径。对很多管理员来说,就在资料库的conf/ 区域,svnserve.conf旁边保存这个文件会很方便。另一方面,可能你想让两个或更多的资料库共享同样的用户文件; 在这种情况下,这个文件应该位于更公共的地方。共享这个用户文件的资料库应该也被配置为有相同的认证范围,因为用户列表本质上定义 了认证范围。不管这个文件在哪儿,一定要正确地设置文件的读写权限。如果你知道哪个(些)用户会被用来运行svnserve,必须限制它们对用户文件只有读权限。

设置访问控制

svnserve.conf文件中设置了另外两个变量:它们决定未认证的(匿名的)用户和认证的用户分别被允许作什么。 anon-accessauth-access这两个变量可以被赋值为 noneread或者write。赋值为none限制了任何种类的访问;read允许对资料库的只读访问,write允许对资料的完全的读/写访问。例如:

[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

事实上这个例子设置是变量的缺省值,以免你忘了定义它们。如果你想更保守点,你可以完全阻止匿名访问。

[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

注意svnserve只理解“一揽子的”访问控制。一个用户或者有全局的读写访问权,全局的读访问权, 或没有访问权。没有对资料库中特定路径的细节的控制。对很多项目和场所,这种水平的访问控制足够了。但是,如果你需要对每个目录 的访问控制,那么你需要使用Apache而不是svnserve来做你的服务器进程。

外部的基于路径的授权

虽然svnserve本身没有提供任何对单个路径授权的方法,但是可以用pre-commit钩子来执行对单个路径的访问控制。 Subversion分发版包含commit-access-control.pl 和更复杂的svnperms.py脚本以便在 pre-commit脚本中使用。

SSH 认证和授权

svnserve内建的认证非常便利,因为它避免了创建真正的系统帐号的需要。另一方面,一些管理员已经建立了一个 很好的SSH认证框架。在这种情况下,所有的项目用户已经有了系统帐号和通过“SSH进入”系统机器的能力。

联合使用SSH和svnserve是很容易的。客户端只要用svn+ssh:// URL模式来连接:

$ whoami
harry

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

foo
bar
baz
…

这里发生的过程是:Subversion客户端调用一个本地的ssh进程,连接到host.example.com,以用户 harry来认证,然后在远程机器上生成一个私有的svnserve进程。svnserve 命令以隧道模式(-t)来调用,它的网络协议从隧道代理ssh建立的加密连接中“穿过”。 svnserve知道它以用户harry在运行,如果客户端执行一个提交,这个认证的用户名将被作为 新修订版的作者存到修订版属性。

当在隧道上运行时,授权主要通过操作系统对资料库数据库文件的权限来来控制;这和Harry通过一个file:/// URL 直接访问资料库时完全相同。如果多个系统用户要直接访问资料库,你应该把它们放到一个共同的用户组中,并且你需要注意他们的umask。 (一定要读“支持多种资料库访问方式”一节)但是即使在隧道情况下,svnserve.conf文件仍然可以被用来阻止访问, 只要设置auth-access = readauth-access = none

也许你认为SSH隧道到这就说完了,但是还没有。Subversion允许你在你的运行时config文件 (参见???)里创建自定义的隧道行为。例如,假设你想使用RSH而不是SSH。只要在你的config文件的[tunnels]段像下面这样定义:

[tunnels]
rsh = rsh

现在,你可以通过和你的新变量名匹配的URL模式来使用这个新的隧道定义:svn+rsh://host/path。当使用新URL模式 时,Subversion 客户端实际上将在后台运行命令rsh host svnserve -t。如果你在URL中包含了用户名(例如, svn+rsh://username@host/path),客户端也将在它的命令(rsh username@host svnserve -t)里包含它。但是你可以定义比这更智能的新隧道模式:

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

这个例子显示了几件事情。首先,它显示如何用Subversion客户端来运行一个非常特殊的带特定选项的隧道(位于/opt/alternate/ssh)命令。在这种情况下,访问svn+joessh:// URL将调用 这个特定的SSH命令并带着-p 29934作为参数——如果你想要隧道程序连接一个非标准的端口时会用得着。

第二,它演示了如何定义自定义的环境变量来代替隧道程序的名字。设置SVN_SSH环境变量是一个替换缺省SSH 隧道代理程序的简便方法。但是,如果你需要有几种不同的替换,分别用于不同的服务器,可能连接不同的端口或传递不同的参数组,那么 你可以使用在这个例子里演示的机制。现在如果我们要设置JOESSH环境变量,那么它的值将完全覆盖隧道变量的值 ——会执行$JOESSH而不是/opt/alternate/ssh -p 29934

最后说明:在使用svn+ssh:// URL访问资料库时,记住是ssh程序在提示你认证,而不是 svn程序。这意味着没有自动密码缓存过程(参见“客户凭证缓存”一节)。如果你想阻止ssh重复的问你密码,你可以使用其它的内存缓存工具, 比如在Unix系统上的ssh-agent,或Windows上的pageant



[20] 参见 RFC 2195。