jail revision 165942
1#!/bin/sh 2# 3# $FreeBSD: head/etc/rc.d/jail 165942 2007-01-11 18:18:57Z simon $ 4# 5 6# PROVIDE: jail 7# REQUIRE: LOGIN cleanvar 8# BEFORE: securelevel 9# KEYWORD: nojail shutdown 10 11. /etc/rc.subr 12 13name="jail" 14rcvar=`set_rcvar` 15start_cmd="jail_start" 16stop_cmd="jail_stop" 17 18# init_variables _j 19# Initialize the various jail variables for jail _j. 20# 21init_variables() 22{ 23 _j="$1" 24 25 if [ -z "$_j" ]; then 26 warn "init_variables: you must specify a jail" 27 return 28 fi 29 30 eval _rootdir=\"\$jail_${_j}_rootdir\" 31 _devdir="${_rootdir}/dev" 32 _fdescdir="${_devdir}/fd" 33 _procdir="${_rootdir}/proc" 34 eval _hostname=\"\$jail_${_j}_hostname\" 35 eval _ip=\"\$jail_${_j}_ip\" 36 eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\" 37 eval _exec=\"\$jail_${_j}_exec\" 38 eval _exec_start=\"\${jail_${_j}_exec_start:-${jail_exec_start}}\" 39 40 i=1 41 while [ true ]; do 42 eval _exec_afterstart${i}=\"\${jail_${_j}_exec_afterstart${i}:-\${jail_exec_afterstart${i}}}\" 43 [ -z "$(eval echo \"\$_exec_afterstart${i}\")" ] && break 44 i=$((i + 1)) 45 done 46 47 eval _exec_stop=\"\${jail_${_j}_exec_stop:-${jail_exec_stop}}\" 48 if [ -n "${_exec}" ]; then 49 # simple/backward-compatible execution 50 _exec_start="${_exec}" 51 _exec_stop="" 52 else 53 # flexible execution 54 if [ -z "${_exec_start}" ]; then 55 _exec_start="/bin/sh /etc/rc" 56 if [ -z "${_exec_stop}" ]; then 57 _exec_stop="/bin/sh /etc/rc.shutdown" 58 fi 59 fi 60 fi 61 62 # The default jail ruleset will be used by rc.subr if none is specified. 63 eval _ruleset=\"\${jail_${_j}_devfs_ruleset:-${jail_devfs_ruleset}}\" 64 eval _devfs=\"\${jail_${_j}_devfs_enable:-${jail_devfs_enable}}\" 65 [ -z "${_devfs}" ] && _devfs="NO" 66 eval _fdescfs=\"\${jail_${_j}_fdescfs_enable:-${jail_fdescfs_enable}}\" 67 [ -z "${_fdescfs}" ] && _fdescfs="NO" 68 eval _procfs=\"\${jail_${_j}_procfs_enable:-${jail_procfs_enable}}\" 69 [ -z "${_procfs}" ] && _procfs="NO" 70 71 eval _mount=\"\${jail_${_j}_mount_enable:-${jail_mount_enable}}\" 72 [ -z "${_mount}" ] && _mount="NO" 73 # "/etc/fstab.${_j}" will be used for {,u}mount(8) if none is specified. 74 eval _fstab=\"\${jail_${_j}_fstab:-${jail_fstab}}\" 75 [ -z "${_fstab}" ] && _fstab="/etc/fstab.${_j}" 76 eval _flags=\"\${jail_${_j}_flags:-${jail_flags}}\" 77 [ -z "${_flags}" ] && _flags="-l -U root" 78 eval _consolelog=\"\${jail_${_j}_consolelog:-${jail_consolelog}}\" 79 [ -z "${_consolelog}" ] && _consolelog="/var/log/jail_${_j}_console.log" 80 81 # Debugging aid 82 # 83 debug "$_j devfs enable: $_devfs" 84 debug "$_j fdescfs enable: $_fdescfs" 85 debug "$_j procfs enable: $_procfs" 86 debug "$_j mount enable: $_mount" 87 debug "$_j hostname: $_hostname" 88 debug "$_j ip: $_ip" 89 debug "$_j interface: $_interface" 90 debug "$_j root: $_rootdir" 91 debug "$_j devdir: $_devdir" 92 debug "$_j fdescdir: $_fdescdir" 93 debug "$_j procdir: $_procdir" 94 debug "$_j ruleset: $_ruleset" 95 debug "$_j fstab: $_fstab" 96 debug "$_j exec start: $_exec_start" 97 debug "$_j consolelog: $_consolelog" 98 99 i=1 100 while [ true ]; do 101 eval out=\"\${_exec_afterstart${i}:-''}\" 102 103 if [ -z "$out" ]; then 104 break; 105 fi 106 107 debug "$_j exec after start #${i}: ${out}" 108 i=$((i + 1)) 109 done 110 111 debug "$_j exec stop: $_exec_stop" 112 debug "$_j flags: $_flags" 113 debug "$_j consolelog: $_consolelog" 114 115 if [ -z "${_hostname}" ]; then 116 err 3 "$name: No hostname has been defined for ${_j}" 117 fi 118 if [ -z "${_rootdir}" ]; then 119 err 3 "$name: No root directory has been defined for ${_j}" 120 fi 121 if [ -z "${_ip}" ]; then 122 err 3 "$name: No IP address has been defined for ${_j}" 123 fi 124 125} 126 127# set_sysctl rc_knob mib msg 128# If the mib sysctl is set according to what rc_knob 129# specifies, this function does nothing. However if 130# rc_knob is set differently than mib, then the mib 131# is set accordingly and msg is displayed followed by 132# an '=" sign and the word 'YES' or 'NO'. 133# 134set_sysctl() 135{ 136 _knob="$1" 137 _mib="$2" 138 _msg="$3" 139 140 _current=`${SYSCTL} -n $_mib 2>/dev/null` 141 if checkyesno $_knob ; then 142 if [ "$_current" -ne 1 ]; then 143 echo -n " ${_msg}=YES" 144 ${SYSCTL_W} 1>/dev/null ${_mib}=1 145 fi 146 else 147 if [ "$_current" -ne 0 ]; then 148 echo -n " ${_msg}=NO" 149 ${SYSCTL_W} 1>/dev/null ${_mib}=0 150 fi 151 fi 152} 153 154# is_current_mountpoint() 155# Is the directory mount point for a currently mounted file 156# system? 157# 158is_current_mountpoint() 159{ 160 local _dir _dir2 161 162 _dir=$1 163 164 _dir=`echo $_dir | sed -Ee 's#//+#/#g' -e 's#/$##'` 165 [ ! -d "${_dir}" ] && return 1 166 _dir2=`df ${_dir} | tail +2 | awk '{ print $6 }'` 167 [ "${_dir}" = "${_dir2}" ] 168 return $? 169} 170 171# is_symlinked_mountpoint() 172# Is a mount point, or any of its parent directories, a symlink? 173# 174is_symlinked_mountpoint() 175{ 176 local _dir 177 178 _dir=$1 179 180 [ -L "$_dir" ] && return 0 181 [ "$_dir" = "/" ] && return 1 182 is_symlinked_mountpoint `dirname $_dir` 183 return $? 184} 185 186# secure_umount 187# Try to unmount a mount point without being vulnerable to 188# symlink attacks. 189# 190secure_umount() 191{ 192 local _dir 193 194 _dir=$1 195 196 if is_current_mountpoint ${_dir}; then 197 umount -f ${_dir} >/dev/null 2>&1 198 else 199 debug "Nothing mounted on ${_dir} - not unmounting" 200 fi 201} 202 203 204# jail_umount_fs 205# This function unmounts certain special filesystems in the 206# currently selected jail. The caller must call the init_variables() 207# routine before calling this one. 208# 209jail_umount_fs() 210{ 211 local _device _mountpt _rest 212 213 if checkyesno _fdescfs; then 214 if [ -d "${_fdescdir}" ] ; then 215 secure_umount ${_fdescdir} 216 fi 217 fi 218 if checkyesno _devfs; then 219 if [ -d "${_devdir}" ] ; then 220 secure_umount ${_devdir} 221 fi 222 fi 223 if checkyesno _procfs; then 224 if [ -d "${_procdir}" ] ; then 225 secure_umount ${_procdir} 226 fi 227 fi 228 if checkyesno _mount; then 229 [ -f "${_fstab}" ] || warn "${_fstab} does not exist" 230 tail -r ${_fstab} | while read _device _mountpt _rest; do 231 case ":${_device}" in 232 :#* | :) 233 continue 234 ;; 235 esac 236 secure_umount ${_mountpt} 237 done 238 fi 239} 240 241# jail_mount_fstab() 242# Mount file systems from a per jail fstab while trying to 243# secure against symlink attacks at the mount points. 244# 245# If we are certain we cannot secure against symlink attacks we 246# do not mount all of the file systems (since we cannot just not 247# mount the file system with the problematic mount point). 248# 249# The caller must call the init_variables() routine before 250# calling this one. 251# 252jail_mount_fstab() 253{ 254 local _device _mountpt _rest 255 256 while read _device _mountpt _rest; do 257 case ":${_device}" in 258 :#* | :) 259 continue 260 ;; 261 esac 262 if is_symlinked_mountpoint ${_mountpt}; then 263 warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}" 264 return 265 fi 266 done <${_fstab} 267 mount -a -F "${_fstab}" 268} 269 270jail_start() 271{ 272 echo -n 'Configuring jails:' 273 set_sysctl jail_set_hostname_allow security.jail.set_hostname_allowed \ 274 set_hostname_allow 275 set_sysctl jail_socket_unixiproute_only \ 276 security.jail.socket_unixiproute_only unixiproute_only 277 set_sysctl jail_sysvipc_allow security.jail.sysvipc_allowed \ 278 sysvipc_allow 279 echo '.' 280 281 echo -n 'Starting jails:' 282 _tmp_dir=`mktemp -d /tmp/jail.XXXXXXXX` || \ 283 err 3 "$name: Can't create temp dir, exiting..." 284 for _jail in ${jail_list} 285 do 286 init_variables $_jail 287 if [ -f /var/run/jail_${_jail}.id ]; then 288 echo -n " [${_hostname} already running (/var/run/jail_${_jail}.id exists)]" 289 continue; 290 fi 291 if [ -n "${_interface}" ]; then 292 ifconfig ${_interface} alias ${_ip} netmask 255.255.255.255 293 fi 294 if checkyesno _mount; then 295 info "Mounting fstab for jail ${_jail} (${_fstab})" 296 if [ ! -f "${_fstab}" ]; then 297 err 3 "$name: ${_fstab} does not exist" 298 fi 299 jail_mount_fstab 300 fi 301 if checkyesno _devfs; then 302 # If devfs is already mounted here, skip it. 303 df -t devfs "${_devdir}" >/dev/null 304 if [ $? -ne 0 ]; then 305 if is_symlinked_mountpoint ${_devdir}; then 306 warn "${_devdir} has symlink as parent - not starting jail ${_jail}" 307 continue 308 fi 309 info "Mounting devfs on ${_devdir}" 310 devfs_mount_jail "${_devdir}" ${_ruleset} 311 # Transitional symlink for old binaries 312 if [ ! -L "${_devdir}/log" ]; then 313 __pwd="`pwd`" 314 cd "${_devdir}" 315 ln -sf ../var/run/log log 316 cd "$__pwd" 317 fi 318 fi 319 320 # XXX - It seems symlinks don't work when there 321 # is a devfs(5) device of the same name. 322 # Jail console output 323 # __pwd="`pwd`" 324 # cd "${_devdir}" 325 # ln -sf ../var/log/console console 326 # cd "$__pwd" 327 fi 328 if checkyesno _fdescfs; then 329 if is_symlinked_mountpoint ${_fdescdir}; then 330 warn "${_fdescdir} has symlink as parent, not mounting" 331 else 332 info "Mounting fdescfs on ${_fdescdir}" 333 mount -t fdescfs fdesc "${_fdescdir}" 334 fi 335 fi 336 if checkyesno _procfs; then 337 if is_symlinked_mountpoint ${_procdir}; then 338 warn "${_procdir} has symlink as parent, not mounting" 339 else 340 info "Mounting procfs onto ${_procdir}" 341 if [ -d "${_procdir}" ] ; then 342 mount -t procfs proc "${_procdir}" 343 fi 344 fi 345 fi 346 _tmp_jail=${_tmp_dir}/jail.$$ 347 eval jail ${_flags} -i ${_rootdir} ${_hostname} \ 348 ${_ip} ${_exec_start} > ${_tmp_jail} 2>&1 349 350 if [ "$?" -eq 0 ] ; then 351 _jail_id=$(head -1 ${_tmp_jail}) 352 i=1 353 while [ true ]; do 354 eval out=\"\${_exec_afterstart${i}:-''}\" 355 356 if [ -z "$out" ]; then 357 break; 358 fi 359 360 jexec "${_jail_id}" ${out} 361 i=$((i + 1)) 362 done 363 364 echo -n " $_hostname" 365 tail +2 ${_tmp_jail} >${_consolelog} 366 echo ${_jail_id} > /var/run/jail_${_jail}.id 367 else 368 jail_umount_fs 369 if [ -n "${_interface}" ]; then 370 ifconfig ${_interface} -alias ${_ip} 371 fi 372 echo " cannot start jail \"${_jail}\": " 373 tail +2 ${_tmp_jail} 374 fi 375 rm -f ${_tmp_jail} 376 done 377 rmdir ${_tmp_dir} 378 echo '.' 379} 380 381jail_stop() 382{ 383 echo -n 'Stopping jails:' 384 for _jail in ${jail_list} 385 do 386 if [ -f "/var/run/jail_${_jail}.id" ]; then 387 _jail_id=$(cat /var/run/jail_${_jail}.id) 388 if [ ! -z "${_jail_id}" ]; then 389 init_variables $_jail 390 if [ -n "${_exec_stop}" ]; then 391 eval env -i /usr/sbin/jexec ${_jail_id} ${_exec_stop} \ 392 >> ${_consolelog} 2>&1 393 fi 394 killall -j ${_jail_id} -TERM > /dev/null 2>&1 395 sleep 1 396 killall -j ${_jail_id} -KILL > /dev/null 2>&1 397 jail_umount_fs 398 echo -n " $_hostname" 399 fi 400 if [ -n "${_interface}" ]; then 401 ifconfig ${_interface} -alias ${_ip} 402 fi 403 rm /var/run/jail_${_jail}.id 404 else 405 echo " cannot stop jail ${_jail}. No jail id in /var/run" 406 fi 407 done 408 echo '.' 409} 410 411load_rc_config $name 412cmd="$1" 413if [ $# -gt 0 ]; then 414 shift 415fi 416if [ -n "$*" ]; then 417 jail_list="$*" 418fi 419run_rc_command "${cmd}" 420