Index: etc/defaults/devfs.rules =================================================================== --- etc/defaults/devfs.rules (revision 227342) +++ etc/defaults/devfs.rules (working copy) @@ -84,3 +84,9 @@ add path stderr unhide add include $devfsrules_hide_all add include $devfsrules_unhide_basic add include $devfsrules_unhide_login + +# Jail with zfs support +# +[devfsrules_jail_zfs=5] +add include $devfsrules_jail +add path zfs unhide Index: etc/defaults/rc.conf =================================================================== --- etc/defaults/rc.conf (revision 227342) +++ etc/defaults/rc.conf (working copy) @@ -667,19 +667,17 @@ opensm_enable="NO" # Opensm(8) for infiniband devi jail_enable="NO" # Set to NO to disable starting of any jails jail_parallel_start="NO" # Start jails in the background jail_list="" # Space separated list of names of jails -jail_set_hostname_allow="YES" # Allow root user in a jail to change its hostname -jail_socket_unixiproute_only="YES" # Route only TCP/IP within a jail -jail_sysvipc_allow="NO" # Allow SystemV IPC use from within a jail # # To use rc's built-in jail infrastructure create entries for # each jail, specified in jail_list, with the following variables. # NOTES: # - replace 'example' with the jail's name. -# - except rootdir, hostname, ip and the _multi addresses, +# - except rootdir, hostname, zfs, ip and the _multi addresses, # all of the following variables may be made global jail variables # if you don't specify a jail name (ie. jail_interface, jail_devfs_ruleset). # +#jail_example_name="myjail" # Jail's name (defaults to name in jail_list) #jail_example_rootdir="/usr/jail/default" # Jail's root directory #jail_example_hostname="default.domain.com" # Jail's hostname #jail_example_interface="" # Jail's interface variable to create IP aliases on @@ -692,13 +690,24 @@ jail_list="" # Space separated list of names of j # specified using a trailing number #jail_example_exec_stop="/bin/sh /etc/rc.shutdown" # command to execute in jail for stopping #jail_example_devfs_enable="NO" # mount devfs in the jail -#jail_example_devfs_ruleset="ruleset_name" # devfs ruleset to apply to jail - - # usually you want "devfsrules_jail". +#jail_example_devfs_ruleset="ruleset_name" # devfs ruleset to apply to jail, defaults + # to "devfsrules_jail" or if jail_example_zfs is + # set to "devfsrules_jail_zfs". #jail_example_fdescfs_enable="NO" # mount fdescfs in the jail #jail_example_procfs_enable="NO" # mount procfs in jail #jail_example_mount_enable="NO" # mount/umount jail's fs #jail_example_fstab="" # fstab(5) for mount/umount #jail_example_flags="-l -U root" # flags for jail(8) +#jail_example_persist="NO" # Create a persistent jail. Jail will be made + # non-persistent if stopped via /etc/rc.d/jail +#jail_example_params="" # Space-separated list of additional + # user-supplied parameters for jail(8) +#jail_example_zfs="" # Space-separated list of ZFS datasets to be + # managed from this jail. For proper operation, + # allow.mount=1 and enforce_statfs=1 (or 0) + # must be added to jail_example_params. + # The "jailed" property must be set to "on" + # on these datasets before starting the jail. ############################################################## ### Define source_rc_confs, the mechanism used by /etc/rc.* ## Index: etc/rc.d/jail =================================================================== --- etc/rc.d/jail (revision 227342) +++ etc/rc.d/jail (working copy) @@ -22,7 +22,33 @@ rcvar=`set_rcvar` start_precmd="jail_prestart" start_cmd="jail_start" stop_cmd="jail_stop" +extra_commands="status create remove" +status_cmd="jail_status" +create_cmd="jail_create" +remove_cmd="jail_remove" +# init_name _j +# Initialize the name variable for jail _j. +# +init_name() +{ + _j="$1" + + if [ -z "$_j" ]; then + warn "init_name: you must specify a jail" + return + fi + + eval _name=\"\$jail_${_j}_name\" + + if [ -z "${_name}" ]; then + _name=${_j} + fi + + # Debugging aid + debug "$_j name: $_name" +} + # init_variables _j # Initialize the various jail variables for jail _j. # @@ -43,6 +69,8 @@ init_variables() eval _ip=\"\$jail_${_j}_ip\" eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\" eval _exec=\"\$jail_${_j}_exec\" + eval _params=\"\${jail_${_j}_params:-${jail_params}}\" + eval _zfs=\"\${jail_${_j}_zfs:-}\" i=0 while : ; do @@ -98,6 +126,11 @@ init_variables() fi # The default jail ruleset will be used by rc.subr if none is specified. + if [ -n "${_zfs}" ]; then + eval jail_devfs_ruleset=\"${jail_devfs_ruleset:-devfsrules_jail_zfs}\" + else + eval jail_devfs_ruleset=\"${jail_devfs_ruleset:-devfsrules_jail}\" + fi eval _ruleset=\"\${jail_${_j}_devfs_ruleset:-${jail_devfs_ruleset}}\" eval _devfs=\"\${jail_${_j}_devfs_enable:-${jail_devfs_enable}}\" [ -z "${_devfs}" ] && _devfs="NO" @@ -116,6 +149,8 @@ init_variables() eval _consolelog=\"\${jail_${_j}_consolelog:-${jail_consolelog}}\" [ -z "${_consolelog}" ] && _consolelog="/var/log/jail_${_j}_console.log" eval _fib=\"\${jail_${_j}_fib:-${jail_fib}}\" + eval _persist=\"\${jail_${_j}_persist:-${jail_persist}}\" + [ -z "${_persist}" ] && _persist="NO" # Debugging aid # @@ -134,6 +169,9 @@ init_variables() debug "$_j procdir: $_procdir" debug "$_j ruleset: $_ruleset" debug "$_j fstab: $_fstab" + debug "$_j persist: $_persist" + debug "$_j params: $_params" + debug "$_j zfs: $_zfs" i=0 while : ; do @@ -194,39 +232,35 @@ init_variables() debug "$_j flags: $_flags" debug "$_j consolelog: $_consolelog" - if [ -z "${_hostname}" ]; then - err 3 "$name: No hostname has been defined for ${_j}" - fi + _params="${_params} name=${_name}" + [ -n "${_hostname}" ] && _params="${_params} host.hostname=${_hostname}" + if [ -z "${_rootdir}" ]; then err 3 "$name: No root directory has been defined for ${_j}" + else + _params="${_params} path=${_rootdir}" fi -} -# set_sysctl rc_knob mib msg -# If the mib sysctl is set according to what rc_knob -# specifies, this function does nothing. However if -# rc_knob is set differently than mib, then the mib -# is set accordingly and msg is displayed followed by -# an '=" sign and the word 'YES' or 'NO'. -# -set_sysctl() -{ - _knob="$1" - _mib="$2" - _msg="$3" + # Compatibility with old sysctl knobs + # + eval _set_hostname_allow="\${jail_${_j}_set_hostname_allow:-${jail_set_hostname_allow}}" + eval _socket_unixiproute_only="\${jail_${_j}_socket_unixiproute_only:-${jail_socket_unixiproute_only}}" + eval _sysvipc_allow="\${jail_${_j}_sysvipc_allow:-${jail_sysvipc_allow}}" + [ -z "${_set_hostname_allow}" ] && _set_hostname_allow="YES" + [ -z "${_socket_unixiproute_only}" ] && _socket_unixiproute_only="YES" + [ -z "${_sysvipc_allow}" ] && _sysvipc_allow="NO" - _current=`${SYSCTL} -n $_mib 2>/dev/null` - if checkyesno $_knob ; then - if [ "$_current" -ne 1 ]; then - echo -n " ${_msg}=YES" - ${SYSCTL} 1>/dev/null ${_mib}=1 - fi - else - if [ "$_current" -ne 0 ]; then - echo -n " ${_msg}=NO" - ${SYSCTL} 1>/dev/null ${_mib}=0 - fi + if ! checkyesno _set_hostname_allow; then + _params="${_params} allow.set_hostname=0" fi + + if ! checkyesno _socket_unixiproute_only; then + _params="${_params} allow.socket_af=1" + fi + + if checkyesno _sysvipc_allow; then + _params="${_params} allow.sysvipc=1" + fi } # is_current_mountpoint() @@ -329,14 +363,21 @@ jail_umount_fs() # jail_mount_fstab() { - local _device _mountpt _rest + local _device _mountpt _field3 _field4 _field5 _field6 - while read _device _mountpt _rest; do - case ":${_device}" in - :#* | :) + while read _device _mountpt _field3 _field4 _field5 _field6; do + case ":${_device}:" in + :#*:|::) + # empty line or comment continue ;; esac + if [ -z "${_field5}" ]; then + # a fstab entry needs at least 5 fields + # everything else is suspicious + warn "${_device} ${_mountpt} ${_field3} ${_field4} ${_field5} ${_field6} not conforming to 5 or more whitespace separated fields" + return + fi if is_symlinked_mountpoint ${_mountpt}; then warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}" return @@ -345,6 +386,38 @@ jail_mount_fstab() mount -a -F "${_fstab}" } +# jail_zfs_jailin +# Make zfs datasets manageable from inside a jail +# the "jailed" dataset property must be set to "on" +jail_zfs_jailin() +{ + if [ -n "${_zfs}" ]; then + for _ds in ${_zfs}; do + _jailed=`zfs get -H jailed ${_ds} 2>/dev/null | awk '{ print $3 }'` + if [ "${_jailed}" = "on" ]; then + debug "${j} jailing zfs dataset: ${_ds}" + zfs jail "${_jail_id}" ${_ds} 2>/dev/null + fi + done + fi +} + +# jail_zfs_jailout +# Unjail zfs datasets +# the "jailed" dataset property must be set to "on" +jail_zfs_jailout() +{ + if [ -n "${_zfs}" ]; then + for _ds in ${_zfs}; do + _jailed=`zfs get -H jailed ${_ds} 2>/dev/null | awk '{ print $3 }'` + if [ "${_jailed}" = "on" ]; then + debug "${j} unjailing zfs dataset: ${_ds}" + zfs unjail "${_jail_id}" ${_ds} 2>/dev/null + fi + done + fi +} + # jail_show_addresses jail # Debug print the input for the given _multi aliases # for a jail for init_variables(). @@ -483,10 +556,27 @@ jail_handle_ips_option() *) ;; esac - # Append address to list of addresses for the jail command. - case "${_addrl}" in - "") _addrl="${_addr}" ;; - *) _addrl="${_addrl},${_addr}" ;; + case "${_type}" in + inet) + # Append address to list of ipv4 addresses for the + # jail command. + case "${_addrl}" in + "") _addrl="${_addr}" ;; + *) _addrl="${_addrl},${_addr}" ;; + esac + ;; + inet6) + # Append address to list of ipv6 addresses for the + # jail command. + case "${_addrl6}" in + "") _addrl6="${_addr}" ;; + *) _addrl6="${_addrl6},${_addr}" ;; + esac + ;; + *) warn "Could not determine address family. Not going" \ + "to set address '${_addr}' for ${_jail}." + continue + ;; esac # Configure interface alias if requested by a given interface @@ -494,14 +584,7 @@ jail_handle_ips_option() case "${_iface}" in "") continue ;; esac - case "${_type}" in - inet) ;; - inet6) ;; - *) warn "Could not determine address family. Not going" \ - "to ${_action} address '${_addr}' for ${_jail}." - continue - ;; - esac + case "${_action}" in add) ifconfig ${_iface} ${_type} ${_addr}${_mask} alias ;; @@ -554,86 +637,176 @@ jail_prestart() fi } +jail_init() +{ + _jail=$1 + init_name $_jail + init_variables $_jail + _jail_id="$(jls -j ${_name} jid 2>/dev/null)" + if [ -n "${_jail_id}" ]; then + return 1 + fi + _addrl="" + _addrl6="" + jail_ips "add" + + [ -n "${_addrl}" ] && _params="${_params} ip4.addr=${_addrl}" + [ -n "${_addrl6}" ] && _params="${_params} ip6.addr=${_addrl6}" + + if [ -n "${_fib}" ]; then + _setfib="setfib -F '${_fib}'" + else + _setfib="" + fi + if checkyesno _mount; then + info "Mounting fstab for jail ${_jail} (${_fstab})" + if [ ! -f "${_fstab}" ]; then + err 3 "$name: ${_fstab} does not exist" + fi + jail_mount_fstab + fi + if checkyesno _devfs; then + # If devfs is already mounted here, skip it. + df -t devfs "${_devdir}" >/dev/null + if [ $? -ne 0 ]; then + if is_symlinked_mountpoint ${_devdir}; then + warn "${_devdir} has symlink as parent - not starting jail ${_jail}" + return 2 + fi + info "Mounting devfs on ${_devdir}" + devfs_mount_jail "${_devdir}" ${_ruleset} + # Transitional symlink for old binaries + if [ ! -L "${_devdir}/log" ]; then + __pwd="`pwd`" + cd "${_devdir}" + ln -sf ../var/run/log log + cd "$__pwd" + fi + fi + + # XXX - It seems symlinks don't work when there + # is a devfs(5) device of the same name. + # Jail console output + # __pwd="`pwd`" + # cd "${_devdir}" + # ln -sf ../var/log/console console + # cd "$__pwd" + fi + if checkyesno _fdescfs; then + if is_symlinked_mountpoint ${_fdescdir}; then + warn "${_fdescdir} has symlink as parent, not mounting" + else + info "Mounting fdescfs on ${_fdescdir}" + mount -t fdescfs fdesc "${_fdescdir}" + fi + fi + if checkyesno _procfs; then + if is_symlinked_mountpoint ${_procdir}; then + warn "${_procdir} has symlink as parent, not mounting" + else + info "Mounting procfs onto ${_procdir}" + if [ -d "${_procdir}" ] ; then + mount -t procfs proc "${_procdir}" + fi + fi + fi + + _jail_id=`${_setfib} jail -i ${_flags} -c \ + ${_params} persist=1` + + if [ -n "${_jail_id}" ]; then + jail_zfs_jailin + return 0 + else + return 2 + fi +} + +jail_destroy() +{ + _jail=$1 + init_name $_jail + init_variables $_jail + _jail_id="$(jls -j ${_name} jid 2>/dev/null)" + if [ -z "${_jail_id}" ]; then + return 1 + fi + jail -m jid=${_jail_id} persist=1 + killall -j ${_jail_id} -TERM > /dev/null 2>&1 + sleep 1 + killall -j ${_jail_id} -KILL > /dev/null 2>&1 + + jail_umount_fs + jail_zfs_jailout + jail -m jid="${_jail_id}" persist=0 + return 0 +} + +jail_create() +{ + echo -n 'Creating jails:' + for _jail in ${jail_list} + do + init_name $_jail + init_variables $_jail + jail_init $_jail + _ret=$? + if [ "$_ret" -eq 0 ]; then + echo -n " ${_jail}" + elif [ "$_ret" -eq 1 ]; then + echo -n " [${_jail} already exists (JID: ${_jail_id})]" + else + echo -n "[${_jail} init error]" + fi + done + echo "." +} + +jail_remove() +{ + echo -n 'Removing jails:' + for _jail in ${jail_list} + do + init_name $_jail + _jail_id="$(jls -j ${_name} jid 2>/dev/null)" + if [ -z "${_jail_id}" ]; then + continue + elif `pgrep -j ${_jail_id} >/dev/null 2>&1`; then + echo -n " [${_jail} has running processes (JID: ${_jail_id})]" + continue + fi + jail_destroy $_jail + if [ "$?" -eq 0 ] ; then + echo -n " ${_jail}" + fi + done + echo "." +} + jail_start() { - echo -n 'Configuring jails:' - set_sysctl jail_set_hostname_allow security.jail.set_hostname_allowed \ - set_hostname_allow - set_sysctl jail_socket_unixiproute_only \ - security.jail.socket_unixiproute_only unixiproute_only - set_sysctl jail_sysvipc_allow security.jail.sysvipc_allowed \ - sysvipc_allow - echo '.' - echo -n 'Starting jails:' _tmp_dir=`mktemp -d /tmp/jail.XXXXXXXX` || \ err 3 "$name: Can't create temp dir, exiting..." + for _jail in ${jail_list} do + init_name $_jail init_variables $_jail - if [ -f /var/run/jail_${_jail}.id ]; then - echo -n " [${_hostname} already running (/var/run/jail_${_jail}.id exists)]" - continue; - fi - _addrl="" - jail_ips "add" - if [ -n "${_fib}" ]; then - _setfib="setfib -F '${_fib}'" + _jail_id="$(jls -j ${_name} jid 2>/dev/null)" + if [ -n "${_jail_id}" ]; then + if `pgrep -j ${_jail_id} >/dev/null 2>&1`; then + echo -n " [${_jail} already running (JID: ${_jail_id})]" + continue + fi else - _setfib="" - fi - if checkyesno _mount; then - info "Mounting fstab for jail ${_jail} (${_fstab})" - if [ ! -f "${_fstab}" ]; then - err 3 "$name: ${_fstab} does not exist" + jail_init $_jail + if [ "$?" -ne 0 ] ; then + echo -n " [${_jail} initialization error]" + continue fi - jail_mount_fstab fi - if checkyesno _devfs; then - # If devfs is already mounted here, skip it. - df -t devfs "${_devdir}" >/dev/null - if [ $? -ne 0 ]; then - if is_symlinked_mountpoint ${_devdir}; then - warn "${_devdir} has symlink as parent - not starting jail ${_jail}" - continue - fi - info "Mounting devfs on ${_devdir}" - devfs_mount_jail "${_devdir}" ${_ruleset} - # Transitional symlink for old binaries - if [ ! -L "${_devdir}/log" ]; then - __pwd="`pwd`" - cd "${_devdir}" - ln -sf ../var/run/log log - cd "$__pwd" - fi - fi - # XXX - It seems symlinks don't work when there - # is a devfs(5) device of the same name. - # Jail console output - # __pwd="`pwd`" - # cd "${_devdir}" - # ln -sf ../var/log/console console - # cd "$__pwd" - fi - if checkyesno _fdescfs; then - if is_symlinked_mountpoint ${_fdescdir}; then - warn "${_fdescdir} has symlink as parent, not mounting" - else - info "Mounting fdescfs on ${_fdescdir}" - mount -t fdescfs fdesc "${_fdescdir}" - fi - fi - if checkyesno _procfs; then - if is_symlinked_mountpoint ${_procdir}; then - warn "${_procdir} has symlink as parent, not mounting" - else - info "Mounting procfs onto ${_procdir}" - if [ -d "${_procdir}" ] ; then - mount -t procfs proc "${_procdir}" - fi - fi - fi _tmp_jail=${_tmp_dir}/jail.$$ i=0 @@ -644,12 +817,13 @@ jail_start() i=$((i + 1)) done - eval ${_setfib} jail ${_flags} -i ${_rootdir} ${_hostname} \ - \"${_addrl}\" ${_exec_start} > ${_tmp_jail} 2>&1 \ - ${_tmp_jail} 2>&1 \ + ${_consolelog} - echo ${_jail_id} > /var/run/jail_${_jail}.id - - i=0 - while : ; do - eval out=\"\${_exec_poststart${i}:-''}\" - [ -z "$out" ] && break - ${out} - i=$((i + 1)) - done else - jail_umount_fs - jail_ips "del" echo " cannot start jail \"${_jail}\": " tail +2 ${_tmp_jail} fi rm -f ${_tmp_jail} done - rmdir ${_tmp_dir} - echo '.' + echo "." } jail_stop() @@ -690,46 +851,71 @@ jail_stop() echo -n 'Stopping jails:' for _jail in ${jail_list} do - if [ -f "/var/run/jail_${_jail}.id" ]; then - _jail_id=$(cat /var/run/jail_${_jail}.id) - if [ ! -z "${_jail_id}" ]; then - init_variables $_jail + init_name $_jail + _jail_id=$(jls -j ${_name} jid 2>/dev/null) + if [ -n "${_jail_id}" ]; then + init_variables $_jail - i=0 - while : ; do - eval out=\"\${_exec_prestop${i}:-''}\" - [ -z "$out" ] && break - ${out} - i=$((i + 1)) - done + if ! `pgrep -j ${_jail_id} >/dev/null 2>&1`; then + echo -n " [${_jail} has no running processes (JID: ${_jail_id})]" + continue + fi - if [ -n "${_exec_stop}" ]; then - eval env -i /usr/sbin/jexec ${_jail_id} ${_exec_stop} \ - >> ${_consolelog} 2>&1 - fi - killall -j ${_jail_id} -TERM > /dev/null 2>&1 - sleep 1 - killall -j ${_jail_id} -KILL > /dev/null 2>&1 - jail_umount_fs - echo -n " $_hostname" + i=0 + while : ; do + eval out=\"\${_exec_prestop${i}:-''}\" + [ -z "$out" ] && break + ${out} + i=$((i + 1)) + done - i=0 - while : ; do - eval out=\"\${_exec_poststop${i}:-''}\" - [ -z "$out" ] && break - ${out} - i=$((i + 1)) - done + if [ -n "${_exec_stop}" ]; then + eval env -i /usr/sbin/jexec ${_jail_id} ${_exec_stop} \ + >> ${_consolelog} 2>&1 fi - jail_ips "del" - rm /var/run/jail_${_jail}.id - else - echo " cannot stop jail ${_jail}. No jail id in /var/run" + + jail -m jid=${_jail_id} persist=1 + killall -j ${_jail_id} -TERM > /dev/null 2>&1 + sleep 1 + killall -j ${_jail_id} -KILL > /dev/null 2>&1 + + if ! checkyesno _persist; then + jail_destroy $_jail + fi + echo -n " $_jail" + + i=0 + while : ; do + eval out=\"\${_exec_poststop${i}:-''}\" + [ -z "$out" ] && break + ${out} + i=$((i + 1)) + done + fi + jail_ips "del" done echo '.' } +jail_status() +{ + for _jail in ${jail_list} + do + init_name $_jail + _jail_id="$(jls -j ${_name} jid 2>/dev/null)" + if [ -n "${_jail_id}" ]; then + _procs=`pgrep -j ${_jail_id} | grep -c .` + if [ "${_procs}" -gt 0 ]; then + _procs=$((${_procs} - 1)) + fi + echo "${_jail} online (JID: ${_jail_id}, processes: ${_procs})" + else + echo "${_jail} offline" + fi + done +} + load_rc_config $name cmd="$1" if [ $# -gt 0 ]; then