The setup described here uses the combination of two well-established
techniques, namely running CVS pserver in a chroot jail, and only
allowing access to the server from the
through a SSH tunnel.
This HOWTO was written with the assumption that the server will operate on FreeBSD.
<email@example.com>, Dmitry Karasik
<firstname.lastname@example.org>, John Polstra
<email@example.com>, and Phil Regnauld
<firstname.lastname@example.org>read the draft version of this HOWTO and made useful suggestions and corrections, both factual and stylistic.
Initially, CVS was designed to provide version control for a group of
developers working on a single machine. There was a CVS
repository somewhere on the file system, and every developer
working on a project had to have a read-write access to the repository
files, including auxiliary files used by
With the advent of the Internet the need for accessing remote repositories arose.
Currently, CVS has several methods of doing this:
access methods make use of a remote shell
:ext: method only) a replacement, such as
the secure shell (SSH).
The developer accessing the repository using this method must have a shell account on the machine where the repository is located.
access method allows a direct client connection to the server using
password authentication. The
daemon on the server machine starts the command
when it receives a TCP connection on a specific port (usually port
2401). All communication between the client and the server goes using
This method does not require every developer to have an account on the server box, but no communication over the network is encrypted, including passwords which are sent as cleartext (well, almost).
These methods allow secure direct client connection. The
uses GSSAPI, the generic interface to network security systems such as
Kerberos 5. The
methods uses the Kerberos version 4 network security system.
:pserver:method is quite convenient for implementing remotely accessible CVS repositories, but its major drawback is that it is very insecure.
Two ways to increase the security of
One of the main problems with
cvs pserver is the fact that
the developer accessing the repository can in fact gain an access to any
file on the server system. This situation gets worse if the developer
has read-write access to the repository; in this case it is possible
that he/she will have the ability to write any file owned be the user
pserver runs as. But anyway, even read access to your
system can be bad enough!
The standard solution for this type of services is to use an
intermediate program that first carries out a
UNIX syscall, then changes its own credentials to those of some
unpriviledged user, and
A method implementing this technique to set up a CVS server is described here.
The second major problem with the
cvs pserver is that all
data transferred during the CVS remote session is unencrypted.
:kserver: access methods
do not have this problem, if setup properly. However, I do not consider
them here --- this HOWTO is only concerned with
:pserver: access method.
Luckily, the popular secure replacement of
ssh, has a feature called
TCP port forwarding, also known as tunnelling.
The idea is that
ssh client allocates a socket listening to
the specified port on the local machine, and any connection made to this
local port (normally from
localhost) is automatically
forwarded to the remote side over an encrypted channel. If you have
never used this feature but you are a user of
are that you have used it anyway without knowing it ---
ssh, in its most typical configuration, automatically
forwards all X11 connections this way.
Since I was not able to find such a tutorial, I decided to write it myself. So this HOWTO is in fact the by-product of that installation.
The computer should:
sshdup and running;
cvsinstalled (1.10 will suffice);
The instructions below are fairly detailed to allow even very inexperienced sysadmin to setup the server.
Since CVS server will not run as root, we need to create a special cvs user. I use UID 287, username cvs, and GID 287, groupname cvs. Of course you are free to use any UID you like, but remember that using existing real or system UIDs and GIDs is generally a bad idea.
Needless to say that this operation must be performed as root.
To add new robotic users I find it easier to use
vipw editor, add the following line:
cvs:*:287:287::0:0:CVS account:/usr/local/site/cvsroot:/sbin/nologinOf course, you might choose a different home directory for the cvs user.
Also note, that ``password disabled'' star and
/sbin/nologin shell are here temporarily. Subsequently,
they will be replaced with ``no password'' void and a special ``sleeping
beauty'' shell, written by Tim
/etc/group file using your favorite text editor, and
add the following line:
mkdir -p /usr/local/site/cvsroot chown cvs.cvs /usr/local/site/cvsroot chmod 700 /usr/local/site/cvsrootIf you would like to have less restricted permissions for the cvs user home directory, do so.
The CVS server will be
right into cvs user home directory. It is trivial to modify the
steps below if you would like to make a jail in a subdirectory of the
cd /usr/local/site/cvsroot mkdir bin dev etc tmp chown cvs.cvs bin dev etc tmp chmod 555 bin dev etc
For a project called projectname, create a repository in a usual way, as follows.
cvs -d /usr/local/site/cvsroot/projectname init chown -R cvs.cvs projectnameIf you plan to serve several distinct repositories, repeat this step as necessary.
cd dev mknod null c 2 2 chown 0.0 null chmod 666 null cd ..
Ideally, you should have the complete sources of your FreeBSD system installed, since this makes this process so much easier. If it is not an option for you, do this step in some other way.
Before proceeding, you will need to change just one line of CVS sources.
The trouble is, that for some unknown reason, FreeBSD library function
which uses syscall
internally, likes to spit out error messages on
stderr in case
of trouble. CVS server uses
unconditionally, though it should better not do it in our setup.
We will edit
cd /usr/src/contrib/cvs/src vi server.cFind the line
initgroups (pw->pw_name, pw->pw_gid);and comment it out:
/* initgroups (pw->pw_name, pw->pw_gid); */Save the file.
Now compile and install the
cd /usr/src/gnu/usr.bin/cvs make NOSHARED=yes cp /usr/obj/usr/src/gnu/usr.bin/cvs/cvs/cvs /usr/local/site/cvsroot/bin cd /usr/local/site/cvsroot chown cvs.cvs bin/cvs chmod 500 bin/cvs
We need some
group files in
cd etc vi master.passwdNote the absence of '/' above. Add the line
cvs::287:287::0:0:CVS account:/:/bin/cvsThe shell line can be anything, but it is better if nobody including
cvsprogram itself can write to it. Save the file.
vi groupAdd the line
cvs:*:287:Save the file. Now run
pwd_mkdb -d . master.passwd cd ..
The actual access control is done by CVS itself. In order to make use
of this, we need to create
files in the
CVSROOT directory in every CVS repository we
There are two ways of doing this for
writers file, and I
will show them both. Only the first method is working for
passwd file due to possible security issues.
cd projectname/CVSROOT vi passwdIf you are planning to provide anonymous read-only access to the projectname repository, add the following line:
cvs::cvsFor every non-anonymous developer add the line of the form:
developerusername:encryptedpassword:cvsWhere encryptedpassword is obtained from the cleartext password (which either yourself or a particular developer has chosen) with the standard system
crypt(3) function. If you do not know how to obtain encrypted version of a password yourself, download this program, and compile it:
cd directory/with/ppw.c cc -o ppw ppw.c -lcryptIt is important that you compile and run this program on the very same machine on which CVS server will be installed.
Launch this program as
./ppw md5 if your system passwords
are MD5-based. If you have DES passwords, launch it as
des. If you have some other password system, you are on your own
at this step.
If you don't know what kind of passwords your local
function supports, you might want to have a look at the
/etc/master.passwd file. If the password field for user
accounts starts with
$1$, you have MD5-based passwords. If
there is no
$ sign at the beginning of the password field,
you have DES passwords. Otherwise, you have something else.
cd /usr/local/site/cvsroot/projectname/CVSROOT vi writersAdd the line of the form
developerusernamefor every developer who will have the write access to the projectname repository. Save the file.
Do not forget to
writers files to
This method is better than the previous one because you save the history
of changes made to
writers. But you have to use manual
creation method for
passwd file anyway.
cd to some (preferably empty) directory and
issue the following command:
cvs -d /usr/local/site/cvsroot/projectname checkout .The current copy of the whole repository will be checked out.
cd CVSROOT vi writersAdd the lines to this file as described above and save the file. Issue the commands:
cvs add writers cvs commit -m initial cd .. cvs -Q release -d CVSROOT rm -rf CVSThen go back to
/usr/local/site/cvsroot/projectname/CVSROOTand change the owner of the files back to cvs. Note then you will not need to do it later, when the system will be fully operational --- you will simply use remote checkouts and commits instead of doing this locally as root.
Download this program and modify it according to the instructions in the source code (this program is a modified for FreeBSD version of the wrapper suggested here).
cc -o run-cvs run-cvs.cInstall it somewhere, make it executable, owned by root.wheel, and make sure nobody except root is able to modify the binary. I keep mine in
Here you will have to make an important choice. If you ever going to use ``remote'' access to repositories locally from the box where the server operates, you will need to choose a TCP port different from CVS default (2401).
Add the following line to
cvspserver stream tcp nowait root /usr/local/site/sbin/run-cvs run-cvsand save the file.
Choose the port value. I use 2410. Add the port description into
cvssshpserver 2410/tcp #CVS super-duper secure network serverThen add the following line to
cvssshpserver stream tcp nowait root /usr/local/site/sbin/run-cvs run-cvs
kill -HUP `cat /var/run/inetd.pid`If for whatever reason you will have to restart
inetdcompletely, make sure you have rather empty environment. In particular, having
HOMEvariable set when you run
inetdwill hit you badly later.
Download this program (again, a slightly
modified version of the original Tim TimeWaster's source).
Compile it and install it somewhere. I use
cc -o zzh zzh.c chown 0.0 zzh chmod 555 zzh mv zzh /usr/local/site/binNow use
vipw(8)again and change the original cvs user line to this:
cvs::287:287::0:0:CVS account:/usr/local/site/cvsroot:/usr/local/site/bin/zzhThe sleeping beauty shell's job is to control the SSH tunnel connection. It is there basically to prevent the nasty ``The following connections are still open'' message from SSH on the client side.
The SSH tunnel will work well and good, but now we need a way to disable somehow normal CVS pserver operations. There are several ways to do it, and here I will describe two of them:
This method is just plain great if you are already using
ipfw on the server machine.
Just disable TCP connections to your run-cvs port (2401, or 2410 and 2401, see step 9) coming through external interfaces, like in this example:
ipfw add 2000 deny log tcp from any to any 2401 via fxp0 ipfw add 2000 deny log tcp from any to any 2410 via fxp0
In recent versions of FreeBSD the
inetd daemon has built-in
TCP wrappers support. In even more recent versions (>= 3.3)
everything is controlled by a single file,
/etc/hosts.allow. Here is what you put at the beginning of
run-cvs : localhost : allow run-cvs : server.host.server.domain : allow run-cvs : ALL : denyOf course you have to put your real server DNS name (or IP address) there.
You will have to restart
in order to have TCP wrappers enabled. Also put
Since the cvs account has no password, you have to enable SSH
connections with empty passwords. Put this line into your
PermitEmptyPasswords yesand restart
kill -HUP `cat /var/run/sshd.pid`
The server setup has been completed.
scvsPerl program and make modifications to it. All the tunable variables are at the top of the file, with comments. Again, this is the slightly modified version of the program developed by Tim TimeWaster.
Do not forget to change the shebang line (
Install it somewhere in your system path. I use
chown 0.0 scvs chmod 555 scvs mv scvs /usr/local/site/binNow you a ready to go. To test the setup, run
scvs -d :pserver:email@example.com/projectname loginIf everything is set correctly up, you will be presented with a prompt for developername CVS password.
After entering the password, you continue to use
program as if it was a normal
cvs command: you can
checkout, update, commit files, and execute other CVS commands.
sshclient for Windows NT, so that connection forwarding is supported, can be non-trivial. At the very least, you should be using the most recent version of Cygwin Unix Compatibility toolkit. You will also have to edit
scvsa bit more, since the paths will almost definitely be very different.
Write me if you wish I present more detailed information here.
If you have any questions or suggestions, write E-mail to me using this address: Anton Berezin <firstname.lastname@example.org>.
Strangely, the online CVS manual is located elsewhere.
The instructions on setting up chrooted CVS pserver (no ssh tunnelling) can be found on www.unixtools.org.
I also used Tim TimeWaster's page describing the setup of SSH tunnel for CVS pserver access (the page is a bit unclear at times, at least for my level of UNIX system administration).
The FreeBSD project website was my source of online UNIX manual pages referenced in the text.