xref: /freebsd/libexec/rc/rc.d/jail (revision 8522d140a568be6044aad4288042c72e8d3b72a7)
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