Merge pull request #1282 from BastilleBSD/zfs-jail

This commit is contained in:
tschettervictor
2025-11-06 20:40:24 -07:00
committed by GitHub
4 changed files with 70 additions and 78 deletions

View File

@@ -85,8 +85,8 @@ and being able to fully manage it from within the jail.
To add a dataset to a jail, we can run
``bastille zfs TARGET jail pool/dataset /path/inside/jail``.
This will mount ``pool/dataset`` into the jail at ``/path/inside/jail`` when the
jail is started, and unmount and unjail it when the jail is stopped.
This will assign ``pool/dataset`` to the jail and mount it
at ``/path/inside/jail``.
You can manually change the path where the dataset will be mounted by
``bastille edit TARGET zfs.conf`` and adjusting the path after you have added it,
@@ -105,34 +105,5 @@ simple.
To remove a dataset from being jailed, we can run
``bastille zfs TARGET unjail pool/dataset``.
Template Approach
^^^^^^^^^^^^^^^^^
While it is possible to "jail" a dataset using a template, it is a bit more
"hacky" than the above apporach.
Below is a template that you can use that will add the necessary bits to the
``jail.conf`` file to "jail" a dataset.
.. code-block:: shell
ARG JAIL_NAME
ARG DATASET
ARG MOUNT
CONFIG set allow.mount
CONFIG set allow.mount.devfs
CONFIG set allow.mount.zfs
CONFIG set enforce_statfs 1
CONFIG set "exec.created += '/sbin/zfs jail ${JAIL_NAME} ${DATASET}'"
CONFIG set "exec.start += '/sbin/zfs set mountpoint=${MOUNT} ${DATASET}'"
RESTART
CONFIG set "exec.prestop += 'jexec -l -U root ${JAIL_NAME} /sbin/zfs umount ${DATASET}'"
CONFIG set "exec.prestop += '/sbin/zfs unjail ${JAIL_NAME} ${DATASET}'"
RESTART
This template can be applied using ``bastille template TARGET project/template --arg DATASET=zpool/dataset --arg MOUNT=/path/inside/jail``.
We do not need the ``JAIL_NAME`` arg, as it will be auto-filled from the supplied ``TARGET`` name.
NOTE: You must unjail any jailed datasets before attempting to destroy
a jail.

View File

@@ -181,19 +181,20 @@ for _jail in ${JAILS}; do
fi
fi
# Start jail
jail ${OPTION} -f "${bastille_jailsdir}/${_jail}/jail.conf" -c "${_jail}"
# Add ZFS jailed datasets
# Validate jailed datasets mountpoint
if [ -s "${bastille_jailsdir}/${_jail}/zfs.conf" ]; then
while read _dataset _mount; do
zfs set jailed=on "${_dataset}"
zfs jail ${_jail} "${_dataset}"
jexec -l -U root "${_jail}" zfs set mountpoint="${_mount}" "${_dataset}"
jexec -l -U root "${_jail}" zfs mount "${_dataset}" 2>/dev/null
while read dataset mount; do
if [ "$(zfs get -H -o value mountpoint ${dataset})" != "${mount}" ]; then
zfs set jailed=off "${dataset}"
zfs set mountpoint="${mount}" "${dataset}"
zfs set jailed=on "${dataset}"
fi
done < "${bastille_jailsdir}/${_jail}/zfs.conf"
fi
# Start jail
jail ${OPTION} -f "${bastille_jailsdir}/${_jail}/jail.conf" -c "${_jail}"
# Add rctl limits
if [ -s "${bastille_jailsdir}/${_jail}/rctl.conf" ]; then
while read _limits; do
@@ -218,4 +219,4 @@ for _jail in ${JAILS}; do
# Delay between jail action
sleep "${DELAY_TIME}"
done
done

View File

@@ -124,14 +124,6 @@ for _jail in ${JAILS}; do
bastille limits "${_jail}" clear
fi
# Unmount any jailed ZFS datasets
if [ -s "${bastille_jailsdir}/${_jail}/zfs.conf" ]; then
while read _dataset _mount; do
jexec -l -U root "${_jail}" zfs umount "${_dataset}"
zfs unjail "${_jail}" "${_dataset}"
done < "${bastille_jailsdir}/${_jail}/zfs.conf"
fi
# Stop jail
jail ${OPTION} -f "${bastille_jailsdir}/${_jail}/jail.conf" -r "${_jail}"

