new fortunes
[feisty_meow.git] / infobase / configuration / ssh / make_chroot_jail.sh
1 #!/bin/bash
2 #
3 # (c) Copyright by Wolfgang Fuschlberger
4 #
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.
9 #
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 #####################################################################
16
17 # first Release: 2004-07-30
18 RELEASE="2007-10-19"
19 #
20 # The latest version of the script is available at
21 #   http://www.fuschlberger.net/programs/ssh-scp-sftp-chroot-jail/
22 #
23 # Feedback is welcome!
24 #
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 #####################################################################
29
30 #
31 # Features:
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 #####################################################################
37
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"
41
42 # Check if we are called with username or update
43 if [ -z "$1" ] ; then
44   echo
45   echo "ERROR: Parameter missing. Did you forget the username?"
46   echo "-------------------------------------------------------------"
47   echo
48   echo "USAGE:"
49   echo "Create new chrooted account or"
50   echo "add existing User to chroot-jail:"
51   echo "-> $0 username"
52   echo
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 "-------------------------------------------------------------"
58   echo
59   echo "Updating files in the chroot-jail:"
60   echo "-> $0 update [/path/to/chroot-shell [/path/to/jail]]"
61   echo "-------------------------------------------------------------"
62   echo
63   echo "To uninstall:"
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"
69   exit
70 fi
71
72 if [ -z "$PATH" ] ; then 
73   PATH=/bin:/usr/bin:/usr/local/bin:/sbin:/usr/sbin
74 fi
75
76 echo
77 echo Release: $RELEASE
78 echo
79
80 echo "Am I root?  "
81 if [ "$(whoami &2>/dev/null)" != "root" ] && [ "$(id -un &2>/dev/null)" != "root" ] ; then
82   echo "  NO!
83
84 Error: You must be root to run this script."
85   exit 1
86 fi
87 echo "  OK";
88
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"
94        DISTRO=DEBIAN;
95 elif [ -f /etc/SuSE-release ];
96   then echo "  Supported Distribution found"
97        echo "  System is running SuSE Linux"
98        DISTRO=SUSE;
99 elif [ -f /etc/fedora-release ];
100   then echo "  Supported Distribution found"
101        echo "  System is running Fedora Linux"
102        DISTRO=FEDORA;
103 elif [ -f /etc/redhat-release ];
104   then echo "  Supported Distribution found"
105        echo "  System is running Red Hat Linux"
106        DISTRO=REDHAT;
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"
108 #exit 1
109 fi
110
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"
120 else
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"
122 fi
123
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 );
130   then echo "  OK";
131   else echo "  failed
132
133 Please install which-binary!
134 "
135 exit 1
136 fi
137
138 echo "Checking for chroot..." 
139 if [ $(which chroot) ];
140   then echo "  OK";
141   else echo "  failed
142
143 chroot not found!
144 Please install chroot-package/binary!
145 "
146 exit 1
147 fi
148
149 echo "Checking for sudo..." 
150 if [ $(which sudo) ]; then
151   echo "  OK";
152 else 
153   echo "  failed
154
155 sudo not found!
156 Please install sudo-package/binary!
157 "
158 exit 1
159 fi
160
161 echo "Checking for dirname..." 
162 if [ $(which dirname) ]; then
163   echo "  OK";
164 else 
165   echo "  failed
166
167 dirname not found!
168 Please install dirname-binary (to be found eg in the package coreutils)!
169 "
170 exit 1
171 fi
172
173 echo "Checking for awk..." 
174 if [ $(which awk) ]; then
175   echo "  OK
176 ";
177 else 
178   echo "  failed
179
180 awk not found!
181 Please install (g)awk-package/binary!
182 "
183 exit 1
184 fi
185
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
190 #
191 #if  (test ! -f /etc/ssh/sshd_config &> /dev/null); then
192 #  echo "
193 #File /etc/ssh/sshd_config not found.
194 #Not checking for path to sftp-server.
195 #  ";
196 #else
197 if [ ! -f ${SSHD_CONFIG} ]
198 then
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"
202 else
203   if !(grep -v "^#" ${SSHD_CONFIG} | grep -i sftp-server &> /dev/null); then
204     echo "Obviously no sftp-server is running on this system.
205 ";
206   else SFTP_SERVER=$(grep -v "^#" ${SSHD_CONFIG} | grep -i sftp-server | awk  '{ print $3}')
207   fi
208 fi
209
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"
212
213 # Get accountname to create / move
214 CHROOT_USERNAME=$1
215
216 if ! [ -z "$2" ] ; then
217   SHELL=$2
218 else
219   SHELL=/bin/chroot-shell
220 fi
221
222 if ! [ -z "$3" ] ; then
223   JAILPATH=$3
224 else
225   JAILPATH=/home/jail
226 fi
227
228 # Exit if user already exists
229 #id $CHROOT_USERNAME > /dev/null 2>&1 && { echo "User exists."; echo "Exiting."; exit 1; }
230
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 {
234 echo "
235 -----------------------------
236 User $CHROOT_USERNAME exists. 
237
238 Are you sure you want to modify the users home directory and lock him into the
239 chroot directory?
240 Are you REALLY sure?
241 Say only yes if you absolutely know what you are doing!"
242   read -p "(yes/no) -> " MODIFYUSER
243   if [ "$MODIFYUSER" != "yes" ]; then
244     echo "
245 Not entered yes. Exiting...."
246     exit 1
247   fi
248 }
249 else
250   CREATEUSER="yes"
251 fi
252
253 # Create $SHELL (shell for jailed accounts)
254 if [ -f ${SHELL} ] ; then
255   echo "
256 -----------------------------
257 The file $SHELL exists. 
258 Probably it was created by this script.
259
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
265   echo "
266 Not entered yes. Exiting...."
267   exit 1
268 fi
269 else
270   echo "Creating $SHELL"
271   echo '#!/bin/sh' > $SHELL
272   echo "$(which sudo) $(which chroot) $JAILPATH /bin/su - \$USER" \"\$@\" >> $SHELL
273   chmod 755 $SHELL
274 fi
275
276 # make common jail for everybody if inexistent
277 if [ ! -d ${JAILPATH} ] ; then
278   mkdir -p ${JAILPATH}
279   echo "Creating ${JAILPATH}"
280 fi
281 cd ${JAILPATH}
282
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"
289   fi
290 done
291 echo
292
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
300 #fi
301
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 
307
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
311
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
316
317 # Define HomeDir for simple referencing
318 HOMEDIR="$JAILPATH/home/$CHROOT_USERNAME"
319
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\"
326 "
327 usermod -d "$HOMEDIR" -m -s "$SHELL" $CHROOT_USERNAME && chmod 700 "$HOMEDIR"
328 fi
329
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"
333
334 # Enter password for new account
335 if !(passwd $CHROOT_USERNAME);
336   then echo "Passwords are probably not the same, try again."
337   exit 1;
338 fi
339 echo
340 }
341 fi
342
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
347
348 # Add users to etc/passwd
349 #
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
354 fi
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
359 # winSCP)
360  grep /etc/group -e "^users" >> ${JAILPATH}/etc/group
361 fi
362
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 
365 # appear in the jail
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
370
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
374
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
378
379 # endif for =! update
380 fi
381
382 # Copy the apps and the related libs
383 echo "Copying necessary library-files to jail (may take some time)"
384
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 '/'
391
392 # Randy K.
393 #
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 /
400 #
401
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
405 #
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
408
409 for app in $APPS;  do
410     # First of all, check that this application exists
411     if [ -x $app ]; then
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
416             mkdir -p .$app_path
417         fi
418
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.
423                 cp -p $app .$app
424
425         # get list of necessary libraries
426         ldd $app >> ${TMPFILE1}
427     fi
428 done
429
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}
435    fi
436 done
437 for lib in $(cat ${TMPFILE2}); do
438     mkdir -p .$(dirname $lib) > /dev/null 2>&1
439
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.
444     cp $lib .$lib
445 done
446
447 #
448 # Now, cleanup the 2 files we created for the library list
449 #
450 #/bin/rm -f ${HOME}/ldlist
451 #/bin/rm -f ${HOME}/ldlist2
452 /bin/rm -f ${TMPFILE1}
453 /bin/rm -f ${TMPFILE2}
454
455 # Necessary files that are not listed by ldd.
456 #
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/
473 else
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/
475 fi
476
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)
480 #
481 #cp /lib/*.* ${JAILPATH}/lib/
482 #cp /lib/lib64/*.* ${JAILPATH}/lib/lib64/ 
483
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/
487
488 # ...and of course the PAM-modules...
489 echo "Copying PAM-Modules to jail"
490 cp -r /lib/security ${JAILPATH}/lib/
491
492 # ...and something else useful for PAM
493 cp -r /etc/security ${JAILPATH}/etc/
494 cp /etc/login.defs ${JAILPATH}/etc/
495
496 if [ -f /etc/DIR_COLORS ] ; then
497   cp /etc/DIR_COLORS ${JAILPATH}/etc/
498 fi 
499
500 # Don't give more permissions than necessary
501 chown root.root ${JAILPATH}/bin/su
502 chmod 700 ${JAILPATH}/bin/su
503
504 exit
505