From 753c537e52fa30287c9fd09a7fd3c8bc50622a55 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 11 Jan 2025 12:06:30 -0700 Subject: [PATCH 01/46] mount: allow [ro|rw|rq|sw|xx] as available mount options --- usr/local/share/bastille/mount.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index ba66ae8b..73d5d5e7 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -89,8 +89,8 @@ elif [ ! -e "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then usage fi -# Mount permissions,options need to start with "ro" or "rw" -if ! echo "${_perms}" | grep -Eq 'r[w|o],.*$'; then +# Mount permissions,options need to include at least on of "ro, rw, rq, sw, xx" +if ! echo "${_perms}" | grep -Eq '[ro|rw|rq|sw|xx]'; then error_notify "Detected invalid mount permissions in FSTAB." warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" From fa4466eb7f479c79737760a282601cd0325faa0e Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 11 Jan 2025 12:07:59 -0700 Subject: [PATCH 02/46] mount: docs, allow all fs type options --- docs/chapters/subcommands/mount.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapters/subcommands/mount.rst b/docs/chapters/subcommands/mount.rst index cabe779c..dfbbd526 100644 --- a/docs/chapters/subcommands/mount.rst +++ b/docs/chapters/subcommands/mount.rst @@ -10,7 +10,7 @@ Syntax follows standard `/etc/fstab` format: Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number] -The 'options' string can include a comma-separated list of mount options, but must start with 'ro' or 'rw'. +The 'options' string can include a comma-separated list of mount options, but must include at least one of (rw,ro,rq.sw,xx) according to fstab documentation. Example: Mount a tmpfs filesystem with options. .. code-block:: shell From f3123bf08de1a6982b3479c2bdae96e2e28d83dd Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 11 Jan 2025 12:08:24 -0700 Subject: [PATCH 03/46] mount: Docs, typo . > , --- docs/chapters/subcommands/mount.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapters/subcommands/mount.rst b/docs/chapters/subcommands/mount.rst index dfbbd526..a5fbc930 100644 --- a/docs/chapters/subcommands/mount.rst +++ b/docs/chapters/subcommands/mount.rst @@ -10,7 +10,7 @@ Syntax follows standard `/etc/fstab` format: Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number] -The 'options' string can include a comma-separated list of mount options, but must include at least one of (rw,ro,rq.sw,xx) according to fstab documentation. +The 'options' string can include a comma-separated list of mount options, but must include at least one of (rw,ro,rq,sw,xx) according to fstab documentation. Example: Mount a tmpfs filesystem with options. .. code-block:: shell From e33dd04ad1c0f9d16b391361d4583177131679b2 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 11 Jan 2025 12:10:27 -0700 Subject: [PATCH 04/46] mount: Docs, one more typo "on > one" --- usr/local/share/bastille/mount.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index 73d5d5e7..c6bf5bf8 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -89,7 +89,7 @@ elif [ ! -e "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then usage fi -# Mount permissions,options need to include at least on of "ro, rw, rq, sw, xx" +# Mount permissions,options need to include at least one of "ro, rw, rq, sw, xx" if ! echo "${_perms}" | grep -Eq '[ro|rw|rq|sw|xx]'; then error_notify "Detected invalid mount permissions in FSTAB." warn "Format: /host/path /jail/path nullfs ro 0 0" From b22d68db13eb4653dd01228ecda0ae5b70a439c2 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Tue, 14 Jan 2025 11:24:10 -0700 Subject: [PATCH 05/46] update: set -d and -f for release updates and think jail updates --- usr/local/share/bastille/update.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/usr/local/share/bastille/update.sh b/usr/local/share/bastille/update.sh index 85d632c0..8ee766fc 100644 --- a/usr/local/share/bastille/update.sh +++ b/usr/local/share/bastille/update.sh @@ -96,6 +96,9 @@ jail_check() { } jail_update() { + local _freebsd_update_conf="${bastille_jailsdir}/${TARGET}/root/etc/freebsd-update.conf" + local _jail_dir="${bastille_jailsdir}/${TARGET}/root" + local _workdir="${bastille_releasesdir}/${TARGET}/root/var/db/freebsd-update" # Update a thick container if [ -d "${bastille_jailsdir}/${TARGET}" ]; then jail_check @@ -103,7 +106,9 @@ jail_update() { if [ -z "${CURRENT_VERSION}" ]; then error_exit "Can't determine '${TARGET}' version." else - env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_jailsdir}/${TARGET}/root" \ + env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${_jail_dir}" \ + -d "${_workdir}" \ + -f "${_freebsd_update_conf}" \ fetch install --currently-running "${CURRENT_VERSION}" fi else @@ -112,6 +117,9 @@ jail_update() { } release_update() { + local _freebsd_update_conf="${bastille_releasesdir}/${TARGET}/etc/freebsd-update.conf" + local _release_dir="${bastille_releasesdir}/${TARGET}" + local _workdir="${bastille_releasesdir}/${TARGET}/var/db/freebsd-update" # Update a release base(affects child containers) if [ -d "${bastille_releasesdir}/${TARGET}" ]; then TARGET_TRIM="${TARGET}" @@ -120,8 +128,12 @@ release_update() { fi env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_releasesdir}/${TARGET}" \ + -d "${_workdir}" \ + -f "${_freebsd_update_conf}" \ fetch --currently-running "${TARGET_TRIM}" env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_releasesdir}/${TARGET}" \ + -d "${_workdir}" \ + -f "${_freebsd_update_conf}" \ install --currently-running "${TARGET_TRIM}" else error_exit "${TARGET} not found. See 'bastille bootstrap'." From 2f7120a176355045e37e76784c5f8789c33dd0b4 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:02:02 -0700 Subject: [PATCH 06/46] update: add debug, code cleanup --- usr/local/share/bastille/update.sh | 113 +++++++++++++++++------------ 1 file changed, 68 insertions(+), 45 deletions(-) diff --git a/usr/local/share/bastille/update.sh b/usr/local/share/bastille/update.sh index 8ee766fc..f2ab3c61 100644 --- a/usr/local/share/bastille/update.sh +++ b/usr/local/share/bastille/update.sh @@ -34,39 +34,62 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille update [release|container|template] | [force]" -} + error_notify "Usage: bastille update [option(s)] TARGET" + cat << EOF + Options: -# Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; -esac + -a | --auto Auto mode. Start/stop jail(s) if required. + -f | --force Force update a release. + -x | --debug Enable debug mode. + +EOF + exit 1 +} if [ $# -gt 2 ] || [ $# -lt 1 ]; then usage fi -bastille_root_check +# Handle options. +OPTION="" +AUTO=0 +while [ "$#" -gt 0 ]; do + case "${1}" in + -h|--help|help) + usage + ;; + -a|--auto) + AUTO=1 + shift + ;; + -f|--force) + OPTION="-F" + shift + ;; + -x|--debug) + enable_debug + shift + ;; + -*) + for _opt in $(echo ${1} | sed 's/-//g' | fold -w1); do + case ${_opt} in + a) AUTO=1 ;; + f) OPTION="-F" ;; + x) enable_debug ;; + *) error_exit "Unknown Option: \"${1}\"" ;; + esac + done + shift + ;; + *) + break + ;; + esac +done TARGET="${1}" -OPTION="${2}" -# Handle options -case "${OPTION}" in - -f|--force) - OPTION="-F" - ;; - *) - OPTION= - ;; -esac - -# Check for unsupported actions -if [ "${TARGET}" = "ALL" ]; then - error_exit "Batch upgrade is unsupported." -fi +bastille_root_check if [ -f "/bin/midnightbsd-version" ]; then echo -e "${COLOR_RED}Not yet supported on MidnightBSD.${COLOR_RESET}" @@ -86,22 +109,25 @@ arch_check() { jail_check() { # Check if the jail is thick and is running - if [ ! "$(/usr/sbin/jls name | awk "/^${TARGET}$/")" ]; then - error_exit "[${TARGET}]: Not started. See 'bastille start ${TARGET}'." - else - if grep -qw "${bastille_jailsdir}/${TARGET}/root/.bastille" "${bastille_jailsdir}/${TARGET}/fstab"; then - error_exit "${TARGET} is not a thick container." - fi + set_target_single "${TARGET}" + check_target_is_running "${TARGET}" || if [ "${AUTO}" -eq 1 ]; then + bastille start "${TARGET}" + else + error_notify "Jail is not running." + error_continue "Use [-a|--auto] to auto-start the jail." + fi + if grep -qw "${bastille_jailsdir}/${TARGET}/root/.bastille" "${bastille_jailsdir}/${TARGET}/fstab"; then + error_notify "${TARGET} is not a thick container." + error_exit "See 'bastille update RELEASE' to update thin jails." fi } jail_update() { local _freebsd_update_conf="${bastille_jailsdir}/${TARGET}/root/etc/freebsd-update.conf" local _jail_dir="${bastille_jailsdir}/${TARGET}/root" - local _workdir="${bastille_releasesdir}/${TARGET}/root/var/db/freebsd-update" + local _workdir="${bastille_jailsdir}/${TARGET}/root/var/db/freebsd-update" # Update a thick container - if [ -d "${bastille_jailsdir}/${TARGET}" ]; then - jail_check + if [ -d "${bastille_jailsdir}/${TARGET}" ]; then CURRENT_VERSION=$(/usr/sbin/jexec -l "${TARGET}" freebsd-version 2>/dev/null) if [ -z "${CURRENT_VERSION}" ]; then error_exit "Can't determine '${TARGET}' version." @@ -111,32 +137,28 @@ jail_update() { -f "${_freebsd_update_conf}" \ fetch install --currently-running "${CURRENT_VERSION}" fi - else - error_exit "${TARGET} not found. See 'bastille bootstrap'." fi } release_update() { local _freebsd_update_conf="${bastille_releasesdir}/${TARGET}/etc/freebsd-update.conf" local _release_dir="${bastille_releasesdir}/${TARGET}" - local _workdir="${bastille_releasesdir}/${TARGET}/var/db/freebsd-update" # Update a release base(affects child containers) - if [ -d "${bastille_releasesdir}/${TARGET}" ]; then + if [ -d "${_release_dir}" ]; then TARGET_TRIM="${TARGET}" if [ -n "${ARCH_I386}" ]; then TARGET_TRIM=$(echo "${TARGET}" | sed 's/-i386//') fi - env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_releasesdir}/${TARGET}" \ - -d "${_workdir}" \ + -d "${bastille_releasesdir}/${TARGET}/var/db/freebsd-update" \ -f "${_freebsd_update_conf}" \ fetch --currently-running "${TARGET_TRIM}" env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_releasesdir}/${TARGET}" \ - -d "${_workdir}" \ + -d "${bastille_releasesdir}/${TARGET}/var/db/freebsd-update" \ -f "${_freebsd_update_conf}" \ install --currently-running "${TARGET_TRIM}" else - error_exit "${TARGET} not found. See 'bastille bootstrap'." + error_exit "${TARGET} not found. See 'bastille bootstrap RELEASE'." fi } @@ -157,10 +179,10 @@ template_update() { templates_update() { # Update all templates _updated_templates=0 - if [ -d "${bastille_templatesdir}" ]; then - # shellcheck disable=SC2045 - for _template_path in $(ls -d "${bastille_templatesdir}"/*/*); do - if [ -d "$_template_path"/.git ]; then + if [ -d ${bastille_templatesdir} ]; then + # shellcheck disable=SC2045 + for _template_path in $(ls -d ${bastille_templatesdir}/*/*); do + if [ -d $_template_path/.git ]; then BASTILLE_TEMPLATE=$(echo "$_template_path" | awk -F / '{ print $(NF-1) "/" $NF }') template_update @@ -186,5 +208,6 @@ elif echo "${TARGET}" | grep -q "[0-9]\{2\}.[0-9]-RELEASE"; then arch_check release_update else + jail_check jail_update fi From 6bddbaab2c72b336ff1f98a6ef5ab7872ec36f12 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:03:36 -0700 Subject: [PATCH 07/46] upgrade: deprecate RELEASE upgrade, fix thick/thin jail upgrades --- usr/local/share/bastille/upgrade.sh | 167 ++++++++++++++++------------ 1 file changed, 95 insertions(+), 72 deletions(-) diff --git a/usr/local/share/bastille/upgrade.sh b/usr/local/share/bastille/upgrade.sh index 5aa06905..a1092948 100644 --- a/usr/local/share/bastille/upgrade.sh +++ b/usr/local/share/bastille/upgrade.sh @@ -34,31 +34,64 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille upgrade release newrelease | target newrelease | target install | [force]" + error_notify "Usage: bastille upgrade [option(s)] TARGET [NEWRELEASE|install]" + cat << EOF + Options: + + -a | --auto Auto mode. Start/stop jail(s) if required. + -f | --force Force upgrade a release. + -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 + ;; + -a|--auto) + AUTO=1 + shift + ;; + -f|--force) + OPTION="-F" + shift + ;; + -x|--debug) + enable_debug + shift + ;; + -*) + for _opt in $(echo ${1} | sed 's/-//g' | fold -w1); do + case ${_opt} in + a) AUTO=1 ;; + f) OPTION="-F" ;; + x) enable_debug ;; + *) error_exit "Unknown Option: \"${1}\"" ;; + esac + done + shift + ;; + *) + break + ;; + esac +done -if [ $# -gt 3 ] || [ $# -lt 2 ]; then +if [ $# -lt 2 ] || [ $# -gt 3 ]; then usage fi +TARGET="${1}" +NEWRELEASE="${2}" + bastille_root_check -TARGET="$1" -NEWRELEASE="$2" -OPTION="$3" - -# Check for unsupported actions -if [ "${TARGET}" = "ALL" ]; then - error_exit "Batch upgrade is unsupported." -fi - +# Check for unsupported actions if [ -f "/bin/midnightbsd-version" ]; then echo -e "${COLOR_RED}Not yet supported on MidnightBSD.${COLOR_RESET}" exit 1 @@ -68,24 +101,14 @@ if freebsd-version | grep -qi HBSD; then error_exit "Not yet supported on HardenedBSD." fi -# Handle options -case "${OPTION}" in - -f|--force) - OPTION="-F" - ;; - *) - OPTION= - ;; -esac - jail_check() { # Check if the jail is thick and is running - if [ ! "$(/usr/sbin/jls name | awk "/^${TARGET}$/")" ]; then - error_exit "[${TARGET}]: Not started. See 'bastille start ${TARGET}'." - else - if grep -qw "${bastille_jailsdir}/${TARGET}/root/.bastille" "${bastille_jailsdir}/${TARGET}/fstab"; then - error_exit "${TARGET} is not a thick container." - fi + set_target_single "${TARGET}" + check_target_is_running "${TARGET}" || if [ "${AUTO}" -eq 1 ]; then + bastille start "${TARGET}" + else + error_notify "Jail is not running." + error_continue "Use [-a|--auto] to auto-start the jail." fi } @@ -96,60 +119,60 @@ release_check() { fi } -release_upgrade() { - # Upgrade a release - if [ -d "${bastille_releasesdir}/${TARGET}" ]; then - release_check - env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_releasesdir}/${TARGET}" --currently-running "${TARGET}" -r "${NEWRELEASE}" upgrade - echo - echo -e "${COLOR_YELLOW}Please run 'bastille upgrade ${TARGET} install' to finish installing updates.${COLOR_RESET}" - else - error_exit "${TARGET} not found. See 'bastille bootstrap'." - fi -} - jail_upgrade() { - # Upgrade a thick container - if [ -d "${bastille_jailsdir}/${TARGET}" ]; then - jail_check - release_check - CURRENT_VERSION=$(jexec -l ${TARGET} freebsd-version) - env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_jailsdir}/${TARGET}/root" --currently-running "${CURRENT_VERSION}" -r ${NEWRELEASE} upgrade + local _jailname="${1}" + local _oldrelease="$(jexec -l ${TARGET} freebsd-version)" + local _newrelease="${2}" + local _jailpath="${bastille_jailsdir}/${TARGET}/root" + local _workdir="${bastille_jailsdir}/${TARGET}/root/var/db/freebsd-update" + local _freebsd_update_conf="${bastille_jailsdir}/${TARGET}/root/etc/freebsd-update.conf" + + jail_check + release_check + + # Upgrade a thin jail + if grep -qw "${bastille_jailsdir}/${TARGET}/root/.bastille" "${bastille_jailsdir}/${TARGET}/fstab"; then + local _oldrelease="$(grep osrelease ${bastille_jailsdir}/${TARGET}/jail.conf | awk -F"= " '{print $2}' | sed 's/;//g')" + local _newrelease="${NEWRELEASE}" + sed -i '' "/.bastille/ s|${_oldrelease}|${_newrelease}|g" "${bastille_jailsdir}/${TARGET}/fstab" + sed -i '' "/osrelease/ s|${_oldrelease}|${_newrelease}|g" "${bastille_jailsdir}/${TARGET}/jail.conf" + info "Upgraded ${TARGET}: ${_oldrelease} -> ${_newrelease}" + info "See 'bastille etcupdate TARGET' to update /etc/rc.conf" + else + # Upgrade a thick jail + env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron \ + --currently-running "${_oldrelease}" \ + -b "${_jailpath}" \ + -d "${_workdir}" \ + -f "${_freebsd_update_conf}" \ + -r "${_newrelease}" \ + upgrade + echo echo -e "${COLOR_YELLOW}Please run 'bastille upgrade ${TARGET} install' to finish installing updates.${COLOR_RESET}" - else - error_exit "${TARGET} not found. See 'bastille bootstrap'." fi } jail_updates_install() { + local _jailpath="${bastille_jailsdir}/${TARGET}/root" + local _workdir="${bastille_jailsdir}/${TARGET}/root/var/db/freebsd-update" + local _freebsd_update_conf="${bastille_jailsdir}/${TARGET}/root/etc/freebsd-update.conf" # Finish installing upgrade on a thick container if [ -d "${bastille_jailsdir}/${TARGET}" ]; then jail_check - env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_jailsdir}/${TARGET}/root" install + env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron \ + -b "${_jailpath}" \ + -d "${_workdir}" \ + -f "${_freebsd_update_conf}" \ + install else - error_exit "${TARGET} not found. See 'bastille bootstrap'." - fi -} - -release_updates_install() { - # Finish installing upgrade on a release - if [ -d "${bastille_releasesdir}/${TARGET}" ]; then - env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_releasesdir}/${TARGET}" install - else - error_exit "${TARGET} not found. See 'bastille bootstrap'." + error_exit "${TARGET} not found. See 'bastille bootstrap RELEASE'." fi } # Check what we should upgrade -if echo "${TARGET}" | grep -q "[0-9]\{2\}.[0-9]-RELEASE"; then - if [ "${NEWRELEASE}" = "install" ]; then - release_updates_install - else - release_upgrade - fi -elif [ "${NEWRELEASE}" = "install" ]; then +if [ "${NEWRELEASE}" = "install" ]; then jail_updates_install else - jail_upgrade + jail_upgrade "${TARGET}" "${NEWRELEASE}" fi From 5394ad8979214786aa2b1579499b517994032187 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Tue, 14 Jan 2025 13:22:30 -0700 Subject: [PATCH 08/46] upgrade: Update "osrelease" entry --- usr/local/share/bastille/upgrade.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/usr/local/share/bastille/upgrade.sh b/usr/local/share/bastille/upgrade.sh index a1092948..74822da3 100644 --- a/usr/local/share/bastille/upgrade.sh +++ b/usr/local/share/bastille/upgrade.sh @@ -134,7 +134,9 @@ jail_upgrade() { if grep -qw "${bastille_jailsdir}/${TARGET}/root/.bastille" "${bastille_jailsdir}/${TARGET}/fstab"; then local _oldrelease="$(grep osrelease ${bastille_jailsdir}/${TARGET}/jail.conf | awk -F"= " '{print $2}' | sed 's/;//g')" local _newrelease="${NEWRELEASE}" + # Update "osrelease" entry inside jail.conf sed -i '' "/.bastille/ s|${_oldrelease}|${_newrelease}|g" "${bastille_jailsdir}/${TARGET}/fstab" + # Update "fstab" entry sed -i '' "/osrelease/ s|${_oldrelease}|${_newrelease}|g" "${bastille_jailsdir}/${TARGET}/jail.conf" info "Upgraded ${TARGET}: ${_oldrelease} -> ${_newrelease}" info "See 'bastille etcupdate TARGET' to update /etc/rc.conf" @@ -145,9 +147,10 @@ jail_upgrade() { -b "${_jailpath}" \ -d "${_workdir}" \ -f "${_freebsd_update_conf}" \ - -r "${_newrelease}" \ - upgrade - + -r "${_newrelease}" upgrade + + # Update "osrelease" entry inside jail.conf + sed -i '' "/osrelease/ s|${_oldrelease}|${_newrelease}|g" "${bastille_jailsdir}/${TARGET}/jail.conf" echo echo -e "${COLOR_YELLOW}Please run 'bastille upgrade ${TARGET} install' to finish installing updates.${COLOR_RESET}" fi From 35f698bc960d97d0c598c6f38e4316772f3d3359 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Thu, 16 Jan 2025 07:25:49 -0700 Subject: [PATCH 09/46] upgrade: use $jailpath for vars --- usr/local/share/bastille/upgrade.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/usr/local/share/bastille/upgrade.sh b/usr/local/share/bastille/upgrade.sh index 74822da3..0b9d2365 100644 --- a/usr/local/share/bastille/upgrade.sh +++ b/usr/local/share/bastille/upgrade.sh @@ -124,8 +124,8 @@ jail_upgrade() { local _oldrelease="$(jexec -l ${TARGET} freebsd-version)" local _newrelease="${2}" local _jailpath="${bastille_jailsdir}/${TARGET}/root" - local _workdir="${bastille_jailsdir}/${TARGET}/root/var/db/freebsd-update" - local _freebsd_update_conf="${bastille_jailsdir}/${TARGET}/root/etc/freebsd-update.conf" + local _workdir="${_jailpath}/var/db/freebsd-update" + local _freebsd_update_conf="${_jailpath}/etc/freebsd-update.conf" jail_check release_check @@ -158,8 +158,8 @@ jail_upgrade() { jail_updates_install() { local _jailpath="${bastille_jailsdir}/${TARGET}/root" - local _workdir="${bastille_jailsdir}/${TARGET}/root/var/db/freebsd-update" - local _freebsd_update_conf="${bastille_jailsdir}/${TARGET}/root/etc/freebsd-update.conf" + local _workdir="${_jailpath}/var/db/freebsd-update" + local _freebsd_update_conf="${_jailpath}/etc/freebsd-update.conf" # Finish installing upgrade on a thick container if [ -d "${bastille_jailsdir}/${TARGET}" ]; then jail_check From e4f58af7707eb18211416c613623763eac1b4377 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Thu, 16 Jan 2025 07:27:48 -0700 Subject: [PATCH 10/46] upgrade: Use -j jailname instead of -b --- usr/local/share/bastille/upgrade.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/local/share/bastille/upgrade.sh b/usr/local/share/bastille/upgrade.sh index 0b9d2365..70db6071 100644 --- a/usr/local/share/bastille/upgrade.sh +++ b/usr/local/share/bastille/upgrade.sh @@ -144,7 +144,7 @@ jail_upgrade() { # Upgrade a thick jail env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron \ --currently-running "${_oldrelease}" \ - -b "${_jailpath}" \ + -j "${_jailname}" \ -d "${_workdir}" \ -f "${_freebsd_update_conf}" \ -r "${_newrelease}" upgrade @@ -164,7 +164,7 @@ jail_updates_install() { if [ -d "${bastille_jailsdir}/${TARGET}" ]; then jail_check env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron \ - -b "${_jailpath}" \ + -j "${_jailname}" \ -d "${_workdir}" \ -f "${_freebsd_update_conf}" \ install From a74f87162ae27b7e417d2df99f347df28a2e390f Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Thu, 16 Jan 2025 07:36:25 -0700 Subject: [PATCH 11/46] update: Use -j jailpath and _release path as vars --- usr/local/share/bastille/update.sh | 33 ++++++++++++++++++------------ 1 file changed, 20 insertions(+), 13 deletions(-) diff --git a/usr/local/share/bastille/update.sh b/usr/local/share/bastille/update.sh index f2ab3c61..f1ee3ef7 100644 --- a/usr/local/share/bastille/update.sh +++ b/usr/local/share/bastille/update.sh @@ -123,38 +123,45 @@ jail_check() { } jail_update() { - local _freebsd_update_conf="${bastille_jailsdir}/${TARGET}/root/etc/freebsd-update.conf" - local _jail_dir="${bastille_jailsdir}/${TARGET}/root" - local _workdir="${bastille_jailsdir}/${TARGET}/root/var/db/freebsd-update" + local _jailname="${1}" + local _jailpath="${bastille_jailsdir}/${TARGET}/root" + local _freebsd_update_conf="${_jailpath}/etc/freebsd-update.conf" + local _workdir="${_jailpath}/var/db/freebsd-update" # Update a thick container if [ -d "${bastille_jailsdir}/${TARGET}" ]; then CURRENT_VERSION=$(/usr/sbin/jexec -l "${TARGET}" freebsd-version 2>/dev/null) if [ -z "${CURRENT_VERSION}" ]; then error_exit "Can't determine '${TARGET}' version." else - env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${_jail_dir}" \ + env PAGER="/bin/cat" freebsd-update ${OPTION} \ + --not-running-from-cron \ + -j "${_jailname}" \ -d "${_workdir}" \ -f "${_freebsd_update_conf}" \ - fetch install --currently-running "${CURRENT_VERSION}" + fetch install fi fi } release_update() { - local _freebsd_update_conf="${bastille_releasesdir}/${TARGET}/etc/freebsd-update.conf" - local _release_dir="${bastille_releasesdir}/${TARGET}" + local _releasepath="${bastille_releasesdir}/${TARGET}" + local _freebsd_update_conf="${_releasepath}/etc/freebsd-update.conf" # Update a release base(affects child containers) - if [ -d "${_release_dir}" ]; then + if [ -d "${_releasepath}" ]; then TARGET_TRIM="${TARGET}" if [ -n "${ARCH_I386}" ]; then TARGET_TRIM=$(echo "${TARGET}" | sed 's/-i386//') fi - env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_releasesdir}/${TARGET}" \ - -d "${bastille_releasesdir}/${TARGET}/var/db/freebsd-update" \ + env PAGER="/bin/cat" freebsd-update ${OPTION} \ + --not-running-from-cron \ + -b "${_releasepath}" \ + -d "${_releasepath}/var/db/freebsd-update" \ -f "${_freebsd_update_conf}" \ fetch --currently-running "${TARGET_TRIM}" - env PAGER="/bin/cat" freebsd-update ${OPTION} --not-running-from-cron -b "${bastille_releasesdir}/${TARGET}" \ - -d "${bastille_releasesdir}/${TARGET}/var/db/freebsd-update" \ + env PAGER="/bin/cat" freebsd-update ${OPTION} \ + --not-running-from-cron \ + -b "${_releasepath}" \ + -d "${_releasepath}/var/db/freebsd-update" \ -f "${_freebsd_update_conf}" \ install --currently-running "${TARGET_TRIM}" else @@ -209,5 +216,5 @@ elif echo "${TARGET}" | grep -q "[0-9]\{2\}.[0-9]-RELEASE"; then release_update else jail_check - jail_update + jail_update "${TARGET}" fi From 6eaa16e2998d77456ee41c60717fb5576778f515 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Fri, 17 Jan 2025 07:34:13 -0700 Subject: [PATCH 12/46] =?UTF-8?q?update:=20use=20=E2=80=9Cworkdir=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- usr/local/share/bastille/update.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/usr/local/share/bastille/update.sh b/usr/local/share/bastille/update.sh index f1ee3ef7..1a387b36 100644 --- a/usr/local/share/bastille/update.sh +++ b/usr/local/share/bastille/update.sh @@ -146,6 +146,7 @@ jail_update() { release_update() { local _releasepath="${bastille_releasesdir}/${TARGET}" local _freebsd_update_conf="${_releasepath}/etc/freebsd-update.conf" + local _workdir="${_releasepath}/var/db/freebsd-update" # Update a release base(affects child containers) if [ -d "${_releasepath}" ]; then TARGET_TRIM="${TARGET}" @@ -155,13 +156,13 @@ release_update() { env PAGER="/bin/cat" freebsd-update ${OPTION} \ --not-running-from-cron \ -b "${_releasepath}" \ - -d "${_releasepath}/var/db/freebsd-update" \ + -d "${_workdir}" \ -f "${_freebsd_update_conf}" \ fetch --currently-running "${TARGET_TRIM}" env PAGER="/bin/cat" freebsd-update ${OPTION} \ --not-running-from-cron \ -b "${_releasepath}" \ - -d "${_releasepath}/var/db/freebsd-update" \ + -d "${_workdir}" \ -f "${_freebsd_update_conf}" \ install --currently-running "${TARGET_TRIM}" else From ee0b8b8f962bd44ca4f8ae3554cef53665b56928 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Fri, 17 Jan 2025 08:11:58 -0700 Subject: [PATCH 13/46] mount: fix perms check --- usr/local/share/bastille/mount.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index c6bf5bf8..604a70d7 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -89,8 +89,8 @@ elif [ ! -e "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then usage fi -# Mount permissions,options need to include at least one of "ro, rw, rq, sw, xx" -if ! echo "${_perms}" | grep -Eq '[ro|rw|rq|sw|xx]'; then +# Mount permissions,options must start with one of "ro, rw, rq, sw, xx" +if ! echo "${_perms}" | grep -Eq '(ro|rw|rq|sw|xx)(,.*)?$'; then error_notify "Detected invalid mount permissions in FSTAB." warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" From d9106b5b3ebd2ebb690ec8898ab1eb8c30c0f8c3 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Fri, 17 Jan 2025 08:13:29 -0700 Subject: [PATCH 14/46] docs: Fix mount perms check docs --- docs/chapters/subcommands/mount.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapters/subcommands/mount.rst b/docs/chapters/subcommands/mount.rst index a5fbc930..172799ad 100644 --- a/docs/chapters/subcommands/mount.rst +++ b/docs/chapters/subcommands/mount.rst @@ -10,7 +10,7 @@ Syntax follows standard `/etc/fstab` format: Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number] -The 'options' string can include a comma-separated list of mount options, but must include at least one of (rw,ro,rq,sw,xx) according to fstab documentation. +The 'options' string can include a comma-separated list of mount options, but must start with one of (rw,ro,rq,sw,xx) according to fstab documentation. Example: Mount a tmpfs filesystem with options. .. code-block:: shell From 5cfbe222adff6789d4d1ae1c43d8ec3daeafa22d Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Fri, 17 Jan 2025 12:27:11 -0700 Subject: [PATCH 15/46] upgrade: Fix jail update var --- usr/local/share/bastille/upgrade.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/usr/local/share/bastille/upgrade.sh b/usr/local/share/bastille/upgrade.sh index 70db6071..2be09435 100644 --- a/usr/local/share/bastille/upgrade.sh +++ b/usr/local/share/bastille/upgrade.sh @@ -157,6 +157,7 @@ jail_upgrade() { } jail_updates_install() { + local _jailname="${1}" local _jailpath="${bastille_jailsdir}/${TARGET}/root" local _workdir="${_jailpath}/var/db/freebsd-update" local _freebsd_update_conf="${_jailpath}/etc/freebsd-update.conf" @@ -175,7 +176,7 @@ jail_updates_install() { # Check what we should upgrade if [ "${NEWRELEASE}" = "install" ]; then - jail_updates_install + jail_updates_install "${TARGET}" else jail_upgrade "${TARGET}" "${NEWRELEASE}" fi From bbb1555eccc148e8353e347ce418d9254cb1b2c0 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 18 Jan 2025 07:33:00 -0700 Subject: [PATCH 16/46] mount: Fix similar path mount grep --- usr/local/share/bastille/mount.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index 1c2a8efb..5895823a 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -117,7 +117,7 @@ for _jail in ${JAILS}; do # 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 + if grep -Eq "[[:blank:]]${_existing_mount}[[:blank:]]" "${bastille_jailsdir}/${_jail}/fstab"; then warn "Mountpoint already present in ${bastille_jailsdir}/${_jail}/fstab" grep -E "[[:blank:]]${_existing_mount}" "${bastille_jailsdir}/${_jail}/fstab" continue From e1a2ed1b6bf9ea5d849d219fefa7acb73da92c3a Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 18 Jan 2025 07:39:42 -0700 Subject: [PATCH 17/46] mount: Fix () > [] --- usr/local/share/bastille/mount.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index 5895823a..398d1bf8 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -92,7 +92,7 @@ elif [ ! -e "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then fi # Mount permissions,options must start with one of "ro, rw, rq, sw, xx" -if ! echo "${_perms}" | grep -Eq '(ro|rw|rq|sw|xx)(,.*)?$'; then +if ! echo "${_perms}" | grep -Eq '[ro|rw|rq|sw|xx](,.*)?$'; then error_notify "Detected invalid mount permissions in FSTAB." warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" From f994df3baddae8a4c68ad9464a750facfa2b62cc Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 18 Jan 2025 07:51:00 -0700 Subject: [PATCH 18/46] mount: Revert [] > () --- usr/local/share/bastille/mount.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index 398d1bf8..5895823a 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -92,7 +92,7 @@ elif [ ! -e "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then fi # Mount permissions,options must start with one of "ro, rw, rq, sw, xx" -if ! echo "${_perms}" | grep -Eq '[ro|rw|rq|sw|xx](,.*)?$'; then +if ! echo "${_perms}" | grep -Eq '(ro|rw|rq|sw|xx)(,.*)?$'; then error_notify "Detected invalid mount permissions in FSTAB." warn "Format: /host/path /jail/path nullfs ro 0 0" warn "Read: ${_fstab}" From 589f19032ae246c941c60a0452498746c7d571df Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 18 Jan 2025 16:32:40 -0700 Subject: [PATCH 19/46] common: Allow name=if for bridge --- usr/local/share/bastille/common.sh | 240 +++++++++++++++++++---------- 1 file changed, 159 insertions(+), 81 deletions(-) diff --git a/usr/local/share/bastille/common.sh b/usr/local/share/bastille/common.sh index 654ff026..1f4e2fa0 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,20 +111,124 @@ 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 } +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 +} + +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 + error_continue "Multiple jails found for ${_TARGET}:\n${_AUTOTARGET}" + return 1 + fi + else + return 2 + fi +} + +set_target() { + local _TARGET=${1} + JAILS="" + TARGET="" + if [ "${_TARGET}" = ALL ] || [ "${_TARGET}" = all ]; then + target_all_jails + else + 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 +} + +set_target_single() { + local _TARGET="${1}" + if [ "${_TARGET}" = ALL ] || [ "${_TARGET}" = all ]; then + error_exit "[all|ALL] not supported with this command." + 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() { + local _JAILS="$(bastille list jails)" + JAILS="" + for _jail in ${_JAILS}; do + if [ -d "${bastille_jailsdir}/${_jail}" ]; then + JAILS="${JAILS} ${_jail}" + fi + done + 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 the FreeBSD vendor MAC prefix for jail MAC prefix "58:9c:fc" local macaddr_prefix="58:9c:fc" - # Hash 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." @@ -140,12 +253,26 @@ generate_vnet_jail_netblock() { 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}" + 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 - local uniq_epair_bridge="0" + 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 @@ -166,25 +293,29 @@ generate_vnet_jail_netblock() { generate_static_mac "${jail_name}" "${external_interface}" cat <<-EOF vnet; - vnet.interface = epair${uniq_epair_bridge}b; - 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 ether ${macaddr}a"; - exec.prestart += "ifconfig epair${uniq_epair_bridge}b ether ${macaddr}b"; - exec.prestart += "ifconfig epair${uniq_epair_bridge}a description \"vnet host interface for Bastille jail ${jail_name}\""; - exec.poststop += "ifconfig ${external_interface} deletem epair${uniq_epair_bridge}a"; - exec.poststop += "ifconfig epair${uniq_epair_bridge}a destroy"; + 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 = epair${uniq_epair_bridge}b; - 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 description \"vnet host interface for Bastille jail ${jail_name}\""; - exec.poststop += "ifconfig ${external_interface} deletem epair${uniq_epair_bridge}a"; - exec.poststop += "ifconfig epair${uniq_epair_bridge}a destroy"; + 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 @@ -213,58 +344,6 @@ EOF fi } -set_target() { - local _TARGET="${1}" - 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 - export TARGET - fi -} - -set_target_single() { - local _TARGET="${1}" - if [ "${_TARGET}" = ALL ] || [ "${_TARGET}" = all ]; then - error_exit "[all|ALL] not supported with this command." - elif [ "$(echo ${_TARGET} | wc -w)" -gt 1 ]; then - error_exit "Error: Command only supports a single TARGET." - elif 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() { - local _JAILS="$(bastille list jails)" - JAILS="" - for _jail in ${_JAILS}; do - if [ -d "${bastille_jailsdir}/${_jail}" ]; then - JAILS="${JAILS} ${_jail}" - fi - done - export JAILS -} - checkyesno() { ## copied from /etc/rc.subr -- cedwards (20231125) ## issue #368 (lowercase values should be parsed) @@ -285,4 +364,3 @@ checkyesno() { ;; esac } - From 04f86c3f5badfb89b3b688726348871ed2827743 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 18 Jan 2025 16:38:29 -0700 Subject: [PATCH 20/46] clone: Allow name=if clone --- usr/local/share/bastille/clone.sh | 236 +++++++++++++++++++++--------- 1 file changed, 167 insertions(+), 69 deletions(-) diff --git a/usr/local/share/bastille/clone.sh b/usr/local/share/bastille/clone.sh index 428bf9c8..290db01b 100644 --- a/usr/local/share/bastille/clone.sh +++ b/usr/local/share/bastille/clone.sh @@ -34,24 +34,78 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille clone TARGET NEW_NAME IPADDRESS" + error_notify "Usage: bastille clone [option(s)] TARGET NEW_NAME IP_ADDRESS" + cat << EOF + Options: + + -a | --auto Auto mode. Start/stop jail(s) if required. Cannot be used with [-l|--live]. + -l | --live Clone a running jail. ZFS only. Jail must be running. Cannot be used with [-f|--force]. + -x | --debug Enable debug mode. + +EOF + exit 1 } -# Handle special-case commands first -case "$1" in -help|-h|--help) - usage - ;; -esac +# Handle options. +AUTO=0 +LIVE=0 +while [ "$#" -gt 0 ]; do + case "${1}" in + -h|--help|help) + usage + ;; + -a|--auto) + AUTO=1 + shift + ;; + -l|--live) + if ! checkyesno bastille_zfs_enable; then + error_exit "[-l|--live] can only be used with ZFS." + else + LIVE=1 + shift + fi + ;; + -x|--debug) + enable_debug + shift + ;; + -*) + for _opt in $(echo ${1} | sed 's/-//g' | fold -w1); do + case ${_opt} in + a) AUTO=1 ;; + l) LIVE=1 ;; + x) enable_debug ;; + *) error_exit "Unknown Option: \"${1}\"" + esac + done + shift + ;; + *) + break + ;; + esac +done -if [ $# -ne 2 ]; then +if [ "${AUTO}" -eq 1 ] && [ "${LIVE}" -eq 1 ]; then + error_exit "[-a|--auto] cannot be used with [-l|--live]" +fi + +if [ $# -ne 3 ]; then usage fi -bastille_root_check +TARGET="${1}" +NEWNAME="${2}" +IP="${3}" -NEWNAME="${1}" -IP="${2}" +bastille_root_check +set_target_single "${TARGET}" + +## don't allow for dots(.) in container names +if echo "${NEWNAME}" | grep -q "[.]"; then + error_exit "Container names may not contain a dot(.)!" +fi validate_ip() { IPX_ADDR="ip4.addr" @@ -89,24 +143,43 @@ update_jailconf() { JAIL_CONFIG="${bastille_jailsdir}/${NEWNAME}/jail.conf" if [ -f "${JAIL_CONFIG}" ]; then if ! grep -qw "path = ${bastille_jailsdir}/${NEWNAME}/root;" "${JAIL_CONFIG}"; then - sed -i '' "s|host.hostname = ${TARGET};|host.hostname = ${NEWNAME};|" "${JAIL_CONFIG}" + sed -i '' "s|host.hostname = ${TARGET};|host.hostname = ${NEWNAME};|" "${JAIL_CONFIG}" sed -i '' "s|exec.consolelog = .*;|exec.consolelog = ${bastille_logsdir}/${NEWNAME}_console.log;|" "${JAIL_CONFIG}" sed -i '' "s|path = .*;|path = ${bastille_jailsdir}/${NEWNAME}/root;|" "${JAIL_CONFIG}" sed -i '' "s|mount.fstab = .*;|mount.fstab = ${bastille_jailsdir}/${NEWNAME}/fstab;|" "${JAIL_CONFIG}" sed -i '' "s|${TARGET} {|${NEWNAME} {|" "${JAIL_CONFIG}" - sed -i '' "s|${IPX_ADDR} = .*;|${IPX_ADDR} = ${IP};|" "${JAIL_CONFIG}" fi fi if grep -qw "vnet;" "${JAIL_CONFIG}"; then update_jailconf_vnet + else + _ip4="$(bastille config ${TARGET} get ip4.addr | sed 's/,/ /g')" + _ip6="$(bastille config ${TARGET} get ip6.addr | sed 's/,/ /g')" + # IP4 + if [ "${_ip4}" != "not set" ]; then + for _ip in ${_ip4}; do + _ip="$(echo ${_ip} | awk -F"|" '{print $2}')" + sed -i '' "/${IPX_ADDR} = .*/ s/${_ip}/${IP}/" "${JAIL_CONFIG}" + sed -i '' "/${IPX_ADDR} += .*/ s/${_ip}/127.0.0.1/" "${JAIL_CONFIG}" + done + fi + # IP6 + if [ "${_ip6}" != "not set" ]; then + for _ip in ${_ip6}; do + _ip="$(echo ${_ip} | awk -F"|" '{print $2}')" + sed -i '' "/${IPX_ADDR} = .*/ s/${_ip}/${IP}/" "${JAIL_CONFIG}" + sed -i '' "/${IPX_ADDR} += .*/ s/${_ip}/127.0.0.1/" "${JAIL_CONFIG}" + done + fi fi } update_jailconf_vnet() { - bastille_jail_rc_conf="${bastille_jailsdir}/${NEWNAME}/root/etc/rc.conf" + local _jail_conf="${bastille_jailsdir}/${NEWNAME}/jail.conf" + local _rc_conf="${bastille_jailsdir}/${NEWNAME}/root/etc/rc.conf" # Determine number of interfaces and define a uniq_epair - local _if_list="$(grep -Eo 'epair[0-9]+|bastille[0-9]+' ${JAIL_CONFIG} | sort -u)" + local _if_list="$(grep -Eo 'epair[0-9]+|bastille[0-9]+' ${_jail_conf} | sort -u)" for _if in ${_if_list}; do local _epair_if_count="$(grep -Eo 'epair[0-9]+' ${bastille_jailsdir}/*/jail.conf | sort -u | wc -l | awk '{print $1}')" local _bastille_if_count="$(grep -Eo 'bastille[0-9]+' ${bastille_jailsdir}/*/jail.conf | sort -u | wc -l | awk '{print $1}')" @@ -116,30 +189,53 @@ update_jailconf_vnet() { # Update bridged VNET config for _num in $(seq 0 "${epair_num_range}"); do if ! grep -oq "epair${_num}" ${bastille_jailsdir}/*/jail.conf; then - # Update jail.conf epair name - local uniq_epair_bridge="${_num}" - local _if_epaira="${_if}a" - local _if_epairb="${_if}b" - local _if_vnet="$(grep ${_if_epairb} "${bastille_jail_rc_conf}" | grep -Eo -m 1 "vnet[0-9]+")" - sed -i '' "s|${_if}|epair${uniq_epair_bridge}|g" "${JAIL_CONFIG}" - # If jail had a static MAC, generate one for clone - if grep ether ${JAIL_CONFIG} | grep -qoc epair${uniq_epair_bridge}; then - local external_interface="$(grep "epair${uniq_epair_bridge}" ${JAIL_CONFIG} | grep -o '[^ ]* addm' | awk '{print $1}')" - generate_static_mac "${NEWNAME}" "${external_interface}" - sed -i '' "s|epair${uniq_epair_bridge}a ether.*:.*:.*:.*:.*:.*a\";|epair${uniq_epair_bridge}a ether ${macaddr}a\";|" "${JAIL_CONFIG}" - sed -i '' "s|epair${uniq_epair_bridge}b ether.*:.*:.*:.*:.*:.*b\";|epair${uniq_epair_bridge}b ether ${macaddr}b\";|" "${JAIL_CONFIG}" + # Generate new epair name + if [ "$(echo -n "e${_num}a_${NEWNAME}" | awk '{print length}')" -lt 16 ]; then + local _new_host_epair="e${_num}a_${NEWNAME}" + local _new_jail_epair="e${_num}b_${NEWNAME}" + else + local _new_host_epair="epair${_num}a" + local _new_jail_epair="epair${_num}b" fi - sed -i '' "s|vnet host interface for Bastille jail ${TARGET}|vnet host interface for Bastille jail ${NEWNAME}|g" "${JAIL_CONFIG}" + # Get epair name from TARGET + if grep -Eoq "e[0-9]+a_${TARGET}" "${_jail_conf}"; then + _target_host_epair="$(grep -Eo -m 1 "e[0-9]+a_${TARGET}" "${_jail_conf}")" + _target_jail_epair="$(grep -Eo -m 1 "e[0-9]+b_${TARGET}" "${_jail_conf}")" + else + _target_host_epair="${_if}a" + _target_jail_epair="${_if}b" + fi + # Replace epair name in jail.conf + sed -i '' "s|${_if}|epair${_num}|g" "${_jail_conf}" + # Replace host epair name in jail.conf + sed -i '' "s|up name ${_target_host_epair}|up name ${_new_host_epair}|g" "${_jail_conf}" + sed -i '' "s|${_target_host_epair} ether|${_new_host_epair} ether|g" "${_jail_conf}" + sed -i '' "s|deletem ${_target_host_epair}|deletem ${_new_host_epair}|g" "${_jail_conf}" + sed -i '' "s|${_target_host_epair} destroy|${_new_host_epair} destroy|g" "${_jail_conf}" + sed -i '' "s|${_target_host_epair} description|${_new_host_epair} description|g" "${_jail_conf}" + # Replace jail epair name in jail.conf + sed -i '' "s|= ${_target_jail_epair};|= ${_new_jail_epair};|g" "${_jail_conf}" + sed -i '' "s|up name ${_target_jail_epair}|up name ${_new_jail_epair}|g" "${_jail_conf}" + sed -i '' "s|${_target_jail_epair} ether|${_new_jail_epair} ether|g" "${_jail_conf}" + # If jail had a static MAC, generate one for clone + if grep -q ether ${_jail_conf}; then + local external_interface="$(grep "${_new_host_epair}" ${_jail_conf} | grep -o '[^ ]* addm' | awk '{print $1}')" + generate_static_mac "${NEWNAME}" "${external_interface}" + sed -i '' "s|${_new_host_epair} ether.*:.*:.*:.*:.*:.*a\";|${_new_host_epair} ether ${macaddr}a\";|" "${_jail_conf}" + sed -i '' "s|${_new_jail_epair} ether.*:.*:.*:.*:.*:.*b\";|${_new_jail_epair} ether ${macaddr}b\";|" "${_jail_conf}" + fi + sed -i '' "s|vnet host interface for Bastille jail ${TARGET}|vnet host interface for Bastille jail ${NEWNAME}|g" "${_jail_conf}" # Update /etc/rc.conf - sed -i '' "s|${_if_epairb}_name|epair${uniq_epair_bridge}b_name|" "${bastille_jail_rc_conf}" - if grep "vnet0" "${bastille_jail_rc_conf}" | grep -q "epair${uniq_epair_bridge}b_name"; then + local _jail_vnet="$(grep ${_target_jail_epair} "${_rc_conf}" | grep -Eo -m 1 "vnet[0-9]+")" + sed -i '' "s|${_target_jail_epair}_name|${_new_jail_epair}_name|" "${_rc_conf}" + if grep "vnet0" "${_rc_conf}" | grep -q "${_new_jail_epair}_name"; then if [ "${IP}" = "0.0.0.0" ]; then - sysrc -f "${bastille_jail_rc_conf}" ifconfig_vnet0="SYNCDHCP" + sysrc -f "${_rc_conf}" ifconfig_vnet0="SYNCDHCP" else - sysrc -f "${bastille_jail_rc_conf}" ifconfig_vnet0="inet ${IP}" + sysrc -f "${_rc_conf}" ifconfig_vnet0="inet ${IP}" fi else - sysrc -f "${bastille_jail_rc_conf}" ifconfig_${_if_vnet}="SYNCDHCP" + sysrc -f "${_rc_conf}" ifconfig_${_jail_vnet}="SYNCDHCP" fi break fi @@ -150,26 +246,26 @@ update_jailconf_vnet() { if ! grep -oq "bastille${_num}" ${bastille_jailsdir}/*/jail.conf; then # Update jail.conf epair name local uniq_epair="bastille${_num}" - local _if_vnet="$(grep ${_if} "${bastille_jail_rc_conf}" | grep -Eo -m 1 "vnet[0-9]+")" - sed -i '' "s|${_if}|${uniq_epair}|g" "${JAIL_CONFIG}" + local _if_vnet="$(grep ${_if} "${_rc_conf}" | grep -Eo -m 1 "vnet[0-9]+")" + sed -i '' "s|${_if}|${uniq_epair}|g" "${_jail_conf}" # If jail had a static MAC, generate one for clone - if grep ether ${JAIL_CONFIG} | grep -qoc ${uniq_epair}; then - local external_interface="$(grep ${uniq_epair} ${JAIL_CONFIG} | grep -o 'addm.*' | awk '{print $3}' | sed 's/["|;]//g')" + if grep ether ${_jail_conf} | grep -qoc ${uniq_epair}; then + local external_interface="$(grep ${uniq_epair} ${_jail_conf} | grep -o 'addm.*' | awk '{print $3}' | sed 's/["|;]//g')" generate_static_mac "${NEWNAME}" "${external_interface}" - sed -i '' "s|${uniq_epair} ether.*:.*:.*:.*:.*:.*a\";|${uniq_epair} ether ${macaddr}a\";|" "${JAIL_CONFIG}" - sed -i '' "s|${uniq_epair} ether.*:.*:.*:.*:.*:.*b\";|${uniq_epair} ether ${macaddr}b\";|" "${JAIL_CONFIG}" + sed -i '' "s|${uniq_epair} ether.*:.*:.*:.*:.*:.*a\";|${uniq_epair} ether ${macaddr}a\";|" "${_jail_conf}" + sed -i '' "s|${uniq_epair} ether.*:.*:.*:.*:.*:.*b\";|${uniq_epair} ether ${macaddr}b\";|" "${_jail_conf}" fi - sed -i '' "s|vnet host interface for Bastille jail ${TARGET}|vnet host interface for Bastille jail ${NEWNAME}|g" "${JAIL_CONFIG}" + sed -i '' "s|vnet host interface for Bastille jail ${TARGET}|vnet host interface for Bastille jail ${NEWNAME}|g" "${_jail_conf}" # Update /etc/rc.conf - sed -i '' "s|ifconfig_e0b_${_if}_name|ifconfig_e0b_${uniq_epair}_name|" "${bastille_jail_rc_conf}" - if grep "vnet0" "${bastille_jail_rc_conf}" | grep -q ${uniq_epair}; then + sed -i '' "s|ifconfig_e0b_${_if}_name|ifconfig_e0b_${uniq_epair}_name|" "${_rc_conf}" + if grep "vnet0" "${_rc_conf}" | grep -q ${uniq_epair}; then if [ "${IP}" = "0.0.0.0" ]; then - sysrc -f "${bastille_jail_rc_conf}" ifconfig_vnet0="SYNCDHCP" + sysrc -f "${_rc_conf}" ifconfig_vnet0="SYNCDHCP" else - sysrc -f "${bastille_jail_rc_conf}" ifconfig_vnet0=" inet ${IP} " + sysrc -f "${_rc_conf}" ifconfig_vnet0=" inet ${IP} " fi else - sysrc -f "${bastille_jail_rc_conf}" ifconfig_${_if_vnet}="SYNCDHCP" + sysrc -f "${_rc_conf}" ifconfig_${_if_vnet}="SYNCDHCP" fi break fi @@ -178,20 +274,21 @@ update_jailconf_vnet() { done } -update_fstab() { - # Update fstab to use the new name - FSTAB_CONFIG="${bastille_jailsdir}/${NEWNAME}/fstab" - if [ -f "${FSTAB_CONFIG}" ]; then - # Update additional fstab paths with new jail path - sed -i '' "s|${bastille_jailsdir}/${TARGET}/root/|${bastille_jailsdir}/${NEWNAME}/root/|" "${FSTAB_CONFIG}" - fi -} - clone_jail() { - # Attempt container clone - info "Attempting to clone '${TARGET}' to ${NEWNAME}..." + + info "Attempting to clone ${TARGET} to ${NEWNAME}..." + if ! [ -d "${bastille_jailsdir}/${NEWNAME}" ]; then if checkyesno bastille_zfs_enable; then + if [ "${LIVE}" -eq 1 ]; then + check_target_is_running "${TARGET}" || error_exit "[-l|--live] can only be used with a running jail." + else check_target_is_stopped "${TARGET}" || if [ "${FORCE}" -eq 1 ]; then + bastille stop "${TARGET}" + else + error_notify "Jail is running." + error_exit "Use [-f|--force] to force stop the jail, or [-l|--live] (ZFS only) to clone a running jail." + fi + fi if [ -n "${bastille_zfs_zpool}" ]; then # Replicate the existing container DATE=$(date +%F-%H%M%S) @@ -207,13 +304,13 @@ clone_jail() { zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NEWNAME}@bastille_clone_${DATE}" fi else - # Just clone the jail directory - # Check if container is running - if [ -n "$(/usr/sbin/jls name | awk "/^${TARGET}$/")" ]; then - error_exit "${TARGET} is running. See 'bastille stop ${TARGET}'." + # Perform container file copy (archive mode) + check_target_is_stopped "${TARGET}" || if [ "${FORCE}" -eq 1 ]; then + bastille stop "${TARGET}" + else + error_notify "Jail is running." + error_exit "Use [-f|--force] to force stop the jail." fi - - # Perform container file copy(archive mode) cp -a "${bastille_jailsdir}/${TARGET}" "${bastille_jailsdir}/${NEWNAME}" fi else @@ -222,7 +319,7 @@ clone_jail() { # Generate jail configuration files update_jailconf - update_fstab + update_fstab "${TARGET}" "${NEWNAME}" # Display the exist status if [ "$?" -ne 0 ]; then @@ -230,14 +327,15 @@ clone_jail() { else info "Cloned '${TARGET}' to '${NEWNAME}' successfully." fi + if [ "${AUTO}" -eq 1 ]; then + if [ "${LIVE}" -eq 0 ]; then + bastille start "${TARGET}" + fi + bastille start "${NEWNAME}" + fi } -## don't allow for dots(.) in container names -if echo "${NEWNAME}" | grep -q "[.]"; then - error_exit "Container names may not contain a dot(.)!" -fi - -## check if ip address is valid +# Check if IP address is valid. if [ -n "${IP}" ]; then validate_ip else From c26d470a7d681b8fe3074868949133547db88dce Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 18 Jan 2025 17:02:19 -0700 Subject: [PATCH 21/46] rename: Allow if=name --- usr/local/share/bastille/rename.sh | 154 +++++++++++++++++++++-------- 1 file changed, 113 insertions(+), 41 deletions(-) diff --git a/usr/local/share/bastille/rename.sh b/usr/local/share/bastille/rename.sh index 20fb8021..52e206f7 100644 --- a/usr/local/share/bastille/rename.sh +++ b/usr/local/share/bastille/rename.sh @@ -1,8 +1,6 @@ #!/bin/sh # -# SPDX-License-Identifier: BSD-3-Clause -# -# Copyright (c) 2018-2025, Christer Edwards +# Copyright (c) 2018-2024, Christer Edwards # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -34,11 +32,62 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille rename TARGET NEW_NAME" + error_notify "Usage: bastille rename [option(s)] TARGET NEW_NAME" + cat << EOF + Options: + + -a | --auto Auto mode. Start/stop jail(s) if required. + -x | --debug Enable debug mode. + +EOF + exit 1 } +# Handle options. +AUTO=0 +while [ "$#" -gt 0 ]; do + case "${1}" in + -h|--help|help) + usage + ;; + -a|--auto) + AUTO=1 + shift + ;; + -*) + for _opt in $(echo ${1} | sed 's/-//g' | fold -w1); do + case ${_opt} in + a) AUTO=1 ;; + x) enable_debug ;; + *) error_exit "Unknown Option: \"${1}\"" + esac + done + shift + ;; + *) + break + ;; + esac +done + +if [ "$#" -ne 2 ]; then + usage +fi + +TARGET="${1}" +NEWNAME="${2}" + +bastille_root_check +set_target_single "${TARGET}" +check_target_is_stopped "${TARGET}" || if [ "${AUTO}" -eq 1 ]; then + bastille stop "${TARGET}" +else + error_notify "Jail is running." + error_exit "Use [-a|--auto] to auto-stop the jail." +fi + validate_name() { - local NAME_VERIFY=${NEWNAME} + local NAME_VERIFY="${NEWNAME}" local NAME_SANITY="$(echo "${NAME_VERIFY}" | tr -c -d 'a-zA-Z0-9-_')" if [ -n "$(echo "${NAME_SANITY}" | awk "/^[-_].*$/" )" ]; then error_exit "Container names may not begin with (-|_) characters!" @@ -47,44 +96,64 @@ validate_name() { fi } -# Handle special-case commands first -case "$1" in -help|-h|--help) - usage - ;; -esac - -if [ $# -ne 1 ]; then - usage -fi - -bastille_root_check - -NEWNAME="${1}" - update_jailconf() { # Update jail.conf - JAIL_CONFIG="${bastille_jailsdir}/${NEWNAME}/jail.conf" - if [ -f "${JAIL_CONFIG}" ]; then - if ! grep -qw "path = ${bastille_jailsdir}/${NEWNAME}/root;" "${JAIL_CONFIG}"; then - sed -i '' "s|host.hostname.*=.*${TARGET};|host.hostname = ${NEWNAME};|" "${JAIL_CONFIG}" - sed -i '' "s|exec.consolelog.*=.*;|exec.consolelog = ${bastille_logsdir}/${NEWNAME}_console.log;|" "${JAIL_CONFIG}" - sed -i '' "s|path.*=.*;|path = ${bastille_jailsdir}/${NEWNAME}/root;|" "${JAIL_CONFIG}" - sed -i '' "s|mount.fstab.*=.*;|mount.fstab = ${bastille_jailsdir}/${NEWNAME}/fstab;|" "${JAIL_CONFIG}" - sed -i '' "s|${TARGET}.*{|${NEWNAME} {|" "${JAIL_CONFIG}" - # Rename vnet interface - sed -i '' "/vnet.interface/s|_${TARGET}\";|_${NEWNAME}\";|" "${JAIL_CONFIG}" - sed -i '' "/ifconfig/s|_${TARGET}|_${NEWNAME}|" "${JAIL_CONFIG}" + local _jail_conf="${bastille_jailsdir}/${NEWNAME}/jail.conf" + local _rc_conf="${bastille_jailsdir}/${NEWNAME}/root/etc/rc.conf" + if [ -f "${_jail_conf}" ]; then + if ! grep -qw "path = ${bastille_jailsdir}/${NEWNAME}/root;" "${_jail_conf}"; then + sed -i '' "s|host.hostname.*=.*${TARGET};|host.hostname = ${NEWNAME};|" "${_jail_conf}" + sed -i '' "s|exec.consolelog.*=.*;|exec.consolelog = ${bastille_logsdir}/${NEWNAME}_console.log;|" "${_jail_conf}" + sed -i '' "s|path.*=.*;|path = ${bastille_jailsdir}/${NEWNAME}/root;|" "${_jail_conf}" + sed -i '' "s|mount.fstab.*=.*;|mount.fstab = ${bastille_jailsdir}/${NEWNAME}/fstab;|" "${_jail_conf}" + sed -i '' "s|${TARGET}.*{|${NEWNAME} {|" "${_jail_conf}" + fi + if grep -qo "vnet;" "${_jail_conf}"; then + update_jailconf_vnet fi fi } -update_fstab() { - # Update fstab to use the new name - FSTAB_CONFIG="${bastille_jailsdir}/${NEWNAME}/fstab" - if [ -f "${FSTAB_CONFIG}" ]; then - sed -i '' "s|${bastille_jailsdir}/${TARGET}|${bastille_jailsdir}/${NEWNAME}|g" "${FSTAB_CONFIG}" - fi +update_jailconf_vnet() { + local _jail_conf="${bastille_jailsdir}/${NEWNAME}/jail.conf" + local _rc_conf="${bastille_jailsdir}/${NEWNAME}/root/etc/rc.conf" + # Change epair name (if needed) + local _if_list="$(grep -Eo 'epair[0-9]+|bastille[0-9]+' ${_jail_conf} | sort -u)" + for _if in ${_if_list}; do + if echo ${_if} | grep -Eoq 'epair[0-9]+'; then + # Check if epair name = jail name + local _epair_num="$(grep -Eo -m 1 "epair[0-9]+" "${_jail_conf}" | grep -Eo "[0-9]+")" + if grep -E "epair[0-9]+a" "${_jail_conf}" | grep -Eo "e[0-9]+a_${TARGET}"; then + local _target_host_epair="$(grep -Eo -m 1 "e[0-9]+a_${TARGET}" "${_jail_conf}")" + local _target_jail_epair="$(grep -Eo -m 1 "e[0-9]+b_${TARGET}" "${_jail_conf}")" + else + local _target_host_epair="$(grep -Eo -m 1 "epair[0-9]+a" "${_jail_conf}")" + local _target_jail_epair="$(grep -Eo -m 1 "epair[0-9]+b" "${_jail_conf}")" + fi + if [ "$(echo -n "e${_epair_num}a_${NEWNAME}" | awk '{print length}')" -lt 16 ]; then + # Generate new epair name + local _new_host_epair="e${_epair_num}a_${NEWNAME}" + local _new_jail_epair="e${_epair_num}b_${NEWNAME}" + else + local _new_host_epair="epair${_epair_num}a" + local _new_jail_epair="epair${_epair_num}b" + fi + # Replace host epair name in jail.conf + sed -i '' "s|up name ${_target_host_epair}|up name ${_new_host_epair}|g" "${_jail_conf}" + sed -i '' "s|${_target_host_epair} ether|${_new_host_epair} ether|g" "${_jail_conf}" + sed -i '' "s|deletem ${_target_host_epair}|deletem ${_new_host_epair}|g" "${_jail_conf}" + sed -i '' "s|${_target_host_epair} destroy|${_new_host_epair} destroy|g" "${_jail_conf}" + sed -i '' "s|${_target_host_epair} description|${_new_host_epair} description|g" "${_jail_conf}" + # Replace jail epair name in jail.conf + sed -i '' "s|= ${_target_jail_epair};|= ${_new_jail_epair};|g" "${_jail_conf}" + sed -i '' "s|up name ${_target_jail_epair}|up name ${_new_jail_epair}|g" "${_jail_conf}" + sed -i '' "s|${_target_jail_epair} ether|${_new_jail_epair} ether|g" "${_jail_conf}" + # Replace epair description + sed -i '' "s|vnet host interface for Bastille jail ${TARGET}|vnet host interface for Bastille jail ${NEWNAME}|g" "${_jail_conf}" + # Replace epair name in /etc/rc.conf + sed -i '' "/ifconfig/ s|${_target_jail_epair}|${_new_jail_epair}|g" "${_rc_conf}" + fi + done } change_name() { @@ -124,24 +193,27 @@ change_name() { fi fi - # Update jail configuration files accordingly + # Update jail conf files update_jailconf - update_fstab + update_fstab "${TARGET}" "${NEWNAME}" # Check exit status and notify if [ "$?" -ne 0 ]; then error_exit "An error has occurred while attempting to rename '${TARGET}'." else info "Renamed '${TARGET}' to '${NEWNAME}' successfully." + if [ "${AUTO}" -eq 1 ]; then + bastille start "${NEWNAME}" + fi fi } -## validate jail name +# Validate NEW_NAME if [ -n "${NEWNAME}" ]; then validate_name fi -## check if a jail already exists with the new name +# Check if a jail already exists with NEW_NAME if [ -d "${bastille_jailsdir}/${NEWNAME}" ]; then error_exit "Jail: ${NEWNAME} already exists." fi From 3f9d903f10656d2f8add96132a4026b7e67321f7 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 18 Jan 2025 17:44:38 -0700 Subject: [PATCH 22/46] bastille: rename+clone > no options command --- usr/local/bin/bastille | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/local/bin/bastille b/usr/local/bin/bastille index ba831d07..04f28e8c 100755 --- a/usr/local/bin/bastille +++ b/usr/local/bin/bastille @@ -164,10 +164,10 @@ version|-v|--version) help|-h|--help) usage ;; -bootstrap|create|destroy|etcupdate|export|htop|import|list|mount|rdr|restart|setup|start|top|umount|update|upgrade|verify) +bootstrap|clone|create|destroy|etcupdate|export|htop|import|list|mount|rdr|rename|restart|setup|start|top|umount|update|upgrade|verify) # Nothing "extra" to do for these commands. -- cwells ;; -clone|config|cmd|console|convert|cp|edit|limits|pkg|rcp|rename|service|stop|sysrc|tags|template|zfs) +config|cmd|console|convert|cp|edit|limits|pkg|rcp|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' From abdf885c3a738c357772b416a51f146ea1cc631b Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 18 Jan 2025 17:47:13 -0700 Subject: [PATCH 23/46] common: Comment about MAC prefix --- usr/local/share/bastille/common.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/usr/local/share/bastille/common.sh b/usr/local/share/bastille/common.sh index 1f4e2fa0..8c7e57e9 100644 --- a/usr/local/share/bastille/common.sh +++ b/usr/local/share/bastille/common.sh @@ -228,6 +228,7 @@ 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 prefix for jail MAC prefix local macaddr_prefix="58:9c:fc" 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 @@ -363,4 +364,4 @@ checkyesno() { return 1 ;; esac -} +} \ No newline at end of file From 73b9b149319d1614bee646377f1d6b79a5b4f5a7 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 18 Jan 2025 22:10:23 -0700 Subject: [PATCH 24/46] clone: typos --- usr/local/share/bastille/clone.sh | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/usr/local/share/bastille/clone.sh b/usr/local/share/bastille/clone.sh index 290db01b..edb6c9f0 100644 --- a/usr/local/share/bastille/clone.sh +++ b/usr/local/share/bastille/clone.sh @@ -38,7 +38,7 @@ usage() { cat << EOF Options: - -a | --auto Auto mode. Start/stop jail(s) if required. Cannot be used with [-l|--live]. + -a | --auto Auto mode. Start/stop jail(s) if required. Cannot be used with [-l|--live]. -l | --live Clone a running jail. ZFS only. Jail must be running. Cannot be used with [-f|--force]. -x | --debug Enable debug mode. @@ -282,11 +282,11 @@ clone_jail() { if checkyesno bastille_zfs_enable; then if [ "${LIVE}" -eq 1 ]; then check_target_is_running "${TARGET}" || error_exit "[-l|--live] can only be used with a running jail." - else check_target_is_stopped "${TARGET}" || if [ "${FORCE}" -eq 1 ]; then + else check_target_is_stopped "${TARGET}" || if [ "${AUTO}" -eq 1 ]; then bastille stop "${TARGET}" else error_notify "Jail is running." - error_exit "Use [-f|--force] to force stop the jail, or [-l|--live] (ZFS only) to clone a running jail." + error_exit "Use [-a|--auto] to force stop the jail, or [-l|--live] (ZFS only) to clone a running jail." fi fi if [ -n "${bastille_zfs_zpool}" ]; then @@ -327,10 +327,7 @@ clone_jail() { else info "Cloned '${TARGET}' to '${NEWNAME}' successfully." fi - if [ "${AUTO}" -eq 1 ]; then - if [ "${LIVE}" -eq 0 ]; then - bastille start "${TARGET}" - fi + if [ "${AUTO}" -eq 1 ] || [ "${LIVE}" -eq 1 ]; then bastille start "${NEWNAME}" fi } From ef8a4e209fceb4d885ddd1d171427de8821a1d40 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 18 Jan 2025 22:14:13 -0700 Subject: [PATCH 25/46] clone: more typos --- usr/local/share/bastille/clone.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/local/share/bastille/clone.sh b/usr/local/share/bastille/clone.sh index edb6c9f0..6d2db6f8 100644 --- a/usr/local/share/bastille/clone.sh +++ b/usr/local/share/bastille/clone.sh @@ -305,11 +305,11 @@ clone_jail() { fi else # Perform container file copy (archive mode) - check_target_is_stopped "${TARGET}" || if [ "${FORCE}" -eq 1 ]; then + check_target_is_stopped "${TARGET}" || if [ "${AUTO}" -eq 1 ]; then bastille stop "${TARGET}" else error_notify "Jail is running." - error_exit "Use [-f|--force] to force stop the jail." + error_exit "Use [-a|--auto] to force stop the jail." fi cp -a "${bastille_jailsdir}/${TARGET}" "${bastille_jailsdir}/${NEWNAME}" fi From 1fc84bc16876116aa08df5848fbd03e4c6d54837 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sun, 19 Jan 2025 08:14:24 -0700 Subject: [PATCH 26/46] clone: Fix retrieving ext if MAC on bridge --- usr/local/share/bastille/clone.sh | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/usr/local/share/bastille/clone.sh b/usr/local/share/bastille/clone.sh index 6d2db6f8..c967cd85 100644 --- a/usr/local/share/bastille/clone.sh +++ b/usr/local/share/bastille/clone.sh @@ -205,8 +205,6 @@ update_jailconf_vnet() { _target_host_epair="${_if}a" _target_jail_epair="${_if}b" fi - # Replace epair name in jail.conf - sed -i '' "s|${_if}|epair${_num}|g" "${_jail_conf}" # Replace host epair name in jail.conf sed -i '' "s|up name ${_target_host_epair}|up name ${_new_host_epair}|g" "${_jail_conf}" sed -i '' "s|${_target_host_epair} ether|${_new_host_epair} ether|g" "${_jail_conf}" @@ -219,11 +217,14 @@ update_jailconf_vnet() { sed -i '' "s|${_target_jail_epair} ether|${_new_jail_epair} ether|g" "${_jail_conf}" # If jail had a static MAC, generate one for clone if grep -q ether ${_jail_conf}; then - local external_interface="$(grep "${_new_host_epair}" ${_jail_conf} | grep -o '[^ ]* addm' | awk '{print $1}')" + local external_interface="$(grep "epair${_num}a" ${_jail_conf} | grep -o '[^ ]* addm' | awk '{print $1}')" generate_static_mac "${NEWNAME}" "${external_interface}" sed -i '' "s|${_new_host_epair} ether.*:.*:.*:.*:.*:.*a\";|${_new_host_epair} ether ${macaddr}a\";|" "${_jail_conf}" sed -i '' "s|${_new_jail_epair} ether.*:.*:.*:.*:.*:.*b\";|${_new_jail_epair} ether ${macaddr}b\";|" "${_jail_conf}" fi + # Replace epair name in jail.conf + sed -i '' "s|${_if}|epair${_num}|g" "${_jail_conf}" + # Replace epair description sed -i '' "s|vnet host interface for Bastille jail ${TARGET}|vnet host interface for Bastille jail ${NEWNAME}|g" "${_jail_conf}" # Update /etc/rc.conf local _jail_vnet="$(grep ${_target_jail_epair} "${_rc_conf}" | grep -Eo -m 1 "vnet[0-9]+")" From c1537e2a206cdf1c7afcaafe00c1a538a72a2242 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sun, 19 Jan 2025 08:22:12 -0700 Subject: [PATCH 27/46] clone: Spacing -1 --- usr/local/share/bastille/clone.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/usr/local/share/bastille/clone.sh b/usr/local/share/bastille/clone.sh index c967cd85..283def73 100644 --- a/usr/local/share/bastille/clone.sh +++ b/usr/local/share/bastille/clone.sh @@ -222,9 +222,9 @@ update_jailconf_vnet() { sed -i '' "s|${_new_host_epair} ether.*:.*:.*:.*:.*:.*a\";|${_new_host_epair} ether ${macaddr}a\";|" "${_jail_conf}" sed -i '' "s|${_new_jail_epair} ether.*:.*:.*:.*:.*:.*b\";|${_new_jail_epair} ether ${macaddr}b\";|" "${_jail_conf}" fi - # Replace epair name in jail.conf - sed -i '' "s|${_if}|epair${_num}|g" "${_jail_conf}" - # Replace epair description + # Replace epair name in jail.conf + sed -i '' "s|${_if}|epair${_num}|g" "${_jail_conf}" + # Replace epair description sed -i '' "s|vnet host interface for Bastille jail ${TARGET}|vnet host interface for Bastille jail ${NEWNAME}|g" "${_jail_conf}" # Update /etc/rc.conf local _jail_vnet="$(grep ${_target_jail_epair} "${_rc_conf}" | grep -Eo -m 1 "vnet[0-9]+")" From 10c82964e3b75dc34a30f4aec9f9cbfc22678495 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sun, 19 Jan 2025 08:27:41 -0700 Subject: [PATCH 28/46] clone: Change MAC after IF --- usr/local/share/bastille/clone.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/local/share/bastille/clone.sh b/usr/local/share/bastille/clone.sh index 283def73..9c945f94 100644 --- a/usr/local/share/bastille/clone.sh +++ b/usr/local/share/bastille/clone.sh @@ -215,6 +215,8 @@ update_jailconf_vnet() { sed -i '' "s|= ${_target_jail_epair};|= ${_new_jail_epair};|g" "${_jail_conf}" sed -i '' "s|up name ${_target_jail_epair}|up name ${_new_jail_epair}|g" "${_jail_conf}" sed -i '' "s|${_target_jail_epair} ether|${_new_jail_epair} ether|g" "${_jail_conf}" + # Replace epair name in jail.conf + sed -i '' "s|${_if}|epair${_num}|g" "${_jail_conf}" # If jail had a static MAC, generate one for clone if grep -q ether ${_jail_conf}; then local external_interface="$(grep "epair${_num}a" ${_jail_conf} | grep -o '[^ ]* addm' | awk '{print $1}')" @@ -222,8 +224,6 @@ update_jailconf_vnet() { sed -i '' "s|${_new_host_epair} ether.*:.*:.*:.*:.*:.*a\";|${_new_host_epair} ether ${macaddr}a\";|" "${_jail_conf}" sed -i '' "s|${_new_jail_epair} ether.*:.*:.*:.*:.*:.*b\";|${_new_jail_epair} ether ${macaddr}b\";|" "${_jail_conf}" fi - # Replace epair name in jail.conf - sed -i '' "s|${_if}|epair${_num}|g" "${_jail_conf}" # Replace epair description sed -i '' "s|vnet host interface for Bastille jail ${TARGET}|vnet host interface for Bastille jail ${NEWNAME}|g" "${_jail_conf}" # Update /etc/rc.conf From 4fc9426bc129ac069b89b0e59adc93fc787fd380 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Mon, 20 Jan 2025 12:36:27 -0700 Subject: [PATCH 29/46] upgrade: let users know to restart and run install command again --- usr/local/share/bastille/upgrade.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/local/share/bastille/upgrade.sh b/usr/local/share/bastille/upgrade.sh index 2be09435..7df54354 100644 --- a/usr/local/share/bastille/upgrade.sh +++ b/usr/local/share/bastille/upgrade.sh @@ -152,7 +152,7 @@ jail_upgrade() { # Update "osrelease" entry inside jail.conf sed -i '' "/osrelease/ s|${_oldrelease}|${_newrelease}|g" "${bastille_jailsdir}/${TARGET}/jail.conf" echo - echo -e "${COLOR_YELLOW}Please run 'bastille upgrade ${TARGET} install' to finish installing updates.${COLOR_RESET}" + echo -e "${COLOR_YELLOW}Please run 'bastille upgrade ${TARGET} install', restart the jail, then run 'bastille upgrade ${TARGET} install' again to finish installing updates.${COLOR_RESET}" fi } From 0bf87b1980b7e2626036543077748d9b06829533 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:28:33 -0700 Subject: [PATCH 30/46] mount: Add debug mode --- usr/local/share/bastille/mount.sh | 33 ++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index 5895823a..9c7a450e 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -34,15 +34,34 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_exit "Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number]" + error_notify "Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number]" + cat << EOF + Options: + + -x | --debug Enable debug mode. + +EOF + exit 1 } -# Handle special-case commands first. -case "${1}" in - help|-h|--help) - usage - ;; -esac +# Handle options. +while [ "$#" -gt 0 ]; do + case "${1}" in + -h|--help|help) + usage + ;; + -x|--debug) + enable_debug + shift + ;; + -*) + error_exit "Unknown Option: \"${1}\"" + ;; + *) + break + ;; + esac +done if [ "$#" -lt 3 ] || [ "$#" -gt 7 ]; then usage From caee31a62118244776d4e20b89363ff4dd4db58e Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:30:23 -0700 Subject: [PATCH 31/46] mount: Spacing --- usr/local/share/bastille/mount.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index 9c7a450e..5aa624f6 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -47,9 +47,9 @@ EOF # Handle options. while [ "$#" -gt 0 ]; do case "${1}" in - -h|--help|help) - usage - ;; + -h|--help|help) + usage + ;; -x|--debug) enable_debug shift From 391abe2335c35158d8f53eeff5df689e05383b31 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:32:35 -0700 Subject: [PATCH 32/46] mount: just change "must start with" to "must include" for documentation --- usr/local/share/bastille/mount.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index 5aa624f6..4ff5d88c 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -110,7 +110,7 @@ elif [ ! -e "${_hostpath}" ] || [ "${_type}" != "nullfs" ]; then usage fi -# Mount permissions,options must start with one of "ro, rw, rq, sw, xx" +# Mount permissions,options must include one of "ro, rw, rq, sw, xx" if ! echo "${_perms}" | grep -Eq '(ro|rw|rq|sw|xx)(,.*)?$'; then error_notify "Detected invalid mount permissions in FSTAB." warn "Format: /host/path /jail/path nullfs ro 0 0" From a49d58124d4da40a6e3cd48caddbdf65a2c6215a Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:33:20 -0700 Subject: [PATCH 33/46] docs: Mount fix --- docs/chapters/subcommands/mount.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/chapters/subcommands/mount.rst b/docs/chapters/subcommands/mount.rst index 172799ad..2f37f47b 100644 --- a/docs/chapters/subcommands/mount.rst +++ b/docs/chapters/subcommands/mount.rst @@ -10,7 +10,7 @@ Syntax follows standard `/etc/fstab` format: Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number] -The 'options' string can include a comma-separated list of mount options, but must start with one of (rw,ro,rq,sw,xx) according to fstab documentation. +The 'options' string can include a comma-separated list of mount options, but must include one of (rw,ro,rq,sw,xx) according to fstab documentation. Example: Mount a tmpfs filesystem with options. .. code-block:: shell From c27f0a7408374f5c66fcf7a7e61ea2582819a515 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:36:54 -0700 Subject: [PATCH 34/46] mount: Add options block to notify message --- usr/local/share/bastille/mount.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index 4ff5d88c..db69638d 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -34,7 +34,7 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_notify "Usage: bastille mount TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number]" + error_notify "Usage: bastille mount [option(s)] TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number]" cat << EOF Options: From 90777ca37e52517c3dfa53a9935cbd2b111c0601 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:38:47 -0700 Subject: [PATCH 35/46] mount: Stay consistent with "Unknown Option." --- usr/local/share/bastille/mount.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index db69638d..d9281696 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -55,7 +55,7 @@ while [ "$#" -gt 0 ]; do shift ;; -*) - error_exit "Unknown Option: \"${1}\"" + error_exit "Unknown Option." ;; *) break From c790e65f13fb9c61fb95a993dce651a987f1a2c2 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:42:39 -0700 Subject: [PATCH 36/46] mount: Call usage on unknown option --- usr/local/share/bastille/mount.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index d9281696..7903eedb 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -54,8 +54,9 @@ while [ "$#" -gt 0 ]; do enable_debug shift ;; - -*) - error_exit "Unknown Option." + --*|-*) + error_notify "Unknown Option." + usage ;; *) break From dee9ab08652a3af6ec3ab57bbe825a7f0bb58813 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:52:16 -0700 Subject: [PATCH 37/46] mount: Remove debug option for later addition --- usr/local/share/bastille/mount.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index 7903eedb..b9b2da2c 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -50,10 +50,6 @@ while [ "$#" -gt 0 ]; do -h|--help|help) usage ;; - -x|--debug) - enable_debug - shift - ;; --*|-*) error_notify "Unknown Option." usage From f0072cebbd1324dcee819931df85074bedba168c Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Mon, 20 Jan 2025 18:53:04 -0700 Subject: [PATCH 38/46] mount: Do not show debug as an option --- usr/local/share/bastille/mount.sh | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/usr/local/share/bastille/mount.sh b/usr/local/share/bastille/mount.sh index b9b2da2c..e2c060e6 100644 --- a/usr/local/share/bastille/mount.sh +++ b/usr/local/share/bastille/mount.sh @@ -34,14 +34,7 @@ . /usr/local/etc/bastille/bastille.conf usage() { - error_notify "Usage: bastille mount [option(s)] TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number]" - cat << EOF - Options: - - -x | --debug Enable debug mode. - -EOF - exit 1 + error_exit "Usage: bastille mount [option(s)] TARGET HOST_PATH JAIL_PATH [filesystem_type options dump pass_number]" } # Handle options. From af78f2b74d1ff9696ff59c22dfb82b4372f6b633 Mon Sep 17 00:00:00 2001 From: JRGTH Date: Tue, 21 Jan 2025 05:55:37 -0400 Subject: [PATCH 39/46] Initial 'bastille setup' cmd rewrite/enhancements This PR will add the Initial 'bastille setup' command rewrite and enhancements, includes the ZFS activation helper, also further enhancements will be added accordingly. Further testing/bug reporting welcome. --- usr/local/share/bastille/setup.sh | 879 +++++++++++++++++++++++++++--- 1 file changed, 806 insertions(+), 73 deletions(-) diff --git a/usr/local/share/bastille/setup.sh b/usr/local/share/bastille/setup.sh index 020d2cf4..bd22d4fa 100644 --- a/usr/local/share/bastille/setup.sh +++ b/usr/local/share/bastille/setup.sh @@ -1,7 +1,5 @@ #!/bin/sh # -# SPDX-License-Identifier: BSD-3-Clause -# # Copyright (c) 2018-2025, Christer Edwards # All rights reserved. # @@ -30,41 +28,410 @@ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -bastille_config="/usr/local/etc/bastille/bastille.conf" +# Let's set some predefined/fallback variables. +bastille_config_path="/usr/local/etc/bastille" +bastille_config="${bastille_config_path}/bastille.conf" +bastille_prefix_default="/usr/local/bastille" +bastille_zfsprefix_default="bastille" +bastille_ifbridge_name="bastille1" + . /usr/local/share/bastille/common.sh # shellcheck source=/usr/local/etc/bastille/bastille.conf . ${bastille_config} usage() { - error_exit "Usage: bastille setup [pf|network|zfs|vnet]" + # Build an independent usage for the `setup` command. + # No short options here for the special purpose --long-options, + # so we can reserve short options for future adds, also the user + # must genuinely agreed on configuration reset/restore so let them type for it. + error_notify "Usage: bastille setup [option]" + + cat << EOF + Options: + + -p | --firewall -- Attempt to configure bastille PF firewall. + -n | --network -- Attempt to configure network loopback interface. + -v | --vnet -- Attempt to configure VNET bridge interface [bastille1]. + -z | --zfs -- Activates ZFS storage features and benefits for bastille. + --conf-network-reset -- Restore bastille default Network options on the config file. + --conf-storage-reset -- Restore bastille default ZFS storage options on the config file. + --conf-restore-clean -- Restore bastille default config file from bastille.conf.sample file. + +EOF + exit 1 } -# Check for too many args +input_error() { + error_exit "Invalid user input, aborting!" +} + +config_runtime() { + # Run here variables considered to be required by bastille by default silently. + if ! sysrc -qn bastille_enable | grep -qi "yes"; then + sysrc bastille_enable="YES" >/dev/null 2>&1 + fi +} + +# Check for too many args. if [ $# -gt 1 ]; then usage fi -# Configure bastille loopback network interface -configure_network() { - info "Configuring ${bastille_network_loopback} loopback interface" - sysrc cloned_interfaces+=lo1 - sysrc ifconfig_lo1_name="${bastille_network_loopback}" +# Handle special-case commands first. +case "${1}" in + help|--help|-h) + usage + ;; +esac - info "Bringing up new interface: ${bastille_network_loopback}" +user_canceled() { + # Don't use 'error_exit' here as this only should inform the user, not panic them. + info "Cancelled by user, exiting!" + exit 1 +} + +config_backup() { + # Create bastille configuration backup with system time appended. + # This should be called each time `bastille setup` attempts to + # write to bastille configuration file. + BACKUP_DATE=$(date +%Y%m%d-%H%M%S) + cp "${bastille_config}" "${bastille_config}.${BACKUP_DATE}" + BACKUP_NAME="${bastille_config}.${BACKUP_DATE}" + info "Config backup created in: [${BACKUP_NAME}]" +} + +config_network_reset() { + # Restore bastille default network options. + warn "Performing Network configuration reset, requested by the user..." + read -p "$(warn "Do you really want to reset 'bastille' network configuration? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + local VAR_ITEMS="bastille_network_loopback=bastille0 bastille_network_pf_ext_if=ext_if + bastille_network_pf_table=jails bastille_network_shared= bastille_network_gateway= bastille_network_gateway6=" + for _item in ${VAR_ITEMS}; do + sysrc -f "${bastille_config}" ${_item} + done + info "Network configuration has been reset successfully!" + exit 0 + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac +} + +config_storage_reset() { + # Restore bastille default ZFS storage options. + warn "Performing ZFS configuration reset, requested by the user..." + read -p "$(warn "Do you really want to reset 'bastille' ZFS configuration? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + local VAR_ITEMS="bastille_zfs_enable= bastille_zfs_zpool= bastille_zfs_prefix=bastille" + for _item in ${VAR_ITEMS}; do + sysrc -f "${bastille_config}" ${_item} + done + + # Let's configure variables with complex values individually to keep it simple/readable for everyone. + sysrc -f "${bastille_config}" bastille_zfs_options="-o compress=lz4 -o atime=off" + sysrc -f "${bastille_config}" bastille_prefix="${bastille_prefix_default}" + info "ZFS configuration has been reset successfully!" + exit 0 + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac +} + +config_restore_global() { + local _response + # This will restore bastille default configuration file from the sample config file. + # Be aware that if the sample configuration file is missing, we can generate a new one, + # but that's highly unlikely to happen so will keep the code smaller here. + warn "Performing Bastille default configuration restore, requested by the user..." + read -p "$(warn "Do you really want to restore 'bastille' default configuration file and start over? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + config_backup + if [ -f "${bastille_config}.sample" ]; then + mv "${bastille_config}" "${bastille_config}.${BACKUP_DATE}" + cp "${bastille_config}.sample" "${bastille_config}" + else + error_exit "Bastille sample configuration file is missing, exiting." + fi + info "Bastille configuration file restored successfully!" + exit 0 + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac +} + +get_zfs_params() { + info "Reading on-disk and bastille ZFS config parameters..." + # Always try to detect and recover on-disk ZFS bastille configuration first. + # Bastille ZFS prefix is always set to "bastille" in the config file by default, + # so will keep things simple here, or considered custom setup if this variable is changed. + BARTILLE_ROOTFS=$(mount | awk '/ \/ / {print $1}') + BASTILLE_UFSBOOT= + BASTILLE_ZFSPOOL= + BASTILLE_PREFIXDEF= + BASTILLE_ZFSENABLE= + BASTILLE_PREFIX_MATCH= + + # Check if the system boots from ZFS. + if echo "${BARTILLE_ROOTFS}" | grep -q -m 1 -E "^/dev/"; then + # Assume the host is running from UFS. + info "This system doesn't boot from ZFS, looking for alternate configuration." + BASTILLE_UFSBOOT="1" + fi + + BASTILLE_PREFIXCONF=$(sysrc -qn -f "${bastille_config}" bastille_prefix) + BASTILLE_PREFIXZFS=$(sysrc -qn -f "${bastille_config}" bastille_zfs_prefix) + + if [ -z "${BASTILLE_PREFIXZFS}" ]; then + BASTILLE_PREFIXZFS="${bastille_zfsprefix_default}" + fi + + if [ -z "${BASTILLE_UFSBOOT}" ]; then + if [ "${BASTILLE_PREFIXZFS}" != "${bastille_zfsprefix_default}" ]; then + BASTILLE_CUSTOM_CONFIG="1" + fi + fi + + # Try to determine "zroot" pool name as it may happens that the user + # customized the "zroot" pool name during the initial FreeBSD installation. + if [ -z "${BASTILLE_UFSBOOT}" ]; then + #BASTILLE_ZFSPOOL=$(df ${bastille_config_path} 2>/dev/null | sed 1d | awk -F '/' '{print $1}') + BASTILLE_ZFSPOOL=$(zfs list -H ${bastille_config_path} 2>/dev/null | awk -F '/' '{print $1}') + fi + + if [ -z "${BASTILLE_UFSBOOT}" ]; then + BASTILLE_PREFIXDEF=$(zfs list -H "${BASTILLE_ZFSPOOL}/${BASTILLE_PREFIXZFS}" 2>/dev/null | awk '{print $5}') + fi + + if [ -n "${BASTILLE_UFSBOOT}" ]; then + # Make sure bastille_prefix is listed by ZFS then try to get bastille_zfs_pool from it. + # Make some additional checks for non ZFS boot systems, also rely on some 'bastille.conf' ZFS parameters. + BASTILLE_PREFIXLOOK=$(zfs list -H "${BASTILLE_PREFIXCONF}" 2>/dev/null | awk '{print $1}') + BASTILLE_ZFSPOOL=$(zfs list -H "${BASTILLE_PREFIXLOOK}" 2>/dev/null | awk -F '/' '{print $1}') + BASTILLE_PREFIXDEF=$(zfs list -H "${BASTILLE_PREFIXCONF}" 2>/dev/null | awk '{print $5}') + + else + # Fallback to default config. + if [ -z "${BASTILLE_PREFIXDEF}" ]; then + BASTILLE_PREFIXDEF="${bastille_prefix_default}" + fi + fi + + if [ "${BASTILLE_PREFIXDEF}" = "${BASTILLE_PREFIXCONF}" ]; then + BASTILLE_PREFIX_MATCH="1" + fi + + # Update 'bastille_prefix' if a custom dataset is detected while reading on-disk configuration. + if [ ! -d "${bastille_prefix}" ] || [ -n "${ZFS_DATASET_DETECT}" ] || [ -n "${BASTILLE_PREFIXDEF}" ]; then + BASTILLE_ZFSENABLE="YES" + bastille_prefix="${BASTILLE_PREFIXDEF}" + else + BASTILLE_ZFSENABLE="NO" + if [ -z "${BASTILLE_UFSBOOT}" ]; then + BASTILLE_PREFIXZFS="" + fi + fi +} + +config_validation(){ + # Perform a basic bastille ZFS configuration check, + if [ -d "${bastille_prefix}" ] && [ -n "${BASTILLE_PREFIX_MATCH}" ] && echo "${bastille_zfs_enable}" | grep -qi "yes" \ + && zfs list "${bastille_zfs_zpool}/${bastille_zfs_prefix}" >/dev/null 2>&1; then + info "Looks like Bastille ZFS storage features has been activated successfully!." + exit 0 + else + if [ ! -d "${bastille_prefix}" ] && [ -z "${BASTILLE_ZFSPOOL}" ]; then + zfs_initial_activation + else + if ! echo "${bastille_zfs_enable}" | grep -qi "no"; then + + # Inform the user bastille ZFS configuration has been tampered and/or on-disk ZFS config has changed. + error_exit "Bastille ZFS misconfiguration detected, please refer to 'bastille.conf' or see 'bastille setup --config-reset'." + fi + fi + fi +} + +show_zfs_params() { + # Show a brief info of the detected and/or pending bastille ZFS configuration parameters. + # Don't need to show bastille zfs enable as this will be enabled by default. + info "*************************************" + info "Bastille Storage Prefix: [${BASTILLE_PREFIXDEF}]" + info "Bastille ZFS Pool: [${BASTILLE_ZFSPOOL}]" + info "Bastille ZFS Prefix: [${BASTILLE_PREFIXZFS}]" + info "*************************************" +} + +write_zfs_opts() { + # Write/update to bastille config file the required and/or misssing parameters. + if [ -z "${bastille_prefix}" ] || [ "${BASTILLE_PREFIXDEF}" != "${bastille_prefix_default}" ]; then + if [ -z "${BASTILLE_PREFIX_MATCH}" ]; then + sysrc -f "${bastille_config}" bastille_prefix="${BASTILLE_PREFIXDEF}" + fi + else + if [ -z "${BASTILLE_PREFIXCONF}" ] && [ -n "${BASTILLE_PREFIXDEF}" ]; then + sysrc -f "${bastille_config}" bastille_prefix="${BASTILLE_PREFIXDEF}" + fi + fi + + if [ -z "${bastille_zfs_enable}" ]; then + sysrc -f "${bastille_config}" bastille_zfs_enable="${BASTILLE_ZFSENABLE}" + fi + if [ -z "${bastille_zfs_zpool}" ]; then + sysrc -f "${bastille_config}" bastille_zfs_zpool="${BASTILLE_ZFSPOOL}" + fi + if [ -z "${bastille_zfs_prefix}" ] || [ "${BASTILLE_PREFIXDEF}" != "${bastille_zfs_prefix}" ]; then + sysrc -f "${bastille_config}" bastille_zfs_prefix="${BASTILLE_PREFIXZFS}" + fi + info "ZFS has been enabled in bastille configuration successfully!" +} + +create_zfs_dataset(){ + info "Creating ZFS dataset [${BASTILLE_ZFSPOOL}/${BASTILLE_PREFIXZFS}] for bastille..." + + if [ -n "${BASTILLE_CONFIG_USER}" ]; then + bastille_prefix="${BASTILLE_PREFIXDEF}" + fi + + # shellcheck disable=SC1073 + if zfs list "${BASTILLE_ZFSPOOL}/${BASTILLE_PREFIXZFS}" >/dev/null 2>&1; then + info "Dataset ${BASTILLE_ZFSPOOL}/${BASTILLE_PREFIXZFS} already exist, skipping." + else + if ! zfs create -p "${bastille_zfs_options}" -o mountpoint="${bastille_prefix}" "${BASTILLE_ZFSPOOL}/${BASTILLE_PREFIXZFS}"; then + error_exit "Failed to create 'bastille_prefix' dataset, exiting." + fi + fi + chmod 0750 "${bastille_prefix}" + info "Bastille ZFS storage features has been activated successfully!" + exit 0 +} + +write_zfs_disable() { + # Explicitly disable ZFS in 'bastille_zfs_enable' + sysrc -f "${bastille_config}" bastille_zfs_enable="NO" + info "ZFS has been disabled in bastille configuration successfully!" +} + +write_zfs_enable() { + # Explicitly enable ZFS in 'bastille_zfs_enable' + # Just empty the 'bastille_zfs_enable' variable so the user can re-run the ZFS activation helper. + # Don't put "YES" here as it will trigger the ZFS validation and failing due missing and/or invalid configuration. + sysrc -f "${bastille_config}" bastille_zfs_enable="" + info "ZFS activation helper enabled!" +} + +zfs_initial_activation() { + local _response= + + # Just let the user interactively select the ZFS items manually from a list for the initial activation. + # This should be performed before `bastille bootstrap` as we already know. + info "Initial bastille ZFS activation helper invoked." + #read -p "$(info "Would you like bastille attempt to auto-detect/activate ZFS for you?, (assuming a standard install was performed) [y/N]: ")" _response + read -p "$(info "Would you like to configure the bastille ZFS options interactively? [y/N]: ")" _response + + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + # Assume the user knows what hes/she doing and want to configure ZFS parameters interactively. + configure_zfs_manually + ;; + [Nn]|[Nn][Oo]) + # Assume the user will manually edit the ZFS parameters in the config file. + user_canceled + ;; + *) + input_error + ;; + esac +} + +configure_network() { + local _response + + # Configure bastille loopback network interface. + # This is an initial attempt to make this function interactive, + # however this may be enhanced in the future by advanced contributors in this topic. + info "This will attempt to configure the loopback network interface [${bastille_network_loopback}]." + read -p "$(warn "Would you like to configure the loopback network interface now? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + # shellcheck disable=SC2104 + break + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac + + info "Configuring ${bastille_network_loopback} loopback interface..." + if ! sysrc -qn cloned_interfaces | grep -qi "lo1"; then + sysrc cloned_interfaces+="lo1" + fi + if ! sysrc -qn ifconfig_lo1_name | grep -qi "${bastille_network_loopback}"; then + sysrc ifconfig_lo1_name="${bastille_network_loopback}" + fi + + info "Bringing up new interface: ${bastille_network_loopback}..." service netif cloneup } configure_vnet() { - info "Configuring bridge interface" - sysrc cloned_interfaces+=bridge1 - sysrc ifconfig_bridge1_name=bastille1 + local _response - info "Bringing up new interface: bastille1" + # This is an initial attempt to make this function interactive, + # however this may be enhanced in the future by advanced contributors in this topic. + info "This will attempt to configure the VNET bridge interface [${bastille_ifbridge_name}]." + read -p "$(warn "Would you like to configure the VNET bridge interface now? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + # shellcheck disable=SC2104 + break + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac + + info "Configuring bridge interface [${bastille_ifbridge_name}]..." + + if ! sysrc -qn cloned_interfaces | grep -qi "${bastille_ifbridge_name}"; then + sysrc cloned_interfaces+="${bastille_ifbridge_name}" + fi + if ! sysrc -qn ifconfig_bridge1_name | grep -qi "${bastille_ifbridge_name}"; then + sysrc ifconfig_bridge1_name="${bastille_ifbridge_name}" + fi + + info "Bringing up new interface: ${bastille_ifbridge_name}..." service netif cloneup - if [ ! -f /etc/devfs.rules ]; then - info "Creating bastille_vnet devfs.rules" + if [ ! -f "/etc/devfs.rules" ]; then + info "Creating bastille_vnet devfs.rules..." cat << EOF > /etc/devfs.rules +# Auto-generated file from `bastille setup` +# devfs configuration information + [bastille_vnet=13] add include \$devfsrules_hide_all add include \$devfsrules_unhide_basic @@ -73,22 +440,47 @@ add include \$devfsrules_jail add include \$devfsrules_jail_vnet add path 'bpf*' unhide EOF + else + warn "File [/etc/devfs.rules] already exist, skipping." + exit 1 fi + exit 0 } -# Configure pf firewall configure_pf() { -# shellcheck disable=SC2154 -if [ ! -f "${bastille_pf_conf}" ]; then - # shellcheck disable=SC3043 - local ext_if - ext_if=$(netstat -rn | awk '/default/ {print $4}' | head -n1) - info "Determined default network interface: ($ext_if)" - info "${bastille_pf_conf} does not exist: creating..." + local _response + + # Configure the PF firewall. + # This is an initial attempt to make this function interactive, + # however this may be enhanced in the future by advanced contributors in this topic. + info "This will attempt to configure the PF firewall parameters in [${bastille_pf_conf}]." + read -p "$(warn "Would you like to configure the PF firewall parameters now? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + # shellcheck disable=SC2104 + break + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac + + # shellcheck disable=SC2154 + if [ ! -f "${bastille_pf_conf}" ]; then + # shellcheck disable=SC3043 + local ext_if + ext_if=$(netstat -rn | awk '/default/ {print $4}' | head -n1) + info "Determined default network interface: ($ext_if)" + info "${bastille_pf_conf} does not exist: creating..." + + # Creating pf.conf file. + cat << EOF > "${bastille_pf_conf}" +# Auto-generated file from `bastille setup` +# packet filter configuration file - ## creating pf.conf - cat << EOF > "${bastille_pf_conf}" -## generated by bastille setup ext_if="$ext_if" set block-policy return @@ -104,58 +496,399 @@ pass out quick keep state antispoof for \$ext_if inet pass in inet proto tcp from any to any port ssh flags S/SA keep state EOF - sysrc pf_enable=YES - warn "pf ruleset created, please review ${bastille_pf_conf} and enable it using 'service pf start'." -else - error_exit "${bastille_pf_conf} already exists. Exiting." -fi + + if ! sysrc -qn pf_enable | grep -qi "yes"; then + sysrc pf_enable="YES" + fi + warn "The pf ruleset file has been created, please review '${bastille_pf_conf}' and enable it using 'service pf start'." + else + warn "${bastille_pf_conf} already exists, skipping." + exit 1 + fi + exit 0 } -# Configure ZFS configure_zfs() { - if [ ! "$(kldstat -m zfs)" ]; then - info "ZFS module not loaded; skipping..." + # Attempt to detect and setup either new or an existing bastille ZFS on-disk configuration. + # This is useful for new users to easily activate the bastille ZFS parameters on a standard installation, + # or to recover an existing on-disk ZFS bastille configuration in case the config file has been borked/reset by the user, + # also a config backup will be created each time the config needs to be modified in the following format: bastille.conf.YYYYMMDD-HHMMSS + # Be aware that the users now need to explicitly enable ZFS in the config file due later config file changes, failing to do so + # before initial `bastille bootstrap` will private the user from activating ZFS storage features without manual intervention. + + ZFS_DATASET_DETECT= + BASTILLE_CUSTOM_CONFIG= + BASTILLE_INITIAL_CONFIG= + local _response= + + if ! kldstat -qm zfs; then + warn "Looks like the ZFS module is not loaded." + warn "If this is not a dedicated ZFS system you can ignore this warning." + exit 1 else - ## attempt to determine bastille_zroot from `zpool list` - bastille_zroot=$(zpool list | grep -v NAME | awk '{print $1}') - if [ "$(echo "${bastille_zroot}" | wc -l)" -gt 1 ]; then - error_notify "Error: Multiple ZFS pools available:\n${bastille_zroot}" - error_notify "Set desired pool using \"sysrc -f ${bastille_config} bastille_zfs_zpool=ZPOOL_NAME\"" - error_exit "Don't forget to also enable ZFS using \"sysrc -f ${bastille_config} bastille_zfs_enable=YES\"" + # If the below statement becomes true, will assume that the user do not want ZFS activation at all regardless of the + # host filesystem, or the default configuration file has been changed officially and set to "NO" by default. + if echo "${bastille_zfs_enable}" | grep -qi "no"; then + info "Looks like Bastille ZFS has been disabled in 'bastille.conf', ZFS activation helper disabled." + read -p "$(warn "Would you like to enable the ZFS activation helper now? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + # Assume the user wants to configure the ZFS parameters. + if config_backup; then + write_zfs_enable + warn "Please run 'bastille setup' again or consult bastille.conf for further configuration." + exit 0 + else + error_exit "Config backup creation failed, exiting." + fi + ;; + [Nn]|[Nn][Oo]) + # Assume the user will manually configure the ZFS parameters, or skip ZFS configuration. + user_canceled + ;; + esac + else + # Attempt to detect if bastille was installed with sane defaults(ports/pkg) and hasn't been bootstrapped yet, + # then offer the user initial ZFS activation option to gain all of the ZFS storage features and benefits. + # This should be performed before `bastille` initial bootstrap because several ZFS datasets will be + # created/configured during the bootstrap process by default. + get_zfs_params + if [ ! -d "${bastille_prefix}" ] && [ -n "${BASTILLE_ZFSPOOL}" ]; then + if [ "${bastille_prefix}" = "${bastille_prefix_default}" ] && [ -z "${BASTILLE_CUSTOM_CONFIG}" ]; then + show_zfs_params + info "Looks like bastille has been installed and hasn't been bootstrapped yet." + read -p "$(warn "Would you like to activate ZFS now to get the features and benefits? [y/N]"): " _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + if [ -n "${BASTILLE_ZFSPOOL}" ]; then + info "Attempting to create a backup file of the current bastille.conf file..." + if config_backup; then + write_zfs_opts + create_zfs_dataset + else + error_exit "Config backup creation failed, exiting." + fi + else + error_exit "Unable to determine the [zroot] pool name, exiting" + fi + ;; + [Nn]|[Nn][Oo]) + info "Looks like you cancelled the ZFS activation." + # Offer the user option to disable ZFS in the configuration file. + # Maybe the user wants to use UFS or ZFS with legacy directories instead. + read -p "$(warn "Would you like to explicitly disable ZFS in the configuration file? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + if config_backup; then + # Assume the user want to skip ZFS configuration regardless. + write_zfs_disable + exit 0 + else + error_exit "Config backup creation failed, exiting." + fi + ;; + [Nn]|[Nn][Oo]) + # Assume the user will manually configure the ZFS parameters by itself. + user_canceled + ;; + *) + input_error + ;; + esac + ;; + *) + input_error + ;; + esac + else + config_validation + fi + else + if [ -d "${bastille_prefix}" ] && [ -z "${bastille_zfs_enable}" ] && [ -z "${bastille_zfs_zpool}" ] && [ -z "${BASTILLE_CUSTOM_CONFIG}" ] && [ -z "${BASTILLE_UFSBOOT}" ]; then + show_zfs_params + # This section is handy if the user has reset the bastille configuration file after a successful ZFS activation. + info "Looks like bastille has been bootstrapped already, but ZFS options are not configured." + info "Attempting to configure default ZFS options for you..." + if zfs list | grep -qw "${bastille_prefix}"; then + ZFS_DATASET_DETECT="1" + read -p "$(warn "Would you like to auto-configure the detected ZFS parameters now? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + if config_backup; then + write_zfs_opts + exit 0 + else + error_exit "Config backup creation failed, exiting." + fi + ;; + [Nn]|[Nn][Oo]) + # Assume the user will manually configure the ZFS parameters by itself. + user_canceled + ;; + *) + input_error + ;; + esac + else + if [ -d "${bastille_prefix}" ]; then + if [ ! "$(ls -A ${bastille_prefix})" ]; then + if ! zfs list | grep -qw "${bastille_prefix}"; then + # If the user want to use ZFS he/she need to remove/rename the existing 'bastille_prefix' directory manually. + # We do not want to cause existing data lost at all due end-user errors. + warn "Looks like bastille prefix is not a ZFS dataset, thus ZFS storage options are not required." + warn "Please refer to 'bastille.conf' and/or verify for alreay existing 'bastille_prefix' directory." + read -p "$(warn "Would you like to explicitly disable ZFS in the configuration file so we don't ask again? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + if config_backup; then + write_zfs_disable + exit 0 + else + error_exit "Config backup creation failed, exiting." + fi + ;; + [Nn]|[Nn][Oo]) + # Assume the user will manually configure the ZFS parameters by itself. + user_canceled + ;; + *) + input_error + ;; + esac + fi + else + error_exit "Looks like 'bastille_prefix' is not a ZFS dataset and is not empty, aborting." + fi + fi + fi + fi + if [ -n "${BASTILLE_CUSTOM_CONFIG}" ]; then + # Attempt to detect an existing on-disk bastille ZFS configuration and let the user interactively select the items manually from a list. + # This should be performed if the user has borked/reset the config file or in the event the setup detected an unusual/customized bastille install. + warn "A custom bastille ZFS configuration has been detected and/or unable to read ZFS configuration properly." + warn "Please refer to 'bastille.conf' config file and/or 'bastille setup -help' for additional info." + zfs_initial_activation + else + config_validation + fi + fi fi - sysrc -f "${bastille_config}" bastille_zfs_enable=YES - sysrc -f "${bastille_config}" bastille_zfs_zpool="${bastille_zroot}" fi } -# Run all base functions (w/o vnet) if no args -if [ $# -eq 0 ]; then - sysrc bastille_enable=YES - configure_network - configure_pf - configure_zfs -fi +configure_zfs_manually() { + BASTILLE_CONFIG_USER= + local ZFSPOOL_COUNT="0" + local ZFSDATA_COUNT="0" + local MPREFIX_COUNT="0" + local _zfsprefix_trim= + local _zfspool_choice= + local _zfspool_select= + local _zfsprefix_choice= + local _zfsprefix_select= + local _zfsmount_choice= + local _zfsmount_select= + local _response= -# Handle special-case commands first. -case "$1" in -help|-h|--help) - usage - ;; -pf|firewall) - configure_pf - ;; -bastille0) - # TODO remove in future release 0.13 - warn "'bastille setup bastille0' will be deprecated in the next 0.13 version." - configure_network - ;; -network|loopback) - configure_network - ;; -zfs|storage) - configure_zfs - ;; -bastille1|vnet|bridge) - configure_vnet - ;; + read -p "$(info "Would you like to configure the ZFS parameters entirely by hand? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + # We will assume the user knows what hes/she doing and want to configure ZFS parameters entirely by hand. + read -p "$(warn "Please enter the desired ZFS pool for bastille: ")" _zfspool_select + read -p "$(warn "Please enter the ZFS dataset for bastille: ")" _zfsprefix_select + read -p "$(warn "Please enter the ZFS mountpoint for bastille: ")" _zfsmount_select + + # Set the parameters and show the user a preview. + BASTILLE_PREFIXDEF="${_zfsmount_select}" + BASTILLE_ZFSPOOL="${_zfspool_select}" + BASTILLE_PREFIXZFS="${_zfsprefix_select}" + show_zfs_params + + # Ask again to make sure the user is confident with the entered parameters. + warn "Are you sure the above bastille ZFS configuration is correct?" + read -p "$(warn "Once bastille is activated it can't be easily undone, do you really want to activate ZFS now? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + BASTILLE_CONFIG_USER="1" + write_zfs_opts + create_zfs_dataset + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac + ;; + [Nn]|[Nn][Oo]) + # shellcheck disable=SC2104 + break + ;; + *) + input_error + ;; + esac + + # Ask here several times as we want the user to be really sure of what they doing, + # We do not want to cause existing data lost at all due end-user errors. + info "Listing available ZFS pools..." + bastille_zpool=$(zpool list -H | awk '{print $1}') + for _zpool in ${bastille_zpool}; do + echo "[${ZFSPOOL_COUNT}] ${_zpool}" + ZFSPOOL_NUM="${ZFSPOOL_NUM} [${ZFSPOOL_COUNT}]${_zpool}" + ZFSPOOL_COUNT=$(expr ${ZFSPOOL_COUNT} + 1) + done + + read -p "$(info "Please select the ZFS pool [NUM] for bastille: ")" _zfspool_choice + if ! echo "${_zfspool_choice}" | grep -Eq "^[0-9]{1,3}$"; then + error_exit "Invalid input number, aborting!" + else + _zfspool_select=$(echo "${ZFSPOOL_NUM}" | grep -wo "\[${_zfspool_choice}\][^ ]*" | sed 's/\[.*\]//g') + # If the user is unsure here, just abort as no input validation will be performed after. + if [ -z "${_zfspool_select}" ]; then + error_exit "No ZFS pool selected, aborting!" + else + info "Selected ZFS pool: [${_zfspool_select}]" + # Ask again to make sure the user is confident in his election. + read -p "$(warn "Are you sure '${_zfspool_select}' is the correct ZFS pool [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + # shellcheck disable=SC2104 + continue + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac + fi + fi + + # Ask on what zfs dataset `bastille` is installed. + info "Listing available ZFS datasets from the selected ZFS pool..." + bastille_zprefix=$(zfs list -H -r "${_zfspool_select}" | awk '{print $1}') + for _zprefix in ${bastille_zprefix}; do + echo "[${ZFSDATA_COUNT}] ${_zprefix}" + ZFSDATA_NUM="${ZFSDATA_NUM} [${ZFSDATA_COUNT}]${_zprefix}" + ZFSDATA_COUNT=$(expr ${ZFSDATA_COUNT} + 1) + done + read -p "$(info "Please select the ZFS dataset [NUM] for bastille: ")" _zfsprefix_choice + if ! echo "${_zfsprefix_choice}" | grep -Eq "^[0-9]{1,3}$"; then + error_exit "Invalid input number, aborting!" + else + _zfsprefix_select=$(echo "${ZFSDATA_NUM}" | grep -wo "\[${_zfsprefix_choice}\][^ ]*" | sed 's/\[.*\]//g') + if [ -z "${_zfsprefix_select}" ]; then + # If the user is unsure here, just abort as no input validation will be performed after. + error_exit "No ZFS dataset selected, aborting!" + else + _zfsprefix_select=$(echo ${ZFSDATA_NUM} | grep -wo "\[${_zfsprefix_choice}\][^ ]*" | sed 's/\[.*\]//g') + _zfsprefix_trim=$(echo ${ZFSDATA_NUM} | grep -wo "\[${_zfsprefix_choice}\][^ ]*" | awk -F "${_zfspool_select}/" 'NR==1{print $2}') + info "Selected ZFS prefix: [${_zfsprefix_select}]" + # Ask again to make sure the user is confident in his election. + read -p "$(warn "Are you sure '${_zfsprefix_select}' is the correct ZFS dataset [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + # shellcheck disable=SC2104 + continue + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac + fi + fi + + _zfsmount_select="${_zfsprefix_select}" + # Ask what zfs mountpoint `bastille` will use. + info "Listing ZFS mountpoints from the selected ZFS dataset: [${_zfsmount_select}]..." + bastille_prefix=$(zfs list -H "${_zfsmount_select}" | awk '{print $5}') + for _zfsmount_choice in ${bastille_prefix}; do + echo "[${MPREFIX_COUNT}] ${_zfsmount_choice}" + MPREFIX_NUM="${MPREFIX_NUM} [${MPREFIX_COUNT}]${_zfsmount_choice}" + MPREFIX_COUNT=$(expr ${MPREFIX_COUNT} + 1) + done + read -p "$(info "Please select the ZFS mountpoint [NUM] for bastille: ")" _zfsmount_choice + if ! echo "${_zfsmount_choice}" | grep -Eq "^[0-9]{1,3}$"; then + error_exit "Invalid input number, aborting!" + else + _zfsmount_select=$(echo ${MPREFIX_NUM} | grep -wo "\[${_zfsmount_choice}\][^ ]*" | sed 's/\[.*\]//g') + if [ -z "${_zfsmount_select}" ]; then + # If the user is unsure here, just abort as no input validation will be performed after. + error_exit "No ZFS mountpoint selected, aborting!" + else + info "Selected bastille storage mountpoint: [${_zfsmount_select}]" + # Ask again to make sure the user is confident in his election. + read -p "$(warn "Are you sure '${_zfsmount_select}' is the correct bastille prefix [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + # Set the parameters and show the user a preview. + BASTILLE_PREFIXDEF="${_zfsmount_select}" + BASTILLE_ZFSPOOL="${_zfspool_select}" + BASTILLE_PREFIXZFS="${_zfsprefix_trim}" + show_zfs_params + warn "Are you sure the above bastille ZFS configuration is correct?" + read -p "$(warn "Once bastille is activated it can't be easily undone, do you really want to activate ZFS now? [y/N]: ")" _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + write_zfs_opts + create_zfs_dataset + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac + fi + fi + +} + +# Runtime required variables. +config_runtime + +# Handle options one at a time per topic, we don't want users to select/process +# multiple options at once just to end with a broked and/or unwanted configuration. +case "${1}" in + --firewall|-p) + configure_pf + ;; + --network|-n|bastille0) + # TODO remove in future release 0.13 + warn "Notice: 'bastille setup bastille0' will be deprecated in the next 0.13 version." + configure_network + ;; + --vnet|-v|bridge) + configure_vnet + ;; + --zfs|-z) + configure_zfs + ;; + --conf-network-reset) + config_network_reset + ;; + --conf-storage-reset) + config_storage_reset + ;; + --conf-restore-clean) + config_restore_global + ;; + *) + usage + ;; esac From 6327d3654b6e3821d5f7e5b319052e30b3232567 Mon Sep 17 00:00:00 2001 From: JRGTH Date: Tue, 21 Jan 2025 06:25:44 -0400 Subject: [PATCH 40/46] Local shellcheck pass using shellcheck version: 0.8.0 --- usr/local/share/bastille/setup.sh | 75 ++++++++++++++++++++----------- 1 file changed, 48 insertions(+), 27 deletions(-) diff --git a/usr/local/share/bastille/setup.sh b/usr/local/share/bastille/setup.sh index bd22d4fa..f4993a15 100644 --- a/usr/local/share/bastille/setup.sh +++ b/usr/local/share/bastille/setup.sh @@ -103,7 +103,8 @@ config_backup() { config_network_reset() { # Restore bastille default network options. warn "Performing Network configuration reset, requested by the user..." - read -p "$(warn "Do you really want to reset 'bastille' network configuration? [y/N]: ")" _response + warn "Do you really want to reset 'bastille' network configuration? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) local VAR_ITEMS="bastille_network_loopback=bastille0 bastille_network_pf_ext_if=ext_if @@ -126,7 +127,8 @@ config_network_reset() { config_storage_reset() { # Restore bastille default ZFS storage options. warn "Performing ZFS configuration reset, requested by the user..." - read -p "$(warn "Do you really want to reset 'bastille' ZFS configuration? [y/N]: ")" _response + warn "Do you really want to reset 'bastille' ZFS configuration? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) local VAR_ITEMS="bastille_zfs_enable= bastille_zfs_zpool= bastille_zfs_prefix=bastille" @@ -155,7 +157,8 @@ config_restore_global() { # Be aware that if the sample configuration file is missing, we can generate a new one, # but that's highly unlikely to happen so will keep the code smaller here. warn "Performing Bastille default configuration restore, requested by the user..." - read -p "$(warn "Do you really want to restore 'bastille' default configuration file and start over? [y/N]: ")" _response + warn "Do you really want to restore 'bastille' default configuration file and start over? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) config_backup @@ -343,9 +346,8 @@ zfs_initial_activation() { # Just let the user interactively select the ZFS items manually from a list for the initial activation. # This should be performed before `bastille bootstrap` as we already know. info "Initial bastille ZFS activation helper invoked." - #read -p "$(info "Would you like bastille attempt to auto-detect/activate ZFS for you?, (assuming a standard install was performed) [y/N]: ")" _response - read -p "$(info "Would you like to configure the bastille ZFS options interactively? [y/N]: ")" _response - + info "Would you like to configure the bastille ZFS options interactively? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) # Assume the user knows what hes/she doing and want to configure ZFS parameters interactively. @@ -368,7 +370,8 @@ configure_network() { # This is an initial attempt to make this function interactive, # however this may be enhanced in the future by advanced contributors in this topic. info "This will attempt to configure the loopback network interface [${bastille_network_loopback}]." - read -p "$(warn "Would you like to configure the loopback network interface now? [y/N]: ")" _response + warn "Would you like to configure the loopback network interface now? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) # shellcheck disable=SC2104 @@ -400,7 +403,8 @@ configure_vnet() { # This is an initial attempt to make this function interactive, # however this may be enhanced in the future by advanced contributors in this topic. info "This will attempt to configure the VNET bridge interface [${bastille_ifbridge_name}]." - read -p "$(warn "Would you like to configure the VNET bridge interface now? [y/N]: ")" _response + warn "Would you like to configure the VNET bridge interface now? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) # shellcheck disable=SC2104 @@ -454,7 +458,8 @@ configure_pf() { # This is an initial attempt to make this function interactive, # however this may be enhanced in the future by advanced contributors in this topic. info "This will attempt to configure the PF firewall parameters in [${bastille_pf_conf}]." - read -p "$(warn "Would you like to configure the PF firewall parameters now? [y/N]: ")" _response + warn "Would you like to configure the PF firewall parameters now? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) # shellcheck disable=SC2104 @@ -518,7 +523,6 @@ configure_zfs() { ZFS_DATASET_DETECT= BASTILLE_CUSTOM_CONFIG= - BASTILLE_INITIAL_CONFIG= local _response= if ! kldstat -qm zfs; then @@ -530,7 +534,8 @@ configure_zfs() { # host filesystem, or the default configuration file has been changed officially and set to "NO" by default. if echo "${bastille_zfs_enable}" | grep -qi "no"; then info "Looks like Bastille ZFS has been disabled in 'bastille.conf', ZFS activation helper disabled." - read -p "$(warn "Would you like to enable the ZFS activation helper now? [y/N]: ")" _response + warn "Would you like to enable the ZFS activation helper now? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) # Assume the user wants to configure the ZFS parameters. @@ -557,7 +562,8 @@ configure_zfs() { if [ "${bastille_prefix}" = "${bastille_prefix_default}" ] && [ -z "${BASTILLE_CUSTOM_CONFIG}" ]; then show_zfs_params info "Looks like bastille has been installed and hasn't been bootstrapped yet." - read -p "$(warn "Would you like to activate ZFS now to get the features and benefits? [y/N]"): " _response + warn "Would you like to activate ZFS now to get the features and benefits? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) if [ -n "${BASTILLE_ZFSPOOL}" ]; then @@ -576,7 +582,8 @@ configure_zfs() { info "Looks like you cancelled the ZFS activation." # Offer the user option to disable ZFS in the configuration file. # Maybe the user wants to use UFS or ZFS with legacy directories instead. - read -p "$(warn "Would you like to explicitly disable ZFS in the configuration file? [y/N]: ")" _response + warn "Would you like to explicitly disable ZFS in the configuration file? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) if config_backup; then @@ -611,7 +618,8 @@ configure_zfs() { info "Attempting to configure default ZFS options for you..." if zfs list | grep -qw "${bastille_prefix}"; then ZFS_DATASET_DETECT="1" - read -p "$(warn "Would you like to auto-configure the detected ZFS parameters now? [y/N]: ")" _response + warn "Would you like to auto-configure the detected ZFS parameters now? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) if config_backup; then @@ -637,7 +645,8 @@ configure_zfs() { # We do not want to cause existing data lost at all due end-user errors. warn "Looks like bastille prefix is not a ZFS dataset, thus ZFS storage options are not required." warn "Please refer to 'bastille.conf' and/or verify for alreay existing 'bastille_prefix' directory." - read -p "$(warn "Would you like to explicitly disable ZFS in the configuration file so we don't ask again? [y/N]: ")" _response + warn "Would you like to explicitly disable ZFS in the configuration file so we don't ask again? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) if config_backup; then @@ -690,13 +699,17 @@ configure_zfs_manually() { local _zfsmount_select= local _response= - read -p "$(info "Would you like to configure the ZFS parameters entirely by hand? [y/N]: ")" _response + info "Would you like to configure the ZFS parameters entirely by hand? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) # We will assume the user knows what hes/she doing and want to configure ZFS parameters entirely by hand. - read -p "$(warn "Please enter the desired ZFS pool for bastille: ")" _zfspool_select - read -p "$(warn "Please enter the ZFS dataset for bastille: ")" _zfsprefix_select - read -p "$(warn "Please enter the ZFS mountpoint for bastille: ")" _zfsmount_select + warn "Please enter the desired ZFS pool for bastille: " + read _zfspool_select + warn "Please enter the ZFS dataset for bastille: " + read _zfsprefix_select + warn "Please enter the ZFS mountpoint for bastille: " + read _zfsmount_select # Set the parameters and show the user a preview. BASTILLE_PREFIXDEF="${_zfsmount_select}" @@ -706,7 +719,8 @@ configure_zfs_manually() { # Ask again to make sure the user is confident with the entered parameters. warn "Are you sure the above bastille ZFS configuration is correct?" - read -p "$(warn "Once bastille is activated it can't be easily undone, do you really want to activate ZFS now? [y/N]: ")" _response + warn "Once bastille is activated it can't be easily undone, do you really want to activate ZFS now? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) BASTILLE_CONFIG_USER="1" @@ -740,7 +754,8 @@ configure_zfs_manually() { ZFSPOOL_COUNT=$(expr ${ZFSPOOL_COUNT} + 1) done - read -p "$(info "Please select the ZFS pool [NUM] for bastille: ")" _zfspool_choice + info "Please select the ZFS pool [NUM] for bastille: " + read _zfspool_choice if ! echo "${_zfspool_choice}" | grep -Eq "^[0-9]{1,3}$"; then error_exit "Invalid input number, aborting!" else @@ -751,7 +766,8 @@ configure_zfs_manually() { else info "Selected ZFS pool: [${_zfspool_select}]" # Ask again to make sure the user is confident in his election. - read -p "$(warn "Are you sure '${_zfspool_select}' is the correct ZFS pool [y/N]: ")" _response + warn "Are you sure '${_zfspool_select}' is the correct ZFS pool [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) # shellcheck disable=SC2104 @@ -775,7 +791,8 @@ configure_zfs_manually() { ZFSDATA_NUM="${ZFSDATA_NUM} [${ZFSDATA_COUNT}]${_zprefix}" ZFSDATA_COUNT=$(expr ${ZFSDATA_COUNT} + 1) done - read -p "$(info "Please select the ZFS dataset [NUM] for bastille: ")" _zfsprefix_choice + info "Please select the ZFS dataset [NUM] for bastille: " + read _zfsprefix_choice if ! echo "${_zfsprefix_choice}" | grep -Eq "^[0-9]{1,3}$"; then error_exit "Invalid input number, aborting!" else @@ -788,7 +805,8 @@ configure_zfs_manually() { _zfsprefix_trim=$(echo ${ZFSDATA_NUM} | grep -wo "\[${_zfsprefix_choice}\][^ ]*" | awk -F "${_zfspool_select}/" 'NR==1{print $2}') info "Selected ZFS prefix: [${_zfsprefix_select}]" # Ask again to make sure the user is confident in his election. - read -p "$(warn "Are you sure '${_zfsprefix_select}' is the correct ZFS dataset [y/N]: ")" _response + warn "Are you sure '${_zfsprefix_select}' is the correct ZFS dataset [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) # shellcheck disable=SC2104 @@ -813,7 +831,8 @@ configure_zfs_manually() { MPREFIX_NUM="${MPREFIX_NUM} [${MPREFIX_COUNT}]${_zfsmount_choice}" MPREFIX_COUNT=$(expr ${MPREFIX_COUNT} + 1) done - read -p "$(info "Please select the ZFS mountpoint [NUM] for bastille: ")" _zfsmount_choice + info "Please select the ZFS mountpoint [NUM] for bastille: " + read _zfsmount_choice if ! echo "${_zfsmount_choice}" | grep -Eq "^[0-9]{1,3}$"; then error_exit "Invalid input number, aborting!" else @@ -824,7 +843,8 @@ configure_zfs_manually() { else info "Selected bastille storage mountpoint: [${_zfsmount_select}]" # Ask again to make sure the user is confident in his election. - read -p "$(warn "Are you sure '${_zfsmount_select}' is the correct bastille prefix [y/N]: ")" _response + warn "Are you sure '${_zfsmount_select}' is the correct bastille prefix [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) # Set the parameters and show the user a preview. @@ -833,7 +853,8 @@ configure_zfs_manually() { BASTILLE_PREFIXZFS="${_zfsprefix_trim}" show_zfs_params warn "Are you sure the above bastille ZFS configuration is correct?" - read -p "$(warn "Once bastille is activated it can't be easily undone, do you really want to activate ZFS now? [y/N]: ")" _response + warn "Once bastille is activated it can't be easily undone, do you really want to activate ZFS now? [y/N]: " + read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) write_zfs_opts From 848bec5fae898dcc6c77cf6a93b29ba22d82ae21 Mon Sep 17 00:00:00 2001 From: JRGTH Date: Tue, 21 Jan 2025 18:17:12 -0400 Subject: [PATCH 41/46] Add --ethernet option for bastille_network_shared Add '-e|--ethernet' option to configure `bastille_network_shared` physical interface, also add config backups to select partial variable resets as well. --- usr/local/share/bastille/setup.sh | 82 +++++++++++++++++++++++++++++-- 1 file changed, 79 insertions(+), 3 deletions(-) diff --git a/usr/local/share/bastille/setup.sh b/usr/local/share/bastille/setup.sh index f4993a15..6069f4c0 100644 --- a/usr/local/share/bastille/setup.sh +++ b/usr/local/share/bastille/setup.sh @@ -51,6 +51,7 @@ usage() { -p | --firewall -- Attempt to configure bastille PF firewall. -n | --network -- Attempt to configure network loopback interface. + -e | --ethernet -- Attempt to configure the network shared interface. -v | --vnet -- Attempt to configure VNET bridge interface [bastille1]. -z | --zfs -- Activates ZFS storage features and benefits for bastille. --conf-network-reset -- Restore bastille default Network options on the config file. @@ -107,6 +108,7 @@ config_network_reset() { read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) + config_backup local VAR_ITEMS="bastille_network_loopback=bastille0 bastille_network_pf_ext_if=ext_if bastille_network_pf_table=jails bastille_network_shared= bastille_network_gateway= bastille_network_gateway6=" for _item in ${VAR_ITEMS}; do @@ -131,6 +133,7 @@ config_storage_reset() { read _response case "${_response}" in [Yy]|[Yy][Ee][Ss]) + config_backup local VAR_ITEMS="bastille_zfs_enable= bastille_zfs_zpool= bastille_zfs_prefix=bastille" for _item in ${VAR_ITEMS}; do sysrc -f "${bastille_config}" ${_item} @@ -363,6 +366,76 @@ zfs_initial_activation() { esac } +configure_ethernet() { + # This will attempt to configure the physical ethernet interface for 'bastille_network_shared', + # commonly used with shared IP jails and/or simple jail network configurations. + + local ETHIF_COUNT="0" + local _ethernet_choice= + local _response= + + # Try to get a list of the available physical network/ethernet interfaces. + local ETHERNET_PHY_ADAPTERS=$(pciconf -lv | grep 'ethernet' -B4 | grep 'class=0x020000' | awk -F '@' '{print $1}') + if [ -z "${ETHERNET_PHY_ADAPTERS}" ]; then + error_exit "Unable to detect for any physical ethernet interfaces, exiting." + fi + + info "This will attempt to configure the physical ethernet interface for [bastille_network_shared]." + warn "Would you like to configure the physical ethernet interface now? [y/N]: " + read _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + # shellcheck disable=SC2104 + break + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac + + info "Listing available physical ethernet interfaces..." + for _ethernetif in ${ETHERNET_PHY_ADAPTERS}; do + echo "[${ETHIF_COUNT}] ${_ethernetif}" + ETHIF_NUM="${ETHIF_NUM} [${ETHIF_COUNT}]${_ethernetif}" + ETHIF_COUNT=$(expr ${ETHIF_COUNT} + 1) + done + + info "Please select the wanted physical ethernet adapter [NUM] to be used as 'bastille_network_shared': " + read _ethernet_choice + if ! echo "${_ethernet_choice}" | grep -Eq "^[0-9]{1,3}$"; then + error_exit "Invalid input number, aborting!" + else + _ethernet_select=$(echo "${ETHIF_NUM}" | grep -wo "\[${_ethernet_choice}\][^ ]*" | sed 's/\[.*\]//g') + # If the user is unsure here, just abort as no input validation will be performed after. + if [ -z "${_ethernet_select}" ]; then + error_exit "No physical ethernet interface selected, aborting!" + else + info "Selected physical ethernet interface: [${_ethernet_select}]" + # Ask again to make sure the user is confident with the election. + warn "Are you sure '${_ethernet_select}' is the correct physical ethernet interface [y/N]: " + read _response + case "${_response}" in + [Yy]|[Yy][Ee][Ss]) + if ! sysrc -f "${bastille_config}" bastille_network_shared | grep -qi "${_ethernet_select}"; then + config_backup + sysrc -f "${bastille_config}" bastille_network_shared="${_ethernet_select}" + fi + exit 0 + ;; + [Nn]|[Nn][Oo]) + user_canceled + ;; + *) + input_error + ;; + esac + fi + fi +} + configure_network() { local _response @@ -765,7 +838,7 @@ configure_zfs_manually() { error_exit "No ZFS pool selected, aborting!" else info "Selected ZFS pool: [${_zfspool_select}]" - # Ask again to make sure the user is confident in his election. + # Ask again to make sure the user is confident with the election. warn "Are you sure '${_zfspool_select}' is the correct ZFS pool [y/N]: " read _response case "${_response}" in @@ -804,7 +877,7 @@ configure_zfs_manually() { _zfsprefix_select=$(echo ${ZFSDATA_NUM} | grep -wo "\[${_zfsprefix_choice}\][^ ]*" | sed 's/\[.*\]//g') _zfsprefix_trim=$(echo ${ZFSDATA_NUM} | grep -wo "\[${_zfsprefix_choice}\][^ ]*" | awk -F "${_zfspool_select}/" 'NR==1{print $2}') info "Selected ZFS prefix: [${_zfsprefix_select}]" - # Ask again to make sure the user is confident in his election. + # Ask again to make sure the user is confident with the election. warn "Are you sure '${_zfsprefix_select}' is the correct ZFS dataset [y/N]: " read _response case "${_response}" in @@ -842,7 +915,7 @@ configure_zfs_manually() { error_exit "No ZFS mountpoint selected, aborting!" else info "Selected bastille storage mountpoint: [${_zfsmount_select}]" - # Ask again to make sure the user is confident in his election. + # Ask again to make sure the user is confident with the election. warn "Are you sure '${_zfsmount_select}' is the correct bastille prefix [y/N]: " read _response case "${_response}" in @@ -889,6 +962,9 @@ case "${1}" in --firewall|-p) configure_pf ;; + --ethernet|-e) + configure_ethernet + ;; --network|-n|bastille0) # TODO remove in future release 0.13 warn "Notice: 'bastille setup bastille0' will be deprecated in the next 0.13 version." From f45fb739134b416c80ddff72ac782873d3ba79a8 Mon Sep 17 00:00:00 2001 From: JRGTH Date: Tue, 21 Jan 2025 18:23:52 -0400 Subject: [PATCH 42/46] Quick shellcheck fix as per suggestion --- usr/local/share/bastille/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/local/share/bastille/setup.sh b/usr/local/share/bastille/setup.sh index 6069f4c0..518ec976 100644 --- a/usr/local/share/bastille/setup.sh +++ b/usr/local/share/bastille/setup.sh @@ -375,7 +375,7 @@ configure_ethernet() { local _response= # Try to get a list of the available physical network/ethernet interfaces. - local ETHERNET_PHY_ADAPTERS=$(pciconf -lv | grep 'ethernet' -B4 | grep 'class=0x020000' | awk -F '@' '{print $1}') + local ETHERNET_PHY_ADAPTERS="$(pciconf -lv | grep 'ethernet' -B4 | grep 'class=0x020000' | awk -F '@' '{print $1}')" if [ -z "${ETHERNET_PHY_ADAPTERS}" ]; then error_exit "Unable to detect for any physical ethernet interfaces, exiting." fi From 050d2008322ea7a3a97bee35e5bcfc47680fd02c Mon Sep 17 00:00:00 2001 From: JRGTH Date: Tue, 21 Jan 2025 18:45:38 -0400 Subject: [PATCH 43/46] Quick cleanup revision --- usr/local/share/bastille/setup.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/usr/local/share/bastille/setup.sh b/usr/local/share/bastille/setup.sh index 518ec976..8e609d95 100644 --- a/usr/local/share/bastille/setup.sh +++ b/usr/local/share/bastille/setup.sh @@ -372,6 +372,7 @@ configure_ethernet() { local ETHIF_COUNT="0" local _ethernet_choice= + local _ethernet_select= local _response= # Try to get a list of the available physical network/ethernet interfaces. @@ -950,7 +951,6 @@ configure_zfs_manually() { esac fi fi - } # Runtime required variables. From 2c45c51e8412b9ed1e97a07454a8093caf4c5c5e Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Fri, 24 Jan 2025 07:33:19 -0700 Subject: [PATCH 44/46] common: Add notes about how MAC is generated --- usr/local/share/bastille/common.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/usr/local/share/bastille/common.sh b/usr/local/share/bastille/common.sh index 8c7e57e9..6837d76d 100644 --- a/usr/local/share/bastille/common.sh +++ b/usr/local/share/bastille/common.sh @@ -228,8 +228,9 @@ 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 prefix for jail MAC prefix + # 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." @@ -364,4 +365,4 @@ checkyesno() { return 1 ;; esac -} \ No newline at end of file +} From 2f594e4e65c8728141220998f8deb1668eaa5e59 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 25 Jan 2025 10:22:23 -0700 Subject: [PATCH 45/46] common: Also check ifconfig for epairs --- usr/local/share/bastille/common.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/local/share/bastille/common.sh b/usr/local/share/bastille/common.sh index 6837d76d..3f861fab 100644 --- a/usr/local/share/bastille/common.sh +++ b/usr/local/share/bastille/common.sh @@ -247,14 +247,14 @@ generate_vnet_jail_netblock() { ## 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 | sort -u | wc -l | awk '{print $1}')" + 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; then + 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} From 1a77041e8e88bc0d9c8b6329645e6b08341ab860 Mon Sep 17 00:00:00 2001 From: tschettervictor <85497460+tschettervictor@users.noreply.github.com> Date: Sat, 25 Jan 2025 10:24:33 -0700 Subject: [PATCH 46/46] clone: Also check ifconfig for epairs --- usr/local/share/bastille/clone.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/usr/local/share/bastille/clone.sh b/usr/local/share/bastille/clone.sh index 9c945f94..998f4674 100644 --- a/usr/local/share/bastille/clone.sh +++ b/usr/local/share/bastille/clone.sh @@ -181,14 +181,14 @@ update_jailconf_vnet() { # Determine number of interfaces and define a uniq_epair local _if_list="$(grep -Eo 'epair[0-9]+|bastille[0-9]+' ${_jail_conf} | sort -u)" for _if in ${_if_list}; do - local _epair_if_count="$(grep -Eo 'epair[0-9]+' ${bastille_jailsdir}/*/jail.conf | sort -u | wc -l | awk '{print $1}')" + local _epair_if_count="$( (grep -Eo '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 -Eo '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 echo ${_if} | grep -Eoq 'epair[0-9]+'; then # Update bridged VNET config for _num in $(seq 0 "${epair_num_range}"); do - if ! grep -oq "epair${_num}" ${bastille_jailsdir}/*/jail.conf; then + if ! grep -Eoq "epair${_num}" ${bastille_jailsdir}/*/jail.conf && ! ifconfig | grep -Eoq "(e${_num}a|epair${_num}a)"; then # Generate new epair name if [ "$(echo -n "e${_num}a_${NEWNAME}" | awk '{print length}')" -lt 16 ]; then local _new_host_epair="e${_num}a_${NEWNAME}"