diff --git a/usr/local/bin/bastille b/usr/local/bin/bastille index 67c3a2b1..af76d811 100755 --- a/usr/local/bin/bastille +++ b/usr/local/bin/bastille @@ -1,6 +1,6 @@ #!/bin/sh # -# Copyright (c) 2018-2019, Christer Edwards +# Copyright (c) 2018-2020, Christer Edwards # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -83,6 +83,7 @@ Available Commands: bootstrap Bootstrap a FreeBSD release for container base. cmd Execute arbitrary command on targeted container(s). console Console into a running container. + convert Convert a Thin container into a Thick container. cp cp(1) files from host to targeted container(s). create Create a new thin container or a thick container if -T|--thick option specified. destroy Destroy a stopped container or a FreeBSD release. @@ -127,7 +128,7 @@ esac # Filter out all non-commands case "${CMD}" in -cmd|cp|create|destroy|list|pkg|restart|start|stop|sysrc|template|verify) +cmd|convert|cp|create|destroy|list|pkg|restart|start|stop|sysrc|template|verify) ;; update|upgrade) ;; diff --git a/usr/local/etc/rc.d/bastille b/usr/local/etc/rc.d/bastille index 0d79ae0d..1ff6530e 100755 --- a/usr/local/etc/rc.d/bastille +++ b/usr/local/etc/rc.d/bastille @@ -29,8 +29,8 @@ restart_cmd="bastille_stop && bastille_start" bastille_start() { - if [ ! -n "${bastille_list}" ]; then - echo "${bastille_list} is undefined" + if [ -z "${bastille_list}" ]; then + echo "bastille_list is undefined" return 1 fi @@ -44,8 +44,8 @@ bastille_start() bastille_stop() { - if [ ! -n "${bastille_list}" ]; then - echo "${bastille_list} is undefined" + if [ -z "${bastille_list}" ]; then + echo "bastille_list is undefined" return 1 fi diff --git a/usr/local/share/bastille/bootstrap.sh b/usr/local/share/bastille/bootstrap.sh index b09eb8f6..f7e45116 100644 --- a/usr/local/share/bastille/bootstrap.sh +++ b/usr/local/share/bastille/bootstrap.sh @@ -250,10 +250,22 @@ bootstrap_directories() { } bootstrap_release() { - ## if release exists, quit + ## if release exists quit, else bootstrap additional distfiles if [ -f "${bastille_releasesdir}/${RELEASE}/COPYRIGHT" ]; then - echo -e "${COLOR_RED}Bootstrap appears complete.${COLOR_RESET}" - exit 1 + ## check distfiles list and skip existing cached files + bastille_bootstrap_archives=$(echo "${bastille_bootstrap_archives}" | sed "s/base//") + bastille_cached_files=$(ls ${bastille_cachedir}/${RELEASE} | grep -v "MANIFEST" | tr -d ".txz") + for distfile in ${bastille_cached_files}; do + bastille_bootstrap_archives=$(echo ${bastille_bootstrap_archives} | sed "s/${distfile}//") + done + + ## check if release already bootstrapped, else continue bootstrapping + if [ -z "${bastille_bootstrap_archives}" ]; then + echo -e "${COLOR_RED}Bootstrap appears complete.${COLOR_RESET}" + exit 1 + else + echo -e "${COLOR_GREEN}Bootstrapping additional distfiles...${COLOR_RESET}" + fi fi for _archive in ${bastille_bootstrap_archives}; do diff --git a/usr/local/share/bastille/convert.sh b/usr/local/share/bastille/convert.sh new file mode 100644 index 00000000..c6378836 --- /dev/null +++ b/usr/local/share/bastille/convert.sh @@ -0,0 +1,168 @@ +#!/bin/sh +# +# Copyright (c) 2018-2020, Christer Edwards +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation +# and/or other materials provided with the distribution. +# +# * Neither the name of the copyright holder nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# 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. + +. /usr/local/share/bastille/colors.pre.sh +. /usr/local/etc/bastille/bastille.conf + +usage() { + echo -e "${COLOR_RED}Usage: bastille convert name.${COLOR_RESET}" + exit 1 +} + +# Handle special-case commands first. +case "$1" in +help|-h|--help) + usage + ;; +esac + +if [ $# -gt 1 ] || [ $# -lt 1 ]; then + usage +fi + +TARGET="${1}" +shift + +error_notify() +{ + # Notify message on error and exit + echo -e "$*" >&2 + exit 1 +} + +convert_symlinks() { + # Work with the symlinks, revert on first cp error + if [ -d "${bastille_releasesdir}/${RELEASE}" ]; then + # Retrieve old symlinks temporarily + for _link in ${SYMLINKS}; do + if [ -L "${_link}" ]; then + mv ${_link} ${_link}.old + fi + done + + # Copy new files to destination jail + for _link in ${SYMLINKS}; do + if [ ! -d "${_link}" ]; then + if [ -d "${bastille_releasesdir}/${RELEASE}/${_link}" ]; then + cp -a "${bastille_releasesdir}/${RELEASE}/${_link}" "${bastille_jailsdir}/${TARGET}/root/${_link}" + fi + if [ $? -ne 0 ]; then + revert_convert + fi + fi + done + + # Remove the old symlinks on success + for _link in ${SYMLINKS}; do + if [ -L "${_link}.old" ]; then + rm -r ${_link}.old + fi + done + else + error_notify "${COLOR_RED}Release must be bootstrapped first, See `bastille bootstrap`.${COLOR_RESET}" + fi +} + +revert_convert() { + # Revert the conversion on first cp error + echo -e "${COLOR_RED}A problem has occurred while copying the files, reverting changes...${COLOR_RESET}" + for _link in ${SYMLINKS}; do + if [ -d "${_link}" ]; then + chflags -R noschg "${bastille_jailsdir}/${TARGET}/root/${_link}" + rm -rf "${bastille_jailsdir}/${TARGET}/root/${_link}" + fi + done + + # Restore previous symlinks + for _link in ${SYMLINKS}; do + if [ -L "${_link}.old" ]; then + mv ${_link}.old ${_link} + fi + done + error_notify "${COLOR_GREEN}Changes for '${TARGET}' has been reverted.${COLOR_RESET}" +} + +start_convert() { + # Attempt container conversion and handle some errors + if [ -d "${bastille_jailsdir}/${TARGET}" ]; then + echo -e "${COLOR_GREEN}Converting '${TARGET}' into a thickjail, this may take a while...${COLOR_RESET}" + + # Set some variables + RELEASE=$(grep -owE '([1-9]{2,2})\.[0-9](-RELEASE|-RC[1-2])' ${bastille_jailsdir}/${TARGET}/fstab) + FSTABMOD=$(grep -w "${bastille_releasesdir}/${RELEASE} ${bastille_jailsdir}/${TARGET}/root/.bastille" ${bastille_jailsdir}/${TARGET}/fstab) + SYMLINKS="bin boot lib libexec rescue sbin usr/bin usr/include usr/lib usr/lib32 usr/libdata usr/libexec usr/ports usr/sbin usr/share usr/src" + + if [ -n "${RELEASE}" ]; then + cd "${bastille_jailsdir}/${TARGET}/root" + + # Work with the symlinks + convert_symlinks + + # Comment the line containing .bastille and rename mountpoint + sed -i '' -E "s|${FSTABMOD}|# Converted from thin to thick container on $(date)|g" "${bastille_jailsdir}/${TARGET}/fstab" + mv ${bastille_jailsdir}/${TARGET}/root/.bastille ${bastille_jailsdir}/${TARGET}/root/.bastille.old + + echo -e "${COLOR_GREEN}Conversion of '${TARGET}' completed successfully!${COLOR_RESET}" + exit 0 + else + error_notify "${COLOR_RED}Can't determine release version, See `bastille bootstrap`.${COLOR_RESET}" + fi + else + error_notify "${COLOR_RED}${TARGET} not found. See bootstrap.${COLOR_RESET}" + fi +} + +# Check compatibility +if [ -n "$(freebsd-version | grep -i HBSD)" ]; then + error_notify "${COLOR_RED}Not yet supported on HardenedBSD.${COLOR_RESET}" +fi + +# Check if container is running +if [ -n "$(jls name | awk "/^${TARGET}$/")" ]; then + error_notify "${COLOR_RED}${TARGET} is running, See `bastille stop`.${COLOR_RESET}" +fi + +# Check if is a thin container +if [ ! -d "${bastille_jailsdir}/${TARGET}/root/.bastille" ]; then + error_notify "${COLOR_RED}${TARGET} is not a thin container.${COLOR_RESET}" +elif ! grep -qw ".bastille" "${bastille_jailsdir}/${TARGET}/fstab"; then + error_notify "${COLOR_RED}${TARGET} is not a thin container.${COLOR_RESET}" +fi + +# Make sure the user agree with the conversion +# Be interactive here since this cannot be easily undone +while :; do + echo -e "${COLOR_RED}Warning: container conversion from thin to thick can't be undone!${COLOR_RESET}" + read -p "Do you really wish to convert '${TARGET}' into a thick container? [y/N]:" yn + case ${yn} in + [Yy]) start_convert;; + [Nn]) exit 0;; + esac +done