3 # (c) Copyright by Wolfgang Fuschlberger
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU General Public License for more details.
14 # ( http://www.fsf.org/licenses/gpl.txt )
15 #####################################################################
17 # first Release: 2004-07-30
20 # The latest version of the script is available at
21 # http://www.fuschlberger.net/programs/ssh-scp-sftp-chroot-jail/
23 # Feedback is welcome!
25 # Thanks for Bugfixes / Enhancements to
26 # Michael Prokop <http://www.michael-prokop.at/chroot/>,
27 # Randy K., Randy D., Jonathan Hunter and everybody else.
28 #####################################################################
32 # - enable scp and sftp in the chroot-jail
33 # - use one directory (default /home/jail/) as chroot for all users
34 # - create new accounts
35 # - move existing accounts to chroot
36 #####################################################################
38 # path to sshd's config file: needed for automatic detection of the locaten of
39 # the sftp-server binary
40 SSHD_CONFIG="/etc/ssh/sshd_config"
42 # Check if we are called with username or update
45 echo "ERROR: Parameter missing. Did you forget the username?"
46 echo "-------------------------------------------------------------"
49 echo "Create new chrooted account or"
50 echo "add existing User to chroot-jail:"
53 echo "or specify \$SHELL and path where the jail should be located:"
54 echo "-> $0 username [/path/to/chroot-shell [/path/to/jail]]"
55 echo "Default shell = /bin/chroot-shell"
56 echo "Default chroot-path = /home/jail"
57 echo "-------------------------------------------------------------"
59 echo "Updating files in the chroot-jail:"
60 echo "-> $0 update [/path/to/chroot-shell [/path/to/jail]]"
61 echo "-------------------------------------------------------------"
64 echo " # userdel \$USER"
65 echo " # rm -rf /home/jail"
66 echo " (this deletes all Users' files!)"
67 echo " # rm -f /bin/chroot-shell"
68 echo " manually delete the User's line from /etc/sudoers"
72 if [ -z "$PATH" ] ; then
73 PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
77 echo Release: $RELEASE
81 if [ "$(whoami &2>/dev/null)" != "root" ] && [ "$(id -un &2>/dev/null)" != "root" ] ; then
84 Error: You must be root to run this script."
89 # Check existence of necessary files
90 echo "Checking distribution... "
91 if [ -f /etc/debian_version ];
92 then echo " Supported Distribution found"
93 echo " System is running Debian Linux"
95 elif [ -f /etc/SuSE-release ];
96 then echo " Supported Distribution found"
97 echo " System is running SuSE Linux"
99 elif [ -f /etc/fedora-release ];
100 then echo " Supported Distribution found"
101 echo " System is running Fedora Linux"
103 elif [ -f /etc/redhat-release ];
104 then echo " Supported Distribution found"
105 echo " System is running Red Hat Linux"
107 else echo -e " failed...........\nThis script works best on Debian, Red Hat, Fedora and SuSE Linux!\nLet's try it nevertheless....\nIf some program files cannot be found adjust the respective path in line 98\n"
111 # Specify the apps you want to copy to the jail
112 if [ "$DISTRO" = SUSE ]; then
113 APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/netcat /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /sbin/unix_chkpwd"
114 elif [ "$DISTRO" = FEDORA ]; then
115 APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/nc /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /sbin/unix_chkpwd"
116 elif [ "$DISTRO" = REDHAT ]; then
117 APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/nc /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /sbin/unix_chkpwd"
118 elif [ "$DISTRO" = DEBIAN ]; then
119 APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /sbin/unix_chkpwd"
121 APPS="/bin/bash /bin/cp /usr/bin/dircolors /bin/ls /bin/mkdir /bin/mv /bin/rm /bin/rmdir /bin/sh /bin/su /usr/bin/groups /usr/bin/id /usr/bin/rsync /usr/bin/ssh /usr/bin/scp /usr/sbin/unix_chkpwd"
124 # Check existence of necessary files
125 echo "Checking for which... "
126 #if [ -f $(which which) ] ;
127 # not good because if which does not exist I look for an
128 # empty filename and get OK nevertheless
129 if ( test -f /usr/bin/which ) || ( test -f /bin/which ) || ( test -f /sbin/which ) || ( test -f /usr/sbin/which );
133 Please install which-binary!
138 echo "Checking for chroot..."
139 if [ $(which chroot) ];
144 Please install chroot-package/binary!
149 echo "Checking for sudo..."
150 if [ $(which sudo) ]; then
156 Please install sudo-package/binary!
161 echo "Checking for dirname..."
162 if [ $(which dirname) ]; then
168 Please install dirname-binary (to be found eg in the package coreutils)!
173 echo "Checking for awk..."
174 if [ $(which awk) ]; then
181 Please install (g)awk-package/binary!
186 # get location of sftp-server binary from /etc/ssh/sshd_config
187 # check for existence of /etc/ssh/sshd_config and for
188 # (uncommented) line with sftp-server filename. If neither exists, just skip
189 # this step and continue without sftp-server
191 #if (test ! -f /etc/ssh/sshd_config &> /dev/null); then
193 #File /etc/ssh/sshd_config not found.
194 #Not checking for path to sftp-server.
197 if [ ! -f ${SSHD_CONFIG} ]
199 echo "File ${SSHD_CONFIG} not found."
200 echo "Not checking for path to sftp-server."
201 echo "Please adjust the global \$SSHD_CONFIG variable"
203 if !(grep -v "^#" ${SSHD_CONFIG} | grep -i sftp-server &> /dev/null); then
204 echo "Obviously no sftp-server is running on this system.
206 else SFTP_SERVER=$(grep -v "^#" ${SSHD_CONFIG} | grep -i sftp-server | awk '{ print $3}')
210 #if !(grep -v "^#" /etc/ssh/sshd_config | grep -i sftp-server /etc/ssh/sshd_config | awk '{ print $3}' &> /dev/null); then
211 APPS="$APPS $SFTP_SERVER"
213 # Get accountname to create / move
216 if ! [ -z "$2" ] ; then
219 SHELL=/bin/chroot-shell
222 if ! [ -z "$3" ] ; then
228 # Exit if user already exists
229 #id $CHROOT_USERNAME > /dev/null 2>&1 && { echo "User exists."; echo "Exiting."; exit 1; }
231 # Check if user already exists and ask for confirmation
232 # we have to trust that root knows what she is doing when saying 'yes'
233 if ( id $CHROOT_USERNAME > /dev/null 2>&1 ) ; then {
235 -----------------------------
236 User $CHROOT_USERNAME exists.
238 Are you sure you want to modify the users home directory and lock him into the
241 Say only yes if you absolutely know what you are doing!"
242 read -p "(yes/no) -> " MODIFYUSER
243 if [ "$MODIFYUSER" != "yes" ]; then
245 Not entered yes. Exiting...."
253 # Create $SHELL (shell for jailed accounts)
254 if [ -f ${SHELL} ] ; then
256 -----------------------------
257 The file $SHELL exists.
258 Probably it was created by this script.
260 Are you sure you want to overwrite it?
261 (you want to say yes for example if you are running the script for the second
262 time when adding more than one account to the jail)"
263 read -p "(yes/no) -> " OVERWRITE
264 if [ "$OVERWRITE" != "yes" ]; then
266 Not entered yes. Exiting...."
270 echo "Creating $SHELL"
271 echo '#!/bin/sh' > $SHELL
272 echo "$(which sudo) $(which chroot) $JAILPATH /bin/su - \$USER" \"\$@\" >> $SHELL
276 # make common jail for everybody if inexistent
277 if [ ! -d ${JAILPATH} ] ; then
279 echo "Creating ${JAILPATH}"
283 # Create directories in jail that do not exist yet
284 JAILDIRS="dev etc etc/pam.d bin home sbin usr usr/bin usr/lib"
285 for directory in $JAILDIRS ; do
286 if [ ! -d "$JAILPATH/$directory" ] ; then
287 mkdir $JAILPATH/"$directory"
288 echo "Creating $JAILPATH/$directory"
293 # Comment in the following lines if your apache can't read the directories and
294 # uses the security contexts
295 # Fix security contexts so Apache can read files
296 #CHCON=$(which chcon)
297 #if [ -n "$CHCON" ] && [ -x $CHCON ]; then
298 # $CHCON -t home_root_t $JAILPATH/home
299 # $CHCON -t user_home_dir_t $JAILPATH/home/$CHROOT_USERNAME
302 # Creating necessary devices
303 [ -r $JAILPATH/dev/urandom ] || mknod $JAILPATH/dev/urandom c 1 9
304 [ -r $JAILPATH/dev/null ] || mknod -m 666 $JAILPATH/dev/null c 1 3
305 [ -r $JAILPATH/dev/zero ] || mknod -m 666 $JAILPATH/dev/zero c 1 5
306 [ -r $JAILPATH/dev/tty ] || mknod -m 666 $JAILPATH/dev/tty c 5 0
308 # if we only want to update the files in the jail
309 # skip the creation of the new account
310 if [ "$1" != "update" ]; then
312 # Modifiy /etc/sudoers to enable chroot-ing for users
313 # must be removed by hand if account is deleted
314 echo "Modifying /etc/sudoers"
315 echo "$CHROOT_USERNAME ALL=NOPASSWD: $(which chroot), /bin/su - $CHROOT_USERNAME" >> /etc/sudoers
317 # Define HomeDir for simple referencing
318 HOMEDIR="$JAILPATH/home/$CHROOT_USERNAME"
320 # Create new account, setting $SHELL to the above created script and
321 # $HOME to $JAILPATH/home/*
322 if [ "$CREATEUSER" != "yes" ] ; then echo "
323 Not creating new User account
324 Modifying User \"$CHROOT_USERNAME\"
325 Copying files in $CHROOT_USERNAME's \$HOME to \"$HOMEDIR\"
327 usermod -d "$HOMEDIR" -m -s "$SHELL" $CHROOT_USERNAME && chmod 700 "$HOMEDIR"
330 if [ "$CREATEUSER" = "yes" ] ; then {
331 echo "Adding User \"$CHROOT_USERNAME\" to system"
332 useradd -m -d "$HOMEDIR" -s "$SHELL" $CHROOT_USERNAME && chmod 700 "$HOMEDIR"
334 # Enter password for new account
335 if !(passwd $CHROOT_USERNAME);
336 then echo "Passwords are probably not the same, try again."
343 # Create /usr/bin/groups in the jail
344 echo "#!/bin/bash" > usr/bin/groups
345 echo "id -Gn" >> usr/bin/groups
346 chmod 755 usr/bin/groups
348 # Add users to etc/passwd
350 # check if file exists (ie we are not called for the first time)
351 # if yes skip root's entry and do not overwrite the file
352 if [ ! -f etc/passwd ] ; then
353 grep /etc/passwd -e "^root" > ${JAILPATH}/etc/passwd
355 if [ ! -f etc/group ] ; then
356 grep /etc/group -e "^root" > ${JAILPATH}/etc/group
357 # add the group for all users to etc/group (otherwise there is a nasty error
358 # message and probably because of that changing directories doesn't work with
360 grep /etc/group -e "^users" >> ${JAILPATH}/etc/group
363 # grep the username which was given to us from /etc/passwd and add it
364 # to ./etc/passwd replacing the $HOME with the directory as it will then
366 echo "Adding User $CHROOT_USERNAME to jail"
367 grep -e "^$CHROOT_USERNAME:" /etc/passwd | \
368 sed -e "s#$JAILPATH##" \
369 -e "s#$SHELL#/bin/bash#" >> ${JAILPATH}/etc/passwd
371 # if the system uses one account/one group we write the
372 # account's group to etc/group
373 grep -e "^$CHROOT_USERNAME:" /etc/group >> ${JAILPATH}/etc/group
375 # write the user's line from /etc/shadow to /home/jail/etc/shadow
376 grep -e "^$CHROOT_USERNAME:" /etc/shadow >> ${JAILPATH}/etc/shadow
377 chmod 600 ${JAILPATH}/etc/shadow
379 # endif for =! update
382 # Copy the apps and the related libs
383 echo "Copying necessary library-files to jail (may take some time)"
385 # The original code worked fine on RedHat 7.3, but did not on FC3.
386 # On FC3, when the 'ldd' is done, there is a 'linux-gate.so.1' that
387 # points to nothing (or a 90xb.....), and it also does not pick up
388 # some files that start with a '/'. To fix this, I am doing the ldd
389 # to a file called ldlist, then going back into the file and pulling
390 # out the libs that start with '/'
394 # The original code worked fine on 2.4 kernel systems. Kernel 2.6
395 # introduced an internal library called 'linux-gate.so.1'. This
396 # 'phantom' library caused non-critical errors to display during the
397 # copy since the file does not actually exist on the file system.
398 # To fix re-direct output of ldd to a file, parse the file and get
399 # library files that start with /
402 # create temporary files with mktemp, if that doesn't work for some reason use
403 # the old method with $HOME/ldlist[2] (so I don't have to check the existence
404 # of the mktemp package / binary at the beginning
406 TMPFILE1=$(mktemp) &> /dev/null || TMPFILE1="${HOME}/ldlist"; if [ -x ${TMPFILE1} ]; then mv ${TMPFILE1} ${TMPFILE1}.bak;fi
407 TMPFILE2=$(mktemp) &> /dev/null || TMPFILE2="${HOME}/ldlist2"; if [ -x ${TMPFILE2} ]; then mv ${TMPFILE2} ${TMPFILE2}.bak;fi
410 # First of all, check that this application exists
412 # Check that the directory exists; create it if not.
413 # app_path=$(echo $app | sed -e 's#\(.\+\)/[^/]\+#\1#')
414 app_path=$(dirname $app)
415 if ! [ -d .$app_path ]; then
419 # If the files in the chroot are on the same file system as the
420 # original files you should be able to use hard links instead of
421 # copying the files, too. Symbolic links cannot be used, because the
422 # original files are outside the chroot.
425 # get list of necessary libraries
426 ldd $app >> ${TMPFILE1}
430 # Clear out any old temporary file before we start
431 for libs in $(cat ${TMPFILE1}); do
432 frst_char="$(echo $libs | cut -c1)"
433 if [ "$frst_char" = "/" ]; then
434 echo "$libs" >> ${TMPFILE2}
437 for lib in $(cat ${TMPFILE2}); do
438 mkdir -p .$(dirname $lib) > /dev/null 2>&1
440 # If the files in the chroot are on the same file system as the original
441 # files you should be able to use hard links instead of copying the files,
442 # too. Symbolic links cannot be used, because the original files are
443 # outside the chroot.
448 # Now, cleanup the 2 files we created for the library list
450 #/bin/rm -f ${HOME}/ldlist
451 #/bin/rm -f ${HOME}/ldlist2
452 /bin/rm -f ${TMPFILE1}
453 /bin/rm -f ${TMPFILE2}
455 # Necessary files that are not listed by ldd.
457 # There might be errors because of files that do not exist but in the end it
458 # may work nevertheless (I added new file names at the end without deleting old
459 # ones for reasons of backward compatibility).
460 # So please test ssh/scp before reporting a bug.
461 if [ "$DISTRO" = SUSE ]; then
462 cp /lib/libnss_compat.so.2 /lib/libnss_files.so.2 /lib/libnss_dns.so.2 /lib/libxcrypt.so.1 ${JAILPATH}/lib/
463 elif [ "$DISTRO" = FEDORA ]; then
464 cp /lib/libnss_compat.so.2 /lib/libnsl.so.1 /lib/libnss_files.so.2 /lib/ld-linux.so.2 /lib/ld-ldb.so.3 /lib/ld-lsb.so.3 /lib/libnss_dns.so.2 /lib/libxcrypt.so.1 ${JAILPATH}/lib/
465 cp /lib/*.* ${JAILPATH}/lib/
466 cp /usr/lib/libcrack.so.2 ${JAILPATH}/usr/lib/
467 elif [ "$DISTRO" = REDHAT ]; then
468 cp /lib/libnss_compat.so.2 /lib/libnsl.so.1 /lib/libnss_files.so.2 /lib/ld-linux.so.2 /lib/ld-lsb.so.1 /lib/libnss_dns.so.2 /lib/libxcrypt.so.1 ${JAILPATH}/lib/
469 # needed for scp on RHEL
470 echo "export LD_LIBRARY_PATH=/usr/kerberos/lib" >> ${JAILPATH}/etc/profile
471 elif [ "$DISTRO" = DEBIAN ]; then
472 cp /lib/libnss_compat.so.2 /lib/libnsl.so.1 /lib/libnss_files.so.2 /lib/libcap.so.1 /lib/libnss_dns.so.2 ${JAILPATH}/lib/
474 cp /lib/libnss_compat.so.2 /lib/libnsl.so.1 /lib/libnss_files.so.2 /lib/libcap.so.1 /lib/libnss_dns.so.2 ${JAILPATH}/lib/
477 # if you are using a 64 bit system and have strange problems with login comment
478 # the following lines in, perhaps it works then (motto: if you can't find the
479 # needed library just copy all of them)
481 #cp /lib/*.* ${JAILPATH}/lib/
482 #cp /lib/lib64/*.* ${JAILPATH}/lib/lib64/
484 # if you are using PAM you need stuff from /etc/pam.d/ in the jail,
485 echo "Copying files from /etc/pam.d/ to jail"
486 cp /etc/pam.d/* ${JAILPATH}/etc/pam.d/
488 # ...and of course the PAM-modules...
489 echo "Copying PAM-Modules to jail"
490 cp -r /lib/security ${JAILPATH}/lib/
492 # ...and something else useful for PAM
493 cp -r /etc/security ${JAILPATH}/etc/
494 cp /etc/login.defs ${JAILPATH}/etc/
496 if [ -f /etc/DIR_COLORS ] ; then
497 cp /etc/DIR_COLORS ${JAILPATH}/etc/
500 # Don't give more permissions than necessary
501 chown root.root ${JAILPATH}/bin/su
502 chmod 700 ${JAILPATH}/bin/su