[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: chrooted cvs
On Fri, 30 Mar 2001, Tillman Hodgson wrote:
> Howdy,
> Whats the preferred way to run a secure (chrooted preferably) cvs server
> acting as a source repository only (i.e, compilation will take place on other
> systems) for trusted authenticated users & anonymous read-only access?
Here's how I did it for my Linux box.
# CVS chroot installation instructions.
# Copyright(C) 2000 Heikki Korpela for Fountain Park Ltd. All Rights Reserved.
# This file may not be distributed in any format,
# seperately or within another package, without
# a prior written permission from the author.
# ----------------------------------------------------------------------
# This is NOT an RPM spec file. (Although
# the instructions should apply for an RPM,
# too.)
# TODO: There are some unfixed incompatibilities
# here; in specific:
# o the OpenBSD's check for -lcrypt
# fails for some reason (and yes, I tried with --enable-shared
# and without -static -s)
# o there are some -v options in rm, ln and cp (which only work
# in GNU versions of these programs)
# o I've left cvsroot-fp in places instead of a <cvsroot>
# o I've left FP internal IP addresses in places
# I have used the following documents as
# helpers for these instructions:
# http://guv.ethz.ch/~flip/cvsd/
# http://www.badgertronics.com/writings/cvs/chroot.html
# http://www.cvshome.org/cyclic/cyclic-pages/security.html
# http://www.linuxdoc.org/HOWTO/CVS-RCS-HOWTO.html
# http://www.kegel.com/cvsadmin.html
# http://www.securityfocus.com/archive/1/72584
# ----------------------------------------------------------------------
# OpenBSD version of CVS sources:
{
mkdir --verbose -p obsd-cvs
cd obsd-cvs
wget --recursive --no-parent ftp://ftp.sunet.se/pub/OpenBSD/src/gnu/usr.bin/cvs
# (the -v option only applies to GNU rm)
find . -name CVS -a -type d | xargs rm -rvf
# XXX TODO: Temporary hack to disable OpenBSD's changes to
# Search for the Sacred Crypt(3); don't uncomment even on OpenBSD,
# please rerun autoheader and autoconf instead
rm -f configure
cd ..
}
# The official CVS version:
{
ncftpget ftp://ftp.cvshome.org/pub/cvs-1,11/cvs-1.11.tar.gz
tar zxvvfp cvs-1.11.tar.gz
# XXX TODO: Temporary hack to disable OpenBSD's changes to
# Search for the Sacred Crypt(3) - UNCOMMENT IF YOU RUN OPENBSD
cp cvs-1.11/configure.in obsd-cvs/
}
diff -uNr cvs-1.11 obsd-cvs > obsd-cvs.patch
# ----------------------------------------------------------------------
# The following SRPMS: (unpack them with e.g. rpm2cpio | cpio -d -v -m -i,
# and copy the patches somewhere)
# Conectiva: cvs-1.10.8-4cl
# Mandrake: cvs-1.11-5mdk
# RedHat: cvs-1.11-3
# Trustix: cvs-1.10.8-8
# ----------------------------------------------------------------------
cd cvs-1.11
# The following patches are to be applied to the official version:
# OpenBSD (various, including info patches, getdate patch, patch to
# make ssh default instead of rsh, a fix to handle replica+real
# repositories better, patch to add read-only support)
patch -p1 < ../obsd-cvs.patch
# Mandrake
# MMAP support ; I recommend sys-zlib patch if you don't
# want mmap.
# (FAILS out of box, affects configure.in)
# commit_readonly reports problems with munmap,
# as do a bunch of imports)
# patch -p0 < ../cvs-srpms/mdk/cvs-1.10.7-mmap.patch
# Fixed to:
# patch -p1 < ../cvs-srpms/mdk/cvs-1.11-mmap.patch
# Patch to use system zlib - mutually exclusive with mmap
# (FAILS out of box, affects configure.in)
patch -p1 < ../cvs-srpms/mdk/cvs-1.10.8-zlib.patch
autoheader
autoconf
# Trustix
# Patch to clear errno in while loops
patch -p1 < ../cvs-srpms/trustix/cvs-1.10.8-errno.patch
# Conectiva (fix to use mktemp - so this assumes the system
# has mktemp, like any reasonably secure system should)
patch -p1 < ../cvs-srpms/cl/cvs-1.10-tmprace.patch
# RH
# Fixes some unlink calls to unlink_file calls and adds
# some existence_error checks
# (FAILS out of box)
# patch -p1 < ../cvs-srpms/rh/cvs-1.11-existence.patch
# Fixed to:
patch -p1 < ../cvs-srpms/rh/cvs-1.11-fixed-existence.patch
# Adds some security to file opening (checks for mode
# attempts that differ from a/w and checks for
# open() success); - seems to cause errors
# with chrooting, so commented out
# patch -p1 < ../cvs-srpms/rh/cvs-1.11-security.patch
# My own.
# Patch to use mkstemp instead of the stinking tempnam:
# ('coz I'm lazy, I'm skipping the checks for the mkstemp in
# config.in, so make sure you have it. If you don't, get it.
# You should have it anyways.)
patch -p1 < ../cvs-1.11-mkstemp.patch
# Fix prog-exploits:
# see http://www.securityfocus.com/archive/1/72584
patch -p1 < ../cvs-1.11-progexploit.patch
# ----------------------------------------------------------------------
# Create the chroot directories
# (Change <project> to project name.)
sudo mkdir -p --verbose /chroot/cvs
sudo mkdir -p --verbose /chroot/cvs/cvsroot-<project>
sudo mkdir -p --verbose /chroot/cvs/{etc,bin,lib,tmp,dev}
# ----------------------------------------------------------------------
# Configure and compile CVS
# (CFLAGS acquired jointly from Trustix rpm SRPM
# (the patch to rpmrc) and Athlon GCC.)
# Please note that I would recommend stack boundary of 2
# for compiling kernels. Also, -mpreferred-stack-boundary doesn't
# seem to work for some versions of gcc.
# export CFLAGS=" -Wall -O3 -march=i586 -fomit-frame-pointer -fno-exceptions -fno-rtti -pipe -s -ffast-math -fexpensive-optimizations -malign-loops=4 -malign-jumps=4 -malign-functions=4 -mpreferred-stack-boundary=4 -funroll-loops -static "
export CFLAGS=" -Wall -O3 -march=i586 -fomit-frame-pointer -fno-exceptions -fno-rtti -pipe -s -ffast-math -fexpensive-optimizations -malign-loops=4 -malign-jumps=4 -malign-functions=4 -funroll-loops -static "
./configure --prefix=/chroot/cvs --enable-static \
--disable-shared --enable-server --disable-client --enable-encryption
make
sudo make install
PATH=$PATH:/sbin:/usr/sbin
# ----------------------------------------------------------------------
# Create a minimal password file in '/chroot/cvs/etc/passwd'.
# Add cvsowner and cvs. They don't need a home or
# a shell.
sudo /usr/sbin/groupadd -g 6000 cvs
sudo /usr/sbin/useradd -c "CVS pseudo account" -g cvs -d / -s /bin/false -M cvs -u 6000
sudo passwd cvs
# Enter password..
sudo useradd -c "CVS pseudo account" -g cvs -d / -s /bin/false -M cvsowner -u 6000
sudo passwd cvsowner
# Enter password..
# Disable the accounts
sudo usermod -e 1970-01-01 -f 0 -L cvs
sudo usermod -e 1970-01-01 -f 0 -L cvsowner
# (You might also want to fix /etc/ssh/sshd_config)
# ----------------------------------------------------------------------
# Fix permissions
sudo chown -R 6000.6000 /chroot/cvs
sudo chmod -R 700 /chroot/cvs
sudo chmod 755 /chroot/cvs
sudo chmod -R 755 /chroot/cvs/dev
sudo chmod -R 777 /chroot/cvs/tmp
# ----------------------------------------------------------------------
# Move the password and group entries to /chroot:
# (passwd, group, shadow)
# *** NB *** This may disrupt important system files *** NB ***
sudo bash
mkdir /tmp/etc
for f in /etc/passwd /etc/group /etc/shadow; do
cp $f $f.bak
grep 'cvs' $f > /chroot/cvs$f
grep -v cvs $f > /tmp/$f
mv -f /tmp/$f $f
done
exit
# These should be unneeded
sudo userdel cvs
sudo userdel cvsowner
sudo groupdel cvs
# ----------------------------------------------------------------------
# Make device entries:
# (The options might differ for your system; consult null(4))
sudo mknod -m 666 /chroot/cvs/dev/zero c 1 3
sudo mknod -m 666 /chroot/cvs/dev/null c 1 5
sudo chown root:mem /chroot/cvs/dev/null /chroot/cvs/dev/zero
# Copy and link libraries:
sudo bash
{
cd /chroot/cvs/lib
cp -apvf /lib/libc-2.*.so .
ln -svf libc-2.*.so libc.so.6
cp -apvf /lib/ld-2.*.so .
ln -svf ld-2.*.so ld-linux.so.2
cp -apvf /lib/libpam* .
cp -apvf /lib/libdl* .
cp -apvf /lib/libnsl-* .
ln -svf libnsl-* libnsl.so
ln -svf libnsl-* libnsl.so.1
cp -apvf /lib/libcrypt-* .
ln -svf libcrypt-* libcrypt.so.1
cp -aprvf /lib/security .
cp -apvf /lib/libnss_files* .
cp -apvf /usr/lib/libz*
}
# ----------------------------------------------------------------------
# Copy pam.d support files
{
cd /chroot/cvs/etc
mkdir --verbose -p pam.d
cd pam.d
cp -rvf /etc/pam.d/* .
}
exit
# ----------------------------------------------------------------------
# Copy binaries and run ldconfig
sudo cp /bin/mktemp /chroot/cvs/bin
sudo cp /sbin/ldconfig /chroot/cvs/bin
sudo chroot /chroot/cvs /bin/ldconfig -v
# ----------------------------------------------------------------------
# Copy time
sudo cp /etc/localtime /chroot/cvs/etc
# Compile the following:
# (http://www.unixtools.org/cvs/run-cvs.c)
# (http://www.kegel.com/cvs-chroot.c)
# ----------------------------------------------------------------------
# Init a CVS repository
sudo chroot /chroot/cvs cvs -d /cvsroot-fp init
# ----------------------------------------------------------------------
cat > /tmp/run-cvs.c <<EOF
#include <stdlib.h>
#include <unistd.h>
int chroot(const char *path);
int execl( const char *path, const char *arg, ...);
int chdir(const char *path);
int setresuid(uid_t ruid, uid_t euid, uid_t suid);
int setresgid(gid_t rgid, gid_t egid, gid_t sgid);
#define BASE "/chroot/cvs"
#define REPOSITORY "/cvsroot-fp"
#define OWNER_UID 6000
#define OWNER_GID 6000
int main(int argc, char *argv[])
{
int res;
/* Why do this? See doc/cvs.info-8, Node: Connection. */
/* (Suggested by Scott Bronson) */
unsetenv("HOME");
res = chdir(BASE);
if ( res ) exit(1);
res = chroot(BASE);
if ( res ) exit(2);
res = setresgid(OWNER_GID, OWNER_GID, OWNER_GID);
if ( res ) exit(3);
res = setresuid(OWNER_UID, OWNER_UID, OWNER_UID);
if ( res ) exit(4);
execl("/bin/cvs", "cvs",
"-f",
"--allow-root=" REPOSITORY,
"pserver",
NULL);
exit(3);
}
EOF
gcc -Wall $CFLAGS /tmp/run-cvs.c -o /tmp/run-cvs
sudo mv /tmp/run-cvs /usr/sbin/run-cvs
rm -f /tmp/run-cvs.c
sudo chmod 755 /usr/sbin/run-cvs
# ----------------------------------------------------------------------
# Add the following to /etc/services:
cvspserver 2401/tcp
# ----------------------------------------------------------------------
# If you use inetd, please switch to xinetd.
# If you don't have xinetd yet, try:
# http://saitti.net/~heko/xinetd/
# Use the following for xinetd.conf (assuming you don't want
# to run anything else: )
sudo bash
cat > /etc/xinetd.conf <<EOF
defaults
{
instances = 25
# Reducing syslog level is recommended after the server is
# confirmed to work.
log_type = FILE /var/log/xinetd.log
log_on_success = HOST PID DURATION USERID EXIT
log_on_failure = HOST RECORD ATTEMPT USERID
only_from = 192.168.31.0 127.0.0.1
disabled = tftp
}
service cvs
{
log_type = FILE /var/log/cvs.log
log_on_success = HOST PID DURATION USERID EXIT
log_on_failure = HOST RECORD ATTEMPT USERID
flags = REUSE NORETRY NODELAY
socket_type = stream
protocol = tcp
wait = no
user = root
nice = -5
server = /usr/sbin/run-cvs
port = 2401
cps = 1 10
max_load = 2
}
EOF
exit
# And probably you will want to change the
# daemon xinetd
# in /etc/rc.d/init.d/xinetd to:
# daemon xinetd -filelog /var/log/xinetd-meta.log -reuse
sudo /etc/rc.d/init.d/xinetd restart
# ----------------------------------------------------------------------
# Compile cvspasswd:
# (http://www.kegel.com/cvspasswd.c)
export CFLAGS=`echo $CFLAGS | sed 's|-static||'`
cat > /tmp/cvspasswd.c <<EOF
/* Trivial password generator for cvs. Compile with 'cc -o cvspasswd cvspasswd.c -lcrypt' */
#define _XOPEN_SOURCE
#include <unistd.h>
#include <stdio.h>
#include <sys/times.h>
#include <time.h>
time_t time(time_t *t);
char *crypt(const char *key, const char *salt);
/* Generate a single character of salt given a random integer. See 'man crypt'. */
int base64(int x)
{
const char b64[64] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./";
return b64[x % 64];
}
int main(int argc, char **argv)
{
char ibuf[256];
char passwd[256];
char saltstr[3];
struct tms t;
if (argc != 2) {
fprintf(stderr, "Usage: cvspasswd username\n");
exit(1);
}
fprintf(stderr, "Password for %s: ", argv[1]);
ibuf[0] = 0;
fgets(ibuf, sizeof(ibuf), stdin);
sscanf(ibuf, "%s", passwd);
saltstr[0] = base64(times(&t));
saltstr[1] = base64(time(0));
saltstr[2] = 0;
printf("%s:%s:cvsowner\n", argv[1], crypt(passwd, saltstr));
exit(0);
}
EOF
gcc -Wall $CFLAGS -lcrypt /tmp/cvspasswd.c -o /tmp/cvspasswd
sudo mv /tmp/cvspasswd /usr/sbin/cvspasswd
rm -f /tmp/cvspasswd.c
# ----------------------------------------------------------------------
# Add CVS users:
sudo bash
cd /chroot/cvs/cvsroot-fp/CVSROOT
cvspasswd <user> >> passwd
<Password>
exit
# ----------------------------------------------------------------------
# Try it out:
# (For any of the following, use:
# strace -f -F -o strace.out -tt <command> <args>
# or
# strace -f -F -o strace.out -tt -p `/sbin/pidof <command>`
# ----------------------------------------------------------------------
# [1] Check that cvs server supports 'pserver':
sudo bash
echo "foo" | chroot /chroot/cvs /bin/cvs -f --allow-root=/cvsroot-fp pserver
exit
# (This should produce an error:
# cvs [pserver aborted]: bad auth protocol start: foo
# [2] cd to the original CVS compile directory and run
# *** THIS WILL TAKE A WHILE *** (several minutes). Be patient -
# it should fail by itself if anything goes wrong; I haven't
# met a timeout yet. If you are in doubt, you can always do a
# ps axuww or top
# (ps axufww on Linux procps version 2.0.6 or compatible)
# (ps axuHww on BSD procps)
# (top S s c d 10, on top revision 1.2)
# This is mostly because the sanity.sh sleeps for a minute at points,
# because it assumes a non-GNU ls that does not support showing
# seconds. Make check will fail some 70% of imports, plus commit_readonly,
# if you applied the mmap patch instead of system zlib.
make check
# [3] Check that you can login in to the cvs server
# (Use pidof xinetd if this fails)
export CVSROOT=:pserver:<user>@127.0.0.1:/cvsroot-fp
cvs login
# [4] Check that you can create a new module
cvs import -m "<Comment>" <module> <vendor> start
# [5] Check that you can create a new file
cvs co <module>
cd <module>
echo "Test" > test.txt
cvs add test.txt
cvs com -m "Test" test.txt
cvs status -v
# ----------------------------------------------------------------------
> -Tillman
<!-- ---------------------- 72 characters -------------------------- -->
Heikki Korpela -- heko@saitti.net