jail revision 259141
1214152Sed#!/bin/sh
2214152Sed#
3214152Sed# $FreeBSD: stable/10/etc/rc.d/jail 259141 2013-12-09 21:32:36Z peter $
4214152Sed#
5214152Sed
6214152Sed# PROVIDE: jail
7214152Sed# REQUIRE: LOGIN FILESYSTEMS
8214152Sed# BEFORE: securelevel
9214152Sed# KEYWORD: nojail shutdown
10214152Sed
11214152Sed. /etc/rc.subr
12214152Sed
13214152Sedname="jail"
14214152Sedrcvar="jail_enable"
15214152Sed
16214152Sedstart_cmd="jail_start"
17214152Sedstart_postcmd="jail_warn"
18214152Sedstop_cmd="jail_stop"
19214152Sedconfig_cmd="jail_config"
20214152Sedconsole_cmd="jail_console"
21214152Sedstatus_cmd="jail_status"
22214152Sedextra_commands="config console status"
23214152Sed: ${jail_conf:=/etc/jail.conf}
24214152Sed: ${jail_program:=/usr/sbin/jail}
25214152Sed: ${jail_consolecmd:=/usr/bin/login -f root}
26214152Sed: ${jail_jexec:=/usr/sbin/jexec}
27214152Sed: ${jail_jls:=/usr/sbin/jls}
28214152Sed
29214152Sedneed_dad_wait=
30214152Sed
31214152Sed# extact_var jail name param num defval
32214152Sed#	Extract value from ${jail_$jail_$name} or ${jail_$name} and
33214152Sed#	set it to $param.  If not defined, $defval is used.
34214152Sed#	When $num is [0-9]*, ${jail_$jail_$name$num} are looked up and
35214152Sed#	$param is set by using +=.
36214152Sed#	When $num is YN or NY, the value is interpret as boolean.
37214152Sedextract_var()
38214152Sed{
39214152Sed	local i _j _name _param _num _def _name1 _name2
40214152Sed	_j=$1
41214152Sed	_name=$2
42214152Sed	_param=$3
43214152Sed	_num=$4
44214152Sed	_def=$5
45214152Sed
46214152Sed	case $_num in
47214152Sed	YN)
48214152Sed		_name1=jail_${_j}_${_name}
49214152Sed		_name2=jail_${_name}
50214152Sed		eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\"
51214152Sed		if checkyesno $_name1; then
52214152Sed			echo "	$_param = 1;"
53214152Sed		else
54214152Sed			echo "	$_param = 0;"
55214152Sed		fi
56214152Sed	;;
57214152Sed	NY)
58214152Sed		_name1=jail_${_j}_${_name}
59214152Sed		_name2=jail_${_name}
60214152Sed		eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\"
61214152Sed		if checkyesno $_name1; then
62214152Sed			echo "	$_param = 0;"
63214152Sed		else
64214152Sed			echo "	$_param = 1;"
65214152Sed		fi
66214152Sed	;;
67214152Sed	[0-9]*)
68214152Sed		i=$_num
69214152Sed		while : ; do
70214152Sed			_name1=jail_${_j}_${_name}${i}
71214152Sed			_name2=jail_${_name}${i}
72214152Sed			eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\"
73214152Sed			if [ -n "$_tmpargs" ]; then 
74214152Sed				echo "	$_param += \"$_tmpargs\";"
75214152Sed			else
76214152Sed				break;
77214152Sed			fi
78214152Sed			i=$(($i + 1))
79214152Sed		done
80214152Sed	;;
81214152Sed	*)
82214152Sed		_name1=jail_${_j}_${_name}
83214152Sed		_name2=jail_${_name}
84214152Sed		eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\"
85214152Sed		if [ -n "$_tmpargs" ]; then
86214152Sed			echo "	$_param = \"$_tmpargs\";"
87214152Sed		fi
88214152Sed	;;
89214152Sed	esac
90214152Sed}
91214152Sed
92214152Sed# parse_options _j
93214152Sed#	Parse options and create a temporary configuration file if necessary.
94214152Sed#
95214152Sedparse_options()
96214152Sed{
97214152Sed	local _j _p
98214152Sed	_j=$1
99214152Sed
100214152Sed	_confwarn=0
101214152Sed	if [ -z "$_j" ]; then
102214152Sed		warn "parse_options: you must specify a jail"
103214152Sed		return
104214152Sed	fi
105214152Sed	eval _jconf=\"\${jail_${_j}_conf:-/etc/jail.${_j}.conf}\"
106214152Sed	eval _rootdir=\"\$jail_${_j}_rootdir\"
107214152Sed	eval _hostname=\"\$jail_${_j}_hostname\"
108214152Sed	if [ -z "$_rootdir" -o \
109214152Sed	     -z "$_hostname" ]; then
110214152Sed		if [ -r "$_jconf" ]; then
111214152Sed			_conf="$_jconf"
112214152Sed			return 0
113214152Sed		elif [ -r "$jail_conf" ]; then
114214152Sed			_conf="$jail_conf"
115214152Sed			return 0
116214152Sed		else
117214152Sed			warn "Invalid configuration for $_j " \
118214152Sed			    "(no jail.conf, no hostname, or no path).  " \
119214152Sed			    "Jail $_j was ignored."
120214152Sed		fi
121214152Sed		return 1
122214152Sed	fi
123214152Sed	eval _ip=\"\$jail_${_j}_ip\"
124214152Sed	if [ -z "$_ip" ] && ! check_kern_features vimage; then
125214152Sed		warn "no ipaddress specified and no vimage support.  " \
126214152Sed		    "Jail $_j was ignored."
127214152Sed		return 1
128214152Sed	fi
129214152Sed	_conf=/var/run/jail.${_j}.conf
130214152Sed	#
131214152Sed	# To relieve confusion, show a warning message.
132214152Sed	#
133214152Sed	_confwarn=1
134214152Sed	if [ -r "$jail_conf" -o -r "$_jconf" ]; then
135214152Sed		if ! checkyesno jail_parallel_start; then
136214152Sed			warn "$_conf is created and used for jail $_j."
137214152Sed		fi
138214152Sed	fi
139214152Sed	/usr/bin/install -m 0644 -o root -g wheel /dev/null $_conf || return 1
140214152Sed
141214152Sed	eval : \${jail_${_j}_flags:=${jail_flags}}
142214152Sed	eval _exec=\"\$jail_${_j}_exec\"
143214152Sed	eval _exec_start=\"\$jail_${_j}_exec_start\"
144214152Sed	eval _exec_stop=\"\$jail_${_j}_exec_stop\"
145214152Sed	if [ -n "${_exec}" ]; then
146214152Sed		#   simple/backward-compatible execution
147214152Sed		_exec_start="${_exec}"
148214152Sed		_exec_stop=""
149214152Sed	else
150214152Sed		#   flexible execution
151214152Sed		if [ -z "${_exec_start}" ]; then
152214152Sed			_exec_start="/bin/sh /etc/rc"
153214152Sed			if [ -z "${_exec_stop}" ]; then
154214152Sed				_exec_stop="/bin/sh /etc/rc.shutdown"
155214152Sed			fi
156214152Sed		fi
157214152Sed	fi
158214152Sed	eval _interface=\"\${jail_${_j}_interface:-${jail_interface}}\"
159214152Sed	eval _parameters=\"\${jail_${_j}_parameters:-${jail_parameters}}\"
160214152Sed	eval _fstab=\"\${jail_${_j}_fstab:-${jail_fstab:-/etc/fstab.$_j}}\"
161214152Sed	(
162214152Sed		date +"# Generated by rc.d/jail at %Y-%m-%d %H:%M:%S"
163214152Sed		echo "$_j {"
164214152Sed		extract_var $_j hostname host.hostname - ""
165214152Sed		extract_var $_j rootdir path - ""
166214152Sed		if [ -n "$_ip" ]; then
167214152Sed			extract_var $_j interface interface - ""
168214152Sed			jail_handle_ips_option $_ip $_interface
169214152Sed			alias=0
170214152Sed			while : ; do
171214152Sed				eval _x=\"\$jail_${_j}_ip_multi${alias}\"
172214152Sed				[ -z "$_x" ] && break
173214152Sed
174214152Sed				jail_handle_ips_option $_x $_interface
175214152Sed				alias=$(($alias + 1))
176214152Sed			done
177214152Sed			case $need_dad_wait in
178214152Sed			1)
179214152Sed				# Sleep to let DAD complete before
180				# starting services.
181				echo "	exec.start += \"sleep " \
182				$(($(${SYSCTL_N} net.inet6.ip6.dad_count) + 1)) \
183				"\";"
184			;;
185			esac
186			# These are applicable only to non-vimage jails. 
187			extract_var $_j fib exec.fib - ""
188			extract_var $_j socket_unixiproute_only \
189			    allow.raw_sockets NY YES
190		else
191			echo "	vnet;"
192			extract_var $_j vnet_interface vnet.interface - ""
193		fi
194
195		echo "	exec.clean;"
196		echo "	exec.system_user = \"root\";"
197		echo "	exec.jail_user = \"root\";"
198		extract_var $_j exec_prestart exec.prestart 0 ""
199		extract_var $_j exec_poststart exec.poststart 0 ""
200		extract_var $_j exec_prestop exec.prestop 0 ""
201		extract_var $_j exec_poststop exec.poststop 0 ""
202
203		echo "	exec.start += \"$_exec_start\";"
204		extract_var $_j exec_afterstart exec.start 1 ""
205		echo "	exec.stop = \"$_exec_stop\";"
206
207		extract_var $_j consolelog exec.consolelog - \
208		    /var/log/jail_${_j}_console.log
209
210		eval : \${jail_${_j}_devfs_enable:=${jail_devfs_enable:-NO}}
211		if checkyesno jail_${_j}_devfs_enable; then
212			echo "	mount.devfs;"
213			eval _ruleset=\${jail_${_j}_devfs_ruleset:-${jail_devfs_ruleset}}
214			case $_ruleset in
215			"")	;;
216			[0-9]*) echo "	devfs_ruleset = \"$_ruleset\";" ;;
217			devfsrules_jail)
218				# XXX: This is the default value,
219				# Let jail(8) to use the default because
220				# mount(8) only accepts an integer. 
221				# This should accept a ruleset name.
222			;;
223			*)	warn "devfs_ruleset must be an integer." ;;
224			esac
225			if [ -r $_fstab ]; then
226				echo "	mount.fstab = \"$_fstab\";"
227			fi
228		fi
229
230		eval : \${jail_${_j}_fdescfs_enable:=${jail_fdescfs_enable:-NO}}
231		if checkyesno jail_${_j}_fdescfs_enable; then
232			echo "	mount.fdescfs;"
233		fi
234		eval : \${jail_${_j}_procfs_enable:=${jail_procfs_enable:-NO}}
235		if checkyesno jail_${_j}_procfs_enable; then
236			echo "	mount += " \
237			    "\"procfs ${_rootdir%/}/proc procfs rw 0 0\";"
238		fi
239
240		eval : \${jail_${_j}_mount_enable:=${jail_mount_enable:-NO}}
241		if checkyesno jail_${_j}_mount_enable; then
242			echo "	allow.mount;" >> $_conf
243		fi
244
245		extract_var $_j set_hostname_allow allow.set_hostname YN NO
246		extract_var $_j 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 _maske 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
385
386	case $1 in
387	_ALL)	return ;;
388	esac
389	for _j in $@; do
390		_j=$(echo $_j | tr /. _)
391		if parse_options $_j; then 
392			echo "$_j: parameters are in $_conf."
393		fi
394	done
395}
396
397jail_console()
398{
399	local _j _cmd
400
401	# One argument that is not _ALL.
402	case $#:$1 in
403	0:*|1:_ALL)	err 3 "Specify a jail name." ;;
404	1:*)		;;
405	esac
406	_j=$(echo $1 | tr /. _)
407	shift
408	case $# in
409	0)	eval _cmd=\${jail_${_j}_consolecmd:-$jail_consolecmd} ;;
410	*)	_cmd=$@ ;;
411	esac
412	$jail_jexec $_j $_cmd
413}
414
415jail_status()
416{
417
418	$jail_jls -N
419}
420
421jail_start()
422{
423	local _j _jid _jn _jl
424
425	if [ $# = 0 ]; then
426		return
427	fi
428	echo -n 'Starting jails:'
429	case $1 in
430	_ALL)
431		command=$jail_program
432		rc_flags=$jail_flags
433		command_args="-f $jail_conf -c"
434		_tmp=`mktemp -t jail` || exit 3
435		if $command $rc_flags $command_args >> $_tmp 2>&1; then
436			$jail_jls -nq | while read IN; do
437				_jn=$(echo $IN | tr " " "\n" | grep ^name=)
438				_jid=$(echo $IN | tr " " "\n" | grep ^jid=)
439				echo -n " ${_jn#name=}"
440				echo "${_jid#jid=}" \
441				    > /var/run/jail_${_jn#name=}.id
442			done
443		else
444			tail -1 $_tmp
445		fi
446		rm -f $_tmp
447		echo '.'
448		return
449	;;
450	esac
451	if checkyesno jail_parallel_start; then
452		#
453		# Start jails in parallel and then check jail id when
454		# jail_parallel_start is YES.
455		#
456		_jl=
457		for _j in $@; do
458			_j=$(echo $_j | tr /. _)
459			parse_options $_j || continue
460
461			_jl="$_jl $_j"
462			eval rc_flags=\${jail_${_j}_flags:-$jail_flags}
463			eval command=\${jail_${_j}_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 -n -j $_j | tr " " "\n" | \
472			    grep ^jid=); then
473				echo "${_jid#jid=}" > /var/run/jail_${_j}.id
474			else
475				rm -f /var/run/jail_${_j}.id
476				echo " cannot start jail " \
477				    "\"${_hostname:-${_j}}\": "
478			fi
479		done
480	else
481		#
482		# Start jails one-by-one when jail_parallel_start is NO.
483		#
484		for _j in $@; do
485			_j=$(echo $_j | tr /. _)
486			parse_options $_j || continue
487
488			eval rc_flags=\${jail_${_j}_flags:-$jail_flags}
489			eval command=\${jail_${_j}_program:-$jail_program}
490			command_args="-i -f $_conf -c $_j"
491			_tmp=`mktemp -t jail` || exit 3
492			if $command $rc_flags $command_args \
493			    >> $_tmp 2>&1 </dev/null; then
494				echo -n " ${_hostname:-${_j}}"
495				_jid=$($jail_jls -n -j $_j | \
496				    tr " " "\n" | grep ^jid=)
497				echo "${_jid#jid=}" > /var/run/jail_${_j}.id
498			else
499				rm -f /var/run/jail_${_j}.id
500				echo " cannot start jail " \
501				    "\"${_hostname:-${_j}}\": "
502				cat $_tmp
503			fi
504			rm -f $_tmp
505		done
506	fi
507	echo '.'
508}
509
510jail_stop()
511{
512	local _j _jn
513
514	if [ $# = 0 ]; then
515		return
516	fi
517	echo -n 'Stopping jails:'
518	case $1 in
519	_ALL)
520		command=$jail_program
521		rc_flags=$jail_flags
522		command_args="-f $jail_conf -r"
523		$jail_jls -nq | while read IN; do
524			_jn=$(echo $IN | tr " " "\n" | grep ^name=)
525			echo -n " ${_jn#name=}"
526			_tmp=`mktemp -t jail` || exit 3
527			$command $rc_flags $command_args ${_jn#name=} \
528			    >> $_tmp 2>&1
529			if $jail_jls -j ${_jn#name=} > /dev/null 2>&1; then
530				tail -1 $_tmp
531			else
532				rm -f /var/run/jail_${_jn#name=}.id
533			fi
534			rm -f $_tmp
535		done
536		echo '.'
537		return
538	;;
539	esac
540	for _j in $@; do
541		_j=$(echo $_j | tr /. _)
542		parse_options $_j || continue
543		if ! $jail_jls -j $_j > /dev/null 2>&1; then
544			continue
545		fi
546		eval command=\${jail_${_j}_program:-$jail_program}
547		echo -n " ${_hostname:-${_j}}"
548		_tmp=`mktemp -t jail` || exit 3
549		$command -q -f $_conf -r $_j >> $_tmp 2>&1
550		if $jail_jls -j $_j > /dev/null 2>&1; then
551			tail -1 $_tmp
552		else
553			rm -f /var/run/jail_${_j}.id
554		fi
555		rm -f $_tmp
556	done
557	echo '.'
558}
559
560jail_warn()
561{
562
563	# To relieve confusion, show a warning message.
564	case $_confwarn in
565	1)	warn "Per-jail configuration via jail_* variables " \
566		    "is obsolete.  Please consider to migrate to $jail_conf."
567	;;
568	esac
569}
570
571load_rc_config $name
572case $# in
5731)	run_rc_command $@ ${jail_list:-_ALL} ;;
574*)	run_rc_command $@ ;;
575esac
576