(?) Secure CVS - SSH tunnel problem

From jonathan soong

Answered By Thomas Adam, Ben Okopnik, Jason Creighton, Kapil Hari Paranjape

Hi Gang,

I have been trying to install CVS securely on a machine that will be live on the Internet.

There are two ways i was hoping to secure it:

  1. chroot jail - this has been done (there are plenty of HOW-TO's on the
  2. secure pserver (pserver is used to remotely login to CVS).

My problem is with (2) - securing pserver:

A common way of addressing this is to replace rsh with ssh, however AFAIK this requires shell accounts on the machine, a situation i _have to avoid.

(!) [Thomas] Why? Creating a "dummy" account is easy enough.

(?) The solution i have which seems feasible is:

Using pserver's user management, tunnelled over ssh with a generic ssh login and some sort of restricted shell.

I'm currently investigation this solution, however i'm not sure if there is a fundamental security flaw in this model, or what the restricted shell should look like.

I was wondering if you had any thoughts/opinions/suggestions on this? Or perhaps be able to point out a *much** easier way to secure it, that i missed!!

Any help would be much appreciated,


(!) [Thomas] If CVS is the only thing that the "users" will be using, then it is conceivable that you can have a "generic" login via SSH whereby this "user" has CVS as its default $SHELL.
While I am not particularly sure of the security implications that my following suggestion has, I think that you could do something like this:
  1. Create a generic account
  2. edit "/etc/shells" and add at the bottom "/usr/bin/cvs"
  3. Save the file.
  4. change the generic user's shell.
(at this point, I am wondering whether or not it is a good idea to create a "wrapper" account for this "new" shell, something like:

See attached shellwrap.thomas.bash.txt

And saving it as "/sbin/cvsshell", which you could then add to "/etc/shells" instead?
(!) [Ben] What happens when somebody suspends or "kill -9"s the shell? What new attack scenarios can you expect from this? What would happen if a local user launched this shell after hosing an environment variable (/a la/ the emacs/IFS attack scenario from the old days)?
(!) [Thomas] Errrm, I guess my answer to this is a bleak one...
(!) [Ben] It's probably best to just launch _shells that way and let those guys answer this kind of questions. :)
(!) [Thomas] Aye...
(Details of step 4.) That way when the user is created,
Then you can use "ssh" to login into the newly created user and the default shell would be CVS by default.
I'm not sure how secure this would be.......
Using "rbash" is not an option in this case.
In almost-as-we-hit-the-press news, it looks like pserver doesn't require the local user to have a useful shell, so /bin/false should work. According to the querent, anyway. I'm not preceisely sure of the configuration on the pserver side that leads to that, though. -- Heather
(!) [Thomas] Before using this, I am sure other people will flame me for it (hey Ben) :) but.......it is a learning curve for me too :)
(!) [Ben] Don't look at me, buddy. It's been at least, what, an hour since I've flamed you? I'm still in my refractory period.
(!) [Thomas] LOL, an hour? Is that all?? Things are looking up for me then :)
Hmmm, it was just an idea..... I'm curious as to whether it would work, minus some of the security implications......
(!) [Ben] To querent: I've never used CVS over SSH, etc., but you might want to take a look at "scponly" <http://www.sublimation.org/scponly/>;. It's designed for the kind of access you're talking about (if I understood you correctly), and is very flexible WRT user management (one anonymous user is fine, so are multi-user setups.)

(?) Hi guys,

Thanks for your help, i decided to implement it like so:

SECURE CVS without multiple unix accounts

  1. make user 'cvsd' who has r/w access to the CVS repository
  2. set 'cvsd's shell to /bin/bash (or some proper shell) in /etc/passwd
  3. set 'cvsd's password to * in /etc/shadow
  4. have all developers who are using the CVS generate an ssh key
  5. put an entry in 'cvsd's /home/cvsd/.ssh/authorized_keys2 file that looks like:

Now only those developers who have sent you keys will be able to login (passwordless) to the CVS machine and will be automatically be dumped to sleep for 3 hours - this will keep the ssh port forward open.

(!) [Thomas] Sounds like a good idea this way.

(?) Now i can securely use CVS's pserver user management, without multiple unix users.

Anyone have any thoughts on the security implications of forcing the users to execute 'sleep 3h' e.g. can this be broken by sending weird signals?

