本文档旨在描述 Apache™ Subversion® 的 1.7.x 系列。如果您运行的是其他版本的 Subversion,强烈建议您访问 https://svnbook.subversion.org.cn/ 并查阅适合您 Subversion 版本的文档。

svnserve,自定义服务器

svnserve 程序是一个轻量级服务器,能够通过使用自定义的有状态协议在 TCP/IP 上与客户端通信。客户端通过使用以 svn://svn+ssh:// 开头的 URL 来联系 svnserve 服务器。本节将解释运行 svnserve 的不同方法、客户端如何向服务器进行身份验证以及如何为您的存储库配置适当的访问控制。

调用服务器

运行 svnserve 程序有几种不同的方法。

  • 运行 svnserve 作为独立守护进程,监听请求。

  • 让 Unix inetd 守护进程在特定端口收到请求时临时生成 svnserve

  • 让 SSH 通过加密隧道调用临时 svnserve

  • 运行 svnserve 作为 Microsoft Windows 服务。

  • 运行 svnserve 作为 launchd 作业。

以下部分将详细介绍这些 svnserve 的各种部署选项。

svnserve 作为守护进程

最简单的选项是运行 svnserve 作为独立的 守护进程 进程。为此,请使用 -d 选项。

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

在守护进程模式下运行 svnserve 时,您可以使用 --listen-port--listen-host 选项来自定义要 绑定 的确切端口和主机名。

如前所述,成功启动 svnserve 后,它会使系统上的每个存储库都可供网络访问。客户端需要在存储库 URL 中指定一个 绝对 路径。例如,如果存储库位于 /var/svn/project1,则客户端可以通过 svn://host.example.com/var/svn/project1 访问它。为了提高安全性,您可以将 -r 选项传递给 svnserve,这会限制它只导出该路径下的存储库。例如

$ svnserve -d -r /var/svn
…

使用 -r 选项实际上修改了程序视为远程文件系统空间根目录的位置。然后,客户端使用从这些 URL 中删除了该路径部分的 URL,从而使 URL 更短(并且更不显眼)。

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

通过 inetd 运行 svnserve

如果您希望 inetd 启动进程,您需要传递 -i (--inetd) 选项。在以下示例中,我们展示了在命令行运行 svnserve -i 的输出,但请注意,这不是您实际启动守护进程的方式;有关如何配置 inetd 以启动 svnserve,请参阅示例后的段落。

$ svnserve -i
( success ( 2 2 ( ) ( edit-pipeline svndiff1 absent-entries commit-revprops d\
epth log-revprops atomic-revprops partial-replay ) ) )

当使用 --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

另一种调用 svnserve 的方法是使用 -t 选项以隧道模式运行。此模式假设远程服务程序(如 rshssh)已成功验证用户身份,并且现在正在调用私有的 svnserve 进程 作为该用户。(请注意,您(用户)很少(如果有的话)需要在命令行中使用 -t 调用 svnserve;相反,SSH 守护进程会为您执行此操作。)svnserve 程序的行为正常(通过 stdinstdout 进行通信),并假设流量正在通过某种隧道自动重定向回客户端。当 svnserve 被像这样的隧道代理调用时,请确保已验证的用户对存储库数据库文件具有完全的读写权限。这与本地用户通过 file:// URL 访问存储库本质上相同。

此选项将在本章后面 名为“通过 SSH 建立隧道” 的部分中详细介绍。

svnserve 作为 Windows 服务

如果您的 Windows 系统是 Windows NT 的后代(Windows 2000 或更高版本),您可以将 svnserve 作为标准的 Windows 服务运行。这通常比使用 --daemon (-d) 选项将其作为独立守护进程运行要好得多。使用守护进程模式需要启动一个控制台,键入一个命令,然后让控制台窗口无限期地运行。但是,Windows 服务在后台运行,可以在启动时自动启动,并且可以使用与其他 Windows 服务相同的管理界面启动和停止。

