This documents the process to restrict a user to a chroot'd env, log their session with script, and allow only ssh activities. Specifically, I use it to lock down and track external vendors or consultants.
Tested on Red Hat 6.2, but it should work on lower RH versions just fine. On systems without SELinux, remove those options from the commands below.
Here's the basic setup:
- rvendor is our user for this example
- chrooters is the group containing all of our restricted users
- login home directory: /home/rvendor/
- chroot directory: /home/restricted_users/chroot_jails/rvendor/
- chroot and 'script' script location: /home/restricted_users/scripts/
- typescript output files from 'script': /home/restricted_users/script_files/
- all the user has access to in the chroot jail are the bash builtins, ssh, and su. Not 100% foolproof, by any means, but it's decent.
Create the account, and a group (to be used for sudo later)
$ grep rvendor /etc/passwd
rvendor:x:9999:9999:chrootd vendor account:/home/rvendor:/bin/bash
$ grep chrooters /etc/group
chrooters::9999:
Create a script to do the actual chroot and log via "script"
$ sudo cat /home/restricted_users/scripts/chrooter
!/bin/bash
FILENAME=`date +%Y%m%d-%H.%M.%S`-$USER.$$
/usr/bin/script -qaf /home/restricted_users/script_files/$USER/$FILENAME -c "/usr/bin/sudo /usr/sbin/chroot /home/restricted_users/chroot_jails/$USER /bin/su - $USER " exit
Create the directory for the chroot'd env
$ sudo mkdir -p /home/restricted_users/chroot_jails/rvendor/{bin,dev,etc,lib64,usr,var}
$ sudo ln -s /home/restricted_users/chroot_jails/rvendor/usr/bin /bin
$ sudo ln -s /home/restricted_users/chroot_jails/rvendor/usr/lib64 /lib64
$ sudo chown -R nobody:chrooters /home/restricted_users/chroot_jails/rvendor
$ sudo chmod -R 070 /home/restricted_users/chroot_jails/rvendor
$ sudo ls -la /home/restricted_users/chroot_jails/rvendor
total 28
d---rwx---. 7 nobody chrooters 4096 Apr 24 14:41 .
drwxr-xr-x. 7 root root 4096 Apr 24 14:41 ..
d---rwx---. 2 nobody chrooters 4096 Apr 24 14:41 bin
d---rwx---. 2 nobody chrooters 4096 Apr 24 14:41 etc
d---rwx---. 2 nobody chrooters 4096 Apr 24 14:41 lib64
d---rwx---. 2 nobody chrooters 4096 Apr 24 15:03 usr
d---rwx---. 2 nobody chrooters 4096 Apr 24 14:41 var
Set up the output area for the script sessions:
$ sudo mkdir -p /home/restricted_users/script_files/rvendor
$ sudo chown rvendor /home/restricted_users/script_files/rvendor
Setup sudo so that the chrooters group can run chroot with no passwd
$ sudo visudo
>>
%chrooters ALL=NOPASSWD: /usr/sbin/chroot
Create the jailed account in the chrooted env
$ sudo cat /home/restricted_users/chroot_jails/rvendor/etc/passwd
root:LK:0:0:root:/:/bin/bash
rvendor:x:9999:9999:chrootd vendor account:/:/bin/bash
$ sudo cat /home/restricted_users/chroot_jails/rvendor/etc/group
root::0:root
chrooters::9999:
Add the required binaries and libs (bash, ssh)
***** cp -pc to preserver file context + permissions for SELinux *****
$ ldd /bin/bash
linux-vdso.so.1 => (0x00007fffca3ff000)
libtinfo.so.5 => /lib64/libtinfo.so.5 (0x0000003b7a200000)
libdl.so.2 => /lib64/libdl.so.2 (0x0000003b78200000)
libc.so.6 => /lib64/libc.so.6 (0x0000003b78600000)
/lib64/ld-linux-x86-64.so.2 (0x0000003b77e00000)
$ sudo cp -pc /bin/bash /home/restricted_users/chroot_jails/rvendor/bin/bash
$ sudo cp -pc /lib64/libtinfo.so.5 /lib64/libdl.so.2 /lib64/libc.so.6 /lib64/ld-linux-x86-64.so.2 /home/rvendor/lib64
$ sudo cp -pc /usr/bin/ssh /home/restricted_users/chroot_jails/rvendor/usr/bin/ssh
$ for x in `ldd /usr/bin/ssh | cut -d' ' -f3 |grep ^/` ; do sudo cp -pcH $x /home/rvendor/$x ; done
$ sudo ls -laZ /home/restricted_users/chroot_jails/rvendor/lib64
d---rwx---. nobody chrooters system_u:object_r:default_t:s0 .
d---rwx---. nobody chrooters system_u:object_r:default_t:s0 ..
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 ld-linux-x86-64.so.2
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libcom_err.so.2
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libcrypto.so.10
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libcrypt.so.1
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libc.so.6
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libdl.so.2
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libfipscheck.so.1
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libfreebl3.so
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libgssapi_krb5.so.2
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libk5crypto.so.3
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libkeyutils.so.1
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libkrb5.so.3
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libkrb5support.so.0
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libnsl.so.1
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libnspr4.so
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libnss3.so
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libnssutil3.so
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libplc4.so
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libplds4.so
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libpthread.so.0
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libresolv.so.2
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libselinux.so.1
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libtinfo.so.5
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libutil.so.1
-rwxr-xr-x. root root system_u:object_r:lib_t:s0 libz.so.1
The Red Hat coreutils su is a bit different, as it is compiled with pam.
So, we'll compile our own to simplify, and remove the pam dependency.
$ wget http://ftp.gnu.org/gnu/coreutils/coreutils-8.16.tar.xz
--2012-05-01 12:12:42-- http://ftp.gnu.org/gnu/coreutils/coreutils-8.16.tar.xz
Resolving ftp.gnu.org... 208.118.235.20, 2001:4830:134:3::b
Connecting to ftp.gnu.org|208.118.235.20|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5042844 (4.8M) [application/x-tar]
Saving to: “coreutils-8.16.tar.xz”
100%[========================================================>] 5,042,844 --.-K/s in 0.09s
2012-05-01 12:12:43 (51.3 MB/s) - “coreutils-8.16.tar.xz” saved [5042844/5042844]
$ tar xvJf coreutils-8.16.tar.xz
$ cd coreutils-8.16
$ ./configure
$ make
Copy the su binary into position:
$ sudo cp src/su /home/restricted_users/chroot_jails/rvendor/bin/
Give it perms and context to match system install:
$ sudo chmod --reference=/bin/su /home/restricted_users/chroot_jails/rvendor/bin/su
$ sudo chcon --reference=/bin/su /home/restricted_users/chroot_jails/rvendor/bin/su
$ sudo ls -alZ /bin/su /home/restricted_users/chroot_jails/rvendor/bin/su
-rwsr-xr-x. root root system_u:object_r:su_exec_t:s0 /bin/su
-rwsr-xr-x. root root system_u:object_r:su_exec_t:s0 /home/rvendor/bin/su
Check the libs to make sure we have what we need
$ sudo ldd /home/restricted_users/chroot_jails/rvendor/bin/su
linux-vdso.so.1 => (0x00007fff1cf2f000)
libcrypt.so.1 => /lib64/libcrypt.so.1 (0x0000003b7ae00000)
libc.so.6 => /lib64/libc.so.6 (0x0000003b78600000)
libfreebl3.so => /lib64/libfreebl3.so (0x0000003b7b200000)
/lib64/ld-linux-x86-64.so.2 (0x0000003b77e00000)
libdl.so.2 => /lib64/libdl.so.2 (0x0000003b78200000)
Now we need is the libs to get us to talk to /etc/passwd /etc/group in the chroot jail
$ sudo cp -cp /lib64/libnss_files.so.2 /lib64/libnss_compat.so.2 /home/restricted_users/chroot_jails/rvendor/lib64
ssh needs some entropy, so, we'll create a /dev/random, urandom, /dev/null devices
(man 4 random, man 4 null)
$ sudo mknod -m 644 /home/restricted_users/chroot_jails/rvendor/dev/random c 1 8
$ sudo mknod -m 644 /home/restricted_users/chroot_jails/rvendor/dev/urandom c 1 9
$ sudo mknod -m 666 /home/restricted_users/chroot_jails/rvendor/dev/null c 1 3
$ sudo mknod -m 666 /home/restricted_users/chroot_jails/rvendor/dev/tty c 5 0
We need an ssh directory
$ sudo mkdir /home/restricted_users/chroot_jails/rvendor/.ssh
$ sudo chown rvendor.chrooters /home/restricted_users/chroot_jails/rvendor/.ssh
$ sudo chmod 700 /home/restricted_users/chroot_jails/rvendor/.ssh
And start the known_hosts file:
$ sudo touch /home/restricted_users/chroot_jails/rvendor/.ssh/known_hosts
$ sudo chown rvendor:chrooters \
/home/restricted_users/chroot_jails/rvendor/.ssh/known_hosts
$ sudo chmod 644 /home/restricted_users/chroot_jails/rvendor/.ssh/known_hosts
OK, at this point, the chroot jail is completely functional. All that are available are bash builtins, and ssh, su. We could firm it up even more by using rbash.... but then, need to move the chrooter script to the rvendor home dir. No biggy, just not really needed at this point
Set up the user's .bash_profile to launch the chrooter script. End the file with 'exit', so bash session log's out when script session terminates. This effectively locks this user to only running within script session, chroot'd.
$ sudo vi /home/rvendor/.bash_profile
$ sudo cat /home/rvendor/.bash_profile
/home/restricted_users/scripts/chrooter
exit
We'll also set the PS1 prompt to include hostname and time stamp to make tracking activity easier. You'll need to have this in the .bash_profile (or .bashrc) on any servers downstream that the account has access to, to be useful.
$ sudo vi /home/restricted_users/chroot_jails/rvendor/.bash_profile
$ sudo cat /home/restricted_users/chroot_jails/rvendor/.bash_profile
PS1="[\t \h] \w $ "
export PS1
Finally, create an /etc/hosts file in the chroot env with the hosts that they are going
to be connecting to.
$ sudo vi /home/restricted_users/chroot_jails/rvendor/etc/hosts
$ sudo cat /home/restricted_users/chroot_jails/rvendor/etc/hosts
XXX.XXX.XXX.XXX thatserver
Here are the results
$ sudo su - rvendor
< do some stuff, ssh out of the host>
And the resulting script file:
$ sudo cat /home/restricted_users/script_files/rvendor/20120502-13.04.00-rvendor.30824
Script started on Wed 02 May 2012 01:04:00 PM MDT
[19:04:00 thisserver] / $ ls -al
-bash: ls: command not found
[19:04:10 thisserver] / $ cd etc
[19:04:12 thisserver] /etc $ ls
-bash: ls: command not found
[19:04:12 thisserver] /etc $ cat hosts
-bash: cat: command not found
[19:04:17 thisserver] /etc $ <this is a TAB/TAB bash completion to show built-ins>
! bind compopt elif fc if printf set time unset
./ break continue else fg in pushd shift times until
: builtin coproc enable fi jobs pwd shopt trap wait
[ caller declare esac for kill read source true while
[[ case dirs eval function let readarray ssh type {
]] cd disown exec getopts local readonly su typeset }
alias command do exit hash logout return suspend ulimit
bash compgen done export help mapfile script test umask
bg complete echo false history popd select then unalias
[19:04:43 thisserver] /etc $ cd
[19:04:46 thisserver] / $ ssh someuser@thatserver
The authenticity of host 'thatserver (XXX.XXX.XXX.XXX)' can't be established.
RSA key fingerprint is 99:67:aa:83:60:fd:2e:2c:81:5f:cc:f4:e3:87:db:49.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added 'thatserver' (RSA) to the list of known hosts.
Password:
Last login: Wed May 2 13:05:09 2012 from thisserver
[13:05:10 thatserver] ~ $ hostname
thatserver
[13:05:11 thatserver] ~ $ date
Wed May 2 13:05:12 MDT 2012
[13:05:12 thatserver] ~ $ blah blah blah
-bash: blah: command not found
[13:05:16 thatserver] ~ $ exit
logout
Connection to thatserver closed.
[19:05:17 thisserver] / $ exit
logout