diff --git a/usr/local/bin/bastille b/usr/local/bin/bastille index 8cd0be98..b34d1be6 100755 --- a/usr/local/bin/bastille +++ b/usr/local/bin/bastille @@ -165,10 +165,10 @@ version|-v|--version) help|-h|--help) usage ;; -bootstrap|clone|console|create|cp|destroy|etcupdate|export|htop|import|jcp|list|mount|pkg|rcp|rdr|rename|restart|setup|start|top|umount|update|upgrade|verify) +bootstrap|clone|console|create|cp|destroy|etcupdate|export|htop|import|jcp|list|mount|pkg|rcp|rdr|rename|restart|setup|start|stop|top|umount|update|upgrade|verify) # Nothing "extra" to do for these commands. -- cwells ;; -config|cmd|convert|edit|limits|service|stop|sysrc|tags|template|zfs) +config|cmd|convert|edit|limits|service|sysrc|tags|template|zfs) # Parse the target and ensure it exists. -- cwells if [ $# -eq 0 ]; then # No target was given, so show the command's help. -- cwells PARAMS='help' diff --git a/usr/local/share/bastille/create.sh b/usr/local/share/bastille/create.sh index dc65d412..e0c7ef21 100644 --- a/usr/local/share/bastille/create.sh +++ b/usr/local/share/bastille/create.sh @@ -36,31 +36,24 @@ usage() { # Build an independent usage for the create command # If no option specified, will create a thin container by default - error_notify "Usage: bastille create [option(s)] name release ip [interface]" + error_notify "Usage: bastille create [option(s)] NAME RELEASE IP_ADDRESS [interface]" cat << EOF Options: - -M | --static-mac -- Generate a static MAC address for jail (VNET only). - -E | --empty -- Creates an empty container, intended for custom jail builds (thin/thick/linux or unsupported). - -L | --linux -- This option is intended for testing with Linux jails, this is considered experimental. - -T | --thick -- Creates a thick container, they consume more space as they are self contained and independent. - -V | --vnet -- Enables VNET, VNET containers are attached to a virtual bridge interface for connectivity. - -C | --clone -- Creates a clone container, they are duplicates of the base release, consume low space and preserves changing data. - -B | --bridge -- Enables VNET, VNET containers are attached to a specified, already existing external bridge. + -D | --dual Creates the jails with both IPv4 and IPv6 networking ('inherit' and 'ip_hostname' only). + -M | --static-mac Generate a static MAC address for jail (VNET only). + -E | --empty Creates an empty container, intended for custom jail builds (thin/thick/linux or unsupported). + -L | --linux This option is intended for testing with Linux jails, this is considered experimental. + -T | --thick Creates a thick container, they consume more space as they are self contained and independent. + -V | --vnet Enables VNET, VNET containers are attached to a virtual bridge interface for connectivity. + -C | --clone Creates a clone container, they are duplicates of the base release, consume low space and preserves changing data. + -B | --bridge Enables VNET, VNET containers are attached to a specified, already existing external bridge. EOF exit 1 } -running_jail() { - if [ -n "$(/usr/sbin/jls name | awk "/^${NAME}$/")" ]; then - error_exit "A running jail matches name." - elif [ -d "${bastille_jailsdir}/${NAME}" ]; then - error_exit "Jail: ${NAME} already created." - fi -} - validate_name() { local NAME_VERIFY=${NAME} local NAME_SANITY="$(echo "${NAME_VERIFY}" | tr -c -d 'a-zA-Z0-9-_')" @@ -74,54 +67,90 @@ validate_name() { } validate_ip() { - ipx_addr="ip4.addr" - ip="$1" - ip6=$(echo "${ip}" | grep -E '^(([a-fA-F0-9:]+$)|([a-fA-F0-9:]+\/[0-9]{1,3}$)|SLAAC)') - if [ -n "${ip6}" ]; then - info "Valid: (${ip6})." + _ip="${1}" + _ip6=$(echo "${_ip}" | grep -E '^(([a-fA-F0-9:]+$)|([a-fA-F0-9:]+\/[0-9]{1,3}$)|SLAAC)') + if [ -n "${_ip6}" ]; then + info "Valid: (${_ip6})." ipx_addr="ip6.addr" - IP6_MODE="new" else - if [ "${ip}" = "DHCP" ]; then - info "Valid: (${ip})." + if [ "${_ip}" = "inherit" ] || [ "${_ip}" = "ip_hostname" ]; then + info "Valid: (${_ip})." else local IFS - if echo "${ip}" | grep -Eq '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))?$'; then - TEST_IP=$(echo "${ip}" | cut -d / -f1) + if echo "${_ip}" | grep -Eq '^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))?$'; then + TEST_IP=$(echo "${_ip}" | cut -d / -f1) IFS=. set ${TEST_IP} for quad in 1 2 3 4; do if eval [ \$$quad -gt 255 ]; then - echo "Invalid: (${TEST_IP})" - exit 1 + error_continue "Invalid: (${TEST_IP})" fi done if ifconfig | grep -qwF "${TEST_IP}"; then warn "Warning: IP address already in use (${TEST_IP})." else - info "Valid: (${ip})." + ipx_addr="ip4.addr" + info "Valid: (${_ip})." fi else - error_exit "Invalid: (${ip})." + error_continue "Invalid: (${_ip})." fi fi fi - if echo "${ip}" | grep -qvE '(SLAAC|DHCP|0[.]0[.]0[.]0)'; then - if [ "${ipx_addr}" = "ip4.addr" ]; then - IP4_ADDR="${ip}" - IP4_DEFINITION="${ipx_addr} = ${ip};" + # Set interface value + if [ ! -f "${bastille_jail_conf}" ]; then + if [ -z "${bastille_network_loopback}" ] && [ -n "${bastille_network_shared}" ]; then + local bastille_jail_conf_interface=${bastille_network_shared} + fi + if [ -n "${bastille_network_loopback}" ] && [ -z "${bastille_network_shared}" ]; then + local bastille_jail_conf_interface=${bastille_network_loopback} + fi + if [ -n "${INTERFACE}" ]; then + local bastille_jail_conf_interface=${INTERFACE} + fi + fi + # Determine IP/Interface mode + if [ "${_ip}" = "inherit" ]; then + if [ -n "${DUAL_STACK}" ]; then + IP4_DEFINITION="ip4 = ${_ip};" + IP6_DEFINITION="ip6 = ${_ip};" + IP6_MODE="new" else - IP6_ADDR="${ip}" - IP6_DEFINITION="${ipx_addr} = ${ip};" + IP4_DEFINITION="ip4 = ${_ip};" + IP6_DEFINITION="" + IP6_MODE="disable" + fi + elif [ "${_ip}" = "ip_hostname" ]; then + if [ -n "${DUAL_STACK}" ]; then + IP_HOSTNAME="${_ip}" + IP4_DEFINITION="${IP_HOSTNAME};" + IP6_DEFINITION="${IP_HOSTNAME};" + IP6_MODE="new" + else + IP_HOSTNAME="${_ip}" + IP4_DEFINITION="${IP_HOSTNAME};" + IP6_DEFINITION="" + IP6_MODE="disable" + fi + elif echo "${_ip}" | grep -qvE '(SLAAC|DHCP|0[.]0[.]0[.]0)'; then + if [ "${ipx_addr}" = "ip4.addr" ]; then + IP4_ADDR="${_ip}" + IP4_DEFINITION="${ipx_addr} = ${bastille_jail_conf_interface}|${_ip};" + elif [ "${ipx_addr}" = "ip6.addr" ]; then + IP6_ADDR="${_ip}" + IP6_DEFINITION="${ipx_addr} = ${bastille_jail_conf_interface}|${_ip};" + IP6_MODE="new" fi fi } + validate_ips() { IP6_MODE="disable" IP4_DEFINITION="" IP6_DEFINITION="" IP4_ADDR="" IP6_ADDR="" + IP_HOSTNAME="" for ip in ${IP}; do validate_ip "${ip}" done @@ -190,7 +219,6 @@ ${NAME} { securelevel = 2; osrelease = ${RELEASE}; - interface = ${bastille_jail_conf_interface}; ${IP4_DEFINITION} ${IP6_DEFINITION} ip6 = ${IP6_MODE}; @@ -199,17 +227,12 @@ EOF } generate_linux_jail_conf() { - if [ "$(sysctl -n security.jail.jailed)" -eq 1 ]; then - devfs_ruleset_value=0 - else - devfs_ruleset_value=4 - fi cat << EOF > "${bastille_jail_conf}" ${NAME} { host.hostname = ${NAME}; mount.fstab = ${bastille_jail_fstab}; path = ${bastille_jail_path}; - devfs_ruleset = ${devfs_ruleset_value}; + devfs_ruleset = 4; enforce_statfs = 1; exec.start = '/bin/true'; @@ -219,8 +242,8 @@ ${NAME} { allow.mount; allow.mount.devfs; - interface = ${bastille_jail_conf_interface}; - ${ipx_addr} = ${IP}; + ${IP4_DEFINITION} + ${IP6_DEFINITION} ip6 = ${IP6_MODE}; } EOF @@ -232,7 +255,7 @@ generate_vnet_jail_conf() { else devfs_ruleset_value=13 fi - NETBLOCK=$(generate_vnet_jail_netblock "$NAME" "${VNET_JAIL_BRIDGE}" "${bastille_jail_conf_interface}" "${STATIC_MAC}") + NETBLOCK=$(generate_vnet_jail_netblock "${NAME}" "${VNET_JAIL_BRIDGE}" "${bastille_jail_conf_interface}" "${STATIC_MAC}") cat << EOF > "${bastille_jail_conf}" ${NAME} { enforce_statfs = 2; @@ -258,8 +281,7 @@ post_create_jail() { # Using relative paths here. # MAKE SURE WE'RE IN THE RIGHT PLACE. - cd "${bastille_jail_path}" || error_exit "Failed to change directory." - echo + cd "${bastille_jail_path}" || error_exit "Could not cd to ${bastille_jail_path}" if [ ! -f "${bastille_jail_conf}" ]; then if [ -z "${bastille_network_loopback}" ] && [ -n "${bastille_network_shared}" ]; then @@ -382,7 +404,7 @@ create_jail() { if [ -z "${THICK_JAIL}" ] && [ -z "${CLONE_JAIL}" ]; then LINK_LIST="bin boot lib libexec rescue sbin usr/bin usr/include usr/lib usr/lib32 usr/libdata usr/libexec usr/sbin usr/share usr/src" - info "Creating a thinjail...\n" + info "Creating a thinjail..." for _link in ${LINK_LIST}; do ln -sf /.bastille/${_link} ${_link} done @@ -416,11 +438,11 @@ create_jail() { info "Creating a clonejail...\n" ## clone the release base to the new basejail SNAP_NAME="bastille-clone-$(date +%Y-%m-%d-%H%M%S)" - # shellcheck disable=SC2140 + # shellcheck disable=SC2140 zfs snapshot "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}" - - # shellcheck disable=SC2140 - zfs clone -p "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}" \ + + # shellcheck disable=SC2140 + zfs clone -p "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}" \ "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root" # Check and apply required settings. @@ -434,20 +456,20 @@ create_jail() { ## take a temp snapshot of the base release SNAP_NAME="bastille-$(date +%Y-%m-%d-%H%M%S)" - # shellcheck disable=SC2140 + # shellcheck disable=SC2140 zfs snapshot "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}" ## replicate the release base to the new thickjail and set the default mountpoint - # shellcheck disable=SC2140 + # shellcheck disable=SC2140 zfs send -R "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}" | \ zfs receive "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root" zfs set ${ZFS_OPTIONS} mountpoint=none "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root" zfs inherit mountpoint "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root" ## cleanup temp snapshots initially - # shellcheck disable=SC2140 + # shellcheck disable=SC2140 zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases/${RELEASE}"@"${SNAP_NAME}" - # shellcheck disable=SC2140 + # shellcheck disable=SC2140 zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME}/root"@"${SNAP_NAME}" fi @@ -526,6 +548,12 @@ create_jail() { fi fi + # Exit if jail was not started, which means something is wrong. + if ! check_target_is_running "${NAME}"; then + bastille destroy "${NAME}" + error_exit "[${NAME}]: Failed to create jail..." + fi + if [ -n "${VNET_JAIL}" ]; then if [ -n "${bastille_template_vnet}" ]; then ## rename interface to generic vnet0 @@ -612,34 +640,33 @@ create_jail() { fi } -# Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; -esac - bastille_root_check -if echo "$3" | grep '@'; then +if echo "${3}" | grep '@'; then # shellcheck disable=SC2034 BASTILLE_JAIL_IP=$(echo "$3" | awk -F@ '{print $2}') # shellcheck disable=SC2034 BASTILLE_JAIL_INTERFACES=$( echo "$3" | awk -F@ '{print $1}') fi -## reset this options +# Handle options. EMPTY_JAIL="" THICK_JAIL="" CLONE_JAIL="" VNET_JAIL="" LINUX_JAIL="" STATIC_MAC="" +DUAL_STACK="" VALIDATE_RELEASE="1" - -# Handle and parse options while [ $# -gt 0 ]; do case "${1}" in + -h|--help|help) + usage + ;; + -D|--dual) + DUAL_STACK="1" + shift + ;; -M|--static-mac) STATIC_MAC="1" shift @@ -669,57 +696,26 @@ while [ $# -gt 0 ]; do CLONE_JAIL="1" shift ;; - -CV|-VC|--clone-vnet) - CLONE_JAIL="1" - VNET_JAIL="1" - shift - ;; - -CB|-BC|--clone-bridge) - CLONE_JAIL="1" - VNET_JAIL="1" - VNET_JAIL_BRIDGE="1" - shift - ;; - -TV|-VT|--thick-vnet) - THICK_JAIL="1" - VNET_JAIL="1" - shift - ;; - -TB|-BT|--thick-bridge) - THICK_JAIL="1" - VNET_JAIL="1" - VNET_JAIL_BRIDGE="1" - shift - ;; - -EB|-BE|--empty-bridge) - EMPTY_JAIL="1" - VNET_JAIL="1" - VNET_JAIL_BRIDGE="1" - shift - ;; - -EV|-VE|--empty-vnet) - EMPTY_JAIL="1" - VNET_JAIL="1" - shift - ;; - -LV|-VL|--linux-vnet) - LINUX_JAIL="1" - VNET_JAIL="1" - shift - ;; - -LB|-BL|--linux-bridge) - LINUX_JAIL="1" - VNET_JAIL="1" - VNET_JAIL_BRIDGE="1" - shift - ;; --no-validate|no-validate) VALIDATE_RELEASE="" shift ;; - --*|-*) - error_notify "Unknown Option." - usage + -*) + for _opt in $(echo ${1} | sed 's/-//g' | fold -w1); do + case ${_opt} in + B) VNET_JAIL=1 VNET_JAIL_BRIDGE=1 ;; + C) CLONE_JAIL=1 ;; + D) DUAL_STACK=1 ;; + E) EMPTY_JAIL=1 ;; + L) LINUX_JAIL=1 ;; + M) STATIC_MAC=1 ;; + T) THICK_JAIL=1 ;; + V) VNET_JAIL=1 ;; + x) enable_debug ;; + *) error_exit "Unknown Option: \"${1}\"" ;; + esac + done + shift ;; *) break @@ -727,7 +723,7 @@ while [ $# -gt 0 ]; do esac done -## validate for combined options +# Validate options if [ -n "${EMPTY_JAIL}" ]; then if [ -n "${CLONE_JAIL}" ] || [ -n "${THICK_JAIL}" ] || [ -n "${VNET_JAIL}" ] || [ -n "${LINUX_JAIL}" ]; then error_exit "Error: Empty jail option can't be used with other options." @@ -908,11 +904,6 @@ else info "Creating empty jail: ${NAME}." fi -## check if a running jail matches name or already exist -if [ -n "${NAME}" ]; then - running_jail -fi - # May not exist on deployments created before Bastille 0.7.20200714, so creating it. -- cwells if [ ! -e "${bastille_templatesdir}/default" ]; then ln -s "${bastille_sharedir}/templates/default" "${bastille_templatesdir}/default" @@ -942,4 +933,8 @@ if [ -z ${bastille_template_vnet+x} ]; then bastille_template_vnet='default/vnet' fi +if check_target_exists "${NAME}"; then + error_exit "Error: Existing jail found: ${NAME}" +fi create_jail "${NAME}" "${RELEASE}" "${IP}" "${INTERFACE}" + diff --git a/usr/local/share/bastille/start.sh b/usr/local/share/bastille/start.sh index 375d49c2..8a0960d1 100644 --- a/usr/local/share/bastille/start.sh +++ b/usr/local/share/bastille/start.sh @@ -34,80 +34,128 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille start TARGET" + error_notify "Usage: bastille start [option(s)] TARGET" + cat << EOF + Options: + + -v | --verbose Print every action on jail start. + -x | --debug Enable debug mode. + +EOF + exit 1 } -# Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; -esac +# Handle options. +OPTION="" +while [ "$#" -gt 0 ]; do + case "${1}" in + -h|--help|help) + usage + ;; + -v|--verbose) + OPTION="-v" + shift + ;; + -x|--debug) + enable_debug + shift + ;; + -*) + for _opt in $(echo ${1} | sed 's/-//g' | fold -w1); do + case ${_opt} in + v) OPTION="-v" ;; + x) enable_debug ;; + *) error_exit "Unknown Option: \"${1}\"" ;; + esac + done + shift + ;; + *) + break + ;; + esac +done -if [ $# -gt 1 ] || [ $# -lt 1 ]; then +if [ "$#" -ne 1 ]; then usage fi -bastille_root_check - TARGET="${1}" -shift -if [ "${TARGET}" = 'ALL' ]; then - JAILS=$(bastille list jails) -fi -if [ "${TARGET}" != 'ALL' ]; then - JAILS=$(bastille list jails | awk "/^${TARGET}$/") - ## check if exist - if [ ! -d "${bastille_jailsdir}/${TARGET}" ]; then - error_exit "[${TARGET}]: Not found." - fi -fi +bastille_root_check +set_target "${TARGET}" for _jail in ${JAILS}; do - ## test if running - if [ "$(/usr/sbin/jls name | awk "/^${_jail}$/")" ]; then - error_notify "[${_jail}]: Already started." - ## test if not running - elif [ ! "$(/usr/sbin/jls name | awk "/^${_jail}$/")" ]; then - # Verify that the configured interface exists. -- cwells - if [ "$(bastille config $_jail get vnet)" != 'enabled' ]; then - _interface=$(bastille config $_jail get interface) - if ! ifconfig | grep "^${_interface}:" >/dev/null; then - error_notify "Error: ${_interface} interface does not exist." - continue - fi + info "[${_jail}]:" + check_target_is_stopped "${_jail}" || error_continue "Jail is already running." + + # Validate interfaces and add IPs to firewall table + if [ "$(bastille config ${_jail} get vnet)" != 'enabled' ]; then + _ip4_interfaces="$(bastille config ${_jail} get ip4.addr | sed 's/,/ /g')" + _ip6_interfaces="$(bastille config ${_jail} get ip6.addr | sed 's/,/ /g')" + # IP4 + if [ "${_ip4_interfaces}" != "not set" ]; then + for _interface in ${_ip4_interfaces}; do + if echo "${_interface}" | grep -q "|"; then + _if="$(echo ${_interface} 2>/dev/null | awk -F"|" '{print $1}')" + _ip="$(echo ${_interface} 2>/dev/null | awk -F"|" '{print $2}' | sed -E 's#/[0-9]+$##g')" + else + _if="$(bastille config ${_jail} get interface)" + _ip="$(echo ${_interface} | sed -E 's#/[0-9]+$##g')" + fi + if ifconfig | grep "^${_if}:" >/dev/null; then + if ifconfig | grep -qwF "${_ip}"; then + warn "Warning: IP address (${_ip}) already in use, continuing..." + fi + ## add ip to firewall table if it is not reachable through local interface (assumes NAT/rdr is needed) + if route -n get ${_ip} | grep "gateway" >/dev/null; then + pfctl -q -t "${bastille_network_pf_table}" -T add "${_ip}" + fi + else + error_continue "Error: ${_if} interface does not exist." + fi + done fi - - ## warn if matching configured (but not online) ip4.addr, ignore if there's no ip4.addr entry - _ip4=$(bastille config "${_jail}" get ip4.addr) - if [ "${_ip4}" != "not set" ]; then - if ifconfig | grep -wF "${_ip4}" >/dev/null; then - error_notify "Error: IP address (${_ip4}) already in use." - continue - fi - ## add ip4.addr to firewall table - pfctl -q -t "${bastille_network_pf_table}" -T add "${_ip4}" - fi - - ## start the container - info "[${_jail}]:" - jail -f "${bastille_jailsdir}/${_jail}/jail.conf" -c "${_jail}" - - ## add rctl limits - if [ -s "${bastille_jailsdir}/${_jail}/rctl.conf" ]; then - while read _limits; do - rctl -a "${_limits}" - done < "${bastille_jailsdir}/${_jail}/rctl.conf" - fi - - ## add rdr rules - if [ -s "${bastille_jailsdir}/${_jail}/rdr.conf" ]; then - while read _rules; do - bastille rdr "${_jail}" ${_rules} - done < "${bastille_jailsdir}/${_jail}/rdr.conf" + # IP6 + if [ "${_ip6_interfaces}" != "not set" ]; then + for _interface in ${_ip6_interfaces}; do + if echo "${_interface}" | grep -q "|"; then + _if="$(echo ${_interface} | awk -F"|" '{print $1}')" + _ip="$(echo ${_interface} | awk -F"|" '{print $2}' | sed -E 's#/[0-9]+$##g')" + else + _if="$(bastille config ${_jail} get interface)" + _ip="$(echo ${_interface} | sed -E 's#/[0-9]+$##g')" + fi + if ifconfig | grep "^${_if}:" >/dev/null; then + if ifconfig | grep -qwF "${_ip}"; then + warn "Warning: IP address (${_ip}) already in use, continuing..." + fi + ## add ip to firewall table if it is not reachable through local interface (assumes NAT/rdr is needed) + if route -n get ${_ip} | grep "gateway" >/dev/null; then + pfctl -q -t "${bastille_network_pf_table}" -T add "${_ip}" + fi + else + error_continue "Error: ${_if} interface does not exist." + fi + done fi fi - echo + + # Start jail + jail ${OPTION} -f "${bastille_jailsdir}/${_jail}/jail.conf" -c "${_jail}" + + # Add rctl limits + if [ -s "${bastille_jailsdir}/${_jail}/rctl.conf" ]; then + while read _limits; do + rctl -a "${_limits}" + done < "${bastille_jailsdir}/${_jail}/rctl.conf" + fi + + # Add rdr rules + if [ -s "${bastille_jailsdir}/${_jail}/rdr.conf" ]; then + while read _rules; do + bastille rdr ${_jail} ${_rules} + done < "${bastille_jailsdir}/${_jail}/rdr.conf" + fi done diff --git a/usr/local/share/bastille/stop.sh b/usr/local/share/bastille/stop.sh index efec51e1..faafe4cc 100644 --- a/usr/local/share/bastille/stop.sh +++ b/usr/local/share/bastille/stop.sh @@ -34,52 +34,104 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille stop TARGET" + error_notify "Usage: bastille stop [option(s)] TARGET" + cat << EOF + Options: + + -v | --verbose Print every action on jail stop. + -x | --debug Enable debug mode. + +EOF + exit 1 } -# Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; -esac +# Handle options. +OPTION="" +while [ "$#" -gt 0 ]; do + case "${1}" in + -h|--help|help) + usage + ;; + -v|--verbose) + OPTION="-v" + shift + ;; + -x|--debug) + enable_debug + shift + ;; + -*) + for _opt in $(echo ${1} | sed 's/-//g' | fold -w1); do + case ${_opt} in + v) OPTION="-v" ;; + x) enable_debug ;; + *) error_exit "Unknown Option: \"${1}\"" ;; + esac + done + shift + ;; + *) + break + ;; + esac +done -if [ $# -ne 0 ]; then +if [ "$#" -ne 1 ]; then usage fi +TARGET="${1}" + bastille_root_check +set_target "${TARGET}" for _jail in ${JAILS}; do - ## test if running - if [ "$(/usr/sbin/jls name | awk "/^${_jail}$/")" ]; then - ## Capture ip4.addr address while still running - _ip4="$(bastille config ${_jail} get ip4.addr)" - # Check if pfctl is present - if [ "${_ip4}" != "not set" ]; then - if [ "$(bastille rdr ${_jail} list)" ]; then - bastille rdr ${_jail} clear - fi - fi + info "[${_jail}]:" + check_target_is_running "${_jail}" || error_continue "Jail is already stopped." - ## remove rctl limits - if [ -s "${bastille_jailsdir}/${_jail}/rctl.conf" ]; then - while read _limits; do - rctl -r "${_limits}" - done < "${bastille_jailsdir}/${_jail}/rctl.conf" - fi - - ## stop container - info "[${_jail}]:" - jail -f "${bastille_jailsdir}/${_jail}/jail.conf" -r "${_jail}" - - ## remove (captured above) ip4.addr from firewall table - if [ -n "${bastille_network_loopback}" ] && [ "${_ip4}" != "not set" ]; then - if grep -qw "interface.*=.*${bastille_network_loopback}" "${bastille_jailsdir}/${_jail}/jail.conf"; then - pfctl -q -t "${bastille_network_pf_table}" -T delete "${_ip4}" + # Remove RDR rules + if [ "$(bastille config ${_jail} get vnet)" != "enabled" ]; then + _ip4="$(bastille config ${_jail} get ip4.addr | sed 's/,/ /g')" + _ip6="$(bastille config ${_jail} get ip6.addr | sed 's/,/ /g')" + if [ "${_ip4}" != "not set" ] || [ "${_ip6}" != "not set" ]; then + if which -s pfctl; then + if [ "$(bastille rdr ${_jail} list)" ]; then + bastille rdr "${_jail}" clear + fi fi fi fi - echo + + # Remove rctl limits + if [ -s "${bastille_jailsdir}/${_jail}/rctl.conf" ]; then + while read _limits; do + rctl -r "${_limits}" + done < "${bastille_jailsdir}/${_jail}/rctl.conf" + fi + + # Stop jail + jail ${OPTION} -f "${bastille_jailsdir}/${_jail}/jail.conf" -r "${_jail}" + + # Remove (captured above) IPs from firewall table + if [ "${_ip4}" != "not set" ]; then + for _ip in ${_ip4}; do + if echo "${_ip}" | grep -q "|"; then + _ip="$(echo ${_ip} | awk -F"|" '{print $2}' | sed -E 's#/[0-9]+$##g')" + else + _ip="$(echo ${_ip} | sed -E 's#/[0-9]+$##g')" + fi + pfctl -q -t "${bastille_network_pf_table}" -T delete "${_ip}" + done + fi + if [ "${_ip6}" != "not set" ]; then + for _ip in ${_ip6}; do + if echo "${_ip}" | grep -q "|"; then + _ip="$(echo ${_ip} | awk -F"|" '{print $2}' | sed -E 's#/[0-9]+$##g')" + else + _ip="$(echo ${_ip} | sed -E 's#/[0-9]+$##g')" + fi + pfctl -q -t "${bastille_network_pf_table}" -T delete "${_ip}" + done + fi done diff --git a/usr/local/share/bastille/template.sh b/usr/local/share/bastille/template.sh index 9e4f42b2..030b79c0 100644 --- a/usr/local/share/bastille/template.sh +++ b/usr/local/share/bastille/template.sh @@ -229,17 +229,34 @@ for _jail in ${JAILS}; do info "[${_jail}]:" info "Applying template: ${TEMPLATE}..." - ## jail-specific variables. + ## get jail ip4 and ip6 values bastille_jail_path=$(/usr/sbin/jls -j "${_jail}" path) - if [ "$(bastille config $TARGET get vnet)" != 'enabled' ]; then - _jail_ip=$(/usr/sbin/jls -j "${_jail}" ip4.addr 2>/dev/null) - _jail_ip6=$(/usr/sbin/jls -j "${_jail}" ip6.addr 2>/dev/null) - if [ -z "${_jail_ip}" ] || [ "${_jail_ip}" = "-" ]; then - error_notify "Jail IP not found: ${_jail}" - _jail_ip='' # In case it was -. -- cwells - fi + if [ "$(bastille config ${_jail} get vnet)" != 'enabled' ]; then + _jail_ip4="$(bastille config ${_jail} get ip4.addr | sed 's/,/ /g' | awk '{print $1}')" + _jail_ip6="$(bastille config ${_jail} get ip6.addr | sed 's/,/ /g' | awk '{print $1}')" fi - + ## remove value if ip4 was not set or disabled, otherwise get value + if [ "${_jail_ip4}" = "not set" ] || [ "${_jail_ip4}" = "disabled" ]; then + _jail_ip4='' # In case it was -. -- cwells + elif echo "${_jail_ip4}" | grep -q "|"; then + _jail_ip4="$(echo ${_jail_ip4} | awk -F"|" '{print $2}' | sed -E 's#/[0-9]+$##g')" + else + _jail_ip4="$(echo ${_jail_ip4} | sed -E 's#/[0-9]+$##g')" + fi + ## remove value if ip6 was not set or disabled, otherwise get value + if [ "${_jail_ip6}" = "not set" ] || [ "${_jail_ip6}" = "disabled" ]; then + _jail_ip6='' # In case it was -. -- cwells + elif echo "${_jail_ip6}" | grep -q "|"; then + _jail_ip6="$(echo ${_jail_ip6} | awk -F"|" '{print $2}' | sed -E 's#/[0-9]+$##g')" + else + _jail_ip6="$(echo ${_jail_ip6} | sed -E 's#/[0-9]+$##g')" + fi + # print error when both ip4 and ip6 are not set + if { [ "${_jail_ip4}" = "not set" ] || [ "${_jail_ip4}" = "disabled" ]; } && \ + { [ "${_jail_ip6}" = "not set" ] || [ "${_jail_ip6}" = "disabled" ]; } then + error_notify "Jail IP not found: ${_jail}" + fi + ## TARGET if [ -s "${bastille_template}/TARGET" ]; then if grep -qw "${_jail}" "${bastille_template}/TARGET"; then @@ -256,7 +273,7 @@ for _jail in ${JAILS}; do # Build a list of sed commands like this: -e 's/${username}/root/g' -e 's/${domain}/example.com/g' # Values provided by default (without being defined by the user) are listed here. -- cwells - ARG_REPLACEMENTS="-e 's/\${JAIL_IP}/${_jail_ip}/g' -e 's/\${JAIL_IP6}/${_jail_ip6}/g' -e 's/\${JAIL_NAME}/${_jail}/g'" + ARG_REPLACEMENTS="-e 's/\${jail_ip4}/${_jail_ip4}/g' -e 's/\${jail_ip6}/${_jail_ip6}/g' -e 's/\${JAIL_NAME}/${_jail}/g'" # This is parsed outside the HOOKS loop so an ARG file can be used with a Bastillefile. -- cwells if [ -s "${bastille_template}/ARG" ]; then while read _line; do