jail revision 302953
1#!/bin/sh
2#
3# $FreeBSD: stable/11/etc/rc.d/jail 302953 2016-07-17 14:05:11Z 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"
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	_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 osreldate osreldate
264		extract_var $_jv osrelease osrelease
265		for _p in $_parameters; do
266			echo "	${_p%\;};"
267		done
268		echo "}"
269	) >> $_conf
270
271	return 0
272}
273
274# jail_extract_address argument iface
275#	The second argument is the string from one of the _ip
276#	or the _multi variables. In case of a comma separated list
277#	only one argument must be passed in at a time.
278#	The function alters the _type, _iface, _addr and _mask variables.
279#
280jail_extract_address()
281{
282	local _i _interface
283	_i=$1
284	_interface=$2
285
286	if [ -z "${_i}" ]; then
287		warn "jail_extract_address: called without input"
288		return
289	fi
290
291	# Check if we have an interface prefix given and split into
292	# iFace and rest.
293	case "${_i}" in
294	*\|*)	# ifN|.. prefix there
295		_iface=${_i%%|*}
296		_r=${_i##*|}
297		;;
298	*)	_iface=""
299		_r=${_i}
300		;;
301	esac
302
303	# In case the IP has no interface given, check if we have a global one.
304	_iface=${_iface:-${_interface}}
305
306	# Set address, cut off any prefix/netmask/prefixlen.
307	_addr=${_r}
308	_addr=${_addr%%[/ ]*}
309
310	# Theoretically we can return here if interface is not set,
311	# as we only care about the _mask if we call ifconfig.
312	# This is not done because we may want to santize IP addresses
313	# based on _type later, and optionally change the type as well.
314
315	# Extract the prefix/netmask/prefixlen part by cutting off the address.
316	_mask=${_r}
317	_mask=`expr "${_mask}" : "${_addr}\(.*\)"`
318
319	# Identify type {inet,inet6}.
320	case "${_addr}" in
321	*\.*\.*\.*)	_type="inet" ;;
322	*:*)		_type="inet6" ;;
323	*)		warn "jail_extract_address: type not identified"
324			;;
325	esac
326
327	# Handle the special /netmask instead of /prefix or
328	# "netmask xxx" case for legacy IP.
329	# We do NOT support shortend class-full netmasks.
330	if [ "${_type}" = "inet" ]; then
331		case "${_mask}" in
332		/*\.*\.*\.*)	_mask=" netmask ${_mask#/}" ;;
333		*)		;;
334		esac
335
336		# In case _mask is still not set use /32.
337		_mask=${_mask:-/32}
338
339	elif [ "${_type}" = "inet6" ]; then
340		# In case _mask is not set for IPv6, use /128.
341		_mask=${_mask:-/128}
342	fi
343}
344
345# jail_handle_ips_option input iface
346#	Handle a single argument imput which can be a comma separated
347#	list of addresses (theoretically with an option interface and
348#	prefix/netmask/prefixlen).
349#
350jail_handle_ips_option()
351{
352	local _x _type _i _defif
353	_x=$1
354	_defif=$2
355
356	if [ -z "${_x}" ]; then
357		# No IP given. This can happen for the primary address
358		# of each address family.
359		return
360	fi
361
362	# Loop, in case we find a comma separated list, we need to handle
363	# each argument on its own.
364	while [ ${#_x} -gt 0 ]; do
365		case "${_x}" in
366		*,*)	# Extract the first argument and strip it off the list.
367			_i=`expr "${_x}" : '^\([^,]*\)'`
368			_x=`expr "${_x}" : "^[^,]*,\(.*\)"`
369		;;
370		*)	_i=${_x}
371			_x=""
372		;;
373		esac
374
375		_type=""
376		_addr=""
377		_mask=""
378		_iface=""
379		jail_extract_address $_i $_defif
380
381		# make sure we got an address.
382		case $_addr in
383		"")	continue ;;
384		*)	;;
385		esac
386
387		# Append address to list of addresses for the jail command.
388		case $_type in
389		inet)
390			echo "	ip4.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";"
391		;;
392		inet6)
393			echo "	ip6.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";"
394			need_dad_wait=1
395		;;
396		esac
397	done
398}
399
400jail_config()
401{
402	local _j _jv
403
404	case $1 in
405	_ALL)	return ;;
406	esac
407	for _j in $@; do
408		_j=$(echo $_j | tr /. _)
409		_jv=$(echo -n $_j | tr -c '[:alnum:]' _)
410		if parse_options $_j $_jv; then 
411			echo "$_j: parameters are in $_conf."
412		fi
413	done
414}
415
416jail_console()
417{
418	local _j _jv _cmd
419
420	# One argument that is not _ALL.
421	case $#:$1 in
422	0:*|1:_ALL)	err 3 "Specify a jail name." ;;
423	1:*)		;;
424	esac
425	_j=$(echo $1 | tr /. _)
426	_jv=$(echo -n $1 | tr -c '[:alnum:]' _)
427	shift
428	case $# in
429	0)	eval _cmd=\${jail_${_jv}_consolecmd:-$jail_consolecmd} ;;
430	*)	_cmd=$@ ;;
431	esac
432	$jail_jexec $_j $_cmd
433}
434
435jail_status()
436{
437
438	$jail_jls -N
439}
440
441jail_start()
442{
443	local _j _jv _jid _id _name
444
445	if [ $# = 0 ]; then
446		return
447	fi
448	echo -n 'Starting jails:'
449	case $1 in
450	_ALL)
451		command=$jail_program
452		rc_flags=$jail_flags
453		command_args="-f $jail_conf -c"
454		_tmp=`mktemp -t jail` || exit 3
455		if $command $rc_flags $command_args >> $_tmp 2>&1; then
456			$jail_jls jid name | while read _id _name; do
457				echo -n " $_name"
458				echo $_id > /var/run/jail_${_name}.id
459			done
460		else
461			tail -1 $_tmp
462		fi
463		rm -f $_tmp
464		echo '.'
465		return
466	;;
467	esac
468	if checkyesno jail_parallel_start; then
469		#
470		# Start jails in parallel and then check jail id when
471		# jail_parallel_start is YES.
472		#
473		for _j in $@; do
474			_j=$(echo $_j | tr /. _)
475			_jv=$(echo -n $_j | tr -c '[:alnum:]' _)
476			parse_options $_j $_jv || continue
477
478			eval rc_flags=\${jail_${_jv}_flags:-$jail_flags}
479			eval command=\${jail_${_jv}_program:-$jail_program}
480			command_args="-i -f $_conf -c $_j"
481			(
482				_tmp=`mktemp -t jail_${_j}` || exit 3
483				if $command $rc_flags $command_args \
484				    >> $_tmp 2>&1 </dev/null; then
485					echo -n " ${_hostname:-${_j}}"
486					_jid=$($jail_jls -j $_j jid)
487					echo $_jid > /var/run/jail_${_j}.id
488				else
489					echo " cannot start jail " \
490					    "\"${_hostname:-${_j}}\": "
491					cat $_tmp
492				fi
493				rm -f $_tmp
494			) &
495		done
496		wait
497	else
498		#
499		# Start jails one-by-one when jail_parallel_start is NO.
500		#
501		for _j in $@; do
502			_j=$(echo $_j | tr /. _)
503			_jv=$(echo -n $_j | tr -c '[:alnum:]' _)
504			parse_options $_j $_jv || continue
505
506			eval rc_flags=\${jail_${_jv}_flags:-$jail_flags}
507			eval command=\${jail_${_jv}_program:-$jail_program}
508			command_args="-i -f $_conf -c $_j"
509			_tmp=`mktemp -t jail` || exit 3
510			if $command $rc_flags $command_args \
511			    >> $_tmp 2>&1 </dev/null; then
512				echo -n " ${_hostname:-${_j}}"
513				_jid=$($jail_jls -j $_j jid)
514				echo $_jid > /var/run/jail_${_j}.id
515			else
516				echo " cannot start jail " \
517				    "\"${_hostname:-${_j}}\": "
518				cat $_tmp
519			fi
520			rm -f $_tmp
521		done
522	fi
523	echo '.'
524}
525
526jail_stop()
527{
528	local _j _jv
529
530	if [ $# = 0 ]; then
531		return
532	fi
533	echo -n 'Stopping jails:'
534	case $1 in
535	_ALL)
536		command=$jail_program
537		rc_flags=$jail_flags
538		command_args="-f $jail_conf -r"
539		if checkyesno jail_reverse_stop; then
540			$jail_jls name | tail -r
541		else
542			$jail_jls name
543		fi | while read _j; do
544			echo -n " $_j"
545			_tmp=`mktemp -t jail` || exit 3
546			$command $rc_flags $command_args $_j >> $_tmp 2>&1
547			if $jail_jls -j $_j > /dev/null 2>&1; then
548				tail -1 $_tmp
549			else
550				rm -f /var/run/jail_${_j}.id
551			fi
552			rm -f $_tmp
553		done
554		echo '.'
555		return
556	;;
557	esac
558	checkyesno jail_reverse_stop && set -- $(reverse_list $@)
559	for _j in $@; do
560		_j=$(echo $_j | tr /. _)
561		_jv=$(echo -n $_j | tr -c '[:alnum:]' _)
562		parse_options $_j $_jv || continue
563		if ! $jail_jls -j $_j > /dev/null 2>&1; then
564			continue
565		fi
566		eval command=\${jail_${_jv}_program:-$jail_program}
567		echo -n " ${_hostname:-${_j}}"
568		_tmp=`mktemp -t jail` || exit 3
569		$command -q -f $_conf -r $_j >> $_tmp 2>&1
570		if $jail_jls -j $_j > /dev/null 2>&1; then
571			tail -1 $_tmp
572		else
573			rm -f /var/run/jail_${_j}.id
574		fi
575		rm -f $_tmp
576	done
577	echo '.'
578}
579
580jail_warn()
581{
582
583	# To relieve confusion, show a warning message.
584	case $_confwarn in
585	1)	warn "Per-jail configuration via jail_* variables " \
586		    "is obsolete.  Please consider migrating to $jail_conf."
587	;;
588	esac
589}
590
591load_rc_config $name
592case $# in
5931)	run_rc_command $@ ${jail_list:-_ALL} ;;
594*)	jail_reverse_stop="no"
595	run_rc_command $@ ;;
596esac
597