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