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