diff --git a/usr/local/share/bastille/common.sh b/usr/local/share/bastille/common.sh index d7be27fb..6fbeada0 100644 --- a/usr/local/share/bastille/common.sh +++ b/usr/local/share/bastille/common.sh @@ -31,7 +31,9 @@ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # Source config file -. /usr/local/etc/bastille/bastille.conf +if [ -f /usr/local/etc/bastille/bastille.conf ]; then + . /usr/local/etc/bastille/bastille.conf +fi COLOR_RED= COLOR_GREEN= @@ -50,24 +52,30 @@ enable_color() { . /usr/local/share/bastille/colors.pre.sh } +enable_debug() { + # Enable debug mode. + warn "***DEBUG MODE***" + set -x +} + # If "NO_COLOR" environment variable is present, or we aren't speaking to a # tty, disable output colors. if [ -z "${NO_COLOR}" ] && [ -t 1 ]; then enable_color fi -# Error/Info functions -error_notify() { - echo -e "${COLOR_RED}$*${COLOR_RESET}" 1>&2 -} - +# Notify message on error, and continue to next jail error_continue() { error_notify "$@" - # Disabling this shellcheck as we only ever call it inside of a loop # shellcheck disable=SC2104 continue } +# Notify message on error, but do not exit +error_notify() { + echo -e "${COLOR_RED}$*${COLOR_RESET}" 1>&2 +} + # Notify message on error and exit error_exit() { error_notify "$@" @@ -84,7 +92,8 @@ warn() { check_target_exists() { local _TARGET="${1}" - if [ ! -d "${bastille_jailsdir}"/"${_TARGET}" ]; then + local _jaillist="$(bastille list jails)" + if ! echo "${_jaillist}" | grep -Eq "^${_TARGET}$"; then return 1 else return 0 @@ -93,7 +102,7 @@ check_target_exists() { check_target_is_running() { local _TARGET="${1}" - if [ ! "$(/usr/sbin/jls name | awk "/^${_TARGET}$/")" ]; then + if ! jls name | grep -Eq "^${_TARGET}$"; then return 1 else return 0 @@ -102,99 +111,67 @@ check_target_is_running() { check_target_is_stopped() { local _TARGET="${1}" - if [ "$(/usr/sbin/jls name | awk "/^${_TARGET}$/")" ]; then + if jls name | grep -Eq "^${_TARGET}$"; then return 1 else return 0 fi } -generate_static_mac() { - local jail_name="${1}" - local external_interface="${2}" - local external_interface_mac="$(ifconfig ${external_interface} | grep ether | awk '{print $2}' | sed 's#:##g')" - local macaddr_prefix="$(echo -n "${external_interface_mac}" | sha256 | cut -b -6 | sed 's/\([0-9a-fA-F][0-9a-fA-F]\)\([0-9a-fA-F][0-9a-fA-F]\)\([0-9a-fA-F]\)/\1:\2:\3/')" - local macaddr_suffix="$(echo -n "${jail_name}" | sha256 | cut -b -5 | sed 's/\([0-9a-fA-F][0-9a-fA-F]\)\([0-9a-fA-F][0-9a-fA-F]\)\([0-9a-fA-F]\)/\1:\2:\3/')" - if [ -z "${macaddr_prefix}" ] || [ -z "${macaddr_suffix}" ]; then - error_notify "Failed to generate MAC address." +get_jail_name() { + local _JID="${1}" + local _jailname="$(jls -j ${_JID} name 2>/dev/null)" + if [ -z "${_jailname}" ]; then + return 1 + else + echo "${_jailname}" fi - macaddr="${macaddr_prefix}:${macaddr_suffix}" - export macaddr } -generate_vnet_jail_netblock() { - local jail_name="${1}" - local use_unique_bridge="${2}" - local external_interface="${3}" - generate_static_mac "${jail_name}" "${external_interface}" - ## determine number of containers + 1 - ## iterate num and grep all jail configs - ## define uniq_epair - local _epair_if_count="$(grep -Eos 'epair[0-9]+' ${bastille_jailsdir}/*/jail.conf | sort -u | wc -l | awk '{print $1}')" - local _vnet_if_count="$(grep -Eos 'bastille[0-9]+' ${bastille_jailsdir}/*/jail.conf | sort -u | wc -l | awk '{print $1}')" - local epair_num_range=$((_epair_if_count + 1)) - local vnet_num_range=$((_vnet_if_count + 1)) - if [ -n "${use_unique_bridge}" ]; then - if [ "${_epair_if_count}" -gt 0 ]; then - for _num in $(seq 0 "${epair_num_range}"); do - if ! grep -Eosq "epair${_num}" ${bastille_jailsdir}/*/jail.conf; then - local uniq_epair_bridge="${_num}" - break - fi - done +jail_autocomplete() { + local _TARGET="${1}" + local _jaillist="$(bastille list jails)" + local _AUTOTARGET="$(echo "${_jaillist}" | grep -E "^${_TARGET}")" + if [ -n "${_AUTOTARGET}" ]; then + if [ "$(echo "${_AUTOTARGET}" | wc -l)" -eq 1 ]; then + echo "${_AUTOTARGET}" else - local uniq_epair_bridge="0" + error_continue "Multiple jails found for ${_TARGET}:\n${_AUTOTARGET}" + return 1 fi else - if [ "${_vnet_if_count}" -gt 0 ]; then - for _num in $(seq 0 "${vnet_num_range}"); do - if ! grep -Eosq "bastillle${_num}" ${bastille_jailsdir}/*/jail.conf; then - local uniq_epair="${_num}" - break - fi - done - else - local uniq_epair="bastille0" - fi - fi - if [ -n "${use_unique_bridge}" ]; then - ## generate bridge config - cat <<-EOF - vnet; - vnet.interface = e${uniq_epair_bridge}b_${jail_name}; - exec.prestart += "ifconfig epair${uniq_epair_bridge} create"; - exec.prestart += "ifconfig ${external_interface} addm epair${uniq_epair_bridge}a"; - exec.prestart += "ifconfig epair${uniq_epair_bridge}a up name e${uniq_epair_bridge}a_${jail_name}"; - exec.prestart += "ifconfig epair${uniq_epair_bridge}b up name e${uniq_epair_bridge}b_${jail_name}"; - exec.prestart += "ifconfig e${uniq_epair_bridge}a_${jail_name} ether ${macaddr}a"; - exec.prestart += "ifconfig e${uniq_epair_bridge}b_${jail_name} ether ${macaddr}b"; - exec.poststop += "ifconfig ${external_interface} deletem e${uniq_epair_bridge}a_${jail_name}"; - exec.poststop += "ifconfig e${uniq_epair_bridge}a_${jail_name} destroy"; -EOF - else - ## generate config - cat <<-EOF - vnet; - vnet.interface = e0b_${uniq_epair}; - exec.prestart += "jib addm ${uniq_epair} ${external_interface}"; - exec.prestart += "ifconfig e0a_${uniq_epair} ether ${macaddr}a"; - exec.prestart += "ifconfig e0b_${uniq_epair} ether ${macaddr}b"; - exec.prestart += "ifconfig e0a_${uniq_epair} description \"vnet host interface for Bastille jail ${jail_name}\""; - exec.poststop += "jib destroy ${uniq_epair}"; -EOF + return 2 fi } set_target() { - local _TARGET="${1}" + local _TARGET=${1} + JAILS="" + TARGET="" if [ "${_TARGET}" = ALL ] || [ "${_TARGET}" = all ]; then target_all_jails else - check_target_exists "${_TARGET}" || error_exit "Jail not found \"${_TARGET}\"" - JAILS="${_TARGET}" - TARGET="${_TARGET}" - export JAILS + for _jail in ${_TARGET}; do + if [ ! -d "${bastille_jailsdir}/${_TARGET}" ] && echo "${_jail}" | grep -Eq '^[0-9]+$'; then + if get_jail_name "${_jail}" > /dev/null; then + _jail="$(get_jail_name ${_jail})" + else + error_continue "Error: JID \"${_jail}\" not found. Is jail running?" + fi + elif ! check_target_exists "${_jail}"; then + if jail_autocomplete "${_jail}" > /dev/null; then + _jail="$(jail_autocomplete ${_jail})" + elif [ $? -eq 2 ]; then + error_continue "Jail not found \"${_jail}\"" + else + exit 1 + fi + fi + TARGET="${TARGET} ${_jail}" + JAILS="${JAILS} ${_jail}" + done export TARGET + export JAILS fi } @@ -202,13 +179,27 @@ set_target_single() { local _TARGET="${1}" if [ "${_TARGET}" = ALL ] || [ "${_TARGET}" = all ]; then error_exit "[all|ALL] not supported with this command." - else - check_target_exists "${_TARGET}" || error_exit "Jail not found \"${_TARGET}\"" - JAILS="${_TARGET}" - TARGET="${_TARGET}" - export JAILS - export TARGET + elif [ "$(echo ${_TARGET} | wc -w)" -gt 1 ]; then + error_exit "Error: Command only supports a single TARGET." + elif [ ! -d "${bastille_jailsdir}/${_TARGET}" ] && echo "${_TARGET}" | grep -Eq '^[0-9]+$'; then + if get_jail_name "${_TARGET}" > /dev/null; then + _TARGET="$(get_jail_name ${_TARGET})" + else + error_exit "Error: JID \"${_TARGET}\" not found. Is jail running?" + fi + elif ! check_target_exists "${_TARGET}"; then + if jail_autocomplete "${_TARGET}" > /dev/null; then + _TARGET="$(jail_autocomplete ${_TARGET})" + elif [ $? -eq 2 ]; then + error_exit "Jail not found \"${_TARGET}\"" + else + exit 1 + fi fi + TARGET="${_TARGET}" + JAILS="${_TARGET}" + export TARGET + export JAILS } target_all_jails() { @@ -222,6 +213,139 @@ target_all_jails() { export JAILS } +update_fstab() { + local _oldname="${1}" + local _newname="${2}" + local _fstab="${bastille_jailsdir}/${_newname}/fstab" + if [ -f "${_fstab}" ]; then + sed -i '' "s|${bastille_jailsdir}/${_oldname}/root/|${bastille_jailsdir}/${_newname}/root/|" "${_fstab}" + else + error_notify "Error: Failed to update fstab: ${_newmane}" + fi +} + +generate_static_mac() { + local jail_name="${1}" + local external_interface="${2}" + local external_interface_mac="$(ifconfig ${external_interface} | grep ether | awk '{print $2}')" + # Use FreeBSD vendor MAC prefix (58:9c:fc) for jail MAC prefix + local macaddr_prefix="58:9c:fc" + # Use hash of interface+jailname for jail MAC suffix + local macaddr_suffix="$(echo -n "${external_interface_mac}${jail_name}" | sed 's#:##g' | sha256 | cut -b -5 | sed 's/\([0-9a-fA-F][0-9a-fA-F]\)\([0-9a-fA-F][0-9a-fA-F]\)\([0-9a-fA-F]\)/\1:\2:\3/')" + if [ -z "${macaddr_prefix}" ] || [ -z "${macaddr_suffix}" ]; then + error_notify "Failed to generate MAC address." + fi + macaddr="${macaddr_prefix}:${macaddr_suffix}" + export macaddr +} + +generate_vnet_jail_netblock() { + local jail_name="${1}" + local use_unique_bridge="${2}" + local external_interface="${3}" + local static_mac="${4}" + ## determine number of interfaces + 1 + ## iterate num and grep all jail configs + ## define uniq_epair + local _epair_if_count="$( (grep -Eos 'epair[0-9]+' ${bastille_jailsdir}/*/jail.conf; ifconfig | grep -Eo '(e[0-9]+a|epair[0-9]+a)' ) | sort -u | wc -l | awk '{print $1}')" + local _bastille_if_count="$(grep -Eos 'bastille[0-9]+' ${bastille_jailsdir}/*/jail.conf | sort -u | wc -l | awk '{print $1}')" + local epair_num_range=$((_epair_if_count + 1)) + local bastille_num_range=$((_bastille_if_count + 1)) + if [ -n "${use_unique_bridge}" ]; then + if [ "${_epair_if_count}" -gt 0 ]; then + for _num in $(seq 0 "${epair_num_range}"); do + if ! grep -Eosq "epair${_num}" ${bastille_jailsdir}/*/jail.conf && ! ifconfig | grep -Eosq "(e${_num}a|epair${_num}a)"; then + if [ "$(echo -n "e${_num}a_${jail_name}" | awk '{print length}')" -lt 16 ]; then + local host_epair=e${_num}a_${jail_name} + local jail_epair=e${_num}b_${jail_name} + else + local host_epair=epair${_num}a + local jail_epair=epair${_num}b + fi + break + fi + done + else + if [ "$(echo -n "e0a_${jail_name}" | awk '{print length}')" -lt 16 ]; then + local _num=0 + local host_epair=e${_num}a_${jail_name} + local jail_epair=e${_num}b_${jail_name} + else + local _num=0 + local host_epair=epair${_num}a + local jail_epair=epair${_num}b + fi + fi + else + if [ "${_bastille_if_count}" -gt 0 ]; then + for _num in $(seq 0 "${bastille_num_range}"); do + if ! grep -Eosq "bastille${_num}" ${bastille_jailsdir}/*/jail.conf; then + local uniq_epair="bastille${_num}" + break + fi + done + else + local uniq_epair="bastille0" + fi + fi + ## If BRIDGE is enabled, generate bridge config, else generate VNET config + if [ -n "${use_unique_bridge}" ]; then + if [ -n "${static_mac}" ]; then + ## Generate bridged VNET config with static MAC address + generate_static_mac "${jail_name}" "${external_interface}" + cat <<-EOF + vnet; + vnet.interface = ${jail_epair}; + exec.prestart += "ifconfig epair${_num} create"; + exec.prestart += "ifconfig ${external_interface} addm epair${_num}a"; + exec.prestart += "ifconfig epair${_num}a up name ${host_epair}"; + exec.prestart += "ifconfig epair${_num}b up name ${jail_epair}"; + exec.prestart += "ifconfig ${host_epair} ether ${macaddr}a"; + exec.prestart += "ifconfig ${jail_epair} ether ${macaddr}b"; + exec.prestart += "ifconfig ${host_epair} description \"vnet host interface for Bastille jail ${jail_name}\""; + exec.poststop += "ifconfig ${external_interface} deletem ${host_epair}"; + exec.poststop += "ifconfig ${host_epair} destroy"; +EOF + else + ## Generate bridged VNET config without static MAC address + cat <<-EOF + vnet; + vnet.interface = ${jail_epair}; + exec.prestart += "ifconfig epair${_num} create"; + exec.prestart += "ifconfig ${external_interface} addm epair${_num}a"; + exec.prestart += "ifconfig epair${_num}a up name ${host_epair}"; + exec.prestart += "ifconfig epair${_num}b up name ${jail_epair}"; + exec.prestart += "ifconfig ${host_epair} description \"vnet host interface for Bastille jail ${jail_name}\""; + exec.poststop += "ifconfig ${external_interface} deletem ${host_epair}"; + exec.poststop += "ifconfig ${host_epair} destroy"; +EOF + fi + else + if [ -n "${static_mac}" ]; then + ## Generate VNET config with static MAC address + generate_static_mac "${jail_name}" "${external_interface}" + cat <<-EOF + vnet; + vnet.interface = e0b_${uniq_epair}; + exec.prestart += "jib addm ${uniq_epair} ${external_interface}"; + exec.prestart += "ifconfig e0a_${uniq_epair} ether ${macaddr}a"; + exec.prestart += "ifconfig e0b_${uniq_epair} ether ${macaddr}b"; + exec.prestart += "ifconfig e0a_${uniq_epair} description \"vnet host interface for Bastille jail ${jail_name}\""; + exec.poststop += "jib destroy ${uniq_epair}"; +EOF + else + ## Generate VNET config without static MAC address + cat <<-EOF + vnet; + vnet.interface = e0b_${uniq_epair}; + exec.prestart += "jib addm ${uniq_epair} ${external_interface}"; + exec.prestart += "ifconfig e0a_${uniq_epair} description \"vnet host interface for Bastille jail ${jail_name}\""; + exec.poststop += "jib destroy ${uniq_epair}"; +EOF + fi + fi +} + checkyesno() { ## copied from /etc/rc.subr -- cedwards (20231125) ## issue #368 (lowercase values should be parsed) @@ -241,5 +365,4 @@ checkyesno() { return 1 ;; esac -} - +} \ No newline at end of file