here are the scripts i wrote to make this easier. these really were made
for my own use, but i hope others may find them useful. i would be
interested to know if anyone else actually does find them useful. would
also be glad to know of any errors/problems/things that can go wrong i
didn't think of.
the first one (jail_new) creates a new jail (and possibly the user).
the second one (jail_pkgadd) adds a package and its dependencies to an
existing jail. they are expected to be in the same directory (jail_new
cannot add packages (-p) otherwise).
to relate to my earlier examples:
$ jail_new -tu _inmate:_chaingang /home/jail
will create the jail in /home/jail and also the user _inmate and group
_chaingang. this case it will be just be a regular shell account (just
chrooted).
$ jail_new -t _inmate:_chaingang /home/jail
will create the jail, but will not create the user:group.
a real case:
$ jail_new -tux -k /home/null/.ssh/id_rsa.pub -p w3m,feh:/usr/release/pkg
browse /home/browse w3m -B
this command sets up the terminal (-t) and X (-x) in a directory (here
/home/browse), creates a user (-u) (in this case 'browse'), uses the given
key file (-k) for the authorized keys, installs the packages (-p) w3m and
feh (and all of their dependencies) from directory /usr/release/pkg, and
sets 'w3m -B' to run automatically via ForceCommand in sshd_config.
this is the equivalent of:
$ jail_new -tux -k /home/null/.ssh/id_rsa.pub browse /home/browse w3m -B
$ jail_pkgadd -p /usr/release/pkg w3m /home/browse
$ jail_pkgadd -p /usr/release/pkg feh /home/browse
if you want bzip2 in there as well, you can always add it later:
$ jail_pkgadd -p /usr/release/pkg bzip2 /home/browse
or, if PKG_PATH is set (and not remote) you can omit -p
$ jail_pkgadd bzip2 /home/browse
if PKG_PATH is set, and is remote, you need:
$ jail_pkgadd -r bzip2 /home/browse
(note: will only allow a single directory for PKG_PATH)
this can be used by running:
$ Xephyr :1 & env DISPLAY=:1 ssh -X browse@localhost
(side note: w3m runs 'display' to display an image, so i create a symlink
to feh to view images)
another case:
$ jail_new -tuxr -k /home/null/.ssh/id_rsa.pub -p
xpdf:scp://null@node02/usr/release/pkg pdf /home/pdf
you need to specify -r (remote) directly to use remote pkg src.
which is the equivalent of:
$ jail_new -tux -k /home/null/.ssh/id_rsa.pub pdf /home/pdf
$ jail_pkgadd -r -p scp://null@node02/usr/release/pkg xpdf /home/pdf
which can be used:
$ cp test.pdf /home/pdf/tmp
$ Xephyr :1 & env DISPLAY=:1 ssh -X browse@localhost xpdf -fullscreen
/tmp/test.pdf
(in this case it may be best not to use ForceCommand, since you may want to
open multiple documents.)
WARNING use at your own peril. if you can't read the scripts, you probably
shouldn't use them, and then i am certain there are other glaring security
flaws you need to know about. i include these because it is a dull pain in
the ass to do this manually, and hopefully someone may get some use out of
them.
other than that, do with it what you wish.
they are as fool-proof as i could make them, so that i don't shoot myself in
the foot accidently (and i have been around long enough to have done that a
few times, even while being careful). but you never know.
jail_new:
--------------------------------------------------
#!/bin/ksh
USAGE="${0##*/} [-jrtux] [-k authkeys] [-p pkg[,pkg2...][:pkgpath]]
user[:group] path [cmd [args ...]]"
[[ "$1" = -h ]] && { echo "USAGE $USAGE"; return 0; }
#-t sets PermitTTY and copies files for term
#-x sets X11Forwarding and copies files for X (fonts,xauth)
#-u creates user; fails if user exists
#-j joins group; needed to join existing group
#-p pkg[,pkg2...][:pkgpath]
#-r allows remote pkg access
#uses existing PKG_PATH
#pkgpath arg overrides PKG_PATH
#only accepts a lone pkgpath
PATH=/sbin:/bin:/usr/sbin:/usr/bin
echov() { eval echo \"\$$1\"; }
isemptyv() { eval [ \${#$1} -eq 0 ]; }
notemptyv() { eval [ \${#$1} -gt 0 ]; }
alias xt='set -o xtrace'
alias xt-='set +o xtrace'
if [ $(id -u) -eq 0 ];then
echo "ERR cannot run as root"
return 1
fi
_sshd_config=/etc/ssh/sshd_config
_sshd_config_tmp=/tmp/sshd_config
trap "rm -f $_sshd_config_tmp" 0 2
#for convenience
_fontdir=/usr/X11R6/lib/X11/fonts
_terminfo=/usr/share/misc/terminfo.db
_termcap=/usr/share/misc/termcap
_do_x=no
_do_tty=no
_do_useradd=
_do_joingrp=
_do_remote=
_authkeys=
_pkg=
_pkgpath=
_userhome=/home/cell
while getopts :jrtuxk:p: _opt;do
case "$_opt" in
j) _do_joingrp=yes ;;
r) _do_remote=-r ;;
t) _do_tty=yes ;;
u) _do_useradd=yes ;;
x) _do_x=yes ;;
k) _authkeys=$OPTARG
if [ ! -f "$_authkeys" ];then
echo "ERR no such file '$_authkeys'"
return 1
fi
;;
p) _pkg=$OPTARG
if [[ "$_pkg" = *:* ]];then
_pkgpath=${_pkg#*:}
_pkg=${_pkg%%:*}
export PKG_PATH=$_pkgpath
else
if isemptyv PKG_PATH;then
echo "ERR PKG_PATH not set and none given"
return 1
fi
_pkgpath=$PKG_PATH
fi
if [[ "$_pkgpath" = *://* ]];then
if isemptyv _do_remote;then
echo "ERR pkgpath is remote; use -r"
return 1
fi
if [[ "$_pkgpath" = *:*:* ]];then
echo "ERR invalid pkgpath '$_pkgpath'; only one dir permitted"
return 1
fi
else
if [[ "$_pkgpath" = *:* ]];then
echo "ERR invalid pkgpath '$_pkgpath'; only one dir permitted"
return 1
fi
fi
if isemptyv _do_remote && [ ! -d "$_pkgpath" ];then
echo "ERR no such dir '$_pkgpath'"
return 1
fi
_cmd_jailpkgadd=$(dirname $0)/jail_pkgadd
if [ ! -f $_cmd_jailpkgadd ];then
echo "ERR cannot locate jail_pkgadd script"
return 1
fi
;;
:) echo "ERR no arg for '-$OPTARG'"; return 1 ;;
*) echo "ERR invalid arg '-$OPTARG'"; return 1 ;;
esac
done
shift $((OPTIND-1))
_user=$1
_jailroot=$2
_cmd=$3
if isemptyv _user;then
echo "ERR user not given"
return 1
fi
if isemptyv _jailroot;then
echo "ERR jailroot path not given"
return 1
fi
shift 2
if [ -e $_jailroot ];then
echo "ERR $_jailroot already exists"
return 1
fi
_authkeysfile=$_jailroot.authkeys
_dbdir=$_jailroot/var/db/pkg
#check/setup user/group
getfreeuid() {
local _uid=1000
local _uid_list="$(awk -F : '{printf $3"\n"}' /etc/passwd)"
local _gid_list="$(awk -F : '{printf $3"\n"}' /etc/group)"
while [ $_uid -gt 900 ];do
_uid=$((_uid-1))
echov _uid_list | grep -q "^$_uid$" && continue
echov _gid_list | grep -q "^$_uid$" && continue
break
done
[ $_uid -lt 900 ] && return 1
echo $_uid
}
_group=${_user#*:}
_user=${_user%:*}
if grep -q ^$_user: /etc/passwd;then
if notemptyv _do_useradd;then
echo "ERR user '$_user' already exists"
return 1
fi
_userhome=$(grep ^$_user: /etc/passwd | awk -F : '{printf $4":"$6}')
_group=${_userhome%:*}
_userhome=${_userhome#*:}
_jailhome=$_jailroot$_userhome
else
if isemptyv _do_useradd;then
echo "ERR user '$_user' does not exist; use -u to create"
return 1
fi
_uid=$(getfreeuid) || { echo "ERR no uid free"; return 1; }
if [[ "$_user" = "$_group" ]];then
_group="=uid"
else
if grep -q ^$_group: /etc/group;then
if isemptyv _do_joingrp;then
echo "ERR group '$_group' exists; use -j to join"
return 1
fi
else
if notemptyv _do_joingrp;then
echo "ERR group '$_group' doesn't exist; cannot use -j to join"
return 1
fi
if ! sudo groupadd -g $_uid $_group;then
echo "ERR failed to add group '$_group' gid '$_uid'"
return 1
fi
fi fi
if ! sudo useradd -u $_uid -g $_group -c "public jail" \
-s /bin/sh -d $_userhome $_user 2>&1
then
echo "ERR useradd failed"
return 1
fi | grep -v "Warning: home"
_group=$_uid
fi
_jailhome=$_jailroot$_userhome
sudo mkdir -p $_jailhome || return 1
cat >$_sshd_config_tmp <<CONF
Match User $_user
ChrootDirectory $_jailroot
AuthorizedKeysFile $_authkeysfile
X11Forwarding $_do_x
PermitTTY $_do_tty
CONF
set -f
notemptyv _cmd && echo " ForceCommand $*" >>$_sshd_config_tmp
set +f
#setup filesystem
isemptyv _authkeys && _authkeys=/dev/null
sudo sh -c "cat $_authkeys >$_authkeysfile" || return 1
sudo chown $_user:$_group $_jailhome $_authkeysfile || return 1
sudo mkdir -p $_jailroot/dev || return 1
if [[ "$_do_tty" = yes ]];then
sudo mknod -m 644 $_jailroot/dev/arandom c 45 3 || return 1
sudo mknod -m 666 $_jailroot/dev/null c 2 2 || return 1
sudo mknod -m 666 $_jailroot/dev/stderr c 22 2 || return 1
sudo mknod -m 666 $_jailroot/dev/stdin c 22 0 || return 1
sudo mknod -m 666 $_jailroot/dev/stdout c 22 1 || return 1
sudo mknod -m 666 $_jailroot/dev/tty c 1 0 || return 1
sudo mknod -m 666 $_jailroot/dev/zero c 2 12 || return 1
#sudo mknod -m 666 $_jailroot/dev/ttyp0 c 5 0 || return 1
#might want to add ttypN to make sshd happy
fi
sudo mkdir -p $_jailroot/{bin,sbin,etc,tmp,var/run} $_dbdir
sudo mkdir -p $_jailroot/usr/{libexec,{,X11R6,local}/{bin,lib}}
sudo cp -p /sbin/ldconfig $_jailroot/sbin
sudo cp -p /usr/libexec/ld.so $_jailroot/usr/libexec
sudo chroot $_jailroot ldconfig /usr/{,X11R6,local}/lib
sudo chmod 1777 $_jailroot/tmp
sudo cp -p /bin/sh $_jailroot/bin
sudo cp -pR /etc/{hosts,resolv.conf,localtime} $_jailroot/etc
if [[ "$_do_x" = yes ]];then
_xauth=/usr/X11R6/bin/xauth
sudo cp -p /bin/sh $_jailroot/bin
sudo cp -p $_xauth $_jailroot$_xauth
for _lib in $(ldd $_xauth | grep ' rlib ' | awk '{printf $7" "}');do
[ -f $_jailroot/$_lib ] && continue
sudo cp -p $_lib $_jailroot/$_lib || return 1
done
sudo cp -rp /etc/fonts $_jailroot/etc || return 1
sudo mkdir -p $_jailroot/${_fontdir%/*} || return 1
sudo cp -rp $_fontdir $_jailroot/$_fontdir || return 1
fi
if [[ "$_do_tty" = yes ]];then
#for terminal eg w3m
sudo mkdir -p $_jailroot${_terminfo%/*} || return 1
sudo cp -p $_terminfo $_termcap $_jailroot${_terminfo%/*} || return 1
#may still need /etc/termcap
#sudo cp -R /etc/termcap $_jailroot/etc || return 1
fi
echo "WARNING will update /etc/ssh/sshd_config with the following:\n"
cat $_sshd_config_tmp
echo
read _update_ssh?"update sshd_config and restart sshd? (type 'yes') "
if notemptyv _update_ssh && [[ "$_update_ssh" = yes ]];then
sudo sh -c "cat $_sshd_config_tmp >>$_sshd_config"
[ -f /var/run/sshd.pid ] && sudo kill -1 $(</var/run/sshd.pid)
fi
if notemptyv _pkg;then
IFS=,
for _thispkg in $_pkg;do
$_cmd_jailpkgadd $_do_remote -p $_pkgpath $_thispkg $_jailroot
done
fi
--------------------------------------------------
jail_pkgadd:
--------------------------------------------------
#!/bin/ksh
USAGE="${0##*/} [-r] [-p pkg_path] pkg [jailroot]"
[[ "$1" = -h ]] && { echo "USAGE $USAGE"; return 0; }
PATH=/sbin:/bin:/usr/sbin:/usr/bin
#a few functions of my own
#these are easier to type, and save me debugging time due to common things
#like quotes, $, etc
#takes a variable name as an arg, hence 'v' suffix
echov() { eval echo \"\$$1\"; }
isemptyv() { eval [ \${#$1} -eq 0 ]; }
notemptyv() { eval [ \${#$1} -gt 0 ]; }
alias usage='echo "USAGE $USAGE"'
alias xt='set -o xtrace'
alias xt-='set +o xtrace'
if [ $(id -u) -eq 0 ];then
echo "ERR should not run as root"
return 1
fi
if [[ "$(id -nG)" != *wsrc* ]];then
echo "ERR user not in wsrc; needed for pkg_add"
return 1
fi
#_pkg_dir read from PKG_PATH
#-p overrides PKG_PATH
#if neither set, uses default
_do_remote=
_pkg_dir=$PKG_PATH
while getopts :rp: _opt;do
case "$_opt" in
r) _do_remote=true ;;
p) _pkg_dir=$OPTARG
;;
:) echo "ERR no arg for '-$OPTARG'"; return 1 ;;
*) echo "ERR invalid arg '-$OPTARG'"; return 1 ;;
esac
done
shift $((OPTIND-1))
_pkg_dir=${_pkg_dir:-/usr/ports/packages/`arch -s`/all}
_pkg_dbdir=/var/db/pkg
export PKG_PATH="$_pkg_dir"
if [[ "$_pkg_dir" = *://* ]];then
if isemptyv _do_remote;then
echo "ERR invalid pkg dir; need -r for remote"
return 1
fi
if [[ "$_pkg_dir" = *:*:* ]];then
echo "ERR invalid pkg dir; only one dir allowed"
return 1
fi
else
_do_remote=
if [[ "$_pkg_dir" = *:* ]];then
echo "ERR invalid pkg dir; only one dir allowed"
return 1
fi
if [ ! -d "$_pkg_dir" ];then
echo "ERR pkg dir '$_pkg_dir' does not exist"
return 1
fi
fi
_pkg=$1
_jailroot=$2
if isemptyv _pkg;then
echo "ERR no package given"
usage
return 1
fi
_jailroot=${_jailroot:-/home/jail}
if [ ! -d "$_jailroot" ];then
echo "ERR no such dir '$_jailroot'"
return 1
fi
_jail_dbdir=$_jailroot$_pkg_dbdir
#set to /tmp to ignore installed package and use PKG_PATH
_pkg_file=$(PKG_DBDIR=/tmp pkg_info -c $_pkg | sed -n 's!^Information
for.*/!!p')
if isemptyv _pkg_file;then
echo "ERR could not find pkg '$_pkg'"
return 1
fi
##get list of required packages
##only works if /usr/ports is in sync with packages in PKG_PATH
#_pkgpath=$(pkg_info -P $_pkg | grep ^[a-z])
#if isemptyv _pkgpath;then
# echo "ERR cannot find port '$_pkg'"
# return 1
#fi
#_pkgpath=/usr/ports/$_pkgpath
#cd $_pkgpath
#_pkg_list=$(make print-run-depends) || return $?
#_pkg_list=$(echov _pkg_list | sed -e 's/.*"\([^"]*\)".*/\1 /' -e 's/ /.tgz /g')
#_pkg_list="${_pkg_list}$_pkg_file"
_depend_list=
function get_depends {
local _pkg=$1 _depend
for _depend in $(pkg_info -f $_pkg | sed -n 's/^@depend.*://p');do
echo $_pkg $_depend
[[ "$_depend_list" = *\ $_depend\ * ]] && continue
get_depends $_depend
_depend_list="$_depend_list $_depend "
done
}
_pkg_list="$(get_depends ${_pkg_file%.tgz} | tsort -r)"
isemptyv _pkg_list && _pkg_list=${_pkg_file%.tgz}
#if local go thru list to make sure pkgs are all there
if isemptyv _do_remote;then
for _pkg in $_pkg_list;do
[ -f $_pkg_dir/$_pkg.tgz ] && continue
echo "ERR pkg '$_pkg' not found"
return 1
done
fi
#keep list of libraries to avoid repeating find
_lib_tree=$(find /usr/{,X11R6}/lib -type f)
for _pkg in $_pkg_list;do
#check if package already installed
[ -d $_jail_dbdir/$_pkg ] && continue
#get list of @wantlib's that need copying
_lib_list=$(pkg_info -f $_pkg | sed -n 's/^@wantlib //p')
#get list of binaries in the pkg to check for more dependencies
_bin_list=$(pkg_info -f $_pkg | sed -n 's/^@bin //p')
#hopefully this handles all the quicks for @lib
for _lib in $_lib_list;do
_lib_relpath=
if [[ "$_lib" = */* ]];then
_lib_relpath=${_lib%/*}
_lib=${_lib##*/}
fi
_lib_ver_min=${_lib##*.}
_lib_name=${_lib%.*}
_lib_ver_maj=${_lib_name##*.}
_lib_name=${_lib_name%.*}
_lib_fn=lib$_lib_name.so.$_lib_ver_maj.$_lib_ver_min
if isemptyv _lib_relpath;then
#ASSUME want libraries only -maxdepth 1
_lib_pathn=$(find $_jailroot/usr/{,X11R6,local}/lib -maxdepth 1 -name
$_lib_fn)
else
_lib_pathn=$_jailroot/usr/local/$_lib_relpath/$_lib_fn
fi
[ -f "$_lib_pathn" ] && continue
#lib not in jail
_lib_pathn=$(echov _lib_tree | grep $_lib_fn$)
if isemptyv _lib_pathn || notemptyv _lib_relpath;then
#never copy from /usr/local
echo "WARN could not find $_lib_fn"
else
sudo cp -p $_lib_pathn $_jailroot/$_lib_pathn
fi
done
#ignore unsigned packages (-D unsigned) since i built them
#sudo env PKG_DBDIR=$_jail_dbdir pkg_add -m -B $_jailroot -D unsigned $_pkg
sudo env PKG_DBDIR=$_jail_dbdir pkg_add -m -B $_jailroot $_pkg
#rebuild ld.so.hints with new libraries
sudo chroot $_jailroot ldconfig /usr/{,X11R6,local}/lib
#go thru binaries in package and get additional dependencies
(cd $_jailroot/usr/local
for _bin in $_bin_list;do
for _lib in $(ldd $_bin | grep ' rlib ' | grep -v /usr/local/lib | awk
'{printf $7" "}');do
[ -f $_jailroot/$_lib ] || sudo cp -p $_lib $_jailroot/$_lib
done
done)
done
#env PKG_DBDIR=$_dbdir pkg_add -B /home/chroot $_pkg
#env PKG_DBDIR=$_dbdir pkg_delete -B /home/chroot $_pkg