View File

@@ -53,6 +53,8 @@ EOF
zfs_jail_dataset() {
local jail_config="${bastille_jailsdir}/${JAIL}/jail.conf"
# Exit if MOUNT or DATASET is empty
if [ -z "${MOUNT}" ] || [ -z "${DATASET}" ]; then
usage
@@ -65,30 +67,48 @@ zfs_jail_dataset() {
if grep -hoqsw "${DATASET}" ${bastille_jailsdir}/*/zfs.conf; then
error_exit "[ERROR]: Dataset already assigned."
fi
# Validate jail state
check_target_is_stopped "${_jail}" || if [ "${AUTO}" -eq 1 ]; then
bastille stop "${_jail}"
check_target_is_stopped "${JAIL}" || if [ "${AUTO}" -eq 1 ]; then
bastille stop "${JAIL}"
else
error_notify "Jail is running."
error_exit "Use [-a|--auto] to auto-stop the jail."
fi
# Add necessary config variables to jail
bastille config ${_jail} set enforce_statfs 1 >/dev/null
bastille config ${_jail} set allow.mount >/dev/null
bastille config ${_jail} set allow.mount.devfs >/dev/null
bastille config ${_jail} set allow.mount.zfs >/dev/null
bastille config ${JAIL} set enforce_statfs 1 >/dev/null
bastille config ${JAIL} set allow.mount >/dev/null
bastille config ${JAIL} set allow.mount.devfs >/dev/null
bastille config ${JAIL} set allow.mount.zfs >/dev/null
# Enable ZFS inside jail
sysrc -f "${bastille_jailsdir}/${JAIL}/root/etc/rc.conf" zfs_enable="YES"
# Jail the dataset
zfs set mountpoint="${MOUNT}" "${DATASET}"
zfs set jailed=on "${DATASET}"
# Add dataset to zfs.conf
echo "${DATASET} ${MOUNT}" >> "${bastille_jailsdir}/${_jail}/zfs.conf"
echo "${DATASET} ${MOUNT}" >> "${bastille_jailsdir}/${JAIL}/zfs.conf"
# Add config to jail.conf
sed -i '' '/^}$/d' "${jail_config}"
cat << EOF >> "${jail_config}"
# Jailed dataset: ${DATASET}
exec.created += "zfs jail ${JAIL} ${DATASET}";
}
EOF
if [ "${AUTO}" -eq 1 ]; then
bastille start "${_jail}"
bastille start "${JAIL}"
fi
}
zfs_unjail_dataset() {
local jail_config="${bastille_jailsdir}/${JAIL}/jail.conf"
# Exit if DATASET is empty
if [ -z "${DATASET}" ]; then
usage
@@ -98,57 +118,65 @@ zfs_unjail_dataset() {
fi
# Validate jail state
check_target_is_stopped "${_jail}" || if [ "${AUTO}" -eq 1 ]; then
bastille stop "${_jail}"
check_target_is_stopped "${JAIL}" || if [ "${AUTO}" -eq 1 ]; then
bastille stop "${JAIL}"
else
error_notify "Jail is running."
error_exit "Use [-a|--auto] to auto-stop the jail."
fi
# Unjail the dataset
zfs set jailed=off "${DATASET}"
zfs umount "${DATASET}"
# Remove dataset from zfs.conf
if ! grep -hoqsw "${DATASET}" ${bastille_jailsdir}/${_jail}/zfs.conf; then
if ! grep -hoqsw "${DATASET}" ${bastille_jailsdir}/${JAIL}/zfs.conf; then
error_exit "[ERROR]: Dataset not present in zfs.conf."
else
sed -i '' "\#.*${DATASET}.*#d" "${bastille_jailsdir}/${_jail}/zfs.conf"
sed -i '' "\#.*${DATASET}.*#d" "${bastille_jailsdir}/${JAIL}/zfs.conf"
fi
# Remove config from jail.conf
sed -i '' "\#.*Jailed dataset: ${DATASET}.*#d" "${jail_config}"
sed -i '' "\#.*zfs jail ${JAIL} ${DATASET}.*#d" "${jail_config}"
if [ "${AUTO}" -eq 1 ]; then
bastille start "${_jail}"
bastille start "${JAIL}"
fi
}
zfs_snapshot() {
# shellcheck disable=SC2140
zfs snapshot -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}"@"${TAG}"
zfs snapshot -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}"@"${TAG}"
_return=$?
}
zfs_rollback() {
# shellcheck disable=SC2140
zfs rollback -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}"@"${TAG}"
zfs rollback -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}"@"${TAG}"
# shellcheck disable=SC2140
zfs rollback -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}/root"@"${TAG}"
zfs rollback -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}/root"@"${TAG}"
_return=$?
}
zfs_destroy_snapshot() {
# shellcheck disable=SC2140
zfs destroy ${OPT_DESTROY} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}"@"${TAG}"
zfs destroy ${OPT_DESTROY} "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}"@"${TAG}"
_return=$?
}
zfs_set_value() {
zfs set "${ATTRIBUTE}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}"
zfs set "${ATTRIBUTE}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}"
_return=$?
}
zfs_get_value() {
zfs get "${ATTRIBUTE}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}"
zfs get "${ATTRIBUTE}" "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}"
_return=$?
}
zfs_disk_usage() {
zfs list -t all -o name,used,avail,refer,mountpoint,compress,ratio -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail}"
zfs list -t all -o name,used,avail,refer,mountpoint,compress,ratio -r "${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL}"
_return=$?
}
@@ -162,10 +190,10 @@ snapshot_checks() {
# Verify rollback snapshots
if [ "${SNAP_ROLLBACK}" -eq 1 ]; then
if [ -n "${TAG}" ]; then
SNAP_TAG_CHECK="$(zfs list -H -t snapshot -o name ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail} | grep -o "${TAG}$" | tail -n 1)"
SNAP_TAG_CHECK="$(zfs list -H -t snapshot -o name ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL} | grep -o "${TAG}$" | tail -n 1)"
else
TAG="$(zfs list -H -t snapshot -o name ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${_jail} | grep -o "bastille_${_jail}_.*$" | tail -n 1)"
SNAP_TAG_CHECK=$(echo ${TAG} | grep -wo "bastille_${_jail}_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}")
TAG="$(zfs list -H -t snapshot -o name ${bastille_zfs_zpool}/${bastille_zfs_prefix}/jails/${JAIL} | grep -o "bastille_${JAIL}_.*$" | tail -n 1)"
SNAP_TAG_CHECK=$(echo ${TAG} | grep -wo "bastille_${JAIL}_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}")
fi
if [ -z "${SNAP_TAG_CHECK}" ]; then
error_continue "[ERROR]: Snapshot not found: ${TAG}"
@@ -177,10 +205,10 @@ snapshot_checks() {
# Generate a relatively short but unique name for the snapshots based on the current date/jail name.
elif [ "${AUTO_TAG}" -eq 1 ]; then
DATE=$(date +%F-%H%M%S)
TAG="bastille_${_jail}_${DATE}"
TAG="bastille_${JAIL}_${DATE}"
# Check for the generated snapshot name.
SNAP_GEN_CHECK=""
SNAP_GEN_CHECK=$(echo ${TAG} | grep -wo "bastille_${_jail}_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}")
SNAP_GEN_CHECK=$(echo ${TAG} | grep -wo "bastille_${JAIL}_[0-9]\{4\}-[0-9]\{2\}-[0-9]\{2\}-[0-9]\{6\}")
if [ -z "${SNAP_GEN_CHECK}" ]; then
error_notify "[ERROR]: Failed to validate snapshot name."
fi
@@ -296,9 +324,9 @@ if [ -z "${bastille_zfs_zpool}" ]; then
error_exit "[ERROR]: ZFS zpool not defined."
fi
for _jail in ${JAILS}; do
for JAIL in ${JAILS}; do
info "\n[${_jail}]:"
info "\n[${JAIL}]:"
case "${ACTION}" in
destroy|destroy_snap|destroy_snapshot)