6.svnserve,一个自定义的服务器

Posted on Posted in 7.服务配置

svnserve,一个自定义的服务器

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

调用服务器

有许多不同方法运行svnserve

  • 作为一个独立守护进程启动svnserve,监听请求。

  • 当特定端口收到一个请求,就会使UNIX的inetd守护进程临时调用svnserve处理。

  • 使用SSH在加密通道发起临时svnserve服务。

  • 以Windows service服务方式运行svnserve

svnserve作为守护进程

使用svnserve最简单的方式是作为独立“守护”进程运行,使用-d选项:

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

         

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

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

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

         

使用-r可以有效地改变文件系统的根位置,客户端可以使用去掉前半部分的路径,留下的要短一些的(更加有提示性)URL:

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

                         

使用svnserve通过inetd

如果你希望inetd启动进程,你需要使用-i--inetd)选项,在这个例子里,我们显示了在命令行中运行svnserve -i的输出,但是请注意这不是如何实际启动daemon; 请继续阅读例子后的文章,学习如何配置inetd启动svnserve

$ 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/bin/svnserve svnserve -i

         

确定“svnowner”用户拥有访问版本库的适当权限,现在如果一个客户连接来到你的服务器的端口3690,inetd会产生一个svnserve进程来做服务。当然,你也可以添加-r到命令行,限制暴露出的版本库。

通过通道使用svnserve

第三种方式使用-t选项的“管道模式”,这个模式假定一个分布式服务程序如RSHSSH已经验证了一个用户,并且以这个用户调用了一个私有svnserve进程,svnserve运作如常(通过stdinstdout通讯),并且可以设想通讯是自动转向到一种通道并传递回客户端,当svnserve被这样的通道代理调用,确定认证用户对版本数据库有完全的读写权限,这与本地用户通过file:///URl访问版本库同样重要。

这个选项将在“SSH 隧道”一节详细讨论。

svnserve作为Windows服务

如果你的Windows系统是Windows NT (2000, 2003, XP, Vista)的后代,你可以将svnserve作为Windows服务运行,这是比使用--daemon (-d)选项直接运行守护进程感觉更好。使用守护进程模式,需要打开命令行窗口,输入命令,然后保持命令行窗口不关闭,而作为Windows服务时,在后台运行,可以在启动时自动执行,并且可以使用同其他Windows服务一致的管理界面启动和停止服务。

你需要使用命令行工具SC.EXE定义新的服务,就像inetd的配置行,你必须在Windows启动时指明svnserve的调用:

C:\> sc create svn         binpath= "C:\svn\bin\svnserve.exe --service -r C:\repos"         displayname= "Subversion Server"         depend= Tcpip         start= auto

         

这样定义了一个新的Windows服务,叫做“svn”,会在启动时(在这个例子里,根目录是C:\repos。)执行特定的svnserve.exe,可是前面这个例子产生了一些错误。

首先,要注意svnserve.exe必须使用--service选项启动。svnserve的其它选项必须在同一行上指定,但你不能使用冲突的选项,例如--daemon (-d)--tunnel--inetd (-i),而选项-r--listen-port都没有问题。第二,调用SC.EXE时必须注意空格:key= value的模式中key=之间必须没有空格,而且在与value之间只能有一个空格。最后,必须注意执行的命令行中的空格,如果目录名中包含了空格(或其它需要回避的字符),为了回避这些字符,请将整个binpath值放在双引号中:

C:\> sc create svn         binpath= "\"C:\program files\svn\bin\svnserve.exe\" --service -r C:\repos"         displayname= "Subversion Server"         depend= Tcpip         start= auto

         

也需要注意单词binpath会造成误解—它的值是一个命令行,而不是可执行的路径,所以我们为了防止有嵌入的空格而使用了引号围绕。

一旦定义了服务,就可以使用标准GUI工具(服务管理控制面板)进行停止、启动和查询,或者是通过命令行:

C:\> net stop svn C:\> net start svn

         

也可以通过删除其定义删除服务:sc delete svn,只需要确定首先停止服务,SC.EXE有许多子命令和选项,更多信息可以运行sc /?查看。

内置的认证和授权

如果一个客户端连接到svnserve进程,如下事情会发生:

  • 客户端选择特定的版本库。

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

  • 依赖于位置和授权政策,

    • 如果没有收到认证请求,客户端可能被允许匿名访问,或者

    • 客户端收到认证请求,或者

    • 如果操作在“通道模式”,客户端会宣布自己已经在外部得到认证。

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

