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