您需要使用命令行工具 SC.EXE 定义新服务。与 inetd 配置行非常类似,您必须指定 svnserve 的确切调用,以便 Windows 在启动时运行。

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

这定义了一个名为 svn 的新 Windows 服务,该服务在启动时执行特定的 svnserve.exe 命令(在本例中,根目录位于 C:\repos)。但是,前面的示例中有一些注意事项。

首先,请注意,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 作为 launchd 作业

Mac OS X(10.4 及更高版本)使用 launchd 来管理进程(包括守护进程),包括系统范围和每个用户范围。一个 launchd 作业由 XML 属性列表文件中的参数指定,launchctl 命令用于管理这些作业的生命周期。

当配置为以 launchd 作业运行时,svnserve 会在需要处理传入的 Subversion svn:// 网络流量时按需自动启动。这比需要您手动调用 svnserve 作为长时间运行的后台进程的配置方便得多。

要将 svnserve 配置为 launchd 作业,首先创建一个名为 /Library/LaunchDaemons/org.apache.subversion.svnserve.plist 的作业定义文件。示例 6.1, “一个 svnserve launchd 作业定义示例” 提供了此类文件的示例。

示例 6.1. 一个 svnserve launchd 作业定义示例

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
    "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
    <dict>
        <key>Label</key>
        <string>org.apache.subversion.svnserve</string>
        <key>ServiceDescription</key>
        <string>Host Subversion repositories using svn:// scheme</string>
        <key>ProgramArguments</key>
        <array>
            <string>/usr/bin/svnserve</string>
            <string>--inetd</string>
            <string>--root=/var/svn</string>
        </array>
        <key>UserName</key>
        <string>svn</string>
        <key>GroupName</key>
        <string>svn</string>
        <key>inetdCompatibility</key>
        <dict>
            <key>Wait</key>
            <false/>
        </dict>
        <key>Sockets</key>
        <dict>
            <key>Listeners</key>
            <array>
                <dict>
                    <key>SockServiceName</key>
                    <string>svn</string>
                    <key>Bonjour</key>
                    <true/>
                </dict>
            </array>
        </dict>
    </dict>
</plist>

[Warning] 警告

launchd 系统可能有点难学。幸运的是,本节中描述的命令有相关文档。例如,在命令行中运行 man launchd 以查看 launchd 本身的联机帮助页,man launchd.plist 以阅读有关作业定义格式的信息,等等。

创建作业定义文件后,可以使用 launchctl load 激活作业。

$ sudo launchctl load \
       -w /Library/LaunchDaemons/org.apache.subversion.svnserve.plist

明确地说,此操作不会立即启动 svnserve。它只是告诉 launchd 如何在传入的网络流量到达 svn 网络端口时启动 svnserve;在处理完流量后,它将被终止。

[Note] 注意

因为我们希望 svnserve 是一个系统范围的守护进程,所以我们需要使用 sudo 以管理员身份管理此作业。还要注意,定义文件中的 UserNameGroupName 键是可选的——如果省略,作业将以加载作业的用户身份运行。

停用作业同样简单——使用 launchctl unload

$ sudo launchctl unload \
       -w /Library/LaunchDaemons/org.apache.subversion.svnserve.plist

launchctl 还提供了一种查询作业状态的方法。如果作业已加载,则将有一行与作业定义文件中指定的 Label 匹配。

$ sudo launchctl list | grep org.apache.subversion.svnserve
-       0       org.apache.subversion.svnserve
$

内置身份验证和授权

当客户端连接到 svnserve 进程时,会发生以下情况

  • 客户端选择一个特定的存储库。

  • 服务器会处理仓库的 conf/svnserve.conf 文件,并开始执行文件中描述的任何身份验证和授权策略。

  • 根据定义的策略,可能会发生以下情况之一:

    • 客户端可能被允许匿名发出请求,而无需进行身份验证挑战。

    • 客户端可能在任何时候被挑战进行身份验证。

    • 如果在隧道模式下运行,客户端将声明自己已在外部进行身份验证(通常通过 SSH)。