当然也有可能,如果客户端在外部通过通道代理认证,如SSH,在那种情况下,服务器简单的检验作为那个用户的运行,然后使用它作为认证用户名,更多信息请看“SSH 隧道”一节

像你已经猜测到的,版本库的svnserve.conf文件是控制认证和授权政策的中央机构,这文件与其它配置文件格式相同(见“运行配置区”一节):小节名称使用方括号标记([]),注释以井号(#)开始,每一小节都有一些参数可以设置(variable = value),让我们浏览这个文件并且学习怎样使用它们。

创建一个用户文件和认证域

此时,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

         

服务进程不仅仅理解对版本库的整体访问控制,也可以细粒度的控制版本库某个文件或目录的访问,为了使用这个特性,你需要定一个包含详细规则的文件,并将变量authz-db指向到这个文件。

[general] password-db = userfile realm = example realm # Specific access rules for specific locations authz-db = authzfile

         

authzfile得语法会在“基于路径的授权”一节讨论,注意变量authz-db并不比anon-accessauth-access更高级,如果定义了所有的变量,要想被允许访问必须满足所有的规则。

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私有进程以用户harry运行。svnserve是以管道模式调用的(-t),它的网络协议是通过ssh“封装的”,被管道代理的svnserve会知道程序是以用户harry运行的,如果客户执行一个提交,认证的用户名会作为版本的参数保存到新的修订本。

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

当使用svn+ssh://的URL访问版本库时,记住是ssh提示请求认证,而svn客户端程序。这意味着密码不会有自动缓存(见“客户端凭证缓存”一节),Subversion客户端通常会建立多个版本库的连接,但用户通常会因为密码缓存特性而没有注意到这一点,当使用svn+ssh://的URL时,用户会为ssh在每次建立连接时重复的询问密码感到讨厌,解决方案是用一个独立的SSH密码缓存工具,像类Unix系统的ssh-agent或者是Windows下的pageant

当在一个管道上运行时,认证通常是基于操作系统对版本库数据库文件的访问控制,这同Harry直接通过file:///的URL直接访问版本库非常类似,如果有多个系统用户要直接访问版本库,你会希望将他们放到一个常见的组里,你应该小心的使用umasks。(确定要阅读“支持多种版本库访问方法”一节)但是即使是在管道模式时,文件svnserve.conf还是可以阻止用户访问,如设置auth-access = readauth-access = none[39]

你会认为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会以-p 29934参数调用特定的SSH程序—对连接到非标准端口的程序非常有用。

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

SSH 配置技巧

不仅仅是可以控制客户端调用ssh方式,也可以控制服务器中的sshd的行为方式,在本小节,我们会展示怎样控制sshd执行svnserve,包括如何让多个用户分享同一个系统帐户。

初始设置

作为开始,定位到你启动svnserve的帐号的主目录,确定这个账户已经安装了一套SSH公开/私有密钥对,用户可以通过公开密钥认证,因为所有如下的技巧围绕着使用SSHauthorized_keys文件,密码认证在这里不会工作。

如果这个文件还不存在,创建一个authorized_keys文件(在UNIX下通常是~/.ssh/authorized_keys),这个文件的每一行描述了一个允许连接的公钥,这些行通常是下面的形式:

  ssh-dsa AAAABtce9euch.... user@example.com

         

第一个字段描述了密钥的类型,第二个字段是未加密的密钥本身,第三个字段是注释。然而,这是一个很少人知道的事实,可以使用一个command来处理整行:

  command="program" ssh-dsa AAAABtce9euch.... user@example.com

         

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

  command="program" TYPE KEY COMMENT

                         

控制调用的命令

因为我们可以指定服务器端执行的命令,我们很容易来选择运行一个特定的svnserve程序来并且传递给它额外的参数:

  command="/path/to/svnserve -t -r /virtual/root" TYPE KEY COMMENT

         

在这个例子里,/path/to/svnserve也许会是一个svnserve程序的包裹脚本,会来设置umask(见“支持多种版本库访问方法”一节)。它也展示了怎样在虚拟根目录定位一个svnserve,就像我们经常在使用守护进程模式下运行svnserve一样。这样做不仅可以把访问限制在系统的一部分,也可以使用户不需要在svn+ssh://URL里输入绝对路径。

多个用户也可以共享同一个帐号,作为为每个用户创建系统帐户的替代,我们创建一个公开/私有密钥对,然后在authorized_users文件里放置各自的公钥,一个用户一行,使用--tunnel-user选项:

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

         

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

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

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

                   


[38] 见RFC 2195。

[39] 请注意,使用svnserve的访问控制进行权限控制将会失去意义,因为用户已经直接访问到了版本库数据。