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