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