远程源码库

你的源代码的工作拷贝也可以存在不同于源码库机器上;这种方式CVS以client/server的模式运作。你先运行一台装有自己工作目录的电脑,它被称为客户机(client);然后告诉它你将连到一台装有源码库的电脑,它被称为服务器(server)。通常,使用远程源码库除了源码库名称格式不同,其他方面与本地机没什么区别:

:method:[[user][:password]@]hostname[:[port]]/path/to/repository

不推荐在在源码库名中带有密码这种方式,因为它会使检出保存纯文本密码到每个工作目录中。cvs login 先使用即可[在客户端使用密码认证]。

具体安装细节要看你连到服务端的方式。

如果方法(method)没有特别指明,源码库名应包括“:”,依据你的平台缺省方法是extserver,描述在[通过rsh连接]。

服务器需求

简单地说,普通电脑就可以作为一台CVS服务器,例如,一台32M或更少内存的电脑,就能处理一般的资源树。

要精确定位的话,当然就比较复杂。通过评估耗用内存比较大的因素就可以确定服务器的内存需求。主要有两个因素,相比之下其它方面占用内存较少。(当然,如果你认为不像[附录 H]中描述的那样,请告诉我,以便于我可以修改这个文档)。

第一个因素是当使用CVS服务器时进行大量的检出。对于每一个要求响应的客户机,服务器都提供两个进程;一般在子进程中应当少占用内存,而在父进程中,尤其是在网络连接比较慢的时候,内存需求就要比单一目录的尺寸大,大2M或更多。

一般通过某一时刻活动的服务数量来衡量CVS服务器的内存,我们应该对此有一个详细的方案。大多数情况下,我们应该使用硬盘交换空间(swap space)来替代父进程对内存的消耗。

另一个因素,是diff作用于大文件,还有对二进制文件操作时。经验规则要求:在校验文件时提供最大文件尺寸的10倍大小的内存,尽管5倍大小的内存就够了。例如,要提交一个10M的文件,这时就需要100M的内存空间(C/S机制或其它机制的CVS服务器)。这样就应该尽量用硬盘交换空间来代替。因为,对服务器内存的占用是暂时的,没有必要专门为一时提交提供固定的内存。

对于客户机,一般来说只要有运行相关操作系统的能力,运行CVS客户端就没在太大的问题。

要想了解CVS服务器对硬盘的要求,参看[创建源码库]。

通过rsh连接

CVS使用rsh协议执行这些操作,因此远程主机需要建立.rhosts来控制用户的访问权限。

例如,假设你是本地机上toe.example.com上的用户mozart,服务器是faun.example.org。 首先,在服务器上bach主目录下的.rhosts的文件中加入下面的内容:

toe.example.com  mozart

再用以下命令rsh从本地机进行测试:

rsh -l bach faun.example.org 'echo $PATH'

接着应该确保rsh可以找到服务器。注意上面例子中rsh打印的路径$PATH应包括服务器上cvs程序所在的路径。你需要在.bashrc.cshrc中设置路径而不是在.login或者.profile中。同时,你需要在客户机上设置环境变量“CVS_SERVER”指向你希望访问的服务器,例如: /usr/local/bin/cvs-1.6.

这里设置的时候,不需要编辑inetd.conf或者启动一个CVS后台服务程序。

有两种方法可以在rsh中使用CVSROOT:server:指定一个内部rsh客户,这种方法仅仅被少数CVS端口支持。:ext:指定一个外部的rsh程序。默认是使用rsh,但是你可以通过设置CVS_RSH环境变量来用另外的程序来访问远程服务器(例如,在HP-UX 9上remsh,因为在HP-UX 9上rsh有一些不同)。这个程序必须是一个可以在客户机和服务器之间来回传送数据而并不修改数据的程序。比如,Windows NT的rsh就不适合作为这样的程序,因为它默认地是在CRLF和LF之间传送数据的。OS/2的CVS通过-brsh来实现这种传递,但是由于这会对标准rsh程序以外的程序引起潜在的问题,这种方法在未来可望被改变。如果你设置CVS_RSHSSH或者使用其它替代程序,本节中关于.rhosts 的例子可能会不适用,建议参考替代程序的文档。

继续我们的例子,假如你希望访问服务器faun.example.org上的源码库/usr/local/cvsroot/中的模块foo,你可以使用以下命令:

cvs -d :ext:bach@faun.example.org:/usr/local/cvsroot checkout foo

