]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - etc/rc.d/jail
MFC r340321: Move definition of $jail_conf variable to /etc/defaults/rc.conf
[FreeBSD/FreeBSD.git] / etc / rc.d / jail
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
13 name="jail"
14 desc="Manage system jails"
15 rcvar="jail_enable"
16
17 start_cmd="jail_start"
18 start_postcmd="jail_warn"
19 stop_cmd="jail_stop"
20 config_cmd="jail_config"
21 console_cmd="jail_console"
22 status_cmd="jail_status"
23 extra_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
29 need_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.
38 extract_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 #
110 parse_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"
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 #
281 jail_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 #
351 jail_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
401 jail_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
417 jail_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
436 jail_status()
437 {
438
439         $jail_jls -N
440 }
441
442 jail_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
530 jail_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
584 jail_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
595 load_rc_config $name
596 case $# in
597 1)      run_rc_command $@ ${jail_list:-_ALL} ;;
598 *)      jail_reverse_stop="no"
599         run_rc_command $@ ;;
600 esac