Merge pull request #1019 from BastilleBSD/migrate

Implement jail migration to remote system
This commit is contained in:
Barry McCormick
2025-05-07 08:07:13 -07:00
committed by GitHub
13 changed files with 623 additions and 69 deletions

View File

@@ -59,28 +59,29 @@ Available Commands:
clone Clone an existing jail.
cmd Execute arbitrary command on targeted jail(s).
config Get, set or remove a config value for the targeted jail(s).
console Console into a running jail.
console Console into a jail.
convert Convert thin jail to thick jail, or convert a jail to a custom release.
cp cp(1) files from host or jail to host or targeted jail(s).
cp cp(1) files from host to targeted jail(s).
create Create a jail.
destroy Destroy a jail or release.
edit Edit jail configuration files (advanced).
export Exports a jail.
export Export a jail.
help Help about any command.
htop Interactive process viewer (requires htop).
import Import a jail.
jcp cp(1) files from a jail to jail(s).
limits Apply resources limits to targeted jail(s). See rctl(8).
list List jails, releases, templates etc...
limits Apply resources limits to targeted jail(s). See rctl(8) and cpuset(1).
list List jails, releases, templates and more...
migrate Migrate targeted jail(s) to a remote system.
mount Mount a volume inside targeted jail(s).
network Add or remove interfaces from targeted jail(s).
pkg Manipulate binary packages within targeted jail(s). See pkg(8).
rcp cp(1) files from a jail to host.
rdr Redirect host port to container port.
rdr Redirect host port to jail port.
rename Rename a jail.
restart Restart a running jail.
service Manage services within targeted jail(s).
setup Attempt to auto-configure network, firewall and storage on new installs.
setup Attempt to auto-configure network, firewall and storage and more...
start Start a stopped jail.
stop Stop a running jail.
sysrc Safely edit rc files within targeted jail(s).
@@ -95,8 +96,8 @@ Available Commands:
Use "bastille -v|--version" for version information.
Use "bastille command -h|--help" for more information about a command.
Use "bastille [-c|--config config.conf] command" to specify a non-default config file.
Use "bastille [-p|--parallel VALUE] command" to run bastille in parallel mode.
Use "bastille -c|--config config.conf command" to specify a non-default config file.
Use "bastille -p|--parallel VALUE command" to run bastille in parallel mode.
```

View File

@@ -1,7 +1,50 @@
=========
Migration
=========
Bastille
--------
Bastille supports migrations to a remote system using the ``migrate`` subcommand.
Prerequisites
^^^^^^^^^^^^^
There are a couple of things that need to be in place before running the ``migrate`` command.
First, you must have bastille configured both locally and remotely to use the same filesystem
configuration. ZFS on both, or UFS on both.
Second, you must create a user on the remote system that will be used to migrate the jail. The user
must be able to log in via SSH using either key-based authentication, or password based authentication.
The user also needs ``sudo`` permissions on the remote system. This user should then be given as the
``USER`` arg in the ``migrate`` command.
If you are using key-based auth, the keys should be stored in the default location at ``$HOME/.ssh/id_rsa``,
where ``$HOME`` is the users home directory. This is the default location for ssh keys, and where Bastille
will try to load them from.
If you want to use password based authentication, simply run ``bastille migrate -p TARGET USER HOST``. This
will prompt you to enter the password for the remote system, which Bastille will then use during the migration
process.
Migration
^^^^^^^^^
To migrate a jail (or multiple jails) we can simply run
``bastille migrate TARGET USER HOST``. This will export the jail(s), send them to the
remote system, and import them.
The ``migrate`` sub-command includes the ``-a|--auto`` option, which will auto-stop the old jail,
migrate it, and attempt to start the migrated jail on the remote system after importing it. See the
warning below about auto-starting the migrated jail.
WARNING: Every system is unique, has different interfaces, bridges, and network configurations.
It is possible, with the right configuration, for jails to start and work normally. But for some
systems, it will be necessary to edit the ``jail.conf`` file of the migrated jail to get it working
properly.
You can optionally set ``-d|--destroy`` to have Bastille destroy the old jail on completion.
iocage
------

View File

@@ -0,0 +1,19 @@
migrate
=======
The ``migrate`` sub-command allows migrating the targeted jail(s) to
another remote system. See the chapter on Migration.
This sub-command supports multiple targets.
.. code-block:: shell
ishmael ~ # bastille migrate help
Usage: bastille migrate [option(s)] TARGET USER HOST
Options:
-a | --auto Auto mode. Start/stop jail(s) if required.
-d | --destroy Destroy local jail after migration.
-p | --password Use password based authentication.
-x | --debug Enable debug mode.

View File

@@ -13,12 +13,13 @@ Below is a list of available options that can be used with the ``setup`` command
.. code-block:: shell
ishmael ~ # bastille setup -h ## display setup help
ishmael ~ # bastille setup -l ## configure loopback interface
ishmael ~ # bastille setup -s ## configure shared interface
ishmael ~ # bastille setup -p ## configure default pf firewall
ishmael ~ # bastille setup -z ## configure ZFS storage
ishmael ~ # bastille setup -v ## configure VNET
ishmael ~ # bastille setup -b ## configure bridge interface
ishmael ~ # bastille setup -f ## configure filesystem/structure
ishmael ~ # bastille setup -l ## configure loopback interface
ishmael ~ # bastille setup -p ## configure default pf firewall
ishmael ~ # bastille setup -s ## configure shared interface
ishmael ~ # bastille setup -v ## configure VNET
ishmael ~ # bastille setup -z ## configure ZFS storage
ishmael ~ # bastille setup ## configure -l -p and -z
The ``-l|loopback`` option will configure a loopback interface called ``bastille0`` that
@@ -36,6 +37,9 @@ networking option. The ``-l|loopback`` and ``-s|shared`` options are only for ca
is not specified during the ``create`` command. If an interface is specified, these options have no effect.
Instead, the specified interface will be used.
The ``-f|--filesystem`` option is to ensure the proper datasets/directories are in place
for using Bastille. This should only have to be run once on a new system.
The ``-s|shared`` option is for cases where you want an actual interface to use with bastille as
opposed to a loopback. Jails will be linked to the shared interface on creation.
@@ -51,10 +55,10 @@ The ``-v|vnet`` option will configure your system for use with VNET ``-V`` jails
The ``-b|bridge`` options will attempt to configure a bridge interface for use with bridged VNET
``-B`` jails.
Running ``bastille setup`` without any options will attempt to auto-configure the ``-l``, ``-p`` and
Running ``bastille setup`` without any options will attempt to auto-configure the ``-f``, ``-l``, ``-p`` and
``-z`` options.
.. code-block:: shell
ishmael ~ # bastille setup help
Usage: bastille setup [-p|pf|firewall] [-l|loopback] [-s|shared] [-z|zfs|storage] [-v|vnet] [-b|bridge]
Usage: bastille setup [-b|bridge] [-f|--filesystem] [-l|loopback] [-p|pf|firewall] [-s|shared] [-v|vnet] [-z|zfs|storage]

View File

@@ -8,46 +8,49 @@ Usage
containerized applications on FreeBSD.
Usage:
bastille command TARGET [args]
bastille command [options(s)] TARGET [option(s)] [args]
Available Commands:
bootstrap Bootstrap a FreeBSD release for container base.
clone Clone an existing container.
cmd Execute arbitrary command on targeted container(s).
config Get or set a config value for the targeted container(s).
console Console into a running container.
convert Convert a Thin container into a Thick container.
cp cp(1) files from host or container to host or 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.
edit Edit container configuration files (advanced).
export Exports a specified container.
bootstrap Bootstrap a release for jail base.
clone Clone an existing jail.
cmd Execute arbitrary command on targeted jail(s).
config Get, set or remove a config value for the targeted jail(s).
console Console into a jail.
convert Convert thin jail to thick jail, or convert a jail to a custom release.
cp cp(1) files from host to targeted jail(s).
create Create a jail.
destroy Destroy a jail or release.
edit Edit jail configuration files (advanced).
export Export a jail.
help Help about any command.
htop Interactive process viewer (requires htop).
import Import a specified container.
jcp cp(1) files from a jail to targeted jail(s).
limits Apply resources limits to targeted container(s). See rctl(8).
list List containers (running).
mount Mount a volume inside the targeted container(s).
pkg Manipulate binary packages within targeted container(s). See pkg(8).
import Import a jail.
jcp cp(1) files from a jail to jail(s).
limits Apply resources limits to targeted jail(s). See rctl(8) and cpuset(1).
list List jails, releases, templates and more...
migrate Migrate targeted jail(s) to a remote system.
mount Mount a volume inside targeted jail(s).
network Add or remove interfaces from targeted jail(s).
pkg Manipulate binary packages within targeted jail(s). See pkg(8).
rcp cp(1) files from a jail to host.
rdr Redirect host port to container port.
rename Rename a container.
restart Restart a running container.
service Manage services within targeted container(s).
setup Attempt to auto-configure network, firewall and storage on new installs.
start Start a stopped container.
stop Stop a running container.
sysrc Safely edit rc files within targeted container(s).
tags Add or remove tags to targeted container(s).
template Apply file templates to targeted container(s).
rdr Redirect host port to jail port.
rename Rename a jail.
restart Restart a running jail.
service Manage services within targeted jail(s).
setup Attempt to auto-configure network, firewall and storage and more...
start Start a stopped jail.
stop Stop a running jail.
sysrc Safely edit rc files within targeted jail(s).
tags Add or remove tags to targeted jail(s).
template Apply file templates to targeted jail(s).
top Display and update information about the top(1) cpu processes.
umount Unmount a volume from within the targeted container(s).
update Update container base -pX release.
upgrade Upgrade container release to X.Y-RELEASE.
umount Unmount a volume from targeted jail(s).
update Update jail base -pX release.
upgrade Upgrade jail release to X.Y-RELEASE.
verify Compare release against a "known good" index.
zfs Manage (get|set) ZFS attributes on targeted container(s).
Use "bastille -v|--version" for version information.
Use "bastille command -h|--help" for more information about a command.
Use "bastille [-c|--config FILE] command" to specify a non-default config file.
Use "bastille -c|--config config.conf command" to specify a non-default config file.
Use "bastille -p|--parallel VALUE command" to run bastille in parallel mode.

View File

@@ -96,6 +96,7 @@ Available Commands:
jcp cp(1) files from a jail to jail(s).
limits Apply resources limits to targeted container(s). See rctl(8).
list List containers (running).
migrate Migrate targetted jail(s) to a remote system.
mount Mount a volume inside the targeted container(s).
network Add/remove network interfaces from targeted container.
pkg Manipulate binary packages within targeted container(s). See pkg(8).
@@ -213,6 +214,7 @@ case "${CMD}" in
import| \
limits| \
list| \
migrate| \
network| \
rcp| \
rdr| \

View File

@@ -9,6 +9,7 @@ bastille_cachedir="${bastille_prefix}/cache" ## default
bastille_jailsdir="${bastille_prefix}/jails" ## default: "${bastille_prefix}/jails"
bastille_releasesdir="${bastille_prefix}/releases" ## default: "${bastille_prefix}/releases"
bastille_templatesdir="${bastille_prefix}/templates" ## default: "${bastille_prefix}/templates"
bastille_migratedir="${bastille_prefix}/migrate" ## default: "${bastille_prefix}/migrate"
bastille_logsdir="/var/log/bastille" ## default: "/var/log/bastille"
## pf configuration path

View File

@@ -108,6 +108,18 @@ bootstrap_directories() {
chmod 0750 "${bastille_backupsdir}"
fi
## ${bastille_migratedir}
if [ ! -d "${bastille_migratedir}" ]; then
if checkyesno bastille_zfs_enable; then
if [ -n "${bastille_zfs_zpool}" ]; then
zfs create ${bastille_zfs_options} -o mountpoint="${bastille_migratedir}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/migrate"
fi
else
mkdir -p "${bastille_migratedir}"
fi
chmod 0750 "${bastille_migratedir}"
fi
## ${bastille_cachedir}
if [ ! -d "${bastille_cachedir}" ]; then
if checkyesno bastille_zfs_enable; then
@@ -182,7 +194,6 @@ bootstrap_directories() {
else
mkdir -p "${bastille_releasesdir}/${RELEASE}"
fi
## create subsequent releases/XX.X-RELEASE datasets
elif [ ! -d "${bastille_releasesdir}/${RELEASE}" ]; then
if checkyesno bastille_zfs_enable; then

View File

@@ -247,6 +247,10 @@ set_target() {
TARGET="${TARGET} ${_jail}"
JAILS="${JAILS} ${_jail}"
done
# Exit if no jails
if [ -z "${TARGET}" ] && [ -z "${JAILS}" ]; then
exit 1
fi
if [ "${_order}" = "forward" ]; then
TARGET="$(list_jail_priority "${TARGET}" | sort -k2 -n | awk '{print $1}')"
JAILS="$(list_jail_priority "${TARGET}" | sort -k2 -n | awk '{print $1}')"
@@ -281,6 +285,10 @@ set_target_single() {
exit 1
fi
fi
# Exit if no jails
if [ -z "${TARGET}" ] && [ -z "${JAILS}" ]; then
exit 1
fi
TARGET="${_TARGET}"
JAILS="${_TARGET}"
export TARGET
@@ -295,6 +303,10 @@ target_all_jails() {
JAILS="${JAILS} ${_jail}"
fi
done
# Exit if no jails
if [ -z "${JAILS}" ]; then
exit 1
fi
if [ "${_order}" = "forward" ]; then
JAILS="$(list_jail_priority "${JAILS}" | sort -k2 -n | awk '{print $1}')"
elif [ "${_order}" = "reverse" ]; then

View File

@@ -50,7 +50,7 @@ usage() {
-v | --verbose Be more verbose during the ZFS send operation.
--xz Export a ZFS jail using XZ(.xz) compressed image.
Note: If no export option specified, the container should be redirected to standard output.
Note: If no export option specified, the jail should be redirected to standard output.
EOF
exit 1
@@ -87,6 +87,7 @@ if [ -n "${bastille_export_options}" ]; then
# Reference "/bastille/issues/443"
DEFAULT_EXPORT_OPTS="${bastille_export_options}"
info "Default export option(s): '${DEFAULT_EXPORT_OPTS}'"
for opt in ${DEFAULT_EXPORT_OPTS}; do
@@ -123,7 +124,9 @@ if [ -n "${bastille_export_options}" ]; then
usage;;
esac
done
else
# Handle options
while [ $# -gt 0 ]; do
case "${1}" in
@@ -194,8 +197,6 @@ fi
bastille_root_check
set_target_single "${TARGET}"
info "\n[${_jail}]:"
# Validate for combined options
if [ "${COMP_OPTION}" -gt "1" ]; then
error_exit "[ERROR]: Only one compression format can be used during export."
@@ -254,7 +255,7 @@ clean_zfs_snap() {
zfs destroy "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_${TARGET}_${DATE}"
}
export_check() {
export_check() {
# Inform the user about the exporting method
if [ -z "${USER_EXPORT}" ]; then
if [ -n "$(/usr/sbin/jls name | awk "/^${TARGET}$/")" ]; then
@@ -302,35 +303,52 @@ jail_export() {
# Attempt to export the container
DATE=$(date +%F-%H%M%S)
if checkyesno bastille_zfs_enable; then
if [ -n "${bastille_zfs_zpool}" ]; then
if [ -n "${RAW_EXPORT}" ]; then
FILE_EXT=""
export_check
# Export the raw container recursively and cleanup temporary snapshots
zfs send ${OPT_ZSEND} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_${TARGET}_${DATE}" \
> "${bastille_backupsdir}/${TARGET}_${DATE}"
clean_zfs_snap
elif [ -n "${GZIP_EXPORT}" ]; then
FILE_EXT=".gz"
export_check
# Export the raw container recursively and cleanup temporary snapshots
zfs send ${OPT_ZSEND} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_${TARGET}_${DATE}" | \
gzip ${bastille_compress_gz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}"
clean_zfs_snap
elif [ -n "${XZ_EXPORT}" ]; then
FILE_EXT=".xz"
export_check
# Export the container recursively and cleanup temporary snapshots
zfs send ${OPT_ZSEND} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${TARGET}@bastille_${TARGET}_${DATE}" | \
xz ${bastille_compress_xz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}"
clean_zfs_snap
else
FILE_EXT=""
USER_EXPORT="1"
export_check
# Quietly export the container recursively, user must redirect standard output
@@ -342,16 +360,21 @@ jail_export() {
fi
else
if [ -n "${TGZ_EXPORT}" ]; then
FILE_EXT=".tgz"
# Create standard tgz backup archive
info "\nExporting '${TARGET}' to a compressed ${FILE_EXT} archive..."
cd "${bastille_jailsdir}" && tar -cf - "${TARGET}" | gzip ${bastille_compress_gz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}"
elif [ -n "${TXZ_EXPORT}" ]; then
FILE_EXT=".txz"
# Create standard txz backup archive
info "\nExporting '${TARGET}' to a compressed ${FILE_EXT} archive..."
cd "${bastille_jailsdir}" && tar -cf - "${TARGET}" | xz ${bastille_compress_xz_options} > "${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}"
else
error_exit "[ERROR]: export option required"
@@ -363,10 +386,13 @@ jail_export() {
error_exit "[ERROR]: Failed to export '${TARGET}' container."
else
if [ -z "${USER_EXPORT}" ]; then
# Generate container checksum file
cd "${bastille_backupsdir}" || error_exit "Failed to change directory."
sha256 -q "${TARGET}_${DATE}${FILE_EXT}" > "${TARGET}_${DATE}.sha256"
info "\nExported '${bastille_backupsdir}/${TARGET}_${DATE}${FILE_EXT}' successfully."
fi
exit 0
fi
@@ -379,18 +405,22 @@ fi
if [ -n "${TARGET}" ]; then
# Validate jail existence
if [ ! -d "${bastille_jailsdir}/${TARGET}" ]; then
error_exit "[ERROR]: Jail not found: ${TARGET}"
fi
# Check if is a ZFS system
# Jail needs to be stopped on non-ZFS systems
if ! checkyesno bastille_zfs_enable; then
# Check if container is running and ask for stop in non ZFS systems
if [ -n "$(/usr/sbin/jls name | awk "/^${TARGET}$/")" ]; then
error_exit "[ERROR]: ${TARGET} is running. See 'bastille stop'."
# Validate jail state
check_target_is_stopped "${TARGET}" || if [ "${AUTO}" -eq 1 ]; then
bastille stop "${TARGET}"
else
info "\n[${TARGET}]:"
error_notify "Jail is running."
error_exit "Use [-a|--auto] to auto-stop the jail."
fi
fi
jail_export
fi
echo

View File

@@ -716,8 +716,8 @@ jail_import() {
# Check for user specified file location
if echo "${TARGET}" | grep -q '\/'; then
GETDIR="${TARGET}"
TARGET=$(echo ${TARGET} | awk -F '\/' '{print $NF}')
bastille_backupsdir=$(echo ${GETDIR} | sed "s/${TARGET}//")
TARGET="$(basename ${TARGET})"
bastille_backupsdir="$(dirname ${GETDIR})"
fi
# Check if backups directory/dataset exist
@@ -740,15 +740,17 @@ else
error_exit "[ERROR]: Archive '${TARGET}' not found."
else
# Assume user will import from standard input
TARGET_TRIM=${TARGET}
TARGET_TRIM="${TARGET}"
USER_IMPORT="1"
fi
fi
# Check if a running jail matches name or already exist
check_target_exists || error_exit "[ERROR]: Jail: ${TARGET_TRIM} already exists."
if check_target_exists "${TARGET_TRIM}"; then
error_exit "[ERROR]: Jail: ${TARGET_TRIM} already exists."
fi
if [ -n "${TARGET}" ]; then
info "\nAttempting to import jail: ${TARGET}..."
info "\nAttempting to import jail: ${TARGET_TRIM}..."
jail_import
fi
fi

View File

@@ -0,0 +1,308 @@
#!/bin/sh
#
# SPDX-License-Identifier: BSD-3-Clause
#
# Copyright (c) 2018-2025, Christer Edwards <christer.edwards@gmail.com>
# 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/common.sh
usage() {
error_notify "Usage: bastille migrate [option(s)] TARGET USER HOST"
cat << EOF
Options:
-a | --auto Auto mode. Start/stop jail(s) if required.
-d | --destroy Destroy local jail after migration.
-p | --password Use password based authentication.
-x | --debug Enable debug mode.
EOF
exit 1
}
# Handle options.
AUTO=0
OPT_DESTROY=0
OPT_PASSWORD=0
while [ "$#" -gt 0 ]; do
case "${1}" in
-h|--help|help)
usage
;;
-a|--auto)
AUTO=1
shift
;;
-d|--destroy)
OPT_DESTROY=1
shift
;;
-p|--password)
OPT_PASSWORD=1
shift
;;
-x|--debug)
enable_debug
shift
;;
-*)
for _opt in $(echo ${1} | sed 's/-//g' | fold -w1); do
case ${_opt} in
a) AUTO=1 ;;
d) OPT_DESTROY=1 ;;
p) OPT_PASSWORD=1 ;;
x) enable_debug ;;
*) error_exit "[ERROR]: Unknown Option: \"${1}\"" ;;
esac
done
shift
;;
*)
break
;;
esac
done
if [ "$#" -ne 3 ]; then
usage
fi
TARGET="${1}"
USER="${2}"
HOST="${3}"
bastille_root_check
set_target "${TARGET}"
validate_host_status() {
local _user="${1}"
local _host="${2}"
info "\nChecking remote host status..."
# Host uptime
if ! ping -c 1 ${_host} >/dev/null 2>/dev/null; then
error_exit "[ERROR]: Host appears to be down"
fi
# Host SSH check
if [ "${OPT_PASSWORD}" -eq 1 ]; then
if ! ${_sshpass_cmd} ssh ${_user}@${_host} exit >/dev/null 2>/dev/null; then
error_notify "[ERROR]: Could not establish ssh connection to host."
error_notify "Please make sure the remote host supports password based authentication"
error_exit "and you are using the correct password for user: '${_user}'"
fi
elif ! ${_sshpass_cmd} ssh ${_opt_ssh_key} ${_user}@${_host} exit >/dev/null 2>/dev/null; then
error_notify "[ERROR]: Could not establish ssh connection to host."
error_notify "Please make sure user '${_user}' has password-less access"
error_exit "or use '-p|--password' for password based authentication."
fi
echo "Host check successful."
}
migrate_cleanup() {
local _jail="${1}"
local _user="${2}"
local _host="${3}"
# Remove archive files from local and remote system
${_sshpass_cmd} ssh ${_opt_ssh_key} ${_user}@${_host} sudo rm -f "${_remote_bastille_migratedir}/${_jail}_*.*"
rm -f ${bastille_migratedir}/${_jail}_*.*
}
migrate_create_export() {
local _jail="${1}"
local _user="${2}"
local _host="${3}"
info "\nPreparing jail for migration..."
# Ensure new migrate directory is created
bastille setup -f
${_sshpass_cmd} ssh ${_opt_ssh_key} ${_user}@${_host} sudo bastille setup -f
# --xz for ZFS, otherwise --txz
if checkyesno bastille_zfs_enable; then
bastille export --xz ${_jail} ${bastille_migratedir}
else
bastille export --txz ${_jail} ${_bastille_migratedir}
fi
}
migrate_jail() {
local _jail="${1}"
local _user="${2}"
local _host="${3}"
local _remote_bastille_zfs_enable="$(${_sshpass_cmd} ssh ${_opt_ssh_key} ${_user}@${_host} sysrc -f /usr/local/etc/bastille/bastille.conf -n bastille_zfs_enable)"
local _remote_bastille_jailsdir="$(${_sshpass_cmd} ssh ${_opt_ssh_key} ${_user}@${_host} sysrc -f /usr/local/etc/bastille/bastille.conf -n bastille_jailsdir)"
local _remote_bastille_migratedir="$(${_sshpass_cmd} ssh ${_opt_ssh_key} ${_user}@${_host} sysrc -f /usr/local/etc/bastille/bastille.conf -n bastille_migratedir)"
local _remote_jail_list="$(${_sshpass_cmd} ssh ${_opt_ssh_key} ${_user}@${_host} bastille list jails)"
# Verify jail does not exist remotely
if echo "${_remote_jail_list}" | grep -Eoqw "${_jail}"; then
error_exit "[ERROR]: Jail already exists on remote system: ${_jail}"
fi
# Verify ZFS on both systems
if checkyesno bastille_zfs_enable; then
if ! checkyesno _remote_bastille_zfs_enable; then
error_notify "[ERROR]: ZFS is enabled locally, but not remotely."
error_exit "Enable ZFS remotely to continue."
else
migrate_create_export "${_jail}" "${_user}" "${_host}"
info "\nAttempting to migrate jail to remote system..."
local _file="$(find "${bastille_migratedir}" -maxdepth 1 -type f | grep -Eo "${_jail}_.*\.xz$" | head -n1)"
local _file_sha256="$(echo ${_file} | sed 's/\..*/.sha256/')"
# Send sha256
if ! ${_sshpass_cmd} scp ${_opt_ssh_key} ${bastille_migratedir}/${_file_sha256} ${_user}@${_host}:${_remote_bastille_migratedir}; then
migrate_cleanup "${_jail}" "${_user}" "${_host}"
error_exit "[ERROR]: Failed to send jail to remote system."
fi
# Send jail export
if ! ${_sshpass_cmd} scp ${_opt_ssh_key} ${bastille_migratedir}/${_file} ${_user}@${_host}:${_remote_bastille_migratedir}; then
migrate_cleanup "${_jail}" "${_user}" "${_host}"
error_exit "[ERROR]: Failed to send jail to remote system."
fi
fi
else
if checkyesno _remote_bastille_zfs_enable; then
error_notify "[ERROR]: ZFS is enabled remotely, but not locally."
error_exit "Enable ZFS locally to continue."
else
info "\nAttempting to migrate jail to remote system..."
migrate_create_export "${_jail}" "${_user}" "${_host}"
local _file="$(find "${bastille_migratedir}" -maxdepth 1 -type f | grep -Eo "${_jail}_.*\.txz$" | head -n1)"
local _file_sha256="$(echo ${_file} | sed 's/\..*/.sha256/')"
# Send sha256
if ! ${_sshpass_cmd} scp ${_opt_ssh_key} ${bastille_migratedir}/${_file_sha256} ${_user}@${_host}:${_remote_bastille_migratedir}; then
migrate_cleanup "${_jail}" "${_user}" "${_host}"
error_exit "[ERROR]: Failed to migrate jail to remote system."
fi
# Send jail export
if ! ${_sshpass_cmd} scp ${_opt_ssh_key} ${bastille_migratedir}/${_file} ${_user}@${_host}:${_remote_bastille_migratedir}; then
migrate_cleanup "${_jail}" "${_user}" "${_host}"
error_exit "[ERROR]: Failed to migrate jail to remote system."
fi
fi
fi
# Import the jail remotely
if ! ${_sshpass_cmd} ssh ${_opt_ssh_key} ${_user}@${_host} sudo bastille import ${_remote_bastille_migratedir}/${_file}; then
migrate_cleanup "${_jail}" "${_user}" "${_host}"
error_exit "[ERROR]: Failed to import jail on remote system."
fi
# Destroy old jail if OPT_DESTROY=1
if [ "${OPT_DESTROY}" -eq 1 ]; then
bastille destroy -af "${_jail}"
fi
# Remove archives
migrate_cleanup "${_jail}" "${_user}" "${_host}"
# Start new jail if AUTO=1
if [ "${AUTO}" -eq 1 ]; then
${_sshpass_cmd} ssh ${_opt_ssh_key} ${_user}@${_host} sudo bastille start "${_jail}"
fi
}
# Determine if user wants to authenticate via password
if [ "${OPT_PASSWORD}" -eq 1 ]; then
if ! which sshpass >/dev/null 2>/dev/null; then
error_exit "[ERROR]: Please install 'sshpass' to use password based authentication."
else
warn "[WARNING]: Password based authentication can be insecure."
printf "Please enter your password: "
# We disable terminal output for the password
stty -echo
read _password
stty echo
printf "\n"
_sshpass_cmd="sshpass -p ${_password}"
fi
else
_sshpass_cmd=
fi
# Get user we want to migrate as
# We need this to pass the ssh keys properly
if [ "${OPT_PASSWORD}" -eq 1 ]; then
_opt_ssh_key=
else
_migrate_user="$(sudo -u ${USER} whoami)"
_migrate_user_home="$(getent passwd migrate | cut -d: -f6)"
_migrate_user_ssh_key="${_migrate_user_home}/.ssh/id_rsa"
_opt_ssh_key="-i ${_migrate_user_ssh_key}"
fi
# Validate host uptime
validate_host_status "${USER}" "${HOST}"
for _jail in ${JAILS}; do
(
# Validate jail state
check_target_is_stopped "${_jail}" || if [ "${AUTO}" -eq 1 ]; then
bastille stop "${_jail}"
else
info "\n[${_jail}]:"
error_notify "Jail is running."
error_continue "Use [-a|--auto] to auto-stop the jail."
fi
info "\nAttempting to migrate '${_jail}' to '${HOST}'..."
migrate_jail "${_jail}" "${USER}" "${HOST}"
info "\nSuccessfully migrated '${_jail}' to '${HOST}'.\n"
) &
bastille_running_jobs "${bastille_process_limit}"
done
wait

View File

@@ -33,14 +33,128 @@
. /usr/local/share/bastille/common.sh
usage() {
error_exit "Usage: bastille setup [-p|pf|firewall] [-l|loopback] [-s|shared] [-z|zfs|storage] [-v|vnet] [-b|bridge]"
error_exit "Usage: bastille setup [-b|bridge] [-f|--filesystem] [-l|loopback] [-p|pf|firewall] [-s|shared] [-v|vnet] [-z|zfs|storage]"
}
# Check for too many args
if [ $# -gt 1 ]; then
if [ "$#" -gt 1 ]; then
usage
fi
configure_filesystem() {
# This is so we dont have to introduce breaking
# changes on new variables added to bastille.conf
# Ensure migrate directory is in place
## ${bastille_migratedir}
if [ -z "${bastille_migratedir}" ]; then
if ! grep -oq "bastille_migratedir=" "${BASTILLE_CONFIG}"; then
sed -i '' 's|bastille_backupsdir=.*|&\nbastille_migratedir=\"${bastille_prefix}/migrate\" ## default: \"${bastille_prefix}/migrate\"|' ${BASTILLE_CONFIG}
# shellcheck disable=SC1090
. ${BASTILLE_CONFIG}
fi
fi
## ${bastille_prefix}
if [ ! -d "${bastille_prefix}" ]; then
if checkyesno bastille_zfs_enable; then
if [ -n "${bastille_zfs_zpool}" ]; then
zfs create ${bastille_zfs_options} -o mountpoint="${bastille_prefix}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}"
fi
else
mkdir -p "${bastille_prefix}"
fi
chmod 0750 "${bastille_prefix}"
# Make sure the dataset is mounted in the proper place
elif [ -d "${bastille_prefix}" ]; then
if ! zfs list "${bastille_zfs_zpool}/${bastille_zfs_prefix}" >/dev/null; then
zfs create ${bastille_zfs_options} -o mountpoint="${bastille_prefix}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}"
elif [ "$(zfs get -H -o value mountpoint ${bastille_zfs_zpool}/${bastille_zfs_prefix})" != "${bastille_prefix}" ]; then
zfs set mountpoint="${bastille_prefix}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}"
fi
fi
## ${bastille_backupsdir}
if [ ! -d "${bastille_backupsdir}" ]; then
if checkyesno bastille_zfs_enable; then
if [ -n "${bastille_zfs_zpool}" ]; then
zfs create ${bastille_zfs_options} -o mountpoint="${bastille_backupsdir}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/backups"
fi
else
mkdir -p "${bastille_backupsdir}"
fi
chmod 0750 "${bastille_backupsdir}"
fi
## ${bastille_cachedir}
if [ ! -d "${bastille_cachedir}" ]; then
if checkyesno bastille_zfs_enable; then
if [ -n "${bastille_zfs_zpool}" ]; then
zfs create ${bastille_zfs_options} -o mountpoint="${bastille_cachedir}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/cache"
fi
else
mkdir -p "${bastille_cachedir}"
fi
fi
## ${bastille_jailsdir}
if [ ! -d "${bastille_jailsdir}" ]; then
if checkyesno bastille_zfs_enable; then
if [ -n "${bastille_zfs_zpool}" ]; then
zfs create ${bastille_zfs_options} -o mountpoint="${bastille_jailsdir}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails"
fi
else
mkdir -p "${bastille_jailsdir}"
fi
fi
## ${bastille_logsdir}
if [ ! -d "${bastille_logsdir}" ]; then
if checkyesno bastille_zfs_enable; then
if [ -n "${bastille_zfs_zpool}" ]; then
zfs create ${bastille_zfs_options} -o mountpoint="${bastille_logsdir}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/logs"
fi
else
mkdir -p "${bastille_logsdir}"
fi
fi
## ${bastille_templatesdir}
if [ ! -d "${bastille_templatesdir}" ]; then
if checkyesno bastille_zfs_enable; then
if [ -n "${bastille_zfs_zpool}" ]; then
zfs create ${bastille_zfs_options} -o mountpoint="${bastille_templatesdir}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/templates"
fi
else
mkdir -p "${bastille_templatesdir}"
fi
fi
## ${bastille_releasesdir}
if [ ! -d "${bastille_releasesdir}" ]; then
if checkyesno bastille_zfs_enable; then
if [ -n "${bastille_zfs_zpool}" ]; then
zfs create ${bastille_zfs_options} -o mountpoint="${bastille_releasesdir}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/releases"
fi
else
mkdir -p "${bastille_releasesdir}"
fi
fi
## ${bastille_migratedir}
if [ ! -d "${bastille_migratedir}" ]; then
if checkyesno bastille_zfs_enable; then
if [ -n "${bastille_zfs_zpool}" ]; then
zfs create ${bastille_zfs_options} -o mountpoint="${bastille_migratedir}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/migrate"
fi
else
mkdir -p "${bastille_migratedir}"
fi
chmod 0750 "${bastille_migratedir}"
fi
}
# Configure netgraph
configure_netgraph() {
if [ ! "$(kldstat -m netgraph)" ]; then
@@ -236,6 +350,7 @@ configure_zfs() {
# Run all base functions (w/o vnet) if no args
if [ $# -eq 0 ]; then
sysrc bastille_enable=YES
configure_filesystem
configure_loopback_interface
configure_pf
configure_zfs
@@ -248,6 +363,9 @@ case "$1" in
-h|--help|help)
usage
;;
-f|--filesystem)
configure_filesystem
;;
-p|pf|firewall)
configure_pf
;;
@@ -320,4 +438,4 @@ case "$1" in
*)
error_exit "[ERROR]: Unknown option: \"${1}\""
;;
esac
esac