Changeset View
Changeset View
Standalone View
Standalone View
head/sbin/init/rc.d/jail
Property | Old Value | New Value |
---|---|---|
svn:executable | null | * \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
#!/bin/sh | |||||
# | |||||
# $FreeBSD$ | |||||
# | |||||
# PROVIDE: jail | |||||
# REQUIRE: LOGIN FILESYSTEMS | |||||
# BEFORE: securelevel | |||||
# KEYWORD: nojail shutdown | |||||
. /etc/rc.subr | |||||
name="jail" | |||||
desc="Manage system jails" | |||||
rcvar="jail_enable" | |||||
start_cmd="jail_start" | |||||
start_postcmd="jail_warn" | |||||
stop_cmd="jail_stop" | |||||
config_cmd="jail_config" | |||||
console_cmd="jail_console" | |||||
status_cmd="jail_status" | |||||
extra_commands="config console status" | |||||
: ${jail_conf:=/etc/jail.conf} | |||||
: ${jail_program:=/usr/sbin/jail} | |||||
: ${jail_consolecmd:=/usr/bin/login -f root} | |||||
: ${jail_jexec:=/usr/sbin/jexec} | |||||
: ${jail_jls:=/usr/sbin/jls} | |||||
need_dad_wait= | |||||
# extract_var jv name param num defval | |||||
# Extract value from ${jail_$jv_$name} or ${jail_$name} and | |||||
# set it to $param. If not defined, $defval is used. | |||||
# When $num is [0-9]*, ${jail_$jv_$name$num} are looked up and | |||||
# $param is set by using +=. $num=0 is optional (params may start at 1). | |||||
# When $num is YN or NY, the value is interpreted as boolean. | |||||
# When $num is @, the value is interpreted as an array separted by IFS. | |||||
extract_var() | |||||
{ | |||||
local i _jv _name _param _num _def _name1 _name2 | |||||
_jv=$1 | |||||
_name=$2 | |||||
_param=$3 | |||||
_num=$4 | |||||
_def=$5 | |||||
case $_num in | |||||
YN) | |||||
_name1=jail_${_jv}_${_name} | |||||
_name2=jail_${_name} | |||||
eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\" | |||||
if checkyesno $_name1; then | |||||
echo " $_param = 1;" | |||||
else | |||||
echo " $_param = 0;" | |||||
fi | |||||
;; | |||||
NY) | |||||
_name1=jail_${_jv}_${_name} | |||||
_name2=jail_${_name} | |||||
eval $_name1=\"\${$_name1:-\${$_name2:-$_def}}\" | |||||
if checkyesno $_name1; then | |||||
echo " $_param = 0;" | |||||
else | |||||
echo " $_param = 1;" | |||||
fi | |||||
;; | |||||
[0-9]*) | |||||
i=$_num | |||||
while : ; do | |||||
_name1=jail_${_jv}_${_name}${i} | |||||
_name2=jail_${_name}${i} | |||||
eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\" | |||||
if [ -n "$_tmpargs" ]; then | |||||
echo " $_param += \"$_tmpargs\";" | |||||
elif [ $i != 0 ]; then | |||||
break; | |||||
fi | |||||
i=$(($i + 1)) | |||||
done | |||||
;; | |||||
@) | |||||
_name1=jail_${_jv}_${_name} | |||||
_name2=jail_${_name} | |||||
eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\" | |||||
set -- $_tmpargs | |||||
if [ $# -gt 0 ]; then | |||||
echo -n " $_param = " | |||||
while [ $# -gt 1 ]; do | |||||
echo -n "\"$1\", " | |||||
shift | |||||
done | |||||
echo "\"$1\";" | |||||
fi | |||||
;; | |||||
*) | |||||
_name1=jail_${_jv}_${_name} | |||||
_name2=jail_${_name} | |||||
eval _tmpargs=\"\${$_name1:-\${$_name2:-$_def}}\" | |||||
if [ -n "$_tmpargs" ]; then | |||||
echo " $_param = \"$_tmpargs\";" | |||||
fi | |||||
;; | |||||
esac | |||||
} | |||||
# parse_options _j _jv | |||||
# Parse options and create a temporary configuration file if necessary. | |||||
# | |||||
parse_options() | |||||
{ | |||||
local _j _jv _p | |||||
_j=$1 | |||||
_jv=$2 | |||||
_confwarn=0 | |||||
if [ -z "$_j" ]; then | |||||
warn "parse_options: you must specify a jail" | |||||
return | |||||
fi | |||||
eval _jconf=\"\${jail_${_jv}_conf:-/etc/jail.${_j}.conf}\" | |||||
eval _rootdir=\"\$jail_${_jv}_rootdir\" | |||||
eval _hostname=\"\$jail_${_jv}_hostname\" | |||||
if [ -z "$_rootdir" -o \ | |||||
-z "$_hostname" ]; then | |||||
if [ -r "$_jconf" ]; then | |||||
_conf="$_jconf" | |||||
return 0 | |||||
elif [ -r "$jail_conf" ]; then | |||||
_conf="$jail_conf" | |||||
return 0 | |||||
else | |||||
warn "Invalid configuration for $_j " \ | |||||
"(no jail.conf, no hostname, or no path). " \ | |||||
"Jail $_j was ignored." | |||||
fi | |||||
return 1 | |||||
fi | |||||
eval _ip=\"\$jail_${_jv}_ip\" | |||||
if [ -z "$_ip" ] && ! check_kern_features vimage; then | |||||
warn "no ipaddress specified and no vimage support. " \ | |||||
"Jail $_j was ignored." | |||||
return 1 | |||||
fi | |||||
_conf=/var/run/jail.${_j}.conf | |||||
# | |||||
# To relieve confusion, show a warning message. | |||||
# | |||||
: ${jail_confwarn:=YES} | |||||
checkyesno jail_confwarn && _confwarn=1 | |||||
if [ -r "$jail_conf" -o -r "$_jconf" ]; then | |||||
if ! checkyesno jail_parallel_start; then | |||||
warn "$_conf is created and used for jail $_j." | |||||
fi | |||||
fi | |||||
/usr/bin/install -m 0644 -o root -g wheel /dev/null $_conf || return 1 | |||||
eval : \${jail_${_jv}_flags:=${jail_flags}} | |||||
eval _exec=\"\$jail_${_jv}_exec\" | |||||
eval _exec_start=\"\$jail_${_jv}_exec_start\" | |||||
eval _exec_stop=\"\$jail_${_jv}_exec_stop\" | |||||
if [ -n "${_exec}" ]; then | |||||
# simple/backward-compatible execution | |||||
_exec_start="${_exec}" | |||||
_exec_stop="" | |||||
else | |||||
# flexible execution | |||||
if [ -z "${_exec_start}" ]; then | |||||
_exec_start="/bin/sh /etc/rc" | |||||
if [ -z "${_exec_stop}" ]; then | |||||
_exec_stop="/bin/sh /etc/rc.shutdown" | |||||
fi | |||||
fi | |||||
fi | |||||
eval _interface=\"\${jail_${_jv}_interface:-${jail_interface}}\" | |||||
eval _parameters=\"\${jail_${_jv}_parameters:-${jail_parameters}}\" | |||||
eval _fstab=\"\${jail_${_jv}_fstab:-${jail_fstab:-/etc/fstab.$_j}}\" | |||||
( | |||||
date +"# Generated by rc.d/jail at %Y-%m-%d %H:%M:%S" | |||||
echo "$_j {" | |||||
extract_var $_jv hostname host.hostname - "" | |||||
extract_var $_jv rootdir path - "" | |||||
if [ -n "$_ip" ]; then | |||||
extract_var $_jv interface interface - "" | |||||
jail_handle_ips_option $_ip $_interface | |||||
alias=0 | |||||
while : ; do | |||||
eval _x=\"\$jail_${_jv}_ip_multi${alias}\" | |||||
[ -z "$_x" ] && break | |||||
jail_handle_ips_option $_x $_interface | |||||
alias=$(($alias + 1)) | |||||
done | |||||
case $need_dad_wait in | |||||
1) | |||||
# Sleep to let DAD complete before | |||||
# starting services. | |||||
echo " exec.start += \"sleep " \ | |||||
$(($(${SYSCTL_N} net.inet6.ip6.dad_count) + 1)) \ | |||||
"\";" | |||||
;; | |||||
esac | |||||
# These are applicable only to non-vimage jails. | |||||
extract_var $_jv fib exec.fib - "" | |||||
extract_var $_jv socket_unixiproute_only \ | |||||
allow.raw_sockets NY YES | |||||
else | |||||
echo " vnet;" | |||||
extract_var $_jv vnet_interface vnet.interface @ "" | |||||
fi | |||||
echo " exec.clean;" | |||||
echo " exec.system_user = \"root\";" | |||||
echo " exec.jail_user = \"root\";" | |||||
extract_var $_jv exec_prestart exec.prestart 0 "" | |||||
extract_var $_jv exec_poststart exec.poststart 0 "" | |||||
extract_var $_jv exec_prestop exec.prestop 0 "" | |||||
extract_var $_jv exec_poststop exec.poststop 0 "" | |||||
echo " exec.start += \"$_exec_start\";" | |||||
extract_var $_jv exec_afterstart exec.start 0 "" | |||||
echo " exec.stop = \"$_exec_stop\";" | |||||
extract_var $_jv consolelog exec.consolelog - \ | |||||
/var/log/jail_${_j}_console.log | |||||
if [ -r $_fstab ]; then | |||||
echo " mount.fstab = \"$_fstab\";" | |||||
fi | |||||
eval : \${jail_${_jv}_devfs_enable:=${jail_devfs_enable:-NO}} | |||||
if checkyesno jail_${_jv}_devfs_enable; then | |||||
echo " mount.devfs;" | |||||
eval _ruleset=\${jail_${_jv}_devfs_ruleset:-${jail_devfs_ruleset}} | |||||
case $_ruleset in | |||||
"") ;; | |||||
[0-9]*) echo " devfs_ruleset = \"$_ruleset\";" ;; | |||||
devfsrules_jail) | |||||
# XXX: This is the default value, | |||||
# Let jail(8) to use the default because | |||||
# mount(8) only accepts an integer. | |||||
# This should accept a ruleset name. | |||||
;; | |||||
*) warn "devfs_ruleset must be an integer." ;; | |||||
esac | |||||
fi | |||||
eval : \${jail_${_jv}_fdescfs_enable:=${jail_fdescfs_enable:-NO}} | |||||
if checkyesno jail_${_jv}_fdescfs_enable; then | |||||
echo " mount.fdescfs;" | |||||
fi | |||||
eval : \${jail_${_jv}_procfs_enable:=${jail_procfs_enable:-NO}} | |||||
if checkyesno jail_${_jv}_procfs_enable; then | |||||
echo " mount.procfs;" | |||||
fi | |||||
eval : \${jail_${_jv}_mount_enable:=${jail_mount_enable:-NO}} | |||||
if checkyesno jail_${_jv}_mount_enable; then | |||||
echo " allow.mount;" | |||||
fi | |||||
extract_var $_jv set_hostname_allow allow.set_hostname YN NO | |||||
extract_var $_jv sysvipc_allow allow.sysvipc YN NO | |||||
extract_var $_jv enforce_statfs enforce_statfs - 2 | |||||
extract_var $_jv osreldate osreldate | |||||
extract_var $_jv osrelease osrelease | |||||
for _p in $_parameters; do | |||||
echo " ${_p%\;};" | |||||
done | |||||
echo "}" | |||||
) >> $_conf | |||||
return 0 | |||||
} | |||||
# jail_extract_address argument iface | |||||
# The second argument is the string from one of the _ip | |||||
# or the _multi variables. In case of a comma separated list | |||||
# only one argument must be passed in at a time. | |||||
# The function alters the _type, _iface, _addr and _mask variables. | |||||
# | |||||
jail_extract_address() | |||||
{ | |||||
local _i _interface | |||||
_i=$1 | |||||
_interface=$2 | |||||
if [ -z "${_i}" ]; then | |||||
warn "jail_extract_address: called without input" | |||||
return | |||||
fi | |||||
# Check if we have an interface prefix given and split into | |||||
# iFace and rest. | |||||
case "${_i}" in | |||||
*\|*) # ifN|.. prefix there | |||||
_iface=${_i%%|*} | |||||
_r=${_i##*|} | |||||
;; | |||||
*) _iface="" | |||||
_r=${_i} | |||||
;; | |||||
esac | |||||
# In case the IP has no interface given, check if we have a global one. | |||||
_iface=${_iface:-${_interface}} | |||||
# Set address, cut off any prefix/netmask/prefixlen. | |||||
_addr=${_r} | |||||
_addr=${_addr%%[/ ]*} | |||||
# Theoretically we can return here if interface is not set, | |||||
# as we only care about the _mask if we call ifconfig. | |||||
# This is not done because we may want to santize IP addresses | |||||
# based on _type later, and optionally change the type as well. | |||||
# Extract the prefix/netmask/prefixlen part by cutting off the address. | |||||
_mask=${_r} | |||||
_mask=`expr -- "${_mask}" : "${_addr}\(.*\)"` | |||||
# Identify type {inet,inet6}. | |||||
case "${_addr}" in | |||||
*\.*\.*\.*) _type="inet" ;; | |||||
*:*) _type="inet6" ;; | |||||
*) warn "jail_extract_address: type not identified" | |||||
;; | |||||
esac | |||||
# Handle the special /netmask instead of /prefix or | |||||
# "netmask xxx" case for legacy IP. | |||||
# We do NOT support shortend class-full netmasks. | |||||
if [ "${_type}" = "inet" ]; then | |||||
case "${_mask}" in | |||||
/*\.*\.*\.*) _mask=" netmask ${_mask#/}" ;; | |||||
*) ;; | |||||
esac | |||||
# In case _mask is still not set use /32. | |||||
_mask=${_mask:-/32} | |||||
elif [ "${_type}" = "inet6" ]; then | |||||
# In case _mask is not set for IPv6, use /128. | |||||
_mask=${_mask:-/128} | |||||
fi | |||||
} | |||||
# jail_handle_ips_option input iface | |||||
# Handle a single argument imput which can be a comma separated | |||||
# list of addresses (theoretically with an option interface and | |||||
# prefix/netmask/prefixlen). | |||||
# | |||||
jail_handle_ips_option() | |||||
{ | |||||
local _x _type _i _defif | |||||
_x=$1 | |||||
_defif=$2 | |||||
if [ -z "${_x}" ]; then | |||||
# No IP given. This can happen for the primary address | |||||
# of each address family. | |||||
return | |||||
fi | |||||
# Loop, in case we find a comma separated list, we need to handle | |||||
# each argument on its own. | |||||
while [ ${#_x} -gt 0 ]; do | |||||
case "${_x}" in | |||||
*,*) # Extract the first argument and strip it off the list. | |||||
_i=`expr -- "${_x}" : '^\([^,]*\)'` | |||||
_x=`expr -- "${_x}" : "^[^,]*,\(.*\)"` | |||||
;; | |||||
*) _i=${_x} | |||||
_x="" | |||||
;; | |||||
esac | |||||
_type="" | |||||
_addr="" | |||||
_mask="" | |||||
_iface="" | |||||
jail_extract_address $_i $_defif | |||||
# make sure we got an address. | |||||
case $_addr in | |||||
"") continue ;; | |||||
*) ;; | |||||
esac | |||||
# Append address to list of addresses for the jail command. | |||||
case $_type in | |||||
inet) | |||||
echo " ip4.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";" | |||||
;; | |||||
inet6) | |||||
echo " ip6.addr += \"${_iface:+${_iface}|}${_addr}${_mask}\";" | |||||
need_dad_wait=1 | |||||
;; | |||||
esac | |||||
done | |||||
} | |||||
jail_config() | |||||
{ | |||||
local _j _jv | |||||
case $1 in | |||||
_ALL) return ;; | |||||
esac | |||||
for _j in $@; do | |||||
_j=$(echo $_j | tr /. _) | |||||
_jv=$(echo -n $_j | tr -c '[:alnum:]' _) | |||||
if parse_options $_j $_jv; then | |||||
echo "$_j: parameters are in $_conf." | |||||
fi | |||||
done | |||||
} | |||||
jail_console() | |||||
{ | |||||
local _j _jv _cmd | |||||
# One argument that is not _ALL. | |||||
case $#:$1 in | |||||
0:*|1:_ALL) err 3 "Specify a jail name." ;; | |||||
1:*) ;; | |||||
esac | |||||
_j=$(echo $1 | tr /. _) | |||||
_jv=$(echo -n $1 | tr -c '[:alnum:]' _) | |||||
shift | |||||
case $# in | |||||
0) eval _cmd=\${jail_${_jv}_consolecmd:-$jail_consolecmd} ;; | |||||
*) _cmd=$@ ;; | |||||
esac | |||||
$jail_jexec $_j $_cmd | |||||
} | |||||
jail_status() | |||||
{ | |||||
$jail_jls -N | |||||
} | |||||
jail_start() | |||||
{ | |||||
local _j _jv _jid _id _name | |||||
if [ $# = 0 ]; then | |||||
return | |||||
fi | |||||
echo -n 'Starting jails:' | |||||
case $1 in | |||||
_ALL) | |||||
command=$jail_program | |||||
rc_flags=$jail_flags | |||||
command_args="-f $jail_conf -c" | |||||
if ! checkyesno jail_parallel_start; then | |||||
command_args="$command_args -p1" | |||||
fi | |||||
_tmp=`mktemp -t jail` || exit 3 | |||||
if $command $rc_flags $command_args >> $_tmp 2>&1; then | |||||
$jail_jls jid name | while read _id _name; do | |||||
echo -n " $_name" | |||||
echo $_id > /var/run/jail_${_name}.id | |||||
done | |||||
else | |||||
cat $_tmp | |||||
fi | |||||
rm -f $_tmp | |||||
echo '.' | |||||
return | |||||
;; | |||||
esac | |||||
if checkyesno jail_parallel_start; then | |||||
# | |||||
# Start jails in parallel and then check jail id when | |||||
# jail_parallel_start is YES. | |||||
# | |||||
for _j in $@; do | |||||
_j=$(echo $_j | tr /. _) | |||||
_jv=$(echo -n $_j | tr -c '[:alnum:]' _) | |||||
parse_options $_j $_jv || continue | |||||
eval rc_flags=\${jail_${_jv}_flags:-$jail_flags} | |||||
eval command=\${jail_${_jv}_program:-$jail_program} | |||||
command_args="-i -f $_conf -c $_j" | |||||
( | |||||
_tmp=`mktemp -t jail_${_j}` || exit 3 | |||||
if $command $rc_flags $command_args \ | |||||
>> $_tmp 2>&1 </dev/null; then | |||||
echo -n " ${_hostname:-${_j}}" | |||||
_jid=$($jail_jls -j $_j jid) | |||||
echo $_jid > /var/run/jail_${_j}.id | |||||
else | |||||
echo " cannot start jail " \ | |||||
"\"${_hostname:-${_j}}\": " | |||||
cat $_tmp | |||||
fi | |||||
rm -f $_tmp | |||||
) & | |||||
done | |||||
wait | |||||
else | |||||
# | |||||
# Start jails one-by-one when jail_parallel_start is NO. | |||||
# | |||||
for _j in $@; do | |||||
_j=$(echo $_j | tr /. _) | |||||
_jv=$(echo -n $_j | tr -c '[:alnum:]' _) | |||||
parse_options $_j $_jv || continue | |||||
eval rc_flags=\${jail_${_jv}_flags:-$jail_flags} | |||||
eval command=\${jail_${_jv}_program:-$jail_program} | |||||
command_args="-i -f $_conf -c $_j" | |||||
_tmp=`mktemp -t jail` || exit 3 | |||||
if $command $rc_flags $command_args \ | |||||
>> $_tmp 2>&1 </dev/null; then | |||||
echo -n " ${_hostname:-${_j}}" | |||||
_jid=$($jail_jls -j $_j jid) | |||||
echo $_jid > /var/run/jail_${_j}.id | |||||
else | |||||
echo " cannot start jail " \ | |||||
"\"${_hostname:-${_j}}\": " | |||||
cat $_tmp | |||||
fi | |||||
rm -f $_tmp | |||||
done | |||||
fi | |||||
echo '.' | |||||
} | |||||
jail_stop() | |||||
{ | |||||
local _j _jv | |||||
if [ $# = 0 ]; then | |||||
return | |||||
fi | |||||
echo -n 'Stopping jails:' | |||||
case $1 in | |||||
_ALL) | |||||
command=$jail_program | |||||
rc_flags=$jail_flags | |||||
command_args="-f $jail_conf -r" | |||||
if checkyesno jail_reverse_stop; then | |||||
$jail_jls name | tail -r | |||||
else | |||||
$jail_jls name | |||||
fi | while read _j; do | |||||
echo -n " $_j" | |||||
_tmp=`mktemp -t jail` || exit 3 | |||||
$command $rc_flags $command_args $_j >> $_tmp 2>&1 | |||||
if $jail_jls -j $_j > /dev/null 2>&1; then | |||||
cat $_tmp | |||||
else | |||||
rm -f /var/run/jail_${_j}.id | |||||
fi | |||||
rm -f $_tmp | |||||
done | |||||
echo '.' | |||||
return | |||||
;; | |||||
esac | |||||
checkyesno jail_reverse_stop && set -- $(reverse_list $@) | |||||
for _j in $@; do | |||||
_j=$(echo $_j | tr /. _) | |||||
_jv=$(echo -n $_j | tr -c '[:alnum:]' _) | |||||
parse_options $_j $_jv || continue | |||||
if ! $jail_jls -j $_j > /dev/null 2>&1; then | |||||
continue | |||||
fi | |||||
eval command=\${jail_${_jv}_program:-$jail_program} | |||||
echo -n " ${_hostname:-${_j}}" | |||||
_tmp=`mktemp -t jail` || exit 3 | |||||
$command -q -f $_conf -r $_j >> $_tmp 2>&1 | |||||
if $jail_jls -j $_j > /dev/null 2>&1; then | |||||
cat $_tmp | |||||
else | |||||
rm -f /var/run/jail_${_j}.id | |||||
fi | |||||
rm -f $_tmp | |||||
done | |||||
echo '.' | |||||
} | |||||
jail_warn() | |||||
{ | |||||
# To relieve confusion, show a warning message. | |||||
case $_confwarn in | |||||
1) warn "Per-jail configuration via jail_* variables " \ | |||||
"is obsolete. Please consider migrating to $jail_conf." | |||||
;; | |||||
esac | |||||
} | |||||
load_rc_config $name | |||||
case $# in | |||||
1) run_rc_command $@ ${jail_list:-_ALL} ;; | |||||
*) jail_reverse_stop="no" | |||||
run_rc_command $@ ;; | |||||
esac |