From 5f7e712b9ee4f91c257099da4abfa8cdc976fc98 Mon Sep 17 00:00:00 2001 From: Jose Date: Thu, 21 Nov 2019 01:28:33 -0400 Subject: [PATCH] Backup and Restore implementation changes --- CHANGELOG | 1 + bastille-init | 123 +++++++++++++++++++++++++-- gui/bastille_manager-lib.inc | 1 + gui/bastille_manager_maintenance.php | 44 ++++++++-- gui/bastille_manager_util.php | 5 +- 5 files changed, 161 insertions(+), 13 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 86afd06..0788302 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -3,6 +3,7 @@ ====================== Version Description +1.0.11......Backup and Restore implementation changes. 1.0.10......Cosmetic changes and version number adjust. 1.0.09......Allow for distfiles download override. 1.0.08......Add option for thickjail creation if supported. diff --git a/bastille-init b/bastille-init index 35276b3..c37cf46 100755 --- a/bastille-init +++ b/bastille-init @@ -51,6 +51,10 @@ SCRIPTNAME=$(basename $0) CONFIG="/cf/conf/config.xml" PRDNAME="Bastille" APPNAME="bastille" +DEF_ZFS_SENDPARAMS="" +DEF_ZFS_RECVPARAM="" +DEF_ZFS_COMPRESS="xz -0 -v --threads=0" +DEF_ZFS_DECOMPRESS="xz -c -d -v --threads=0" EXTLOGFILE="${CWDIR}/log/bastille_ext.log" FULLAPPNAME="${APPNAME}-dist" WWWPATH="/usr/local/www" @@ -71,6 +75,12 @@ BATSILLE_URL="https://github.com/BastilleBSD/${APPNAME}/archive/${BRANCH}.zip" # BASTILE_VERSION="https://raw.githubusercontent.com/BastilleBSD/${APPNAME}/${BRANCH}/usr/local/bin/${APPNAME}" GITURL="https://github.com/JRGTH/xigmanas-${APPNAME}-extension/archive/${BRANCH}.zip" VERFILE="https://raw.githubusercontent.com/JRGTH/xigmanas-${APPNAME}-extension/${BRANCH}/version" +ARG="$2" + +# Required +if [ -f "${BASTILLECONF}" ]; then + . /${BASTILLECONF} +fi error_notify() { @@ -88,6 +98,8 @@ runtime_config() sysrc -f ${INSTALLPATH}/${BASTILLECONF} bastille_prefix="${CWDIR}" >/dev/null 2>&1 fi fi + + # Check for directories. if [ ! -d ${CWDIR}/backups ]; then mkdir -p ${CWDIR}/backups fi @@ -100,6 +112,20 @@ runtime_config() if [ ! -d ${CWDIR}/locale-bastille ]; then mkdir -p ${CWDIR}/locale-bastille fi + + # Set rquired zfs send/recv parameters is missing. + if ! grep -qw "ZFS_SENDPARAMS=" ${CWDIR}${EXTCONF} >/dev/null 2>&1; then + sysrc -f ${CWDIR}${EXTCONF} ZFS_SENDPARAMS="${DEF_ZFS_SENDPARAMS}" >/dev/null 2>&1 + fi + if ! grep -qw "ZFS_RECVPARAM=" ${CWDIR}${EXTCONF} >/dev/null 2>&1; then + sysrc -f ${CWDIR}${EXTCONF} ZFS_RECVPARAM="${DEF_ZFS_RECVPARAM}" >/dev/null 2>&1 + fi + if ! grep -qw "DEFAULT_COMPRESS=" ${CWDIR}${EXTCONF} >/dev/null 2>&1; then + sysrc -f ${CWDIR}${EXTCONF} ZFS_COMPRESS="${DEF_ZFS_COMPRESS}" >/dev/null 2>&1 + fi + if ! grep -qw "DEFAULT_DECOMPRESS=" ${CWDIR}${EXTCONF} >/dev/null 2>&1; then + sysrc -f ${CWDIR}${EXTCONF} ZFS_DECOMPRESS="${DEF_ZFS_DECOMPRESS}" >/dev/null 2>&1 + fi } bastille_initial_download() @@ -307,7 +333,9 @@ sys_symlinkdir() fi else if [ -f "${BASTILLECONF_EXT}" ]; then - cp ${BASTILLECONF_EXT} ${INSTALLPATH}/${USRLOCAL}/etc/${APPNAME}/${APPNAME}.conf + if [ ! -f "${INSTALLPATH}/${USRLOCAL}/etc/${APPNAME}/${APPNAME}.conf" ]; then + cp ${BASTILLECONF_EXT} ${INSTALLPATH}/${USRLOCAL}/etc/${APPNAME}/${APPNAME}.conf + fi fi fi @@ -455,6 +483,87 @@ gui_disable() fi } +jail_backup() +{ + # Backup container on request. + ZFS_COMPRESS=$(sysrc -f ${CWDIR}${EXTCONF} -qn ZFS_COMPRESS) + ZFS_SENDPARAMS=$(sysrc -f ${CWDIR}${EXTCONF} -qn ZFS_SENDPARAMS) + JAIL_NAME="${ARG}" + DATE=$(date +%Y-%m-%d-%H%M%S) + EXCLUDE="--exclude=.bastille --exclude=.template" + if [ -n "${JAIL_NAME}" ]; then + if [ -d "${CWDIR}/jails/${JAIL_NAME}" ]; then + if [ "${bastille_zfs_enable}" = "YES" ]; then + if [ ! -z "${bastille_zfs_zpool}" ]; then + # Take a temp snapshot of the jail. + SNAP_NAME="bastille-$(date +%Y-%m-%d-%H%M%S)" + zfs snapshot -r ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL_NAME}@${SNAP_NAME} + + # Backup the jail then cleanup temp zfs snapshots. + zfs send ${ZFS_SENDPARAMS} -R ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL_NAME}@${SNAP_NAME} | ${ZFS_COMPRESS} > ${CWDIR}/backups/${JAIL_NAME}-${DATE}.zfs + zfs destroy -r ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL_NAME}@${SNAP_NAME} + fi + else + # Create backup file with tar. + cd ${CWDIR}/jails && tar ${EXCLUDE} -zcf ${JAIL_NAME}-${DATE}.tar ${JAIL_NAME} && mv ${JAIL_NAME}-${DATE}.tar ${CWDIR}/backups + fi + if [ $? -ne 0 ]; then + echo "Failed to backup '${JAIL_NAME}' container." + exit 1 + else + exit 0 + fi + else + echo "Container '${JAIL_NAME}' does not exist." + exit 1 + fi + else + exit 0 + fi +} + +jail_restore() +{ + # Restore container on request. + ZFS_DECOMPRESS=$(sysrc -f ${CWDIR}${EXTCONF} -qn ZFS_DECOMPRESS) + ZFS_RECVPARAM=$(sysrc -f ${CWDIR}${EXTCONF} -qn ZFS_RECVPARAM) + BACKUP_FILE="${ARG}" + NAME_TRIM=$(echo ${BACKUP_FILE} | awk '{print $1}' | grep -o '[^/]*$' | cut -d '-' -f1) + if [ -f "${CWDIR}/backups/${BACKUP_FILE}" ]; then + if [ -d "${CWDIR}/jails" ]; then + if [ ! -d "${CWDIR}/jails/${NAME_TRIM}" ]; then + if [ "${bastille_zfs_enable}" = "YES" ]; then + if [ ! -z "${bastille_zfs_zpool}" ]; then + # Restore from zfs file and mount jail/root dataset. + ${ZFS_DECOMPRESS} ${CWDIR}/backups/${BACKUP_FILE} | zfs receive ${ZFS_RECVPARAM} ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME_TRIM} + zfs mount ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${NAME_TRIM}/root + fi + else + # Restore from tar file. + tar -xf ${CWDIR}/backups/${BACKUP_FILE} -C ${CWDIR}/jails + fi + if [ $? -ne 0 ]; then + echo "Failed to restore '${NAME_TRIM}' container." + exit 1 + else + mkdir -p ${CWDIR}/jails/${NAME_TRIM}/root/.bastille + mkdir -p ${CWDIR}/jails/${NAME_TRIM}/root/.template + exit 0 + fi + else + echo "Container '${NAME_TRIM}' directory/dataset already exist." + exit 1 + fi + else + echo "Jails directory/dataset does not exist." + exit 1 + fi + else + echo "Archive '${BACKUP_FILE}' does not exist." + exit 1 + fi +} + pkg_upgrade() { # Re-fetch bastille package and extract. @@ -695,9 +804,9 @@ bastille_init() # Run-time configuration. runtime_config -while getopts ":ospruxRvgth" option; do +while getopts ":ospruxUvgtBRh" option; do case ${option} in - [h]) echo "Usage: ${SCRIPTNAME} -[option]"; + [h]) echo "Usage: ${SCRIPTNAME} -[option] | [container]"; echo "Options:" echo " -s Start All ${PRDNAME} Containers." echo " -p Stop All ${PRDNAME} Containers." @@ -706,8 +815,10 @@ while getopts ":ospruxRvgth" option; do echo " -v Display product versions." echo " -g Enables the addon GUI." echo " -t Disable the addon GUI." + echo " -B Backup a ${PRDNAME} container." + echo " -R Restore a ${PRDNAME} container." echo " -x Reset ${PRDNAME}/Extension config." - echo " -R Remove ${PRDNAME} (Extension files only)." + echo " -U Uninstall ${PRDNAME} (Extension files only)." echo " -h Display this help message."; exit 0;; [o]) OBI_INSTALL="ON";; # To prevent nested PHP-CGI call for installation with OBI. [s]) bastille_start;; @@ -715,10 +826,12 @@ while getopts ":ospruxRvgth" option; do [r]) bastille_restart;; [u]) pkg_upgrade;; [x]) reset_install;; - [R]) remove_addon;; + [U]) remove_addon;; [v]) get_versions;; [g]) gui_enable; exit 0 ;; # For enable the addon gui. [t]) gui_disable; exit 0 ;; # For disable the addon gui. + [B]) jail_backup;; + [R]) jail_restore;; [?]) echo "Invalid option, -h for usage."; exit 1;; esac done diff --git a/gui/bastille_manager-lib.inc b/gui/bastille_manager-lib.inc index 8b9c0d0..59b2e59 100755 --- a/gui/bastille_manager-lib.inc +++ b/gui/bastille_manager-lib.inc @@ -40,6 +40,7 @@ require_once 'system.inc'; // internal PHP functions rather than external shell commands. //$rootfolder = dirname($config['rc']['postinit']['cmd'][$i]); +$application = "Bastille Manager"; $restore_name = "restore"; $confdir = "/var/etc/bastille_conf"; $cwdir = exec("/usr/bin/grep 'INSTALL_DIR=' {$confdir}/conf/bastille_config | /usr/bin/cut -d'\"' -f2"); diff --git a/gui/bastille_manager_maintenance.php b/gui/bastille_manager_maintenance.php index 2b689e0..90f283f 100644 --- a/gui/bastille_manager_maintenance.php +++ b/gui/bastille_manager_maintenance.php @@ -41,7 +41,6 @@ require("auth.inc"); require("guiconfig.inc"); require_once("bastille_manager-lib.inc"); -$application = "Bastille"; $pgtitle = array(gtext("Extensions"), "Bastille", "Maintenance"); // For legacy product versions. @@ -87,7 +86,7 @@ if ($_POST) { if (is_dir($confdir)) mwexec("rm -Rf {$confdir}", true); mwexec("rm /usr/local/www/bastille_manager_gui.php && rm -R /usr/local/www/ext/bastille", true); mwexec("{$rootfolder}/usr/local/sbin/bastille-init -t", true); - $uninstall_cmd = "echo 'y' | /usr/local/sbin/bastille-init -R"; + $uninstall_cmd = "echo 'y' | /usr/local/sbin/bastille-init -U"; mwexec($uninstall_cmd, true); if (is_link("/usr/local/share/{$prdname}")) mwexec("rm /usr/local/share/{$prdname}", true); if (is_link("/var/cache/pkg")) mwexec("rm /var/cache/pkg", true); @@ -137,18 +136,50 @@ if ($_POST) { unset($retval);mwexec($cmd,$retval); if ($retval == 0) { $savemsg .= gtext("Extension settings saved successfully."); - exec("echo '{$date}: {$application} Extension settings saved successfully' >> {$logfile}"); + exec("echo '{$date}: {$application}: Extension settings saved successfully' >> {$logfile}"); } else { $input_errors[] = gtext("Failed to save extension settings."); - exec("echo '{$date}: {$application} Failed to save extension settings' >> {$logfile}"); + exec("echo '{$date}: {$application}: Failed to save extension settings' >> {$logfile}"); } } else { $input_errors[] = gtext("Failed to save extension settings."); - exec("echo '{$date}: {$application} Failed to save extension settings' >> {$logfile}"); + exec("echo '{$date}: {$application}: Failed to save extension settings' >> {$logfile}"); } } + + if (isset($_POST['restore']) && $_POST['restore']) { + // Ensure to have NO whitespace & trailing slash. + $backup_file = rtrim(trim($_POST['backup_path']),'/'); + $filename_trim = exec("echo {$backup_file} | awk '{print $1}' | grep -o '[^/]*$'"); + $jailname_trim = exec("echo {$backup_file} | awk '{print $1}' | grep -o '[^/]*$' | cut -d '-' -f1"); + + if ("{$backup_file}" == "") { + $input_errors[] = gtext("Error: backup file undefined."); + } + + if (is_dir("{$jail_dir}/{$jailname_trim}")): + $input_errors[] = gtext("Container directory/dataset already exist."); + else: + if (is_file($backup_file)) { + $cmd = ("/usr/local/sbin/bastille-init -R '{$filename_trim}'"); + unset($retval);mwexec($cmd,$retval); + if ($retval == 0) { + $savemsg .= gtext("Container restored successfully."); + exec("echo '{$date}: {$application}: Container restored successfully from {$filename_trim}' >> {$logfile}"); + } + else { + $input_errors[] = gtext("Failed to restore container."); + exec("echo '{$date}: {$application}: Failed to restore container from {$filename_trim}' >> {$logfile}"); + } + } + else { + $input_errors[] = gtext("Failed to restore container, file not found."); + exec("echo '{$date}: {$application}: Failed to restore container, file {$filename_trim} not found' >> {$logfile}"); + } + endif; + } } function get_version_bastille() { @@ -240,11 +271,12 @@ $(document).ready(function(){ - +
" value=""/> " value="" /> + " value="" />
diff --git a/gui/bastille_manager_util.php b/gui/bastille_manager_util.php index 165b78f..2e404be 100644 --- a/gui/bastille_manager_util.php +++ b/gui/bastille_manager_util.php @@ -114,15 +114,16 @@ if($_POST): $container['jailname'] = $_POST['jailname']; $confirm_name = $pconfig['confirmname']; $item = $container['jailname']; - $date = (strftime('%Y-%m-%d-%H%M%S')); - $cmd = ("cd {$rootfolder}/jails && /usr/bin/tar -cf {$item}-{$date}.tar --exclude=.bastille --exclude=.template {$item} && /bin/mv {$item}-{$date}.tar {$backup_path}"); + $cmd = ("/usr/local/sbin/bastille-init -B '{$item}'"); unset($output,$retval);mwexec2($cmd,$output,$retval); if($retval == 0): $savemsg .= gtext("Container backup process completed successfully."); + exec("echo '{$date}: {$application}: Container backup process completed successfully for {$item}' >> {$logfile}"); //header('Location: bastille_manager_gui.php'); //exit; else: $errormsg .= gtext("Failed to backup container."); + exec("echo '{$date}: {$application}: Failed to backup container {$item}' >> {$logfile}"); endif; endif; break;