svnserve 服务器默认情况下只知道如何发送 CRAM-MD5[52] 身份验证挑战。本质上,服务器会向客户端发送少量数据。客户端使用 MD5 哈希算法来创建数据和密码的指纹,然后将指纹作为响应发送。服务器使用存储的密码执行相同的计算,以验证结果是否相同。 实际密码不会在网络上传输。

如果您的 svnserve 服务器是使用 SASL 支持构建的,它不仅知道如何发送 CRAM-MD5 挑战,而且可能还知道许多其他身份验证机制。请参阅本章后面的 名为“使用 svnserve 与 SASL”的部分,了解如何配置 SASL 身份验证和加密。

当然,客户端也可以通过隧道代理(例如 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 可以设置为 nonereadwrite。将值设置为 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 变量并不互斥;如果所有变量都同时定义,则必须满足所有规则才能允许访问。

使用 svnserve 与 SASL

对于许多团队来说,svnserve 内置的 CRAM-MD5 身份验证已经足够了。但是,如果您的服务器(以及您的 Subversion 客户端)使用 Cyrus Simple Authentication and Security Layer (SASL) 库构建,您将可以使用多种身份验证和加密选项。

通常,当 Subversion 客户端连接到 svnserve 时,服务器会发送一个问候语,其中列出了它支持的功能列表,客户端会以类似的功能列表进行响应。如果服务器配置为需要身份验证,它会发送一个挑战,其中列出了可用的身份验证机制;客户端通过选择其中一种机制进行响应,然后通过一些往返消息进行身份验证。即使没有 SASL 功能,客户端和服务器也天生知道如何使用 CRAM-MD5 和 ANONYMOUS 机制(参见 名为“内置身份验证和授权”的部分)。如果服务器和客户端链接到 SASL,则可能还提供许多其他身份验证机制。但是,您需要在服务器端显式配置 SASL 才能宣传它们。

使用 SASL 进行身份验证

要在服务器上激活特定的 SASL 机制,您需要执行两件事。首先,在您的存储库的 svnserve.conf 文件中创建一个 [sasl] 部分,其中包含一个初始键值对

[sasl]
use-sasl = true

其次,创建一个名为 svn.conf 的主 SASL 配置文件,放在 SASL 库可以找到的位置——通常在 SASL 插件所在的目录中。您需要在您的特定系统上找到插件目录,例如 /usr/lib/sasl2//etc/sasl2/。(请注意,这不是存储库中存在的 svnserve.conf 文件!)

在 Windows 服务器上,您还需要编辑系统注册表(使用 regedit 等工具)以告诉 SASL 在哪里找到内容。创建一个名为 [HKEY_LOCAL_MACHINE\SOFTWARE\Carnegie Mellon\Project Cyrus\SASL Library] 的注册表项,并在其中放置两个键:一个名为 SearchPath 的键(其值为包含 SASL sasl*.dll 插件库的目录的路径),以及一个名为 ConfFile 的键(其值为包含您创建的 svn.conf 文件的父目录的路径)。

由于 SASL 提供了如此多种身份验证机制,尝试描述每种可能的服务器端配置将是愚蠢的(并且远远超出了本书的范围)。相反,我们建议您阅读 SASL 源代码的 doc/ 子目录中提供的文档。它详细介绍了每种机制以及如何为每种机制适当地配置服务器。为了讨论的目的,我们将只演示配置 DIGEST-MD5 机制的简单示例。例如,如果您的 svn.conf 文件包含以下内容

pwcheck_method: auxprop
auxprop_plugin: sasldb
sasldb_path: /etc/my_sasldb
mech_list: DIGEST-MD5

您已告诉 SASL 向客户端宣传 DIGEST-MD5 机制,并检查位于 /etc/my_sasldb 的私有密码数据库中的用户密码。然后,系统管理员可以使用 saslpasswd2 程序在数据库中添加或修改用户名和密码

$ saslpasswd2 -c -f /etc/my_sasldb -u realm username

一些警告:首先,确保 saslpasswd2realm 参数与您在仓库的 svnserve.conf 文件中定义的 realm 相同;如果它们不匹配,身份验证将失败。此外,由于 SASL 的缺陷,公共 realm 必须是一个不包含空格的字符串。最后,如果您决定使用标准 SASL 密码数据库,请确保 svnserve 程序对该文件具有读访问权限(如果使用 OTP 等机制,可能还需要写访问权限)。

这只是配置 SASL 的一种简单方法。还有许多其他身份验证机制可用,密码可以存储在其他地方,例如 LDAP 或 SQL 数据库中。有关详细信息,请参阅完整的 SASL 文档。

请记住,如果您将服务器配置为仅允许某些 SASL 身份验证机制,则所有连接的客户端也必须支持 SASL。任何没有 SASL 支持的 Subversion 客户端(包括所有 1.5 之前的客户端)都无法进行身份验证。一方面,这种限制可能正是您想要的(我的客户端必须全部使用 Kerberos!”)。但是,如果您仍然希望非 SASL 客户端能够进行身份验证,请确保将 CRAM-MD5 机制作为选项进行宣传。所有客户端都可以使用 CRAM-MD5,无论它们是否具有 SASL 功能。

SASL 加密

如果特定机制支持,SASL 也可以执行数据加密。内置的 CRAM-MD5 机制不支持加密,但 DIGEST-MD5 支持,而 SRP 等机制实际上需要使用 OpenSSL 库。要启用或禁用不同级别的加密,您可以在仓库的 svnserve.conf 文件中设置两个值

[sasl]
use-sasl = true
min-encryption = 128
max-encryption = 256

min-encryptionmax-encryption 变量控制服务器要求的加密级别。要完全禁用加密,请将这两个值都设置为 0。要启用简单的数据校验和(即,防止篡改并保证数据完整性,但不进行加密),请将这两个值都设置为 1。如果您希望允许(但不强制)加密,请将最小值设置为 0,并将最大值设置为某个位长度。要无条件地要求加密,请将这两个值都设置为大于 1 的数字。在我们之前的示例中,我们要求客户端至少进行 128 位加密,但最多不超过 256 位加密。

通过 SSH 建立隧道

svnserve 的内置身份验证(和 SASL 支持)非常方便,因为它避免了创建真实系统帐户的需要。另一方面,一些管理员已经建立了完善的 SSH 身份验证框架。在这些情况下,所有项目的用户都拥有系统帐户,并且能够 SSH 登录 服务器机器。

将 SSH 与 svnserve 结合使用非常容易。客户端只需使用 svn+ssh:// URL 方案进行连接

$ whoami
harry

$ svn list svn+ssh://host.example.com/repos/project
[email protected]'s password:  *****

foo
bar
baz
…

在本例中,Subversion 客户端调用本地 ssh 进程,连接到 host.example.com,以用户 harryssh 的身份进行身份验证(根据 SSH 用户配置),然后在远程机器上以用户 harryssh 的身份启动一个私有的 svnserve 进程。 svnserve 命令在隧道模式下被调用 (-t),它的网络协议被 隧道化 通过 ssh(隧道代理)进行加密连接。如果客户端执行提交操作,经过身份验证的用户名 harryssh 将被用作新修订版本的作者。

这里需要理解的重要一点是,Subversion 客户端 没有 连接到正在运行的 svnserve 守护进程。这种访问方式不需要守护进程,也不需要在存在守护进程的情况下进行识别。它完全依赖于 ssh 启动临时 svnserve 进程的能力,该进程在网络连接关闭时终止。

当使用 svn+ssh:// URL 访问存储库时,请记住,是 ssh 程序提示进行身份验证,而不是 svn 客户端程序。这意味着没有自动密码缓存功能(参见 名为“Caching credentials”的部分)。Subversion 客户端通常会与存储库建立多个连接,尽管用户通常不会注意到这一点,因为存在密码缓存功能。但是,当使用 svn+ssh:// URL 时,用户可能会因 ssh 对每个出站连接重复询问密码而感到困扰。解决方法是在类 Unix 系统上使用单独的 SSH 密码缓存工具,例如 ssh-agent,或在 Windows 上使用 pageant

当通过隧道运行时,授权主要由操作系统对存储库数据库文件的权限控制;这与 Harry 通过 file:// URL 直接访问存储库的方式非常相似。如果多个系统用户将直接访问存储库,您可能希望将它们放入一个公共组中,并且需要小心 umask(请务必阅读本章后面 名为“Supporting Multiple Repository Access Methods”的部分)。但是,即使在隧道情况下,您仍然可以使用 svnserve.conf 文件来阻止访问,只需设置 auth-access = readauth-access = none 即可。[53]

你可能会认为 SSH 隧道的故事到此为止,但事实并非如此。Subversion 允许你在运行时的 config 文件中创建自定义隧道行为(参见 名为“运行时配置区域”的部分)。例如,假设你想要使用 RSH 而不是 SSH。[54] 在你的 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)。

