diff --git a/docs/chapters/subcommands/mount.rst b/docs/chapters/subcommands/mount.rst index f7fb0ee3..9add58f0 100644 --- a/docs/chapters/subcommands/mount.rst +++ b/docs/chapters/subcommands/mount.rst @@ -6,11 +6,40 @@ To mount storage within the container use `bastille mount`. .. code-block:: shell - ishmael ~ # bastille mount azkaban /storage/foo /media/foo nullfs ro 0 0 + ishmael ~ # bastille mount azkaban /storage/foo media/foo nullfs ro 0 0 [azkaban]: + Added: /media/foo /usr/local/bastille/jails/azkaban/root/media/foo nullfs ro 0 0 + ishmael ~ # bastille mount azkaban /storage/bar /media/bar nullfs ro 0 0 + [azkaban]: + Added: /media/bar /usr/local/bastille/jails/azkaban/root/media/bar nullfs ro 0 0 + +Notice the JAIL_PATH format can be /media/foo or simply media/bar. The leading slash / is optional. The HOST_PATH howerver, must be the full path including the leading slash /. + +It is also possible to mount individual files into a jail as seen below. +Bastille will not mount if a file is already present at the specified mount point. +If you do not specify a file name, bastille will mount the file underneath the specified directory as seen in the second example below. + +.. code-block:: shell + + ishmael ~ # bastille mount azkaban /etc/rc.conf /mnt/etc/rc.conf nullfs ro 0 0 + [azkaban]: + Added: /etc/rc.conf /usr/local/bastille/jails/azkaban/root/mnt/etc/rc.conf nullfs ro 0 0 + ishmael ~ # bastille mount azkaban /etc/rc.conf /media/bar nullfs ro 0 0 + [azkaban]: + Added: /etc/rc.conf usr/local/bastille/jails/azkaban/root/media/bar/rc.conf nullfs ro 0 0 + +It is also possible (but not recommended) to have spaces in the directories that are mounted. +It is necessary to escape each space with a backslash \ and enclose the mount point in quotes "" as seen below. +It is possible to do the same for the jail path, but again, not recommemded. + +.. code-block:: shell + + ishmael ~ # bastille mount azkaban "/storage/my\ directory\ with\ spaces" /media/foo nullfs ro 0 0 + [azkaban]: + Added: /storage/my\040directory\040with\040spaces /usr/local/bastille/jails/azkaban/root/media/foo nullfs ro 0 0 Syntax follows standard `/etc/fstab` format: .. code-block:: shell - Usage: bastille mount TARGET host_path container_path [filesystem_type options dump pass_number] + Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number] diff --git a/docs/chapters/subcommands/umount.rst b/docs/chapters/subcommands/umount.rst index f4aaeb49..cdcdabdb 100644 --- a/docs/chapters/subcommands/umount.rst +++ b/docs/chapters/subcommands/umount.rst @@ -8,9 +8,21 @@ To unmount storage from a container use `bastille umount`. ishmael ~ # bastille umount azkaban /media/foo [azkaban]: + Unmounted: /usr/local/bastille/jails/jail4/root/media/foo + ishmael ~ # bastille umount azkaban /mnt/etc/rc.conf + [azkaban]: + Unmounted: /usr/local/bastille/jails/jail4/root/mnt/etc/rc.conf -Syntax requires only the container path to unmount: +Syntax requires only the jail path to unmount. .. code-block:: shell - Usage: bastille umount TARGET container_path + Usage: bastille umount TARGET JAIL_PATH + +If the directory you are unmounting has spaces, make sure to escape them with a backslash \, and enclode the mount point in quotes "". + +.. code-block:: shell + + ishmael ~ # bastille umount azkaban "/media/foo\ with\ spaces" + [azkaban]: + Unmounted: /usr/local/bastille/jails/jail4/root/media/foo with spaces diff --git a/usr/local/bin/bastille b/usr/local/bin/bastille index 49d27950..5c78318a 100755 --- a/usr/local/bin/bastille +++ b/usr/local/bin/bastille @@ -32,10 +32,20 @@ PATH=${PATH}:/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin . /usr/local/share/bastille/common.sh -## check for config existance +## check for config existence bastille_conf_check() { if [ ! -r "/usr/local/etc/bastille/bastille.conf" ]; then - error_exit "Missing Configuration" + warn "Configuration file not found. Do yu want to create it with default values? [y/N]" + read answer + case "${answer}" in + [Nn][Oo]|[Nn]|"") + error_exit "No configuration file has been generated. Exiting." + ;; + [Yy][Ee][Ss]|[Yy]) + cp /usr/local/etc/bastille/bastille.conf.sample /usr/local/etc/bastille/bastille.conf + info "Configuration file has been generated. Continuing with default values" + ;; + esac fi } @@ -147,10 +157,10 @@ version|-v|--version) help|-h|--help) usage ;; -bootstrap|create|destroy|export|htop|import|list|rdr|restart|setup|start|top|update|upgrade|verify) +bootstrap|create|destroy|export|htop|import|list|mount|rdr|restart|setup|start|top|umount|update|upgrade|verify) # Nothing "extra" to do for these commands. -- cwells ;; -clone|config|cmd|console|convert|cp|edit|limits|mount|pkg|rcp|rename|service|stop|sysrc|tags|template|umount|zfs) +clone|config|cmd|console|convert|cp|edit|limits|pkg|rcp|rename|service|stop|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/common.sh b/usr/local/share/bastille/common.sh index eb0bd9bd..2d6038f8 100644 --- a/usr/local/share/bastille/common.sh +++ b/usr/local/share/bastille/common.sh @@ -59,6 +59,13 @@ error_notify() { echo -e "${COLOR_RED}$*${COLOR_RESET}" 1>&2 } +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 and exit error_exit() { error_notify "$@" @@ -73,11 +80,9 @@ warn() { echo -e "${COLOR_YELLOW}$*${COLOR_RESET}" } -# Main functions check_target_exists() { local _TARGET="${1}" if [ ! -d "${bastille_jailsdir}"/"${_TARGET}" ]; then - error_notify "Jail not found \"${_TARGET}\"" return 1 else return 0 @@ -104,27 +109,6 @@ check_target_is_stopped() { fi } -checkyesno() { - ## copied from /etc/rc.subr -- cedwards (20231125) - ## issue #368 (lowercase values should be parsed) - ## now used for all bastille_zfs_enable=YES|NO tests - ## example: if checkyesno bastille_zfs_enable; then ... - ## returns 0 for enabled; returns 1 for disabled - eval _value=\$${1} - case $_value in - [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) - return 0 - ;; - [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) - return 1 - ;; - *) - warn "\$${1} is not set properly - see rc.conf(5)." - return 1 - ;; - esac -} - generate_static_mac() { local jail_name="${1}" local external_interface="${2}" @@ -221,5 +205,25 @@ target_all_jails() { fi done export JAILS + +checkyesno() { + ## copied from /etc/rc.subr -- cedwards (20231125) + ## issue #368 (lowercase values should be parsed) + ## now used for all bastille_zfs_enable=YES|NO tests + ## example: if checkyesno bastille_zfs_enable; then ... + ## returns 0 for enabled; returns 1 for disabled + eval _value=\$${1} + case $_value in + [Yy][Ee][Ss]|[Tt][Rr][Uu][Ee]|[Oo][Nn]|1) + return 0 + ;; + [Nn][Oo]|[Ff][Aa][Ll][Ss][Ee]|[Oo][Ff][Ff]|0) + return 1 + ;; + *) + warn "\$${1} is not set properly - see rc.conf(5)." + return 1 + ;; + esac } diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index bb0e6615..95e84071 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -32,96 +32,127 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille mount TARGET host_path container_path [filesystem_type options dump pass_number]" + error_exit "Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number]" } # Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; +case "${1}" in + help|-h|--help) + usage + ;; esac -if [ $# -lt 2 ]; then +if [ "$#" -lt 3 ] || [ "$#" -gt 6 ]; then usage -elif [ $# -eq 2 ]; then - _fstab="$@ nullfs ro 0 0" +fi + +TARGET="${1}" +shift + +if [ "$#" -eq 2 ]; then + _fstab="$(echo "$* nullfs ro 0 0" | sed 's#\\ #\\040#g')" else - _fstab="$@" + _fstab="$(echo "$*" | sed 's#\\ #\\040#g')" fi bastille_root_check +set_target "${TARGET}" -## assign needed variables -_hostpath=$(echo "${_fstab}" | awk '{print $1}') -_jailpath=$(echo "${_fstab}" | awk '{print $2}') +# Assign variables +_hostpath_fstab=$(echo "${_fstab}" | awk '{print $1}') +_hostpath="$(echo "${_hostpath_fstab}" 2>/dev/null | sed 's#\\040# #g')" +_jailpath_fstab=$(echo "${_fstab}" | awk '{print $2}') +_jailpath="$(echo "${_jailpath_fstab}" 2>/dev/null | sed 's#\\040# #g')" _type=$(echo "${_fstab}" | awk '{print $3}') _perms=$(echo "${_fstab}" | awk '{print $4}') _checks=$(echo "${_fstab}" | awk '{print $5" "$6}') -## if any variables are empty, bail out +# Exit if any variables are empty if [ -z "${_hostpath}" ] || [ -z "${_jailpath}" ] || [ -z "${_type}" ] || [ -z "${_perms}" ] || [ -z "${_checks}" ]; then error_notify "FSTAB format not recognized." - warn "Format: /host/path jail/path nullfs ro 0 0" + warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" - exit 1 + usage fi -# if host path doesn't exist, type is not "nullfs" or are using advanced mount type "tmpfs,linprocfs,linsysfs, fdescfs, -# procfs" +# Exit if host path doesn't exist, type is not "nullfs", or mount is an advanced mount type "tmpfs,linprocfs,linsysfs,fdescfs,procfs" if { [ "${_hostpath}" = "tmpfs" ] && [ "$_type" = "tmpfs" ]; } || \ { [ "${_hostpath}" = "linprocfs" ] && [ "${_type}" = "linprocfs" ]; } || \ { [ "${_hostpath}" = "linsysfs" ] && [ "${_type}" = "linsysfs" ]; } || \ { [ "${_hostpath}" = "proc" ] && [ "${_type}" = "procfs" ]; } || \ { [ "${_hostpath}" = "fdesc" ] && [ "${_type}" = "fdescfs" ]; } then warn "Detected advanced mount type ${_hostpath}" -elif [ ! -d "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then - error_notify "Detected invalid host path or incorrect mount type in FSTAB." - warn "Format: /host/path jail/path nullfs ro 0 0" +elif [ ! -e "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then + error_notify "Invalid host path or incorrect mount type in FSTAB." + warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" - exit 1 + usage fi -## if mount permissions are not "ro" or "rw" +# Mount permissions need to be "ro" or "rw" if [ "${_perms}" != "ro" ] && [ "${_perms}" != "rw" ]; then error_notify "Detected invalid mount permissions in FSTAB." - warn "Format: /host/path jail/path nullfs ro 0 0" + warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" - exit 1 + usage fi -## if check & pass are not "0 0 - 1 1"; bail out +# Dump and pass need to be "0 0 - 1 1" if [ "${_checks}" != "0 0" ] && [ "${_checks}" != "1 0" ] && [ "${_checks}" != "0 1" ] && [ "${_checks}" != "1 1" ]; then error_notify "Detected invalid fstab options in FSTAB." - warn "Format: /host/path jail/path nullfs ro 0 0" + warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" - exit 1 + usage fi for _jail in ${JAILS}; do + info "[${_jail}]:" - ## aggregate variables into FSTAB entry - _fullpath="${bastille_jailsdir}/${_jail}/root/${_jailpath}" - _fstab_entry="${_hostpath} ${_fullpath} ${_type} ${_perms} ${_checks}" + _fullpath_fstab="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath_fstab}" 2>/dev/null | sed 's#//#/#' )" + _fullpath="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath}" 2>/dev/null | sed 's#//#/#' )" + _fstab_entry="${_hostpath_fstab} ${_fullpath_fstab} ${_type} ${_perms} ${_checks}" - ## Create mount point if it does not exist. -- cwells - if [ ! -d "${_fullpath}" ]; then - if ! mkdir -p "${_fullpath}"; then - error_exit "Failed to create mount point inside jail." - fi - fi - - ## if entry doesn't exist, add; else show existing entry - if ! egrep -q "[[:blank:]]${_fullpath}[[:blank:]]" "${bastille_jailsdir}/${_jail}/fstab" 2> /dev/null; then - if ! echo "${_fstab_entry}" >> "${bastille_jailsdir}/${_jail}/fstab"; then - error_exit "Failed to create fstab entry: ${_fstab_entry}" - fi - echo "Added: ${_fstab_entry}" - else + # Check if mount point has already been added + _existing_mount="$(echo ${_fullpath_fstab} 2>/dev/null | sed 's#\\#\\\\#g')" + if grep -Eq "[[:blank:]]${_existing_mount}.*[[:blank:]]" "${bastille_jailsdir}/${_jail}/fstab"; then warn "Mountpoint already present in ${bastille_jailsdir}/${_jail}/fstab" - egrep "[[:blank:]]${_fullpath}[[:blank:]]" "${bastille_jailsdir}/${_jail}/fstab" + grep -E "[[:blank:]]${_existing_mount}" "${bastille_jailsdir}/${_jail}/fstab" + continue fi - mount -F "${bastille_jailsdir}/${_jail}/fstab" -a - echo + + + # Create mount point if it does not exist + if [ -d "${_hostpath}" ] && [ ! -d "${_fullpath}" ]; then + mkdir -p "${_fullpath}" || error_continue "Failed to create mount point." + elif [ -f "${_hostpath}" ] ; then + _filename="$( basename ${_hostpath} )" + if echo "${_fullpath}" 2>/dev/null | grep -qow "${_filename}"; then + mkdir -p "$( dirname "${_fullpath}" )" || error_continue "Failed to create mount point." + if [ ! -f "${_fullpath}" ]; then + touch "${_fullpath}" || error_continue "Failed to create mount point." + else + error_notify "Failed. File exists at mount point." + warn "${_fullpath}" + continue + fi + else + _fullpath_fstab="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath_fstab}/${_filename}" 2>/dev/null | sed 's#//#/#' )" + _fullpath="$( echo "${bastille_jailsdir}/${_jail}/root/${_jailpath}/${_filename}" 2>/dev/null | sed 's#//#/#' )" + _fstab_entry="${_hostpath_fstab} ${_fullpath_fstab} ${_type} ${_perms} ${_checks}" + mkdir -p "$( dirname "${_fullpath}" )" || error_continue "Failed to create mount point." + if [ ! -f "${_fullpath}" ]; then + touch "${_fullpath}" || error_continue "Failed to create mount point." + else + error_notify "Failed. File exists at mount point." + warn "${_fullpath}" + continue + fi + fi + fi + + # Add entry to fstab and mount + echo "${_fstab_entry}" >> "${bastille_jailsdir}/${_jail}/fstab" || error_continue "Failed to create fstab entry: ${_fstab_entry}" + mount -F "${bastille_jailsdir}/${_jail}/fstab" -a || error_continue "Failed to mount volume: ${_fullpath}" + echo "Added: ${_fstab_entry}" done diff --git a/usr/local/share/bastille/setup.sh b/usr/local/share/bastille/setup.sh index f6153e5b..b069ea32 100644 --- a/usr/local/share/bastille/setup.sh +++ b/usr/local/share/bastille/setup.sh @@ -30,13 +30,6 @@ bastille_config="/usr/local/etc/bastille/bastille.conf" . /usr/local/share/bastille/common.sh - -# TODO: This not going to take effect since Bastille checks the file -# before running this subcommand. We will need to check an strategy. -if [ ! -f "${bastille_config}" ]; then - cp /usr/local/etc/bastille/bastille.conf.sample ${bastille_config} -fi - # shellcheck source=/usr/local/etc/bastille/bastille.conf . ${bastille_config} diff --git a/usr/local/share/bastille/umount.sh b/usr/local/share/bastille/umount.sh index dfd57664..ebbb52c5 100644 --- a/usr/local/share/bastille/umount.sh +++ b/usr/local/share/bastille/umount.sh @@ -32,43 +32,57 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille umount TARGET container_path" + error_exit "Usage: bastille umount TARGET JAIL_PATH" } # Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; +case "${1}" in + help|-h|--help) + usage + ;; esac -if [ $# -ne 1 ]; then +if [ "$#" -ne 2 ]; then usage fi -bastille_root_check +TARGET="${1}" +MOUNT_PATH="${2}" -MOUNT_PATH=$1 +bastille_root_check +set_target "${TARGET}" for _jail in ${JAILS}; do + info "[${_jail}]:" - _jailpath="${bastille_jailsdir}/${_jail}/root/${MOUNT_PATH}" + _jailpath="$( echo "${bastille_jailsdir}/${_jail}/root/${MOUNT_PATH}" 2>/dev/null | sed 's#//#/#' | sed 's#\\##g')" + _mount="$( mount | grep -Eo "[[:blank:]]${_jailpath}[[:blank:]]" )" + _jailpath_fstab="$(echo "${bastille_jailsdir}/${_jail}/root/${MOUNT_PATH}" | sed 's#//#/#g' | sed 's# #\\#g' | sed 's#\\#\\\\040#g')" + _fstab_entry="$(grep -Eo "[[:blank:]]${_jailpath_fstab}[[:blank:]]" ${bastille_jailsdir}/${_jail}/fstab)" - if [ ! -d "${_jailpath}" ]; then - error_exit "The specified mount point does not exist inside the jail." + # Exit if mount point non-existent + if [ -z "${_mount}" ] && [ -z "${_fstab_entry}" ]; then + error_continue "The specified mount point does not exist." fi - # Unmount the volume. -- cwells - if ! umount "${_jailpath}"; then - error_exit "Failed to unmount volume: ${MOUNT_PATH}" + # Unmount + if [ -n "${_mount}" ]; then + umount "${_jailpath}" || error_continue "Failed to unmount volume: ${MOUNT_PATH}" fi - # Remove the entry from fstab so it is not automounted in the future. -- cwells - if ! sed -E -i '' "\, +${_jailpath} +,d" "${bastille_jailsdir}/${_jail}/fstab"; then - error_exit "Failed to delete fstab entry: ${_fstab_entry}" + # Remove entry from fstab + if [ -n "${_fstab_entry}" ]; then + if ! sed -E -i '' "\, +${_jailpath_fstab} +,d" "${bastille_jailsdir}/${_jail}/fstab"; then + error_continue "Failed to delete fstab entry: ${MOUNT_PATH}" + fi fi - echo "Unmounted: ${MOUNT_PATH}" - echo + # Delete if mount point was a file + if [ -f "${_jailpath}" ]; then + rm -f "${_jailpath}" || error_continue "Failed to unmount volume: ${MOUNT_PATH}" + fi + + echo "Unmounted: ${_jailpath}" + done