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>, Dave Robison <daver@vicor.com> 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
--Apple-Mail-25--994899661 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="ISO-8859-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 confidentia= l. If you are not the intended recipient, please: (i) delete the message an= d all copies; (ii) do not disclose, distribute or use the message in any ma= nner; and (iii) notify the sender immediately. In addition, please be aware= that any message addressed to our domain is subject to archiving and revie= w by persons other than the intended recipient. Thank you. _____________ --Apple-Mail-25--994899661 Content-Disposition: attachment; filename="vimage_rc.20110827104104.patch" Content-Type: application/octet-stream; name="vimage_rc.20110827104104.patch" Content-Transfer-Encoding: 7bit --- 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}" --Apple-Mail-25--994899661 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="us-ascii" 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: =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D BEGIN EXCERPT =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D= # # Vimages # vimage_enable=3D"YES" # Set to NO to disable starting of any vimages vimage_list=3D" vnettest " # Space-separated list of names of vimages clone_interfaces=3D"" # Initialize list of epair/bridge interfaces = to create # # Global settings for all Vimages # vimage_services=3D"sshd" ####################### VIMAGE: vnettest cloned_interfaces=3D"$cloned_interfaces epair0 bridge0" ifconfig_bridge0=3D"addm fxp0 addm epair0a" vimage_vnettest_rootdir=3D"/usr/jails/vnettest" # root = directory vimage_vnettest_hostname=3D"vnettest.jbsd.vicor.com" # hostname vimage_vnettest_devfs_enable=3D"YES" # mount devfs vimage_vnettest_vnets=3D"epair0b" # network = interfaces ####################### VIMAGE: {name} #cloned_interfaces=3D"$cloned_interfaces epair{N} bridge{N}" #ifconfig_bridge{N}=3D"addm {iface} addm epair{N}a" #vimage_{name}_rootdir=3D"/usr/jails/{name}" # root = directory #vimage_{name}_hostname=3D"{hostname}" # hostname #vimage_{name}_devfs_enable=3D"YES" # mount devfs #vimage_{name}_vnets=3D"epair{N}b" # network = interfaces =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D END EXCERPT =3D=3D=3D=3D=3D=3D=3D=3D=3D=3D --=20 Cheers, Devin= --Apple-Mail-25--994899661--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?CAC979C8-3129-4E62-9D76-D1D0CCE001F0>