(如果用户在本地机和远程主机上的用户名相同,bach@可以被忽略)

通过密码认证直接连接

CVS客户端也可以通过一种密码协议直接连接服务器。这在rsh不可用的时候(例如,服务器在防火墙后面)和无法使用网络安全协议Kerberos的时候尤其有用。

要使用这种方法,必须在服务器和客户端进行一些调整。

在服务器端设置密码认证

首先,你可能需要在$CVSROOT$CVSROOT/CVSROOT上设定权限。请参阅[密码认证中的安全事项]获得更多细节。

在服务器端,需要编辑文件/etc/inetd.confinetd它在正确的端口上收到一个连接的时候运行cvs pserver命令。默认的CVS端口号是2401;当然,如果客户端重新修改了CVS_AUTH_PORT就会有一些不同。也可以在CVSROOT设置[远程源码库]或用CVS_CLIENT_PORT变量覆盖[附录 D]。

如果你的inetd/etc/inetd.conf中允许原始的端口号定义,那么下面的命令就足够了(所有命令在inetd.conf中都是单行):

2401  stream  tcp  nowait  root  /usr/local/bin/cvs
cvs -f --allow-root=/usr/cvsroot pserver

(你也可以使用-T选项来指定一个临时目录。)

-allow-root选项指定一个允许的CVSROOT目录。试图使用一个另外的CVS目录的客户将被拒绝连接。如果你希望设置多个允许的CVS目录,重复设置这一选项就行了。(不幸的是,许多版本的inetd对参数的数量和/或命令长度有限制。通常解决方法是在inetd中运行一个脚本,然后在脚本中调用所需的参数。)

如果你希望inetd使用符号化的服务名称来代替原始端口号,需要在/etc/services里添加以下内容:

cvspserver      2401/tcp

并且在inetd.conf中使用“cvspserver”代替“2401”。

如果你的系统用xinetd替代inetd,步骤有些不同。创建一个/etc/xinetd.d/cvspserver文件,包含下列内容:

service cvspserver
{
   port        = 2401
   socket_type = stream
   protocol    = tcp
   wait        = no
   user        = root
   passenv     = PATH
   server      = /usr/local/bin/cvs
   server_args = -f --allow-root=/usr/cvsroot pserver
}

(如果已在/etc/services中定义cvspserver, 可以省略port这一行。)

做了上面的修改之后,需要重新运行你的inetd,或者采取其它可以使inetd重新读取它的初始化文件。

如果有说明设置的问题,参阅[Trouble making a connection to a CVS server 在 附录 F]。

因为客户端以明文存储和传输密码,请参阅[密码认证中的安全事项]获得更多细节,所以CVS使用了一个独立的CVS密码文件,来保证用户连接源码库的时候不会危及定期密码的安全。这个文件是$CVSROOT/CVSROOT/passwd[管理文件-administrative files]。除了只用较少的域(cvs用户名称,可有可无的密码和服务器的用户名称)以外,它的格式和Unix上的/etc/passwd相似,采用分号分隔。例如:

anonymous:
bach:ULtgRLXo7NRxs
spwang:1sOp854gDF3DY
melissa:tGX1fS8sun6rY:pubcvs
qproj:XR4EZcEs0szik:pubcvs

(密码使用Unix标准的crypt()函数加密,因此它可以从常规Unix /etc/passwd密码文件粘贴过来。)

范例中第一行允许任何cvs客户机使用anonymous来访问,密码可以用任意字符还可以用空密码。(这是一个站点允许匿名进行只读访问的典型用法;"只读(read-only)"部分的信息,参看[只读存储仓的访问]。)

第二、三行允许用户bachspwang使用密码来访问。

第四行允许用户melissa用密码来访问,但在服务器上使用pubcvs用户名。虽然系统上不须有melissa用户名,但pubcvs必不可少。

第五行显示服务器上用户可以共享:客户机用用户名qproj登录,然后和melissa一样使用pubcvs来使用CVS。用这种方法你可以为每个项目设置一个服务器系统用户名,然后在$CVSROOT/CVSROOT/passwd文件中为每个开发者设置一行。开发者名字不同,而系统用户名相同。用不同的cvs用户名目的是在cvs操作中作纪录:当melissa提交了项目中的一个修改,历史纪录中纪录的提交用户名是melissa而不是pubcvs。而共享一个系统用户的好处是便于安排源码库中的权限。

