Date: Sat, 27 Aug 2011 10:47:12 -0700 From: Devin Teske <devin.teske@fisglobal.com> To: FreeBSD Hackers <freebsd-hackers@freebsd.org> Cc: Julian Elischer <julian@freebsd.org>, FreeBSD Jail <freebsd-jail@freebsd.org>, FreeBSD RC <freebsd-rc@freebsd.org> Subject: [PATCH] Add /etc/rc.d/vimage startup script for creating vnet jails Message-ID: <CAC979C8-3129-4E62-9D76-D1D0CCE001F0@fisglobal.com>
next in thread | raw e-mail | index | archive | help
[-- Attachment #1 --]
Hi All,
I'd like to submit a patch for review (attached) that adds a new /etc/rc.d script named "vimage".
_____________
The information contained in this message is proprietary and/or confidential. If you are not the intended recipient, please: (i) delete the message and all copies; (ii) do not disclose, distribute or use the message in any manner; and (iii) notify the sender immediately. In addition, please be aware that any message addressed to our domain is subject to archiving and review by persons other than the intended recipient. Thank you.
_____________
[-- Attachment #2 --]
--- etc/defaults/rc.conf.orig Fri Aug 26 20:36:52 2011
+++ etc/defaults/rc.conf Sat Aug 27 10:34:54 2011
@@ -697,6 +697,43 @@
#jail_example_flags="-l -U root" # flags for jail(8)
##############################################################
+### Vimage Configuration #####################################
+##############################################################
+vimage_enable="NO" # Set to NO to disable starting of any vimages
+vimage_parallel_start="NO" # Start vimages in the background
+vimage_list="" # Space separated list of names of vimages
+vimage_set_hostname_allow="YES" # Allow root user in a vimage to change its hostname
+vimage_socket_unixiproute_only="NO" # Route only TCP/IP within a vimage
+vimage_sysvipc_allow="YES" # Allow SystemV IPC use from within a vimage
+
+#
+# To use rc's built-in vimage infrastructure create entries for
+# each vimage, specified in vimage_list, with the following variables.
+# NOTES:
+# - replace 'example' with the vimage's name.
+# - except rootdir, and hostname, all of the following variables may be made
+# global vimage variables if you don't specify a vimage name (ie.
+# vimage_fib, vimage_devfs_ruleset).
+#
+#vimage_example_rootdir="/usr/jail/default" # Vimage's root directory
+#vimage_example_hostname="default.domain.com" # Vimage's hostname
+#vimage_example_vnets="epair0b" # Vimage's vnet interfaces
+#vimage_example_exec_start="/bin/sh /etc/rc" # command to execute in vimage for starting
+#vimage_example_services="sshd ipfw zfs" # services to start after starting vimage
+#vimage_example_exec_afterstart0="/bin/sh command" # command to execute after the one for
+ # starting the vimage. More than one can
+ # be specified using a trailing number
+#vimage_example_exec_stop="/bin/sh /etc/rc.shutdown" # command to execute in vimage for stopping
+#vimage_example_devfs_enable="NO" # mount devfs in the vimage
+#vimage_example_devfs_ruleset="ruleset_name" # devfs ruleset to apply to vimage -
+ # usually you want "devfsrules_jail".
+#vimage_example_fdescfs_enable="NO" # mount fdescfs in the vimage
+#vimage_example_procfs_enable="NO" # mount procfs in vimage
+#vimage_example_mount_enable="NO" # mount/umount vimage's fs
+#vimage_example_fstab="" # fstab(5) for mount/umount
+#vimage_example_flags="-l -U root" # flags for jail(8)
+
+##############################################################
### Define source_rc_confs, the mechanism used by /etc/rc.* ##
### scripts to source rc_conf_files overrides safely. ##
##############################################################
--- etc/rc.d/vimage.orig Sat Aug 27 10:26:53 2011
+++ etc/rc.d/vimage Sat Aug 27 10:36:03 2011
@@ -0,0 +1,551 @@
+#!/bin/sh
+#
+# $FreeBSD$
+#
+
+# PROVIDE: vimage
+# REQUIRE: LOGIN cleanvar
+# BEFORE: securelevel
+# KEYWORD: nojail shutdown
+
+# WARNING: This script deals with untrusted data (the data and
+# processes inside the vimage) and care must be taken when changing the
+# code related to this! If you have any doubt whether a change is
+# correct and have security impact, please get the patch reviewed by
+# the FreeBSD Security Team prior to commit.
+
+. /etc/rc.subr
+
+name="vimage"
+rcvar=`set_rcvar`
+
+start_precmd="vimage_prestart"
+start_cmd="vimage_start"
+stop_cmd="vimage_stop"
+
+# init_variables _v
+# Initialize the various vimage variables for vimage _v.
+#
+init_variables()
+{
+ _v="$1"
+
+ if [ -z "$_v" ]; then
+ warn "init_variables: you must specify a vimage"
+ return
+ fi
+
+ eval _rootdir=\"\$vimage_${_v}_rootdir\"
+ _devdir="${_rootdir}/dev"
+ _fdescdir="${_devdir}/fd"
+ _procdir="${_rootdir}/proc"
+ eval _hostname=\"\$vimage_${_v}_hostname\"
+ eval _vnets=\"\$vimage_${_v}_vnets\"
+ eval _exec=\"\$vimage_${_v}_exec\"
+
+ i=0
+ while : ; do
+ eval _exec_prestart${i}=\"\${vimage_${_v}_exec_prestart${i}:-\${vimage_exec_prestart${i}}}\"
+ [ -z "$(eval echo \"\$_exec_prestart${i}\")" ] && break
+ i=$((i + 1))
+ done
+
+ eval _exec_start=\"\${vimage_${_v}_exec_start:-${vimage_exec_start}}\"
+ eval _services=\"\${vimage_${_v}_services:-${vimage_services}}\"
+
+ i=1
+ while : ; do
+ eval _exec_afterstart${i}=\"\${vimage_${_v}_exec_afterstart${i}:-\${vimage_exec_afterstart${i}}}\"
+ [ -z "$(eval echo \"\$_exec_afterstart${i}\")" ] && break
+ i=$((i + 1))
+ done
+
+ i=0
+ while : ; do
+ eval _exec_poststart${i}=\"\${vimage_${_v}_exec_poststart${i}:-\${vimage_exec_poststart${i}}}\"
+ [ -z "$(eval echo \"\$_exec_poststart${i}\")" ] && break
+ i=$((i + 1))
+ done
+
+ i=0
+ while : ; do
+ eval _exec_prestop${i}=\"\${vimage_${_v}_exec_prestop${i}:-\${vimage_exec_prestop${i}}}\"
+ [ -z "$(eval echo \"\$_exec_prestop${i}\")" ] && break
+ i=$((i + 1))
+ done
+
+ eval _exec_stop=\"\${vimage_${_v}_exec_stop:-${vimage_exec_stop}}\"
+
+ i=0
+ while : ; do
+ eval _exec_poststop${i}=\"\${vimage_${_v}_exec_poststop${i}:-\${vimage_exec_poststop${i}}}\"
+ [ -z "$(eval echo \"\$_exec_poststop${i}\")" ] && break
+ i=$((i + 1))
+ done
+
+ if [ -n "${_exec}" ]; then
+ # simple/backward-compatible execution
+ _exec_start="${_exec}"
+ _exec_stop=""
+ else
+ # flexible execution
+ if [ -z "${_exec_start}" ]; then
+ _exec_start="/bin/sh /etc/rc"
+ if [ -z "${_exec_stop}" ]; then
+ _exec_stop="/bin/sh /etc/rc.shutdown"
+ fi
+ fi
+ fi
+
+ # The default jail ruleset will be used by rc.subr if none is specified.
+ eval _ruleset=\"\${vimage_${_v}_devfs_ruleset:-${vimage_devfs_ruleset}}\"
+ eval _devfs=\"\${vimage_${_v}_devfs_enable:-${vimage_devfs_enable}}\"
+ [ -z "${_devfs}" ] && _devfs="NO"
+ eval _fdescfs=\"\${vimage_${_v}_fdescfs_enable:-${vimage_fdescfs_enable}}\"
+ [ -z "${_fdescfs}" ] && _fdescfs="NO"
+ eval _procfs=\"\${vimage_${_v}_procfs_enable:-${vimage_procfs_enable}}\"
+ [ -z "${_procfs}" ] && _procfs="NO"
+
+ eval _mount=\"\${vimage_${_v}_mount_enable:-${vimage_mount_enable}}\"
+ [ -z "${_mount}" ] && _mount="NO"
+ # "/etc/fstab.${_v}" will be used for {,u}mount(8) if none is specified.
+ eval _fstab=\"\${vimage_${_v}_fstab:-${vimage_fstab}}\"
+ [ -z "${_fstab}" ] && _fstab="/etc/fstab.${_v}"
+ eval _flags=\"\${vimage_${_v}_flags:-${vimage_flags}}\"
+ [ -z "${_flags}" ] && _flags="-l -U root"
+ eval _consolelog=\"\${vimage_${_v}_consolelog:-${vimage_consolelog}}\"
+ [ -z "${_consolelog}" ] && _consolelog="/var/log/vimage_${_v}_console.log"
+
+ # Debugging aid
+ #
+ debug "$_v devfs enable: $_devfs"
+ debug "$_v fdescfs enable: $_fdescfs"
+ debug "$_v procfs enable: $_procfs"
+ debug "$_v mount enable: $_mount"
+ debug "$_v hostname: $_hostname"
+ debug "$_v vnets: $_vnets"
+ debug "$_v services: $_services"
+ debug "$_v root: $_rootdir"
+ debug "$_v devdir: $_devdir"
+ debug "$_v fdescdir: $_fdescdir"
+ debug "$_v procdir: $_procdir"
+ debug "$_v ruleset: $_ruleset"
+ debug "$_v fstab: $_fstab"
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_prestart${i}:-''}\"
+ if [ -z "$out" ]; then
+ break
+ fi
+ debug "$_v exec pre-start #${i}: ${out}"
+ i=$((i + 1))
+ done
+
+ debug "$_v exec start: $_exec_start"
+
+ i=1
+ while : ; do
+ eval out=\"\${_exec_afterstart${i}:-''}\"
+
+ if [ -z "$out" ]; then
+ break;
+ fi
+
+ debug "$_v exec after start #${i}: ${out}"
+ i=$((i + 1))
+ done
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_poststart${i}:-''}\"
+ if [ -z "$out" ]; then
+ break
+ fi
+ debug "$_v exec post-start #${i}: ${out}"
+ i=$((i + 1))
+ done
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_prestop${i}:-''}\"
+ if [ -z "$out" ]; then
+ break
+ fi
+ debug "$_v exec pre-stop #${i}: ${out}"
+ i=$((i + 1))
+ done
+
+ debug "$_v exec stop: $_exec_stop"
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_poststop${i}:-''}\"
+ if [ -z "$out" ]; then
+ break
+ fi
+ debug "$_v exec post-stop #${i}: ${out}"
+ i=$((i + 1))
+ done
+
+ debug "$_v flags: $_flags"
+ debug "$_v consolelog: $_consolelog"
+
+ if [ -z "${_hostname}" ]; then
+ err 3 "$name: No hostname has been defined for ${_v}"
+ fi
+ if [ -z "${_rootdir}" ]; then
+ err 3 "$name: No root directory has been defined for ${_v}"
+ 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"
+
+ _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
+ fi
+}
+
+# is_current_mountpoint()
+# Is the directory mount point for a currently mounted file
+# system?
+#
+is_current_mountpoint()
+{
+ local _dir _dir2
+
+ _dir=$1
+
+ _dir=`echo $_dir | sed -Ee 's#//+#/#g' -e 's#/$##'`
+ [ ! -d "${_dir}" ] && return 1
+ _dir2=`df ${_dir} | tail +2 | awk '{ print $6 }'`
+ [ "${_dir}" = "${_dir2}" ]
+ return $?
+}
+
+# is_symlinked_mountpoint()
+# Is a mount point, or any of its parent directories, a symlink?
+#
+is_symlinked_mountpoint()
+{
+ local _dir
+
+ _dir=$1
+
+ [ -L "$_dir" ] && return 0
+ [ "$_dir" = "/" ] && return 1
+ is_symlinked_mountpoint `dirname $_dir`
+ return $?
+}
+
+# secure_umount
+# Try to unmount a mount point without being vulnerable to
+# symlink attacks.
+#
+secure_umount()
+{
+ local _dir
+
+ _dir=$1
+
+ if is_current_mountpoint ${_dir}; then
+ umount -f ${_dir} >/dev/null 2>&1
+ else
+ debug "Nothing mounted on ${_dir} - not unmounting"
+ fi
+}
+
+
+# vimage_umount_fs
+# This function unmounts certain special filesystems in the
+# currently selected vimage. The caller must call the init_variables()
+# routine before calling this one.
+#
+vimage_umount_fs()
+{
+ local _device _mountpt _rest
+
+ if checkyesno _fdescfs; then
+ if [ -d "${_fdescdir}" ] ; then
+ secure_umount ${_fdescdir}
+ fi
+ fi
+ if checkyesno _devfs; then
+ if [ -d "${_devdir}" ] ; then
+ secure_umount ${_devdir}
+ fi
+ fi
+ if checkyesno _procfs; then
+ if [ -d "${_procdir}" ] ; then
+ secure_umount ${_procdir}
+ fi
+ fi
+ if checkyesno _mount; then
+ [ -f "${_fstab}" ] || warn "${_fstab} does not exist"
+ tail -r ${_fstab} | while read _device _mountpt _rest; do
+ case ":${_device}" in
+ :#* | :)
+ continue
+ ;;
+ esac
+ secure_umount ${_mountpt}
+ done
+ fi
+}
+
+# vimage_mount_fstab()
+# Mount file systems from a per vimage fstab while trying to
+# secure against symlink attacks at the mount points.
+#
+# If we are certain we cannot secure against symlink attacks we
+# do not mount all of the file systems (since we cannot just not
+# mount the file system with the problematic mount point).
+#
+# The caller must call the init_variables() routine before
+# calling this one.
+#
+vimage_mount_fstab()
+{
+ local _device _mountpt _rest
+
+ while read _device _mountpt _rest; do
+ case ":${_device}" in
+ :#* | :)
+ continue
+ ;;
+ esac
+ if is_symlinked_mountpoint ${_mountpt}; then
+ warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}"
+ return
+ fi
+ done <${_fstab}
+ mount -a -F "${_fstab}"
+}
+
+vimage_prestart()
+{
+ if checkyesno vimage_parallel_start; then
+ command_args="&"
+ fi
+}
+
+vimage_start()
+{
+ echo -n 'Configuring vimages:'
+ set_sysctl vimage_set_hostname_allow \
+ security.jail.set_hostname_allowed \
+ set_hostname_allow
+ set_sysctl vimage_socket_unixiproute_only \
+ security.jail.socket_unixiproute_only unixiproute_only
+ set_sysctl vimage_sysvipc_allow security.jail.sysvipc_allowed \
+ sysvipc_allow
+ echo '.'
+
+ echo -n 'Starting vimages:'
+ _tmp_dir=`mktemp -d /tmp/vimage.XXXXXXXX` || \
+ err 3 "$name: Can't create temp dir, exiting..."
+ for _vimage in ${vimage_list}
+ do
+ init_variables $_vimage
+ if [ -f /var/run/vimage_${_vimage}.id ]; then
+ echo -n " [${_hostname} already running (/var/run/vimage_${_vimage}.id exists)]"
+ continue;
+ fi
+ if checkyesno _mount; then
+ info "Mounting fstab for vimage ${_vimage} (${_fstab})"
+ if [ ! -f "${_fstab}" ]; then
+ err 3 "$name: ${_fstab} does not exist"
+ fi
+ vimage_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 vimage ${_vimage}"
+ 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_vimage=${_tmp_dir}/vimage.$$
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_prestart${i}:-''}\"
+ [ -z "$out" ] && break
+ ${out}
+ i=$((i + 1))
+ done
+
+ eval jail ${_flags} -i -c vnet name=\"${_vimage}\" \
+ host.hostname=\"${_hostname}\" \
+ path=\"${_rootdir}\" persist > ${_tmp_vimage} 2>&1
+
+ if [ "$?" -eq 0 ] ; then
+ _vimage_id=$(head -1 ${_tmp_vimage})
+
+ for _vnet in ${_vnets}; do
+ ifconfig ${_vnet} vnet "${_vimage_id}" \
+ > /dev/null 2>&1
+
+ case ${_vnet} in
+ epair[0-9]*[ab])
+ ifconfig ${_vnet%?}a up \
+ > /dev/null 2>&1;;
+ esac
+ done
+
+ eval jexec \"${_vimage_id}\" \
+ ${_exec_start} >> ${_tmp_vimage} 2>&1
+
+ for _service in netif routing ${_services}; do
+ eval jexec \"${_vimage_id}\" /bin/sh \
+ /usr/sbin/service ${_service} start \
+ >> ${_tmp_vimage} 2>&1
+ done
+
+ i=1
+ while : ; do
+ eval out=\"\${_exec_afterstart${i}:-''}\"
+
+ if [ -z "$out" ]; then
+ break;
+ fi
+
+ jexec "${_vimage_id}" ${out}
+ i=$((i + 1))
+ done
+
+ echo -n " $_hostname"
+ tail +2 ${_tmp_vimage} >${_consolelog}
+ echo ${_vimage_id} > /var/run/vimage_${_vimage}.id
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_poststart${i}:-''}\"
+ [ -z "$out" ] && break
+ ${out}
+ i=$((i + 1))
+ done
+ else
+ vimage_umount_fs
+ echo " cannot start vimage \"${_vimage}\": "
+ tail +2 ${_tmp_vimage}
+ fi
+ rm -f ${_tmp_vimage}
+ done
+ rmdir ${_tmp_dir}
+ echo '.'
+}
+
+vimage_stop()
+{
+ echo -n 'Stopping vimages:'
+ for _vimage in ${vimage_list}
+ do
+ if [ -f "/var/run/vimage_${_vimage}.id" ]; then
+ _vimage_id=$(cat /var/run/vimage_${_vimage}.id)
+ if [ ! -z "${_vimage_id}" ]; then
+ init_variables $_vimage
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_prestop${i}:-''}\"
+ [ -z "$out" ] && break
+ ${out}
+ i=$((i + 1))
+ done
+
+ if [ -n "${_exec_stop}" ]; then
+ eval env -i /usr/sbin/jexec ${_vimage_id} ${_exec_stop} \
+ >> ${_consolelog} 2>&1
+ fi
+ killall -j ${_vimage_id} -TERM > /dev/null 2>&1
+ sleep 1
+ killall -j ${_vimage_id} -KILL > /dev/null 2>&1
+ vimage_umount_fs
+ echo -n " $_hostname"
+
+ i=0
+ while : ; do
+ eval out=\"\${_exec_poststop${i}:-''}\"
+ [ -z "$out" ] && break
+ ${out}
+ i=$((i + 1))
+ done
+ fi
+ rm /var/run/vimage_${_vimage}.id
+ jail -r ${_vimage}
+ else
+ echo " cannot stop vimage ${_vimage}. No vimage id in /var/run"
+ fi
+ done
+ echo '.'
+}
+
+load_rc_config $name
+cmd="$1"
+if [ $# -gt 0 ]; then
+ shift
+fi
+if [ -n "$*" ]; then
+ vimage_list="$*"
+fi
+
+run_rc_command "${cmd}"
[-- Attachment #3 --]
Essentially, a hand-tweaked version of /etc/rc.d/jail with added/removed features.
Here's how we're using it in /etc/rc.conf to successfully start up vimage jails at boot time:
========== BEGIN EXCERPT ==========
#
# Vimages
#
vimage_enable="YES" # Set to NO to disable starting of any vimages
vimage_list="
vnettest
" # Space-separated list of names of vimages
clone_interfaces="" # Initialize list of epair/bridge interfaces to create
#
# Global settings for all Vimages
#
vimage_services="sshd"
####################### VIMAGE: vnettest
cloned_interfaces="$cloned_interfaces epair0 bridge0"
ifconfig_bridge0="addm fxp0 addm epair0a"
vimage_vnettest_rootdir="/usr/jails/vnettest" # root directory
vimage_vnettest_hostname="vnettest.jbsd.vicor.com" # hostname
vimage_vnettest_devfs_enable="YES" # mount devfs
vimage_vnettest_vnets="epair0b" # network interfaces
####################### VIMAGE: {name}
#cloned_interfaces="$cloned_interfaces epair{N} bridge{N}"
#ifconfig_bridge{N}="addm {iface} addm epair{N}a"
#vimage_{name}_rootdir="/usr/jails/{name}" # root directory
#vimage_{name}_hostname="{hostname}" # hostname
#vimage_{name}_devfs_enable="YES" # mount devfs
#vimage_{name}_vnets="epair{N}b" # network interfaces
========== END EXCERPT ==========
--
Cheers,
Devin
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAC979C8-3129-4E62-9D76-D1D0CCE001F0>
