mirror of
https://github.com/hackacad/bastille.git
synced 2025-12-20 17:20:12 +01:00
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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user