(!) [Thomas] Assuming that the command "sleep 3h" is spawned once the user logs in, then as with any process this can be killed by doing:
kill -9 $(pidof "sleep 3h")
(I have seen the command "pidof" on Debian, SuSE and RH -- it might not be distributed with Slackware as this claims to be more POSIX compliant, something that "pidof" is not).
(!) [Jason] Sure enough, slackware 8.1 has this command: (And, just for the record, Slackware is more BSD-ish. I've never heard a claim that it is more POSIX compliant.)
~$ about pidof
/sbin/pidof:    symbolic link to killall5
/sbin/killall5: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV),
dynamically linked (uses shared libs), stripped
~$ greppack killall5
(Of course, to use the 'about' and 'greppack' scripts, you'd have to ask me to post them.)
Last I recall POSIX was a stnadard that declared minimum shell and syscall functionality, so I don't see why it would insist on having you leave a feature out. In fact "minimum" is the key since merely implementing POSIX alone doesn't get a usable runtime environment, as proved by Microsoft. -- Heather
(!) [Thomas] The more traditional method, is to use something like....
kill -9 $(ps aux | grep "sleep\ 3h" | grep -v "sleep\ 3h" | awk '{print
If this happens then the rest of your command will fail.
The security implications of this, is that the rest of the command will never get executed. I came up with a "bash daemon" script three years ago that would re-spawn itself by "exec loop4mail $!" which used the same process number as the initial "loop4mail &" command.
Security was not paramount in that case.
If the command is killed, then the users will most likely be left dangling at the Bash prompt.....
(!) [Ben] Well, the "about" script is rather obvious,
(!) [Jason] Basically, the only thing it does is follow symlinks recursivly, and calls "file" with a full list.
(!) [Thomas] Hmmm, I have a similar script to yours that you describe here, Jason, except that mine "traverses" the symlinks until file returns anything != to another symlink. If it does, then it keeps traversing.
(!) [Jason] Okay, I think I see what you're saying now: A symlink will never point to more than one thing. Therefore, we could solve the problem with a loop, breaking out of it when there are no more symlinks to process. Recursion is not required.
Hmm... that's interesting. However, I already wrote the recursive version already, so I'll stick with that. :-)
If a symlink doesn't point to anything, it will fail a test for file existance:
~/tmp$ ln -s doesnotexist symlink
~/tmp$ ls -l
total 0
lrwxrwxrwx    1 jason    users          12 May 27 10:46 symlink ->
~/tmp$ [ -e symlink ] && echo "symlink exists"
Circular symlinks are fun too.......
(!) [Thomas] My logic in this is simple in that a symlink must point to a physical store of data, albeit a directory, file, block file, etc. Also, you might want to look at the program "chase" which is rather useful in these situations too.
(!) [Jason] Haven't heard of that one and it's not on my system.
(!) [Kapil] Two programs that are useful to traverse symlinks come with standard distributions: namei (util-linux) and readlink (coreutils/fileutils)
	$ namei /usr/bin/vi
	f: /usr/bin/vi
	 d /
	 d usr
	 d bin
	 l vi -> /etc/alternatives/vi
	   d /
	   d etc
	   d alternatives
	   l vi -> /usr/bin/nvi
	     d /
	     d usr
	     d bin
	     - nvi
	$ readlink -f /usr/bin/vi
(!) [Thomas] This feature might be superfluous to your initial script, but I find it quite useful. "find" is a very powerful utility.
So I shall extend you the same offer, and say that I'll post you my script, if you like.... :)
(!) [Ben] ...but "greppack" has to do with Slackware's package management...
(!) [Jason] Bingo. All it does is print the name of a file if a regex matches somewhere in it, because Slackware's package "management" is quite simple.
[time passes]
I was just looking at the options for 'grep' and it turns out that I could just call grep, like so:
grep killall5 -l /var/log/packages/*
'-l' causes grep to print the names of the files that match, not the lines that match.

Jason Creighton, CEO of Wheel Reinvention Corp.
(Our motto: "Code reuse is silly")
(!) [Ben] ... and so would not be anything like Debian - where you'd just do "dpkg -S killall5" to find out the package it came from. I'll say this: in almost everything I've ever thought to ask of a packaging system, between "dpkg", "apt-get", and "apt-cache", Debian has a good, well-thought-out answer. The one thing that's not handled - and I don't really see how it could be without adding about 5MB that most folks would never use - is looking up a file that's in the Debian distro but is not installed on my system. I handle that by downloading the "Contents-i386.gz" file once every few months and "zgrep"ping through it; it's saved my bacon many, many times when a compile went wrong.
(!) [Kapil] To make this lookup faster you may want to install "dlocate" which is to "dpkg" (info part) what "locate" is to "find".
(!) [Ben] Cool - thank you! That was my one minor gripe about "dpkg" - on my system, it takes about 20 seconds (which is years in computer time :) to look things up.
(!) [Kapil] And for those with network connectivity:
Contains a search link as well.
(!) [Ben] Unfortunately, that does not describe me very well. :( Otherwise, I'd just have written a little Perl interface to the search page and been done with it. Instead, I download a 5MB or so file when I have good connectivity so I have it to use for the rest of the time.