当服务器认证密码的时候,它会在CVS密码文件里查找该用户。如果该用户被找到,则比较密码。如果用户没有被找到,或者CVS密码文件不存在,服务器会试图使用系统的用户查找机制来匹配密码。如果都没有,cvs操作会失败(无论是客户机是否提供了正确的密码)。

密码和系统用户都可以省略(如果系统用户省略,那么与密码分隔的冒号也省略),例如,下面的$CVSROOT/CVSROOT/passwd例子是有效的:

anonymous::pubcvs
fish:rKa5jzULzmhOo:kfogel
sussman:1sOp854gDF3DY

当密码域为空,客户机可以用空密码在内的任何密码登录。但分隔冒号必需存在。

cvs还可借助于系统认证。认证的时候,服务器先检查$CVSROOT/CVSROOT/passwd文件中的用户,如果找到,就使用上面的方法进行。 如果没有找到,或passwd文件不存在,服务器将采用操作系统的用户认证机制(这种机制可以通过设置config文件中的SystemAuth=no来禁止[The CVSROOT/config configuration file 在 附录 C])。这种方法有安全方面的漏洞:登录密码通过网络传输的是纯文本,参阅[密码认证中的安全事项]。

现在在CVS密码文件里加入密码passwd的唯一方法是从别处粘贴而来。也许有一天,CVS中会添加一条密码生成命令cvs passwd

不像其他在$CVSROOT/CVSROOT目录下的文件可以通过cvs检出来修改,passwd只能在服务器上进行编辑。这也是出于安全方面的考量。如果你的确需要将passwd检出,参看[The checkoutlist file 在 附录 C]。

在客户端使用密码认证

要让cvs通过密码验证服务器运行远程源码库,需要指定pserver协议,用户名(可选),源码库主机,端口号(可选),源码库路径。例如:

cvs -d :pserver:faun.example.org:/usr/local/cvsroot checkout someproj

CVSROOT=:pserver:bach@faun.example.org:2401/usr/local/cvsroot
cvs checkout someproj

除了那些公共源码库(例如,不需要密码的用户名),首先要用密码登录log in。源码库校验登录密码然后将它保存起来。这是通过login命令完成,如果在$CVSROOT没有提供密码,它将提示你输入:

cvs -d :pserver:bach@faun.example.org:/usr/local/cvsroot login
CVS password:

cvs -d :pserver:bach:p4ss30rd@faun.example.org:/usr/local/cvsroot login

输入密码,服务器进行验证。如果通过,username, host, repository, password被纪录,以后源码库不再要求执行cvs login命令。(如果失败,cvs提示密码错误,并不纪录任何信息)

密码默认地存储在$HOME/.cvspass文件中。它的格式是可读的,但是在通常情况下不要试图编辑它。密码不是以明文存储的,通常经过了编码来保护它们避免被无心发现的危险(例如,被碰巧看到这个文件的系统管理员或其他人无心地看到)。

你可以改变该文件的默认位置,方法是设置CVS_PASSFILE变量。设置这个变量,请确保cvs login之前进行。如不这样做,以后的cvs命令将无法找到密码传输给服务器。

登录之后,所有的cvs命令访问该源码库都使用保存的密码。例如:

cvs -d :pserver:bach@faun.example.org:/usr/local/cvsroot checkout foo

将正常工作(除非更改了密码,那样你需要重新运行cvs login)。

注意,如果在源码库定义上没注明:pserver:,cvs会采用默认的rsh来连接服务器[通过rsh连接]。

不过,如你已经有一个检出的工作拷贝并在里面使用过cvs命令,以后将不再指明源码库的定义,因为cvs可以从CVS子目录获取。

使用cvs logout命令可以将远程源码库的密码从CVS_PASSFILE中删除。

密码认证中的安全事项

密码在客户端是以明文的一般编码方式存储的,也是以同样的方式传输的。这种编码仅仅可以避免密码被无心发现的危险(例如,被碰巧看到这个文件的系统管理员看到),它甚至不能阻止一个想获得密码的初级的攻击者。

独立的CVS密码文件[在服务器端设置密码认证]允许用户使用不同的密码获取源码库的权限。在另一方面,一旦用户拥有对源码库的非只读权限,他就可以在服务器上通过一系列方法运行程序。因而,源码库的权限比系统相同权限要宽得多。可以通过修改CVS来防止这一情况,但是在本文写作以前,还没有人这样做过。

