The svnserve program is a lightweight
server, capable of speaking to clients over TCP/IP using a
custom, stateful protocol. Clients contact an
svnserve server by using URLs that begin with
the svn://
or svn+ssh://
schema. This section will explain the different ways of running
svnserve, how clients authenticate themselves
to the server, and how to configure appropriate access control
to your repositories.
There a few different ways to invoke the
svnserve program. If invoked with no
options, you'll see nothing but a help message. However, if
you're planning to have inetd launch the
process, then you can pass the -i
(--inetd
) option:
$ svnserve -i ( success ( 1 2 ( ANONYMOUS ) ( edit-pipeline ) ) )
When invoked with the --inetd
option,
svnserve attempts to speak with a
Subversion client via stdin and
stdout using a custom protocol. This is
the standard behavior for a program being run via
inetd. The IANA has reserved port 3690
for the Subversion protocol, so on a Unix-like system you can
add lines to /etc/services
like these (if
they don't already exist):
svn 3690/tcp # Subversion svn 3690/udp # Subversion
And if your system is using a classic Unix-like
inetd daemon, you can add this line to
/etc/inetd.conf
:
svn stream tcp nowait svnowner /usr/local/bin/svnserve svnserve -i
Make sure “svnowner” is a user which has appropriate permissions to access your repositories. Now, when a client connection comes into your server on port 3690, inetd will spawn an svnserve process to service it.
A second option is to run svnserve as a
standalone “daemon” process. Use the
-d
option for this:
$ svnserve -d $ # svnserve is now running, listening on port 3690
When running svnserve in daemon mode,
you can use the --listen-port=
and
--listen-host=
options to customize the exact
port and hostname to “bind” to.
There's still a third way to invoke
svnserve, and that's in “tunnel
mode”, with the -t
option. This mode
assumes that a remote-service program such as
RSH or SSH has
successfully authenticated a user and is now invoking a
private svnserve process as that
user. The svnserve program
behaves normally (communicating via stdin
and stdout), and assumes that the traffic
is being automatically redirected over some sort of tunnel
back to the client. When svnserve is
invoked by a tunnel agent like this, be sure that the
authenticated user has full read and write access to the
repository database files. (See Servers and Permissions: A Word of Warning.) It's essentially the same as
a local user accessing the repository via
file:///
URLs.
Once the svnserve program is running,
it makes every repository on your system available to the
network. A client needs to specify an
absolute path in the repository URL. For
example, if a repository is located at
/usr/local/repositories/project1
, then a
client would reach it via svn://host.example.com/usr/local/repositories/project1
. To increase security, you can pass the
-r
option to svnserve,
which restricts it to exporting only repositories below that
path:
$ svnserve -d -r /usr/local/repositories …
Using the -r
option effectively
modifies the location that the program treats as the root of
the remote filesystem space. Clients then use URLs that
have that path portion removed from them, leaving much
shorter (and much less revealing) URLs:
$ svn checkout svn://host.example.com/project1 …
When a client connects to an svnserve process, the following things happen:
The client selects a specific repository.
The server processes the repository's
conf/svnserve.conf
file, and begins to
enforce any authentication and authorization policies defined
therein.
Depending on the situation and authorization policies,
the client may be allowed to make requests anonymously, without ever receiving an authentication challenge, OR
the client may be challenged for authentication at any time, OR
if operating in “tunnel mode”, the client will declare itself to be already externally authenticated.
At the time of writing, the server only knows how to send a CRAM-MD5 [21] authentication challenge. In essence, the server sends a bit of data to the client. The client uses the MD5 hash algorithm to create a fingerprint of the data and password combined, then sends the fingerprint as a response. The server performs the same computation with the stored password to verify that the result is identical. At no point does the actual password travel over the network.
It's also possible, of course, for the client to be externally authenticated via a tunnel agent, such as SSH. In that case, the server simply examines the user it's running as, and uses it as the authenticated username.
As you've already guessed, a repository's
svnserve.conf
file is the central
mechanism for controlling authentication and authorization
policies. The file has the same format as other configuration
files (see the section called “Runtime Configuration Area”): section names
are marked by square brackets ([
and
]
), comments begin with hashes
(#
), and each section contains
specific variables that can be set (variable =
value
). Let's walk through this file and learn how
to use them.
For now, the [general]
section of the
svnserve.conf
has all the variables you
need. Begin by defining a file which contains usernames and
passwords, and an authentication realm:
[general] password-db = userfile realm = example realm
The realm
is a name that you define.
It tells clients which sort of “authentication
namespace” they're connecting to; the Subversion
client displays it in the authentication prompt, and uses
it as a key (along with the server's hostname and port)
for caching credentials on disk (see the section called “Client Credentials Caching”.) The
password-db
variable points to a
separate file that contains a list of usernames and
passwords, using the same familiar format. For
example:
[users] harry = foopassword sally = barpassword
The value of password-db
can be an
absolute or relative path to the users file. For many
admins, it's easy to keep the file right in the
conf/
area of the repository, alongside
svnserve.conf
. On the other hand, it's
possible you may want to have two or more repositories share
the same users file; in that case, the file should probably
live in a more public place. The repositories sharing the
users file should also be configured to have the same realm,
since the list of users essentially defines an
authentication realm. Wherever the file lives, be sure to
set the file's read and write permissions appropriately. If
you know which user(s) svnserve will run
as, restrict read access to the user file as necessary.
There are two more variables to set in the
svnserve.conf
file: they determine what
unauthenticated (anonymous) and authenticated users are
allowed to do. The variables anon-access
and auth-access
can be set to the values
none
, read
, or
write
. Setting the value to
none
restricts all access of any kind;
read
allows read-only access to the
repository, and write
allows complete
read/write access to the repository. For example:
[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
The example settings are, in fact, the default values of the variables, should you forget to define them. If you want to be even more conservative, you can block anonymous access completely:
[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
Notice that svnserve only understands “blanket” access control. A user either has universal read/write access, universal read access, or no access. There is no detailed control over access to specific paths within the repository. For many projects and sites, this level of access control is more than adequate. However, if you need per-directory access control, you'll need to use Apache instead of svnserve as your server process.
svnserve's built-in authentication can be very handy, because it avoids the need to create real system accounts. On the other hand, some administrators already have well-established SSH authentication frameworks in place. In these situations, all of the project's users already have system accounts and the ability to “SSH into” the server machine.
It's easy to use SSH in conjunction with
svnserve. The client simply uses the
svn+ssh://
URL schema to connect:
$ whoami harry $ svn list svn+ssh://host.example.com/repos/project harry@host.example.com's password: ***** foo bar baz …
What's happening here is that the Subversion client is
invoking a local ssh process, connecting to
host.example.com
, authenticating as the user
harry
, then spawning a private
svnserve process on the remote machine,
running as the user harry
. The
svnserve command is being invoked in tunnel
mode (-t
) and all network protocol is being
“tunneled” over the encrypted connection by
ssh, the tunnel-agent.
svnserve is aware that it's running as the user
harry
, and if the client performs a commit,
the authenticated username will be attributed as the author of
the new revision.
When running over a tunnel, authorization is primarily
controlled by operating system permissions to the repository's
database files; it's very much the same as if Harry were
accessing the repository directly via a
file:///
URL. If multiple system users are
going to be accessing the repository directly, you may want to
place them into a common group, and you'll need to be careful
about umasks. (Be sure to read the section called “Supporting Multiple Repository Access Methods”.) But even in the case of
tunneling, the svnserve.conf
file can
still be used to block access, by simply setting
auth-access = read
or auth-access
= none
.
You'd think that the story of SSH tunneling would end
here, but it doesn't. Subversion allows you to create custom
tunnel behaviors in your run-time config
file (see the section called “Runtime Configuration Area”.) For example,
suppose you want to use RSH instead of SSH. In the
[tunnels]
section of your
config
file, simply define it like
this:
[tunnels] rsh = rsh
And now, you can use this new tunnel definition by using a
URL schema that matches the name of your new variable:
svn+rsh://host/path
. When using the new
URL schema, the Subversion client will actually be running the
command rsh host svnserve -t behind the
scenes. If you include a username in the URL (for example,
svn+rsh://username@host/path
) the client
will also include that in its command (rsh
username@host svnserve -t.) But you can define new
tunneling schemes to be much more clever than that:
[tunnels] joessh = $JOESSH /opt/alternate/ssh -p 29934
This example demonstrates a couple of things. First, it
shows how to make the Subversion client launch a very specific
tunneling binary (the one located at
/opt/alternate/ssh
) with specific
options. In this case, accessing a
svn+joessh://
URL would invoke the
particular SSH binary with -p 29934
as
arguments—useful if you want the tunnel program to
connect to a non-standard port.
Second, it shows how to define a custom environment
variable that can override the name of the tunneling program.
Setting the SVN_SSH
environment variable is
a convenient way to override the default SSH tunnel agent.
But if you need to have several different overrides for
different servers, each perhaps contacting a different port or
passing a different set of options, you can use the mechanism
demonstrated in this example. Now if we were to set the
JOESSH
environment variable, its value
would override the entire value of the tunnel
variable—$JOESSH would be executed instead of
/opt/alternate/ssh -p 29934
.
A final note: when using svn+ssh://
URLs to access a repository, remember that it's the
ssh program prompting you for
authentication, and not the
svn program. That means there's no
automatic password caching going on (see the section called “Client Credentials Caching”). If you want to prevent
ssh from repeatedly asking for your
password, you'll need to use a separate memory-caching tool
like ssh-agent on a Unix-like system, or
pageant on Windows.