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