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