1#!/bin/sh 2# 3# 4 5# PROVIDE: jail 6# REQUIRE: LOGIN FILESYSTEMS 7# BEFORE: securelevel 8# KEYWORD: shutdown 9 10. /etc/rc.subr 11 12name="jail" 13desc="Manage system jails" 14rcvar="jail_enable" 15 16start_cmd="jail_start" 17start_postcmd="jail_warn" 18stop_cmd="jail_stop" 19config_cmd="jail_config" 20console_cmd="jail_console" 21status_cmd="jail_status" 22extra_commands="config console status" 23: ${jail_program:=/usr/sbin/jail} 24: ${jail_consolecmd:=/usr/bin/login -f root} 25: ${jail_jexec:=/usr/sbin/jexec} 26: ${jail_jls:=/usr/sbin/jls} 27 28need_dad_wait= 29 30# extract_var jv name param num defval 31# Extract value from ${jail_$jv_$name} or ${jail_$name} and 32# set it to $param. If not defined, $defval is used. 33# When $num is [0-9]*, ${jail_$jv_$name$num} are looked up and 34# $param is set by using +=. $num=0 is optional (params may start at 1). 35# When $num is YN or NY, the value is interpreted as boolean. 36# When $num is @, the value is interpreted as an array separted by IFS. 37extract_var() 38{ 39 local i _jv _name _param _num _def _name1 _name2 40 _jv=$1 41 _name=$2 42 _param=$3 43 _num=$4 44 _def=$5 45 46 case $_num in 47 YN) 48 _name1=jail_${_jv}_${_name} 49 _name2=jail_${_name} 50 eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\" 51 if checkyesno $_name1; then 52 echo " $_param = 1;" 53 else 54 echo " $_param = 0;" 55 fi 56 ;; 57 NY) 58 _name1=jail_${_jv}_${_name} 59 _name2=jail_${_name} 60 eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\" 61 if checkyesno $_name1; then 62 echo " $_param = 0;" 63 else 64 echo " $_param = 1;" 65 fi 66 ;; 67 [0-9]*) 68 i=$_num 69 while : ; do 70 _name1=jail_${_jv}_${_name}${i} 71 _name2=jail_${_name}${i} 72 eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\" 73 if [ -n "$_tmpargs" ]; then 74 echo " $_param += \"$_tmpargs\";" 75 elif [ $i != 0 ]; then 76 break; 77 fi 78 i=$(($i + 1)) 79 done 80 ;; 81 @) 82 _name1=jail_${_jv}_${_name} 83 _name2=jail_${_name} 84 eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\" 85 set -- $_tmpargs 86 if [ $# -gt 0 ]; then 87 echo -n " $_param = " 88 while [ $# -gt 1 ]; do 89 echo -n "\"$1\", " 90 shift 91 done 92 echo "\"$1\";" 93 fi 94 ;; 95 *) 96 _name1=jail_${_jv}_${_name} 97 _name2=jail_${_name} 98 eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\" 99 if [ -n "$_tmpargs" ]; then 100 echo " $_param = \"$_tmpargs\";" 101 fi 102 ;; 103 esac 104} 105 106# parse_options _j _jv 107# Parse options and create a temporary configuration file if necessary. 108# 109parse_options() 110{ 111 local _j _jv _p 112 _j=$1 113 _jv=$2 114 115 _confwarn=0 116 if [ -z "$_j" ]; then 117 warn "parse_options: you must specify a jail" 118 return 119 fi 120 eval _jconf=\"\${jail_${_jv}_conf:-/etc/jail.${_j}.conf}\" 121 eval _rootdir=\"\$jail_${_jv}_rootdir\" 122 eval _jconfdir=\"/etc/jail.conf.d/${_j}.conf\" 123 eval _hostname=\"\$jail_${_jv}_hostname\" 124 if [ -z "$_rootdir" -o \ 125 -z "$_hostname" ]; then 126 if [ -r "$_jconf" ]; then 127 _conf="$_jconf" 128 return 0 129 elif [ -r "$_jconfdir" ] && ! egrep -q \ 130 '^\s*\.include\s*["'\'']?/etc/jail.conf.d/' "$jail_conf" \ 131 2>/dev/null; then 132 _conf="$_jconfdir" 133 return 0 134 elif [ -r "$jail_conf" ]; then 135 _conf="$jail_conf" 136 return 0 137 else 138 warn "Invalid configuration for $_j " \ 139 "(no jail.conf, no hostname, or no path). " \ 140 "Jail $_j was ignored." 141 fi 142 return 1 143 fi 144 eval _ip=\"\$jail_${_jv}_ip\" 145 if [ -z "$_ip" ] && ! check_kern_features vimage; then 146 warn "no ipaddress specified and no vimage support. " \ 147 "Jail $_j was ignored." 148 return 1 149 fi 150 _conf=/var/run/jail.${_j}.conf 151 # 152 # To relieve confusion, show a warning message. 153 # 154 : ${jail_confwarn:=YES} 155 checkyesno jail_confwarn && _confwarn=1 156 if [ -r "$jail_conf" -o -r "$_jconf" ]; then 157 if ! checkyesno jail_parallel_start; then 158 warn "$_conf is created and used for jail $_j." 159 fi 160 fi 161 /usr/bin/install -m 0644 -o root -g wheel /dev/null $_conf || return 1 162 163 eval : \${jail_${_jv}_flags:=${jail_flags}} 164 eval _exec=\"\$jail_${_jv}_exec\" 165 eval _exec_start=\"\$jail_${_jv}_exec_start\" 166 eval _exec_stop=\"\$jail_${_jv}_exec_stop\" 167 if [ -n "${_exec}" ]; then 168 # simple/backward-compatible execution 169 _exec_start="${_exec}" 170 _exec_stop="" 171 else 172 # flexible execution 173 if [ -z "${_exec_start}" ]; then 174 _exec_start="/bin/sh /etc/rc" 175 if [ -z "${_exec_stop}" ]; then 176 _exec_stop="/bin/sh /etc/rc.shutdown jail" 177 fi 178 fi 179 fi 180 eval _interface=\"\${jail_${_jv}_interface:-${jail_interface}}\" 181 eval _parameters=\"\${jail_${_jv}_parameters:-${jail_parameters}}\" 182 eval _fstab=\"\${jail_${_jv}_fstab:-${jail_fstab:-/etc/fstab.$_j}}\" 183 ( 184 date +"# Generated by rc.d/jail at %Y-%m-%d %H:%M:%S" 185 echo "$_j {" 186 extract_var $_jv hostname host.hostname - "" 187 extract_var $_jv rootdir path - "" 188 if [ -n "$_ip" ]; then 189 extract_var $_jv interface interface - "" 190 jail_handle_ips_option $_ip $_interface 191 alias=0 192 while : ; do 193 eval _x=\"\$jail_${_jv}_ip_multi${alias}\" 194 [ -z "$_x" ] && break 195 196 jail_handle_ips_option $_x $_interface 197 alias=$(($alias + 1)) 198 done 199 case $need_dad_wait in 200 1) 201 # Sleep to let DAD complete before 202 # starting services. 203 echo " exec.start += \"sleep " \ 204 $(($(${SYSCTL_N} net.inet6.ip6.dad_count) + 1)) \ 205 "\";" 206 ;; 207 esac 208 # These are applicable only to non-vimage jails. 209 extract_var $_jv fib exec.fib - "" 210 extract_var $_jv socket_unixiproute_only \ 211 allow.raw_sockets NY YES 212 else 213 echo " vnet;" 214 extract_var $_jv vnet_interface vnet.interface @ "" 215 fi 216 217 echo " exec.clean;" 218 echo " exec.system_user = \"root\";" 219 echo " exec.jail_user = \"root\";" 220 extract_var $_jv exec_prestart exec.prestart 0 "" 221 extract_var $_jv exec_poststart exec.poststart 0 "" 222 extract_var $_jv exec_prestop exec.prestop 0 "" 223 extract_var $_jv exec_poststop exec.poststop 0 "" 224 225 echo " exec.start += \"$_exec_start\";" 226 extract_var $_jv exec_afterstart exec.start 0 "" 227 echo " exec.stop = \"$_exec_stop\";" 228 229 extract_var $_jv consolelog exec.consolelog - \ 230 /var/log/jail_${_j}_console.log 231 232 if [ -r $_fstab ]; then 233 echo " mount.fstab = \"$_fstab\";" 234 fi 235 236 eval : \${jail_${_jv}_devfs_enable:=${jail_devfs_enable:-NO}} 237 if checkyesno jail_${_jv}_devfs_enable; then 238 echo " mount.devfs;" 239 eval _ruleset=\${jail_${_jv}_devfs_ruleset:-${jail_devfs_ruleset}} 240 case $_ruleset in 241 "") ;; 242 [0-9]*) echo " devfs_ruleset = \"$_ruleset\";" ;; 243 devfsrules_jail) 244 # XXX: This is the default value, 245 # Let jail(8) to use the default because 246 # mount(8) only accepts an integer. 247 # This should accept a ruleset name. 248 ;; 249 *) warn "devfs_ruleset must be an integer." ;; 250 esac 251 fi 252 eval : \${jail_${_jv}_fdescfs_enable:=${jail_fdescfs_enable:-NO}} 253 if checkyesno jail_${_jv}_fdescfs_enable; then 254 echo " mount.fdescfs;" 255 fi 256 eval : \${jail_${_jv}_procfs_enable:=${jail_procfs_enable:-NO}} 257 if checkyesno jail_${_jv}_procfs_enable; then 258 echo " mount.procfs;" 259 fi 260 261 eval : \${jail_${_jv}_mount_enable:=${jail_mount_enable:-NO}} 262 if checkyesno jail_${_jv}_mount_enable; then 263 echo " allow.mount;" 264 fi 265 266 extract_var $_jv set_hostname_allow allow.set_hostname YN NO 267 extract_var $_jv sysvipc_allow allow.sysvipc YN NO 268 extract_var $_jv enforce_statfs enforce_statfs - 2 269 extract_var $_jv osreldate osreldate 270 extract_var $_jv osrelease osrelease 271 272 _zfs_dataset="$(eval echo \$jail_${_jv}_zfs_dataset)" 273 if [ -n "$_zfs_dataset" ]; then 274 for ds in $_zfs_dataset; do 275 echo " zfs.dataset += ${ds};" 276 done 277 fi 278 for _p in $_parameters; do 279 echo " ${_p%\;};" 280 done 281 echo "}" 282 ) >> $_conf 283 284 return 0 285} 286 287# jail_extract_address argument iface 288# The second argument is the string from one of the _ip 289# or the _multi variables. In case of a comma separated list 290# only one argument must be passed in at a time. 291# The function alters the _type, _iface, _addr and _mask variables. 292# 293jail_extract_address() 294{ 295 local _i _interface 296 _i=$1 297 _interface=$2 298 299 if [ -z "${_i}" ]; then 300 warn "jail_extract_address: called without input" 301 return 302 fi 303 304 # Check if we have an interface prefix given and split into 305 # iFace and rest. 306 case "${_i}" in 307 *\|*) # ifN|.. prefix there 308 _iface=${_i%%|*} 309 _r=${_i##*|} 310 ;; 311 *) _iface="" 312 _r=${_i} 313 ;; 314 esac 315 316 # In case the IP has no interface given, check if we have a global one. 317 _iface=${_iface:-${_interface}} 318 319 # Set address, cut off any prefix/netmask/prefixlen. 320 _addr=${_r} 321 _addr=${_addr%%[/ ]*} 322 323 # Theoretically we can return here if interface is not set, 324 # as we only care about the _mask if we call ifconfig. 325 # This is not done because we may want to santize IP addresses 326 # based on _type later, and optionally change the type as well. 327 328 # Extract the prefix/netmask/prefixlen part by cutting off the address. 329 _mask=${_r} 330 _mask=`expr -- "${_mask}" : "${_addr}\(.*\)"` 331 332 # Identify type {inet,inet6}. 333 case "${_addr}" in 334 *\.*\.*\.*) _type="inet" ;; 335 *:*) _type="inet6" ;; 336 *) warn "jail_extract_address: type not identified" 337 ;; 338 esac 339 340 # Handle the special /netmask instead of /prefix or 341 # "netmask xxx" case for legacy IP. 342 # We do NOT support shortend class-full netmasks. 343 if [ "${_type}" = "inet" ]; then 344 case "${_mask}" in 345 /*\.*\.*\.*) _mask=" netmask ${_mask#/}" ;; 346 *) ;; 347 esac 348 349 # In case _mask is still not set use /32. 350 _mask=${_mask:-/32} 351 352 elif [ "${_type}" = "inet6" ]; then 353 # In case _mask is not set for IPv6, use /128. 354 _mask=${_mask:-/128} 355 fi 356} 357 358# jail_handle_ips_option input iface 359# Handle a single argument imput which can be a comma separated 360# list of addresses (theoretically with an option interface and 361# prefix/netmask/prefixlen). 362# 363jail_handle_ips_option() 364{ 365 local _x _type _i _defif 366 _x=$1 367 _defif=$2 368 369 if [ -z "${_x}" ]; then 370 # No IP given. This can happen for the primary address 371 # of each address family. 372 return 373 fi 374 375 # Loop, in case we find a comma separated list, we need to handle 376 # each argument on its own. 377 while [ ${#_x} -gt 0 ]; do 378 case "${_x}" in 379 *,*) # Extract the first argument and strip it off the list. 380 _i=`expr -- "${_x}" : '^\([^,]*\)'` 381 _x=`expr -- "${_x}" : "^[^,]*,\(.*\)"` 382 ;; 383 *) _i=${_x} 384 _x="" 385 ;; 386 esac 387 388 _type="" 389 _addr="" 390 _mask="" 391 _iface="" 392 jail_extract_address $_i $_defif 393 394 # make sure we got an address. 395 case $_addr in 396 "") continue ;; 397 *) ;; 398 esac 399 400 # Append address to list of addresses for the jail command. 401 case $_type in 402 inet) 403 echo " ip4.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";" 404 ;; 405 inet6) 406 echo " ip6.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";" 407 need_dad_wait=1 408 ;; 409 esac 410 done 411} 412 413jail_config() 414{ 415 local _j _jv 416 417 case $1 in 418 _ALL) return ;; 419 esac 420 for _j in $@; do 421 _j=$(echo $_j | tr /. _) 422 _jv=$(echo -n $_j | tr -c '[:alnum:]' _) 423 if parse_options $_j $_jv; then 424 echo "$_j: parameters are in $_conf." 425 fi 426 done 427} 428 429jail_console() 430{ 431 local _j _jv _cmd 432 433 # One argument that is not _ALL. 434 case $#:$1 in 435 0:*|1:_ALL) err 3 "Specify a jail name." ;; 436 1:*) ;; 437 esac 438 _j=$(echo $1 | tr /. _) 439 _jv=$(echo -n $1 | tr -c '[:alnum:]' _) 440 shift 441 case $# in 442 0) eval _cmd=\${jail_${_jv}_consolecmd:-$jail_consolecmd} ;; 443 *) _cmd=$@ ;; 444 esac 445 $jail_jexec $_j $_cmd 446} 447 448jail_status() 449{ 450 451 $jail_jls -N 452} 453 454jail_start() 455{ 456 local _j _jv _jid _id _name 457 458 if [ $# = 0 ]; then 459 return 460 fi 461 startmsg -n 'Starting jails:' 462 case $1 in 463 _ALL) 464 command=$jail_program 465 rc_flags=$jail_flags 466 command_args="-f $jail_conf -c" 467 if ! checkyesno jail_parallel_start; then 468 command_args="$command_args -p1" 469 fi 470 _tmp=`mktemp -t jail` || exit 3 471 if $command $rc_flags $command_args >> $_tmp 2>&1; then 472 $jail_jls jid name | while read _id _name; do 473 startmsg -n " $_name" 474 echo $_id > /var/run/jail_${_name}.id 475 done 476 else 477 cat $_tmp 478 fi 479 rm -f $_tmp 480 startmsg '.' 481 return 482 ;; 483 esac 484 if checkyesno jail_parallel_start; then 485 # 486 # Start jails in parallel and then check jail id when 487 # jail_parallel_start is YES. 488 # 489 for _j in $@; do 490 _j=$(echo $_j | tr /. _) 491 _jv=$(echo -n $_j | tr -c '[:alnum:]' _) 492 parse_options $_j $_jv || continue 493 494 eval rc_flags=\${jail_${_jv}_flags:-$jail_flags} 495 eval command=\${jail_${_jv}_program:-$jail_program} 496 command_args="-i -f $_conf -c $_j" 497 ( 498 _tmp=`mktemp -t jail_${_j}` || exit 3 499 if $command $rc_flags $command_args \ 500 >> $_tmp 2>&1 </dev/null; then 501 startmsg -n " ${_hostname:-${_j}}" 502 _jid=$($jail_jls -j $_j jid) 503 echo $_jid > /var/run/jail_${_j}.id 504 else 505 startmsg " cannot start jail " \ 506 "\"${_hostname:-${_j}}\": " 507 cat $_tmp 508 fi 509 rm -f $_tmp 510 ) & 511 done 512 wait 513 else 514 # 515 # Start jails one-by-one when jail_parallel_start is NO. 516 # 517 for _j in $@; do 518 _j=$(echo $_j | tr /. _) 519 _jv=$(echo -n $_j | tr -c '[:alnum:]' _) 520 parse_options $_j $_jv || continue 521 522 eval rc_flags=\${jail_${_jv}_flags:-$jail_flags} 523 eval command=\${jail_${_jv}_program:-$jail_program} 524 command_args="-i -f $_conf -c $_j" 525 _tmp=`mktemp -t jail` || exit 3 526 if $command $rc_flags $command_args \ 527 >> $_tmp 2>&1 </dev/null; then 528 startmsg -n " ${_hostname:-${_j}}" 529 _jid=$($jail_jls -j $_j jid) 530 echo $_jid > /var/run/jail_${_j}.id 531 else 532 startmsg " cannot start jail " \ 533 "\"${_hostname:-${_j}}\": " 534 cat $_tmp 535 fi 536 rm -f $_tmp 537 done 538 fi 539 startmsg '.' 540} 541 542jail_stop() 543{ 544 local _j _jv 545 546 if [ $# = 0 ]; then 547 return 548 fi 549 echo -n 'Stopping jails:' 550 case $1 in 551 _ALL) 552 command=$jail_program 553 rc_flags=$jail_flags 554 command_args="-f $jail_conf -r" 555 if checkyesno jail_reverse_stop; then 556 $jail_jls name | tail -r 557 else 558 $jail_jls name 559 fi | while read _j; do 560 echo -n " $_j" 561 _tmp=`mktemp -t jail` || exit 3 562 $command $rc_flags $command_args $_j >> $_tmp 2>&1 563 if $jail_jls -j $_j > /dev/null 2>&1; then 564 cat $_tmp 565 else 566 rm -f /var/run/jail_${_j}.id 567 fi 568 rm -f $_tmp 569 done 570 echo '.' 571 return 572 ;; 573 esac 574 checkyesno jail_reverse_stop && set -- $(reverse_list $@) 575 for _j in $@; do 576 _j=$(echo $_j | tr /. _) 577 _jv=$(echo -n $_j | tr -c '[:alnum:]' _) 578 parse_options $_j $_jv || continue 579 if ! $jail_jls -j $_j > /dev/null 2>&1; then 580 continue 581 fi 582 eval command=\${jail_${_jv}_program:-$jail_program} 583 echo -n " ${_hostname:-${_j}}" 584 _tmp=`mktemp -t jail` || exit 3 585 $command -q -f $_conf -r $_j >> $_tmp 2>&1 586 if $jail_jls -j $_j > /dev/null 2>&1; then 587 cat $_tmp 588 else 589 rm -f /var/run/jail_${_j}.id 590 fi 591 rm -f $_tmp 592 done 593 echo '.' 594} 595 596jail_warn() 597{ 598 599 # To relieve confusion, show a warning message. 600 case $_confwarn in 601 1) warn "Per-jail configuration via jail_* variables " \ 602 "is obsolete. Please consider migrating to $jail_conf." 603 ;; 604 esac 605} 606 607load_rc_config $name 608case $# in 6091) run_rc_command $@ ${jail_list:-_ALL} ;; 610*) jail_reverse_stop="no" 611 run_rc_command $@ ;; 612esac 613