jail revision 230099
1#!/bin/sh 2# 3# $FreeBSD: head/etc/rc.d/jail 230099 2012-01-14 02:18:41Z dougb $ 4# 5 6# PROVIDE: jail 7# REQUIRE: LOGIN cleanvar 8# BEFORE: securelevel 9# KEYWORD: nojail shutdown 10 11# WARNING: This script deals with untrusted data (the data and 12# processes inside the jails) and care must be taken when changing the 13# code related to this! If you have any doubt whether a change is 14# correct and have security impact, please get the patch reviewed by 15# the FreeBSD Security Team prior to commit. 16 17. /etc/rc.subr 18 19name="jail" 20rcvar="jail_enable" 21 22start_precmd="jail_prestart" 23start_cmd="jail_start" 24stop_cmd="jail_stop" 25 26# init_variables _j 27# Initialize the various jail variables for jail _j. 28# 29init_variables() 30{ 31 _j="$1" 32 33 if [ -z "$_j" ]; then 34 warn "init_variables: you must specify a jail" 35 return 36 fi 37 38 eval _rootdir=\"\$jail_${_j}_rootdir\" 39 _devdir="${_rootdir}/dev" 40 _fdescdir="${_devdir}/fd" 41 _procdir="${_rootdir}/proc" 42 eval _hostname=\"\$jail_${_j}_hostname\" 43 eval _ip=\"\$jail_${_j}_ip\" 44 eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\" 45 eval _exec=\"\$jail_${_j}_exec\" 46 47 i=0 48 while : ; do 49 eval _exec_prestart${i}=\"\${jail_${_j}_exec_prestart${i}:-\${jail_exec_prestart${i}}}\" 50 [ -z "$(eval echo \"\$_exec_prestart${i}\")" ] && break 51 i=$((i + 1)) 52 done 53 54 eval _exec_start=\"\${jail_${_j}_exec_start:-${jail_exec_start}}\" 55 56 i=1 57 while : ; do 58 eval _exec_afterstart${i}=\"\${jail_${_j}_exec_afterstart${i}:-\${jail_exec_afterstart${i}}}\" 59 [ -z "$(eval echo \"\$_exec_afterstart${i}\")" ] && break 60 i=$((i + 1)) 61 done 62 63 i=0 64 while : ; do 65 eval _exec_poststart${i}=\"\${jail_${_j}_exec_poststart${i}:-\${jail_exec_poststart${i}}}\" 66 [ -z "$(eval echo \"\$_exec_poststart${i}\")" ] && break 67 i=$((i + 1)) 68 done 69 70 i=0 71 while : ; do 72 eval _exec_prestop${i}=\"\${jail_${_j}_exec_prestop${i}:-\${jail_exec_prestop${i}}}\" 73 [ -z "$(eval echo \"\$_exec_prestop${i}\")" ] && break 74 i=$((i + 1)) 75 done 76 77 eval _exec_stop=\"\${jail_${_j}_exec_stop:-${jail_exec_stop}}\" 78 79 i=0 80 while : ; do 81 eval _exec_poststop${i}=\"\${jail_${_j}_exec_poststop${i}:-\${jail_exec_poststop${i}}}\" 82 [ -z "$(eval echo \"\$_exec_poststop${i}\")" ] && break 83 i=$((i + 1)) 84 done 85 86 if [ -n "${_exec}" ]; then 87 # simple/backward-compatible execution 88 _exec_start="${_exec}" 89 _exec_stop="" 90 else 91 # flexible execution 92 if [ -z "${_exec_start}" ]; then 93 _exec_start="/bin/sh /etc/rc" 94 if [ -z "${_exec_stop}" ]; then 95 _exec_stop="/bin/sh /etc/rc.shutdown" 96 fi 97 fi 98 fi 99 100 # The default jail ruleset will be used by rc.subr if none is specified. 101 eval _ruleset=\"\${jail_${_j}_devfs_ruleset:-${jail_devfs_ruleset}}\" 102 eval _devfs=\"\${jail_${_j}_devfs_enable:-${jail_devfs_enable}}\" 103 [ -z "${_devfs}" ] && _devfs="NO" 104 eval _fdescfs=\"\${jail_${_j}_fdescfs_enable:-${jail_fdescfs_enable}}\" 105 [ -z "${_fdescfs}" ] && _fdescfs="NO" 106 eval _procfs=\"\${jail_${_j}_procfs_enable:-${jail_procfs_enable}}\" 107 [ -z "${_procfs}" ] && _procfs="NO" 108 109 eval _mount=\"\${jail_${_j}_mount_enable:-${jail_mount_enable}}\" 110 [ -z "${_mount}" ] && _mount="NO" 111 # "/etc/fstab.${_j}" will be used for {,u}mount(8) if none is specified. 112 eval _fstab=\"\${jail_${_j}_fstab:-${jail_fstab}}\" 113 [ -z "${_fstab}" ] && _fstab="/etc/fstab.${_j}" 114 eval _flags=\"\${jail_${_j}_flags:-${jail_flags}}\" 115 [ -z "${_flags}" ] && _flags="-l -U root" 116 eval _consolelog=\"\${jail_${_j}_consolelog:-${jail_consolelog}}\" 117 [ -z "${_consolelog}" ] && _consolelog="/var/log/jail_${_j}_console.log" 118 eval _fib=\"\${jail_${_j}_fib:-${jail_fib}}\" 119 120 # Debugging aid 121 # 122 debug "$_j devfs enable: $_devfs" 123 debug "$_j fdescfs enable: $_fdescfs" 124 debug "$_j procfs enable: $_procfs" 125 debug "$_j mount enable: $_mount" 126 debug "$_j hostname: $_hostname" 127 debug "$_j ip: $_ip" 128 jail_show_addresses ${_j} 129 debug "$_j interface: $_interface" 130 debug "$_j fib: $_fib" 131 debug "$_j root: $_rootdir" 132 debug "$_j devdir: $_devdir" 133 debug "$_j fdescdir: $_fdescdir" 134 debug "$_j procdir: $_procdir" 135 debug "$_j ruleset: $_ruleset" 136 debug "$_j fstab: $_fstab" 137 138 i=0 139 while : ; do 140 eval out=\"\${_exec_prestart${i}:-''}\" 141 if [ -z "$out" ]; then 142 break 143 fi 144 debug "$_j exec pre-start #${i}: ${out}" 145 i=$((i + 1)) 146 done 147 148 debug "$_j exec start: $_exec_start" 149 150 i=1 151 while : ; do 152 eval out=\"\${_exec_afterstart${i}:-''}\" 153 154 if [ -z "$out" ]; then 155 break; 156 fi 157 158 debug "$_j exec after start #${i}: ${out}" 159 i=$((i + 1)) 160 done 161 162 i=0 163 while : ; do 164 eval out=\"\${_exec_poststart${i}:-''}\" 165 if [ -z "$out" ]; then 166 break 167 fi 168 debug "$_j exec post-start #${i}: ${out}" 169 i=$((i + 1)) 170 done 171 172 i=0 173 while : ; do 174 eval out=\"\${_exec_prestop${i}:-''}\" 175 if [ -z "$out" ]; then 176 break 177 fi 178 debug "$_j exec pre-stop #${i}: ${out}" 179 i=$((i + 1)) 180 done 181 182 debug "$_j exec stop: $_exec_stop" 183 184 i=0 185 while : ; do 186 eval out=\"\${_exec_poststop${i}:-''}\" 187 if [ -z "$out" ]; then 188 break 189 fi 190 debug "$_j exec post-stop #${i}: ${out}" 191 i=$((i + 1)) 192 done 193 194 debug "$_j flags: $_flags" 195 debug "$_j consolelog: $_consolelog" 196 197 if [ -z "${_hostname}" ]; then 198 err 3 "$name: No hostname has been defined for ${_j}" 199 fi 200 if [ -z "${_rootdir}" ]; then 201 err 3 "$name: No root directory has been defined for ${_j}" 202 fi 203} 204 205# set_sysctl rc_knob mib msg 206# If the mib sysctl is set according to what rc_knob 207# specifies, this function does nothing. However if 208# rc_knob is set differently than mib, then the mib 209# is set accordingly and msg is displayed followed by 210# an '=" sign and the word 'YES' or 'NO'. 211# 212set_sysctl() 213{ 214 _knob="$1" 215 _mib="$2" 216 _msg="$3" 217 218 _current=`${SYSCTL} -n $_mib 2>/dev/null` 219 if checkyesno $_knob ; then 220 if [ "$_current" -ne 1 ]; then 221 echo -n " ${_msg}=YES" 222 ${SYSCTL} 1>/dev/null ${_mib}=1 223 fi 224 else 225 if [ "$_current" -ne 0 ]; then 226 echo -n " ${_msg}=NO" 227 ${SYSCTL} 1>/dev/null ${_mib}=0 228 fi 229 fi 230} 231 232# is_current_mountpoint() 233# Is the directory mount point for a currently mounted file 234# system? 235# 236is_current_mountpoint() 237{ 238 local _dir _dir2 239 240 _dir=$1 241 242 _dir=`echo $_dir | sed -Ee 's#//+#/#g' -e 's#/$##'` 243 [ ! -d "${_dir}" ] && return 1 244 _dir2=`df ${_dir} | tail +2 | awk '{ print $6 }'` 245 [ "${_dir}" = "${_dir2}" ] 246 return $? 247} 248 249# is_symlinked_mountpoint() 250# Is a mount point, or any of its parent directories, a symlink? 251# 252is_symlinked_mountpoint() 253{ 254 local _dir 255 256 _dir=$1 257 258 [ -L "$_dir" ] && return 0 259 [ "$_dir" = "/" ] && return 1 260 is_symlinked_mountpoint `dirname $_dir` 261 return $? 262} 263 264# secure_umount 265# Try to unmount a mount point without being vulnerable to 266# symlink attacks. 267# 268secure_umount() 269{ 270 local _dir 271 272 _dir=$1 273 274 if is_current_mountpoint ${_dir}; then 275 umount -f ${_dir} >/dev/null 2>&1 276 else 277 debug "Nothing mounted on ${_dir} - not unmounting" 278 fi 279} 280 281 282# jail_umount_fs 283# This function unmounts certain special filesystems in the 284# currently selected jail. The caller must call the init_variables() 285# routine before calling this one. 286# 287jail_umount_fs() 288{ 289 local _device _mountpt _rest 290 291 if checkyesno _fdescfs; then 292 if [ -d "${_fdescdir}" ] ; then 293 secure_umount ${_fdescdir} 294 fi 295 fi 296 if checkyesno _devfs; then 297 if [ -d "${_devdir}" ] ; then 298 secure_umount ${_devdir} 299 fi 300 fi 301 if checkyesno _procfs; then 302 if [ -d "${_procdir}" ] ; then 303 secure_umount ${_procdir} 304 fi 305 fi 306 if checkyesno _mount; then 307 [ -f "${_fstab}" ] || warn "${_fstab} does not exist" 308 tail -r ${_fstab} | while read _device _mountpt _rest; do 309 case ":${_device}" in 310 :#* | :) 311 continue 312 ;; 313 esac 314 secure_umount ${_mountpt} 315 done 316 fi 317} 318 319# jail_mount_fstab() 320# Mount file systems from a per jail fstab while trying to 321# secure against symlink attacks at the mount points. 322# 323# If we are certain we cannot secure against symlink attacks we 324# do not mount all of the file systems (since we cannot just not 325# mount the file system with the problematic mount point). 326# 327# The caller must call the init_variables() routine before 328# calling this one. 329# 330jail_mount_fstab() 331{ 332 local _device _mountpt _rest 333 334 while read _device _mountpt _rest; do 335 case ":${_device}" in 336 :#* | :) 337 continue 338 ;; 339 esac 340 if is_symlinked_mountpoint ${_mountpt}; then 341 warn "${_mountpt} has symlink as parent - not mounting from ${_fstab}" 342 return 343 fi 344 done <${_fstab} 345 mount -a -F "${_fstab}" 346} 347 348# jail_show_addresses jail 349# Debug print the input for the given _multi aliases 350# for a jail for init_variables(). 351# 352jail_show_addresses() 353{ 354 local _j _type alias 355 _j="$1" 356 alias=0 357 358 if [ -z "${_j}" ]; then 359 warn "jail_show_addresses: you must specify a jail" 360 return 361 fi 362 363 while : ; do 364 eval _addr=\"\$jail_${_j}_ip_multi${alias}\" 365 if [ -n "${_addr}" ]; then 366 debug "${_j} ip_multi${alias}: $_addr" 367 alias=$((${alias} + 1)) 368 else 369 break 370 fi 371 done 372} 373 374# jail_extract_address argument 375# The second argument is the string from one of the _ip 376# or the _multi variables. In case of a comma separated list 377# only one argument must be passed in at a time. 378# The function alters the _type, _iface, _addr and _mask variables. 379# 380jail_extract_address() 381{ 382 local _i 383 _i=$1 384 385 if [ -z "${_i}" ]; then 386 warn "jail_extract_address: called without input" 387 return 388 fi 389 390 # Check if we have an interface prefix given and split into 391 # iFace and rest. 392 case "${_i}" in 393 *\|*) # ifN|.. prefix there 394 _iface=${_i%%|*} 395 _r=${_i##*|} 396 ;; 397 *) _iface="" 398 _r=${_i} 399 ;; 400 esac 401 402 # In case the IP has no interface given, check if we have a global one. 403 _iface=${_iface:-${_interface}} 404 405 # Set address, cut off any prefix/netmask/prefixlen. 406 _addr=${_r} 407 _addr=${_addr%%[/ ]*} 408 409 # Theoretically we can return here if interface is not set, 410 # as we only care about the _mask if we call ifconfig. 411 # This is not done because we may want to santize IP addresses 412 # based on _type later, and optionally change the type as well. 413 414 # Extract the prefix/netmask/prefixlen part by cutting off the address. 415 _mask=${_r} 416 _mask=`expr "${_mask}" : "${_addr}\(.*\)"` 417 418 # Identify type {inet,inet6}. 419 case "${_addr}" in 420 *\.*\.*\.*) _type="inet" ;; 421 *:*) _type="inet6" ;; 422 *) warn "jail_extract_address: type not identified" 423 ;; 424 esac 425 426 # Handle the special /netmask instead of /prefix or 427 # "netmask xxx" case for legacy IP. 428 # We do NOT support shortend class-full netmasks. 429 if [ "${_type}" = "inet" ]; then 430 case "${_mask}" in 431 /*\.*\.*\.*) _mask=" netmask ${_mask#/}" ;; 432 *) ;; 433 esac 434 435 # In case _mask is still not set use /32. 436 _mask=${_mask:-/32} 437 438 elif [ "${_type}" = "inet6" ]; then 439 # In case _maske is not set for IPv6, use /128. 440 _mask=${_mask:-/128} 441 fi 442} 443 444# jail_handle_ips_option {add,del} input 445# Handle a single argument imput which can be a comma separated 446# list of addresses (theoretically with an option interface and 447# prefix/netmask/prefixlen). 448# 449jail_handle_ips_option() 450{ 451 local _x _action _type _i 452 _action=$1 453 _x=$2 454 455 if [ -z "${_x}" ]; then 456 # No IP given. This can happen for the primary address 457 # of each address family. 458 return 459 fi 460 461 # Loop, in case we find a comma separated list, we need to handle 462 # each argument on its own. 463 while [ ${#_x} -gt 0 ]; do 464 case "${_x}" in 465 *,*) # Extract the first argument and strip it off the list. 466 _i=`expr "${_x}" : '^\([^,]*\)'` 467 _x=`expr "${_x}" : "^[^,]*,\(.*\)"` 468 ;; 469 *) _i=${_x} 470 _x="" 471 ;; 472 esac 473 474 _type="" 475 _iface="" 476 _addr="" 477 _mask="" 478 jail_extract_address "${_i}" 479 480 # make sure we got an address. 481 case "${_addr}" in 482 "") continue ;; 483 *) ;; 484 esac 485 486 # Append address to list of addresses for the jail command. 487 case "${_addrl}" in 488 "") _addrl="${_addr}" ;; 489 *) _addrl="${_addrl},${_addr}" ;; 490 esac 491 492 # Configure interface alias if requested by a given interface 493 # and if we could correctly parse everything. 494 case "${_iface}" in 495 "") continue ;; 496 esac 497 case "${_type}" in 498 inet) ;; 499 inet6) ;; 500 *) warn "Could not determine address family. Not going" \ 501 "to ${_action} address '${_addr}' for ${_jail}." 502 continue 503 ;; 504 esac 505 case "${_action}" in 506 add) ifconfig ${_iface} ${_type} ${_addr}${_mask} alias 507 ;; 508 del) # When removing the IP, ignore the _mask. 509 ifconfig ${_iface} ${_type} ${_addr} -alias 510 ;; 511 esac 512 done 513} 514 515# jail_ips {add,del} 516# Extract the comma separated list of addresses and return them 517# for the jail command. 518# Handle more than one address via the _multi option as well. 519# If an interface is given also add/remove an alias for the 520# address with an optional netmask. 521# 522jail_ips() 523{ 524 local _action 525 _action=$1 526 527 case "${_action}" in 528 add) ;; 529 del) ;; 530 *) warn "jail_ips: invalid action '${_action}'" 531 return 532 ;; 533 esac 534 535 # Handle addresses. 536 jail_handle_ips_option ${_action} "${_ip}" 537 # Handle jail_xxx_ip_multi<N> 538 alias=0 539 while : ; do 540 eval _x=\"\$jail_${_jail}_ip_multi${alias}\" 541 case "${_x}" in 542 "") break ;; 543 *) jail_handle_ips_option ${_action} "${_x}" 544 alias=$((${alias} + 1)) 545 ;; 546 esac 547 done 548} 549 550jail_prestart() 551{ 552 if checkyesno jail_parallel_start; then 553 command_args='&' 554 fi 555} 556 557jail_start() 558{ 559 echo -n 'Configuring jails:' 560 set_sysctl jail_set_hostname_allow security.jail.set_hostname_allowed \ 561 set_hostname_allow 562 set_sysctl jail_socket_unixiproute_only \ 563 security.jail.socket_unixiproute_only unixiproute_only 564 set_sysctl jail_sysvipc_allow security.jail.sysvipc_allowed \ 565 sysvipc_allow 566 echo '.' 567 568 echo -n 'Starting jails:' 569 _tmp_dir=`mktemp -d /tmp/jail.XXXXXXXX` || \ 570 err 3 "$name: Can't create temp dir, exiting..." 571 for _jail in ${jail_list} 572 do 573 init_variables $_jail 574 if [ -f /var/run/jail_${_jail}.id ]; then 575 echo -n " [${_hostname} already running (/var/run/jail_${_jail}.id exists)]" 576 continue; 577 fi 578 _addrl="" 579 jail_ips "add" 580 if [ -n "${_fib}" ]; then 581 _setfib="setfib -F '${_fib}'" 582 else 583 _setfib="" 584 fi 585 if checkyesno _mount; then 586 info "Mounting fstab for jail ${_jail} (${_fstab})" 587 if [ ! -f "${_fstab}" ]; then 588 err 3 "$name: ${_fstab} does not exist" 589 fi 590 jail_mount_fstab 591 fi 592 if checkyesno _devfs; then 593 # If devfs is already mounted here, skip it. 594 df -t devfs "${_devdir}" >/dev/null 595 if [ $? -ne 0 ]; then 596 if is_symlinked_mountpoint ${_devdir}; then 597 warn "${_devdir} has symlink as parent - not starting jail ${_jail}" 598 continue 599 fi 600 info "Mounting devfs on ${_devdir}" 601 devfs_mount_jail "${_devdir}" ${_ruleset} 602 # Transitional symlink for old binaries 603 if [ ! -L "${_devdir}/log" ]; then 604 __pwd="`pwd`" 605 cd "${_devdir}" 606 ln -sf ../var/run/log log 607 cd "$__pwd" 608 fi 609 fi 610 611 # XXX - It seems symlinks don't work when there 612 # is a devfs(5) device of the same name. 613 # Jail console output 614 # __pwd="`pwd`" 615 # cd "${_devdir}" 616 # ln -sf ../var/log/console console 617 # cd "$__pwd" 618 fi 619 if checkyesno _fdescfs; then 620 if is_symlinked_mountpoint ${_fdescdir}; then 621 warn "${_fdescdir} has symlink as parent, not mounting" 622 else 623 info "Mounting fdescfs on ${_fdescdir}" 624 mount -t fdescfs fdesc "${_fdescdir}" 625 fi 626 fi 627 if checkyesno _procfs; then 628 if is_symlinked_mountpoint ${_procdir}; then 629 warn "${_procdir} has symlink as parent, not mounting" 630 else 631 info "Mounting procfs onto ${_procdir}" 632 if [ -d "${_procdir}" ] ; then 633 mount -t procfs proc "${_procdir}" 634 fi 635 fi 636 fi 637 _tmp_jail=${_tmp_dir}/jail.$$ 638 639 i=0 640 while : ; do 641 eval out=\"\${_exec_prestart${i}:-''}\" 642 [ -z "$out" ] && break 643 ${out} 644 i=$((i + 1)) 645 done 646 647 eval ${_setfib} jail ${_flags} -i ${_rootdir} ${_hostname} \ 648 \"${_addrl}\" ${_exec_start} > ${_tmp_jail} 2>&1 \ 649 </dev/null 650 651 if [ "$?" -eq 0 ] ; then 652 _jail_id=$(head -1 ${_tmp_jail}) 653 i=1 654 while : ; do 655 eval out=\"\${_exec_afterstart${i}:-''}\" 656 657 if [ -z "$out" ]; then 658 break; 659 fi 660 661 jexec "${_jail_id}" ${out} 662 i=$((i + 1)) 663 done 664 665 echo -n " $_hostname" 666 tail +2 ${_tmp_jail} >${_consolelog} 667 echo ${_jail_id} > /var/run/jail_${_jail}.id 668 669 i=0 670 while : ; do 671 eval out=\"\${_exec_poststart${i}:-''}\" 672 [ -z "$out" ] && break 673 ${out} 674 i=$((i + 1)) 675 done 676 else 677 jail_umount_fs 678 jail_ips "del" 679 echo " cannot start jail \"${_jail}\": " 680 tail +2 ${_tmp_jail} 681 fi 682 rm -f ${_tmp_jail} 683 done 684 rmdir ${_tmp_dir} 685 echo '.' 686} 687 688jail_stop() 689{ 690 echo -n 'Stopping jails:' 691 for _jail in ${jail_list} 692 do 693 if [ -f "/var/run/jail_${_jail}.id" ]; then 694 _jail_id=$(cat /var/run/jail_${_jail}.id) 695 if [ ! -z "${_jail_id}" ]; then 696 init_variables $_jail 697 698 i=0 699 while : ; do 700 eval out=\"\${_exec_prestop${i}:-''}\" 701 [ -z "$out" ] && break 702 ${out} 703 i=$((i + 1)) 704 done 705 706 if [ -n "${_exec_stop}" ]; then 707 eval env -i /usr/sbin/jexec ${_jail_id} ${_exec_stop} \ 708 >> ${_consolelog} 2>&1 709 fi 710 killall -j ${_jail_id} -TERM > /dev/null 2>&1 711 sleep 1 712 killall -j ${_jail_id} -KILL > /dev/null 2>&1 713 jail_umount_fs 714 echo -n " $_hostname" 715 716 i=0 717 while : ; do 718 eval out=\"\${_exec_poststop${i}:-''}\" 719 [ -z "$out" ] && break 720 ${out} 721 i=$((i + 1)) 722 done 723 fi 724 jail_ips "del" 725 rm /var/run/jail_${_jail}.id 726 else 727 echo " cannot stop jail ${_jail}. No jail id in /var/run" 728 fi 729 done 730 echo '.' 731} 732 733load_rc_config $name 734cmd="$1" 735if [ $# -gt 0 ]; then 736 shift 737fi 738if [ -n "$*" ]; then 739 jail_list="$*" 740fi 741 742run_rc_command "${cmd}" 743