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.
This commit is contained in:
JRGTH
2025-01-21 05:55:37 -04:00
parent 05dc2b8d6a
commit af78f2b74d

View File

@@ -1,7 +1,5 @@
#!/bin/sh
#
# SPDX-License-Identifier: BSD-3-Clause
#
# Copyright (c) 2018-2025, Christer Edwards <christer.edwards@gmail.com>
# 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