请注意:因为$CVSROOT/CVSROOT文件夹包含密码文件passwd和其它与安全审核相关的文件,你必须象严格控制/etc目录的许可权限一样严格地控制这个目录的许可权限。同样的,$CVSROOT目录本身和它在文件目录树结构中的任何上级目录也要被严格控制。任何对这些目录具有写权限的用户应是最高级别的系统用户。注意:如果你没有使用“pserver”选项的话,这些许可常常会更加严格。

概括地讲,任何获得密码的用户就获得源码库的访问权限,同时也获得了部分系统的权限。任何可以截获网络数据包或者能够读取被保护的文件(例如,只读文件)用户都可能获得密码。如果你希望获得更高的安全性能,请使用Kerberos网络安全系统。

通过GSSAPI直接连接

GSSAPI是一套类似Kerberos 5的通用网络安全系统接口。如果你拥有一套GSSAPI库,就可以通过TCP连接直接建立CVS连接,由GSSAPI进行安全鉴别。

要做到这一点,需要在GSSAPI的支持下重新编译CVS。当配置CVS的时候,CVS会检测使用kerberos 5的GSSAPI库是否存在。你也可以使用-with-gssapi标志来配置。

这样CVS的连接就使用GSSAPI来进行安全鉴别了,但是消息流默认并被鉴别。你必须使用“-a” 全局选项来要求消息流进行安全鉴别。

传输数据默认被加密。加密支持必须同时加入到客户端和服务器端,然后使用-enable-encrypt 配置选项来开启加密支持功能。最后你必须使用“ -x”全局选项来要求加密。

在服务器端处理GSSAPI连接的服务器和密码鉴别服务器应该是同一台服务器,请参阅密码鉴定服务器一节[在服务器端设置密码认证]。如果你已经使用了诸如基于Kerberos的GSSAPI机制来提供功能强大的安全鉴别功能了,你可能希望关闭通过明文密码鉴别的功能。要做到这一点,创建一个空的CVSROOT/passwd密码文件,并且在配置文件里设置SystemAuth=no,请参阅配置一节[The CVSROOT/config configuration file 在 附录 C]。

GSSAPI服务器使用cvs/hostname的主名称,其中主机名称hostname是服务器主机的规范名称。GSSAPI需要设置这一名称。

要使用GSSAPI进行连接,要在命令中使用:gserver:关键字。例如:

cvs -d :gserver:faun.example.org:/usr/local/cvsroot checkout foo

通过kerberos直接连接

就像[通过rsh连接]中描述的,使用kerberos最容易的方式是使用基于kerberos的rsh。使用rsh最大的缺点是所有的数据都需要通过另外的程序传递,因此这种方式会慢一些。如果你安装了kerberos就可以通过直接的TCP连接建立CVS连接,使用kerberos进行安全鉴别。

本节的讨论都是基于kerberos网络安全系统第四版的,在前一节中已经讨论过基于GSSAPI通用网络安全接口的Kerberos第五版支持。

要获得这种支持,CVS需要在kerberos的支持下重新被编译。在配置CVS的时候,系统会自动检测kerberos是否存在。你也可以使用“-with-krb4”标记来配置CVS。

传输数据默认被加密。加密支持必须同时加入到客户端和服务器端,然后使用-enable-encryption配置选项来开启加密支持功能。最后你必须使用-x全局选项来要求加密。

你需要编辑服务器的inetd.conf来运行cvs kserver。客户端默认使用1999端口。如果你希望使用别的端口可以在CVSROOT[远程源码库]或客户端的CVS_CLIENT_PORT环境变量中指定它。

配置完成后当你希望使用CVS的时候,和通常方式下(generally kinit)一样先获得密码,这样你就可以登录进服务器。然后使用以下命令:

cvs -d :kserver:faun.example.org:/usr/local/cvsroot checkout foo

CVS的早期版本在通过rsh连接的时候会异常返回,但现在的版本已经修正了这一问题。

通过fork连接

这种访问方法允许你用远程登录协议访问本机磁盘上的源码库。也就是说除了远程方式的一些问题外其他的跟:local:没什么两样。

在日常使用中你可以选择:local::fork:。当然:fork:最初是为了测试cvs远程访问协议。除了使用远程协议进行连接,我们忽略了网络相关的连接,超时,认证问题。

要用fork方法连接,使用:fork:和本机源码库路径。例如:

cvs -d :fork:/usr/local/cvsroot checkout foo

:ext:一样,服务器默认调用cvs,也可在CVS_SERVER环境变量中指定。