[Warning] 警告

请注意,在定义基于 RSH 的隧道时,我们在隧道命令行中添加了 -- 选项结束参数。这样做是为了防止格式错误的主机名被视为隧道命令的另一个选项。你应该对其他隧道程序(例如,SSH)也这样做。

但你可以定义新的隧道方案,使其比这更智能

[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 公钥/私钥对,并且用户可以通过公钥认证登录。密码认证将不起作用,因为以下所有 SSH 技巧都围绕使用 SSH authorized_keys 文件。

如果不存在,请创建 authorized_keys 文件(在 Unix 上,通常为 ~/.ssh/authorized_keys)。此文件中的每一行都描述一个允许连接的公钥。这些行通常采用以下形式:

  ssh-dsa AAAABtce9euch… [email protected]

第一个字段描述密钥类型,第二个字段是密钥本身的 Base64 编码,第三个字段是注释。但是,鲜为人知的是,整行可以以 command 字段开头

  command="program" ssh-dsa AAAABtce9euch… [email protected]

当设置了 command 字段时,SSH 守护进程将运行指定的程序,而不是 Subversion 客户端请求的典型隧道模式 svnserve 调用。这为许多服务器端技巧打开了大门。在以下示例中,我们将文件中的行缩写为

  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_keys 文件中, 每行一个, 并使用 --tunnel-user 选项。

  command="svnserve -t --tunnel-user=harry" TYPE1 KEY1 [email protected]
  command="svnserve -t --tunnel-user=sally" TYPE2 KEY2 [email protected]

此示例允许 Harry 和 Sally 通过公钥认证连接到同一个账户。 他们每个人都有一个自定义命令会被执行; --tunnel-user 选项告诉 svnserve 假设命名参数是经过身份验证的用户。 如果没有 --tunnel-user, 则所有提交看起来都像是来自同一个共享系统账户。

最后要提醒一点: 通过公钥在共享账户中授予用户对服务器的访问权限, 即使你已经设置了 authorized_keys 中的 command 值, 也可能仍然允许其他形式的 SSH 访问。 例如, 用户可能仍然可以通过 SSH 获取 shell 访问权限, 或者能够通过你的服务器执行 X11 或通用端口转发。 为了尽可能减少用户的权限, 你可能需要在 command 后面立即指定一些限制性选项。

  command="svnserve -t --tunnel-user=harry",no-port-forwarding,no-agent-forw
arding,no-X11-forwarding,no-pty TYPE1 KEY1 [email protected]

请注意, 所有这些都必须在一行上——真正的一行上——因为 SSH authorized_keys 文件甚至不允许使用传统的反斜杠字符 (\) 来进行行延续。 我们之所以用换行符来显示它, 只是为了让它适应书本的物理页面。



[52] 参见 RFC 2195。

[53] 请注意, 使用任何形式的 svnserve 强制访问控制都是没有意义的; 用户已经可以直接访问仓库数据库。

[54] 我们实际上不推荐这样做, 因为 RSH 的安全性明显低于 SSH。

TortoiseSVN 官方中文版 1.14.7 发布